On this page:
1.1 Using the Handler API
1.2 Using the Response API
8.12

1 Introduction to http123🔗ℹ

This library’s http-client<%> objects support two styles of usage: a high-level handler-based interface and a low-level response-based interface.

1.1 Using the Handler API🔗ℹ

This section introduces the high-level, handlers-based client API.

Create a client with default header fields and content handlers for expected content types:
> (define client
    (http-client #:add-header
                 `([x-racket-version ,(version)])
                 #:add-content-handlers
                 `([application/json ,read-json])))
Header fields can be specified in several different forms.

Construct a request with a method and URL:
> (define time-req (request 'GET "http://date.jsontest.com/"))
A request can also contain a header and data (a message body). Fields in the request header override any fields of the same name in the client’s default header.

Execute the request and handle the response:
> (send client handle time-req)

'#hasheq((date . "07-21-2021")

         (milliseconds_since_epoch . 1626909431013)

         (time . "11:17:11 PM"))

The Content-Type of this response is application/json, which the client is configured to handle using read-json.

If the client has no handler for the response’s content type, it raises an exception. For example:
> (send client handle (request 'GET "https://tools.ietf.org/rfc/rfc7540.txt"))

handle: no content handler matched

  code: 'unhandled-content

  response: 200 response with text/plain body

  received: 'yes

You can create a new client with additional handlers by calling the fork method. The resulting client shares its connections with the original client.
> (define client2
    (send client fork
          #:add-content-handlers
          `([text/plain ,(lambda (in) (read-string 40 in))]
            [*/* ,(lambda (in)
                    (format "something called ~s, I guess"
                            (send (current-response) get-content-type)))])))
> (send client2 handle (request 'GET "https://tools.ietf.org/rfc/rfc7540.txt"))

"\n\n\n\n\n\nInternet Engineering Task Force (I"

> (send client2 handle (request 'GET "https://www.google.com/"))

"something called text/html, I guess"

By default, the content handlers are only called for responses with status code 200 (“Found”). The default response handler raises an exception for any other response.
> (send client2 handle (request 'GET "https://mirror.racket-lang.org/no-such-file.html"))

handle: no response handler matched

  code: 'unhandled-response

  response: 404 response with text/html body

  received: 'yes

Additional response handlers can be added for individual status codes or status classes:
> (define client3
    (send client2 fork
          #:add-response-handlers
          `([404 ,(lambda (client resp) 'not-found)]
            [client-error ,(lambda (client resp) 'failed)])))
> (send client3 handle (request 'GET "https://racket-lang.org/secret-plans.scrbl"))

'failed

> (send client3 handle (request 'GET "https://mirror.racket-lang.org/no-such-file.html"))

'not-found

A response handler can call back to the client’s handle-response-content method to process the content of responses with other status codes:
> (define client4
    (send client3 fork
          #:add-response-handlers
          `([404 ,(lambda (client resp)
                    (list 'not-found (send client handle-response-content resp)))])))
> (send client4 handle (request 'GET "https://mirror.racket-lang.org/no-such-file.html"))

'(not-found "something called text/html, I guess")

1.2 Using the Response API🔗ℹ

This section introduces a lower-level client API that users can use to simply retrieve response objects.

Create a client:
> (define client (http-client))

Create a request:
> (define header '("Accept-Encoding: gzip, deflate"
                   (accept-language #"en")
                   (#"User-Agent" #"racket-http123/0.1")))
> (define req (request 'GET "https://www.google.com/" header #f))

Use sync-request to perform the request and get a response:
> (define resp (send client sync-request req))
> ; ... prune away some header fields ...
> resp

(new http2-response%

 (status-code 200)

 (request

  (request

   'GET

   (url "https" #f "www.google.com" #f #t (list (path/param "" '())) '() #f)

   '((#"accept-encoding" #"gzip, deflate")

     (#"accept-language" #"en")

     (#"user-agent" #"racket-http123/0.1"))

   #f))

 (header-fields

  '((#"date" #"Wed, 21 Jul 2021 23:17:18 GMT")

    (#"expires" #"-1")

    (#"cache-control" #"private, max-age=0")

    (#"content-type" #"text/html; charset=ISO-8859-1")

    (#"p3p"

     #"CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"")

    (#"content-encoding" #"gzip")

    (#"server" #"gws")

    (#"content-length" #"6151")

    (#"x-xss-protection" #"0")

    (#"x-frame-options" #"SAMEORIGIN")))

 ...)

> (send resp get-status-code)

200

The response’s content is available as an input port. If the response specifies a Content-Encoding of gzip or deflate, the content is automatically decompressed.
> (read-string 15 (send resp get-content-in))

"<!doctype html>"

Once read, content data is gone forever!
> (read-string 5 (send resp get-content-in))

"<html"

Using async-request it is possible to submit send requests and receive responses as they arrive. In particular, with an HTTP/2 connection, responses may arrive in an order different from the order the requests were sent. Of course, responses using different connections are always unordered.

The event returned by async-request produces a thunk as its synchronization result; apply the thunk to get the response or to raise an exception if there was an error getting the response. (See Synchronizable Event Results for rationale.)
> (define ietf-evt
    (send client async-request
          (request 'GET "https://tools.ietf.org/rfc/rfc7540.txt")))
> (define google-evt
    (send client async-request
          (request 'GET "https://www.google.com/")))
> ((sync ietf-evt google-evt))

(new http2-response%

 (status-code 200)

 (request

  (request

   'GET

   (url "https" #f "www.google.com" #f #t (list (path/param "" '())) '() #f)

   '((#"accept-encoding" #"gzip, deflate")

     (#"user-agent" #"racket-http123/0.1"))

   #f))

 (header-fields

  '((#"date" #"Wed, 21 Jul 2021 23:17:19 GMT")

    (#"expires" #"-1")

    (#"cache-control" #"private, max-age=0")

    (#"content-type" #"text/html; charset=ISO-8859-1")

    (#"p3p"

     #"CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"")

    (#"content-encoding" #"gzip")

    (#"server" #"gws")

    (#"content-length" #"5903")

    (#"x-xss-protection" #"0")

    (#"x-frame-options" #"SAMEORIGIN")))

 ...)