Simple Request/Response Model
The simplest possible conception of an http request in Haskell would be a function of the form
Request a -> IO (Response b). In Dormouse, we seek to deviate as little as possible from that form and where we do, we do so only to support additional expressiveness and safety against constructing incorrect requests.
Dormouse's representation of HTTP requests look like this:
Hopefully there is nothing too surprising going on here, an HTTP request is an entity consisting of a request method, url, a collection of headers and a content body. Let's take a look at each of the type parameters:
urltype parameter tags the request with the Url scheme such that we can define functions that shoud accept strictly
https; or either of the above.
methodtype parameter tags the request with the name of the HTTP verb, this is used, for example to define functions that will not type-check if you attempt to supply a request body to a request method that does not allow them, e.g.
bodytype parameter allows us to support arbitrary data in the content body of the request.
acceptTagtype parameters are a little more complicated, these are phantom types that are used to indicate media types in the request
Acceptheaders. We will see how they are used later!
Let's also introduce some Dormouse defined tyes:
HeaderNameis simply a type alias for
CI ByteString(a case-insensitive Strict ByteString).
HttpMethod methodis a GADT with all your standard HTTP Verbs (e.g.
GET :: HttpMethod "GET") defined as well as support for custom methods.
Note: Serialisation of the request is handled by a multi-parameter type class which considers the
body of the response, i.e. the structure of the data, and the
contentTag which describes the format the data should be serialised in.
Dormouse provides a series of functions that can be used to create and incrementally transform an HTTP request towards a finalised form. Below, we describe a suggested path for convenient construction of
Step one: request method and URL
Dormouse defines some convenient helper functions for creating request templates for each HTTP Verb against a supplied Url.
Here is the one for post:
This generates a basic HTTP POST request with no payload.
Step two: request body
If we are using an HTTP method like
POST that permits request bodies, we can supply a request body using the
The supply function transforms the initial request in to ways:
- It adds the supplied request body to the request.
- It adds a
Content-Typeheader to the request based on the supplied
In this example, a
Content-Type: application/json header would be added to the request.
Step three: supply an accept header
We can supply an
Accept header in our request using the
This function fixes the
acceptTag phantom type and sets a corresponding
While this function is attached to the request, it is primarily useful because it allows us to introduce expectations at the type level about what kind of response we expect to receive.
Dormouse's representation of HTTP responses look like this:
I think this is mostly self explanatory but the
body type parameter allows us to support arbitrary data in the content body of the response.
Getting an HTTP response in Dormouse is most often achieved by the
expect function. Let's take a look at its type signature
This function does several things:
- Serialises the arbitrary data in the HTTP request's content body into a stream of
- Delegates sending the HTTP request, including the content body stream of
Word8s to some
- Receives the HTTP response, including a content body stream of
- Deserialises the HTTP responses' content body stream of
Word8s into some expected type