123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175(*
* Copyright (c) 2014 Anil Madhavapeddy <anil@recoil.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*)openSexplib.StdopenAstringtypeservice={name:string;port:int;tls:bool}[@@derivingsexp](** Module type for a {{!resolution}resolver} that can map URIs to
concrete {{!Conduit.endp}endpoints} that stream connections can be
established with. *)moduletypeS=sig(** Abstract type of the cooperative threading library used, normally
defined via the {!IO} module type *)type+'aio(** State handle for a running resolver *)typet[@@derivingsexp](** Abstract type for a service entry, which maps a URI scheme into
a protocol handler and TCP port *)typesvc[@@derivingsexp](** A rewrite function resolves a {{!svc}service} and a URI into
a concrete endpoint. *)typerewrite_fn=svc->Uri.t->Conduit.endpio(** A service function maps the string (such as [http] or [ftp]) from
a URI scheme into a {{!svc}service} description that includes
enough metadata about the service to subsequently {{!rewrite_fn}resolve}
it into an {{!Conduit.endp}endpoint}. *)typeservice_fn=string->svcoptionioval(++):service_fn->service_fn->service_fn(** [init ?service ?rewrites] will initialize the resolver and return
a state handler. The {{!service_fn}service} argument should
contain the system-specific resolution mechanism for URI schemas.
The [rewrites] argument can optionally override a subset of the
URI domain name with the given {!rewrite_fn} to permit custom
resolution rules. For example, a rewrite rule for ".xen" would
let the rewrite function resolve hostnames such as "foo.xen"
into a shared memory channel for the "foo" virtual machine. *)valinit:?service:service_fn->?rewrites:(string*rewrite_fn)list->unit->t(** [add_rewrite ~host f t] will add to the [t] resolver the [f] rewrite rule
for all the domain names that shortest-prefix match [host] *)valadd_rewrite:host:string->f:rewrite_fn->t->unitvalset_service:f:service_fn->t->unitvalservice:t->service_fn(** [resolve_uri ?rewrites ~uri t] will use [t] to resolve the
[uri] into a concrete endpoint. Any [rewrites] that are passed
in will be overlayed on the existing rules within the [t]
resolver, but not otherwise modify it. *)valresolve_uri:?rewrites:(string*rewrite_fn)list->uri:Uri.t->t->Conduit.endpioendmoduleMake(IO:Conduit.IO)=structopenIOtypesvc=service[@@derivingsexp]type'aio='aIO.t(** A rewrite modifies an input URI with more specialization
towards a concrete [endp] *)typerewrite_fn=service->Uri.t->Conduit.endpIO.t[@@derivingsexp]typeservice_fn=string->serviceoptionIO.t[@@derivingsexp]typet={default_lookup:rewrite_fn;mutabledomains:rewrite_fnConduit_trie.t;mutableservice:service_fn;}[@@derivingsexp]letdefault_lookup_uri=(* TODO log *)lethost=matchUri.hosturiwith|None->""|Somehost->hostinreturn(`Unknownhost)letdefault_service_name=(* TODO log *)returnNonelethost_to_domain_listhost=(* TODO: slow, specialise the Trie to be a rev string list instead *)String.concat~sep:"."(List.rev(String.cuts~sep:"."host))letadd_rewrite~host~ft=t.domains<-Conduit_trie.insert(host_to_domain_listhost)ft.domainsletset_service~ft=t.service<-fletservicet=t.servicelet(++)fgh=fh>>=function|None->gh|x->returnxletinit?(service=default_service)?(rewrites=[])()=letdomains=Conduit_trie.emptyinlett={domains;default_lookup;service}inList.iter(fun(host,f)->add_rewrite~host~ft)rewrites;tletresolve_uri?rewrites~urit=(* Find the service associated with the URI *)matchUri.schemeuriwith|None->return(`Unknown"no scheme")|Somescheme->begint.servicescheme>>=function|None->return(`Unknown"unknown scheme")|Someservice->lethost=matchUri.hosturiwith|None->"localhost"|Somehost->hostinlettrie=(* If there are local rewrites, add them to the trie *)matchrewriteswith|None->t.domains|Somerewrites->List.fold_left(funacc(host,f)->Conduit_trie.insert(host_to_domain_listhost)facc)t.domainsrewritesin(* Find the longest prefix function that matches this host *)letfn=matchConduit_trie.longest_prefix(host_to_domain_listhost)triewith|None->t.default_lookup|Somefn->fninfnserviceuri>>=funendp->ifservice.tlsthenreturn(`TLS(host,endp))elsereturnendpendend