/test/ws_SUITE.erl
Erlang | 579 lines | 516 code | 31 blank | 32 comment | 1 complexity | 2361f12f6d6185b760368523b171dd21 MD5 | raw file
1%% Copyright (c) 2011-2013, Lo誰c Hoguin <essen@ninenines.eu> 2%% 3%% Permission to use, copy, modify, and/or distribute this software for any 4%% purpose with or without fee is hereby granted, provided that the above 5%% copyright notice and this permission notice appear in all copies. 6%% 7%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15-module(ws_SUITE). 16 17-include_lib("common_test/include/ct.hrl"). 18 19%% ct. 20-export([all/0]). 21-export([groups/0]). 22-export([init_per_suite/1]). 23-export([end_per_suite/1]). 24-export([init_per_group/2]). 25-export([end_per_group/2]). 26 27%% Tests. 28-export([ws0/1]). 29-export([ws8/1]). 30-export([ws8_init_shutdown/1]). 31-export([ws8_single_bytes/1]). 32-export([ws13/1]). 33-export([ws_send_close/1]). 34-export([ws_send_close_payload/1]). 35-export([ws_send_many/1]). 36-export([ws_text_fragments/1]). 37-export([ws_timeout_hibernate/1]). 38-export([ws_timeout_cancel/1]). 39-export([ws_timeout_reset/1]). 40-export([ws_upgrade_with_opts/1]). 41 42%% ct. 43 44all() -> 45 [{group, ws}]. 46 47groups() -> 48 BaseTests = [ 49 ws0, 50 ws8, 51 ws8_init_shutdown, 52 ws8_single_bytes, 53 ws13, 54 ws_send_close, 55 ws_send_close_payload, 56 ws_send_many, 57 ws_text_fragments, 58 ws_timeout_hibernate, 59 ws_timeout_cancel, 60 ws_timeout_reset, 61 ws_upgrade_with_opts 62 ], 63 [{ws, [], BaseTests}]. 64 65init_per_suite(Config) -> 66 application:start(crypto), 67 application:start(ranch), 68 application:start(cowboy), 69 Config. 70 71end_per_suite(_Config) -> 72 application:stop(cowboy), 73 application:stop(ranch), 74 application:stop(crypto), 75 ok. 76 77init_per_group(ws, Config) -> 78 Port = 33080, 79 cowboy:start_http(ws, 100, [{port, Port}], [ 80 {env, [{dispatch, init_dispatch()}]} 81 ]), 82 [{port, Port}|Config]. 83 84end_per_group(Listener, _Config) -> 85 cowboy:stop_listener(Listener), 86 ok. 87 88%% Dispatch configuration. 89 90init_dispatch() -> 91 cowboy_router:compile([ 92 {"localhost", [ 93 {"/websocket", websocket_handler, []}, 94 {"/ws_echo_handler", websocket_echo_handler, []}, 95 {"/ws_init_shutdown", websocket_handler_init_shutdown, []}, 96 {"/ws_send_many", ws_send_many_handler, [ 97 {sequence, [ 98 {text, <<"one">>}, 99 {text, <<"two">>}, 100 {text, <<"seven!">>}]} 101 ]}, 102 {"/ws_send_close", ws_send_many_handler, [ 103 {sequence, [ 104 {text, <<"send">>}, 105 close, 106 {text, <<"won't be received">>}]} 107 ]}, 108 {"/ws_send_close_payload", ws_send_many_handler, [ 109 {sequence, [ 110 {text, <<"send">>}, 111 {close, 1001, <<"some text!">>}, 112 {text, <<"won't be received">>}]} 113 ]}, 114 {"/ws_timeout_hibernate", ws_timeout_hibernate_handler, []}, 115 {"/ws_timeout_cancel", ws_timeout_cancel_handler, []}, 116 {"/ws_upgrade_with_opts", ws_upgrade_with_opts_handler, 117 <<"failure">>} 118 ]} 119 ]). 120 121%% ws and wss. 122 123%% We do not support hixie76 anymore. 124ws0(Config) -> 125 {port, Port} = lists:keyfind(port, 1, Config), 126 {ok, Socket} = gen_tcp:connect("localhost", Port, 127 [binary, {active, false}, {packet, raw}]), 128 ok = gen_tcp:send(Socket, 129 "GET /websocket HTTP/1.1\r\n" 130 "Host: localhost\r\n" 131 "Connection: Upgrade\r\n" 132 "Upgrade: WebSocket\r\n" 133 "Origin: http://localhost\r\n" 134 "Sec-Websocket-Key1: Y\" 4 1Lj!957b8@0H756!i\r\n" 135 "Sec-Websocket-Key2: 1711 M;4\\74 80<6\r\n" 136 "\r\n"), 137 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 138 {ok, {http_response, {1, 1}, 400, _}, _} 139 = erlang:decode_packet(http, Handshake, []). 140 141ws8(Config) -> 142 {port, Port} = lists:keyfind(port, 1, Config), 143 {ok, Socket} = gen_tcp:connect("localhost", Port, 144 [binary, {active, false}, {packet, raw}]), 145 ok = gen_tcp:send(Socket, [ 146 "GET /websocket HTTP/1.1\r\n" 147 "Host: localhost\r\n" 148 "Connection: Upgrade\r\n" 149 "Upgrade: websocket\r\n" 150 "Sec-WebSocket-Origin: http://localhost\r\n" 151 "Sec-WebSocket-Version: 8\r\n" 152 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 153 "\r\n"]), 154 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 155 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 156 = erlang:decode_packet(http, Handshake, []), 157 [Headers, <<>>] = websocket_headers( 158 erlang:decode_packet(httph, Rest, []), []), 159 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 160 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 161 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 162 = lists:keyfind("sec-websocket-accept", 1, Headers), 163 ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d, 164 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>), 165 {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} 166 = gen_tcp:recv(Socket, 0, 6000), 167 {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>} 168 = gen_tcp:recv(Socket, 0, 6000), 169 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 170 = gen_tcp:recv(Socket, 0, 6000), 171 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 172 = gen_tcp:recv(Socket, 0, 6000), 173 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 174 = gen_tcp:recv(Socket, 0, 6000), 175 ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 1:1, 0:7, 0:32 >>), %% ping 176 {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong 177 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 178 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 179 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 180 ok. 181 182ws8_init_shutdown(Config) -> 183 {port, Port} = lists:keyfind(port, 1, Config), 184 {ok, Socket} = gen_tcp:connect("localhost", Port, 185 [binary, {active, false}, {packet, raw}]), 186 ok = gen_tcp:send(Socket, [ 187 "GET /ws_init_shutdown HTTP/1.1\r\n" 188 "Host: localhost\r\n" 189 "Connection: Upgrade\r\n" 190 "Upgrade: websocket\r\n" 191 "Sec-WebSocket-Origin: http://localhost\r\n" 192 "Sec-WebSocket-Version: 8\r\n" 193 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 194 "\r\n"]), 195 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 196 {ok, {http_response, {1, 1}, 403, "Forbidden"}, _Rest} 197 = erlang:decode_packet(http, Handshake, []), 198 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 199 ok. 200 201ws8_single_bytes(Config) -> 202 {port, Port} = lists:keyfind(port, 1, Config), 203 {ok, Socket} = gen_tcp:connect("localhost", Port, 204 [binary, {active, false}, {packet, raw}]), 205 ok = gen_tcp:send(Socket, [ 206 "GET /websocket HTTP/1.1\r\n" 207 "Host: localhost\r\n" 208 "Connection: Upgrade\r\n" 209 "Upgrade: websocket\r\n" 210 "Sec-WebSocket-Origin: http://localhost\r\n" 211 "Sec-WebSocket-Version: 8\r\n" 212 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 213 "\r\n"]), 214 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 215 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 216 = erlang:decode_packet(http, Handshake, []), 217 [Headers, <<>>] = websocket_headers( 218 erlang:decode_packet(httph, Rest, []), []), 219 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 220 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 221 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 222 = lists:keyfind("sec-websocket-accept", 1, Headers), 223 ok = gen_tcp:send(Socket, << 16#81 >>), %% send one byte 224 ok = timer:sleep(100), %% sleep for a period 225 ok = gen_tcp:send(Socket, << 16#85 >>), %% send another and so on 226 ok = timer:sleep(100), 227 ok = gen_tcp:send(Socket, << 16#37 >>), 228 ok = timer:sleep(100), 229 ok = gen_tcp:send(Socket, << 16#fa >>), 230 ok = timer:sleep(100), 231 ok = gen_tcp:send(Socket, << 16#21 >>), 232 ok = timer:sleep(100), 233 ok = gen_tcp:send(Socket, << 16#3d >>), 234 ok = timer:sleep(100), 235 ok = gen_tcp:send(Socket, << 16#7f >>), 236 ok = timer:sleep(100), 237 ok = gen_tcp:send(Socket, << 16#9f >>), 238 ok = timer:sleep(100), 239 ok = gen_tcp:send(Socket, << 16#4d >>), 240 ok = timer:sleep(100), 241 ok = gen_tcp:send(Socket, << 16#51 >>), 242 ok = timer:sleep(100), 243 ok = gen_tcp:send(Socket, << 16#58 >>), 244 {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>} 245 = gen_tcp:recv(Socket, 0, 6000), 246 {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} 247 = gen_tcp:recv(Socket, 0, 6000), 248 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 249 = gen_tcp:recv(Socket, 0, 6000), 250 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 251 = gen_tcp:recv(Socket, 0, 6000), 252 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 253 = gen_tcp:recv(Socket, 0, 6000), 254 ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 1:1, 0:7, 0:32 >>), %% ping 255 {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong 256 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 257 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 258 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 259 ok. 260 261ws13(Config) -> 262 {port, Port} = lists:keyfind(port, 1, Config), 263 {ok, Socket} = gen_tcp:connect("localhost", Port, 264 [binary, {active, false}, {packet, raw}]), 265 ok = gen_tcp:send(Socket, [ 266 "GET /websocket HTTP/1.1\r\n" 267 "Host: localhost\r\n" 268 "Connection: Upgrade\r\n" 269 "Origin: http://localhost\r\n" 270 "Sec-WebSocket-Version: 13\r\n" 271 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 272 "Upgrade: websocket\r\n" 273 "\r\n"]), 274 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 275 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 276 = erlang:decode_packet(http, Handshake, []), 277 [Headers, <<>>] = websocket_headers( 278 erlang:decode_packet(httph, Rest, []), []), 279 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 280 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 281 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 282 = lists:keyfind("sec-websocket-accept", 1, Headers), 283 %% text 284 ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d, 285 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>), 286 {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} 287 = gen_tcp:recv(Socket, 0, 6000), 288 %% binary (empty) 289 ok = gen_tcp:send(Socket, << 1:1, 0:3, 2:4, 1:1, 0:7, 0:32 >>), 290 {ok, << 1:1, 0:3, 2:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 291 %% binary 292 ok = gen_tcp:send(Socket, << 16#82, 16#85, 16#37, 16#fa, 16#21, 16#3d, 293 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>), 294 {ok, << 1:1, 0:3, 2:4, 0:1, 5:7, "Hello" >>} 295 = gen_tcp:recv(Socket, 0, 6000), 296 %% Receives. 297 {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>} 298 = gen_tcp:recv(Socket, 0, 6000), 299 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 300 = gen_tcp:recv(Socket, 0, 6000), 301 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 302 = gen_tcp:recv(Socket, 0, 6000), 303 {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>} 304 = gen_tcp:recv(Socket, 0, 6000), 305 ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 1:1, 0:7, 0:32 >>), %% ping 306 {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong 307 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 308 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 309 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 310 ok. 311 312ws_send_close(Config) -> 313 {port, Port} = lists:keyfind(port, 1, Config), 314 {ok, Socket} = gen_tcp:connect("localhost", Port, 315 [binary, {active, false}, {packet, raw}]), 316 ok = gen_tcp:send(Socket, [ 317 "GET /ws_send_close HTTP/1.1\r\n" 318 "Host: localhost\r\n" 319 "Connection: Upgrade\r\n" 320 "Upgrade: websocket\r\n" 321 "Sec-WebSocket-Origin: http://localhost\r\n" 322 "Sec-WebSocket-Version: 8\r\n" 323 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 324 "\r\n"]), 325 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 326 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 327 = erlang:decode_packet(http, Handshake, []), 328 [Headers, <<>>] = websocket_headers( 329 erlang:decode_packet(httph, Rest, []), []), 330 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 331 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 332 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 333 = lists:keyfind("sec-websocket-accept", 1, Headers), 334 %% We catch all frames at once and check them directly. 335 {ok, Many} = gen_tcp:recv(Socket, 8, 6000), 336 << 1:1, 0:3, 1:4, 0:1, 4:7, "send", 337 1:1, 0:3, 8:4, 0:8 >> = Many, 338 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 339 ok. 340 341ws_send_close_payload(Config) -> 342 {port, Port} = lists:keyfind(port, 1, Config), 343 {ok, Socket} = gen_tcp:connect("localhost", Port, 344 [binary, {active, false}, {packet, raw}]), 345 ok = gen_tcp:send(Socket, [ 346 "GET /ws_send_close_payload HTTP/1.1\r\n" 347 "Host: localhost\r\n" 348 "Connection: Upgrade\r\n" 349 "Upgrade: websocket\r\n" 350 "Sec-WebSocket-Origin: http://localhost\r\n" 351 "Sec-WebSocket-Version: 8\r\n" 352 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 353 "\r\n"]), 354 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 355 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 356 = erlang:decode_packet(http, Handshake, []), 357 [Headers, <<>>] = websocket_headers( 358 erlang:decode_packet(httph, Rest, []), []), 359 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 360 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 361 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 362 = lists:keyfind("sec-websocket-accept", 1, Headers), 363 %% We catch all frames at once and check them directly. 364 {ok, Many} = gen_tcp:recv(Socket, 20, 6000), 365 << 1:1, 0:3, 1:4, 0:1, 4:7, "send", 366 1:1, 0:3, 8:4, 0:1, 12:7, 1001:16, "some text!" >> = Many, 367 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 368 ok. 369 370ws_send_many(Config) -> 371 {port, Port} = lists:keyfind(port, 1, Config), 372 {ok, Socket} = gen_tcp:connect("localhost", Port, 373 [binary, {active, false}, {packet, raw}]), 374 ok = gen_tcp:send(Socket, [ 375 "GET /ws_send_many HTTP/1.1\r\n" 376 "Host: localhost\r\n" 377 "Connection: Upgrade\r\n" 378 "Upgrade: websocket\r\n" 379 "Sec-WebSocket-Origin: http://localhost\r\n" 380 "Sec-WebSocket-Version: 8\r\n" 381 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 382 "\r\n"]), 383 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 384 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 385 = erlang:decode_packet(http, Handshake, []), 386 [Headers, <<>>] = websocket_headers( 387 erlang:decode_packet(httph, Rest, []), []), 388 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 389 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 390 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 391 = lists:keyfind("sec-websocket-accept", 1, Headers), 392 %% We catch all frames at once and check them directly. 393 {ok, Many} = gen_tcp:recv(Socket, 18, 6000), 394 << 1:1, 0:3, 1:4, 0:1, 3:7, "one", 395 1:1, 0:3, 1:4, 0:1, 3:7, "two", 396 1:1, 0:3, 1:4, 0:1, 6:7, "seven!" >> = Many, 397 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 398 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 399 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 400 ok. 401 402ws_text_fragments(Config) -> 403 {port, Port} = lists:keyfind(port, 1, Config), 404 {ok, Socket} = gen_tcp:connect("localhost", Port, 405 [binary, {active, false}, {packet, raw}]), 406 ok = gen_tcp:send(Socket, [ 407 "GET /ws_echo_handler HTTP/1.1\r\n" 408 "Host: localhost\r\n" 409 "Connection: Upgrade\r\n" 410 "Upgrade: websocket\r\n" 411 "Sec-WebSocket-Origin: http://localhost\r\n" 412 "Sec-WebSocket-Version: 8\r\n" 413 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 414 "\r\n"]), 415 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 416 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 417 = erlang:decode_packet(http, Handshake, []), 418 [Headers, <<>>] = websocket_headers( 419 erlang:decode_packet(httph, Rest, []), []), 420 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 421 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 422 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 423 = lists:keyfind("sec-websocket-accept", 1, Headers), 424 425 ok = gen_tcp:send(Socket, [ 426 << 0:1, 0:3, 1:4, 1:1, 5:7 >>, 427 << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, 428 << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]), 429 ok = gen_tcp:send(Socket, [ 430 << 1:1, 0:3, 0:4, 1:1, 5:7 >>, 431 << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, 432 << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]), 433 {ok, << 1:1, 0:3, 1:4, 0:1, 10:7, "HelloHello" >>} 434 = gen_tcp:recv(Socket, 0, 6000), 435 436 ok = gen_tcp:send(Socket, [ 437 %% #1 438 << 0:1, 0:3, 1:4, 1:1, 5:7 >>, 439 << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, 440 << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>, 441 %% #2 442 << 0:1, 0:3, 0:4, 1:1, 5:7 >>, 443 << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, 444 << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>, 445 %% #3 446 << 1:1, 0:3, 0:4, 1:1, 5:7 >>, 447 << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>, 448 << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]), 449 {ok, << 1:1, 0:3, 1:4, 0:1, 15:7, "HelloHelloHello" >>} 450 = gen_tcp:recv(Socket, 0, 6000), 451 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 452 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 453 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 454 ok. 455 456ws_timeout_hibernate(Config) -> 457 {port, Port} = lists:keyfind(port, 1, Config), 458 {ok, Socket} = gen_tcp:connect("localhost", Port, 459 [binary, {active, false}, {packet, raw}]), 460 ok = gen_tcp:send(Socket, [ 461 "GET /ws_timeout_hibernate HTTP/1.1\r\n" 462 "Host: localhost\r\n" 463 "Connection: Upgrade\r\n" 464 "Upgrade: websocket\r\n" 465 "Sec-WebSocket-Origin: http://localhost\r\n" 466 "Sec-WebSocket-Version: 8\r\n" 467 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 468 "\r\n"]), 469 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 470 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 471 = erlang:decode_packet(http, Handshake, []), 472 [Headers, <<>>] = websocket_headers( 473 erlang:decode_packet(httph, Rest, []), []), 474 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 475 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 476 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 477 = lists:keyfind("sec-websocket-accept", 1, Headers), 478 {ok, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>} = gen_tcp:recv(Socket, 0, 6000), 479 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 480 ok. 481 482ws_timeout_cancel(Config) -> 483 %% Erlang messages to a socket should not cancel the timeout 484 {port, Port} = lists:keyfind(port, 1, Config), 485 {ok, Socket} = gen_tcp:connect("localhost", Port, 486 [binary, {active, false}, {packet, raw}]), 487 ok = gen_tcp:send(Socket, [ 488 "GET /ws_timeout_cancel HTTP/1.1\r\n" 489 "Host: localhost\r\n" 490 "Connection: Upgrade\r\n" 491 "Upgrade: websocket\r\n" 492 "Sec-WebSocket-Origin: http://localhost\r\n" 493 "Sec-WebSocket-Version: 8\r\n" 494 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 495 "\r\n"]), 496 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 497 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 498 = erlang:decode_packet(http, Handshake, []), 499 [Headers, <<>>] = websocket_headers( 500 erlang:decode_packet(httph, Rest, []), []), 501 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 502 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 503 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 504 = lists:keyfind("sec-websocket-accept", 1, Headers), 505 {ok, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>} = gen_tcp:recv(Socket, 0, 6000), 506 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 507 ok. 508 509ws_timeout_reset(Config) -> 510 %% Erlang messages across a socket should reset the timeout 511 {port, Port} = lists:keyfind(port, 1, Config), 512 {ok, Socket} = gen_tcp:connect("localhost", Port, 513 [binary, {active, false}, {packet, raw}]), 514 ok = gen_tcp:send(Socket, [ 515 "GET /ws_timeout_cancel HTTP/1.1\r\n" 516 "Host: localhost\r\n" 517 "Connection: Upgrade\r\n" 518 "Upgrade: websocket\r\n" 519 "Sec-WebSocket-Origin: http://localhost\r\n" 520 "Sec-Websocket-Version: 13\r\n" 521 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 522 "\r\n"]), 523 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 524 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 525 = erlang:decode_packet(http, Handshake, []), 526 [Headers, <<>>] = websocket_headers( 527 erlang:decode_packet(httph, Rest, []), []), 528 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 529 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 530 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 531 = lists:keyfind("sec-websocket-accept", 1, Headers), 532 [begin 533 ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d, 534 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>), 535 {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>} 536 = gen_tcp:recv(Socket, 0, 6000), 537 ok = timer:sleep(500) 538 end || _ <- [1, 2, 3, 4]], 539 {ok, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>} = gen_tcp:recv(Socket, 0, 6000), 540 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 541 ok. 542 543ws_upgrade_with_opts(Config) -> 544 {port, Port} = lists:keyfind(port, 1, Config), 545 {ok, Socket} = gen_tcp:connect("localhost", Port, 546 [binary, {active, false}, {packet, raw}]), 547 ok = gen_tcp:send(Socket, [ 548 "GET /ws_upgrade_with_opts HTTP/1.1\r\n" 549 "Host: localhost\r\n" 550 "Connection: Upgrade\r\n" 551 "Upgrade: websocket\r\n" 552 "Sec-WebSocket-Origin: http://localhost\r\n" 553 "Sec-WebSocket-Version: 8\r\n" 554 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 555 "\r\n"]), 556 {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000), 557 {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest} 558 = erlang:decode_packet(http, Handshake, []), 559 [Headers, <<>>] = websocket_headers( 560 erlang:decode_packet(httph, Rest, []), []), 561 {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers), 562 {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers), 563 {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="} 564 = lists:keyfind("sec-websocket-accept", 1, Headers), 565 {ok, Response} = gen_tcp:recv(Socket, 9, 6000), 566 << 1:1, 0:3, 1:4, 0:1, 7:7, "success" >> = Response, 567 ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 1:1, 0:7, 0:32 >>), %% close 568 {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), 569 {error, closed} = gen_tcp:recv(Socket, 0, 6000), 570 ok. 571 572%% Internal. 573 574websocket_headers({ok, http_eoh, Rest}, Acc) -> 575 [Acc, Rest]; 576websocket_headers({ok, {http_header, _I, Key, _R, Value}, Rest}, Acc) -> 577 F = fun(S) when is_atom(S) -> S; (S) -> string:to_lower(S) end, 578 websocket_headers(erlang:decode_packet(httph, Rest, []), 579 [{F(Key), Value}|Acc]).