ws_SUITE.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. %% Copyright (c) 2011, 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. -module(ws_SUITE).
  15. -include_lib("common_test/include/ct.hrl").
  16. -export([all/0, groups/0, init_per_suite/1, end_per_suite/1,
  17. init_per_group/2, end_per_group/2]). %% ct.
  18. -export([ws0/1, ws8/1, ws8_single_bytes/1, ws8_init_shutdown/1,
  19. ws13/1, ws_timeout_hibernate/1, ws_text_fragments/1]). %% ws.
  20. %% ct.
  21. all() ->
  22. [{group, ws}].
  23. groups() ->
  24. BaseTests = [ws0, ws8, ws8_single_bytes, ws8_init_shutdown, ws13,
  25. ws_timeout_hibernate, ws_text_fragments],
  26. [{ws, [], BaseTests}].
  27. init_per_suite(Config) ->
  28. application:start(inets),
  29. application:start(crypto),
  30. application:start(ranch),
  31. application:start(cowboy),
  32. Config.
  33. end_per_suite(_Config) ->
  34. application:stop(cowboy),
  35. application:stop(ranch),
  36. application:stop(crypto),
  37. application:stop(inets),
  38. ok.
  39. init_per_group(ws, Config) ->
  40. Port = 33080,
  41. cowboy:start_http(ws, 100, [{port, Port}], [
  42. {dispatch, init_dispatch()}
  43. ]),
  44. [{port, Port}|Config].
  45. end_per_group(Listener, _Config) ->
  46. cowboy:stop_listener(Listener),
  47. ok.
  48. %% Dispatch configuration.
  49. init_dispatch() ->
  50. [
  51. {[<<"localhost">>], [
  52. {[<<"websocket">>], websocket_handler, []},
  53. {[<<"ws_timeout_hibernate">>], ws_timeout_hibernate_handler, []},
  54. {[<<"ws_init_shutdown">>], websocket_handler_init_shutdown, []},
  55. {[<<"ws_echo_handler">>], websocket_echo_handler, []}
  56. ]}
  57. ].
  58. %% ws and wss.
  59. %% This test makes sure the code works even if we wait for a reply
  60. %% before sending the third challenge key in the GET body.
  61. %%
  62. %% This ensures that Cowboy will work fine with proxies on hixie.
  63. ws0(Config) ->
  64. {port, Port} = lists:keyfind(port, 1, Config),
  65. {ok, Socket} = gen_tcp:connect("localhost", Port,
  66. [binary, {active, false}, {packet, raw}]),
  67. ok = gen_tcp:send(Socket,
  68. "GET /websocket HTTP/1.1\r\n"
  69. "Host: localhost\r\n"
  70. "Connection: Upgrade\r\n"
  71. "Upgrade: WebSocket\r\n"
  72. "Origin: http://localhost\r\n"
  73. "Sec-Websocket-Key1: Y\" 4 1Lj!957b8@0H756!i\r\n"
  74. "Sec-Websocket-Key2: 1711 M;4\\74 80<6\r\n"
  75. "\r\n"),
  76. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  77. {ok, {http_response, {1, 1}, 101, "WebSocket Protocol Handshake"}, Rest}
  78. = erlang:decode_packet(http, Handshake, []),
  79. [Headers, <<>>] = websocket_headers(
  80. erlang:decode_packet(httph, Rest, []), []),
  81. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  82. {'Upgrade', "WebSocket"} = lists:keyfind('Upgrade', 1, Headers),
  83. {"sec-websocket-location", "ws://localhost/websocket"}
  84. = lists:keyfind("sec-websocket-location", 1, Headers),
  85. {"sec-websocket-origin", "http://localhost"}
  86. = lists:keyfind("sec-websocket-origin", 1, Headers),
  87. ok = gen_tcp:send(Socket, <<15,245,8,18,2,204,133,33>>),
  88. {ok, Body} = gen_tcp:recv(Socket, 0, 6000),
  89. <<169,244,191,103,146,33,149,59,74,104,67,5,99,118,171,236>> = Body,
  90. ok = gen_tcp:send(Socket, << 0, "client_msg", 255 >>),
  91. {ok, << 0, "client_msg", 255 >>} = gen_tcp:recv(Socket, 0, 6000),
  92. {ok, << 0, "websocket_init", 255 >>} = gen_tcp:recv(Socket, 0, 6000),
  93. {ok, << 0, "websocket_handle", 255 >>} = gen_tcp:recv(Socket, 0, 6000),
  94. {ok, << 0, "websocket_handle", 255 >>} = gen_tcp:recv(Socket, 0, 6000),
  95. {ok, << 0, "websocket_handle", 255 >>} = gen_tcp:recv(Socket, 0, 6000),
  96. %% We try to send another HTTP request to make sure
  97. %% the server closed the request.
  98. ok = gen_tcp:send(Socket, [
  99. << 255, 0 >>, %% Close websocket command.
  100. "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" %% Server should ignore it.
  101. ]),
  102. {ok, << 255, 0 >>} = gen_tcp:recv(Socket, 0, 6000),
  103. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  104. ok.
  105. ws8(Config) ->
  106. {port, Port} = lists:keyfind(port, 1, Config),
  107. {ok, Socket} = gen_tcp:connect("localhost", Port,
  108. [binary, {active, false}, {packet, raw}]),
  109. ok = gen_tcp:send(Socket, [
  110. "GET /websocket HTTP/1.1\r\n"
  111. "Host: localhost\r\n"
  112. "Connection: Upgrade\r\n"
  113. "Upgrade: websocket\r\n"
  114. "Sec-WebSocket-Origin: http://localhost\r\n"
  115. "Sec-WebSocket-Version: 8\r\n"
  116. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  117. "\r\n"]),
  118. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  119. {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
  120. = erlang:decode_packet(http, Handshake, []),
  121. [Headers, <<>>] = websocket_headers(
  122. erlang:decode_packet(httph, Rest, []), []),
  123. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  124. {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
  125. {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
  126. = lists:keyfind("sec-websocket-accept", 1, Headers),
  127. ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d,
  128. 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>),
  129. {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>}
  130. = gen_tcp:recv(Socket, 0, 6000),
  131. {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>}
  132. = gen_tcp:recv(Socket, 0, 6000),
  133. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  134. = gen_tcp:recv(Socket, 0, 6000),
  135. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  136. = gen_tcp:recv(Socket, 0, 6000),
  137. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  138. = gen_tcp:recv(Socket, 0, 6000),
  139. ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 0:8 >>), %% ping
  140. {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong
  141. ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>), %% close
  142. {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  143. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  144. ok.
  145. ws8_single_bytes(Config) ->
  146. {port, Port} = lists:keyfind(port, 1, Config),
  147. {ok, Socket} = gen_tcp:connect("localhost", Port,
  148. [binary, {active, false}, {packet, raw}]),
  149. ok = gen_tcp:send(Socket, [
  150. "GET /websocket HTTP/1.1\r\n"
  151. "Host: localhost\r\n"
  152. "Connection: Upgrade\r\n"
  153. "Upgrade: websocket\r\n"
  154. "Sec-WebSocket-Origin: http://localhost\r\n"
  155. "Sec-WebSocket-Version: 8\r\n"
  156. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  157. "\r\n"]),
  158. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  159. {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
  160. = erlang:decode_packet(http, Handshake, []),
  161. [Headers, <<>>] = websocket_headers(
  162. erlang:decode_packet(httph, Rest, []), []),
  163. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  164. {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
  165. {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
  166. = lists:keyfind("sec-websocket-accept", 1, Headers),
  167. ok = gen_tcp:send(Socket, << 16#81 >>), %% send one byte
  168. ok = timer:sleep(100), %% sleep for a period
  169. ok = gen_tcp:send(Socket, << 16#85 >>), %% send another and so on
  170. ok = timer:sleep(100),
  171. ok = gen_tcp:send(Socket, << 16#37 >>),
  172. ok = timer:sleep(100),
  173. ok = gen_tcp:send(Socket, << 16#fa >>),
  174. ok = timer:sleep(100),
  175. ok = gen_tcp:send(Socket, << 16#21 >>),
  176. ok = timer:sleep(100),
  177. ok = gen_tcp:send(Socket, << 16#3d >>),
  178. ok = timer:sleep(100),
  179. ok = gen_tcp:send(Socket, << 16#7f >>),
  180. ok = timer:sleep(100),
  181. ok = gen_tcp:send(Socket, << 16#9f >>),
  182. ok = timer:sleep(100),
  183. ok = gen_tcp:send(Socket, << 16#4d >>),
  184. ok = timer:sleep(100),
  185. ok = gen_tcp:send(Socket, << 16#51 >>),
  186. ok = timer:sleep(100),
  187. ok = gen_tcp:send(Socket, << 16#58 >>),
  188. {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>}
  189. = gen_tcp:recv(Socket, 0, 6000),
  190. {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>}
  191. = gen_tcp:recv(Socket, 0, 6000),
  192. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  193. = gen_tcp:recv(Socket, 0, 6000),
  194. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  195. = gen_tcp:recv(Socket, 0, 6000),
  196. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  197. = gen_tcp:recv(Socket, 0, 6000),
  198. ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 0:8 >>), %% ping
  199. {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong
  200. ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>), %% close
  201. {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  202. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  203. ok.
  204. ws_timeout_hibernate(Config) ->
  205. {port, Port} = lists:keyfind(port, 1, Config),
  206. {ok, Socket} = gen_tcp:connect("localhost", Port,
  207. [binary, {active, false}, {packet, raw}]),
  208. ok = gen_tcp:send(Socket, [
  209. "GET /ws_timeout_hibernate HTTP/1.1\r\n"
  210. "Host: localhost\r\n"
  211. "Connection: Upgrade\r\n"
  212. "Upgrade: websocket\r\n"
  213. "Sec-WebSocket-Origin: http://localhost\r\n"
  214. "Sec-WebSocket-Version: 8\r\n"
  215. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  216. "\r\n"]),
  217. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  218. {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
  219. = erlang:decode_packet(http, Handshake, []),
  220. [Headers, <<>>] = websocket_headers(
  221. erlang:decode_packet(httph, Rest, []), []),
  222. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  223. {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
  224. {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
  225. = lists:keyfind("sec-websocket-accept", 1, Headers),
  226. {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  227. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  228. ok.
  229. ws8_init_shutdown(Config) ->
  230. {port, Port} = lists:keyfind(port, 1, Config),
  231. {ok, Socket} = gen_tcp:connect("localhost", Port,
  232. [binary, {active, false}, {packet, raw}]),
  233. ok = gen_tcp:send(Socket, [
  234. "GET /ws_init_shutdown HTTP/1.1\r\n"
  235. "Host: localhost\r\n"
  236. "Connection: Upgrade\r\n"
  237. "Upgrade: websocket\r\n"
  238. "Sec-WebSocket-Origin: http://localhost\r\n"
  239. "Sec-WebSocket-Version: 8\r\n"
  240. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  241. "\r\n"]),
  242. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  243. {ok, {http_response, {1, 1}, 403, "Forbidden"}, _Rest}
  244. = erlang:decode_packet(http, Handshake, []),
  245. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  246. ok.
  247. ws13(Config) ->
  248. {port, Port} = lists:keyfind(port, 1, Config),
  249. {ok, Socket} = gen_tcp:connect("localhost", Port,
  250. [binary, {active, false}, {packet, raw}]),
  251. ok = gen_tcp:send(Socket, [
  252. "GET /websocket HTTP/1.1\r\n"
  253. "Host: localhost\r\n"
  254. "Connection: Upgrade\r\n"
  255. "Origin: http://localhost\r\n"
  256. "Sec-WebSocket-Version: 13\r\n"
  257. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  258. "Upgrade: websocket\r\n"
  259. "\r\n"]),
  260. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  261. {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
  262. = erlang:decode_packet(http, Handshake, []),
  263. [Headers, <<>>] = websocket_headers(
  264. erlang:decode_packet(httph, Rest, []), []),
  265. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  266. {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
  267. {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
  268. = lists:keyfind("sec-websocket-accept", 1, Headers),
  269. %% text
  270. ok = gen_tcp:send(Socket, << 16#81, 16#85, 16#37, 16#fa, 16#21, 16#3d,
  271. 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>),
  272. {ok, << 1:1, 0:3, 1:4, 0:1, 5:7, "Hello" >>}
  273. = gen_tcp:recv(Socket, 0, 6000),
  274. %% binary (empty)
  275. ok = gen_tcp:send(Socket, << 1:1, 0:3, 2:4, 0:8 >>),
  276. {ok, << 1:1, 0:3, 2:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  277. %% binary
  278. ok = gen_tcp:send(Socket, << 16#82, 16#85, 16#37, 16#fa, 16#21, 16#3d,
  279. 16#7f, 16#9f, 16#4d, 16#51, 16#58 >>),
  280. {ok, << 1:1, 0:3, 2:4, 0:1, 5:7, "Hello" >>}
  281. = gen_tcp:recv(Socket, 0, 6000),
  282. %% Receives.
  283. {ok, << 1:1, 0:3, 1:4, 0:1, 14:7, "websocket_init" >>}
  284. = gen_tcp:recv(Socket, 0, 6000),
  285. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  286. = gen_tcp:recv(Socket, 0, 6000),
  287. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  288. = gen_tcp:recv(Socket, 0, 6000),
  289. {ok, << 1:1, 0:3, 1:4, 0:1, 16:7, "websocket_handle" >>}
  290. = gen_tcp:recv(Socket, 0, 6000),
  291. ok = gen_tcp:send(Socket, << 1:1, 0:3, 9:4, 0:8 >>), %% ping
  292. {ok, << 1:1, 0:3, 10:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000), %% pong
  293. ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>), %% close
  294. {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  295. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  296. ok.
  297. ws_text_fragments(Config) ->
  298. {port, Port} = lists:keyfind(port, 1, Config),
  299. {ok, Socket} = gen_tcp:connect("localhost", Port,
  300. [binary, {active, false}, {packet, raw}]),
  301. ok = gen_tcp:send(Socket, [
  302. "GET /ws_echo_handler HTTP/1.1\r\n"
  303. "Host: localhost\r\n"
  304. "Connection: Upgrade\r\n"
  305. "Upgrade: websocket\r\n"
  306. "Sec-WebSocket-Origin: http://localhost\r\n"
  307. "Sec-WebSocket-Version: 8\r\n"
  308. "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
  309. "\r\n"]),
  310. {ok, Handshake} = gen_tcp:recv(Socket, 0, 6000),
  311. {ok, {http_response, {1, 1}, 101, "Switching Protocols"}, Rest}
  312. = erlang:decode_packet(http, Handshake, []),
  313. [Headers, <<>>] = websocket_headers(
  314. erlang:decode_packet(httph, Rest, []), []),
  315. {'Connection', "Upgrade"} = lists:keyfind('Connection', 1, Headers),
  316. {'Upgrade', "websocket"} = lists:keyfind('Upgrade', 1, Headers),
  317. {"sec-websocket-accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}
  318. = lists:keyfind("sec-websocket-accept", 1, Headers),
  319. ok = gen_tcp:send(Socket, [
  320. << 0:1, 0:3, 1:4, 1:1, 5:7 >>,
  321. << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>,
  322. << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]),
  323. ok = gen_tcp:send(Socket, [
  324. << 1:1, 0:3, 0:4, 1:1, 5:7 >>,
  325. << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>,
  326. << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]),
  327. {ok, << 1:1, 0:3, 1:4, 0:1, 10:7, "HelloHello" >>}
  328. = gen_tcp:recv(Socket, 0, 6000),
  329. ok = gen_tcp:send(Socket, [
  330. %% #1
  331. << 0:1, 0:3, 1:4, 1:1, 5:7 >>,
  332. << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>,
  333. << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>,
  334. %% #2
  335. << 0:1, 0:3, 0:4, 1:1, 5:7 >>,
  336. << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>,
  337. << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>,
  338. %% #3
  339. << 1:1, 0:3, 0:4, 1:1, 5:7 >>,
  340. << 16#37 >>, << 16#fa >>, << 16#21 >>, << 16#3d >>, << 16#7f >>,
  341. << 16#9f >>, << 16#4d >>, << 16#51 >>, << 16#58 >>]),
  342. {ok, << 1:1, 0:3, 1:4, 0:1, 15:7, "HelloHelloHello" >>}
  343. = gen_tcp:recv(Socket, 0, 6000),
  344. ok = gen_tcp:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>), %% close
  345. {ok, << 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 0, 6000),
  346. {error, closed} = gen_tcp:recv(Socket, 0, 6000),
  347. ok.
  348. websocket_headers({ok, http_eoh, Rest}, Acc) ->
  349. [Acc, Rest];
  350. websocket_headers({ok, {http_header, _I, Key, _R, Value}, Rest}, Acc) ->
  351. F = fun(S) when is_atom(S) -> S; (S) -> string:to_lower(S) end,
  352. websocket_headers(erlang:decode_packet(httph, Rest, []),
  353. [{F(Key), Value}|Acc]).