123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709(*----------------------------------------------------------------------------
* 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.
*---------------------------------------------------------------------------*)openAngstrom(* We use the tail-recursive variant of `skip_many` from
* https://github.com/inhabitedtype/angstrom/pull/219 to avoid memory leaks in
* long-running connections. The original `skip_many` can build up a list of
* error handlers that may never be released. *)letskip_manyp=fix(funm->p>>|(fun_->true)<|>returnfalse>>=function|true->m|false->return())letdefault_frame_header={Frame.payload_length=0;flags=Flags.default_flags;stream_id=-1l;frame_type=Unknown(-1)}typeparse_context={mutableframe_header:Frame.frame_header;mutableremaining_bytes_to_skip:int;mutabledid_report_stream_error:bool;(* TODO: This should change as new settings frames arrive, but we don't yet
* resize the read buffer. *)max_frame_size:int}letconnection_errorerror_codemsg=ErrorError.(ConnectionError(error_code,msg))letstream_errorerror_codestream_id=ErrorError.(StreamError(stream_id,error_code))letparse_uint24o1o2o3=(o1lsl16)lor(o2lsl8)loro3letframe_length=(* From RFC7540§4.1:
* Length: The length of the frame payload expressed as an unsigned 24-bit
* integer. *)lift3parse_uint24any_uint8any_uint8any_uint8letframe_type=(* From RFC7540§4.1:
* Type: The 8-bit type of the frame. The frame type determines the format
* and semantics of the frame. Implementations MUST ignore and discard any
* frame that has a type that is unknown. *)liftFrame.FrameType.parseany_uint8letflags=(* From RFC7540§4.1:
* Flags: An 8-bit field reserved for boolean flags specific to the frame
* type. *)any_uint8letparse_stream_identifiern=(* From RFC7540§4.1:
* Stream Identifier: A stream identifier (see Section 5.1.1) expressed as
* an unsigned 31-bit integer. The value 0x0 is reserved for frames that
* are associated with the connection as a whole as opposed to an
* individual stream. *)Int32.(logandn(sub(shift_left1l31)1l))letstream_identifier=liftparse_stream_identifierBE.any_int32letparse_frame_header=lift4(funpayload_lengthframe_typeflagsstream_id->{Frame.flags;payload_length;stream_id;frame_type})frame_lengthframe_typeflagsstream_identifier<?>"frame_header"(* The parser commits after parsing the frame header so that the entire
* underlying buffer can be used to store the payload length. This matters
* because the size of the buffer that gets allocated is the maximum frame
* payload negotiated by the HTTP/2 settings synchronization. The 9 octets
* that make up the frame header are, therefore, very important in order for
* h2 not to return a FRAME_SIZE_ERROR. *)<*commitletparse_padded_payload{Frame.payload_length;flags;_}parser=ifFlags.test_paddedflagsthenany_uint8>>=funpad_length->(* From RFC7540§6.1:
* Pad Length: An 8-bit field containing the length of the frame
* padding in units of octets.
*
* Data: Application data. The amount of data is the remainder of the
* frame payload after subtracting the length of the other fields that
* are present.
*
* Padding: Padding octets that contain no application semantic
* value. *)ifpad_length>=payload_lengththen(* From RFC7540§6.1:
* If the length of the padding is the length of the frame payload or
* greater, the recipient MUST treat this as a connection error
* (Section 5.4.1) of type PROTOCOL_ERROR. *)advance(payload_length-1)>>|fun()->connection_errorProtocolError"Padding size exceeds payload size"else(* Subtract the octet that contains the length of padding, and the
* padding octets. *)letrelevant_length=payload_length-1-pad_lengthinparserrelevant_length<*advancepad_lengthelseparserpayload_lengthletparse_data_frame({Frame.stream_id;payload_length;_}asframe_header)=ifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.1:
* DATA frames MUST be associated with a stream. If a DATA frame is
* received whose stream identifier field is 0x0, the recipient MUST
* respond with a connection error (Section 5.4.1) of type
* PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"Data frames must be associated with a stream"elseletparse_datalength=lift(funbs->Ok(Frame.Databs))(take_bigstringlength)inparse_padded_payloadframe_headerparse_dataletparse_priority=lift2(funstream_dependencyweight->lete=Priority.test_exclusivestream_dependencyin{Priority.exclusive=e(* 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. *);weight=weight+1;stream_dependency=parse_stream_identifierstream_dependency})BE.any_int32any_uint8letparse_headers_frameframe_header=let{Frame.payload_length;stream_id;flags;_}=frame_headerinifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.2:
* HEADERS frames MUST be associated with a stream. If a HEADERS frame is
* received whose stream identifier field is 0x0, the recipient MUST
* respond with a connection error (Section 5.4.1) of type
* PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"HEADERS must be associated with a stream"elseletparse_headerslength=ifFlags.test_priorityflagsthenlift2(funpriorityheaders->Ok(Frame.Headers(priority,headers)))parse_priority(* See RFC7540§6.3:
* Stream Dependency (4 octets) + Weight (1 octet). *)(take_bigstring(length-5))elselift(funheaders_block->Ok(Frame.Headers(Priority.default_priority,headers_block)))(take_bigstringlength)inparse_padded_payloadframe_headerparse_headersletparse_priority_frame{Frame.payload_length;stream_id;_}=ifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.3:
* The PRIORITY frame always identifies a stream. If a PRIORITY frame is
* received with a stream identifier of 0x0, the recipient MUST respond
* with a connection error (Section 5.4.1) of type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"PRIORITY must be associated with a stream"elseifpayload_length<>5then(* From RFC7540§6.3:
* A PRIORITY frame with a length other than 5 octets MUST be treated as
* a stream error (Section 5.4.2) of type FRAME_SIZE_ERROR. *)advancepayload_length>>|fun()->stream_errorFrameSizeErrorstream_idelselift(funpriority->Ok(Frame.Prioritypriority))parse_priorityletparse_error_code=liftError_code.parseBE.any_int32letparse_rst_stream_frame{Frame.payload_length;stream_id;_}=ifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.4:
* RST_STREAM frames MUST be associated with a stream. If a RST_STREAM
* frame is received with a stream identifier of 0x0, the recipient MUST
* treat this as a connection error (Section 5.4.1) of type
* PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"RST_STREAM must be associated with a stream"elseifpayload_length<>4then(* From RFC7540§6.4:
* A RST_STREAM frame with a length other than 4 octets MUST be treated
* as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. *)advancepayload_length>>|fun()->connection_errorFrameSizeError"RST_STREAM payload must be 4 octets in length"elselift(funerror_code->Ok(Frame.RSTStreamerror_code))parse_error_codeletparse_settings_frame{Frame.payload_length;stream_id;flags;_}=ifnot(Stream_identifier.is_connectionstream_id)then(* From RFC7540§6.5:
* If an endpoint receives a SETTINGS frame whose stream identifier field
* is anything other than 0x0, the endpoint MUST respond with a
* connection error (Section 5.4.1) of type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"SETTINGS must be associated with stream id 0x0"elseifpayload_lengthmod6<>0then(* From RFC7540§6.5:
* A SETTINGS frame with a length other than a multiple of 6 octets MUST
* be treated as a connection error (Section 5.4.1) of type
* FRAME_SIZE_ERROR. *)advancepayload_length>>|fun()->connection_errorFrameSizeError"SETTINGS payload size must be a multiple of 6"elseifFlags.test_ackflags&&payload_length<>0then(* From RFC7540§6.5:
* Receipt of a SETTINGS frame with the ACK flag set and a length field
* value other than 0 MUST be treated as a connection error
* (Section 5.4.1) of type FRAME_SIZE_ERROR. *)advancepayload_length>>|fun()->connection_errorFrameSizeError"SETTINGS with ACK must be empty"elseletnum_settings=payload_length/Settings.octets_per_settinginSettings.parse_settings_payloadnum_settings>>|funxs->Ok(Frame.Settingsxs)letparse_push_promise_frameframe_header=let{Frame.payload_length;stream_id;_}=frame_headerinifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.6:
* The stream identifier of a PUSH_PROMISE frame indicates the
* stream it is associated with. If the stream identifier field
* specifies the value 0x0, a recipient MUST respond with a
* connection error (Section 5.4.1) of type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"PUSH must be associated with a stream"elseletparse_push_promiselength=lift2(funpromised_stream_idfragment->ifStream_identifier.is_connectionpromised_stream_idthen(* From RFC7540§6.6:
* A receiver MUST treat the receipt of a PUSH_PROMISE that
* promises an illegal stream identifier (Section 5.1.1) as a
* connection error (Section 5.4.1) of type PROTOCOL_ERROR. *)connection_errorProtocolError"PUSH must not promise stream id 0x0"elseifStream_identifier.is_requestpromised_stream_idthen(* From RFC7540§6.6:
* A receiver MUST treat the receipt of a PUSH_PROMISE that
* promises an illegal stream identifier (Section 5.1.1) as a
* connection error (Section 5.4.1) of type PROTOCOL_ERROR.
*
* Note: An odd-numbered stream is an invalid stream identifier for
* the server, and only the server can send PUSH_PROMISE frames:
*
* From RFC7540§8.2.1:
* PUSH_PROMISE frames MUST NOT be sent by the client. *)connection_errorProtocolError"PUSH must be associated with an even-numbered stream id"elseOkFrame.(PushPromise(promised_stream_id,fragment)))stream_identifier(* 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. *)(take_bigstring(length-4))inparse_padded_payloadframe_headerparse_push_promiseletparse_ping_frame{Frame.payload_length;stream_id;_}=ifnot(Stream_identifier.is_connectionstream_id)then(* From RFC7540§6.7:
* PING frames are not associated with any individual stream. If a PING
* frame is received with a stream identifier field value other than
* 0x0, the recipient MUST respond with a connection error
* (Section 5.4.1) of type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"PING must be associated with stream id 0x0"elseifpayload_length<>8then(* From RFC7540§6.7:
* Receipt of a PING frame with a length field value other than 8 MUST
* be treated as a connection error (Section 5.4.1) of type
* FRAME_SIZE_ERROR. *)advancepayload_length>>|fun()->connection_errorFrameSizeError"PING payload must be 8 octets in length"elselift(funbs->Ok(Frame.Pingbs))(take_bigstringpayload_length)letparse_go_away_frame{Frame.payload_length;stream_id;_}=ifnot(Stream_identifier.is_connectionstream_id)then(* From RFC7540§6.8:
* The GOAWAY frame applies to the connection, not a specific stream. An
* endpoint MUST treat a GOAWAY frame with a stream identifier other than
* 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"GOAWAY must be associated with stream id 0x0"elselift3(funlast_stream_iderrdebug_data->Ok(Frame.GoAway(last_stream_id,err,debug_data)))stream_identifierparse_error_code(take_bigstring(payload_length-8))letparse_window_update_frame{Frame.stream_id;payload_length;_}=(* From RFC7540§6.9:
* A WINDOW_UPDATE frame with a length other than 4 octets MUST be treated
* as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. *)ifpayload_length<>4thenadvancepayload_length>>|fun()->connection_errorFrameSizeError"WINDOW_UPDATE payload must be 4 octets in length"elselift(funuint->(* From RFC7540§6.9:
* The frame 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. *)letwindow_size_increment=Util.clear_bit_int32uint31inifInt32.equalwindow_size_increment0lthenif(* From RFC7540§6.9:
* A receiver MUST treat the receipt of a WINDOW_UPDATE frame
* with an flow-control window increment of 0 as a stream error
* (Section 5.4.2) of type PROTOCOL_ERROR; errors on the
* connection flow-control window MUST be treated as a connection
* error (Section 5.4.1). *)Stream_identifier.is_connectionstream_idthenconnection_errorProtocolError"Window update must not be 0"elsestream_errorProtocolErrorstream_idelseOk(Frame.WindowUpdatewindow_size_increment))BE.any_int32letparse_continuation_frame{Frame.payload_length;stream_id;_}=ifStream_identifier.is_connectionstream_idthen(* From RFC7540§6.10:
* CONTINUATION frames MUST be associated with a stream. If a
* CONTINUATION frame is received whose stream identifier field is 0x0,
* the recipient MUST respond with a connection error (Section 5.4.1) of
* type PROTOCOL_ERROR. *)advancepayload_length>>|fun()->connection_errorProtocolError"CONTINUATION must be associated with a stream"elselift(funblock_fragment->Ok(Frame.Continuationblock_fragment))(take_bigstringpayload_length)letparse_unknown_frametyp{Frame.payload_length;_}=lift(funbigstring->Ok(Frame.Unknown(typ,bigstring)))(take_bigstringpayload_length)letparse_frame_payload({Frame.frame_type;_}asframe_header)=(matchframe_typewith|Frame.FrameType.Data->parse_data_frameframe_header|Headers->parse_headers_frameframe_header|Priority->parse_priority_frameframe_header|RSTStream->parse_rst_stream_frameframe_header|Settings->parse_settings_frameframe_header|PushPromise->parse_push_promise_frameframe_header|Ping->parse_ping_frameframe_header|GoAway->parse_go_away_frameframe_header|WindowUpdate->parse_window_update_frameframe_header|Continuation->parse_continuation_frameframe_header|Unknowntyp->parse_unknown_frametypframe_header)<?>"frame_payload"letparse_frameparse_context=parse_frame_header>>=fun({Frame.payload_length;_}asframe_header)->(* If we're parsing a new frame, we didn't yet send a stream error on it *)parse_context.did_report_stream_error<-false;parse_context.frame_header<-frame_header;(* h2 does unbuffered parsing and the bigarray we read input from is
* allocated based on the maximum frame payload negotiated by HTTP/2
* communication. If the underlying buffer is smaller than what
* the frame can fit, we want to skip the remaining input and skip to the
* next frame.
*
* From RFC7540§5.4.2:
* A stream error is an error related to a specific stream that does
* not affect processing of other streams. *)letis_frame_size_error=payload_length>parse_context.max_frame_sizeinifis_frame_size_errorthenparse_context.remaining_bytes_to_skip<-parse_context.remaining_bytes_to_skip+payload_length;lift(function|Okframe_payload->Ok{Frame.frame_header;frame_payload}|Errore->Errore)(parse_frame_payloadframe_header)(* This is the client connection preface. *)letconnection_preface=(* 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. *)stringFrame.connection_preface<?>"connection preface"moduleReader=structmoduleAU=Angstrom.Unbufferedtypeparse_error=(* Parse error reported by Angstrom *)[`Parseofstringlist*string|(* Full error information *)`ErrorofError.t|(* Just the error code, need to puzzle back connection or stream info *)`Error_codeofError_code.t]type'errorparse_state=|Initial|Failof'error|Partialof(Bigstringaf.t->off:int->len:int->AU.more->(unit,'error)resultAU.state)type'errort={parser:(unit,'error)resultAngstrom.t;mutableparse_state:'errorparse_state(* The state of the parse for the current request *);mutableclosed:bool(* Whether the input source has left the building, indicating that no
* further input will be received. *);parse_context:parse_context(* The current stream identifier being processed, in order to discern
* whether the error that needs to be assembled is a stream or
* connection error. *)}typeframe=parse_errortletcreateparserparse_context={parser;parse_state=Initial;closed=false;parse_context}letcreate_parse_contextmax_frame_size={frame_header=default_frame_header;remaining_bytes_to_skip=0;did_report_stream_error=false;max_frame_size}letsettings_prefaceparse_context=(* From RFC7540§3.5:
* [...] the connection preface starts with the string
* PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n). This sequence MUST be followed by
* a SETTINGS frame (Section 6.5), which MAY be empty. *)parse_frameparse_context>>|function|Ok({frame_payload=Frame.Settingssettings_list;_}asframe)->Ok(frame,settings_list)|Ok{frame_payload=Frame.GoAway(_,error_code,debug_data);_}->(* From RFC7540§9.2.1:
* An endpoint MAY immediately terminate an HTTP/2 connection that does
* not meet these TLS requirements with a connection error (Section
* 5.4.1) of type INADEQUATE_SECURITY.
*
* Note: we are liberal on purpose in this branch instead of only
* accepting an error of type `INADEQUATE_SECURITY`. If an endpoint is
* sending us a `GOAWAY` frame we probably did something wrong and
* deserve to know what that is. *)Error(`ErrorError.(ConnectionError(error_code,Bigstringaf.to_stringdebug_data)))|Ok_->(* From RFC7540§3.5:
* Clients and servers MUST treat an invalid connection preface as a
* connection error (Section 5.4.1) of type PROTOCOL_ERROR. A GOAWAY
* frame (Section 6.8) MAY be omitted in this case, since an invalid
* preface indicates that the peer is not using HTTP/2. *)Error(`ErrorError.(ConnectionError(ProtocolError,"Invalid connection preface")))|Errore->Error(`Errore)letconnection_preface_and_frames~max_frame_sizepreface_parserpreface_handlerframe_handler=letparse_context=create_parse_contextmax_frame_sizeinletparser=preface_parserparse_context<*commit>>=function|Ok(frame,settings_list)->preface_handlerframesettings_list;(* After having received a valid connection preface, we can start
* reading other frames now. *)skip_many(parse_frameparse_context<*commit>>|frame_handler)>>|fun()->Ok()|Error_aserror->returnerrorincreateparserparse_contextletclient_framespreface_handlerframe_handler=connection_preface_and_frames(* From RFC7540§3.5:
* The server connection preface consists of a potentially empty
* SETTINGS frame (Section 6.5) that MUST be the first frame the server
* sends in the HTTP/2 connection. *)settings_prefacepreface_handlerframe_handlerletserver_frames~max_frame_sizepreface_handlerframe_handler=connection_preface_and_frames~max_frame_size(funparse_context->(* From RFC7540§3.5:
* The client connection preface starts with a sequence of 24 octets,
* which in hex notation is:
*
* 0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
* That is, the connection preface starts with the string
* PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n). This sequence MUST be followed
* by a SETTINGS frame (Section 6.5), which MAY be empty. *)connection_preface*>settings_prefaceparse_context)preface_handlerframe_handlerletis_closedt=t.closedlettransitiontstate=matchstatewith|AU.Done(consumed,Ok())->t.parse_state<-Initial;consumed|Done(consumed,Errorerror)->t.parse_state<-Failerror;consumed|Fail(consumed,marks,msg)->t.parse_state<-Fail(`Parse(marks,msg));consumed|Partial{committed;continue}->(* If we have bytes to skip over then it means we've spotted a
* FRAME_SIZE_ERROR, a case where, due to our unbuffered parsing, the
* payload length declared in a frame header is larger than the
* underlying buffer can fit. *)ift.parse_context.remaining_bytes_to_skip>0thent.parse_state<-Fail(`Error_codeError_code.FrameSizeError)elset.parse_state<-Partialcontinue;committedletstarttstate=matchstatewith|AU.Done_->failwith"h2.Parse.Reader.unable to start parser"|Fail(0,marks,msg)->t.parse_state<-Fail(`Parse(marks,msg))|Partial{committed=0;continue}->t.parse_state<-Partialcontinue|Partial_|Fail_->assertfalseletrecread_with_moretbs~off~lenmore=letconsumed=matcht.parse_statewith|Fail_->letparser_ctx=t.parse_contextinletremaining_bytes=parser_ctx.remaining_bytes_to_skipin(* Just skip input if we need to *)ifremaining_bytes>0then(assert(remaining_bytes>=len);letremaining_bytes'=remaining_bytes-leninparser_ctx.remaining_bytes_to_skip<-remaining_bytes';assert(remaining_bytes'>=0);ifremaining_bytes'=0then(* Reset the parser state to `Done` so that we can read the next
* frame (after skipping through the bad input) *)t.parse_state<-Initial;len)else0|Initial->startt(AU.parset.parser);read_with_moretbs~off~lenmore|Partialcontinue->transitiont(continuebsmore~off~len)in(matchmorewithComplete->t.closed<-true|Incomplete->());consumedletforce_closet=t.closed<-trueletfail_to_stringmarkserr=String.concat" > "marks^": "^errletnext_from_errort?(msg="")error_code=ift.parse_context.frame_header==default_frame_headerthen`ErrorError.(ConnectionError(error_code,msg))elsematcht.parse_context,error_codewith|({frame_header={frame_type=Headers|PushPromise|Continuation|Settings|Unknown_;_};_},Error_code.FrameSizeError)|{frame_header={Frame.stream_id=0x0l;_};_},_->(* From RFC7540§4.2:
* A frame size error in a frame that could alter the state of the
* entire connection MUST be treated as a connection error (Section
* 5.4.1); this includes any frame carrying a header block (Section
* 4.3) (that is, HEADERS, PUSH_PROMISE, and CONTINUATION), SETTINGS,
* and any frame with a stream identifier of 0. *)`ErrorError.(ConnectionError(error_code,msg))|{did_report_stream_error=true;_},_->(* If the parser is in a `Fail` state and would report a stream error,
* just issue a `Read` operation if we've already reported that error. *)ift.closedthen`Closeelse`Read|{frame_header={Frame.stream_id;_};_},_->t.parse_context.did_report_stream_error<-true;`ErrorError.(StreamError(stream_id,error_code))letnextt=matcht.parse_statewith|Failerror->(matcherrorwith|`Errore->`Errore|`Error_codeerror_code->next_from_errorterror_code|`Parse(marks,msg)->leterror_code=matchmarks,msgwith|["frame_payload"],"not enough input"->(* From RFC7540§4.2:
* An endpoint MUST send an error code of FRAME_SIZE_ERROR if a
* frame exceeds the size defined in SETTINGS_MAX_FRAME_SIZE,
* exceeds any limit defined for the frame type, or is too small
* to contain mandatory frame data. *)Error_code.FrameSizeError|_->Error_code.ProtocolErrorinnext_from_errort~msg:(fail_to_stringmarksmsg)error_code)|_whent.closed->`Close|Partial_->`Read|Initial->ift.closedthen`Closeelse`Readend