HTTP request handling cookbook

A few conventions and recipes to handle requests.

Error handling

HTTP request processing entails a lot of error handling. Webs encourages you to deal with it using Stdlib.result values in which the error case is an HTTP error response.

By using the result binding operators, combinators in Webs.Http.Request and your own, you can mostly highlight the "happy" path in your code while being extremely accurate and correct in your HTTP error handling.

For example:

open Result.Syntax
open Webs

let respond : Http.Request.t -> (Http.Response.t, Http.Response.t) result =
fun request ->
  (* Make sure we have no empty or trailing segments *)
  let* () = Http.Request.clean_path request in
  (* Make sure that's only a GET or POST request *) in
  let* meth = Http.Request.allow Http.Method.[get,post] request in
  (* Extract the query *)
  let* q = Http.Request.to_query request in
  …

In the code above:

When it's time to response distinguishing between the Ok _ and Error _ responses is no longer relevant so you can simply Result.retract the result type:

let service request = Result.retract (respond request)
let main () = Webs_quick.serve service
let () = if !Sys.interactive then () else exit (main ())

Dealing with paths

What is the path and the service path ?

The service path is not a concept you find in HTTP. It is added by Webs to allow your service to be attached at arbitrary point of a server's path hierarchy. It assumes that your service gets attached to a root of the path hierarchy, typically you specify this root to the connector that runs your service. This root is made available in the request value as Http.Request.service_path.

Connectors are in charge of stripping the service path from the raw request path and provide the resulting path as the Webs.Http.Request.path property in the request values they hand you out.

How do I clean paths ?

The Webs.Http.Request.clean_path allows to clean paths. Note that this function does not handle dot segments and the resulting paths remain dangerious for file seving. See serving_files for more details.

TODO. What's the idea with not handling dot segments ?

How do I access the raw path ?

The Webs.Http.Request.raw_path property of a request has the path and query as found in the HTTP request. Note that using this property is not a good idea as it makes your responses sensitive to where your service is attached.

Dealing with queries

How do I access the raw path query ?

The Webs.Http.Request.raw_path property of a request has the path and query as found in the HTTP request. The Webs.Http.Request.query function detaches the query from the path.

How do I parse the query ?

The Webs.Http.Request.to_query parses a query regardless of the request method and thus regardless of whether the request is in the body (POST method) or in the request path (GET method).

TODO say something about file and multiparts and that it may not be a good idea in these cases.

Dealing with methods

How do I constrain the allowed methods ?

The Webs.Http.Request.allow combinator allows you to constrain and match the allowed methods of a request. For examples:

open Result.Syntax
open Webs

let respond : Http.Request.t -> (Http.Response.t, Http.Response.t) result =
fun request ->
  let* meth = Http.Request.allow Http.Method.[get,head] request in
  match meth with
  | `GET -> …
  | `HEAD -> …

If the request has not the right method a suitable Webs.Http.Status.method_not_allowed_405 response which includes a proper Webs.Http.Headers.allow header is returned in the Error _ case.

Dealing with headers

Dealing with cookies

Dealing with etags