123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587(*----------------------------------------------------------------------------
* Copyright (c) 2017 Inhabited Type LLC.
* Copyright (c) 2019 Antonio N. Monteiro.
*
* 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.
*---------------------------------------------------------------------------*)openFaradaytypeframe_info={flags:Flags.t;stream_id:Stream_identifier.t;padding:Bigstringaf.toption;max_frame_payload:int}letwrite_uint24tn=letwrite_octetto=write_uint8t(oland0xff)inwrite_octett(nlsr16);write_octett(nlsr8);write_octettnletwrite_frame_headertframe_header=let{Frame.payload_length;flags;stream_id;frame_type}=frame_headerinwrite_uint24tpayload_length;write_uint8t(Frame.FrameType.serializeframe_type);write_uint8tflags;BE.write_uint32tstream_idletwrite_frame_with_paddingtinfoframe_typelengthwriter=letheader,writer=matchinfo.paddingwith|None->letheader={Frame.payload_length=length;flags=info.flags;stream_id=info.stream_id;frame_type}inheader,writer|Somepadding->letpad_length=Bigstringaf.lengthpaddinginletwriter't=write_uint8tpad_length;writert;schedule_bigstring~off:0~len:pad_lengthtpaddinginletheader={Frame.payload_length=length+pad_length+1;flags=Flags.set_paddedinfo.flags;stream_id=info.stream_id;frame_type}inheader,writer'inwrite_frame_headertheader;writertletwrite_data_framet?off?leninfobody=letwritert=write_stringt?off?lenbodyinletlength=matchlenwithSomelen->len|None->String.lengthbodyinwrite_frame_with_paddingtinfoDatalengthwriterletschedule_data_frametinfo?off?lenbstr=letwritert=schedule_bigstringt?off?lenbstrinletlength=matchlenwithSomelen->len|None->Bigstringaf.lengthbstrinwrite_frame_with_paddingtinfoDatalengthwriterletwrite_priorityt{Priority.exclusive;stream_dependency;weight}=letstream_dependency_id=ifexclusivethenPriority.set_exclusivestream_dependencyelsestream_dependencyinBE.write_uint32tstream_dependency_id;(* From RFC7540§6.3:
* An unsigned 8-bit integer representing a priority weight for the stream
* (see Section 5.3). Add one to the value to obtain a weight between 1 and
* 256.
*
* Note: we store priority with values from 1 to 256, so decrement here. *)write_uint8t(weight-1)letbounded_schedule_iovecst~leniovecs=letreclooptremainingiovecs=matchremaining,iovecswith|0,_|_,[]->()|remaining,{Httpaf.IOVec.buffer;off;len}::xs->ifremaining<lenthenschedule_bigstringt~off~len:remainingbufferelse(schedule_bigstringt~off~lenbuffer;loopt(remaining-len)xs)inlooptleniovecsletwrite_headers_frametinfo?priority?leniovecs=letlen=matchlenwithSomelen->len|None->Httpaf.IOVec.lengthviovecsinmatchprioritywith|None->(* See RFC7540§6.3:
* Just the Header Block Fragment length if no priority. *)letwritert=bounded_schedule_iovecst~leniovecsinwrite_frame_with_paddingtinfoHeaderslenwriter|Somepriority->(* See RFC7540§6.2:
* Exclusive Bit & Stream Dependency (4 octets) + Weight (1 octet) +
* Header Block Fragment length. *)letpayload_length=len+5inletinfo'={infowithflags=Flags.set_priorityinfo.flags}inletwritert=write_prioritytpriority;bounded_schedule_iovecst~leniovecsinwrite_frame_with_paddingtinfo'Headerspayload_lengthwriterletwrite_priority_frametinfopriority=letheader={Frame.flags=info.flags;stream_id=info.stream_id(* See RFC7540§6.3:
* Stream Dependency (4 octets) + Weight (1 octet). *);payload_length=5;frame_type=Priority}inwrite_frame_headertheader;write_prioritytpriorityletwrite_rst_stream_frametinfoe=letheader={Frame.flags=info.flags;stream_id=info.stream_id(* From RFC7540§6.4:
* The RST_STREAM frame contains a single unsigned, 32-bit integer
* identifying the error code (Section 7). *);payload_length=4;frame_type=RSTStream}inwrite_frame_headertheader;BE.write_uint32t(Error.serializee)letwrite_settings_frametinfosettings=letrecwrite_settings_payload=function|[]->()|(key,value)::xs->(* From RFC7540§6.5.1:
* The payload of a SETTINGS frame consists of zero or more parameters,
* each consisting of an unsigned 16-bit setting identifier and an
* unsigned 32-bit value. *)BE.write_uint16t(Settings.serialize_keykey);BE.write_uint32t(Int32.of_intvalue);write_settings_payloadxsinletheader={Frame.flags=info.flags;stream_id=info.stream_id(* From RFC7540§6.5.1:
* The payload of a SETTINGS frame consists of zero or more
* parameters, each consisting of an unsigned 16-bit setting
* identifier and an unsigned 32-bit value. *);payload_length=List.lengthsettings*6;frame_type=Settings}inwrite_frame_headertheader;write_settings_payloadsettingsletwrite_push_promise_frametinfo~promised_id?leniovecs=letlen=matchlenwithSomelen->len|None->Httpaf.IOVec.lengthviovecsinletpayload_length=(* From RFC7540§6.6:
* The PUSH_PROMISE frame includes the unsigned 31-bit identifier of the
* stream the endpoint plans to create along with a set of headers that
* provide additional context for the stream. *)4+leninletwritert=BE.write_uint32tpromised_id;bounded_schedule_iovecst~leniovecsinwrite_frame_with_paddingtinfoPushPromisepayload_lengthwriterletdefault_ping_payload=(* From RFC7540§6.7:
* In addition to the frame header, PING frames MUST contain 8 octets of
* opaque data in the payload. *)letbstr=Bigstringaf.create8infori=0to7doBigstringaf.setbstri'\000'done;bstrletwrite_ping_frametinfo?(off=0)payload=(* From RFC7540§6.7:
* In addition to the frame header, PING frames MUST contain 8 octets of
* opaque data in the payload. *)letpayload_length=8inletheader={Frame.flags=info.flags;stream_id=info.stream_id;payload_length;frame_type=Ping}inwrite_frame_headertheader;schedule_bigstring~off~len:payload_lengthtpayloadletwrite_go_away_frametinfostream_iderror_codedebug_data=letdebug_data_len=Bigstringaf.lengthdebug_datainletheader={Frame.flags=info.flags;stream_id=info.stream_id(* See RFC7540§6.8:
* Last-Stream-ID (4 octets) + Error Code (4 octets) + Additional
* Debug Data (opaque) *);payload_length=8+debug_data_len;frame_type=GoAway}inwrite_frame_headertheader;BE.write_uint32tstream_id;BE.write_uint32t(Error.serializeerror_code);schedule_bigstringt~off:0~len:debug_data_lendebug_dataletwrite_window_update_frametinfowindow_size=letheader={Frame.flags=info.flags;stream_id=info.stream_id(* From RFC7540§6.9:
* The payload of a WINDOW_UPDATE frame is one reserved bit plus an
* unsigned 31-bit integer indicating the number of octets that the
* sender can transmit in addition to the existing flow-control
* window. *);payload_length=4;frame_type=WindowUpdate}inwrite_frame_headertheader;BE.write_uint32t(Int32.of_intwindow_size)letwrite_continuation_frametinfo?leniovecs=letlen=matchlenwithSomelen->len|None->Httpaf.IOVec.lengthviovecsinletheader={Frame.flags=info.flags;stream_id=info.stream_id;payload_length=len;frame_type=Continuation}inwrite_frame_headertheader;bounded_schedule_iovecst~leniovecsletwrite_connection_prefacet=(* From RFC7540§3.5:
* In HTTP/2, each endpoint is required to send a connection preface as a
* final confirmation of the protocol in use and to establish the initial
* settings for the HTTP/2 connection. [...] The client connection preface
* starts with a sequence of 24 octets, [...] the string
* PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n. *)write_stringt"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"moduleWriter=structtypet={buffer:Bigstringaf.t(* The buffer that the encoder uses for buffered writes. Managed by
* the control module for the encoder. *);encoder:Faraday.t(* The encoder that handles encoding for writes. Uses the [buffer]
* referenced above internally. *);mutabledrained_bytes:int(* The number of bytes that were not written due to the output stream
* being closed before all buffered output could be written. Useful
* for detecting error cases. *);headers_block_buffer:Bigstringaf.t}letcreatebuffer_size=letbuffer=Bigstringaf.createbuffer_sizeinletencoder=Faraday.of_bigstringbufferin{buffer;encoder;drained_bytes=0;headers_block_buffer=Bigstringaf.create0x1000}letfaradayt=t.encoderletmake_frame_info?padding?(flags=Flags.default_flags)?(max_frame_size=Config.default.read_buffer_size)stream_id={flags;stream_id;padding;max_frame_payload=max_frame_size}letwrite_connection_prefacetsettings_list=write_connection_prefacet.encoder;letframe_info=make_frame_infoStream_identifier.connectionin(* From RFC7540§3.5:
* This sequence MUST be followed by a SETTINGS frame (Section 6.5),
* which MAY be empty. *)write_settings_framet.encoderframe_infosettings_listletchunk_data_frames?(off=0)~fframe_infototal_length=let{max_frame_payload;_}=frame_infoinifmax_frame_payload<total_lengththenletrecloop~offremaining=ifmax_frame_payload<remainingthen((* Note: If we're splitting data into several frames, only the last
* one should contain the END_STREAM flag, so unset it here if it's
* set. *)letframe_info={frame_infowithflags=Flags.clear_end_streamframe_info.flags}inf~off~len:max_frame_payloadframe_info;loop~off:(off+max_frame_payload)(remaining-max_frame_payload))elsef~off~len:remainingframe_infoinloop~offtotal_lengthelsef~off~len:total_lengthframe_infoletwrite_datatframe_info?off?lenstr=lettotal_length=matchlenwithSomelen->len|None->String.lengthstrinchunk_data_framesframe_info?offtotal_length~f:(fun~off~lenframe_info->write_data_framet.encoderframe_info~off~lenstr)letschedule_datatframe_info?off?lenbstr=lettotal_length=matchlenwithSomelen->len|None->Bigstringaf.lengthbstrinchunk_data_framesframe_info?offtotal_length~f:(fun~off~lenframe_info->schedule_data_framet.encoderframe_info~off~lenbstr)(* Chunk header block fragments into HEADERS|PUSH_PROMISE + CONTINUATION
* frames. *)letchunk_header_block_fragmentstframe_info?(has_priority=false)~(write_frame:Faraday.t->frame_info->?len:int->Bigstringaf.tioveclist->unit)faraday=letblock_size=Faraday.pending_bytesfaradayinlettotal_length=ifhas_prioritythen(* See RFC7540§6.2: Exclusive Bit & Stream Dependency (4 octets) +
Weight (1 octet) + Header Block Fragment length. *)block_size+5elseblock_sizeinlet{max_frame_payload;_}=frame_infoinifmax_frame_payload<total_lengththen(letheaders_block_len=ifhas_prioritythenmax_frame_payload-5elsemax_frame_payloadinignore(Faraday.serializefaraday(funiovecs->write_framet.encoderframe_info~len:headers_block_leniovecs;`Okheaders_block_len));letrecloopremaining=ifmax_frame_payload<remainingthen((* Note: Don't reuse flags from frame info as CONTINUATION frames
* only define END_HEADERS.
*
* From RFC7540§6.10:
* The CONTINUATION frame defines the following flag:
*
* END_HEADERS (0x4): When set, bit 2 indicates that this frame
* ends a header block (Section 4.3). *)letframe_info={frame_infowithflags=Flags.default_flags}inignore(Faraday.serializefaraday(funiovecs->write_continuation_framet.encoderframe_info~len:max_frame_payloadiovecs;`Okmax_frame_payload));loop(remaining-max_frame_payload))elseletframe_info={frame_infowithflags=Flags.(set_end_headerdefault_flags)}inignore(Faraday.serializefaraday(funiovecs->write_continuation_framet.encoderframe_info~len:remainingiovecs;`Okremaining))inloop(block_size-headers_block_len))elseletframe_info={frame_infowithflags=Flags.set_end_headerframe_info.flags}inignore(Faraday.serializefaraday(funiovecs->letlen=Httpaf.IOVec.lengthviovecsinwrite_framet.encoderframe_info~leniovecs;`Oklen))letencode_headershpack_encoderfaradayheaders=List.iter(funheader->Hpack.Encoder.encode_headerhpack_encoderfaradayheader)(Headers.to_hpack_listheaders)letwrite_request_like_framethpack_encoder~write_frameframe_inforequest=let{Request.meth;target;scheme;headers}=requestinletfaraday=Faraday.of_bigstringt.headers_block_bufferinHpack.Encoder.encode_headerhpack_encoderfaraday{Headers.name=":method";value=Httpaf.Method.to_stringmeth;sensitive=false};ifmeth<>`CONNECTthen((* From RFC7540§8.3:
* The :scheme and :path pseudo-header fields MUST be omitted. *)Hpack.Encoder.encode_headerhpack_encoderfaraday{Headers.name=":path";value=target;sensitive=false};Hpack.Encoder.encode_headerhpack_encoderfaraday{Headers.name=":scheme";value=scheme;sensitive=false});encode_headershpack_encoderfaradayheaders;chunk_header_block_fragmentstframe_info~write_framefaradayletwrite_request_headersthpack_encoder?priorityframe_inforequest=letwrite_frame=write_headers_frame?priorityinwrite_request_like_framethpack_encoder~write_frameframe_inforequestletwrite_push_promisethpack_encoderframe_info~promised_idrequest=letwrite_frame=write_push_promise_frame~promised_idinwrite_request_like_framethpack_encoder~write_frameframe_inforequestletwrite_response_headersthpack_encoder?priorityframe_inforesponse=let{Response.status;headers;_}=responseinletfaraday=Faraday.of_bigstringt.headers_block_bufferin(* From RFC7540§8.1.2.4:
* For HTTP/2 responses, a single :status pseudo-header field is defined
* that carries the HTTP status code field (see [RFC7231], Section 6).
* This pseudo-header field MUST be included in all responses; otherwise,
* the response is malformed (Section 8.1.2.6). *)Hpack.Encoder.encode_headerhpack_encoderfaraday{Headers.name=":status";value=Status.to_stringstatus;sensitive=false};encode_headershpack_encoderfaradayheaders;lethas_priority=matchprioritywithSome_->true|None->falseinchunk_header_block_fragmentstframe_info~write_frame:(write_headers_frame?priority)~has_priorityfaradayletwrite_rst_streamtframe_infoe=write_rst_stream_framet.encoderframe_infoeletwrite_window_updatetframe_infon=write_window_update_framet.encoderframe_infonletschedule_iovecst~lenframe_infoiovecs=letwritert~iovecs=bounded_schedule_iovecst~leniovecsinchunk_data_framesframe_infolen~f:(fun~off~lenframe_info->write_frame_with_paddingt.encoderframe_infoDatalen(writer~iovecs:(Httpaf.IOVec.shiftviovecsoff)))letwrite_prioritytframe_infopriority=write_priority_framet.encoderframe_infopriorityletwrite_settingstframe_infosettings=write_settings_framet.encoderframe_infosettingsletwrite_pingtframe_info?offpayload=write_ping_framet.encoderframe_info?offpayloadletwrite_go_awaytframe_info~debug_data~last_stream_iderror=write_go_away_framet.encoderframe_infolast_stream_iderrordebug_dataletflushtf=flusht.encoderfletyieldt=Faraday.yieldt.encoderletcloset=Faraday.closet.encoderletclose_and_draint=Faraday.closet.encoder;letdrained=Faraday.draint.encoderint.drained_bytes<-t.drained_bytes+drainedletis_closedt=Faraday.is_closedt.encoderletdrained_bytest=t.drained_bytesletreport_resulttresult=matchresultwith`Closed->closet|`Oklen->shiftt.encoderlenletnextt=matchFaraday.operationt.encoderwith|`Close->`Close(drained_bytest)|`Yield->`Yield|`Writeviovecs->`Writeiovecsend