add HTML for example
Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/ecee927a Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/ecee927a Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/ecee927a Branch: refs/heads/1843-feature-bigcouch Commit: ecee927a04a51858677cd13def37f575ebbb33e7 Parents: 8ecae10 Author: Bob Ippolito <[email protected]> Authored: Wed Dec 25 15:06:50 2013 -0800 Committer: Bob Ippolito <[email protected]> Committed: Wed Dec 25 15:06:50 2013 -0800 ---------------------------------------------------------------------- .gitignore | 2 + examples/websocket/index.html | 59 +++++++++++++++++ examples/websocket/websocket.erl | 121 ++++++++++++++++++---------------- src/mochiweb_websocket.erl | 10 ++- 4 files changed, 135 insertions(+), 57 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ecee927a/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index 09f877f..8f4edf4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ /TEST-*.xml /deps *.swp +*.beam +*.dump http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ecee927a/examples/websocket/index.html ---------------------------------------------------------------------- diff --git a/examples/websocket/index.html b/examples/websocket/index.html new file mode 100644 index 0000000..6926aba --- /dev/null +++ b/examples/websocket/index.html @@ -0,0 +1,59 @@ +<!doctype html> +<html> +<head> + <title>Websockets With Mochiweb Demo</title> +</head> +<body> +<h1>Mochiweb websocket demo</h1> + + <div id="connect"> + <button id="btnConn">Connect</button> + State: <span id="connstate" style="font-weight:bold;"></span> + </div> + <br/><i>Protip: open your javascript error console, just in case..</i><br/> + <hr/> + <div id="connected"> + <form id="sendForm"> + <input id="phrase" type="text"/> + <input id="btnSend" class="button" type="submit" name="connect" + value="Send"/> + </form> + </div> + <hr/> + <div id="msgs"></div> + + <script type="text/javascript"> + var ws; + if (!window.WebSocket) { + alert("WebSocket not supported by this browser"); + } + function $(id) { + return document.getElementById(id); + } + function go() { + ws = new WebSocket("ws://" + location.host + "/"); + ws.onopen = function () { + $('connstate').innerHTML = 'CONNECTED'; + } + ws.onclose = function () { + $('connstate').innerHTML = 'CLOSED'; + } + ws.onmessage = function (e) { + var p = document.createElement('pre'); + p.appendChild(document.createTextNode(e.data)); + $('msgs').appendChild(p); + } + } + $('sendForm').onsubmit = function (event) { + var p = $('phrase'); + ws.send(p.value); + p.value=''; + return false; + } + $('btnConn').onclick = function(event) { + go(); return false; + }; + </script> + </body> +</html> + http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ecee927a/examples/websocket/websocket.erl ---------------------------------------------------------------------- diff --git a/examples/websocket/websocket.erl b/examples/websocket/websocket.erl index b392a20..4049941 100644 --- a/examples/websocket/websocket.erl +++ b/examples/websocket/websocket.erl @@ -1,82 +1,93 @@ -module(websocket). -% The MIT License (MIT) +%% To run: erlc websocket.erl && erl -pa ../../ebin -s websocket + +%% The MIT License (MIT) %% Copyright (c) 2012 Zadane.pl sp. z o.o. -% Permission is hereby granted, free of charge, to any person obtaining a copy -% of this software and associated documentation files (the "Software"), to deal -% in the Software without restriction, including without limitation the rights -% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -% copies of the Software, and to permit persons to whom the Software is -% furnished to do so, subject to the following conditions: +%% Permission is hereby granted, free of charge, to any person obtaining a copy +%% of this software and associated documentation files (the "Software"), to deal +%% in the Software without restriction, including without limitation the rights +%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +%% copies of the Software, and to permit persons to whom the Software is +%% furnished to do so, subject to the following conditions: -% The above copyright notice and this permission notice shall be included in -% all copies or substantial portions of the Software. +%% The above copyright notice and this permission notice shall be included in +%% all copies or substantial portions of the Software. -% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -% THE SOFTWARE. +%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +%% THE SOFTWARE. --export([start_link/0, ws_loop/3, loop/1]). +-export([start/0, start_link/0, ws_loop/3, loop/1]). -% -% Mochiweb websocket example -% -% [1]: At first you have to start HTTP server which will listen for HTTP requests -% and eventually upgrade connection to websocket -% [2]: Attempt to upgrade connection to websocket. -% Function mochiweb_websocket:upgrade_connection/2: -% * first argument is mochiweb_request -% * second is M:F which will handle further websocket messages. -% Function return two funs: -% * ReentryWs/1 - use it to enter to messages handling loop (in this example ws_loop/3) -% * ReplyChannel/1 - use to send messages to client. May be passed to other processes -% [3]: Example of sending message to client -% [4]: State that will be passed to message handling loop -% [5]: Pass controll to messages handling loop. From this moment each message received from client -% can be handled... -% [6]: ...here as Payload. State is variable intended for holiding your custom state. ReplyChannel -% is the same function as in [3]. -% Notice! Payload is list of messages received from client. Websocket framing mechanism -% concatenates messages which are sent one after another in short time. -% [7]: Print payload received from client and send it back -% [8]: Message handling function must return new state value -start_link() -> - % [1] - Loop = fun (Req) -> - ?MODULE:loop(Req) - end, +%% +%% Mochiweb websocket example +%% +%% [1]: At first you have to start HTTP server which will listen for HTTP +%% requests and eventually upgrade connection to websocket +%% [2]: Attempt to upgrade connection to websocket. +%% Function mochiweb_websocket:upgrade_connection/2: +%% * first argument is mochiweb_request +%% * second is M:F which will handle further websocket messages. +%% Function return two funs: +%% * ReentryWs/1 - use it to enter to messages handling loop +%% (in this example ws_loop/3) +%% * ReplyChannel/1 - use to send messages to client. May be passed to +%% other processes +%% [3]: Example of sending message to client +%% [4]: State that will be passed to message handling loop +%% [5]: Pass control to messages handling loop. From this moment each message +%% received from client can be handled... +%% [6]: ...here as Payload. State is variable intended for holding your custom +%% state. ReplyChannel is the same function as in [3]. +%% Notice! Payload is list of messages received from client. Websocket +%% framing mechanism concatenates messages which are sent one after another +%% in short time. +%% [7]: Print payload received from client and send it back +%% [8]: Message handling function must return new state value +start() -> + application:start(sasl), + start_link(), + erlang:hibernate(?MODULE, start, []). +start_link() -> + %% [1] + io:format("Listening at http://127.0.0.1:8080/~n"), mochiweb_http:start_link([ - {name, client_access}, - {loop, Loop}, + {name, client_access}, + {loop, fun ?MODULE:loop/1}, {port, 8080} ]). ws_loop(Payload, State, ReplyChannel) -> - % [6] + %% [6] - % [7] + %% [7] io:format("Received data: ~p~n", [Payload]), Received = list_to_binary(Payload), ReplyChannel(<<"Received ", Received/binary>>), - % [8] + %% [8] State. loop(Req) -> - % [2] - {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, {?MODULE, ws_loop}), + H = mochiweb_request:get_header_value("Upgrade", Req), + loop(Req, H =/= undefined andalso string:to_lower(H) =:= "websocket"). - % [3] +loop(Req, false) -> + mochiweb_request:serve_file("index.html", "./", Req); +loop(Req, true) -> + {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection( + Req, fun ?MODULE:ws_loop/3), + %% [3] ReplyChannel(<<"Hello">>), - - % [4] + %% [4] InitialState = [], - % [5] + %% [5] ReentryWs(InitialState). http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ecee927a/src/mochiweb_websocket.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_websocket.erl b/src/mochiweb_websocket.erl index 1880f86..8309a11 100644 --- a/src/mochiweb_websocket.erl +++ b/src/mochiweb_websocket.erl @@ -45,7 +45,6 @@ request(Socket, Body, State, WsVersion, ReplyChannel) -> exit(normal); {tcp, _, WsFrames} -> - {M, F} = Body, case parse_frames(WsVersion, WsFrames, Socket) of close -> mochiweb_socket:close(Socket), @@ -56,7 +55,7 @@ request(Socket, Body, State, WsVersion, ReplyChannel) -> exit(normal); Payload -> - NewState = M:F(Payload, State, ReplyChannel), + NewState = call_body(Body, Payload, State, ReplyChannel), loop(Socket, Body, NewState, WsVersion, ReplyChannel) end; @@ -65,6 +64,13 @@ request(Socket, Body, State, WsVersion, ReplyChannel) -> exit(normal) end. +call_body({M, F, A}, Payload, State, ReplyChannel) -> + erlang:apply(M, F, [Payload, State, ReplyChannel | A]); +call_body({M, F}, Payload, State, ReplyChannel) -> + M:F(Payload, State, ReplyChannel); +call_body(Body, Payload, State, ReplyChannel) -> + Body(Payload, State, ReplyChannel). + send(Socket, Payload, hybi) -> Len = payload_length(iolist_size(Payload)), Data = <<1:1, 0:3, 1:4, 0:1, Len/bits, Payload/binary>>,
