123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151(*
* 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=sigtype+'aio(** Abstract type of the cooperative threading library used, normally defined
via the {!IO} module type *)typet[@@derivingsexp](** State handle for a running resolver *)typesvc[@@derivingsexp](** Abstract type for a service entry, which maps a URI scheme into a protocol
handler and TCP port *)typerewrite_fn=svc->Uri.t->Conduit.endpio(** A rewrite function resolves a {{!svc} service} and a URI into a concrete
endpoint. *)typeservice_fn=string->svcoptionio(** 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}. *)val(++):service_fn->service_fn->service_fnvalinit:?service:service_fn->?rewrites:(string*rewrite_fn)list->unit->t(** [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. *)valadd_rewrite:host:string->f:rewrite_fn->t->unit(** [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] *)valset_service:f:service_fn->t->unitvalservice:t->service_fnvalresolve_uri:?rewrites:(string*rewrite_fn)list->uri:Uri.t->t->Conduit.endpio(** [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. *)endmoduleMake(IO:Conduit.IO)=structopenIOtypesvc=service[@@derivingsexp]type'aio='aIO.ttyperewrite_fn=service->Uri.t->Conduit.endpIO.t[@@derivingsexp](** A rewrite modifies an input URI with more specialization towards a
concrete [endp] *)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.hosturiwithNone->""|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>>=functionNone->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->(t.servicescheme>>=function|None->return(`Unknown"unknown scheme")|Someservice->lethost=matchUri.hosturiwithNone->"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))elsereturnendp)end