aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_websocket.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cowboy_websocket.erl')
-rw-r--r--src/cowboy_websocket.erl70
1 files changed, 33 insertions, 37 deletions
diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl
index b8ec2e1..bc9bd31 100644
--- a/src/cowboy_websocket.erl
+++ b/src/cowboy_websocket.erl
@@ -32,6 +32,12 @@
| {module(), Req, any(), timeout()}
| {module(), Req, any(), timeout(), hibernate}
when Req::cowboy_req:req().
+
+-callback websocket_init(Req, State)
+ -> {ok, Req, State}
+ when Req::cowboy_req:req(), State::any().
+-optional_callbacks([websocket_init/2]).
+
-callback websocket_handle({text | binary | ping | pong, binary()}, Req, State)
-> {ok, Req, State}
| {ok, Req, State, hibernate}
@@ -70,17 +76,18 @@
-spec upgrade(Req, Env, module(), any(), timeout(), run | hibernate)
-> {ok, Req, Env}
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
-upgrade(Req, Env, Handler, HandlerState, Timeout, Hibernate) ->
- State = #state{handler=Handler, timeout=Timeout,
- hibernate=Hibernate =:= hibernate},
- %% @todo We need to fail if HTTP/2.
- try websocket_upgrade(State, Req) of
- {ok, State2, Req2} ->
- websocket_handshake(State2, Req2, HandlerState, Env)
+%% @todo Immediately crash if a response has already been sent.
+%% @todo Error out if HTTP/2.
+upgrade(Req0, Env, Handler, HandlerState, Timeout, Hibernate) ->
+ try websocket_upgrade(#state{handler=Handler, timeout=Timeout,
+ hibernate=Hibernate =:= hibernate}, Req0) of
+ {ok, State, Req} ->
+ websocket_handshake(State, Req, HandlerState, Env)
catch _:_ ->
+ %% @todo Probably log something here?
%% @todo Test that we can have 2 /ws 400 status code in a row on the same connection.
- cowboy_req:reply(400, Req),
- {ok, Req, Env}
+ %% @todo Does this even work?
+ {ok, cowboy_req:reply(400, Req0), Env}
end.
-spec websocket_upgrade(#state{}, Req)
@@ -91,7 +98,7 @@ websocket_upgrade(State, Req) ->
%% @todo Should probably send a 426 if the Upgrade header is missing.
[<<"websocket">>] = cowboy_req:parse_header(<<"upgrade">>, Req),
Version = cowboy_req:header(<<"sec-websocket-version">>, Req),
- IntVersion = list_to_integer(binary_to_list(Version)),
+ IntVersion = binary_to_integer(Version),
true = (IntVersion =:= 7) orelse (IntVersion =:= 8)
orelse (IntVersion =:= 13),
Key = cowboy_req:header(<<"sec-websocket-key">>, Req),
@@ -100,45 +107,44 @@ websocket_upgrade(State, Req) ->
-spec websocket_extensions(#state{}, Req)
-> {ok, #state{}, Req} when Req::cowboy_req:req().
-websocket_extensions(State, Req) ->
+websocket_extensions(State, Req=#{ref := Ref}) ->
%% @todo We want different options for this. For example
%% * compress everything auto
%% * compress only text auto
%% * compress only binary auto
%% * compress nothing auto (but still enabled it)
%% * disable compression
- Compress = maps:get(websocket_compress, Req, false),
- Req2 = Req#{websocket_compress => false},
- case {Compress, cowboy_req:parse_header(<<"sec-websocket-extensions">>, Req2)} of
+ Compress = maps:get(websocket_compress, ranch:get_protocol_options(Ref), false),
+ case {Compress, cowboy_req:parse_header(<<"sec-websocket-extensions">>, Req)} of
{true, Extensions} when Extensions =/= undefined ->
- websocket_extensions(State, Req2, Extensions, []);
+ websocket_extensions(State, Req, Extensions, []);
_ ->
- {ok, State, Req2}
+ {ok, State, Req}
end.
websocket_extensions(State, Req, [], []) ->
{ok, State, Req};
websocket_extensions(State, Req, [], [<<", ">>|RespHeader]) ->
{ok, State, cowboy_req:set_resp_header(<<"sec-websocket-extensions">>, lists:reverse(RespHeader), Req)};
-websocket_extensions(State=#state{extensions=Extensions}, Req, [{<<"permessage-deflate">>, Params}|Tail], RespHeader) ->
+websocket_extensions(State=#state{extensions=Extensions}, Req=#{pid := Pid},
+ [{<<"permessage-deflate">>, Params}|Tail], RespHeader) ->
%% @todo Make deflate options configurable.
Opts = #{level => best_compression, mem_level => 8, strategy => default},
- case cow_ws:negotiate_permessage_deflate(Params, Extensions, Opts) of
+ case cow_ws:negotiate_permessage_deflate(Params, Extensions, Opts#{owner => Pid}) of
{ok, RespExt, Extensions2} ->
- Req2 = Req#{websocket_compress => true},
websocket_extensions(State#state{extensions=Extensions2},
- Req2, Tail, [<<", ">>, RespExt|RespHeader]);
+ Req, Tail, [<<", ">>, RespExt|RespHeader]);
ignore ->
websocket_extensions(State, Req, Tail, RespHeader)
end;
-websocket_extensions(State=#state{extensions=Extensions}, Req, [{<<"x-webkit-deflate-frame">>, Params}|Tail], RespHeader) ->
+websocket_extensions(State=#state{extensions=Extensions}, Req=#{pid := Pid},
+ [{<<"x-webkit-deflate-frame">>, Params}|Tail], RespHeader) ->
%% @todo Make deflate options configurable.
Opts = #{level => best_compression, mem_level => 8, strategy => default},
- case cow_ws:negotiate_x_webkit_deflate_frame(Params, Extensions, Opts) of
+ case cow_ws:negotiate_x_webkit_deflate_frame(Params, Extensions, Opts#{owner => Pid}) of
{ok, RespExt, Extensions2} ->
- Req2 = cowboy_req:set_meta(websocket_compress, true, Req),
websocket_extensions(State#state{extensions=Extensions2},
- Req2, Tail, [<<", ">>, RespExt|RespHeader]);
+ Req, Tail, [<<", ">>, RespExt|RespHeader]);
ignore ->
websocket_extensions(State, Req, Tail, RespHeader)
end;
@@ -152,13 +158,11 @@ websocket_handshake(State=#state{key=Key},
Req=#{pid := Pid, streamid := StreamID}, HandlerState, Env) ->
Challenge = base64:encode(crypto:hash(sha,
<< Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" >>)),
- RespHeaders = maps:get(resp_headers, Req, #{}),
- Headers = maps:merge(RespHeaders, #{
- %% @todo Hmm should those be here or in cowboy_http?
+ Headers = cowboy_req:response_headers(#{
<<"connection">> => <<"Upgrade">>,
<<"upgrade">> => <<"websocket">>,
<<"sec-websocket-accept">> => Challenge
- }),
+ }, Req),
Pid ! {{Pid, StreamID}, {switch_protocol, Headers, ?MODULE, {Req, State, HandlerState}}},
{ok, Req, Env}.
@@ -369,15 +373,7 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState,
websocket_close(State, Req2, HandlerState2, stop)
catch Class:Reason ->
_ = websocket_close(State, Req, HandlerState, {crash, Class, Reason}),
- exit({cowboy_handler, [
- {class, Class},
- {reason, Reason},
- {mfa, {Handler, Callback, 3}},
- {stacktrace, erlang:get_stacktrace()},
- {msg, Message},
- {req, Req},
- {state, HandlerState}
- ]})
+ erlang:raise(Class, Reason, erlang:get_stacktrace())
end.
-spec websocket_send(cow_ws:frame(), #state{}) -> ok | stop | {error, atom()}.