;; ----------------------------------------------------------------------------- ;; The File Server ;; The goal of this section is to develop a server that can locate the files ;; named in requests and send them back as responses. ;; PROBLEM 1: Understanding Requests, Validating their Legality ;; ---------- ;; Design the function legal-request. It ensures that the request line of the ;; request is a legal GET request. If not, it returns _false_. Otherwise, ;; it produces the uri part of the request. ;; Hints: ;; (1) The function performs two separate tasks. ;; (2) Read up on substring and string-length. ;; (3) You also want to know about string->list, list->string, and reverse. ;; Request -> String u false (define (legal-request full-request) (local ((define request-line (first full-request))) (cond [(well-formed request-line) (extract-uri request-line)] [else false]))) ;; String -> Boolean ;; is the request-line well-formed? (define (well-formed request) (local ((define length (string-length request)) (define left (string-length "GET ")) (define relength (string-length " HTTP/1.X")) (define right (- length relength)) (define protocol (substring request right length))) (and (string=? "GET " (substring request 0 left)) (or (string=? " HTTP/1.1" protocol) (string=? " HTTP/1.0" protocol)) (>= length (+ left relength))))) ;; String -> String ;; drop all chars from the beginning and end of string, ;; up to the first whitespace in each direction (define (extract-uri s) (local ((define loc (string->list s)) (define (remove-spaces loc) (cond [(empty? loc) (error 'legal-request "can't happen")] [else (cond [(char=? (first loc) #\space) (rest loc)] [else (remove-spaces (rest loc))])]))) (list->string (remove-spaces (reverse (remove-spaces (reverse loc))))))) ;; TESTS: (define req1 "GET HTTP/1.1") (define req2 "GET /home/matthias/ HTTP/1.0") (define req3 "GET HTTP/1.1") 'well-formed (equal? (well-formed req1) false) (equal? (well-formed req2) true) (equal? (well-formed req3) true) 'extract-uri (equal? (extract-uri req3) "") (equal? (extract-uri req2) "/home/matthias/") 'legal-request (equal? (legal-request (list req1)) false) (equal? (legal-request (list req2)) "/home/matthias/") (equal? (legal-request (list req3)) "") ;; PROBLEM 2: Turning a URI into a Path to a File ;; ---------- ;; Design the function full-path. It consumes a string that represents a URI ;; and returns a string representing a full path as described in the background ;; file. ;; Hint: (directory-exists? string) computes whether the given path is a directory ;; (and whether the directory exists). ;; String -> String (define (full-path p0) (cond [(string=? p0 "") "index.html"] [else (local ((define le (string-length p0)) (define p (substring p0 1 le))) (cond [(string=? p "") "index.html"] [(char=? (string-ref p (- le 2)) #\/) (string-append p "index.html")] [(directory-exists? p) (string-append p "/index.html")] [else p]))])) ;; TESTS: 'full-path (equal? (full-path "/matthias") "matthias/index.html") (equal? (full-path "/") "index.html") (equal? (full-path "") "index.html") (equal? (full-path "/matthias/matthew") "matthias/matthew/index.html") ;; Problem 3: Determining the Mime Type ;; --------- ;; Design a function that determine the mime type of the path. ;; It consumes a string representing the full path. If it ends in ".html", the ;; mime type is "text/html". If it ends in ".txt", it is "text/text". Otherwise ;; it is false. ;; String -> Mime ;; extract the mime type from the path (define (mime p) (local ((define length (string-length p))) (cond [(and (>= length 5) (string=? (substring p (- length 5) length) ".html")) "text/html"] [(and (>= length 4) (string=? (substring p (- length 4) length) ".txt")) "text/text"] [else false]))) (equal? (mime "index.html") "text/html") (equal? (mime "matthias/index.txt") "text/text") (equal? (mime "matthias/jones.mp3") false) ;; Problem 4: Compose the Response ;; --------- ;; Design a function that computes a Response to a given Request, using the ;; functions from the first three problems plus read-all from io.ss. ;; Request -> Response (define (request-produce request) (local ((define uri (legal-request request))) (cond [(string? uri) (local ((define fpath (full-path uri)) (define mimet (mime fpath))) (cond [(file-exists? fpath) (create-response 200 "Okay" "Saturday" mimet (read-all fpath))] [else (create-response 404 FNF "Saturday" "text/text" (list FNF))]))] [else (create-response 404 FNF "Saturday" "text/text" (list FNF))]))) (define FNF "File not found") ;; TESTS: You may want to use the following assumptions/tests: 'request-produce "assumptions: (1) index.html contains the words 'hello world'" "(2) matthias/matthew/index.html contains the words 'hello world'" "(3) matthias/robby doesn't exists" ;; illegal request (equal? (request-produce '("GET HTTP/1.1")) (create-response 404 FNF "Saturday" "text/text" (list FNF))) ;; request for non-existing file (equal? (request-produce '("GET /matthias/robby HTTP/1.1")) (create-response 404 FNF "Saturday" "text/text" (list FNF))) (equal? (request-produce '("GET / HTTP/1.1")) (create-response 200 "Okay" "Saturday" "text/html" '("hello world"))) (equal? (request-produce '("GET /matthias/matthew HTTP/1.1")) (create-response 200 "Okay" "Saturday" "text/html" '("hello world"))) (equal? (request-produce '("GET /matthias/matthew/ HTTP/1.1")) (create-response 200 "Okay" "Saturday" "text/html" '("hello world"))) ;; PROBLEM 5: Run server run ;; ---------- ;; After you have _designed_ the function of problem 4, run ; (server ... the-name-of-your-function-here ...) ;; and connect with a browser to the server at url http://127.0.0.1:4005/ .