(* $Id: webdav_client_ha.ml 9 2015-01-12 20:00:52Z gerd $ *)
open Webdav_client
open Webdav_compat
exception Service_unavailable of string
let configure_pipeline p host ip tmo retry =
let opts = p # get_options in
let opts' =
{ opts with
Http_client.maximum_connection_failures = retry;
maximum_message_errors = retry;
connection_timeout = tmo;
resolver = (fun _esys h p ->
if h=host then
p (Some ip)
else
opts.Http_client.resolver _esys h p);
} in
p # set_options opts';
()
let pre_resolve base_urls =
(* Returns list (base_url, host, ip) for the input list *)
List.flatten
(List.map
(fun u ->
try
let h =
Neturl.url_host
(Neturl.parse_url
~base_syntax:Neturl.ip_url_syntax
~accept_8bits:true
(Neturl.fixup_url_string
u)) in
let ip_opt =
try Some(Unix.inet_addr_of_string h)
with _ -> None in
match ip_opt with
| Some ip ->
[ u, h, ip ]
| None ->
let he = Unix.gethostbyname h in
Array.to_list
(Array.map
(fun ip -> ( u, h, ip ))
he.Unix.h_addr_list
)
with
| _ -> []
)
base_urls)
let select_endpoint base_urls tmo =
let l = pre_resolve base_urls in
if l=[] then
raise(Service_unavailable "Hostname resolution error");
let esys = Unixqueue.create_unix_event_system() in
let r = List.map (fun _ -> ref None) l in
let t0 = Unix.gettimeofday() in
List.iter2
(fun (u, h, ip) d_opt ->
let p = new Http_client.pipeline in
configure_pipeline p h ip tmo 0;
p # set_event_system esys;
let m = new Http_client.options u in
p # add_with_callback
m
(fun _ ->
let t1 = Unix.gettimeofday() in
let ok =
match m#status with
| `Successful | `Redirection | `Client_error -> true
| _ -> false in
if ok then
d_opt := Some (t1 -. t0)
)
)
l
r;
Unixqueue.run esys;
let best = ref None in
List.iter2
(fun (u, h, ip) d_opt ->
match !d_opt with
| None -> ()
| Some d ->
( match !best with
| None ->
best := Some(u, h, ip, d)
| Some (_,_,_,d1) ->
if d < 2. *. d1 then
best := Some(u, h, ip, d)
)
)
l
r;
match !best with
| None ->
raise(Service_unavailable "No endpoint ready for service")
| Some(u,h,ip,_) ->
(u, ip)
let webdav_client ?pipeline (url, ip) =
let wc = new Webdav_client.webdav_client ?pipeline url in
let h =
Neturl.url_host
(Neturl.parse_url
~base_syntax:Neturl.ip_url_syntax
~accept_8bits:true
(Neturl.fixup_url_string
url)) in
configure_pipeline wc#pipeline h ip 300.0 2;
wc