From 35a0e27d0406cbbc969c9c70081f0152d4361bab Mon Sep 17 00:00:00 2001 From: Eric Cestari Date: Mon, 13 Sep 2010 12:04:52 +0200 Subject: [PATCH] [TECH-1511] clean support for websockets. Added handlers in configuration file --- src/web/ejabberd_http.erl | 43 +++++++++++++++++++++++++++------- src/web/ejabberd_http.hrl | 4 +++- src/web/ejabberd_websocket.erl | 20 +++++++++------- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/web/ejabberd_http.erl b/src/web/ejabberd_http.erl index 5365a5b69..4be20e817 100644 --- a/src/web/ejabberd_http.erl +++ b/src/web/ejabberd_http.erl @@ -65,7 +65,8 @@ request_tp, request_headers = [], end_of_request = false, - trail = "" + trail = "", + websocket_handlers = [] }). @@ -134,11 +135,16 @@ init({SockMod, Socket}, Opts) -> false -> [] end, ?DEBUG("S: ~p~n", [RequestHandlers]), - + WebSocketHandlers = case lists:keysearch(websocket_handlers, 1, Opts) of + {value, {websocket_handlers, WH}} -> WH; + false -> [] + end, + ?DEBUG("WS: ~p~n", [WebSocketHandlers]), ?INFO_MSG("started: ~p", [{SockMod1, Socket1}]), State = #state{sockmod = SockMod1, socket = Socket1, - request_handlers = RequestHandlers}, + request_handlers = RequestHandlers, + websocket_handlers = WebSocketHandlers}, receive_headers(State). @@ -148,6 +154,9 @@ become_controller(_Pid) -> socket_type() -> raw. + +send_text(State, none) -> + exit(normal); send_text(State, Text) -> case catch (State#state.sockmod):send(State#state.socket, Text) of ok -> ok; @@ -315,6 +324,23 @@ get_transfer_protocol(SockMod, HostPort) -> %% found, answer with HTTP 404. process([], _) -> ejabberd_web:error(not_found); +process(Handlers, #ws{} = Ws)-> + [{HandlerPathPrefix, HandlerModule} | HandlersLeft] = Handlers, + case (lists:prefix(HandlerPathPrefix, Ws#ws.path) or + (HandlerPathPrefix==Ws#ws.path)) of + true -> + ?DEBUG("~p matches ~p", [Ws#ws.path, HandlerPathPrefix]), + %% LocalPath is the path "local to the handler", i.e. if + %% the handler was registered to handle "/test/" and the + %% requested path is "/test/foo/bar", the local path is + %% ["foo", "bar"] + LocalPath = lists:nthtail(length(HandlerPathPrefix), Ws#ws.path), + ejabberd_hooks:run(ws_debug, [{LocalPath, Ws}]), + ejabberd_websocket:connect(Ws#ws{local_path = LocalPath}, HandlerModule); + false -> + ?DEBUG("HandlersLeft : ~p ", [HandlersLeft]), + process(HandlersLeft, Ws) + end; process(Handlers, Request) -> [{HandlerPathPrefix, HandlerModule} | HandlersLeft] = Handlers, @@ -344,6 +370,7 @@ process_request(#state{request_method = Method, request_tp = TP, request_headers = RequestHeaders, sockmod = SockMod, + websocket_handlers = WebSocketHandlers, socket = Socket} = State) when Method=:='GET' orelse Method=:='HEAD' orelse Method=:='DELETE' orelse Method=:='OPTIONS' -> case (catch url_decode_q_split(Path)) of @@ -375,17 +402,17 @@ process_request(#state{request_method = Method, Ws = #ws{socket = Socket, sockmod = SockMod, ws_autoexit = true, - path = Path, + path = LPath, vsn = VSN, host = Host, + port = Port, origin = Origin, headers = RequestHeaders }, - - - + ?DEBUG("WS: ~p/~p~n", [WebSocketHandlers, Path]), ?DEBUG("It is a websocket version : ~p",[VSN]), - ejabberd_websocket:connect(Ws, websocket_test); + process(WebSocketHandlers, Ws), + none; false -> Request = #request{method = Method, path = LPath, diff --git a/src/web/ejabberd_http.hrl b/src/web/ejabberd_http.hrl index 09f8ff832..5a163b799 100644 --- a/src/web/ejabberd_http.hrl +++ b/src/web/ejabberd_http.hrl @@ -45,6 +45,8 @@ vsn, % {Maj,Min} | {'draft-hixie', Ver} origin, % the originator host, % the host + port, path, % the websocket GET request path - headers % [{Tag, Val}] + headers, % [{Tag, Val}] + local_path }). \ No newline at end of file diff --git a/src/web/ejabberd_websocket.erl b/src/web/ejabberd_websocket.erl index e18a77860..a85b6f448 100644 --- a/src/web/ejabberd_websocket.erl +++ b/src/web/ejabberd_websocket.erl @@ -41,7 +41,7 @@ -compile(export_all). -include("ejabberd.hrl"). -include("jlib.hrl"). --include ("ejabberd_http.hrl"). +-include("ejabberd_http.hrl"). check(_Path, Headers)-> ?DEBUG("testing for a websocket request path: ~p headers: ~p", [_Path, Headers]), @@ -51,9 +51,9 @@ check(_Path, Headers)-> check_websockets(VsnSupported, Headers). % Connect and handshake with Websocket. -connect(#ws{vsn = Vsn, socket = Socket, origin=Origin, host=Host,sockmod = SockMod, path = Path, headers = Headers, ws_autoexit = WsAutoExit} = Ws, WsLoop) -> +connect(#ws{vsn = Vsn, socket = Socket, origin=Origin, host=Host, port=Port, sockmod = SockMod, path = Path, headers = Headers, ws_autoexit = WsAutoExit} = Ws, WsLoop) -> % build handshake - HandshakeServer = handshake(Vsn, Socket,SockMod, Headers, {Path, Origin, Host}), + HandshakeServer = handshake(Vsn, Socket,SockMod, Headers, {Path, Origin, Host, Port}), % send handshake back ?DEBUG("building handshake response : ~p", [HandshakeServer]), SockMod:send(Socket, HandshakeServer), @@ -136,7 +136,7 @@ check_headers(Headers, RequiredHeaders) -> % Function: List % Description: Builds the server handshake response. -handshake({'draft-hixie', 76}, Sock,SocketMod, Headers, {Path, Origin, Host}) -> +handshake({'draft-hixie', 76}, Sock,SocketMod, Headers, {Path, Origin, Host, Port}) -> % build data {_, Key1} = lists:keyfind("Sec-Websocket-Key1",1, Headers), {_, Key2} = lists:keyfind("Sec-Websocket-Key2",1, Headers), @@ -156,22 +156,26 @@ handshake({'draft-hixie', 76}, Sock,SocketMod, Headers, {Path, Origin, Host}) -> ?ERROR_MSG("tcp error treating data: ~p", [_Other]), <<>> end, - ?DEBUG("got content in body of websocket request: ~p", [Body]), + ?DEBUG("got content in body of websocket request: ~p, ~p", [Body,string:join([Host, Path],"/")]), % prepare handhsake response ["HTTP/1.1 101 WebSocket Protocol Handshake\r\n", "Upgrade: WebSocket\r\n", "Connection: Upgrade\r\n", "Sec-WebSocket-Origin: ", Origin, "\r\n", - "Sec-WebSocket-Location: ws://", Host, ":5280",Path, "\r\n\r\n", + "Sec-WebSocket-Location: ws://", + string:join([Host, integer_to_list(Port)],":"), + "/",string:join(Path,"/") , "\r\n\r\n", build_challenge({'draft-hixie', 76}, {Key1, Key2, Body}) ]; -handshake({'draft-hixie', 68}, _Sock,_SocketMod, _Headers, {Path, Origin, Host}) -> +handshake({'draft-hixie', 68}, _Sock,_SocketMod, _Headers, {Path, Origin, Host, Port}) -> % prepare handhsake response ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n", "Upgrade: WebSocket\r\n", "Connection: Upgrade\r\n", "WebSocket-Origin: ", Origin , "\r\n", - "WebSocket-Location: ws://", lists:concat([Host, Path]), "\r\n\r\n" + "WebSocket-Location: ws://", + lists:concat([Host, integer_to_list(Port)]), + "/",string:join(Path,"/"), "\r\n\r\n" ]. % Function: List