mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-01 14:44:07 +02:00
Make websocket work over tls
This commit is contained in:
parent
6b3f228327
commit
2163cbb22e
@ -332,8 +332,8 @@ get_transfer_protocol(SockMod, HostPort) ->
|
|||||||
%% matches the requested URL path, and pass control to it. If none is
|
%% matches the requested URL path, and pass control to it. If none is
|
||||||
%% found, answer with HTTP 404.
|
%% found, answer with HTTP 404.
|
||||||
|
|
||||||
process([], _, _, _) -> ejabberd_web:error(not_found);
|
process([], _, _, _, _) -> ejabberd_web:error(not_found);
|
||||||
process(Handlers, Request, Socket, SockMod) ->
|
process(Handlers, Request, Socket, SockMod, Trail) ->
|
||||||
{HandlerPathPrefix, HandlerModule, HandlerOpts, HandlersLeft} =
|
{HandlerPathPrefix, HandlerModule, HandlerOpts, HandlersLeft} =
|
||||||
case Handlers of
|
case Handlers of
|
||||||
[{Pfx, Mod} | Tail] ->
|
[{Pfx, Mod} | Tail] ->
|
||||||
@ -352,16 +352,16 @@ process(Handlers, Request, Socket, SockMod) ->
|
|||||||
%% ["foo", "bar"]
|
%% ["foo", "bar"]
|
||||||
LocalPath = lists:nthtail(length(HandlerPathPrefix), Request#request.path),
|
LocalPath = lists:nthtail(length(HandlerPathPrefix), Request#request.path),
|
||||||
code:ensure_loaded(HandlerModule),
|
code:ensure_loaded(HandlerModule),
|
||||||
R = case erlang:function_exported(HandlerModule, socket_handoff, 5) of
|
R = case erlang:function_exported(HandlerModule, socket_handoff, 6) of
|
||||||
false ->
|
false ->
|
||||||
HandlerModule:process(LocalPath, Request);
|
HandlerModule:process(LocalPath, Request);
|
||||||
_ ->
|
_ ->
|
||||||
HandlerModule:socket_handoff(LocalPath, Request, Socket, SockMod, HandlerOpts)
|
HandlerModule:socket_handoff(LocalPath, Request, Socket, SockMod, Trail, HandlerOpts)
|
||||||
end,
|
end,
|
||||||
ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]),
|
ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]),
|
||||||
R;
|
R;
|
||||||
false ->
|
false ->
|
||||||
process(HandlersLeft, Request, Socket, SockMod)
|
process(HandlersLeft, Request, Socket, SockMod, Trail)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
extract_path_query(#state{request_method = Method,
|
extract_path_query(#state{request_method = Method,
|
||||||
@ -417,7 +417,8 @@ process_request(#state{request_method = Method,
|
|||||||
request_tp = TP,
|
request_tp = TP,
|
||||||
websocket_handlers = WebSocketHandlers,
|
websocket_handlers = WebSocketHandlers,
|
||||||
request_headers = RequestHeaders,
|
request_headers = RequestHeaders,
|
||||||
request_handlers = RequestHandlers} = State) ->
|
request_handlers = RequestHandlers,
|
||||||
|
trail = Trail} = State) ->
|
||||||
case extract_path_query(State) of
|
case extract_path_query(State) of
|
||||||
false ->
|
false ->
|
||||||
make_bad_request(State);
|
make_bad_request(State);
|
||||||
@ -442,7 +443,7 @@ process_request(#state{request_method = Method,
|
|||||||
tp = TP,
|
tp = TP,
|
||||||
headers = RequestHeaders,
|
headers = RequestHeaders,
|
||||||
ip = IP},
|
ip = IP},
|
||||||
case process(RequestHandlers ++ WebSocketHandlers, Request, Socket, SockMod) of
|
case process(RequestHandlers ++ WebSocketHandlers, Request, Socket, SockMod, Trail) of
|
||||||
El when is_record(El, xmlel) ->
|
El when is_record(El, xmlel) ->
|
||||||
make_xhtml_output(State, 200, [], El);
|
make_xhtml_output(State, 200, [], El);
|
||||||
{Status, Headers, El}
|
{Status, Headers, El}
|
||||||
|
@ -46,7 +46,8 @@
|
|||||||
path = [] :: [binary()],
|
path = [] :: [binary()],
|
||||||
headers = [] :: [{atom() | binary(), binary()}],
|
headers = [] :: [{atom() | binary(), binary()}],
|
||||||
local_path = [] :: [binary()],
|
local_path = [] :: [binary()],
|
||||||
q = [] :: [{binary() | nokey, binary()}]}).
|
q = [] :: [{binary() | nokey, binary()}],
|
||||||
|
buf :: binary()}).
|
||||||
|
|
||||||
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
|
-type method() :: 'GET' | 'HEAD' | 'DELETE' | 'OPTIONS' | 'PUT' | 'POST' | 'TRACE'.
|
||||||
-type protocol() :: http | https.
|
-type protocol() :: http | https.
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
handle_sync_event/4, code_change/4, handle_info/3,
|
handle_sync_event/4, code_change/4, handle_info/3,
|
||||||
terminate/3, send/2, setopts/2, sockname/1, peername/1,
|
terminate/3, send/2, setopts/2, sockname/1, peername/1,
|
||||||
controlling_process/2, become_controller/2, close/1,
|
controlling_process/2, become_controller/2, close/1,
|
||||||
socket_handoff/5]).
|
socket_handoff/6]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
@ -99,9 +99,9 @@ become_controller(FsmRef, C2SPid) ->
|
|||||||
close({http_ws, FsmRef, _IP}) ->
|
close({http_ws, FsmRef, _IP}) ->
|
||||||
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
||||||
|
|
||||||
socket_handoff(LocalPath, Request, Socket, SockMod, Opts) ->
|
socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts) ->
|
||||||
ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod,
|
ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod,
|
||||||
Opts, ?MODULE).
|
Buf, Opts, ?MODULE).
|
||||||
|
|
||||||
%%% Internal
|
%%% Internal
|
||||||
|
|
||||||
|
@ -35,8 +35,7 @@
|
|||||||
handle_sync_event/4, code_change/4, handle_info/3,
|
handle_sync_event/4, code_change/4, handle_info/3,
|
||||||
terminate/3, send_xml/2, setopts/2, sockname/1,
|
terminate/3, send_xml/2, setopts/2, sockname/1,
|
||||||
peername/1, controlling_process/2, become_controller/2,
|
peername/1, controlling_process/2, become_controller/2,
|
||||||
close/1,
|
close/1, socket_handoff/6]).
|
||||||
socket_handoff/5]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
@ -98,9 +97,9 @@ become_controller(FsmRef, C2SPid) ->
|
|||||||
close({http_ws, FsmRef, _IP}) ->
|
close({http_ws, FsmRef, _IP}) ->
|
||||||
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
catch gen_fsm:sync_send_all_state_event(FsmRef, close).
|
||||||
|
|
||||||
socket_handoff(LocalPath, Request, Socket, SockMod, Opts) ->
|
socket_handoff(LocalPath, Request, Socket, SockMod, Buf, Opts) ->
|
||||||
ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod,
|
ejabberd_websocket:socket_handoff(LocalPath, Request, Socket, SockMod,
|
||||||
Opts, ?MODULE).
|
Buf, Opts, ?MODULE).
|
||||||
|
|
||||||
%%% Internal
|
%%% Internal
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
-author('ecestari@process-one.net').
|
-author('ecestari@process-one.net').
|
||||||
|
|
||||||
-export([connect/2, check/2, socket_handoff/6]).
|
-export([connect/2, check/2, socket_handoff/7]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ is_acceptable(LocalPath, Origin, IP, Q, Headers, Protocol, _Origins,
|
|||||||
|
|
||||||
socket_handoff(LocalPath, #request{method = 'GET', ip = IP, q = Q, path = Path,
|
socket_handoff(LocalPath, #request{method = 'GET', ip = IP, q = Q, path = Path,
|
||||||
headers = Headers, host = Host, port = Port},
|
headers = Headers, host = Host, port = Port},
|
||||||
Socket, SockMod, Opts, HandlerModule) ->
|
Socket, SockMod, Buf, Opts, HandlerModule) ->
|
||||||
{_, Origin} = case lists:keyfind(<<"Sec-Websocket-Origin">>, 1, Headers) of
|
{_, Origin} = case lists:keyfind(<<"Sec-Websocket-Origin">>, 1, Headers) of
|
||||||
false ->
|
false ->
|
||||||
case lists:keyfind(<<"Origin">>, 1, Headers) of
|
case lists:keyfind(<<"Origin">>, 1, Headers) of
|
||||||
@ -128,7 +128,8 @@ socket_handoff(LocalPath, #request{method = 'GET', ip = IP, q = Q, path = Path,
|
|||||||
port = Port,
|
port = Port,
|
||||||
path = Path,
|
path = Path,
|
||||||
headers = Headers,
|
headers = Headers,
|
||||||
local_path = LocalPath},
|
local_path = LocalPath,
|
||||||
|
buf = Buf},
|
||||||
|
|
||||||
connect(WS, HandlerModule);
|
connect(WS, HandlerModule);
|
||||||
_ ->
|
_ ->
|
||||||
@ -139,11 +140,11 @@ socket_handoff(LocalPath, #request{method = 'GET', ip = IP, q = Q, path = Path,
|
|||||||
children = [{xmlcdata, <<"403 Forbiden">>}]}}
|
children = [{xmlcdata, <<"403 Forbiden">>}]}}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
socket_handoff(_, #request{method = 'OPTIONS'}, _, _, _, _) ->
|
socket_handoff(_, #request{method = 'OPTIONS'}, _, _, _, _, _) ->
|
||||||
{200, ?OPTIONS_HEADER, []};
|
{200, ?OPTIONS_HEADER, []};
|
||||||
socket_handoff(_, #request{method = 'HEAD'}, _, _, _, _) ->
|
socket_handoff(_, #request{method = 'HEAD'}, _, _, _, _, _) ->
|
||||||
{200, ?HEADER, []};
|
{200, ?HEADER, []};
|
||||||
socket_handoff(_, _, _, _, _, _) ->
|
socket_handoff(_, _, _, _, _, _, _) ->
|
||||||
{400, ?HEADER, #xmlel{name = <<"h1">>,
|
{400, ?HEADER, #xmlel{name = <<"h1">>,
|
||||||
children = [{xmlcdata, <<"400 Bad Request">>}]}}.
|
children = [{xmlcdata, <<"400 Bad Request">>}]}}.
|
||||||
|
|
||||||
@ -178,22 +179,30 @@ get_human_html_xmlel() ->
|
|||||||
"use WebSocket connection you need a Jabber/XMPP "
|
"use WebSocket connection you need a Jabber/XMPP "
|
||||||
"client that supports it.">>}]}]}]}.
|
"client that supports it.">>}]}]}]}.
|
||||||
|
|
||||||
connect(#ws{vsn = Vsn, socket = Socket, q = Q,
|
connect(#ws{vsn = Vsn, socket = Socket, sockmod = SockMod, origin = Origin,
|
||||||
origin = Origin, host = Host, port = Port,
|
host = Host, ws_autoexit = WsAutoExit} = Ws,
|
||||||
sockmod = SockMod, path = Path, headers = Headers,
|
|
||||||
ws_autoexit = WsAutoExit} =
|
|
||||||
Ws,
|
|
||||||
WsLoop) ->
|
WsLoop) ->
|
||||||
HandshakeServer = handshake(Vsn, Socket, SockMod,
|
% build handshake
|
||||||
Headers, {Path, Q, Origin, Host, Port}),
|
{NewWs, HandshakeResponse} = handshake(Ws),
|
||||||
SockMod:send(Socket, HandshakeServer),
|
% send handshake back
|
||||||
|
SockMod:send(Socket, HandshakeResponse),
|
||||||
|
|
||||||
?DEBUG("Sent handshake response : ~p",
|
?DEBUG("Sent handshake response : ~p",
|
||||||
[HandshakeServer]),
|
[HandshakeResponse]),
|
||||||
Ws0 = ejabberd_ws:new(Ws#ws{origin = Origin,
|
Ws0 = ejabberd_ws:new(Ws#ws{origin = Origin,
|
||||||
host = Host},
|
host = Host},
|
||||||
self()),
|
self()),
|
||||||
{ok, WsHandleLoopPid} = WsLoop:start_link(Ws0),
|
{ok, WsHandleLoopPid} = WsLoop:start_link(Ws0),
|
||||||
erlang:monitor(process, WsHandleLoopPid),
|
erlang:monitor(process, WsHandleLoopPid),
|
||||||
|
|
||||||
|
case NewWs#ws.buf of
|
||||||
|
<<>> ->
|
||||||
|
ok;
|
||||||
|
Data ->
|
||||||
|
self() ! {raw, Socket, Data}
|
||||||
|
end,
|
||||||
|
|
||||||
|
% set opts
|
||||||
case SockMod of
|
case SockMod of
|
||||||
gen_tcp ->
|
gen_tcp ->
|
||||||
inet:setopts(Socket, [{packet, 0}, {active, true}]);
|
inet:setopts(Socket, [{packet, 0}, {active, true}]);
|
||||||
@ -273,28 +282,18 @@ check_headers(Headers, RequiredHeaders) ->
|
|||||||
MissingHeaders -> MissingHeaders
|
MissingHeaders -> MissingHeaders
|
||||||
end.
|
end.
|
||||||
|
|
||||||
handshake({'draft-hixie', 0}, Sock, SocketMod, Headers,
|
recv_data(#ws{buf = Buf} = Ws, Length, _Timeout) when size(Buf) >= Length->
|
||||||
{Path, Q, Origin, Host, Port}) ->
|
<<Data:Length, Tail/binary>> = Buf,
|
||||||
{_, Key1} = lists:keyfind(<<"Sec-Websocket-Key1">>, 1,
|
{Ws#ws{buf = Tail}, Data};
|
||||||
Headers),
|
recv_data(#ws{buf = Buf, socket = Sock, sockmod = SockMod} = Ws, Length, Timeout) ->
|
||||||
{_, Key2} = lists:keyfind(<<"Sec-Websocket-Key2">>, 1,
|
case SockMod of
|
||||||
Headers),
|
|
||||||
HostPort = case lists:keyfind('Host', 1, Headers) of
|
|
||||||
{_, Value} -> Value;
|
|
||||||
_ ->
|
|
||||||
str:join([Host,
|
|
||||||
jlib:integer_to_binary(Port)],
|
|
||||||
<<":">>)
|
|
||||||
end,
|
|
||||||
case SocketMod of
|
|
||||||
gen_tcp ->
|
gen_tcp ->
|
||||||
inet:setopts(Sock, [{packet, raw}, {active, false}]);
|
inet:setopts(Sock, [{packet, raw}, {active, false}]);
|
||||||
_ ->
|
_ ->
|
||||||
SocketMod:setopts(Sock,
|
SockMod:setopts(Sock, [{packet, raw}, {active, false}])
|
||||||
[{packet, raw}, {active, false}])
|
|
||||||
end,
|
end,
|
||||||
Body = case SocketMod:recv(Sock, 8, 30 * 1000) of
|
Data = case SockMod:recv(Sock, Length - size(Buf), Timeout) of
|
||||||
{ok, Bin} -> Bin;
|
{ok, Bin} -> <<Buf/binary, Bin/binary>>;
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
?WARNING_MSG("timeout in reading websocket body", []),
|
?WARNING_MSG("timeout in reading websocket body", []),
|
||||||
<<>>;
|
<<>>;
|
||||||
@ -302,13 +301,35 @@ handshake({'draft-hixie', 0}, Sock, SocketMod, Headers,
|
|||||||
?ERROR_MSG("tcp error treating data: ~p", [_Other]),
|
?ERROR_MSG("tcp error treating data: ~p", [_Other]),
|
||||||
<<>>
|
<<>>
|
||||||
end,
|
end,
|
||||||
QParams = lists:map(fun ({nokey, <<>>}) -> none;
|
{Ws#ws{buf = <<>>}, Data}.
|
||||||
({K, V}) -> <<K/binary, "=", V/binary>>
|
|
||||||
|
% Function: List
|
||||||
|
% Description: Builds the server handshake response.
|
||||||
|
handshake(#ws{vsn = {'draft-hixie', 0}, headers = Headers, path = Path,
|
||||||
|
q = Q, origin = Origin, host = Host, port = Port,
|
||||||
|
sockmod = SockMod} = State) ->
|
||||||
|
|
||||||
|
{_, Key1} = lists:keyfind(<<"Sec-Websocket-Key1">>, 1, Headers),
|
||||||
|
{_, Key2} = lists:keyfind(<<"Sec-Websocket-Key2">>, 1, Headers),
|
||||||
|
HostPort = case lists:keyfind('Host', 1, Headers) of
|
||||||
|
{_, Value} -> Value;
|
||||||
|
_ -> string:join([Host, integer_to_list(Port)],":")
|
||||||
end,
|
end,
|
||||||
Q),
|
{NewState, Body} = recv_data(State, 8, 30*1000),
|
||||||
|
|
||||||
|
QParams = lists:map(
|
||||||
|
fun({nokey,[]})->
|
||||||
|
none;
|
||||||
|
({K, V})->
|
||||||
|
<<K/binary, "=", V/binary>>
|
||||||
|
end, Q),
|
||||||
QString = case QParams of
|
QString = case QParams of
|
||||||
[none] -> <<"">>;
|
[none]-> "";
|
||||||
QParams -> <<"?", (str:join(QParams, <<"&">>))/binary>>
|
QParams-> "?" ++ string:join(QParams, "&")
|
||||||
|
end,
|
||||||
|
Protocol = case SockMod of
|
||||||
|
gen_tcp -> <<"ws://">>;
|
||||||
|
_ -> <<"wss://">>
|
||||||
end,
|
end,
|
||||||
SubProtocolHeader = case find_subprotocol(Headers) of
|
SubProtocolHeader = case find_subprotocol(Headers) of
|
||||||
false ->
|
false ->
|
||||||
@ -316,22 +337,26 @@ handshake({'draft-hixie', 0}, Sock, SocketMod, Headers,
|
|||||||
V ->
|
V ->
|
||||||
[<<"Sec-Websocket-Protocol:">>, V, <<"\r\n">>]
|
[<<"Sec-Websocket-Protocol:">>, V, <<"\r\n">>]
|
||||||
end,
|
end,
|
||||||
[<<"HTTP/1.1 101 WebSocket Protocol Handshake\r\n">>,
|
{NewState, [<<"HTTP/1.1 101 WebSocket Protocol Handshake\r\n">>,
|
||||||
<<"Upgrade: WebSocket\r\n">>,
|
<<"Upgrade: WebSocket\r\n">>,
|
||||||
<<"Connection: Upgrade\r\n">>,
|
<<"Connection: Upgrade\r\n">>,
|
||||||
<<"Sec-WebSocket-Origin: ">>, Origin, <<"\r\n">>,
|
<<"Sec-WebSocket-Origin: ">>, Origin, <<"\r\n">>,
|
||||||
SubProtocolHeader,
|
SubProtocolHeader,
|
||||||
<<"Sec-WebSocket-Location: ws://">>, HostPort, <<"/">>,
|
<<"Sec-WebSocket-Location: ">>, Protocol, HostPort, <<"/">>,
|
||||||
str:join(Path, <<"/">>), QString, <<"\r\n\r\n">>,
|
str:join(Path, <<"/">>), QString, <<"\r\n\r\n">>,
|
||||||
build_challenge({'draft-hixie', 0},
|
build_challenge({'draft-hixie', 0},
|
||||||
{Key1, Key2, Body})];
|
{Key1, Key2, Body})]};
|
||||||
handshake({'draft-hixie', 68}, _Sock, _SocketMod,
|
handshake(#ws{vsn = {'draft-hixie', 68}, headers = Headers, origin = Origin,
|
||||||
Headers, {Path, _Q, Origin, Host, Port}) ->
|
path = Path, host = Host, port = Port, sockmod = SockMod} = State) ->
|
||||||
SubProtocolHeader = case find_subprotocol(Headers) of
|
SubProtocolHeader = case find_subprotocol(Headers) of
|
||||||
false ->
|
false ->
|
||||||
[];
|
[];
|
||||||
V ->
|
V ->
|
||||||
[<<"Sec-Websocket-Protocol:">>, V, <<"\r\n">>]
|
[<<"Websocket-Protocol:">>, V, <<"\r\n">>]
|
||||||
|
end,
|
||||||
|
Protocol = case SockMod of
|
||||||
|
gen_tcp -> "ws://";
|
||||||
|
_ -> "wss://"
|
||||||
end,
|
end,
|
||||||
HostPort = case lists:keyfind('Host', 1, Headers) of
|
HostPort = case lists:keyfind('Host', 1, Headers) of
|
||||||
{_, Value} -> Value;
|
{_, Value} -> Value;
|
||||||
@ -340,16 +365,15 @@ handshake({'draft-hixie', 68}, _Sock, _SocketMod,
|
|||||||
iolist_to_binary(integer_to_list(Port))],
|
iolist_to_binary(integer_to_list(Port))],
|
||||||
<<":">>)
|
<<":">>)
|
||||||
end,
|
end,
|
||||||
[<<"HTTP/1.1 101 Web Socket Protocol Handshake\r\n">>,
|
{State, [<<"HTTP/1.1 101 Web Socket Protocol Handshake\r\n">>,
|
||||||
<<"Upgrade: WebSocket\r\n">>,
|
<<"Upgrade: WebSocket\r\n">>,
|
||||||
<<"Connection: Upgrade\r\n">>,
|
<<"Connection: Upgrade\r\n">>,
|
||||||
<<"WebSocket-Origin: ">>, Origin, <<"\r\n">>,
|
<<"WebSocket-Origin: ">>, Origin, <<"\r\n">>,
|
||||||
SubProtocolHeader,
|
SubProtocolHeader,
|
||||||
<<"WebSocket-Location: ws://">>,
|
<<"WebSocket-Location: ">>, Protocol,
|
||||||
HostPort, <<"/">>, str:join(Path, <<"/">>),
|
HostPort, <<"/">>, str:join(Path, <<"/">>),
|
||||||
<<"\r\n\r\n">>];
|
<<"\r\n\r\n">>]};
|
||||||
handshake({'draft-hybi', _}, _Sock, _SocketMod, Headers,
|
handshake(#ws{vsn = {'draft-hybi', _}, headers = Headers} = State) ->
|
||||||
{_Path, _Q, _Origin, _Host, _Port}) ->
|
|
||||||
{_, Key} = lists:keyfind(<<"Sec-Websocket-Key">>, 1,
|
{_, Key} = lists:keyfind(<<"Sec-Websocket-Key">>, 1,
|
||||||
Headers),
|
Headers),
|
||||||
SubProtocolHeader = case find_subprotocol(Headers) of
|
SubProtocolHeader = case find_subprotocol(Headers) of
|
||||||
@ -360,11 +384,11 @@ handshake({'draft-hybi', _}, _Sock, _SocketMod, Headers,
|
|||||||
end,
|
end,
|
||||||
Hash = jlib:encode_base64(
|
Hash = jlib:encode_base64(
|
||||||
sha:sha1(<<Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>)),
|
sha:sha1(<<Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>)),
|
||||||
[<<"HTTP/1.1 101 Switching Protocols\r\n">>,
|
{State, [<<"HTTP/1.1 101 Switching Protocols\r\n">>,
|
||||||
<<"Upgrade: websocket\r\n">>,
|
<<"Upgrade: websocket\r\n">>,
|
||||||
<<"Connection: Upgrade\r\n">>,
|
<<"Connection: Upgrade\r\n">>,
|
||||||
SubProtocolHeader,
|
SubProtocolHeader,
|
||||||
<<"Sec-WebSocket-Accept: ">>, Hash, <<"\r\n\r\n">>].
|
<<"Sec-WebSocket-Accept: ">>, Hash, <<"\r\n\r\n">>]}.
|
||||||
|
|
||||||
build_challenge({'draft-hixie', 0},
|
build_challenge({'draft-hixie', 0},
|
||||||
{Key1, Key2, Key3}) ->
|
{Key1, Key2, Key3}) ->
|
||||||
@ -395,17 +419,17 @@ find_subprotocol(Headers) ->
|
|||||||
ws_loop(Vsn, HandlerState, Socket, WsHandleLoopPid,
|
ws_loop(Vsn, HandlerState, Socket, WsHandleLoopPid,
|
||||||
SocketMode, WsAutoExit) ->
|
SocketMode, WsAutoExit) ->
|
||||||
receive
|
receive
|
||||||
{tcp, Socket, Data} ->
|
{DataType, _Socket, Data} when DataType =:= tcp orelse DataType =:= raw ->
|
||||||
{NewHandlerState, ToSend} = handle_data(Vsn,
|
case handle_data(DataType, Vsn, HandlerState, Data, Socket, WsHandleLoopPid, SocketMode, WsAutoExit) of
|
||||||
HandlerState, Data, Socket,
|
{NewHandlerState, ToSend} ->
|
||||||
WsHandleLoopPid, SocketMode,
|
|
||||||
WsAutoExit),
|
|
||||||
lists:foreach(fun(Pkt) -> SocketMode:send(Socket, Pkt)
|
lists:foreach(fun(Pkt) -> SocketMode:send(Socket, Pkt)
|
||||||
end,
|
end, ToSend),
|
||||||
ToSend),
|
ws_loop(Vsn, NewHandlerState, Socket, WsHandleLoopPid, SocketMode, WsAutoExit);
|
||||||
ws_loop(Vsn, NewHandlerState, Socket, WsHandleLoopPid,
|
Error ->
|
||||||
SocketMode, WsAutoExit);
|
?DEBUG("tls decode error ~p", [Error]),
|
||||||
{tcp_closed, Socket} ->
|
websocket_close(Socket, WsHandleLoopPid, SocketMode, WsAutoExit)
|
||||||
|
end;
|
||||||
|
{tcp_closed, _Socket} ->
|
||||||
?DEBUG("tcp connection was closed, exit", []),
|
?DEBUG("tcp connection was closed, exit", []),
|
||||||
websocket_close(Socket, WsHandleLoopPid, SocketMode,
|
websocket_close(Socket, WsHandleLoopPid, SocketMode,
|
||||||
WsAutoExit);
|
WsAutoExit);
|
||||||
@ -609,6 +633,16 @@ process_hybi_8(#hybi_8_state{unprocessed =
|
|||||||
process_hybi_8(State#hybi_8_state{unprocessed = <<>>},
|
process_hybi_8(State#hybi_8_state{unprocessed = <<>>},
|
||||||
<<UnprocessedPre/binary, Data/binary>>).
|
<<UnprocessedPre/binary, Data/binary>>).
|
||||||
|
|
||||||
|
handle_data(tcp, Vsn, State, Data, Socket, WsHandleLoopPid, tls, WsAutoExit) ->
|
||||||
|
case tls:recv_data(Socket, Data) of
|
||||||
|
{ok, NewData} ->
|
||||||
|
handle_data(Vsn, State, NewData, Socket, WsHandleLoopPid, tls, WsAutoExit);
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
handle_data(_, Vsn, State, Data, Socket, WsHandleLoopPid, SockMod, WsAutoExit) ->
|
||||||
|
handle_data(Vsn, State, Data, Socket, WsHandleLoopPid, SockMod, WsAutoExit).
|
||||||
|
|
||||||
handle_data({'draft-hybi', _}, State, Data, _Socket,
|
handle_data({'draft-hybi', _}, State, Data, _Socket,
|
||||||
WsHandleLoopPid, _SocketMode, _WsAutoExit) ->
|
WsHandleLoopPid, _SocketMode, _WsAutoExit) ->
|
||||||
{NewState, Recv, Send} = process_hybi_8(State, Data),
|
{NewState, Recv, Send} = process_hybi_8(State, Data),
|
||||||
|
Loading…
Reference in New Issue
Block a user