On this page:
4.1 Requests
header
headers-assq
headers-assq*
binding
binding: form
binding: file
bindings-assq
request
4.2 Bindings
request-bindings
request-headers
extract-binding/ single
extract-bindings
exists-binding?
4.3 Responses
response/ basic
response/ full
response/ incremental
response/ c
make-xexpr-response
normalize-response
TEXT/ HTML-MIME-TYPE
4.4 Placing Cookies
make-cookie
cookie->header
xexpr-response/ cookies
4.5 Extracting Cookies
client-cookie
request-cookies
4.6 Redirect
redirect-to
redirection-status?
permanently
temporarily
see-other
4.7 Basic Authentication
make-basic-auth-header
request->basic-credentials
4.8 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
Version: 4.2

4 HTTP: Hypertext Transfer Protocol

 (require web-server/http)

The Web Server implements many HTTP RFCs that are provided by this module.

4.1 Requests

 (require web-server/http/request-structs)

(struct header (field value))
  field : bytes?
  value : bytes?

Represents a header of field to value.

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

(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 binding (id))
  id : bytes?

Represents a binding of id.

(struct (binding:form binding) (value))
  value : bytes?

Represents a form binding of id to value.

(struct (binding:file binding) (filename headers content))
  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.)

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

(struct request (method
    uri
    headers/raw
    bindings/raw
    post-data/raw
    host-ip
    host-port
    client-ip))
  method : bytes?
  uri : url?
  headers/raw : (listof header?)
  bindings/raw : (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.

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)

These functions, while convenient, could introduce subtle bugs into your application. Examples: that they are case-insensitive could introduce a bug; 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.

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

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

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

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

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

(struct response/basic (code message seconds mime headers))
  code : number?
  message : bytes?
  seconds : number?
  mime : bytes?
  headers : (listof header?)

A basic HTTP response containing no body. code is the response code, message the message, seconds the generation time, mime the MIME type of the file, and extras are the extra headers, in addition to those produced by the server.

Example:

  (make-response/basic
   301 #"Moved Permanently"
   (current-seconds) TEXT/HTML-MIME-TYPE
   (list (make-header #"Location"
                      #"http://www.plt-scheme.org/downloads")))

(struct (response/full response/basic) (body))
  body : (listof bytes?)

As with response/basic, except with body as the response body.

Example:

  (make-response/full
   301 #"Moved Permanently"
   (current-seconds) TEXT/HTML-MIME-TYPE
   (list (make-header #"Location"
                      #"http://www.plt-scheme.org/downloads"))
   (list #"<html><body><p>"
         #"Please go to <a href=\""
         #"http://www.plt-scheme.org/downloads"
         #"\">here</a> instead."
         #"</p></body></html>"))

(struct (response/incremental response/basic) (generator))
  generator : ((() () #:rest (listof bytes?) . ->* . any) . -> . any)

As with response/basic, except with generator as a function that is called to generate the response body, by being given an output-response function that outputs the content it is called with.

Here is a short example:

  (make-response/incremental
    200 #"OK" (current-seconds)
    #"application/octet-stream"
    (list (make-header #"Content-Disposition"
                       #"attachment; filename=\"file\""))
    (lambda (send/bytes)
      (send/bytes #"Some content")
      (send/bytes)
      (send/bytes #"Even" #"more" #"content!")
      (send/bytes #"Now we're done")))

response/c : contract?

Equivalent to

  (or/c response/basic?
        (cons/c bytes? (listof (or/c string? bytes?)))
        xexpr/c)

(make-xexpr-response xexpr    
  [#:code code    
  #:message message    
  #:seconds seconds    
  #:mime-type mime-type    
  #:headers headers])  response/full?
  xexpr : xexpr/c
  code : number? = 200
  message : bytes? = #"Okay"
  seconds : number? = (current-seconds)
  mime-type : bytes? = TEXT/HTML-MIME-TYPE
  headers : (listof header?) = empty

Equivalent to

  (make-response/full
   code message seconds mime-type headers
   (list (string->bytes/utf-8 (xexpr->string xexpr))))

(normalize-response close? response)
  (or/c response/full? response/incremental?)
  close? : boolean?
  response : response/c

Coerces response into a full response, filling in additional details where appropriate.

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)

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

(make-cookie name    
  value    
  [#:comment comment    
  #:domain domain    
  #:max-age max-age    
  #:path path    
  #:secure? secure?])  cookie?
  name : string?
  value : string?
  comment : (or/c false/c string?) = #f
  domain : (or/c false/c valid-domain?) = #f
  max-age : (or/c false/c exact-nonnegative-integer?) = #f
  path : (or/c false/c string?) = #f
  secure? : (or/c false/c boolean?) = #f

Constructs a cookie with the appropriate fields.

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

Constructs a header that sets the cookie.

(xexpr-response/cookies cookies xexpr)  response/full?
  cookies : (listof cookie?)
  xexpr : xexpr/c

Constructs a response using xexpr that sets all the cookies in cookies.

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)
      (xexpr-response/cookies
       (list time-cookie id-cookie)
       `(html (head (title "Cookie Example"))
              (body (h1 "You're cookie'd!"))))))

Warning: When using cookies, make sure you follow the advice of the MIT Cookie Eaters, or you will be susceptible to dangerous attacks.

4.5 Extracting Cookies

 (require web-server/http/cookie-parse)

(struct client-cookie (name value domain path))
  name : string?
  value : string?
  domain : (or/c false/c valid-domain?)
  path : (or/c false/c string?)

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

(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)
     `(html (head (title "Hello!"))
            (body
             (h1 "Hello "
                 ,who))))

4.6 Redirect

 (require web-server/http/redirect)

(redirect-to uri    
  [perm/temp    
  #:headers headers])  response/c
  uri : non-empty-string/c
  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)

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

Determines if v is one of the following values.

permanently : redirection-status?

A redirection-status? for permanent redirections.

temporarily : redirection-status?

A redirection-status? for temporary redirections.

see-other : redirection-status?

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

4.7 Basic Authentication

 (require web-server/http/basic-auth)

An implementation of HTTP Basic Authentication.

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

(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)
       `(html (head (title "Basic Auth Test"))
              (body (h1 "User: " ,(bytes->string/utf-8 user))
                    (h1 "Pass: " ,(bytes->string/utf-8 pass))))]
      [else
       (make-response/basic
        401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
        (list
         (make-basic-auth-header
          (format "Basic Auth Test: ~a" (gensym)))))]))

4.8 Digest Authentication

 (require web-server/http/digest-auth)

An implementation of HTTP Digest Authentication.

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

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

username*realm->password/c : contract?

Used to look up the password for a user is a realm.

Equivalent to (-> string? string? string?).

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

Used to compute the user’s secret hash.

Equivalent to (-> string? string? bytes?).

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

(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 scheme/pretty)
  
  (define private-key "private-key")
  (define opaque "opaque")
  
  (define (start req)
    (match (request->digest-credentials req)
      [#f
       (make-response/basic
        401 #"Unauthorized" (current-seconds) TEXT/HTML-MIME-TYPE
        (list (make-digest-auth-header
               (format "Digest Auth Test: ~a" (gensym))
               private-key opaque)))]
      [alist
       (define check
         (make-check-digest-credentials
          (password->digest-HA1 (lambda (username realm) "pass"))))
       (define pass?
         (check "GET" alist))
       `(html (head (title "Digest Auth Test"))
              (body
               (h1 ,(if pass? "Pass!" "No Pass!"))
               (pre ,(pretty-format alist))))]))