Module Webs.Req

HTTP requests.

Request bodies

type body = unit -> (bytes * int * int) option

The type for request bodies.

Bodies are blocking functions pulled by services to yield byte chunks of data of the request body as Some (bytes, first, len) values. The bytes value must not be modified and is readable from first to first+len until the next call to the function. The function returns None at the end of stream.

val empty_body : body

empty_body is an empty body.

val body_to_string : body -> string

body_to_string b accumulates the body to a string.

HTTP Requests

type t

The type for HTTP requests.

val v : ?init:t -> ?body:body -> ?body_length:int option -> ?headers:Http.headers -> ?meth:Http.meth -> ?path:Http.path -> ?query:string option -> ?request_target:string -> ?service_path:Http.path -> ?version:Http.version -> unit -> t

v ~init () is an HTTP request with given attributes and for those that are unspecified the ones of init (defaults to default).

Important. This is not checked by the module but clients of this function, at least connectors, should maintain these invariant:

  • request_target is the raw request target, still percent encoded.
  • Path.concat service_path path should represent the path of request_target.
  • query (if any) should correspond to the query of request_target.

Routing function may tweak paths but it's a good idea to keep request_target unchanged.

val default : t

default is a request whose body is empty_body, body_length is None, headers is Http.Headers.empty, meth is `GET, path is [""], query is None, request_target is "/", sevice_path is [""], version is (1,1).

val body : t -> body

body r is r's body.

val body_length : t -> int option

body_length r is r's request body length (if known).

val headers : t -> Http.headers

headers r is r's HTTP headers. Includes at least the Http.host header.

val meth : t -> Http.meth

meth r is r's HTTP method.

val path : t -> Http.path

path r should be (see v) the absolute path of request_target, stripped by service_path.

val query : t -> string option

query r should be (see v) the query (without the '?') of request_target. Note that query string may be the empty string which is different from None (no '?' in the request target). To decode the query (and handle those that are POSTed) see to_query.

val request_target : t -> string

request_target is r's request target. This should be the raw request, still percent encoded. Note that you usually rather want to use the convenience path and query which should (see v) derived from this value.

val service_path : t -> Http.path

service_path r is the path on which the root of the service is served. This is usually set by the connector. The path value of r is usually the path mentioned in request_target stripped by the service path.

val version : t -> Http.version

version r is r's HTTP version.

FIXME. now that we have init in v consider removing those.

val with_headers : Http.headers -> t -> t

with_headers hs r is r with headers hs.

val with_body : body_length:int option -> body -> t -> t

with_body blen b r is r with body length blen and body b.

val with_path : Http.path -> t -> t

with_path p r is r with path p.

val with_service_path : Http.path -> t -> t

with_service_path p r is r with service path r.

val pp : Stdlib.Format.formatter -> t -> unit

pp ppf req prints and unspecified representation of req on ppf but guarantees not to consume the body.

val echo : ?status:Http.status -> t -> Resp.t

echo r returns r as a 404 text/plain document (and by doing so violates numerous HTTP's musts). This includes the request body, which is consumed by the service.

Request deconstruction and responses

Request deconstruction helpers. These functions directly error with responses that have the right statuses and empty bodies.

Header decoding

val decode_header : Http.name -> (string -> ('a, string) Stdlib.result) -> t -> ('a optionResp.t) Stdlib.result

decode_header h dec r decodes header h (if any) in r. Errors with Http.bad_request_400 in case of decoding errors.

Method constraints

module Allow : sig ... end

Method constraints.

Cookies

find_cookie ~name r is the value of cookie name or None if undefined in r. Errors on header or cookie decoding errors.

Service forwarding

FIXME.

val service_redirect : ?explain:string -> int -> Http.path -> t -> Resp.t

service_redirect status p r redirects r to the service path p (this means r's service_path is prefixed to p) with status status. See also Resp.redirect.

val forward_service : strip:Http.path -> t -> (tResp.t) Stdlib.result

forward_service ~strip r is:

  • Ok r' with r' the request r with a path made of r's path stripped by strip and a service_root made of r's service root concatenated with strip.
  • Error _ with a Http.not_found_404 if stripping results in None.

FIXME. Because of the new behaviour of Http.Path.strip_prefix on root. This may introduce empty path segments that did not exist originally in the request when one concatenates the service root and the path. Would that be an argument to let also represent the root path ?

val to_service : strip:Http.path -> t -> (tResp.t) Stdlib.result

to_service ~strip r strips strip from r's path and appends it to the service root. Errors with Http.not_found_404 if stripping strip results in None.

Queries

Warning. Http.query values are untrusted, you need to properly validate their data.

val to_query : t -> (Http.queryResp.t) Stdlib.result

to_query r extracts a query from r. This is

Path cleaning

There's more than one way to handle empty segments and trailing slashes in request paths. The scheme proposed here simply always redirects to paths in which all empty segments, and thus trailing slashes, are removed; except on the root path. The advantage is that no elaborate file extension logic on the final segment is needed to route file serving.

val clean_path : t -> (tResp.t) Stdlib.result

clean_path r is:

  • Ok r if r's path is [], [""] or if it has no empty segment.
  • Error _ with a Http.moved_permanently_301 to r's path without empty segments or the root if that results in the empty path.

Warning. This cleaning does not touch dot segments or percent-encoded directory separators that may be present in the path. You should still use that function or to_absolute_filepath for mapping paths to file paths.

Absolute file paths

val to_absolute_filepath : ?strip:Http.path -> root:Http.fpath -> t -> (Http.fpathResp.t) Stdlib.result

absolute_filepath ~strip ~root r determines an absolute file path strictly rooted in root by stripping strip (defaults to [""]) from r's path, converting the result to an absolute filepath and prefixing it with docroot.

Errors with Http.not_found_404 if stripping strip results in None and Http.bad_request_400 if the absolute path conversion fails.