4 HTTP: Hypertext Transfer Protocol

▼ |  | Web Applications in Racket

1 | Running Web Servlets
2 | Stateful Servlets
3 | Stateless Servlets
4 | HTTP: Hypertext Transfer Protocol
5 | URL-Based Dispatch
6 | Formlets: Functional Form Abstraction
7 | Templates: Separation of View
8 | Page: Short-hand for Common Patterns
9 | Testing Servlets
10 | Troubleshooting and Tips
| Index

► | 4 | HTTP: Hypertext Transfer Protocol

4.1 | Requests
4.2 | Bindings
4.3 | Responses
4.4 | Placing Cookies
4.5 | Authenticated Cookies
4.6 | Extracting Cookies
4.7 | Redirect
4.8 | Basic Authentication
4.9 | Digest Authentication
4.10 | X-expression Support

On this page:

4.1`` Requests
header
headers-assq
headers-assq*
binding
binding: form
binding: file
bindings-assq
bindings-assq-all
request
request-bindings/ raw
4.2`` Bindings
request-bindings
request-headers
extract-binding/ single
extract-bindings
exists-binding?
4.3`` Responses
response
response/ full
response/ output
TEXT/ HTML-MIME-TYPE
4.4`` Placing Cookies
make-cookie
cookie->header
4.5`` Authenticated Cookies
make-id-cookie
request-id-cookie
valid-id-cookie?
logout-id-cookie
make-secret-salt/ file
4.6`` Extracting Cookies
client-cookie
request-cookies
4.7`` Redirect
redirect-to
redirection-status?
permanently
temporarily
see-other
4.8`` Basic Authentication
make-basic-auth-header
request->basic-credentials
4.9`` Digest Authentication
make-digest-auth-header
request->digest-credentials
username*realm->password/ c
username*realm->digest-HA1/ c
password->digest-HA1
make-check-digest-credentials
4.10`` X-expression Support
response/ xexpr

top ← prev up next →

### 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?

Represents a header of field to value.

> procedure (headers-assq id heads) → (or/c false/c header?)
> id : bytes?
> heads : (listof header?)

Returns the header with a field equal to id from heads or #f.

> procedure (headers-assq* id heads) → (or/c false/c header?)
> id : bytes?
> heads : (listof header?)

Returns the header with a field case-insensitively equal to id from heads or #f.
You almost always want to use this, rather than headers-assq because Web browsers may send headers with arbitrary casing.

> > struct
> > (struct |  | binding (id)
> #:extra-constructor-name make-binding)
> id : bytes?

Represents a binding of id.

> > struct
> > (struct |  | binding:form binding (value)
> #:extra-constructor-name make-binding:form)
> value : bytes?

Represents a form binding of id to value.

> > struct
> > (struct |  | binding:file binding (filename headers content)
> #:extra-constructor-name make-binding:file)
> filename : bytes?
> headers : (listof header?)
> content : bytes?

Represents the uploading of the file filename with the id id and the content content, where headers are the additional headers from the MIME envelope the file was in. (For example, the #"Content-Type" header may be included by some browsers.)

> procedure (bindings-assq id binds) → (or/c false/c binding?)
> id : bytes?
> binds : (listof binding?)

Returns the binding with an id equal to id from binds or #f.

> procedure (bindings-assq-all id binds) → (listof binding?)
> id : bytes?
> binds : (listof binding?)

Like bindings-assq, but returns a list of all bindings matching id.

> > 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?

An HTTP method request to uri from client-ip to the server at host-ip:host-port with headers/raw headers, bindings/raw GET and POST queries and post-data/raw POST data.
You are unlikely to need to construct a request struct.

> procedure (request-bindings/raw r) → (listof binding?)
> r : request?

Forces (request-bindings/raw-promise r).

Here is an example typical of what you will find in many applications:

> (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

These functions, while convenient, could introduce subtle errors into your application. Examples: that they are case-insensitive could introduce an error; if the data submitted is not in UTF-8 format, then the conversion to a string will fail; if an attacker submits a form field as if it were a file, when it is not, then the request-bindings will hold a bytes? object and your program will error; and, for file uploads you lose the filename. Therefore, we recommend against their use, but they are provided for compatibility with old code.

> procedure (request-bindings req)
> | → |  |
> (listof (or/c (cons/c symbol? string?)
> (cons/c symbol? bytes?)))
> req : request?

Translates the request-bindings/raw of req by interpreting bytes? as string?s, except in the case of binding:file bindings, which are left as is. Ids are then translated into lowercase symbols.

> procedure (request-headers req) → (listof (cons/c symbol? string?))
> req : request?

Translates the request-headers/raw of req by interpreting bytes? as string?s. Ids are then translated into lowercase symbols.

> procedure (extract-binding/single id binds) → string?
> id : symbol?
> binds : (listof (cons/c symbol? string?))

Returns the single binding associated with id in the a-list binds if there is exactly one binding. Otherwise raises exn:fail.

> procedure (extract-bindings id binds) → (listof string?)
> id : symbol?
> binds : (listof (cons/c symbol? string?))

Returns a list of all the bindings of id in the a-list binds.

> procedure (exists-binding? id binds) → boolean?
> id : symbol?
> binds : (listof (cons/c symbol? string))

Returns #t if binds contains a binding for id. Otherwise, #f.

Here is an example typical of what you will find in many applications:

> (define (get-number req)
> (string->number
> (extract-binding/single
> 'number
> (request-bindings req))))

#### 4.3`` Responses

(require web-server/http/response-structs)
| package: web-server-lib

> struct (struct response (code message seconds mime headers output))
> code : number?
> message : bytes?
> seconds : number?
> mime : (or/c false/c bytes?)
> headers : (listof header?)
> output : (output-port? . -> . any)

An HTTP response where output produces the body by writing to the output port. code is the response code, message the message, seconds the generation time, mime the MIME type of the file, and headers are the headers.
If headers does not include Date, Last-Modified, or Server headers, then the server will automatically add them, where Date is based on current-seconds, Last-Modified is based on seconds, and Server is Racket.
If headers does not include Content-Type and mime is not #f, then mime is added as a Content-Type header.
The server will always replace your Connection header if it needs to ensure the connection will be closed. (Typically with an HTTP/1.0 client.)
The server will always puts headers it generates before those in the response structure and guarantees that the headers supplied appear in the output in the order given. (This is relevant if multiple occurrences of the same header have a different interpretation by the client, such as with Set-Cookie.)

Examples:

> (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 weaked to allow any as the result (instead of demanding void?).

> > procedure
> > (response/full |  | code |  |  |  |
> > |  | message |  |  |  |
> > |  | seconds |  |  |  |
> > |  | mime |  |  |  |
> > |  | headers |  |  |  |
> > |  | body) |  | → |  | response?
> code : number?
> message : bytes?
> seconds : number?
> mime : (or/c false/c bytes?)
> headers : (listof header?)
> body : (listof bytes?)

A constructor for responses where body is the response body.

Example:

> (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>"))

> > 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 : bytes? = #"Okay"
> seconds : number? = (current-seconds)
> mime-type : (or/c bytes? #f) = TEXT/HTML-MIME-TYPE
> headers : (listof header?) = '()

Equivalent to

> (response code message seconds mime-type headers output)

Changed in version 1.2 of package web-server-lib: Contract on output weaked to allow any as the result (instead of demanding void?).

> value TEXT/HTML-MIME-TYPE : bytes?

Equivalent to #"text/html; charset=utf-8".
Warning: If you include a Content-Length header in a response that is inaccurate, there will be an error in transmission that the server will not catch.

#### 4.4`` Placing Cookies

(require web-server/http/cookie)
| package: web-server-lib

This module provides functions to create cookies and responses that set them.

> > 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

Constructs a cookie with the appropriate fields.
This is a wrapper around make-cookie from net/cookies/server for backwards compatability. The comment argument is ignored. If expires is given as a string, it should match RFC 7231, Section 7.1.1.2, in which case it will be converted to a date? value. If conversion fails, an exn:fail:contract? is raised.

> procedure (cookie->header c) → header?
> c : cookie?

Constructs a header that sets the cookie.

Examples:

> (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

Cookies are useful for storing information of user’s browsers and particularly useful for storing identifying information for authentication, sessions, etc. However, there are inherent difficulties when using cookies as authenticators, because cookie data is fully controlled by the user, and thus cannot be trusted.
This module provides functions for creating and verifying authenticated cookies that are intrinsically timestamped. It is based on the algorithm proposed by the MIT Cookie Eaters: if you store the data data at time authored-seconds, then the user will receive digest&authored-seconds&data, where digest is an HMAC-SHA1 digest of authored-seconds and data, using an arbitrary secret key. When you receive a cookie, it will reverify this digest and check that the cookie’s authored-seconds is not after a timeout period, and only then return the cookie data to the program.
The interface represents the secret key as a byte string. The best way to generate this is by using random bytes from something like OpenSSL or /dev/random. This FAQ lists a few options. A convenient purely Racket-based option is available (make-secret-salt/file), which is implemented using crypto-random-bytes.

> > 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

Generates an authenticated cookie named name containing value, signed with secret-salt.
The calling conventions allow secret-salt to be given either as a keyword argument (mirroring the style of make-cookie) or a by-position argument (for compatability with older versions of this library).
The other arguments are passed to make-cookie; however, note that the default value for http-only? is #t. Users will also likely want to set secure? to #t when using HTTPS.

> > 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

Extracts the first authenticated cookie named name that was previously signed with secret-salt from request, with the allowable age of the cookie is controlled by shelf-life and timeout as with valid-id-cookie?.
If no valid cookie is available, returns #f.

> > 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

Recognizes authenticated cookies named name that were previously signed with secret-salt. Values satisfying either cookie? or client-cookie? can be recognized.
The shelf-life specifies the maximum age of the cookie in seconds. Cookies created more than shelf-life seconds ago will not be considered valid. The default value, +inf.0, permits all properly named and signed cookies.
Counterintuitively, the timeout argument requires that the cookie have been created before a certain moment in time: in other words, it requires that the cookie be older than a certain age. This is not usually what you want to restrict. Specifically, valid-id-cookie? tests that (<= authored timeout), where authored is the value returned by (current-seconds) when the cookie was created. The default value, +inf.0, permits all properly named and signed cookies.

> > 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

Generates a cookie named name that is not validly authenticated and expires in the past.
This will cause non-malicious browsers to overwrite a previously set cookie. If you use authenticated cookies for login information, you could send this to cause a "logout". However, malicious browsers do not need to respect such an overwrite. Therefore, this is not an effective way to implement timeouts or protect users on public (i.e. possibly compromised) computers. The only way to securely logout on the compromised computer is to have server-side state keeping track of which cookies (sessions, etc.) are invalid. Depending on your application, it may be better to track live sessions or dead sessions, or never set cookies to begin with and just use continuations, which you can revoke with send/finish.

> procedure (make-secret-salt/file secret-salt-path) → bytes?
> secret-salt-path : path-string?

Extracts the bytes from secret-salt-path. If secret-salt-path does not exist, then it is created and initialized with 128 random bytes.

#### 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?)

While server cookies are represented with cookie?s, cookies that come from the client are represented with a client-cookie structure.

> procedure (request-cookies req) → (listof client-cookie?)
> req : request?

Extracts the cookies from req’s headers.

Examples:

> (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 |  |  |  |
> > | [ | perm/temp |  |  |  |
> > |  | #:headers headers]) |  | → |  | response?
> uri : non-empty-string?
> perm/temp : redirection-status? = temporarily
> headers : (listof header?) = (list)

Generates an HTTP response that redirects the browser to uri, while including the headers in the response.
Example: (redirect-to "http://www.add-three-numbers.com" permanently)

> procedure (redirection-status? v) → boolean?
> v : any/c

Determines if v is one of the following values.

> value permanently : redirection-status?

A redirection-status? for permanent redirections.

> value temporarily : redirection-status?

A redirection-status? for temporary redirections.

> value see-other : redirection-status?

A redirection-status? for "see-other" redirections.

#### 4.8`` Basic Authentication

(require web-server/http/basic-auth)
| package: web-server-lib

An implementation of HTTP Basic Authentication.

> procedure (make-basic-auth-header realm) → header?
> realm : string?

Returns a header that instructs the Web browser to request a username and password from the client using Basic authentication with realm as the realm.

> procedure (request->basic-credentials req)
> → (or/c false/c (cons/c bytes? bytes?))
> req : request?

Returns a pair of the username and password from the authentication header in req if they are present, or #f.

Example:

> #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

An implementation of HTTP Digest Authentication.

> > procedure
> > (make-digest-auth-header |  | realm |  |  |  |
> > |  | private-key |  |  |  |
> > |  | opaque) |  | → |  | header?
> realm : string?
> private-key : string?
> opaque : string?

Returns a header that instructs the Web browser to request a username and password from the client using Digest authentication with realm as the realm, private-key as the server’s contribution to the nonce, and opaque as the opaque data passed through the client.

> procedure (request->digest-credentials req)
> → (or/c false/c (listof (cons/c symbol? string?)))
> req : request?

Returns the Digest credentials from req (if they appear) as an association list.

> value username*realm->password/c : contract?

Used to look up the password for a user is a realm.
Equivalent to (-> string? string? string?).

> value username*realm->digest-HA1/c : contract?

Used to compute the user’s secret hash.
Equivalent to (-> string? string? bytes?).

> procedure (password->digest-HA1 lookup-password)
> → username*realm->digest-HA1/c
> lookup-password : username*realm->password/c

Uses lookup-password to find the password, then computes the secret hash of it.

> procedure (make-check-digest-credentials lookup-HA1)
> → (string? (listof (cons/c symbol? string?)) . -> . boolean?)
> lookup-HA1 : username*realm->digest-HA1/c

Constructs a function that checks whether particular Digest credentials (the second argument of the returned function) are correct given the HTTP method provided as the first argument and the secret hash computed by lookup-HA1.
This is will result in an exception if the Digest credentials are missing portions.

Example:

> #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 : number? = 200
> message : bytes? = #"Okay"
> seconds : number? = (current-seconds)
> mime-type : (or/c false/c bytes?) = TEXT/HTML-MIME-TYPE
> headers : (listof header?) = empty
> cookies : (listof cookie?) = empty
> preamble : bytes? = #""

Equivalent to

> (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!.

top ← prev up next →