From f5c1bfc232e1ad2cdb8782a4ce111d628ac8937a Mon Sep 17 00:00:00 2001 From: Joe DeVivo Date: Wed, 16 Mar 2016 10:08:23 -0700 Subject: [PATCH] Added tests for RFC-7540 Section 3.5 --- src/http2_connection.erl | 27 ++++++++++--- test/http2_spec_3_5_SUITE.erl | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 test/http2_spec_3_5_SUITE.erl diff --git a/src/http2_connection.erl b/src/http2_connection.erl index 873423f1..2f99b168 100644 --- a/src/http2_connection.erl +++ b/src/http2_connection.erl @@ -1206,8 +1206,9 @@ start_http2_server( }=Conn) -> lager:info("[server] StartHTTP2 settings: ~p", [Http2Settings]), - case sock:recv(Socket, length(?PREFACE), 5000) of - {ok, <>} -> + + case accept_preface(Socket) of + ok -> ok = active_once(Socket), NewState = Conn#connection{ @@ -1219,9 +1220,25 @@ start_http2_server( handshake, send_settings(Http2Settings, NewState) }; - BadPreface -> - lager:debug("[server] Bad Preface: ~p", [BadPreface]), - go_away(?PROTOCOL_ERROR, Conn) + {error, invalid_preface} -> + lager:debug("[server] Invalid Preface"), + {next_state, closing, Conn} + end. + +%% We're going to iterate through the preface string until we're done +%% or hit a mismatch +accept_preface(Socket) -> + accept_preface(Socket, <>). + +accept_preface(_Socket, <<>>) -> + ok; +accept_preface(Socket, <>) -> + case sock:recv(Socket, 1, 5000) of + {ok, <>} -> + accept_preface(Socket, Rem); + _E -> + sock:close(Socket), + {error, invalid_preface} end. %% Incoming data is a series of frames. With a passive socket we can just: diff --git a/test/http2_spec_3_5_SUITE.erl b/test/http2_spec_3_5_SUITE.erl new file mode 100644 index 00000000..ad88757e --- /dev/null +++ b/test/http2_spec_3_5_SUITE.erl @@ -0,0 +1,75 @@ +-module(http2_spec_3_5_SUITE). + +-include("http2.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). +-compile([export_all]). + +all() -> + [ + sends_invalid_connection_preface, + sends_incomplete_connection_preface + ]. + +init_per_suite(Config) -> + application:ensure_started(crypto), + chatterbox_test_buddy:start(Config). + +end_per_suite(Config) -> + chatterbox_test_buddy:stop(Config), + ok. + +%% Inspired by https://github.com/summerwind/h2spec/blob/master/3_5.go +sends_invalid_connection_preface(Config) -> + %% Preface correct except for last character + send_invalid_connection_preface(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\rQ">>, Config), + %% Preface incorrect at first character + send_invalid_connection_preface(<<"QRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>, Config), + %% Just plain wrong + send_invalid_connection_preface(<<"INVALID CONNECTION PREFACE\r\n\r\n">>, Config), + ok. + +send_invalid_connection_preface(Preface, _Config) -> + {ok, Port} = application:get_env(chatterbox, port), + ClientOptions = [ + binary, + {packet, raw}, + {active, false} + ], + {ok, SSLOptions} = application:get_env(chatterbox, ssl_options), + Options = ClientOptions ++ SSLOptions ++ [{client_preferred_next_protocols, {client, [<<"h2">>]}}], + + {ok, Socket} = ssl:connect("localhost", Port, Options), + + ssl:send(Socket, Preface), + + ssl:recv(Socket, 0, 1000), + + {error, closed} = ssl:send(Socket, <<"something else">>), + {error, closed} = ssl:connection_information(Socket), + ok. + +sends_incomplete_connection_preface(_Config) -> + {ok, Port} = application:get_env(chatterbox, port), + ClientOptions = [ + binary, + {packet, raw}, + {active, false} + ], + {ok, SSLOptions} = application:get_env(chatterbox, ssl_options), + Options = ClientOptions ++ SSLOptions ++ [{client_preferred_next_protocols, {client, [<<"h2">>]}}], + + {ok, Socket} = ssl:connect("localhost", Port, Options), + + ssl:send(Socket, <<"PRI * HTTP/2.0">>), + + ssl:recv(Socket, 0, 1000), + + {ok, _ConnectionInfo} = ssl:connection_information(Socket), + + %% There's a 5 second timeout before the socket will be closed + ssl:recv(Socket, 0, 5000), + + {error, closed} = ssl:send(Socket, <<"something else">>), + {error, closed} = ssl:connection_information(Socket), + ok.