(*---------------------------------------------------------------------------- * 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. *---------------------------------------------------------------------------*) open Angstrom (* 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. *) let skip_many p = fix (fun m -> p >>| (fun _ -> true) <|> return false >>= function | true -> m | false -> return ()) let default_frame_header = { Frame.payload_length = 0 ; flags = Flags.default_flags ; stream_id = -1l ; frame_type = Unknown (-1) } type parse_context = { mutable frame_header : Frame.frame_header ; mutable remaining_bytes_to_skip : int ; mutable did_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 } let connection_error error_code msg = Error Error.(ConnectionError (error_code, msg)) let stream_error error_code stream_id = Error Error.(StreamError (stream_id, error_code)) let parse_uint24 o1 o2 o3 = (o1 lsl 16) lor (o2 lsl 8) lor o3 let frame_length = (* From RFC7540§4.1: * Length: The length of the frame payload expressed as an unsigned 24-bit * integer. *) lift3 parse_uint24 any_uint8 any_uint8 any_uint8 let frame_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. *) lift Frame.FrameType.parse any_uint8 let flags = (* From RFC7540§4.1: * Flags: An 8-bit field reserved for boolean flags specific to the frame * type. *) any_uint8 let parse_stream_identifier n = (* 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.(logand n (sub (shift_left 1l 31) 1l)) let stream_identifier = lift parse_stream_identifier BE.any_int32 let parse_frame_header = lift4 (fun payload_length frame_type flags stream_id -> { Frame.flags; payload_length; stream_id; frame_type }) frame_length frame_type flags stream_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. *) <* commit let parse_padded_payload { Frame.payload_length; flags; _ } parser = if Flags.test_padded flags then any_uint8 >>= fun pad_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. *) if pad_length >= payload_length then (* 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_error ProtocolError "Padding size exceeds payload size" else (* Subtract the octet that contains the length of padding, and the * padding octets. *) let relevant_length = payload_length - 1 - pad_length in parser relevant_length <* advance pad_length else parser payload_length let parse_data_frame ({ Frame.stream_id; payload_length; _ } as frame_header) = if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "Data frames must be associated with a stream" else let parse_data length = lift (fun bs -> Ok (Frame.Data bs)) (take_bigstring length) in parse_padded_payload frame_header parse_data let parse_priority = lift2 (fun stream_dependency weight -> let e = Priority.test_exclusive stream_dependency in { 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_identifier stream_dependency }) BE.any_int32 any_uint8 let parse_headers_frame frame_header = let { Frame.payload_length; stream_id; flags; _ } = frame_header in if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "HEADERS must be associated with a stream" else let parse_headers length = if Flags.test_priority flags then lift2 (fun priority headers -> Ok (Frame.Headers (priority, headers))) parse_priority (* See RFC7540§6.3: * Stream Dependency (4 octets) + Weight (1 octet). *) (take_bigstring (length - 5)) else lift (fun headers_block -> Ok (Frame.Headers (Priority.default_priority, headers_block))) (take_bigstring length) in parse_padded_payload frame_header parse_headers let parse_priority_frame { Frame.payload_length; stream_id; _ } = if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "PRIORITY must be associated with a stream" else if payload_length <> 5 then (* 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. *) advance payload_length >>| fun () -> stream_error FrameSizeError stream_id else lift (fun priority -> Ok (Frame.Priority priority)) parse_priority let parse_error_code = lift Error_code.parse BE.any_int32 let parse_rst_stream_frame { Frame.payload_length; stream_id; _ } = if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "RST_STREAM must be associated with a stream" else if payload_length <> 4 then (* 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. *) advance payload_length >>| fun () -> connection_error FrameSizeError "RST_STREAM payload must be 4 octets in length" else lift (fun error_code -> Ok (Frame.RSTStream error_code)) parse_error_code let parse_settings_frame { Frame.payload_length; stream_id; flags; _ } = if not (Stream_identifier.is_connection stream_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. *) advance payload_length >>| fun () -> connection_error ProtocolError "SETTINGS must be associated with stream id 0x0" else if payload_length mod 6 <> 0 then (* 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. *) advance payload_length >>| fun () -> connection_error FrameSizeError "SETTINGS payload size must be a multiple of 6" else if Flags.test_ack flags && payload_length <> 0 then (* 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. *) advance payload_length >>| fun () -> connection_error FrameSizeError "SETTINGS with ACK must be empty" else let num_settings = payload_length / Settings.octets_per_setting in Settings.parse_settings_payload num_settings >>| fun xs -> Ok (Frame.Settings xs) let parse_push_promise_frame frame_header = let { Frame.payload_length; stream_id; _ } = frame_header in if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "PUSH must be associated with a stream" else let parse_push_promise length = lift2 (fun promised_stream_id fragment -> if Stream_identifier.is_connection promised_stream_id then (* 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_error ProtocolError "PUSH must not promise stream id 0x0" else if Stream_identifier.is_request promised_stream_id then (* 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_error ProtocolError "PUSH must be associated with an even-numbered stream id" else Ok Frame.(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)) in parse_padded_payload frame_header parse_push_promise let parse_ping_frame { Frame.payload_length; stream_id; _ } = if not (Stream_identifier.is_connection stream_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. *) advance payload_length >>| fun () -> connection_error ProtocolError "PING must be associated with stream id 0x0" else if payload_length <> 8 then (* 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. *) advance payload_length >>| fun () -> connection_error FrameSizeError "PING payload must be 8 octets in length" else lift (fun bs -> Ok (Frame.Ping bs)) (take_bigstring payload_length) let parse_go_away_frame { Frame.payload_length; stream_id; _ } = if not (Stream_identifier.is_connection stream_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. *) advance payload_length >>| fun () -> connection_error ProtocolError "GOAWAY must be associated with stream id 0x0" else lift3 (fun last_stream_id err debug_data -> Ok (Frame.GoAway (last_stream_id, err, debug_data))) stream_identifier parse_error_code (take_bigstring (payload_length - 8)) let parse_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. *) if payload_length <> 4 then advance payload_length >>| fun () -> connection_error FrameSizeError "WINDOW_UPDATE payload must be 4 octets in length" else lift (fun uint -> (* 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. *) let window_size_increment = Util.clear_bit_int32 uint 31 in if Int32.equal window_size_increment 0l then if (* 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_connection stream_id then connection_error ProtocolError "Window update must not be 0" else stream_error ProtocolError stream_id else Ok (Frame.WindowUpdate window_size_increment)) BE.any_int32 let parse_continuation_frame { Frame.payload_length; stream_id; _ } = if Stream_identifier.is_connection stream_id then (* 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. *) advance payload_length >>| fun () -> connection_error ProtocolError "CONTINUATION must be associated with a stream" else lift (fun block_fragment -> Ok (Frame.Continuation block_fragment)) (take_bigstring payload_length) let parse_unknown_frame typ { Frame.payload_length; _ } = lift (fun bigstring -> Ok (Frame.Unknown (typ, bigstring))) (take_bigstring payload_length) let parse_frame_payload ({ Frame.frame_type; _ } as frame_header) = (match frame_type with | Frame.FrameType.Data -> parse_data_frame frame_header | Headers -> parse_headers_frame frame_header | Priority -> parse_priority_frame frame_header | RSTStream -> parse_rst_stream_frame frame_header | Settings -> parse_settings_frame frame_header | PushPromise -> parse_push_promise_frame frame_header | Ping -> parse_ping_frame frame_header | GoAway -> parse_go_away_frame frame_header | WindowUpdate -> parse_window_update_frame frame_header | Continuation -> parse_continuation_frame frame_header | Unknown typ -> parse_unknown_frame typ frame_header) "frame_payload" let parse_frame parse_context = parse_frame_header >>= fun ({ Frame.payload_length; _ } as frame_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. *) let is_frame_size_error = payload_length > parse_context.max_frame_size in if is_frame_size_error then parse_context.remaining_bytes_to_skip <- parse_context.remaining_bytes_to_skip + payload_length; lift (function | Ok frame_payload -> Ok { Frame.frame_header; frame_payload } | Error e -> Error e) (parse_frame_payload frame_header) (* This is the client connection preface. *) let connection_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. *) string Frame.connection_preface "connection preface" module Reader = struct module AU = Angstrom.Unbuffered type parse_error = (* Parse error reported by Angstrom *) [ `Parse of string list * string | (* Full error information *) `Error of Error.t | (* Just the error code, need to puzzle back connection or stream info *) `Error_code of Error_code.t ] type 'error parse_state = | Initial | Fail of 'error | Partial of (Bigstringaf.t -> off:int -> len:int -> AU.more -> (unit, 'error) result AU.state) type 'error t = { parser : (unit, 'error) result Angstrom.t ; mutable parse_state : 'error parse_state (* The state of the parse for the current request *) ; mutable closed : 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. *) } type frame = parse_error t let create parser parse_context = { parser; parse_state = Initial; closed = false; parse_context } let create_parse_context max_frame_size = { frame_header = default_frame_header ; remaining_bytes_to_skip = 0 ; did_report_stream_error = false ; max_frame_size } let settings_preface parse_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_frame parse_context >>| function | Ok ({ frame_payload = Frame.Settings settings_list; _ } as frame) -> 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 (`Error Error.(ConnectionError (error_code, Bigstringaf.to_string debug_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 (`Error Error.(ConnectionError (ProtocolError, "Invalid connection preface"))) | Error e -> Error (`Error e) let connection_preface_and_frames ~max_frame_size preface_parser preface_handler frame_handler = let parse_context = create_parse_context max_frame_size in let parser = preface_parser parse_context <* commit >>= function | Ok (frame, settings_list) -> preface_handler frame settings_list; (* After having received a valid connection preface, we can start * reading other frames now. *) skip_many (parse_frame parse_context <* commit >>| frame_handler) >>| fun () -> Ok () | Error _ as error -> return error in create parser parse_context let client_frames preface_handler frame_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_preface preface_handler frame_handler let server_frames ~max_frame_size preface_handler frame_handler = connection_preface_and_frames ~max_frame_size (fun parse_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_preface parse_context) preface_handler frame_handler let is_closed t = t.closed let transition t state = match state with | AU.Done (consumed, Ok ()) -> t.parse_state <- Initial; consumed | Done (consumed, Error error) -> t.parse_state <- Fail error; 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. *) if t.parse_context.remaining_bytes_to_skip > 0 then t.parse_state <- Fail (`Error_code Error_code.FrameSizeError) else t.parse_state <- Partial continue; committed let start t state = match state with | 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 <- Partial continue | Partial _ | Fail _ -> assert false let rec read_with_more t bs ~off ~len more = let consumed = match t.parse_state with | Fail _ -> let parser_ctx = t.parse_context in let remaining_bytes = parser_ctx.remaining_bytes_to_skip in (* Just skip input if we need to *) if remaining_bytes > 0 then ( assert (remaining_bytes >= len); let remaining_bytes' = remaining_bytes - len in parser_ctx.remaining_bytes_to_skip <- remaining_bytes'; assert (remaining_bytes' >= 0); if remaining_bytes' = 0 then (* 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) else 0 | Initial -> start t (AU.parse t.parser); read_with_more t bs ~off ~len more | Partial continue -> transition t (continue bs more ~off ~len) in (match more with Complete -> t.closed <- true | Incomplete -> ()); consumed let force_close t = t.closed <- true let fail_to_string marks err = String.concat " > " marks ^ ": " ^ err let next_from_error t ?(msg = "") error_code = if t.parse_context.frame_header == default_frame_header then `Error Error.(ConnectionError (error_code, msg)) else match t.parse_context, error_code with | ( { 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. *) `Error Error.(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. *) if t.closed then `Close else `Read | { frame_header = { Frame.stream_id; _ }; _ }, _ -> t.parse_context.did_report_stream_error <- true; `Error Error.(StreamError (stream_id, error_code)) let next t = match t.parse_state with | Fail error -> (match error with | `Error e -> `Error e | `Error_code error_code -> next_from_error t error_code | `Parse (marks, msg) -> let error_code = match marks, msg with | [ "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.ProtocolError in next_from_error t ~msg:(fail_to_string marks msg) error_code) | _ when t.closed -> `Close | Partial _ -> `Read | Initial -> if t.closed then `Close else `Read end