open H2__ module Option = struct let get = function Some x -> x | None -> failwith "Option.get: None" let map f = function Some x -> Some (f x) | None -> None end let read_all path = let file = open_in path in try really_input_string file (in_channel_length file) with | exn -> close_in file; raise exn let bs_to_string bs = let off = 0 in let len = Bigstringaf.length bs in Bigstringaf.substring ~off ~len bs let bs_of_string s = Bigstringaf.of_string ~off:0 ~len:(String.length s) s let string_of_hex s = Hex.to_string (`Hex s) let hex_of_string s = let (`Hex hex) = Hex.of_string s in String.uppercase_ascii hex let make_iovecs bs = [ { Httpun_types.IOVec.buffer = bs; off = 0; len = Bigstringaf.length bs } ] let write_frame ?padding t { Frame.frame_header; frame_payload } = let open Serialize in let { Frame.flags; stream_id; _ } = frame_header in let info = Writer.make_frame_info ~flags ?padding stream_id in match frame_payload with | Data body -> Writer.schedule_data t info body | Headers (priority, headers_block) -> (* Block already HPACK-encoded. *) write_headers_frame t.encoder info ~priority (make_iovecs headers_block) | Priority p -> Writer.write_priority t info p | RSTStream e -> Writer.write_rst_stream t info e | Settings settings -> Writer.write_settings t info settings | PushPromise (promised_id, header_block) -> write_push_promise_frame t.encoder info ~promised_id (make_iovecs header_block) | Ping payload -> Writer.write_ping t info payload | GoAway (last_stream_id, error, debug_data) -> Writer.write_go_away t info ~debug_data ~last_stream_id error | WindowUpdate window_size -> Writer.write_window_update t info window_size | Continuation header_block -> write_continuation_frame t.encoder info (make_iovecs header_block) | Unknown (code, payload) -> write_unknown_frame t.encoder ~code info payload let serialize_frame ?padding frame = let open Serialize in let { Frame.payload_length; _ } = frame.Frame.frame_header in let writer = Writer.create payload_length in write_frame ?padding writer frame; Faraday.serialize_to_bigstring (Writer.faraday writer) let serialize_frame_string ?padding frame = let bs = serialize_frame ?padding frame in bs_to_string bs let opt_exn = function Some x -> x | None -> failwith "opt_exn: None" let encode_headers hpack_encoder headers = let f = Faraday.create 0x1000 in Serialize.Writer.encode_headers hpack_encoder f headers; Faraday.serialize_to_bigstring f let decode_headers decoder bigstring = let parser = Angstrom.Buffered.parse (Hpack.Decoder.decode_headers decoder) in let state = Angstrom.Buffered.feed parser (`Bigstring bigstring) in let state' = Angstrom.Buffered.feed state `Eof in match Angstrom.Buffered.state_to_option state' with | Some (Ok headers) -> headers | Some _ | None -> assert false let preface = let writer = Serialize.Writer.create 0x400 in Serialize.Writer.write_connection_preface writer []; Faraday.serialize_to_string (Serialize.Writer.faraday writer) let handle_preface t = let open Parse in let preface_len = String.length preface in ignore @@ Reader.read_with_more t (bs_of_string preface) ~off:0 ~len:preface_len Incomplete let parse_frames_bigstring wire = let open Parse in let frames = ref [] in let handler = function | Ok frame -> frames := frame :: !frames | _ -> Alcotest.fail "Expected frame to parse successfully." in let reader = Reader.server_frames ~max_frame_size:H2.Settings.default.max_frame_size (fun _ -> ignore) handler in handle_preface reader; let _read = Reader.read_with_more reader wire ~off:0 ~len:(Bigstringaf.length wire) Incomplete in List.rev !frames let parse_frames wire = parse_frames_bigstring (bs_of_string wire)