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?
Changed in version 1.6 of package web-server-lib: Extended to support a port-based representation: see binding:file/port-in.
procedure
(binding:file/port-in binding) → input-port?
binding : binding:file/port?
procedure
(binding:file/port? v) → boolean?
v : any/c
procedure
(make-binding:file/port id filename headers content-in) → binding:file/port? id : bytes? filename : bytes? headers : (listof header?) content-in : input-port?
A given byte of input may be either stored in the binding:file-content field or read directly by from the input port, but never both; and
If the input port has already been closed directly when binding:file-content is called for the first time, binding:file-content will raise an exception.
Added in version 1.6 of package web-server-lib.
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 #f bytes?) host-ip : string? host-port : number? client-ip : string?
Changed in version 1.6 of package web-server-lib: Fixed to answer #f to serializable?, as all request instances contain non-serializable pieces.
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 |
206 |
| Partial Content |
300 |
| Multiple Choices |
301 |
| Moved Permanently |
302 |
| Found |
303 |
| See Other |
305 |
| Use Proxy |
307 |
| Temporary Redirect |
308 |
| Permanent 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
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 : cookie-name? value : 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 : cookie-name? secret-salt : bytes? value : 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.
Changed in version 1.6: Changed to accept any cookie-name? or cookie-value?
(rather than only strings) for the name and value arguments,
respectively, for consistency with make-cookie.
Fixed a bug that had incorrectly truncated cookie signatures:
note that previous versions of this library will not recognize cookies
created by the fixed make-id-cookie as validly signed, and vice versa.
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 : 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 : cookie-name? secret-salt : bytes? request : request? timeout : real? = +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.
Changed in version 1.6: Changed name argument to accept any cookie-name?
as with make-id-cookie.
Corrected the documented contract for the timeout argument.
Fixed a bug that had incorrectly truncated cookie signatures:
note that the fixed request-id-cookie will reject cookies
created by previous versions of this library, and vice versa.
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 : cookie-name? secret-salt : bytes? timeout : real? = +inf.0 shelf-life : real? = +inf.0
Added in version 1.3 of package web-server-lib.
Changed in version 1.6: Changed name argument to accept any cookie-name?
as with make-id-cookie.
Corrected the documented contract for the timeout argument.
Fixed a bug that had incorrectly truncated cookie signatures:
note that the fixed valid-id-cookie? will reject cookies
created by previous versions of this library, and vice versa.
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.
Changed in version 1.6: Fixed to accept any cookie-name? for the name
argument, as was previously documented.
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".
4.11 Empty Responses
(require web-server/http/empty) | package: web-server-lib |
procedure
(response/empty [ #:code code #:message message #:cookies cookies #:seconds seconds #:headers headers]) → response? code : number? = 204 message : (or/c false/c bytes?) = #f cookies : (listof cookie?) = '() seconds : number? = (current-seconds) headers : (listof header?) = '()
(response code message seconds #f headers (λ (o) (write-bytes #"" o)))
Added in version 1.6 of package web-server-lib.
Changed in version 1.7: Make default response code 204 rather than 200.
Changed in version 1.7: Ensure a Content-Length header is present in the response, with value 0.
4.12 JSON Support
(require web-server/http/json) | package: web-server-lib |
JSON is a widely used data format for the web. Racket’s JSON library meets the web server with response/jsexpr, which is for JSON what response/xexpr is for XML.
procedure
(response/jsexpr jsexpr [ #:code code #:message message #:seconds seconds #:mime-type mime-type #:headers headers #:cookies cookies]) → response? jsexpr : jsexpr? code : response-code/c = 200 message : (or/c #f bytes?) = #f seconds : real? = (current-seconds) mime-type : (or/c #f bytes?) = APPLICATION/JSON-MIME-TYPE headers : (listof header?) = empty cookies : (listof cookie?) = empty
(response/full code message seconds mime-type (append headers (map cookie->header cookies)) (list (jsexpr->bytes jsexpr)))
See the documentation for response/full to see how message, if #f, is turned into a bytes?.
Added in version 1.5 of package web-server-lib.