cowboy_websocket.erl 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  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. %% @doc Websocket protocol implementation.
  15. %%
  16. %% Cowboy supports versions 7 through 17 of the Websocket drafts.
  17. %% It also supports RFC6455, the proposed standard for Websocket.
  18. -module(cowboy_websocket).
  19. %% API.
  20. -export([upgrade/4]).
  21. %% Internal.
  22. -export([handler_loop/4]).
  23. -type close_code() :: 1000..4999.
  24. -export_type([close_code/0]).
  25. -type frame() :: close | ping | pong
  26. | {text | binary | close | ping | pong, binary()}
  27. | {close, close_code(), binary()}.
  28. -export_type([frame/0]).
  29. -type opcode() :: 0 | 1 | 2 | 8 | 9 | 10.
  30. -type mask_key() :: 0..16#ffffffff.
  31. -type frag_state() :: undefined
  32. | {nofin, opcode(), binary()} | {fin, opcode(), binary()}.
  33. -record(state, {
  34. env :: cowboy_middleware:env(),
  35. socket = undefined :: inet:socket(),
  36. transport = undefined :: module(),
  37. handler :: module(),
  38. handler_opts :: any(),
  39. key = undefined :: undefined | binary(),
  40. timeout = infinity :: timeout(),
  41. timeout_ref = undefined :: undefined | reference(),
  42. messages = undefined :: undefined | {atom(), atom(), atom()},
  43. hibernate = false :: boolean(),
  44. frag_state = undefined :: frag_state(),
  45. utf8_state = <<>> :: binary()
  46. }).
  47. %% @doc Upgrade an HTTP request to the Websocket protocol.
  48. %%
  49. %% You do not need to call this function manually. To upgrade to the Websocket
  50. %% protocol, you simply need to return <em>{upgrade, protocol, {@module}}</em>
  51. %% in your <em>cowboy_http_handler:init/3</em> handler function.
  52. -spec upgrade(Req, Env, module(), any())
  53. -> {ok, Req, Env} | {error, 400, Req}
  54. | {suspend, module(), atom(), [any()]}
  55. when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  56. upgrade(Req, Env, Handler, HandlerOpts) ->
  57. {_, ListenerPid} = lists:keyfind(listener, 1, Env),
  58. ranch_listener:remove_connection(ListenerPid),
  59. [Socket, Transport] = cowboy_req:get([socket, transport], Req),
  60. State = #state{env=Env, socket=Socket, transport=Transport,
  61. handler=Handler, handler_opts=HandlerOpts},
  62. case catch websocket_upgrade(State, Req) of
  63. {ok, State2, Req2} -> handler_init(State2, Req2);
  64. {'EXIT', _Reason} -> upgrade_error(Req, Env)
  65. end.
  66. -spec websocket_upgrade(#state{}, Req)
  67. -> {ok, #state{}, Req} when Req::cowboy_req:req().
  68. websocket_upgrade(State, Req) ->
  69. {ok, ConnTokens, Req2}
  70. = cowboy_req:parse_header(<<"connection">>, Req),
  71. true = lists:member(<<"upgrade">>, ConnTokens),
  72. %% @todo Should probably send a 426 if the Upgrade header is missing.
  73. {ok, [<<"websocket">>], Req3}
  74. = cowboy_req:parse_header(<<"upgrade">>, Req2),
  75. {Version, Req4} = cowboy_req:header(<<"sec-websocket-version">>, Req3),
  76. IntVersion = list_to_integer(binary_to_list(Version)),
  77. true = (IntVersion =:= 7) orelse (IntVersion =:= 8)
  78. orelse (IntVersion =:= 13),
  79. {Key, Req5} = cowboy_req:header(<<"sec-websocket-key">>, Req4),
  80. false = Key =:= undefined,
  81. {ok, State#state{key=Key},
  82. cowboy_req:set_meta(websocket_version, IntVersion, Req5)}.
  83. -spec handler_init(#state{}, Req)
  84. -> {ok, Req, cowboy_middleware:env()} | {error, 400, Req}
  85. | {suspend, module(), atom(), [any()]}
  86. when Req::cowboy_req:req().
  87. handler_init(State=#state{env=Env, transport=Transport,
  88. handler=Handler, handler_opts=HandlerOpts}, Req) ->
  89. try Handler:websocket_init(Transport:name(), Req, HandlerOpts) of
  90. {ok, Req2, HandlerState} ->
  91. websocket_handshake(State, Req2, HandlerState);
  92. {ok, Req2, HandlerState, hibernate} ->
  93. websocket_handshake(State#state{hibernate=true},
  94. Req2, HandlerState);
  95. {ok, Req2, HandlerState, Timeout} ->
  96. websocket_handshake(State#state{timeout=Timeout},
  97. Req2, HandlerState);
  98. {ok, Req2, HandlerState, Timeout, hibernate} ->
  99. websocket_handshake(State#state{timeout=Timeout,
  100. hibernate=true}, Req2, HandlerState);
  101. {shutdown, Req2} ->
  102. cowboy_req:ensure_response(Req2, 400),
  103. {ok, Req2, [{result, closed}|Env]}
  104. catch Class:Reason ->
  105. error_logger:error_msg(
  106. "** Cowboy handler ~p terminating in ~p/~p~n"
  107. " for the reason ~p:~p~n** Options were ~p~n"
  108. "** Request was ~p~n** Stacktrace: ~p~n~n",
  109. [Handler, websocket_init, 3, Class, Reason, HandlerOpts,
  110. cowboy_req:to_list(Req),erlang:get_stacktrace()]),
  111. upgrade_error(Req, Env)
  112. end.
  113. %% Only send an error reply if there is no resp_sent message.
  114. -spec upgrade_error(Req, Env) -> {ok, Req, Env} | {error, 400, Req}
  115. when Req::cowboy_req:req(), Env::cowboy_middleware:env().
  116. upgrade_error(Req, Env) ->
  117. receive
  118. {cowboy_req, resp_sent} ->
  119. {ok, Req, [{result, closed}|Env]}
  120. after 0 ->
  121. {error, 400, Req}
  122. end.
  123. -spec websocket_handshake(#state{}, Req, any())
  124. -> {ok, Req, cowboy_middleware:env()}
  125. | {suspend, module(), atom(), [any()]}
  126. when Req::cowboy_req:req().
  127. websocket_handshake(State=#state{transport=Transport, key=Key},
  128. Req, HandlerState) ->
  129. Challenge = base64:encode(crypto:sha(
  130. << Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" >>)),
  131. {ok, Req2} = cowboy_req:upgrade_reply(
  132. 101,
  133. [{<<"upgrade">>, <<"websocket">>},
  134. {<<"sec-websocket-accept">>, Challenge}],
  135. Req),
  136. %% Flush the resp_sent message before moving on.
  137. receive {cowboy_req, resp_sent} -> ok after 0 -> ok end,
  138. State2 = handler_loop_timeout(State),
  139. handler_before_loop(State2#state{key=undefined,
  140. messages=Transport:messages()}, Req2, HandlerState, <<>>).
  141. -spec handler_before_loop(#state{}, Req, any(), binary())
  142. -> {ok, Req, cowboy_middleware:env()}
  143. | {suspend, module(), atom(), [any()]}
  144. when Req::cowboy_req:req().
  145. handler_before_loop(State=#state{
  146. socket=Socket, transport=Transport, hibernate=true},
  147. Req, HandlerState, SoFar) ->
  148. Transport:setopts(Socket, [{active, once}]),
  149. {suspend, ?MODULE, handler_loop,
  150. [State#state{hibernate=false}, Req, HandlerState, SoFar]};
  151. handler_before_loop(State=#state{socket=Socket, transport=Transport},
  152. Req, HandlerState, SoFar) ->
  153. Transport:setopts(Socket, [{active, once}]),
  154. handler_loop(State, Req, HandlerState, SoFar).
  155. -spec handler_loop_timeout(#state{}) -> #state{}.
  156. handler_loop_timeout(State=#state{timeout=infinity}) ->
  157. State#state{timeout_ref=undefined};
  158. handler_loop_timeout(State=#state{timeout=Timeout, timeout_ref=PrevRef}) ->
  159. _ = case PrevRef of undefined -> ignore; PrevRef ->
  160. erlang:cancel_timer(PrevRef) end,
  161. TRef = erlang:start_timer(Timeout, self(), ?MODULE),
  162. State#state{timeout_ref=TRef}.
  163. %% @private
  164. -spec handler_loop(#state{}, Req, any(), binary())
  165. -> {ok, Req, cowboy_middleware:env()}
  166. | {suspend, module(), atom(), [any()]}
  167. when Req::cowboy_req:req().
  168. handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error},
  169. timeout_ref=TRef}, Req, HandlerState, SoFar) ->
  170. receive
  171. {OK, Socket, Data} ->
  172. State2 = handler_loop_timeout(State),
  173. websocket_data(State2, Req, HandlerState,
  174. << SoFar/binary, Data/binary >>);
  175. {Closed, Socket} ->
  176. handler_terminate(State, Req, HandlerState, {error, closed});
  177. {Error, Socket, Reason} ->
  178. handler_terminate(State, Req, HandlerState, {error, Reason});
  179. {timeout, TRef, ?MODULE} ->
  180. websocket_close(State, Req, HandlerState, {normal, timeout});
  181. {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
  182. handler_loop(State, Req, HandlerState, SoFar);
  183. Message ->
  184. handler_call(State, Req, HandlerState,
  185. SoFar, websocket_info, Message, fun handler_before_loop/4)
  186. end.
  187. %% All frames passing through this function are considered valid,
  188. %% with the only exception of text and close frames with a payload
  189. %% which may still contain errors.
  190. -spec websocket_data(#state{}, Req, any(), binary())
  191. -> {ok, Req, cowboy_middleware:env()}
  192. | {suspend, module(), atom(), [any()]}
  193. when Req::cowboy_req:req().
  194. %% RSV bits MUST be 0 unless an extension is negotiated
  195. %% that defines meanings for non-zero values.
  196. websocket_data(State, Req, HandlerState, << _:1, Rsv:3, _/bits >>)
  197. when Rsv =/= 0 ->
  198. websocket_close(State, Req, HandlerState, {error, badframe});
  199. %% Invalid opcode. Note that these opcodes may be used by extensions.
  200. websocket_data(State, Req, HandlerState, << _:4, Opcode:4, _/bits >>)
  201. when Opcode > 2, Opcode =/= 8, Opcode =/= 9, Opcode =/= 10 ->
  202. websocket_close(State, Req, HandlerState, {error, badframe});
  203. %% Control frames MUST NOT be fragmented.
  204. websocket_data(State, Req, HandlerState, << 0:1, _:3, Opcode:4, _/bits >>)
  205. when Opcode >= 8 ->
  206. websocket_close(State, Req, HandlerState, {error, badframe});
  207. %% A frame MUST NOT use the zero opcode unless fragmentation was initiated.
  208. websocket_data(State=#state{frag_state=undefined}, Req, HandlerState,
  209. << _:4, 0:4, _/bits >>) ->
  210. websocket_close(State, Req, HandlerState, {error, badframe});
  211. %% Non-control opcode when expecting control message or next fragment.
  212. websocket_data(State=#state{frag_state={nofin, _, _}}, Req, HandlerState,
  213. << _:4, Opcode:4, _/bits >>)
  214. when Opcode =/= 0, Opcode < 8 ->
  215. websocket_close(State, Req, HandlerState, {error, badframe});
  216. %% Close control frame length MUST be 0 or >= 2.
  217. websocket_data(State, Req, HandlerState, << _:4, 8:4, _:1, 1:7, _/bits >>) ->
  218. websocket_close(State, Req, HandlerState, {error, badframe});
  219. %% Close control frame with incomplete close code. Need more data.
  220. websocket_data(State, Req, HandlerState,
  221. Data = << _:4, 8:4, 1:1, Len:7, _/bits >>)
  222. when Len > 1, byte_size(Data) < 8 ->
  223. handler_before_loop(State, Req, HandlerState, Data);
  224. %% 7 bits payload length.
  225. websocket_data(State, Req, HandlerState, << Fin:1, _Rsv:3, Opcode:4, 1:1,
  226. Len:7, MaskKey:32, Rest/bits >>)
  227. when Len < 126 ->
  228. websocket_data(State, Req, HandlerState,
  229. Opcode, Len, MaskKey, Rest, Fin);
  230. %% 16 bits payload length.
  231. websocket_data(State, Req, HandlerState, << Fin:1, _Rsv:3, Opcode:4, 1:1,
  232. 126:7, Len:16, MaskKey:32, Rest/bits >>)
  233. when Len > 125, Opcode < 8 ->
  234. websocket_data(State, Req, HandlerState,
  235. Opcode, Len, MaskKey, Rest, Fin);
  236. %% 63 bits payload length.
  237. websocket_data(State, Req, HandlerState, << Fin:1, _Rsv:3, Opcode:4, 1:1,
  238. 127:7, 0:1, Len:63, MaskKey:32, Rest/bits >>)
  239. when Len > 16#ffff, Opcode < 8 ->
  240. websocket_data(State, Req, HandlerState,
  241. Opcode, Len, MaskKey, Rest, Fin);
  242. %% When payload length is over 63 bits, the most significant bit MUST be 0.
  243. websocket_data(State, Req, HandlerState, << _:8, 1:1, 127:7, 1:1, _:7, _/binary >>) ->
  244. websocket_close(State, Req, HandlerState, {error, badframe});
  245. %% All frames sent from the client to the server are masked.
  246. websocket_data(State, Req, HandlerState, << _:8, 0:1, _/bits >>) ->
  247. websocket_close(State, Req, HandlerState, {error, badframe});
  248. %% For the next two clauses, it can be one of the following:
  249. %%
  250. %% * The minimal number of bytes MUST be used to encode the length
  251. %% * All control frames MUST have a payload length of 125 bytes or less
  252. websocket_data(State, Req, HandlerState, << _:9, 126:7, _:48, _/bits >>) ->
  253. websocket_close(State, Req, HandlerState, {error, badframe});
  254. websocket_data(State, Req, HandlerState, << _:9, 127:7, _:96, _/bits >>) ->
  255. websocket_close(State, Req, HandlerState, {error, badframe});
  256. %% Need more data.
  257. websocket_data(State, Req, HandlerState, Data) ->
  258. handler_before_loop(State, Req, HandlerState, Data).
  259. %% Initialize or update fragmentation state.
  260. -spec websocket_data(#state{}, Req, any(),
  261. opcode(), non_neg_integer(), mask_key(), binary(), 0 | 1)
  262. -> {ok, Req, cowboy_middleware:env()}
  263. | {suspend, module(), atom(), [any()]}
  264. when Req::cowboy_req:req().
  265. %% The opcode is only included in the first frame fragment.
  266. websocket_data(State=#state{frag_state=undefined}, Req, HandlerState,
  267. Opcode, Len, MaskKey, Data, 0) ->
  268. websocket_payload(State#state{frag_state={nofin, Opcode, <<>>}},
  269. Req, HandlerState, 0, Len, MaskKey, <<>>, Data);
  270. %% Subsequent frame fragments.
  271. websocket_data(State=#state{frag_state={nofin, _, _}}, Req, HandlerState,
  272. 0, Len, MaskKey, Data, 0) ->
  273. websocket_payload(State, Req, HandlerState,
  274. 0, Len, MaskKey, <<>>, Data);
  275. %% Final frame fragment.
  276. websocket_data(State=#state{frag_state={nofin, Opcode, SoFar}},
  277. Req, HandlerState, 0, Len, MaskKey, Data, 1) ->
  278. websocket_payload(State#state{frag_state={fin, Opcode, SoFar}},
  279. Req, HandlerState, 0, Len, MaskKey, <<>>, Data);
  280. %% Unfragmented frame.
  281. websocket_data(State, Req, HandlerState, Opcode, Len, MaskKey, Data, 1) ->
  282. websocket_payload(State, Req, HandlerState,
  283. Opcode, Len, MaskKey, <<>>, Data).
  284. -spec websocket_payload(#state{}, Req, any(),
  285. opcode(), non_neg_integer(), mask_key(), binary(), binary())
  286. -> {ok, Req, cowboy_middleware:env()}
  287. | {suspend, module(), atom(), [any()]}
  288. when Req::cowboy_req:req().
  289. %% Close control frames with a payload MUST contain a valid close code.
  290. websocket_payload(State, Req, HandlerState,
  291. Opcode=8, Len, MaskKey, <<>>, << MaskedCode:2/binary, Rest/bits >>) ->
  292. Unmasked = << Code:16 >> = websocket_unmask(MaskedCode, MaskKey, <<>>),
  293. if Code < 1000; Code =:= 1004; Code =:= 1005; Code =:= 1006;
  294. (Code > 1011) and (Code < 3000); Code > 4999 ->
  295. websocket_close(State, Req, HandlerState, {error, badframe});
  296. true ->
  297. websocket_payload(State, Req, HandlerState,
  298. Opcode, Len - 2, MaskKey, Unmasked, Rest)
  299. end;
  300. %% Text frames and close control frames MUST have a payload that is valid UTF-8.
  301. websocket_payload(State=#state{utf8_state=Incomplete},
  302. Req, HandlerState, Opcode, Len, MaskKey, Unmasked, Data)
  303. when (byte_size(Data) < Len) andalso ((Opcode =:= 1) orelse
  304. ((Opcode =:= 8) andalso (Unmasked =/= <<>>))) ->
  305. Unmasked2 = websocket_unmask(Data,
  306. rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>),
  307. case is_utf8(<< Incomplete/binary, Unmasked2/binary >>) of
  308. false ->
  309. websocket_close(State, Req, HandlerState, {error, badencoding});
  310. Utf8State ->
  311. websocket_payload_loop(State#state{utf8_state=Utf8State},
  312. Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey,
  313. << Unmasked/binary, Unmasked2/binary >>)
  314. end;
  315. websocket_payload(State=#state{utf8_state=Incomplete},
  316. Req, HandlerState, Opcode, Len, MaskKey, Unmasked, Data)
  317. when Opcode =:= 1; (Opcode =:= 8) and (Unmasked =/= <<>>) ->
  318. << End:Len/binary, Rest/bits >> = Data,
  319. Unmasked2 = websocket_unmask(End,
  320. rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>),
  321. case is_utf8(<< Incomplete/binary, Unmasked2/binary >>) of
  322. <<>> ->
  323. websocket_dispatch(State#state{utf8_state= <<>>},
  324. Req, HandlerState, Rest, Opcode,
  325. << Unmasked/binary, Unmasked2/binary >>);
  326. _ ->
  327. websocket_close(State, Req, HandlerState, {error, badencoding})
  328. end;
  329. %% Fragmented text frames may cut payload in the middle of UTF-8 codepoints.
  330. websocket_payload(State=#state{frag_state={_, 1, _}, utf8_state=Incomplete},
  331. Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, Data)
  332. when byte_size(Data) < Len ->
  333. Unmasked2 = websocket_unmask(Data,
  334. rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>),
  335. case is_utf8(<< Incomplete/binary, Unmasked2/binary >>) of
  336. false ->
  337. websocket_close(State, Req, HandlerState, {error, badencoding});
  338. Utf8State ->
  339. websocket_payload_loop(State#state{utf8_state=Utf8State},
  340. Req, HandlerState, Opcode, Len - byte_size(Data), MaskKey,
  341. << Unmasked/binary, Unmasked2/binary >>)
  342. end;
  343. websocket_payload(State=#state{frag_state={Fin, 1, _}, utf8_state=Incomplete},
  344. Req, HandlerState, Opcode=0, Len, MaskKey, Unmasked, Data) ->
  345. << End:Len/binary, Rest/bits >> = Data,
  346. Unmasked2 = websocket_unmask(End,
  347. rotate_mask_key(MaskKey, byte_size(Unmasked)), <<>>),
  348. case is_utf8(<< Incomplete/binary, Unmasked2/binary >>) of
  349. <<>> ->
  350. websocket_dispatch(State#state{utf8_state= <<>>},
  351. Req, HandlerState, Rest, Opcode,
  352. << Unmasked/binary, Unmasked2/binary >>);
  353. Utf8State when is_binary(Utf8State), Fin =:= nofin ->
  354. websocket_dispatch(State#state{utf8_state=Utf8State},
  355. Req, HandlerState, Rest, Opcode,
  356. << Unmasked/binary, Unmasked2/binary >>);
  357. _ ->
  358. websocket_close(State, Req, HandlerState, {error, badencoding})
  359. end;
  360. %% Other frames have a binary payload.
  361. websocket_payload(State, Req, HandlerState,
  362. Opcode, Len, MaskKey, Unmasked, Data)
  363. when byte_size(Data) < Len ->
  364. Unmasked2 = websocket_unmask(Data,
  365. rotate_mask_key(MaskKey, byte_size(Unmasked)), Unmasked),
  366. websocket_payload_loop(State, Req, HandlerState,
  367. Opcode, Len - byte_size(Data), MaskKey, Unmasked2);
  368. websocket_payload(State, Req, HandlerState,
  369. Opcode, Len, MaskKey, Unmasked, Data) ->
  370. << End:Len/binary, Rest/bits >> = Data,
  371. Unmasked2 = websocket_unmask(End,
  372. rotate_mask_key(MaskKey, byte_size(Unmasked)), Unmasked),
  373. websocket_dispatch(State, Req, HandlerState, Rest, Opcode, Unmasked2).
  374. -spec websocket_unmask(B, mask_key(), B) -> B when B::binary().
  375. websocket_unmask(<<>>, _, Unmasked) ->
  376. Unmasked;
  377. websocket_unmask(<< O:32, Rest/bits >>, MaskKey, Acc) ->
  378. T = O bxor MaskKey,
  379. websocket_unmask(Rest, MaskKey, << Acc/binary, T:32 >>);
  380. websocket_unmask(<< O:24 >>, MaskKey, Acc) ->
  381. << MaskKey2:24, _:8 >> = << MaskKey:32 >>,
  382. T = O bxor MaskKey2,
  383. << Acc/binary, T:24 >>;
  384. websocket_unmask(<< O:16 >>, MaskKey, Acc) ->
  385. << MaskKey2:16, _:16 >> = << MaskKey:32 >>,
  386. T = O bxor MaskKey2,
  387. << Acc/binary, T:16 >>;
  388. websocket_unmask(<< O:8 >>, MaskKey, Acc) ->
  389. << MaskKey2:8, _:24 >> = << MaskKey:32 >>,
  390. T = O bxor MaskKey2,
  391. << Acc/binary, T:8 >>.
  392. %% Because we unmask on the fly we need to continue from the right mask byte.
  393. -spec rotate_mask_key(mask_key(), non_neg_integer()) -> mask_key().
  394. rotate_mask_key(MaskKey, UnmaskedLen) ->
  395. Left = UnmaskedLen rem 4,
  396. Right = 4 - Left,
  397. (MaskKey bsl (Left * 8)) + (MaskKey bsr (Right * 8)).
  398. %% Returns <<>> if the argument is valid UTF-8, false if not,
  399. %% or the incomplete part of the argument if we need more data.
  400. -spec is_utf8(binary()) -> false | binary().
  401. is_utf8(Valid = <<>>) ->
  402. Valid;
  403. is_utf8(<< _/utf8, Rest/binary >>) ->
  404. is_utf8(Rest);
  405. %% 2 bytes. Codepages C0 and C1 are invalid; fail early.
  406. is_utf8(<< 2#1100000:7, _/bits >>) ->
  407. false;
  408. is_utf8(Incomplete = << 2#110:3, _:5 >>) ->
  409. Incomplete;
  410. %% 3 bytes.
  411. is_utf8(Incomplete = << 2#1110:4, _:4 >>) ->
  412. Incomplete;
  413. is_utf8(Incomplete = << 2#1110:4, _:4, 2#10:2, _:6 >>) ->
  414. Incomplete;
  415. %% 4 bytes. Codepage F4 may have invalid values greater than 0x10FFFF.
  416. is_utf8(<< 2#11110100:8, 2#10:2, High:6, _/bits >>) when High >= 2#10000 ->
  417. false;
  418. is_utf8(Incomplete = << 2#11110:5, _:3 >>) ->
  419. Incomplete;
  420. is_utf8(Incomplete = << 2#11110:5, _:3, 2#10:2, _:6 >>) ->
  421. Incomplete;
  422. is_utf8(Incomplete = << 2#11110:5, _:3, 2#10:2, _:6, 2#10:2, _:6 >>) ->
  423. Incomplete;
  424. %% Invalid.
  425. is_utf8(_) ->
  426. false.
  427. -spec websocket_payload_loop(#state{}, Req, any(),
  428. opcode(), non_neg_integer(), mask_key(), binary())
  429. -> {ok, Req, cowboy_middleware:env()}
  430. | {suspend, module(), atom(), [any()]}
  431. when Req::cowboy_req:req().
  432. websocket_payload_loop(State=#state{socket=Socket, transport=Transport,
  433. messages={OK, Closed, Error}, timeout_ref=TRef},
  434. Req, HandlerState, Opcode, Len, MaskKey, Unmasked) ->
  435. Transport:setopts(Socket, [{active, once}]),
  436. receive
  437. {OK, Socket, Data} ->
  438. State2 = handler_loop_timeout(State),
  439. websocket_payload(State2, Req, HandlerState,
  440. Opcode, Len, MaskKey, Unmasked, Data);
  441. {Closed, Socket} ->
  442. handler_terminate(State, Req, HandlerState, {error, closed});
  443. {Error, Socket, Reason} ->
  444. handler_terminate(State, Req, HandlerState, {error, Reason});
  445. {timeout, TRef, ?MODULE} ->
  446. websocket_close(State, Req, HandlerState, {normal, timeout});
  447. {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
  448. websocket_payload_loop(State, Req, HandlerState,
  449. Opcode, Len, MaskKey, Unmasked);
  450. Message ->
  451. handler_call(State, Req, HandlerState,
  452. <<>>, websocket_info, Message,
  453. fun (State2, Req2, HandlerState2, _) ->
  454. websocket_payload_loop(State2, Req2, HandlerState2,
  455. Opcode, Len, MaskKey, Unmasked)
  456. end)
  457. end.
  458. -spec websocket_dispatch(#state{}, Req, any(), binary(), opcode(), binary())
  459. -> {ok, Req, cowboy_middleware:env()}
  460. | {suspend, module(), atom(), [any()]}
  461. when Req::cowboy_req:req().
  462. %% Continuation frame.
  463. websocket_dispatch(State=#state{frag_state={nofin, Opcode, SoFar}},
  464. Req, HandlerState, RemainingData, 0, Payload) ->
  465. websocket_data(State#state{frag_state={nofin, Opcode,
  466. << SoFar/binary, Payload/binary >>}}, Req, HandlerState, RemainingData);
  467. %% Last continuation frame.
  468. websocket_dispatch(State=#state{frag_state={fin, Opcode, SoFar}},
  469. Req, HandlerState, RemainingData, 0, Payload) ->
  470. websocket_dispatch(State#state{frag_state=undefined}, Req, HandlerState,
  471. RemainingData, Opcode, << SoFar/binary, Payload/binary >>);
  472. %% Text frame.
  473. websocket_dispatch(State, Req, HandlerState, RemainingData, 1, Payload) ->
  474. handler_call(State, Req, HandlerState, RemainingData,
  475. websocket_handle, {text, Payload}, fun websocket_data/4);
  476. %% Binary frame.
  477. websocket_dispatch(State, Req, HandlerState, RemainingData, 2, Payload) ->
  478. handler_call(State, Req, HandlerState, RemainingData,
  479. websocket_handle, {binary, Payload}, fun websocket_data/4);
  480. %% Close control frame.
  481. websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, <<>>) ->
  482. websocket_close(State, Req, HandlerState, {remote, closed});
  483. websocket_dispatch(State, Req, HandlerState, _RemainingData, 8,
  484. << Code:16, Payload/bits >>) ->
  485. websocket_close(State, Req, HandlerState, {remote, Code, Payload});
  486. %% Ping control frame. Send a pong back and forward the ping to the handler.
  487. websocket_dispatch(State=#state{socket=Socket, transport=Transport},
  488. Req, HandlerState, RemainingData, 9, Payload) ->
  489. Len = payload_length_to_binary(byte_size(Payload)),
  490. Transport:send(Socket, << 1:1, 0:3, 10:4, 0:1, Len/bits, Payload/binary >>),
  491. handler_call(State, Req, HandlerState, RemainingData,
  492. websocket_handle, {ping, Payload}, fun websocket_data/4);
  493. %% Pong control frame.
  494. websocket_dispatch(State, Req, HandlerState, RemainingData, 10, Payload) ->
  495. handler_call(State, Req, HandlerState, RemainingData,
  496. websocket_handle, {pong, Payload}, fun websocket_data/4).
  497. -spec handler_call(#state{}, Req, any(), binary(), atom(), any(), fun())
  498. -> {ok, Req, cowboy_middleware:env()}
  499. | {suspend, module(), atom(), [any()]}
  500. when Req::cowboy_req:req().
  501. handler_call(State=#state{handler=Handler, handler_opts=HandlerOpts}, Req,
  502. HandlerState, RemainingData, Callback, Message, NextState) ->
  503. try Handler:Callback(Message, Req, HandlerState) of
  504. {ok, Req2, HandlerState2} ->
  505. NextState(State, Req2, HandlerState2, RemainingData);
  506. {ok, Req2, HandlerState2, hibernate} ->
  507. NextState(State#state{hibernate=true},
  508. Req2, HandlerState2, RemainingData);
  509. {reply, Payload, Req2, HandlerState2}
  510. when is_tuple(Payload) ->
  511. case websocket_send(Payload, State) of
  512. ok ->
  513. NextState(State, Req2, HandlerState2, RemainingData);
  514. shutdown ->
  515. handler_terminate(State, Req2, HandlerState,
  516. {normal, shutdown});
  517. {error, _} = Error ->
  518. handler_terminate(State, Req2, HandlerState2, Error)
  519. end;
  520. {reply, Payload, Req2, HandlerState2, hibernate}
  521. when is_tuple(Payload) ->
  522. case websocket_send(Payload, State) of
  523. ok ->
  524. NextState(State#state{hibernate=true},
  525. Req2, HandlerState2, RemainingData);
  526. shutdown ->
  527. handler_terminate(State, Req2, HandlerState,
  528. {normal, shutdown});
  529. {error, _} = Error ->
  530. handler_terminate(State, Req2, HandlerState2, Error)
  531. end;
  532. {reply, Payload, Req2, HandlerState2}
  533. when is_list(Payload) ->
  534. case websocket_send_many(Payload, State) of
  535. ok ->
  536. NextState(State, Req2, HandlerState2, RemainingData);
  537. shutdown ->
  538. handler_terminate(State, Req2, HandlerState,
  539. {normal, shutdown});
  540. {error, _} = Error ->
  541. handler_terminate(State, Req2, HandlerState2, Error)
  542. end;
  543. {reply, Payload, Req2, HandlerState2, hibernate}
  544. when is_list(Payload) ->
  545. case websocket_send_many(Payload, State) of
  546. ok ->
  547. NextState(State#state{hibernate=true},
  548. Req2, HandlerState2, RemainingData);
  549. shutdown ->
  550. handler_terminate(State, Req2, HandlerState,
  551. {normal, shutdown});
  552. {error, _} = Error ->
  553. handler_terminate(State, Req2, HandlerState2, Error)
  554. end;
  555. {shutdown, Req2, HandlerState2} ->
  556. websocket_close(State, Req2, HandlerState2, {normal, shutdown})
  557. catch Class:Reason ->
  558. PLReq = cowboy_req:to_list(Req),
  559. error_logger:error_msg(
  560. "** Cowboy handler ~p terminating in ~p/~p~n"
  561. " for the reason ~p:~p~n** Message was ~p~n"
  562. "** Options were ~p~n** Handler state was ~p~n"
  563. "** Request was ~p~n** Stacktrace: ~p~n~n",
  564. [Handler, Callback, 3, Class, Reason, Message, HandlerOpts,
  565. HandlerState, PLReq, erlang:get_stacktrace()]),
  566. websocket_close(State, Req, HandlerState, {error, handler})
  567. end.
  568. websocket_opcode(text) -> 1;
  569. websocket_opcode(binary) -> 2;
  570. websocket_opcode(close) -> 8;
  571. websocket_opcode(ping) -> 9;
  572. websocket_opcode(pong) -> 10.
  573. -spec websocket_send(frame(), #state{})
  574. -> ok | shutdown | {error, atom()}.
  575. websocket_send(Type, #state{socket=Socket, transport=Transport})
  576. when Type =:= close ->
  577. Opcode = websocket_opcode(Type),
  578. case Transport:send(Socket, << 1:1, 0:3, Opcode:4, 0:8 >>) of
  579. ok -> shutdown;
  580. Error -> Error
  581. end;
  582. websocket_send(Type, #state{socket=Socket, transport=Transport})
  583. when Type =:= ping; Type =:= pong ->
  584. Opcode = websocket_opcode(Type),
  585. Transport:send(Socket, << 1:1, 0:3, Opcode:4, 0:8 >>);
  586. websocket_send({close, Payload}, State) ->
  587. websocket_send({close, 1000, Payload}, State);
  588. websocket_send({Type = close, StatusCode, Payload}, #state{
  589. socket=Socket, transport=Transport}) ->
  590. Opcode = websocket_opcode(Type),
  591. Len = 2 + iolist_size(Payload),
  592. %% Control packets must not be > 125 in length.
  593. true = Len =< 125,
  594. BinLen = payload_length_to_binary(Len),
  595. Transport:send(Socket,
  596. [<< 1:1, 0:3, Opcode:4, 0:1, BinLen/bits, StatusCode:16 >>, Payload]),
  597. shutdown;
  598. websocket_send({Type, Payload}, #state{socket=Socket, transport=Transport}) ->
  599. Opcode = websocket_opcode(Type),
  600. Len = iolist_size(Payload),
  601. %% Control packets must not be > 125 in length.
  602. true = if Type =:= ping; Type =:= pong ->
  603. Len =< 125;
  604. true ->
  605. true
  606. end,
  607. BinLen = payload_length_to_binary(Len),
  608. Transport:send(Socket,
  609. [<< 1:1, 0:3, Opcode:4, 0:1, BinLen/bits >>, Payload]).
  610. -spec websocket_send_many([frame()], #state{})
  611. -> ok | shutdown | {error, atom()}.
  612. websocket_send_many([], _) ->
  613. ok;
  614. websocket_send_many([Frame|Tail], State) ->
  615. case websocket_send(Frame, State) of
  616. ok -> websocket_send_many(Tail, State);
  617. shutdown -> shutdown;
  618. Error -> Error
  619. end.
  620. -spec websocket_close(#state{}, Req, any(),
  621. {atom(), atom()} | {remote, close_code(), binary()})
  622. -> {ok, Req, cowboy_middleware:env()}
  623. when Req::cowboy_req:req().
  624. websocket_close(State=#state{socket=Socket, transport=Transport},
  625. Req, HandlerState, Reason) ->
  626. case Reason of
  627. {normal, _} ->
  628. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>);
  629. {error, badframe} ->
  630. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1002:16 >>);
  631. {error, badencoding} ->
  632. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1007:16 >>);
  633. {error, handler} ->
  634. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, 1011:16 >>);
  635. {remote, closed} ->
  636. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>);
  637. {remote, Code, _} ->
  638. Transport:send(Socket, << 1:1, 0:3, 8:4, 0:1, 2:7, Code:16 >>)
  639. end,
  640. handler_terminate(State, Req, HandlerState, Reason).
  641. -spec handler_terminate(#state{}, Req, any(), atom() | {atom(), atom()})
  642. -> {ok, Req, cowboy_middleware:env()}
  643. when Req::cowboy_req:req().
  644. handler_terminate(#state{env=Env, handler=Handler, handler_opts=HandlerOpts},
  645. Req, HandlerState, TerminateReason) ->
  646. try
  647. Handler:websocket_terminate(TerminateReason, Req, HandlerState)
  648. catch Class:Reason ->
  649. PLReq = cowboy_req:to_list(Req),
  650. error_logger:error_msg(
  651. "** Cowboy handler ~p terminating in ~p/~p~n"
  652. " for the reason ~p:~p~n** Initial reason was ~p~n"
  653. "** Options were ~p~n** Handler state was ~p~n"
  654. "** Request was ~p~n** Stacktrace: ~p~n~n",
  655. [Handler, websocket_terminate, 3, Class, Reason, TerminateReason,
  656. HandlerOpts, HandlerState, PLReq, erlang:get_stacktrace()])
  657. end,
  658. {ok, Req, [{result, closed}|Env]}.
  659. -spec payload_length_to_binary(0..16#7fffffffffffffff)
  660. -> << _:7 >> | << _:23 >> | << _:71 >>.
  661. payload_length_to_binary(N) ->
  662. case N of
  663. N when N =< 125 -> << N:7 >>;
  664. N when N =< 16#ffff -> << 126:7, N:16 >>;
  665. N when N =< 16#7fffffffffffffff -> << 127:7, N:64 >>
  666. end.