mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-15 12:40:25 +00:00
Put socket and transport into cowboy_websocket's #state{}
As part of the work to make cowboy_req:req() opaque.
This commit is contained in:
parent
bb4bd9ac96
commit
35ebe0b108
1 changed files with 36 additions and 33 deletions
|
@ -40,6 +40,8 @@
|
||||||
{fin, opcode(), binary()}. %% last fragment has been seen.
|
{fin, opcode(), binary()}. %% last fragment has been seen.
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
|
socket = undefined :: inet:socket(),
|
||||||
|
transport = undefined :: module(),
|
||||||
version :: 0 | 7 | 8 | 13,
|
version :: 0 | 7 | 8 | 13,
|
||||||
handler :: module(),
|
handler :: module(),
|
||||||
opts :: any(),
|
opts :: any(),
|
||||||
|
@ -61,8 +63,11 @@
|
||||||
-spec upgrade(pid(), module(), any(), cowboy_req:req()) -> closed.
|
-spec upgrade(pid(), module(), any(), cowboy_req:req()) -> closed.
|
||||||
upgrade(ListenerPid, Handler, Opts, Req) ->
|
upgrade(ListenerPid, Handler, Opts, Req) ->
|
||||||
ranch_listener:remove_connection(ListenerPid),
|
ranch_listener:remove_connection(ListenerPid),
|
||||||
case catch websocket_upgrade(#state{handler=Handler, opts=Opts}, Req) of
|
{ok, Transport, Socket} = cowboy_req:transport(Req),
|
||||||
{ok, State, Req2} -> handler_init(State, Req2);
|
State = #state{socket=Socket, transport=Transport,
|
||||||
|
handler=Handler, opts=Opts},
|
||||||
|
case catch websocket_upgrade(State, Req) of
|
||||||
|
{ok, State2, Req2} -> handler_init(State2, Req2);
|
||||||
{'EXIT', _Reason} -> upgrade_error(Req)
|
{'EXIT', _Reason} -> upgrade_error(Req)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -107,8 +112,8 @@ websocket_upgrade(Version, State, Req=#http_req{meta=Meta})
|
||||||
Req2#http_req{meta=[{websocket_version, IntVersion}|Meta]}}.
|
Req2#http_req{meta=[{websocket_version, IntVersion}|Meta]}}.
|
||||||
|
|
||||||
-spec handler_init(#state{}, cowboy_req:req()) -> closed.
|
-spec handler_init(#state{}, cowboy_req:req()) -> closed.
|
||||||
handler_init(State=#state{handler=Handler, opts=Opts},
|
handler_init(State=#state{transport=Transport, handler=Handler, opts=Opts},
|
||||||
Req=#http_req{transport=Transport}) ->
|
Req) ->
|
||||||
try Handler:websocket_init(Transport:name(), Req, Opts) of
|
try Handler:websocket_init(Transport:name(), Req, Opts) of
|
||||||
{ok, Req2, HandlerState} ->
|
{ok, Req2, HandlerState} ->
|
||||||
websocket_handshake(State, Req2, HandlerState);
|
websocket_handshake(State, Req2, HandlerState);
|
||||||
|
@ -154,10 +159,10 @@ upgrade_denied(#http_req{socket=Socket, transport=Transport,
|
||||||
closed.
|
closed.
|
||||||
|
|
||||||
-spec websocket_handshake(#state{}, cowboy_req:req(), any()) -> closed.
|
-spec websocket_handshake(#state{}, cowboy_req:req(), any()) -> closed.
|
||||||
websocket_handshake(State=#state{version=0, origin=Origin,
|
websocket_handshake(State=#state{socket=Socket, transport=Transport,
|
||||||
challenge={Key1, Key2}}, Req=#http_req{socket=Socket,
|
version=0, origin=Origin, challenge={Key1, Key2}},
|
||||||
transport=Transport, host=Host, port=Port,
|
Req=#http_req{host=Host, port=Port, path=Path, raw_qs=QS},
|
||||||
path=Path, raw_qs=QS}, HandlerState) ->
|
HandlerState) ->
|
||||||
Location = hixie76_location(Transport:name(), Host, Port, Path, QS),
|
Location = hixie76_location(Transport:name(), Host, Port, Path, QS),
|
||||||
{ok, Req2} = cowboy_req:upgrade_reply(
|
{ok, Req2} = cowboy_req:upgrade_reply(
|
||||||
<<"101 WebSocket Protocol Handshake">>,
|
<<"101 WebSocket Protocol Handshake">>,
|
||||||
|
@ -185,8 +190,8 @@ websocket_handshake(State=#state{version=0, origin=Origin,
|
||||||
_Any ->
|
_Any ->
|
||||||
closed %% If an error happened reading the body, stop there.
|
closed %% If an error happened reading the body, stop there.
|
||||||
end;
|
end;
|
||||||
websocket_handshake(State=#state{challenge=Challenge},
|
websocket_handshake(State=#state{transport=Transport, challenge=Challenge},
|
||||||
Req=#http_req{transport=Transport}, HandlerState) ->
|
Req, HandlerState) ->
|
||||||
{ok, Req2} = cowboy_req:upgrade_reply(
|
{ok, Req2} = cowboy_req:upgrade_reply(
|
||||||
101,
|
101,
|
||||||
[{<<"Upgrade">>, <<"websocket">>},
|
[{<<"Upgrade">>, <<"websocket">>},
|
||||||
|
@ -198,16 +203,16 @@ websocket_handshake(State=#state{challenge=Challenge},
|
||||||
Req2, HandlerState, <<>>).
|
Req2, HandlerState, <<>>).
|
||||||
|
|
||||||
-spec handler_before_loop(#state{}, cowboy_req:req(), any(), binary()) -> closed.
|
-spec handler_before_loop(#state{}, cowboy_req:req(), any(), binary()) -> closed.
|
||||||
handler_before_loop(State=#state{hibernate=true},
|
handler_before_loop(State=#state{
|
||||||
Req=#http_req{socket=Socket, transport=Transport},
|
socket=Socket, transport=Transport, hibernate=true},
|
||||||
HandlerState, SoFar) ->
|
Req, HandlerState, SoFar) ->
|
||||||
Transport:setopts(Socket, [{active, once}]),
|
Transport:setopts(Socket, [{active, once}]),
|
||||||
State2 = handler_loop_timeout(State),
|
State2 = handler_loop_timeout(State),
|
||||||
catch erlang:hibernate(?MODULE, handler_loop,
|
catch erlang:hibernate(?MODULE, handler_loop,
|
||||||
[State2#state{hibernate=false}, Req, HandlerState, SoFar]),
|
[State2#state{hibernate=false}, Req, HandlerState, SoFar]),
|
||||||
closed;
|
closed;
|
||||||
handler_before_loop(State, Req=#http_req{socket=Socket, transport=Transport},
|
handler_before_loop(State=#state{socket=Socket, transport=Transport},
|
||||||
HandlerState, SoFar) ->
|
Req, HandlerState, SoFar) ->
|
||||||
Transport:setopts(Socket, [{active, once}]),
|
Transport:setopts(Socket, [{active, once}]),
|
||||||
State2 = handler_loop_timeout(State),
|
State2 = handler_loop_timeout(State),
|
||||||
handler_loop(State2, Req, HandlerState, SoFar).
|
handler_loop(State2, Req, HandlerState, SoFar).
|
||||||
|
@ -223,8 +228,9 @@ handler_loop_timeout(State=#state{timeout=Timeout, timeout_ref=PrevRef}) ->
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
-spec handler_loop(#state{}, cowboy_req:req(), any(), binary()) -> closed.
|
-spec handler_loop(#state{}, cowboy_req:req(), any(), binary()) -> closed.
|
||||||
handler_loop(State=#state{messages={OK, Closed, Error}, timeout_ref=TRef},
|
handler_loop(State=#state{
|
||||||
Req=#http_req{socket=Socket}, HandlerState, SoFar) ->
|
socket=Socket, messages={OK, Closed, Error}, timeout_ref=TRef},
|
||||||
|
Req, HandlerState, SoFar) ->
|
||||||
receive
|
receive
|
||||||
{OK, Socket, Data} ->
|
{OK, Socket, Data} ->
|
||||||
websocket_data(State, Req, HandlerState,
|
websocket_data(State, Req, HandlerState,
|
||||||
|
@ -299,7 +305,6 @@ websocket_data(State, Req, HandlerState,
|
||||||
websocket_data(State, Req, HandlerState, _Data) ->
|
websocket_data(State, Req, HandlerState, _Data) ->
|
||||||
websocket_close(State, Req, HandlerState, {error, badframe}).
|
websocket_close(State, Req, HandlerState, {error, badframe}).
|
||||||
|
|
||||||
|
|
||||||
-spec websocket_data(#state{}, cowboy_req:req(), any(), non_neg_integer(),
|
-spec websocket_data(#state{}, cowboy_req:req(), any(), non_neg_integer(),
|
||||||
non_neg_integer(), non_neg_integer(), non_neg_integer(),
|
non_neg_integer(), non_neg_integer(), non_neg_integer(),
|
||||||
non_neg_integer(), binary(), binary()) -> closed.
|
non_neg_integer(), binary(), binary()) -> closed.
|
||||||
|
@ -354,7 +359,6 @@ websocket_data(State, Req, HandlerState, _Fin, _Rsv, _Opcode, _Mask,
|
||||||
_PayloadLen, _Rest, _Data) ->
|
_PayloadLen, _Rest, _Data) ->
|
||||||
websocket_close(State, Req, HandlerState, {error, badframe}).
|
websocket_close(State, Req, HandlerState, {error, badframe}).
|
||||||
|
|
||||||
|
|
||||||
%% hybi routing depending on whether unmasking is needed.
|
%% hybi routing depending on whether unmasking is needed.
|
||||||
-spec websocket_before_unmask(#state{}, cowboy_req:req(), any(), binary(),
|
-spec websocket_before_unmask(#state{}, cowboy_req:req(), any(), binary(),
|
||||||
binary(), opcode(), 0 | 1, non_neg_integer() | undefined) -> closed.
|
binary(), opcode(), 0 | 1, non_neg_integer() | undefined) -> closed.
|
||||||
|
@ -442,8 +446,8 @@ websocket_dispatch(State, Req, HandlerState, RemainingData, 2, Payload) ->
|
||||||
websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, _Payload) ->
|
websocket_dispatch(State, Req, HandlerState, _RemainingData, 8, _Payload) ->
|
||||||
websocket_close(State, Req, HandlerState, {normal, closed});
|
websocket_close(State, Req, HandlerState, {normal, closed});
|
||||||
%% Ping control frame. Send a pong back and forward the ping to the handler.
|
%% Ping control frame. Send a pong back and forward the ping to the handler.
|
||||||
websocket_dispatch(State, Req=#http_req{socket=Socket, transport=Transport},
|
websocket_dispatch(State=#state{socket=Socket, transport=Transport},
|
||||||
HandlerState, RemainingData, 9, Payload) ->
|
Req, HandlerState, RemainingData, 9, Payload) ->
|
||||||
Len = hybi_payload_length(byte_size(Payload)),
|
Len = hybi_payload_length(byte_size(Payload)),
|
||||||
Transport:send(Socket, << 1:1, 0:3, 10:4, 0:1, Len/bits, Payload/binary >>),
|
Transport:send(Socket, << 1:1, 0:3, 10:4, 0:1, Len/bits, Payload/binary >>),
|
||||||
handler_call(State, Req, HandlerState, RemainingData,
|
handler_call(State, Req, HandlerState, RemainingData,
|
||||||
|
@ -464,10 +468,10 @@ handler_call(State=#state{handler=Handler, opts=Opts}, Req, HandlerState,
|
||||||
NextState(State#state{hibernate=true},
|
NextState(State#state{hibernate=true},
|
||||||
Req2, HandlerState2, RemainingData);
|
Req2, HandlerState2, RemainingData);
|
||||||
{reply, Payload, Req2, HandlerState2} ->
|
{reply, Payload, Req2, HandlerState2} ->
|
||||||
websocket_send(Payload, State, Req2),
|
websocket_send(Payload, State),
|
||||||
NextState(State, Req2, HandlerState2, RemainingData);
|
NextState(State, Req2, HandlerState2, RemainingData);
|
||||||
{reply, Payload, Req2, HandlerState2, hibernate} ->
|
{reply, Payload, Req2, HandlerState2, hibernate} ->
|
||||||
websocket_send(Payload, State, Req2),
|
websocket_send(Payload, State),
|
||||||
NextState(State#state{hibernate=true},
|
NextState(State#state{hibernate=true},
|
||||||
Req2, HandlerState2, RemainingData);
|
Req2, HandlerState2, RemainingData);
|
||||||
{shutdown, Req2, HandlerState2} ->
|
{shutdown, Req2, HandlerState2} ->
|
||||||
|
@ -484,16 +488,15 @@ handler_call(State=#state{handler=Handler, opts=Opts}, Req, HandlerState,
|
||||||
websocket_close(State, Req, HandlerState, {error, handler})
|
websocket_close(State, Req, HandlerState, {error, handler})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec websocket_send(binary(), #state{}, cowboy_req:req()) -> closed | ignore.
|
-spec websocket_send(binary(), #state{}) -> closed | ignore.
|
||||||
%% hixie-76 text frame.
|
%% hixie-76 text frame.
|
||||||
websocket_send({text, Payload}, #state{version=0},
|
websocket_send({text, Payload}, #state{
|
||||||
#http_req{socket=Socket, transport=Transport}) ->
|
socket=Socket, transport=Transport, version=0}) ->
|
||||||
Transport:send(Socket, [0, Payload, 255]);
|
Transport:send(Socket, [0, Payload, 255]);
|
||||||
%% Ignore all unknown frame types for compatibility with hixie 76.
|
%% Ignore all unknown frame types for compatibility with hixie 76.
|
||||||
websocket_send(_Any, #state{version=0}, _Req) ->
|
websocket_send(_Any, #state{version=0}) ->
|
||||||
ignore;
|
ignore;
|
||||||
websocket_send({Type, Payload}, _State,
|
websocket_send({Type, Payload}, #state{socket=Socket, transport=Transport}) ->
|
||||||
#http_req{socket=Socket, transport=Transport}) ->
|
|
||||||
Opcode = case Type of
|
Opcode = case Type of
|
||||||
text -> 1;
|
text -> 1;
|
||||||
binary -> 2;
|
binary -> 2;
|
||||||
|
@ -506,13 +509,13 @@ websocket_send({Type, Payload}, _State,
|
||||||
|
|
||||||
-spec websocket_close(#state{}, cowboy_req:req(), any(), {atom(), atom()})
|
-spec websocket_close(#state{}, cowboy_req:req(), any(), {atom(), atom()})
|
||||||
-> closed.
|
-> closed.
|
||||||
websocket_close(State=#state{version=0}, Req=#http_req{socket=Socket,
|
websocket_close(State=#state{socket=Socket, transport=Transport, version=0},
|
||||||
transport=Transport}, HandlerState, Reason) ->
|
Req, HandlerState, Reason) ->
|
||||||
Transport:send(Socket, << 255, 0 >>),
|
Transport:send(Socket, << 255, 0 >>),
|
||||||
handler_terminate(State, Req, HandlerState, Reason);
|
handler_terminate(State, Req, HandlerState, Reason);
|
||||||
%% @todo Send a Payload? Using Reason is usually good but we're quite careless.
|
%% @todo Send a Payload? Using Reason is usually good but we're quite careless.
|
||||||
websocket_close(State, Req=#http_req{socket=Socket,
|
websocket_close(State=#state{socket=Socket, transport=Transport},
|
||||||
transport=Transport}, HandlerState, Reason) ->
|
Req, HandlerState, Reason) ->
|
||||||
Transport:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>),
|
Transport:send(Socket, << 1:1, 0:3, 8:4, 0:8 >>),
|
||||||
handler_terminate(State, Req, HandlerState, Reason).
|
handler_terminate(State, Req, HandlerState, Reason).
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue