Webs_bazaar.KurlKinds of URL requests and their formatters.
This module provides support to describe and handle URL requests by multiple client-defined OCaml types in a bidirectional and modular manner.
A kind value represents a subset of bare URL requests by values of a custom request OCaml type. Kinds are then used:
TODO.
bare type or on formatting functions ? Likely not on bare, you never get fragments to decode so it seems a bit OT.URLable (well with Hc every aspect becomes URLable). Bare could embody Webs.Http.Request.t.Kurl.kindThe type for bare URL requests. This embodies an HTTP method, an URL path, an URL query and an optional URL path file extension used by some URL formatting modes.
val bare :
?ext:string ->
?query:Webs.Http.Query.t ->
Webs.Http.Method.t ->
Webs.Http.Path.t ->
barebare m p ~query ~ext is an URL request to path p with method m, query query (defaults to Webs.Http.Query.empty) and URL path file extension ext (defaults to "").
module Bare : sig ... endBare URL requests.
type 'a enc = 'a -> bareThe type for encoding an URL request value of type 'a to a bare URL request.
type 'a dec = bare -> ('a option, Webs.Http.Response.t) Stdlib.resultThe type for decoding a bare URL request to a request value of type 'a. The path in bare url is relative to the service tree binding point.
The function returns:
Ok (Some r), if the request belongs to the kind of URL requests with value r.Ok None, if the request does not belong to the kind of URL requests. This lets other services attached to tree consider the prefix or an extension of it.Error r, if the request was meant to belong to the kind of URL requests but directly responding with r at that point was deemed appropriate. Examples are bad request, not allowed, etc. Possibly not found but if you want to let other subtrees consider the prefix use Ok NoneTODO.
TODO. Provide easy redirect.
val allow :
'a Webs.Http.Method.constraint' list ->
bare ->
('a, Webs.Http.Response.t) Stdlib.resultmeths ms u is:
Ok (Bare.meth u) if List.mem (Bare.meth u, Bare.meth u) msError _ with a 405 not allowed response otherwise.val kind :
?root_is:[ `Dir of string option | `File ] ->
?name:string ->
'a enc ->
'a dec ->
'a kindkind ?root_is ?name enc dec is a kind using enc and dec to codec bare URL request values. name is a name used for debugging purposes (defaults to "").
root_is defines formatting behaviour of the root path ([""]) relative to the service binding path. With:
`File the binding path encodes the root. Note that since the binding path must be well-formed it is guaranteed to lack a trailing slash. If extension formatting is requested and there is one in the encoded bare, it is appended to it. For example if the URL kind is bound to path ["mykind"] and enc encodes a bare URL with path [""] the formatted URL will be "/mykind".`Dir seg the binding path with a trailing slash is the resulting path. If extension formatting is requested and seg is Some seg then both segment seg (e.g. "index") and the extension are appended, otherwise the path is left as is. For example if the URL kind is bound to path ["mykind"] and enc encodes a bare URL with path [""] the formatted URL will be "/mykind/" or /mykind/seg if extension formatting is requested.TODO. Should root_is also affect decoding ?
IMPORTANT. Kinds have a notion of identity and they can only be bound once in a tree.
module Kind : sig ... endKinds of URL requests
any is Kind.bare ~name:"any" ~root_is:(`Dir (Some "index")) (). This is a catch-all bare request kind defined. It is always added at the root of URL formatters.
In conjunction with Bare.of_req_referer it allows for example to easily format relative URLs for requests performed by HTML page components or for request that do not exist in the service space like 404.
You can also bind it at the root of your service tree as the last service. It will provide you with a catch all handler.
The type for services handling URL requests with a value of type 'a, see service.
service k f is a service handling URL requests of kind k with f.
The return type of 'a is common to all services. Usually this is a function which when given arguments common to all services effectively handles the request. The service type simply hides the first, request kind specific, argument of these functions.
map_service f s applies f to the service of s.
The type for service trees binding constant HTTP paths to services of type 'a.
val empty : unit -> 'a treeempty () is an empty service tree.
val find_service :
'a tree ->
bare ->
('a option, Webs.Http.Response.t) Stdlib.resultfind_service t u finds the service for handling u's Webs.Http.Request.path. Ok None is returned if no URL request kind matched. Error _ is returned if a URL request kind matched but errored.
val bind : Webs.Http.Path.t -> 'a service -> 'a tree -> 'a treebind p s t is t with well-formed path p bound to s. Note shorter bindings to p in t take precedence, but s takes precedence over existing bindings at p. More precisely:
s' on a prefix of p in t, s will only be invoked for those requests for which the kind of s' decodes to Ok None.s' at p in t, s' will only be invoked for those requests for which the kind of s decodes Ok None.Raises Invalid_argument if s is already bound in t or if p is not well-formed.
unbind s t is t with the binding of s removed (if any).
val unbind_path : Webs.Http.Path.t -> 'a tree -> 'a treeunbind_path p t is t with all services bound to p removed (if any).
val service_path : 'a service -> 'a tree -> Webs.Http.Path.t optionservice_path s t is the path to which s is bound in t (if any).
val path_services : Webs.Http.Path.t -> 'a tree -> 'a service listpath_services p t are the services bound to p in t (if any).
val fold_paths :
(Webs.Http.Path.t -> 'a service list -> 'b -> 'b) ->
'a tree ->
'b ->
'bfold_paths f t acc folds f and acc over all paths of t in lexicographic order. This includes paths on which the service list is empty.
module Fmt : sig ... endURL request formatters.
HTTP path handling is a mess.
For this module a well-formed path is either the root path [""] or a non-empty Webs.Http.Request.path devoid of empty segments (including trailing ones).
To lessen the mess, in Kurl you can only bind to well-formed paths in service trees and URL formatters. Note that this doesn't mean that your kinds can't produce non well-formed paths.
It's not mandatory but also a good idea to use Webs.Http.Request.clean_path before handing your requests to find_service.
HTTP clients makes no difference between:
https://example.org = https://example.org/but they do between:
https://example.org/a ≠ https://example.org/a/This leads to all sorts of difficulties for abitrarily rebinding services, since the root is treated differently.
For this reason in tree services we never give an empty path to decode. That is if a service is bound on /a/b both requests with path /a/b/ and /a/b will result in path / ("root" of the service) in the bare URL to decode. For this reason it's a good idea to canonicalize requests before with Webs.Http.Request.clean_path.
Todo describe Kind.root_is.