4 HTTP: Hypertext Transfer Protocol
(require web-server/http) | package: web-server-lib |
The Web Server implements many HTTP libraries that are provided by this module.
4.1 Requests
(require web-server/http/request-structs) | |
package: web-server-lib |
struct
(struct header (field value) #:extra-constructor-name make-header) field : bytes? value : bytes?
struct
(struct binding (id) #:extra-constructor-name make-binding) id : bytes?
struct
(struct binding:form binding (value) #:extra-constructor-name make-binding:form) value : bytes?
struct
(struct binding:file binding (filename headers content) #:extra-constructor-name make-binding:file) filename : bytes? headers : (listof header?) content : bytes?
struct
(struct request ( method uri headers/raw bindings/raw-promise post-data/raw host-ip host-port client-ip) #:extra-constructor-name make-request) method : bytes? uri : url? headers/raw : (listof header?) bindings/raw-promise : (promise/c (listof binding?)) post-data/raw : (or/c false/c bytes?) host-ip : string? host-port : number? client-ip : string?
procedure
(request-bindings/raw r) → (listof binding?)
r : request?
(define (get-number req) (match (bindings-assq #"number" (request-bindings/raw req)) [(? binding:form? b) (string->number (bytes->string/utf-8 (binding:form-value b)))] [_ (get-number (request-number))]))
4.2 Bindings
(require web-server/http/bindings) | |
package: web-server-lib |
procedure
(request-bindings req)
→
(listof (or/c (cons/c symbol? string?) (cons/c symbol? bytes?))) req : request?
procedure
(extract-binding/single id binds) → string?
id : symbol? binds : (listof (cons/c symbol? string?))
procedure
(extract-bindings id binds) → (listof string?)
id : symbol? binds : (listof (cons/c symbol? string?))
procedure
(exists-binding? id binds) → boolean?
id : symbol? binds : (listof (cons/c symbol? string))
(define (get-number req) (string->number (extract-binding/single 'number (request-bindings req))))
4.3 Responses
struct
(struct response (code message seconds mime headers output))
code : response-code/c message : bytes? seconds : real? mime : (or/c #f bytes?) headers : (listof header?) output : (output-port? . -> . any)
value
response-code/c : flat-contract? = (integer-in 100 999)
(response 301 #"OK" (current-seconds) TEXT/HTML-MIME-TYPE empty (λ (op) (write-bytes #"<html><body>Hello, World!</body></html>" op))) (response 301 #"Moved Permanently" (current-seconds) TEXT/HTML-MIME-TYPE (list (make-header #"Location" #"http://racket-lang.org/download")) (λ (op) (write-bytes #"Moved" op))) (response 304 #"Not Modified" (current-seconds) #f (list (make-header #"Location" #"http://racket-lang.org/download")) void)
Changed in version 1.2 of package web-server-lib: Contract on output weakened to allow any
as the result (instead of demanding void?).
Changed in version 1.3: Added response-code/c and made the
contracts on code and seconds
stronger (rather than accepting number?).
procedure
(response/full code message seconds mime headers body) → response? code : response-code/c message : (or/c #f bytes?) seconds : real? mime : (or/c #f bytes?) headers : (listof header?) body : (listof bytes?)
(response/full 301 #"Moved Permanently" (current-seconds) TEXT/HTML-MIME-TYPE (list (make-header #"Location" #"http://racket-lang.org/download")) (list #"<html><body><p>" #"Please go to <a href=\"" #"http://racket-lang.org/download" #"\">here</a> instead." #"</p></body></html>"))
Code |
| Message |
100 |
| Continue |
101 |
| Switching Protocols |
200 |
| OK |
201 |
| Created |
202 |
| Accepted |
203 |
| Non-Authoritative Information |
204 |
| No Content |
205 |
| Reset Content |
300 |
| Multiple Choices |
301 |
| Moved Permanently |
302 |
| Found |
303 |
| See Other |
305 |
| Use Proxy |
307 |
| Temporary Redirect |
400 |
| Bad Request |
401 |
| Unauthorized |
402 |
| Payment Required |
403 |
| Forbidden |
404 |
| Not Found |
405 |
| Method Not Allowed |
406 |
| Not Acceptable |
407 |
| Proxy Authentication Required |
408 |
| Request Timeout |
409 |
| Conflict |
410 |
| Gone |
411 |
| Length Required |
413 |
| Payload Too Large |
414 |
| URI Too Long |
415 |
| Unsupported Media Type |
417 |
| Expectation Failed |
426 |
| Upgrade Required |
500 |
| Internal Server Error |
501 |
| Not Implemented |
502 |
| Bad Gateway |
503 |
| Service Unavailable |
504 |
| Gateway Timeout |
505 |
| HTTP Version Not Supported |
Changed in version 1.3 of package web-server-lib: Updated contracts on code and seconds as with response.
procedure
(response/output output [ #:code code #:message message #:seconds seconds #:mime-type mime-type #:headers headers]) → response? output : (-> output-port? any) code : number? = 200 message : (or/c false/c bytes?) = #f seconds : number? = (current-seconds) mime-type : (or/c bytes? #f) = TEXT/HTML-MIME-TYPE headers : (listof header?) = '()
(response code message seconds mime-type headers output)
Changed in version 1.2 of package web-server-lib: Contract on output weakened to allow any
as the result (instead of demanding void?).
Changed in version 1.3: Updated contracts on code and seconds
as with response.
Changed in version 1.4: Contract on message relaxed to allow both #f and a bytes?, with a default of #f. Previously, bytes? was required, and had a default of #"Okay".
value
4.4 Placing Cookies
(require web-server/http/cookie) | |
package: web-server-lib |
procedure
(make-cookie name value [ #:comment comment #:domain domain #:max-age max-age #:path path #:expires expires #:secure? secure? #:http-only? http-only? #:extension extension]) → cookie? name : cookie-name? value : cookie-value? comment : any/c = #f domain : (or/c domain-value? #f) = #f max-age : (or/c (and/c integer? positive?) #f) = #f path : (or/c path/extension-value? #f) = #f expires : (or/c date? string? #f) = #f secure? : any/c = #f http-only? : any/c = #f extension : (or/c path/extension-value? #f) = #f
Changed in version 1.3 of package web-server-lib: Added support for RFC 6265 via net/cookies/server. Enforce stronger contracts on string-valued arguments. Allow expires to be a date? and allow secure to be any/c (rather than boolean?). Forbid 0 for max-age. Support http-only? and extension arguments. Ignore comment.
procedure
(cookie->header c) → header?
c : cookie?
(define time-cookie (make-cookie "time" (number->string (current-seconds)))) (define id-cookie (make-cookie "id" "joseph" #:secure? #t)) (redirect-to "http://localhost/logged-in" see-other #:headers (map cookie->header (list time-cookie id-cookie))) (send/suspend (lambda (k-url) (response/xexpr #:cookies (list time-cookie id-cookie) `(html (head (title "Cookie Example")) (body (h1 "You're cookie'd!"))))))
4.5 Authenticated Cookies
(require web-server/http/id-cookie) | |
package: web-server-lib |
procedure
(make-id-cookie name value #:key secret-salt [ #:path path #:expires expires #:max-age max-age #:domain domain #:secure? secure? #:http-only? http-only? #:extension extension]) → cookie? name : (and/c string? cookie-name?) value : (and/c string? cookie-value?) secret-salt : bytes? path : (or/c path/extension-value? #f) = #f expires : (or/c date? #f) = #f max-age : (or/c (and/c integer? positive?) #f) = #f domain : (or/c domain-value? #f) = #f secure? : any/c = #f http-only? : any/c = #f extension : (or/c path/extension-value? #f) = #f
(make-id-cookie name secret-salt value [ #:path path #:expires expires #:max-age max-age #:domain domain #:secure? secure? #:http-only? http-only? #:extension extension]) → cookie? name : (and/c string? cookie-name?) secret-salt : bytes? value : (and/c string? cookie-value?) path : (or/c path/extension-value? #f) = #f expires : (or/c date? #f) = #f max-age : (or/c (and/c integer? positive?) #f) = #f domain : (or/c domain-value? #f) = #f secure? : any/c = #f http-only? : any/c = #t extension : (or/c path/extension-value? #f) = #f
Changed in version 1.3 of package web-server-lib: Added support for RFC 6265 as with make-cookie, including adding the optional arguments expires, max-age, domain, secure, extension, and http-only? (which is #true by default). Allowed secret-salt to be given with the keyword #:key instead of by position.
procedure
(request-id-cookie request #:name name #:key secret-salt [ #:timeout timeout #:shelf-life shelf-life]) → (or/c #f (and/c string? cookie-value?)) request : request? name : (and/c string? cookie-name?) secret-salt : bytes? timeout : real? = +inf.0 shelf-life : real? = +inf.0
(request-id-cookie name secret-salt request [ #:timeout timeout #:shelf-life shelf-life]) → (or/c #f (and/c string? cookie-value?)) name : (and/c string? cookie-name?) secret-salt : bytes? request : request? timeout : number? = +inf.0 shelf-life : real? = +inf.0
Changed in version 1.3 of package web-server-lib: Added shelf-life argument and support for giving name and secret-salt by keyword instead of by position. Added support for RFC 6265 as with make-cookie.
procedure
(valid-id-cookie? cookie #:name name #:key secret-salt [ #:timeout timeout #:shelf-life shelf-life]) → (or/c #f (and/c string? cookie-value?)) cookie : any/c name : (and/c string? cookie-name?) secret-salt : bytes? timeout : number? = +inf.0 shelf-life : real? = +inf.0
Added in version 1.3 of package web-server-lib.
procedure
(logout-id-cookie name [ #:path path #:domain domain]) → cookie? name : cookie-name? path : (or/c #f string?) = #f domain : (or/c domain-value? #f) = #f
Changed in version 1.3 of package web-server-lib: Added support for RFC 6265 as with make-cookie, including adding the domain argument.
procedure
(make-secret-salt/file secret-salt-path) → bytes?
secret-salt-path : path-string?
Changed in version 1.3 of package web-server-lib: Changed to use cryptographic-quality randomness to initialize secret-salt-path.
4.6 Extracting Cookies
(require web-server/http/cookie-parse) | |
package: web-server-lib |
struct
(struct client-cookie (name value domain path) #:extra-constructor-name make-client-cookie) name : (and/c string? cookie-name?) value : (and/c string? cookie-value?) domain : (or/c #f domain-value?) path : (or/c #f path/extension-value?)
procedure
(request-cookies req) → (listof client-cookie?)
req : request?
Changed in version 1.3 of package web-server-lib: Added support for RFC 6265 via net/cookies/common.
(define (start req) (define cookies (request-cookies req)) (define id-cookie (findf (lambda (c) (string=? "id" (client-cookie-name c))) cookies)) (if id-cookie (hello (client-cookie-value id-cookie)) (redirect-to (url->string (request-uri req)) see-other #:headers (list (cookie->header (make-cookie "id" "joseph")))))) (define (hello who) (response/xexpr `(html (head (title "Hello!")) (body (h1 "Hello " ,who)))))
4.7 Redirect
(require web-server/http/redirect) | |
package: web-server-lib |
procedure
(redirect-to uri [status #:headers headers]) → response?
uri : non-empty-string? status : redirection-status? = temporarily headers : (listof header?) = '()
procedure
(redirection-status? v) → boolean?
v : any/c
value
value
value
value
RFC 7231 suggests using 307 Temporary Redirect, i.e. temporarily/same-method. This has the disadvantage that search engines and others won’t update references to the old URI.
RFC 7538 specifies a new HTTP status, 308 Permanent Redirect, which forbids changing the request method, analogously to 307 Temporary Redirect. However, the RFC also highlights some important deployment considerations for this status. In particular, older browsers—
including, as of this writing, some that remain in relatively common use— do not understand this status and will fall back to the semantics of 300 Multiple Choices, which is often undesirable. The application can note the method of the original request and use permanently for GET and HEAD requests or one of the other alternatives for other methods.
Changed in version 1.3 of package web-server-lib: Added temporarily/same-method.
4.8 Basic Authentication
(require web-server/http/basic-auth) | |
package: web-server-lib |
procedure
(make-basic-auth-header realm) → header?
realm : string?
#lang web-server/insta (define (start req) (match (request->basic-credentials req) [(cons user pass) (response/xexpr `(html (head (title "Basic Auth Test")) (body (h1 "User: " ,(bytes->string/utf-8 user)) (h1 "Pass: " ,(bytes->string/utf-8 pass)))))] [else (response 401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE (list (make-basic-auth-header (format "Basic Auth Test: ~a" (gensym)))) void)]))
4.9 Digest Authentication
(require web-server/http/digest-auth) | |
package: web-server-lib |
procedure
(make-digest-auth-header realm private-key opaque) → header? realm : string? private-key : string? opaque : string?
procedure
(password->digest-HA1 lookup-password)
→ username*realm->digest-HA1/c lookup-password : username*realm->password/c
procedure
(make-check-digest-credentials lookup-HA1)
→ (string? (listof (cons/c symbol? string?)) . -> . boolean?) lookup-HA1 : username*realm->digest-HA1/c
#lang web-server/insta (require racket/pretty) (define private-key "private-key") (define opaque "opaque") (define (start req) (match (request->digest-credentials req) [#f (response 401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE (list (make-digest-auth-header (format "Digest Auth Test: ~a" (gensym)) private-key opaque)) void)] [alist (define check (make-check-digest-credentials (password->digest-HA1 (lambda (username realm) "pass")))) (define pass? (check "GET" alist)) (response/xexpr `(html (head (title "Digest Auth Test")) (body (h1 ,(if pass? "Pass!" "No Pass!")) (pre ,(pretty-format alist)))))]))
4.10 X-expression Support
(require web-server/http/xexpr) | package: web-server-lib |
procedure
(response/xexpr xexpr [ #:code code #:message message #:seconds seconds #:mime-type mime-type #:headers headers #:cookies cookies #:preamble preamble]) → response? xexpr : xexpr/c code : response-code/c = 200 message : (or/c #f bytes?) = #f seconds : real? = (current-seconds) mime-type : (or/c #f bytes?) = TEXT/HTML-MIME-TYPE headers : (listof header?) = empty cookies : (listof cookie?) = empty preamble : bytes? = #""
(response/full code message seconds mime-type (append headers (map cookie->header cookies)) (list preamble (string->bytes/utf-8 (xexpr->string xexpr))))
This is a viable function to pass to set-any->response!.
See the documentation for response/full to see how #f is handled for message.
Changed in version 1.3 of package web-server-lib: Updated contracts on code and seconds
as with response.
Changed in version 1.4: Contract on message relaxed to allow both #f and bytes?, with a default of #f. Previously, bytes? was required, and had a default of #"Okay".