This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
339
unikernel/duniverse/ocaml-h2/lib_test/test_frames.ml
Normal file
339
unikernel/duniverse/ocaml-h2/lib_test/test_frames.ml
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
open H2__
|
||||
open Test_common
|
||||
module Json = Yojson.Basic.Util
|
||||
|
||||
let fixtures_dir = "http2-frame-test-case"
|
||||
|
||||
let success_fixtures, error_fixtures =
|
||||
fixtures_dir
|
||||
|> Sys.readdir
|
||||
|> Array.to_list
|
||||
|> List.map (fun dir -> dir, Filename.concat fixtures_dir dir)
|
||||
|> List.filter (fun (_, dir) -> Sys.is_directory dir)
|
||||
|> List.map (fun (dir, fullpath) ->
|
||||
let files_in_dir =
|
||||
fullpath
|
||||
|> Sys.readdir
|
||||
|> Array.to_list
|
||||
|> List.map (fun file -> Filename.concat fullpath file)
|
||||
|> List.filter (fun file ->
|
||||
(not (Sys.is_directory file)) && Filename.extension file = ".json")
|
||||
in
|
||||
dir, files_in_dir)
|
||||
|> List.partition (fun (dir, _) -> dir <> "error")
|
||||
|
||||
module P = struct
|
||||
open Parse
|
||||
|
||||
let parse_fn wire success_handler error_handler =
|
||||
let handler = function
|
||||
| Ok frame -> success_handler frame
|
||||
| Error e -> error_handler e
|
||||
in
|
||||
let reader =
|
||||
Reader.server_frames
|
||||
~max_frame_size:Settings.default.max_frame_size
|
||||
(fun _ -> ignore)
|
||||
handler
|
||||
in
|
||||
handle_preface reader;
|
||||
let wire_bs = wire |> string_of_hex |> bs_of_string in
|
||||
let _read =
|
||||
Reader.read_with_more
|
||||
reader
|
||||
wire_bs
|
||||
~off:0
|
||||
~len:(Bigstringaf.length wire_bs)
|
||||
Incomplete
|
||||
in
|
||||
reader
|
||||
|
||||
let parse_success wire handler =
|
||||
let reader =
|
||||
parse_fn wire handler (fun _ ->
|
||||
Alcotest.(fail "Expected to have thrown an error parsing frame."))
|
||||
in
|
||||
match Reader.next reader with
|
||||
| `Read -> ()
|
||||
| `Close | `Error _ ->
|
||||
Alcotest.(fail "Expected to have sucessfully parsed frame.")
|
||||
|
||||
let parse_error wire handler =
|
||||
let reader =
|
||||
parse_fn
|
||||
wire
|
||||
(fun _ ->
|
||||
Alcotest.(fail "Expected to have thrown an error parsing frame."))
|
||||
handler
|
||||
in
|
||||
match Reader.next reader with
|
||||
| `Read -> ()
|
||||
| `Close ->
|
||||
Alcotest.(fail "Expected to have thrown an error parsing frame.")
|
||||
| `Error e -> handler e
|
||||
|
||||
let serialize ?padding frame =
|
||||
let output = Test_common.serialize_frame_string ?padding frame in
|
||||
hex_of_string output
|
||||
end
|
||||
|
||||
let frame_testable =
|
||||
(module struct
|
||||
type t = Frame.t
|
||||
|
||||
let priority_to_yojson priority =
|
||||
if priority != Priority.default_priority
|
||||
then
|
||||
let { Priority.exclusive; stream_dependency; weight } = priority in
|
||||
[ "stream_dependency", `Int (Int32.to_int stream_dependency)
|
||||
; "weight", `Int weight
|
||||
; "exclusive", `Bool exclusive
|
||||
]
|
||||
else [ "stream_dependency", `Null; "weight", `Null; "exclusive", `Null ]
|
||||
|
||||
let bs_to_string bs =
|
||||
let off = 0 in
|
||||
let len = Bigstringaf.length bs in
|
||||
Bigstringaf.substring ~off ~len bs
|
||||
|
||||
let frame_payload_to_json frame_type payload =
|
||||
let others =
|
||||
match payload with
|
||||
| Frame.Data data -> [ "data", `String (bs_to_string data) ]
|
||||
| Headers (priority, fragment) ->
|
||||
("header_block_fragment", `String (bs_to_string fragment))
|
||||
:: priority_to_yojson priority
|
||||
| Priority priority -> priority_to_yojson priority
|
||||
| RSTStream error_code ->
|
||||
[ "error_code", `Int (Error_code.serialize error_code |> Int32.to_int)
|
||||
]
|
||||
| Settings settings_list ->
|
||||
[ ( "settings"
|
||||
, `List
|
||||
(List.map
|
||||
(fun setting ->
|
||||
`List
|
||||
[ `Int (Settings.serialize_key setting)
|
||||
; `Int
|
||||
(match setting with
|
||||
| MaxConcurrentStreams value
|
||||
| InitialWindowSize value ->
|
||||
Int32.to_int value
|
||||
| HeaderTableSize value
|
||||
| EnablePush value
|
||||
| MaxFrameSize value
|
||||
| MaxHeaderListSize value ->
|
||||
value)
|
||||
])
|
||||
settings_list) )
|
||||
]
|
||||
| PushPromise (stream_identifier, fragment) ->
|
||||
[ "header_block_fragment", `String (bs_to_string fragment)
|
||||
; "promised_stream_id", `Int (Int32.to_int stream_identifier)
|
||||
]
|
||||
| Ping data -> [ "opaque_data", `String (bs_to_string data) ]
|
||||
| GoAway (stream_identifier, error_code, debug_data) ->
|
||||
[ "error_code", `Int (Error_code.serialize error_code |> Int32.to_int)
|
||||
; "additional_debug_data", `String (bs_to_string debug_data)
|
||||
; "last_stream_id", `Int (Int32.to_int stream_identifier)
|
||||
]
|
||||
| WindowUpdate increment ->
|
||||
[ "window_size_increment", `Int (Int32.to_int increment) ]
|
||||
| Continuation fragment ->
|
||||
[ "header_block_fragment", `String (bs_to_string fragment) ]
|
||||
| Unknown (_frame_type, _) -> assert false
|
||||
in
|
||||
`Assoc (("type", `Int (Frame.FrameType.serialize frame_type)) :: others)
|
||||
|
||||
let frame_to_yojson { Frame.frame_header; frame_payload } =
|
||||
let { Frame.payload_length; flags; stream_id; frame_type } =
|
||||
frame_header
|
||||
in
|
||||
`Assoc
|
||||
[ "length", `Int payload_length
|
||||
; "flags", `Int flags
|
||||
; "stream_identifier", `Int (Int32.to_int stream_id)
|
||||
; "frame_payload", frame_payload_to_json frame_type frame_payload
|
||||
]
|
||||
|
||||
let pp formatter t =
|
||||
Format.pp_print_text
|
||||
formatter
|
||||
(t |> frame_to_yojson |> Yojson.Safe.pretty_to_string)
|
||||
|
||||
let equal = ( = )
|
||||
end : Alcotest.TESTABLE
|
||||
with type t = Frame.t)
|
||||
|
||||
let priority_of_json json =
|
||||
let stream_dependency =
|
||||
Json.(json |> member "stream_dependency" |> to_int_option)
|
||||
in
|
||||
let weight = Json.(json |> member "weight" |> to_int_option) in
|
||||
let exclusive = Json.(json |> member "exclusive" |> to_bool_option) in
|
||||
match stream_dependency, weight, exclusive with
|
||||
| Some stream_dependency, Some weight, Some exclusive ->
|
||||
{ Priority.exclusive
|
||||
; stream_dependency = Int32.of_int stream_dependency
|
||||
; weight
|
||||
}
|
||||
| _ -> Priority.default_priority
|
||||
|
||||
let frame_type_of_string = function
|
||||
| "data" -> Frame.FrameType.Data
|
||||
| "headers" -> Headers
|
||||
| "priority" -> Priority
|
||||
| "rst_stream" -> RSTStream
|
||||
| "settings" -> Settings
|
||||
| "push_promise" -> PushPromise
|
||||
| "ping" -> Ping
|
||||
| "goaway" -> GoAway
|
||||
| "window_update" -> WindowUpdate
|
||||
| "continuation" -> Continuation
|
||||
| _ -> assert false
|
||||
|
||||
let frame_payload_of_json frame_type json =
|
||||
match frame_type with
|
||||
| Frame.FrameType.Data ->
|
||||
Frame.Data Json.(json |> member "data" |> to_string |> bs_of_string)
|
||||
| Headers ->
|
||||
let priority = priority_of_json json in
|
||||
let fragment =
|
||||
Json.(json |> member "header_block_fragment" |> to_string |> bs_of_string)
|
||||
in
|
||||
Headers (priority, fragment)
|
||||
| Priority -> Priority (priority_of_json json)
|
||||
| RSTStream ->
|
||||
let error_code =
|
||||
Json.(json |> member "error_code" |> to_int |> Int32.of_int)
|
||||
in
|
||||
RSTStream (Error_code.parse error_code)
|
||||
| Settings ->
|
||||
let settings =
|
||||
List.map
|
||||
(fun setting_json ->
|
||||
let setting = Json.to_list setting_json in
|
||||
let key_value = List.nth setting 1 |> Json.to_int in
|
||||
match Json.to_int (List.hd setting) with
|
||||
| 0x1 -> Settings.HeaderTableSize key_value
|
||||
| 0x2 -> EnablePush key_value
|
||||
| 0x3 -> MaxConcurrentStreams (Int32.of_int key_value)
|
||||
| 0x4 -> InitialWindowSize (Int32.of_int key_value)
|
||||
| 0x5 -> MaxFrameSize key_value
|
||||
| 0x6 -> MaxHeaderListSize key_value
|
||||
| _ -> raise (Invalid_argument "settings key id"))
|
||||
Json.(json |> member "settings" |> to_list)
|
||||
in
|
||||
Settings settings
|
||||
| PushPromise ->
|
||||
let fragment =
|
||||
Json.(json |> member "header_block_fragment" |> to_string |> bs_of_string)
|
||||
in
|
||||
let promised_stream_id =
|
||||
Json.(json |> member "promised_stream_id" |> to_int |> Int32.of_int)
|
||||
in
|
||||
PushPromise (promised_stream_id, fragment)
|
||||
| Ping ->
|
||||
let opaque_data =
|
||||
Json.(json |> member "opaque_data" |> to_string |> bs_of_string)
|
||||
in
|
||||
Ping opaque_data
|
||||
| GoAway ->
|
||||
let error_code =
|
||||
Json.(json |> member "error_code" |> to_int |> Int32.of_int)
|
||||
in
|
||||
let last_stream_id =
|
||||
Json.(json |> member "last_stream_id" |> to_int |> Int32.of_int)
|
||||
in
|
||||
let debug_data =
|
||||
Json.(json |> member "additional_debug_data" |> to_string |> bs_of_string)
|
||||
in
|
||||
GoAway (last_stream_id, Error_code.parse error_code, debug_data)
|
||||
| WindowUpdate ->
|
||||
let window_size_increment =
|
||||
Json.(json |> member "window_size_increment" |> to_int) |> Int32.of_int
|
||||
in
|
||||
WindowUpdate window_size_increment
|
||||
| Continuation ->
|
||||
let fragment =
|
||||
Json.(json |> member "header_block_fragment" |> to_string |> bs_of_string)
|
||||
in
|
||||
Continuation fragment
|
||||
| Unknown _ -> assert false
|
||||
|
||||
let success_suites =
|
||||
let gen_suite ~frame_type filename =
|
||||
let fixture = read_all filename |> Yojson.Basic.from_string in
|
||||
let test_case_name = Json.(fixture |> member "description" |> to_string) in
|
||||
( Printf.sprintf "%s: %s" filename test_case_name
|
||||
, `Quick
|
||||
, fun () ->
|
||||
let expected_frame_json = Json.(member "frame" fixture) in
|
||||
let frame_payload_json =
|
||||
Json.(expected_frame_json |> member "frame_payload")
|
||||
in
|
||||
let frame_type = frame_type_of_string frame_type in
|
||||
let expected_frame =
|
||||
{ Frame.frame_header =
|
||||
{ payload_length =
|
||||
Json.(expected_frame_json |> member "length" |> to_int)
|
||||
; flags = Json.(expected_frame_json |> member "flags" |> to_int)
|
||||
; stream_id =
|
||||
Json.(
|
||||
expected_frame_json
|
||||
|> member "stream_identifier"
|
||||
|> to_int
|
||||
|> Int32.of_int)
|
||||
; frame_type
|
||||
}
|
||||
; frame_payload = frame_payload_of_json frame_type frame_payload_json
|
||||
}
|
||||
in
|
||||
let wire = Json.(fixture |> member "wire" |> to_string) in
|
||||
P.parse_success wire (fun frame ->
|
||||
Alcotest.check frame_testable "Frames are equal" expected_frame frame;
|
||||
let padding =
|
||||
match
|
||||
Json.(frame_payload_json |> member "padding" |> to_string_option)
|
||||
with
|
||||
| Some padding ->
|
||||
Some
|
||||
Bigstringaf.(
|
||||
of_string ~off:0 ~len:(String.length padding) padding)
|
||||
| None -> None
|
||||
in
|
||||
let serialized_wire = P.serialize ?padding frame in
|
||||
Alcotest.(
|
||||
check string "Parse / Serialize roundtripping" wire serialized_wire))
|
||||
)
|
||||
in
|
||||
List.map
|
||||
(fun (name, files) ->
|
||||
let suite_name = Printf.sprintf "%s frame" name in
|
||||
suite_name, List.map (gen_suite ~frame_type:name) files)
|
||||
success_fixtures
|
||||
|
||||
let error_suites =
|
||||
let gen_suite filename =
|
||||
let fixture = read_all filename |> Yojson.Basic.from_string in
|
||||
let test_case_name = Json.(fixture |> member "description" |> to_string) in
|
||||
( Printf.sprintf "%s: %s" filename test_case_name
|
||||
, `Quick
|
||||
, fun () ->
|
||||
let possible_errors = Json.(member "error" fixture |> to_list) in
|
||||
let expected_error =
|
||||
possible_errors |> List.hd |> Json.to_int |> Int32.of_int
|
||||
in
|
||||
let wire = Json.(fixture |> member "wire" |> to_string) in
|
||||
P.parse_error wire (function
|
||||
| Error.ConnectionError (e, _) | Error.StreamError (_, e) ->
|
||||
Alcotest.(check int32)
|
||||
"Expected Error Code"
|
||||
expected_error
|
||||
(Error_code.serialize e)) )
|
||||
in
|
||||
List.map
|
||||
(fun (_, files) -> "Error tests", List.map gen_suite files)
|
||||
error_fixtures
|
||||
|
||||
let () = Alcotest.run "lambda-runtime" (success_suites @ error_suites)
|
||||
Loading…
Add table
Add a link
Reference in a new issue