123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108(*{{{ Copyright (c) 2012-2013 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.Stdtypet={encoding:Transfer.encoding;headers:Header.t;version:Code.version;status:Code.status_code;flush:bool;}[@@derivingfields,sexp]letmake?(version=`HTTP_1_1)?(status=`OK)?(flush=false)?(encoding=Transfer.Chunked)?headers()=letheaders=matchheaderswithNone->Header.init()|Someh->hin{encoding;headers;version;flush;status}letpp_humppfr=Format.fprintfppf"%s"(r|>sexp_of_t|>Sexplib.Sexp.to_string_hum)typett=tmoduleMake(IO:S.IO)=structtypet=ttmoduleIO=IOmoduleHeader_IO=Header_io.Make(IO)moduleTransfer_IO=Transfer_io.Make(IO)typereader=Transfer_IO.readertypewriter=Transfer_IO.writeropenIOletparse_response_fst_lineic=letopenCodeinread_lineic>>=function|Someresponse_line->beginmatchStringext.splitresponse_line~on:' 'with|version_raw::code_raw::_->beginmatchversion_of_stringversion_rawwith|`HTTP_1_0|`HTTP_1_1asv->return(`Ok(v,(status_of_code(int_of_stringcode_raw))))|`Other_->return(`Invalid("Malformed response version: "^version_raw))end|_->return(`Invalid("Malformed response first line: "^response_line))end|None->return`Eofletreadic=parse_response_fst_lineic>>=function|`Eof->return`Eof|`Invalidreasonasr->returnr|`Ok(version,status)->Header_IO.parseic>>=funheaders->letencoding=Header.get_transfer_encodingheadersinletflush=falseinreturn(`Ok{encoding;headers;version;status;flush})letallowed_bodyresponse=(* rfc7230#section-5.7.1 *)matchstatusresponsewith|#Code.informational_status|`No_content|`Not_modified->false|#Code.status_code->truelethas_bodyresponse=ifallowed_bodyresponsethenTransfer.has_body(encodingresponse)else`Noletmake_body_reader{encoding;_}ic=Transfer_IO.make_readerencodingicletread_body_chunk=Transfer_IO.readletwrite_headerresoc=writeoc(Printf.sprintf"%s %s\r\n"(Code.string_of_versionres.version)(Code.string_of_statusres.status))>>=fun()->letheaders=ifallowed_bodyresthenHeader.add_transfer_encodingres.headersres.encodingelseres.headersinHeader_IO.writeheadersocletmake_body_writer?flush{encoding;_}oc=Transfer_IO.make_writer?flushencodingocletwrite_body=Transfer_IO.writeletwrite_footer{encoding;_}oc=matchencodingwith|Transfer.Chunked->(* TODO Trailer header support *)IO.writeoc"0\r\n\r\n"|Transfer.Fixed_|Transfer.Unknown->return()letwrite?flushfnreqoc=write_headerreqoc>>=fun()->letwriter=make_body_writer?flushreqocinfnwriter>>=fun()->write_footerreqocend