123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035(*----------------------------------------------------------------------------
Copyright (c) 2015 Inhabited Type LLC.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the author nor the names of his contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------*)openCohttpmoduletypeIO=sigtype+'atval(>>=):'at->('a->'bt)->'btvalreturn:'a->'atendmoduleRd=RdmoduletypeS=sigtype+'aiotype'aresult=|Okof'a|Errorofinttype('a,'body)op='bodyRd.t->('aresult*'bodyRd.t)iotype'bodyprovider=('body,'body)optype'bodyacceptor=(bool,'body)optypewww_authenticate={scheme:string;realm:string;params:(string*string)list}typeauth=[`Authorized|`Basicofstring|`Challengeofwww_authenticate|`RedirectofUri.t]typeproperty_response=[`Ok|`Infinite_not_allowed|`Property_forbidden|`Property_unauthorized|`Property_not_found|`Multistatus]typereport_response=[`Multistatus|`Property_not_found]typecollection_created_response=[`Created|`Forbidden|`Method_not_allowed|`Conflict|`Unsupported_media_type|`Insufficient_storage]valcontinue:'a->('a,'body)opvalrespond:?body:'body->int->('a,'body)opclassvirtual['body]resource:objectconstraint'body=[>`Empty]methodvirtualcontent_types_provided:((string*('bodyprovider))list,'body)opmethodvirtualcontent_types_accepted:((string*('bodyacceptor))list,'body)opmethodresource_exists:(bool,'body)opmethodservice_available:(bool,'body)opmethodis_authorized:(auth,'body)opmethodforbidden:(bool,'body)opmethodmalformed_request:(bool,'body)opmethoduri_too_long:(bool,'body)opmethodknown_content_type:(bool,'body)opmethodvalid_content_headers:(bool,'body)opmethodvalid_entity_length:(bool,'body)opmethodoptions:((string*string)list,'body)opmethodallowed_methods:(Code.methlist,'body)opmethodknown_methods:(Code.methlist,'body)opmethoddelete_resource:(bool,'body)opmethoddelete_completed:(bool,'body)opmethodprocess_post:(bool,'body)opmethodprocess_property:(property_response,'body)opmethodreport:(report_response,'body)opmethodcannot_create:(unit,'body)opmethodcreate_collection:(collection_created_response,'body)opmethodlanguage_available:(bool,'body)opmethodcharsets_provided:((string*('body->'body))list,'body)opmethodencodings_provided:((string*('body->'body))list,'body)opmethodvariances:(stringlist,'body)opmethodis_conflict:(bool,'body)opmethodmultiple_choices:(bool,'body)opmethodpreviously_existed:(bool,'body)opmethodmoved_permanently:(Uri.toption,'body)opmethodmoved_temporarily:(Uri.toption,'body)opmethodlast_modified:(stringoption,'body)opmethodexpires:(stringoption,'body)opmethodgenerate_etag:(stringoption,'body)opmethodfinish_request:(unit,'body)opmethodpost_is_create:(bool,'body)opmethodcreate_path:(string,'body)opmethodallow_missing_post:(bool,'body)opendvalto_handler:?dispatch_path:string->?path_info:(string*string)list->resource:('bodyresource)->body:'body->request:Request.t->unit->(Code.status_code*Header.t*'body*stringlist)iovaldispatch:((Dispatch.tag*string)list*Dispatch.typ*(unit->'bodyresource))list->body:'body->request:Request.t->(Code.status_code*Header.t*'body*stringlist)optioniovaldispatch':(string*(unit->'bodyresource))list->body:'body->request:Request.t->(Code.status_code*Header.t*'body*stringlist)optionioendletdefault_variances=["Accept";"Accept-Encoding";"Accept-Charset";"Accept-Language"]moduletypeCLOCK=sigvalnow:unit->intendmoduleMake(IO:IO)(Clock:CLOCK)=structtype+'aio='aIO.topenIOtype'aresult=|Okof'a|Errorofinttype('a,'body)op='bodyRd.t->('aresult*'bodyRd.t)iotype'bodyprovider=('body,'body)optype'bodyacceptor=(bool,'body)optypewww_authenticate={scheme:string;realm:string;params:(string*string)list}typeauth=[`Authorized|`Basicofstring|`Challengeofwww_authenticate|`RedirectofUri.t]typeproperty_response=[`Ok|`Infinite_not_allowed|`Property_forbidden|`Property_unauthorized|`Property_not_found|`Multistatus]typereport_response=[`Multistatus|`Property_not_found]typecollection_created_response=[`Created|`Forbidden|`Method_not_allowed|`Conflict|`Unsupported_media_type|`Insufficient_storage]let(>>=?)mf=m>>=function|Okx,rd->fxrd|Errorcode,rd->return(Errorcode,rd)letcontinuexrd=return(Okx,rd)letrespond?bodyxrd=letrd=matchbodywith|None->rd|Someresp_body->{rdwithRd.resp_body}inreturn(Errorx,rd)classvirtual['body]resource=object(self)constraint'body=[>`Empty]methodvirtualcontent_types_provided:((string*('bodyprovider))list,'body)opmethodvirtualcontent_types_accepted:((string*('bodyacceptor))list,'body)opmethodresource_exists(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodservice_available(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodis_authorized(rd:'bodyRd.t):(authresult*'bodyRd.t)IO.t=continue`Authorizedrdmethodforbidden(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodmalformed_request(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethoduri_too_long(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodknown_content_type(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodvalid_content_headers(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodvalid_entity_length(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodoptions(rd:'bodyRd.t):((string*string)listresult*'bodyRd.t)IO.t=self#allowed_methodsrd>>=?funmethsrd->continue["allow",String.concat","(List.mapCode.string_of_methodmeths)]rdmethodallowed_methods(rd:'bodyRd.t):(Code.methlistresult*'bodyRd.t)IO.t=continue[`GET;`HEAD]rdmethodknown_methods(rd:'bodyRd.t):(Code.methlistresult*'bodyRd.t)IO.t=continue[`GET;`HEAD;`POST;`PUT;`DELETE;`Other"TRACE";`Other"CONNECT";`OPTIONS]rdmethoddelete_resource(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethoddelete_completed(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodprocess_post(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodprocess_property(rd:'bodyRd.t):(property_responseresult*'bodyRd.t)IO.t=continue`Okrdmethodreport(rd:'bodyRd.t):(report_responseresult*'bodyRd.t)IO.t=continue`Property_not_foundrdmethodcannot_create(rd:'bodyRd.t):(unitresult*'bodyRd.t)IO.t=continue()rdmethodcreate_collection(rd:'bodyRd.t):(collection_created_responseresult*'bodyRd.t)IO.t=continue`Method_not_allowedrdmethodlanguage_available(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuetruerdmethodcharsets_provided(rd:'bodyRd.t):((string*('body->'body))listresult*'bodyRd.t)IO.t=continue[]rdmethodencodings_provided(rd:'bodyRd.t):((string*('body->'body))listresult*'bodyRd.t)IO.t=continue["identity",funx->x]rdmethodvariances(rd:'bodyRd.t):(stringlistresult*'bodyRd.t)IO.t=continue[]rdmethodis_conflict(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodmultiple_choices(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodpreviously_existed(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodmoved_permanently(rd:'bodyRd.t):(Uri.toptionresult*'bodyRd.t)IO.t=continueNonerdmethodmoved_temporarily(rd:'bodyRd.t):(Uri.toptionresult*'bodyRd.t)IO.t=continueNonerdmethodlast_modified(rd:'bodyRd.t):(stringoptionresult*'bodyRd.t)IO.t=continueNonerdmethodexpires(rd:'bodyRd.t):(stringoptionresult*'bodyRd.t)IO.t=continueNonerdmethodgenerate_etag(rd:'bodyRd.t):(stringoptionresult*'bodyRd.t)IO.t=continueNonerdmethodfinish_request(rd:'bodyRd.t):(unitresult*'bodyRd.t)IO.t=continue()rdmethodpost_is_create(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdmethodcreate_path(rd:'bodyRd.t):(stringresult*'bodyRd.t)IO.t=continue""rdmethodallow_missing_post(rd:'bodyRd.t):(boolresult*'bodyRd.t)IO.t=continuefalserdendlet(>>~)mf=mfclass['body]logic~(resource:'bodyresource)~(rd:'bodyRd.t)()=object(self)constraint'body=[>`Empty]valmutablepath=([]:stringlist)valmutablerd=rdvalmutablecontent_type=Nonevalmutablecharset=Nonevalmutableencoding=Nonemethodprivateencode_body=letcf=matchcharsetwith|None->funx->x|Some(_,f)->finletef=matchencodingwith|None->funx->x|Some(_,f)->finrd<-{rdwithRd.resp_body=ef(cfrd.Rd.resp_body)}(** [#meth] returns the [Code.meth] of the [Request.t] object. *)methodprivatemeth=rd.Rd.methmethodprivateuri=rd.Rd.urimethodprivateset_response_headerkv=rd<-Rd.with_resp_headers(funheaders->Header.replaceheaderskv)rdmethodprivateget_request_headerk=Header.getrd.Rd.req_headerskmethodprivateget_response_headerk=Header.getrd.Rd.resp_headerskmethodprivateis_redirect=rd.Rd.resp_redirectmethodprivaterespond~status():(Code.status_code*Header.t*'body)IO.t=self#run_opresource#finish_request>>~fun()->return(status,rd.Rd.resp_headers,rd.Rd.resp_body)methodprivatehaltcode:(Code.status_code*Header.t*'body)IO.t=letstatus=Code.status_of_codecodeinself#respond~status()methodprivatechoose_charsetacceptablek=(* XXX(seliopou): This breaks the {run_op} so watch out in the even that
* this, or {run_op} must change behavior in order to keep them
* consistent. *)resource#charsets_providedrd>>=function|Ok[],rd'->rd<-rd';k`Any|Okavailable,rd'->rd<-rd';charset<-Encoding.choose_charset~available~acceptable;k(`Onecharset)|Errorn,rd'->rd<-rd';self#haltnmethodprivatechoose_encodingacceptablek=resource#encodings_providedrd>>=function|Okavailable,rd'->rd<-rd';encoding<-Encoding.choose~available~acceptable;kencoding|Errorn,rd'->rd<-rd';self#haltn(** [run_op op] runs [op] with the current request and response
information, and will perform any appropriate bookkeeping that needs to
be done given the result. *)methodprivaterun_op:'a.('a,'body)op->('a->(Code.status_code*Header.t*'body)IO.t)->(Code.status_code*Header.t*'body)IO.t=funopk->oprd>>=function|Oka,rd'->rd<-rd';ka|Errorn,rd'->rd<-rd';self#haltnmethodprivaterun_provider:'bodyprovider->_->(Code.status_code*Header.t*'body)IO.t=funproviderk->providerrd>>=function|Okresp_body,rd'->rd<-{rd'withRd.resp_body};k()|Errorn,rd'->rd<-rd';self#haltnmethodprivateaccept_helperk=letheader=matchself#get_request_header"content-type"with|None->Some"application/octet-stream"|Sometype_->Sometype_inself#run_opresource#content_types_accepted>>~funprovided->matchMediatype.match_headerprovidedheaderwith|None->self#halt415|Some(_,of_content)->self#run_opof_content>>~functioncomplete->ifcompletethenself#encode_body;kcompletemethodprivatedstate=path<-state::pathmethodrun:(Code.status_code*Header.t*'body*stringlist)IO.t=self#v3b13>>=fun(code,headers,body)->return(code,headers,body,List.revpath)methodv3b13:(Code.status_code*Header.t*'body)IO.t=self#d"v3b13";self#run_opresource#service_available>>~function|true->self#v3b12|false->self#halt503methodv3b12:(Code.status_code*Header.t*'body)IO.t=self#d"v3b12";letmeth=self#methinself#run_opresource#known_methods>>~fun(meths:Code.methlist)->ifList.exists(funx->Code.compare_methodmethx=0)methsthenself#v3b11elseself#halt501methodv3b11:(Code.status_code*Header.t*'body)IO.t=self#d"v3b11";self#run_opresource#uri_too_long>>~function|true->self#halt414|false->self#v3b10methodv3b10:(Code.status_code*Header.t*'body)IO.t=self#d"v3b10";letmeth=self#methinself#run_opresource#allowed_methods>>~fun(meths:Code.methlist)->ifList.exists(funx->Code.compare_methodmethx=0)methsthenself#v3b9elsebeginletallow=String.concat","(List.mapCode.string_of_methodmeths)inself#set_response_header"allow"allow;self#halt405endmethodv3b9:(Code.status_code*Header.t*'body)IO.t=self#d"v3b9";self#run_opresource#malformed_request>>~function|true->self#halt400|false->self#v3b8methodv3b8:(Code.status_code*Header.t*'body)IO.t=self#d"v3b8";self#run_opresource#is_authorized>>~function|`Authorized->self#v3b7|`Basicrealm->self#set_response_header"WWW-Authenticate"("Basic realm=\""^realm^"\"");self#halt401|`Challengeauth->letchallenge=letbuffer=Buffer.create80inletadd_kv(k,v)=Buffer.add_charbuffer' ';Buffer.add_stringbufferk;Buffer.add_stringbuffer"=\"";Buffer.add_stringbufferv;Buffer.add_stringbuffer"\"";inBuffer.add_stringbufferauth.scheme;add_kv("realm",auth.realm);List.iteradd_kvauth.params;Buffer.contentsbufferinself#set_response_header"WWW-Authenticate"challenge;self#halt401|`Redirecturi->rd<-Rd.redirectUri.(to_stringuri)rd;self#halt303methodv3b7:(Code.status_code*Header.t*'body)IO.t=self#d"v3b7";self#run_opresource#forbidden>>~function|true->self#halt403|false->self#v3b6methodv3b6:(Code.status_code*Header.t*'body)IO.t=self#d"v3b6";self#run_opresource#valid_content_headers>>~function|true->self#v3b5|false->self#halt501methodv3b5:(Code.status_code*Header.t*'body)IO.t=self#d"v3b5";self#run_opresource#known_content_type>>~function|true->self#v3b4|false->self#halt415methodv3b4:(Code.status_code*Header.t*'body)IO.t=self#d"v3b4";self#run_opresource#valid_entity_length>>~function|true->self#v3b3|false->self#halt413methodv3b3:(Code.status_code*Header.t*'body)IO.t=self#d"v3b3";matchself#methwith|`OPTIONS->self#run_opresource#options>>~funheaders->List.iter(fun(k,v)->self#set_response_headerkv)headers;self#respond~status:`OK()|_->self#v3c3methodv3c3:(Code.status_code*Header.t*'body)IO.t=self#d"v3c3";self#run_opresource#content_types_provided>>~funcontent_types->matchself#get_request_header"accept"with|None->beginmatchcontent_typeswith|[]->self#halt500|t::_->content_type<-Somet;self#v3d4end|Some_->self#v3c4methodv3c4:(Code.status_code*Header.t*'body)IO.t=self#d"v3c4";self#run_opresource#content_types_provided>>~funcontent_types->letheader=self#get_request_header"accept"inmatchMediatype.match_headercontent_typesheaderwith|None->self#halt406|Somet->content_type<-Somet;self#v3d4methodv3d4:(Code.status_code*Header.t*'body)IO.t=self#d"v3d4";matchself#get_request_header"accept-language"with|None->self#v3e5|Some_->self#v3d5methodv3d5:(Code.status_code*Header.t*'body)IO.t=self#d"v3d5";self#run_opresource#language_available>>~function|true->self#v3e5|false->self#halt406methodv3e5:(Code.status_code*Header.t*'body)IO.t=self#d"v3e5";matchself#get_request_header"accept-charset"with|None->beginself#choose_charset(Accept.charsetsNone)>>~function|`Any|`One(Some_)->self#v3f6|`OneNone->self#halt406end|Some_->self#v3e6methodv3e6:(Code.status_code*Header.t*'body)IO.t=self#d"v3e6";matchself#get_request_header"accept-charset"with|None->assertfalse|Someacceptable->beginself#choose_charset(Accept.charsets(Someacceptable))>>~function|`Any|`One(Some_)->self#v3f6|`OneNone->self#halt406endmethodv3f6:(Code.status_code*Header.t*'body)IO.t=self#d"v3f6";lettype_=matchcontent_typewith|None->assertfalse|Some(type_,_)->type_inletvalue=matchcharsetwith|None->type_|Some(charset,_)->Printf.sprintf"%s; charset=%s"type_charsetinself#set_response_header"Content-Type"value;matchself#get_request_header"accept-encoding"with|None->letacceptable=Accept.encodings(Some"identity;q=1.0,*;q=0.5")inself#choose_encodingacceptable>>~funchosen->beginmatchchosenwith|None->self#halt406|Some_->self#v3g7end|Some_->self#v3f7methodv3f7:(Code.status_code*Header.t*'body)IO.t=self#d"v3f7";matchself#get_request_header"accept-encoding"with|None->assertfalse|Someacceptable->letacceptable=Accept.encodings(Someacceptable)inself#choose_encodingacceptable>>~funchosen->beginmatchchosenwith|None->self#halt406|Some_->self#v3g7endmethodv3g7:(Code.status_code*Header.t*'body)IO.t=self#d"v3g7";self#run_opresource#variances>>~funvariances->letvariances=variances@default_variancesinbeginmatchString.concat", "varianceswith|""->()|vary->self#set_response_header"vary"varyend;self#run_opresource#resource_exists>>~function|true->self#v3g7b|false->self#v3h7bmethodv3g7b:(Code.status_code*Header.t*'body)IO.t=self#d"v3g7b";matchself#methwith|`Other"PROPFIND"|`Other"PROPPATCH"->beginself#run_opresource#process_property>>~function|`Ok->self#respond~status:`OK()|`Infinite_not_allowed->self#respond~status:(`Code403)()|`Property_forbidden->self#respond~status:(`Code403)()|`Property_unauthorized->self#respond~status:(`Code401)()|`Property_not_found->self#respond~status:(`Code404)()|`Multistatus->self#respond~status:(`Code207)()end|`Other"MKCOL"|`Other"MKCALENDAR"->self#run_opresource#cannot_create>>~fun()->self#respond~status:(`Code403)()|`Other"REPORT"->beginself#run_opresource#report>>~function|`Multistatus->self#respond~status:(`Code207)()|`Property_not_found->self#respond~status:(`Code404)()end|_->self#v3g8methodv3g8:(Code.status_code*Header.t*'body)IO.t=self#d"v3g8";matchself#get_request_header"if-match"with|None->self#v3h10|Some_->self#v3g9methodv3g9:(Code.status_code*Header.t*'body)IO.t=self#d"v3g9";matchself#get_request_header"if-match"with|None->assertfalse|Some"*"->self#v3h10|Some_->self#v3g11methodv3g11:(Code.status_code*Header.t*'body)IO.t=self#d"v3g11";matchself#get_request_header"if-match"with|None->assertfalse|Someif_match_header->self#run_opresource#generate_etag>>~function|None->self#halt412|Someetag->beginmatchList.memetag(Etag.from_headerif_match_header)with|true->self#v3h10|false->self#halt412endmethodv3h7:(Code.status_code*Header.t*'body)IO.t=self#d"v3h7";matchself#get_request_header"if-match"with|None->self#v3i7|Some_->self#halt412methodv3h7b:(Code.status_code*Header.t*'body)IO.t=self#d"v3h7b";matchself#methwith|`Other"MKCOL"|`Other"MKCALENDAR"->beginself#run_opresource#create_collection>>~function|`Created->self#respond~status:`Created()|`Forbidden->self#respond~status:(`Code403)()|`Method_not_allowed->self#respond~status:(`Code405)()|`Conflict->self#respond~status:(`Code409)()|`Unsupported_media_type->self#respond~status:(`Code415)()|`Insufficient_storage->self#respond~status:(`Code507)()end|_->self#v3h7methodv3h10:(Code.status_code*Header.t*'body)IO.t=self#d"v3h10";matchself#get_request_header"if-unmodified-since"with|None->self#v3i12|Some_->self#v3h11methodv3h11:(Code.status_code*Header.t*'body)IO.t=self#d"v3h11";letd=self#get_request_header"if-unmodified-since"inmatchdwith|None->self#v3i12|Somed'->match(Rfc1123.parse_dated')with|None->self#v3i12|Some_->self#v3h12methodv3h12:(Code.status_code*Header.t*'body)IO.t=self#d"v3h12";tryletu_mod=self#get_request_header"if-unmodified-since"inself#run_opresource#last_modified>>~funl_mod->match(u_mod,l_mod)with|(Someu_mod',Somel_mod')->(match(Rfc1123.parse_date_exnl_mod')>(Rfc1123.parse_date_exnu_mod')with|false->self#v3i12|true->self#halt412)|(_,_)->self#v3i12withInvalid_argument_->self#halt412methodv3i4:(Code.status_code*Header.t*'body)IO.t=self#d"v3i4";self#run_opresource#moved_permanently>>~function|None->self#v3p3|Someuri->self#set_response_header"Location"(Uri.to_stringuri);self#respond~status:`Moved_permanently()methodv3i7:(Code.status_code*Header.t*'body)IO.t=self#d"v3i7";matchself#methwith|`OPTIONS->assertfalse|`PUT->self#v3i4|_->self#v3k7methodv3i12:(Code.status_code*Header.t*'body)IO.t=self#d"v3i12";matchself#get_request_header"if-none-match"with|None->self#v3l13|Some_->self#v3i13methodv3i13:(Code.status_code*Header.t*'body)IO.t=self#d"v3i13";matchself#get_request_header"if-none-match"with|None->assertfalse|Some"*"->self#v3j18|Some_->self#v3k13methodv3k7:(Code.status_code*Header.t*'body)IO.t=self#d"v3k7";self#run_opresource#previously_existed>>~function|true->self#v3k5|false->self#v3l7methodv3k5:(Code.status_code*Header.t*'body)IO.t=self#d"v3k5";self#run_opresource#moved_permanently>>~function|None->self#v3l5|Someuri->self#set_response_header"location"(Uri.to_stringuri);self#respond~status:`Moved_permanently()methodv3k13:(Code.status_code*Header.t*'body)IO.t=self#d"v3k13";matchself#get_request_header"if-none-match"with|None->assertfalse|Someif_none_match_header->self#run_opresource#generate_etag>>~function|None->self#v3l13|Someetag->beginmatchList.memetag(Etag.from_headerif_none_match_header)with|true->self#v3j18|false->self#v3l13endmethodv3l5:(Code.status_code*Header.t*'body)IO.t=(* XXX(seliopou): For now, no POSTs to non-existent resources allowed. *)self#d"v3l5";self#run_opresource#moved_temporarily>>~function|None->self#v3m5|Someuri->self#set_response_header"location"(Uri.to_stringuri);self#respond~status:`Temporary_redirect()methodv3l7:(Code.status_code*Header.t*'body)IO.t=self#d"v3l7";matchself#methwith|`POST->self#v3m7|_->self#halt404methodv3l13:(Code.status_code*Header.t*'body)IO.t=self#d"v3l13";matchself#get_request_header"if-modified-since"with|None->self#v3m16|Some_->self#v3l14methodv3l14:(Code.status_code*Header.t*'body)IO.t=self#d"v3l14";match(self#get_request_header"if-modified-since")with|None->self#v3m16|Somedate->match(Rfc1123.parse_datedate)with|Some_->self#v3l15|None->self#v3m16methodv3l15:(Code.status_code*Header.t*'body)IO.t=self#d"v3l15";letnow=Clock.now()inmatch(self#get_request_header"if-modified-since")with|None->self#v3l17|Somedate->matchRfc1123.parse_datedatewith|None->self#v3l17|Somed->match(d>now)with|true->self#v3m16|false->self#v3l17methodv3l17:(Code.status_code*Header.t*'body)IO.t=self#d"v3l17";tryletu_mod=self#get_request_header"if-modified-since"inself#run_opresource#last_modified>>~funl_mod->match(u_mod,l_mod)with|(Somel_mod',Someu_mod')->(match(Rfc1123.parse_date_exnl_mod')>(Rfc1123.parse_date_exnu_mod')with|true->self#v3m16|false->self#halt304)|(_,_)->self#halt304withInvalid_argument_->self#halt304methodv3j18:(Code.status_code*Header.t*'body)IO.t=self#d"v3j18";matchself#methwith|`GET|`HEAD->self#halt304|_->self#halt412methodv3m5:(Code.status_code*Header.t*'body)IO.t=self#d"v3m5";matchself#methwith|`POST->self#v3n5|_->self#halt410methodv3m7:(Code.status_code*Header.t*'body)IO.t=self#d"v3m7";self#run_opresource#allow_missing_post>>~function|true->self#v3n11|false->self#halt404methodv3m16:(Code.status_code*Header.t*'body)IO.t=self#d"v3m16";matchself#methwith|`OPTIONS->assertfalse|`DELETE->self#v3m20|_->self#v3n16methodv3m20:(Code.status_code*Header.t*'body)IO.t=self#d"v3m20";self#run_opresource#delete_resource>>~fundeleted->ifdeletedthenself#run_opresource#delete_completed>>~function|true->self#v3o20|false->self#respond~status:`Accepted()elseself#halt500methodv3n5:(Code.status_code*Header.t*'body)IO.t=self#d"v3n5";self#run_opresource#allow_missing_post>>~function|true->self#v3n11|false->self#halt410methodv3n11:(Code.status_code*Header.t*'body)IO.t=letstage2(typea)(_:a)=ifself#is_redirectthenmatchself#get_response_header"location"with|None->self#halt500|Some_->self#respond~status:`See_other()elseself#v3p11inself#d"v3n11";self#run_opresource#post_is_create>>~function|true->self#run_opresource#create_path>>~funnew_resource->(* get full path, based on base uri *)(* set disp path on rd *)leturi'=Uri.with_pathself#uri(Uri.pathself#uri^"/"^new_resource)in(* set location header on rd *)self#set_response_header"Location"(Uri.to_stringuri');self#accept_helperstage2|false->self#run_opresource#process_post>>~funexecuted->ifexecutedthenbeginself#encode_body;stage2()endelseself#halt500methodv3n16:(Code.status_code*Header.t*'body)IO.t=self#d"v3n16";matchself#methwith|`OPTIONS|`DELETE->assertfalse|`POST->self#v3n11|_->self#v3o16methodv3o14:(Code.status_code*Header.t*'body)IO.t=self#d"v3o14";self#run_opresource#is_conflict>>~function|true->self#halt409|false->self#accept_helper(fun_->self#v3p11)methodv3o16:(Code.status_code*Header.t*'body)IO.t=self#d"v3o16";matchself#methwith|`OPTIONS|`DELETE|`POST->assertfalse|`PUT->self#v3o14|_->self#v3o18methodv3o18:(Code.status_code*Header.t*'body)IO.t=self#d"v3o18";matchself#methwith(* The HTTP method could be POST if the request comes via v3o20 *)|`OPTIONS->assertfalse|`HEAD|`GET->let_,to_content=matchcontent_typewith|None->assertfalse|Somex->xinself#run_opresource#generate_etag>>~funetag->beginmatchetagwith|None->()|Someetag->self#set_response_header"ETag"(Etag.escapeetag)end;(* XXX(seliopou) last modified *)(* XXX(seliopou) expires *)self#run_providerto_content>>~fun()->self#encode_body;self#v3o18b|_->self#v3o18bmethodv3o18b:(Code.status_code*Header.t*'body)IO.t=self#run_opresource#multiple_choices>>~function|true->self#halt300|false->self#respond~status:`OK()methodv3o20:(Code.status_code*Header.t*'body)IO.t=self#d"v3o20";matchrd.Rd.resp_bodywith|`Empty->self#respond~status:`No_content()|_->self#v3o18methodv3p3:(Code.status_code*Header.t*'body)IO.t=self#d"v3p3";self#run_opresource#is_conflict>>~function|true->self#halt409|false->self#accept_helper(fun_->self#v3p11)methodv3p11:(Code.status_code*Header.t*'body)IO.t=self#d"v3p11";matchself#get_response_header"location"with|None->self#v3o20|Some_->self#respond~status:`Created()endletto_handler?dispatch_path?path_info~resource~body~request()=letrd=Rd.make~req_body:body?dispatch_path?path_info~request()inletlogic=newlogic~resource~rd()inlogic#run;;letdispatchtable=lettable=Dispatch.create(List.map(fun(p,t,mk_resource)->(p,t,funpath_infodispatch_path~body~request->letresource=mk_resource()into_handler?dispatch_path~path_info~resource~body~request()))table)infun~body~request->letpath=Uri.path(Cohttp.Request.urirequest)inmatchDispatch.dispatchtablepathwith|None->returnNone|Somehandler->handler~body~request>>=funx->return(Somex)letdispatch'table=dispatch(List.map(fun(m,r)->letp,t=Dispatch.of_dslmin(p,t,r))table)end