123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146(*{{{ 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.
*
}}}*)openSexplib0.Sexp_convtypet=Http.Response.t={encoding:Transfer.encoding;headers:Header.t;version:Code.version;status:Code.status_code;flush:bool;}[@@derivingsexp]letcompare{headers;flush;version;encoding;status}y=matchHeader.compareheadersy.headerswith|0->(matchBool.compareflushy.flushwith|0->(matchStdlib.comparestatusy.statuswith|0->(matchCode.compare_versionversiony.versionwith|0->Stdlib.compareencodingy.encoding|i->i)|i->i)|i->i)|i->iletheaderst=t.headersletencodingt=t.encodingletversiont=t.versionletstatust=t.statusletflusht=t.flushletmake?(version=`HTTP_1_1)?(status=`OK)?(flush=false)?(encoding=Transfer.Chunked)?(headers=Header.init())()=letencoding=matchHeader.get_transfer_encodingheaderswith|Transfer.Unknown->encoding|encoding->encodingin{encoding;headers;version;flush;status}letpp_humppfr=Format.fprintfppf"%s"(r|>sexp_of_t|>Sexplib0.Sexp.to_string_hum)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`Notypett=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->(matchString.split_on_char' 'response_linewith|version_raw::code_raw::_->(matchversion_of_stringversion_rawwith|(`HTTP_1_0|`HTTP_1_1)asv->return(`Ok(v,status_of_code(int_of_stringcode_raw)))|`Other_->return(`Invalid("Malformed response version: "^version_raw)))|_->return(`Invalid("Malformed response first line: "^response_line)))|None->return`Eofletreadic=parse_response_fst_lineic>>=function|`Eof->return`Eof|`Invalid_reasonasr->returnr|`Ok(version,status)->Header_IO.parseic>>=funheaders->letencoding=Header.get_transfer_encodingheadersinletflush=falseinreturn(`Ok{encoding;headers;version;status;flush})letmake_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_footerreqocendmodulePrivate=structmoduleMake=Makeend