From 80e798fa47f41b28902e8f615ccc3b709a33bb83 Mon Sep 17 00:00:00 2001 From: Joe DeVivo Date: Sat, 9 May 2015 13:28:42 -0700 Subject: [PATCH] Passing test for MAX_FRAME_SIZE --- common_test/http2_frame_size_SUITE.erl | 6 +++- include/http2.hrl | 2 +- src/chatterbox.app.src | 8 ++++- src/chatterbox.erl | 22 +++++++++++-- src/chatterbox_fsm.erl | 24 ++++++++++++-- src/http2_frame.erl | 43 ++++++++++++++++---------- src/http2_frame_goaway.erl | 2 +- 7 files changed, 82 insertions(+), 25 deletions(-) diff --git a/common_test/http2_frame_size_SUITE.erl b/common_test/http2_frame_size_SUITE.erl index 4a8dc78a..1029fef7 100644 --- a/common_test/http2_frame_size_SUITE.erl +++ b/common_test/http2_frame_size_SUITE.erl @@ -26,8 +26,12 @@ frame_too_big(Config) -> %% How do I get the response? Should be GOAWAY with FRAME_SIZE_ERROR timer:sleep(10000), - Resp = http2c:get_frames(Client, 3), + Resp = http2c:get_frames(Client, 0), ct:pal("Resp: ~p", [Resp]), ?assertEqual(1, length(Resp)), + + [{_GoAwayH, GoAway}] = Resp, + ?FRAME_SIZE_ERROR = GoAway#goaway.error_code, + ok. diff --git a/include/http2.hrl b/include/http2.hrl index 1e023b16..8195b066 100644 --- a/include/http2.hrl +++ b/include/http2.hrl @@ -137,7 +137,7 @@ -record(goaway, { last_stream_id :: stream_id(), error_code :: error_code(), - additional_debug_data :: binary() + additional_debug_data = <<>> :: binary() }). -type goaway() :: #goaway{}. diff --git a/src/chatterbox.app.src b/src/chatterbox.app.src index be793fd7..e5e85d1a 100644 --- a/src/chatterbox.app.src +++ b/src/chatterbox.app.src @@ -15,7 +15,13 @@ {mod, { chatterbox_app, []}}, {env, [ {port, 80}, - {concurrent_acceptors, 20} + {concurrent_acceptors, 20}, + {header_table_size, 4096}, + {enable_push, 1}, + {max_concurrent_streams, unlimited}, + {initial_window_size, 65535}, + {max_frame_size, 16384}, + {max_header_list_size, unlimited} ]} ]}. %% vim: set filetype=erlang tabstop=2 diff --git a/src/chatterbox.erl b/src/chatterbox.erl index fd06128d..3b28fb00 100644 --- a/src/chatterbox.erl +++ b/src/chatterbox.erl @@ -1,6 +1,22 @@ -module(chatterbox). --export([hello/0]). +-include("http2.hrl"). -hello() -> - howdy. +-export([settings/0]). + +settings() -> + {ok, HTS} = application:get_env(?MODULE, header_table_size), + {ok, EP} = application:get_env(?MODULE, enable_push), + {ok, MCS} = application:get_env(?MODULE, max_concurrent_streams), + {ok, IWS} = application:get_env(?MODULE, initial_window_size), + {ok, MFS} = application:get_env(?MODULE, max_frame_size), + {ok, MHLS} = application:get_env(?MODULE, max_header_list_size), + + #settings{ + header_table_size=HTS, + enable_push=EP, + max_concurrent_streams=MCS, + initial_window_size=IWS, + max_frame_size=MFS, + max_header_list_size=MHLS + }. diff --git a/src/chatterbox_fsm.erl b/src/chatterbox_fsm.erl index 1b15d1e0..ae86d473 100644 --- a/src/chatterbox_fsm.erl +++ b/src/chatterbox_fsm.erl @@ -9,7 +9,7 @@ frame_backlog = [], settings_backlog = [], client_settings = undefined, - server_settings = #settings{}, + server_settings = chatterbox:settings() ::settings(), next_available_stream_id = 2 :: stream_id(), streams = [] :: [proplists:property()], decode_context = hpack:new_decode_context() :: hpack:decode_context(), @@ -30,7 +30,9 @@ -export([accept/2, settings_handshake/2, - connected/2]). + connected/2, + closing/2 + ]). start_link(Socket) -> gen_fsm:start_link(?MODULE, Socket, []). @@ -157,11 +159,29 @@ connected(start_frame, S = #chatterbox_fsm_state{socket=Socket}) -> gen_fsm:send_event(self(), start_frame), Response. +closing(StateName, State) -> + lager:debug("[closing] ~p", [StateName]), + {next_state, StateName, State}. %% Maybe use something like this for readability later %% -define(SOCKET_PM, #chatterbox_fsm_state{socket=Socket}). -spec route_frame(frame(), #chatterbox_fsm_state{}) -> {next_state, connected, #chatterbox_fsm_state{}}. +route_frame({#frame_header{length=L}, _}, + S = #chatterbox_fsm_state{socket={T,Socket}, + server_settings=#settings{max_frame_size=MFS}, + next_available_stream_id=NAS}) + when L > MFS -> + GoAway = #goaway{ + last_stream_id=NAS, + error_code=?FRAME_SIZE_ERROR + }, + GoAwayBin = http2_frame:to_binary({#frame_header{ + stream_id=0 + }, GoAway}), + T:send(Socket, GoAwayBin), + {next_state, closing, S}; + route_frame({H=#frame_header{stream_id=StreamId}, _Payload}, S = #chatterbox_fsm_state{socket=_Socket}) when H#frame_header.type == ?DATA -> lager:debug("Received DATA Frame for Stream ~p", [StreamId]), diff --git a/src/http2_frame.erl b/src/http2_frame.erl index df1bda1d..0b84ee9b 100644 --- a/src/http2_frame.erl +++ b/src/http2_frame.erl @@ -33,10 +33,9 @@ -spec read(socket()) -> {frame_header(), payload()}. read(Socket) -> {H, <<>>} = read_header(Socket), - lager:debug("HeaderBytes: ~p", [H]), + %%lager:debug("HeaderBytes: ~p", [H]), {ok, Payload, <<>>} = read_payload(Socket, H), - lager:debug("PayloadBytes: ~p", [Payload]), - + %%lager:debug("PayloadBytes: ~p", [Payload]), lager:debug(format({H, Payload})), {H, Payload}. @@ -136,9 +135,13 @@ format(<<>>) -> "". -spec to_binary(frame()) -> iodata(). to_binary({Header, Payload}) -> - HeaderBin = header_to_binary(Header), + {Type, PayloadBin} = payload_to_binary(Payload), + NewHeader = Header#frame_header{ + length = iodata_size(PayloadBin), + type = Type + }, + HeaderBin = header_to_binary(NewHeader), lager:debug("HeaderBin: ~p", [HeaderBin]), - PayloadBin = payload_to_binary(Payload), lager:debug("PayloadBin: ~p", [PayloadBin]), [HeaderBin, PayloadBin]. @@ -149,16 +152,24 @@ header_to_binary(#frame_header{ flags=F, stream_id=StreamId }) -> + lager:debug("H: ~p", [L]), <>. --spec payload_to_binary(payload()) -> iodata(). -payload_to_binary(P=#data{}) -> http2_frame_data:to_binary(P); -payload_to_binary(P=#headers{}) -> http2_frame_headers:to_binary(P); -payload_to_binary(P=#priority{}) -> http2_frame_priority:to_binary(P); -payload_to_binary(P=#rst_stream{}) -> http2_frame_rst_stream:to_binary(P); -payload_to_binary(P=#settings{}) -> http2_frame_settings:to_binary(P); -payload_to_binary(P=#push_promise{}) -> http2_frame_push_promise:to_binary(P); -payload_to_binary(P=#ping{}) -> http2_frame_ping:to_binary(P); -payload_to_binary(P=#goaway{}) -> http2_frame_goaway:to_binary(P); -payload_to_binary(P=#window_update{}) -> http2_frame_window_update:to_binary(P); -payload_to_binary(P=#continuation{}) -> http2_frame_continuation:to_binary(P). +-spec payload_to_binary(payload()) -> {frame_type(), iodata()}. +payload_to_binary(P=#data{}) -> {?DATA, http2_frame_data:to_binary(P)}; +payload_to_binary(P=#headers{}) -> {?HEADERS, http2_frame_headers:to_binary(P)}; +payload_to_binary(P=#priority{}) -> {?PRIORITY, http2_frame_priority:to_binary(P)}; +payload_to_binary(P=#rst_stream{}) -> {?RST_STREAM, http2_frame_rst_stream:to_binary(P)}; +payload_to_binary(P=#settings{}) -> {?SETTINGS, http2_frame_settings:to_binary(P)}; +payload_to_binary(P=#push_promise{}) -> {?PUSH_PROMISE, http2_frame_push_promise:to_binary(P)}; +payload_to_binary(P=#ping{}) -> {?PING, http2_frame_ping:to_binary(P)}; +payload_to_binary(P=#goaway{}) -> {?GOAWAY, http2_frame_goaway:to_binary(P)}; +payload_to_binary(P=#window_update{}) -> {?WINDOW_UPDATE, http2_frame_window_update:to_binary(P)}; +payload_to_binary(P=#continuation{}) -> {?CONTINUATION, http2_frame_continuation:to_binary(P)}. + +iodata_size(L) when is_list(L) -> + lists:foldl(fun(X, Acc) -> + Acc + iodata_size(X) + end, 0, L); +iodata_size(B) when is_binary(B) -> + byte_size(B). diff --git a/src/http2_frame_goaway.erl b/src/http2_frame_goaway.erl index 27bd88a8..2125cf52 100644 --- a/src/http2_frame_goaway.erl +++ b/src/http2_frame_goaway.erl @@ -32,4 +32,4 @@ to_binary(#goaway{ error_code=EC, additional_debug_data=ADD }) -> - <<0:1,LSID:31,EC:32,ADD>>. + [<<0:1,LSID:31,EC:32>>,ADD].