From d0f711a61d54e3286b71017d20a9cc8fe1eff7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 22 Sep 2011 21:33:56 +0200 Subject: Add a test for websocket hibernate + timeout and fix this use case The issue was that we were calling erlang:hibernate before a receive .. after .. end call. Erlang hibernates the process before reaching the receive instruction and we therefore couldn't enter the after clause when hibernating. This is now fixed by using erlang:send_after instead and receiving that message instead of using an after clause. --- test/http_SUITE.erl | 33 +++++++++++++++++++++++++++++++-- test/ws_timeout_hibernate_handler.erl | 29 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 test/ws_timeout_hibernate_handler.erl (limited to 'test') diff --git a/test/http_SUITE.erl b/test/http_SUITE.erl index 8708824..813aa15 100644 --- a/test/http_SUITE.erl +++ b/test/http_SUITE.erl @@ -19,7 +19,8 @@ -export([all/0, groups/0, init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2]). %% ct. -export([chunked_response/1, headers_dupe/1, headers_huge/1, - keepalive_nl/1, nc_rand/1, pipeline/1, raw/1, ws0/1, ws8/1]). %% http. + keepalive_nl/1, nc_rand/1, pipeline/1, raw/1, + ws0/1, ws8/1, ws_timeout_hibernate/1]). %% http. -export([http_200/1, http_404/1]). %% http and https. -export([http_10_hostless/1]). %% misc. @@ -31,7 +32,8 @@ all() -> groups() -> BaseTests = [http_200, http_404], [{http, [], [chunked_response, headers_dupe, headers_huge, - keepalive_nl, nc_rand, pipeline, raw, ws0, ws8] ++ BaseTests}, + keepalive_nl, nc_rand, pipeline, raw, + ws0, ws8, ws_timeout_hibernate] ++ BaseTests}, {https, [], BaseTests}, {misc, [], [http_10_hostless]}]. init_per_suite(Config) -> @@ -90,6 +92,7 @@ init_http_dispatch() -> {[<<"localhost">>], [ {[<<"chunked_response">>], chunked_handler, []}, {[<<"websocket">>], websocket_handler, []}, + {[<<"ws_timeout_hibernate">>], ws_timeout_hibernate_handler, []}, {[<<"headers">>, <<"dupe">>], http_handler, [{headers, [{<<"Connection">>, <<"close">>}]}]}, {[], http_handler, []} @@ -297,6 +300,32 @@ ws8(Config) -> {error, closed} = gen_tcp:recv(Socket, 0, 6000), ok. +ws_timeout_hibernate(Config) -> + {port, Port} = lists:keyfind(port, 1, Config), + {ok, Socket} = gen_tcp:connect("localhost", Port, + [binary, {active, false}, {packet, raw}]), + ok = gen_tcp:send(Socket, [ + "GET /ws_timeout_hibernate HTTP/1.1\r\n" + "Host: localhost\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Origin: http://localhost\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "\r\n"]), + {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), + {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} + = erlang:decode_packet(http, Handshake, []), + [Headers, <<>>] = websocket_headers( + erlang:decode_packet(httph, Rest, []), []), + {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), + {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), + {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} + = lists:keyfind("sec-websocket-accept", 1, Headers), + {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), + {error, closed} = gen_tcp:recv(Socket, 0, 6000), + ok. + websocket_headers({ok, http_eoh, Rest}, Acc) -> [Acc, Rest]; websocket_headers({ok, {http_header, _I, Key, _R, Value}, Rest}, Acc) -> diff --git a/test/ws_timeout_hibernate_handler.erl b/test/ws_timeout_hibernate_handler.erl new file mode 100644 index 0000000..777948a --- /dev/null +++ b/test/ws_timeout_hibernate_handler.erl @@ -0,0 +1,29 @@ +%% Feel free to use, reuse and abuse the code in this file. + +-module(ws_timeout_hibernate_handler). +-behaviour(cowboy_http_handler). +-behaviour(cowboy_http_websocket_handler). +-export([init/3, handle/2, terminate/2]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). + +init(_Any, _Req, _Opts) -> + {upgrade, protocol, cowboy_http_websocket}. + +handle(_Req, _State) -> + exit(badarg). + +terminate(_Req, _State) -> + exit(badarg). + +websocket_init(_TransportName, Req, _Opts) -> + {ok, Req, undefined, 1000, hibernate}. + +websocket_handle(_Frame, Req, State) -> + {ok, Req, State, hibernate}. + +websocket_info(_Info, Req, State) -> + {ok, Req, State, hibernate}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. -- cgit v1.2.3