Permalink
Browse files

* added support to websocket protocol hixie draft 76 (thanks to serg…

…io veiga)

 * added support to multiple websocket draft protocols (for backwards compatibility)
 * added ws_autoexit option which allows to get an event on websocket controlling processes (issue track #15, suggestion of esente)
 * added headers also in misultin websockets (thanks to jlirochon)
 * made it basho's rebar friendly (thanks to mrinalwadhwa)
  • Loading branch information...
1 parent ce00531 commit 7374dac4cc56cfeaf119f7f3bca825ff62a58d32 @ostinelli committed Aug 11, 2010
View
@@ -8,7 +8,7 @@ ERLC_FLAGS := -W -I $(INCLUDE_DIR) -o $(EBIN_DIR)
all:
@mkdir -p $(EBIN_DIR)
$(ERLC) $(ERLC_FLAGS) $(SRC_DIR)/*.erl
- @cp $(SRC_DIR)/*.app $(EBIN_DIR)/
+ @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app
clean:
@rm -rf $(EBIN_DIR)/*
@@ -17,10 +17,10 @@ clean:
debug:
@mkdir -p $(EBIN_DIR)
$(ERLC) -D log_debug $(ERLC_FLAGS) $(SRC_DIR)/*.erl
- @cp $(SRC_DIR)/*.app $(EBIN_DIR)/
+ @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app
example:
@mkdir -p $(EBIN_DIR)
$(ERLC) $(ERLC_FLAGS) $(SRC_DIR)/*.erl
- @cp $(SRC_DIR)/*.app $(EBIN_DIR)/
+ @cp $(SRC_DIR)/misultin.app.src $(EBIN_DIR)/misultin.app
$(ERLC) $(ERLC_FLAGS) $(EXAMPLES_DIR)/*.erl
View
@@ -63,6 +63,12 @@ API Documentation is available online on the Misultin's wiki: http://code.google
CHANGELOG
==========================================================================================================
+0.6.1: - added support to websocket protocol hixie draft 76 [thanks to sergio veiga]
+ - added support to multiple websocket draft protocols [for backwards compatibility]
+ - added ws_autoexit option which allows to get an event on websocket controlling processes [issue track #15, suggestion of esente]
+ - added headers also in misultin websockets [thanks to jlirochon]
+ - made it basho's rebar friendly [thanks to mrinalwadhwa]
+
0.6: - added HTTP compression option
- refactoring of the main server loop, so that it is now isolated from the HTTP functionality
- removed unnecessary compilation warnings
@@ -0,0 +1,103 @@
+% ==========================================================================================================
+% MISULTIN - Example: Shows misultin Websocket With an event support.
+%
+% >-|-|-(°>
+%
+% Copyright (C) 2010, Roberto Ostinelli <roberto@ostinelli.net>
+% All rights reserved.
+%
+% BSD License
+%
+% Redistribution and use in source and binary forms, with or without modification, are permitted provided
+% that the following conditions are met:
+%
+% * Redistributions of source code must retain the above copyright notice, this list of conditions and the
+% following disclaimer.
+% * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+% the following disclaimer in the documentation and/or other materials provided with the distribution.
+% * Neither the name of the authors nor the names of its contributors may be used to endorse or promote
+% products derived from this software without specific prior written permission.
+%
+% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+% ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+% POSSIBILITY OF SUCH DAMAGE.
+% ==========================================================================================================
+-module(misultin_websocket_event_example).
+-export([start/1, stop/0]).
+
+% start misultin http server
+start(Port) ->
+ misultin:start_link([
+ {port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end},
+ {ws_loop, fun(Ws) -> handle_websocket(Ws) end}, {ws_autoexit, false}
+ ]).
+
+% stop misultin
+stop() ->
+ misultin:stop().
+
+% callback on request received
+handle_http(Req, Port) ->
+ % output
+ Req:ok([{"Content-Type", "text/html"}],
+ ["
+ <html>
+ <head>
+ <script type=\"text/javascript\">
+ function addStatus(text){
+ var date = new Date();
+ document.getElementById('status').innerHTML = document.getElementById('status').innerHTML + date + \": \" + text + \"<br>\";
+ }
+ function ready(){
+ if (\"WebSocket\" in window) {
+ // browser supports websockets
+ var ws = new WebSocket(\"ws://localhost:", integer_to_list(Port) ,"/service\");
+ ws.onopen = function() {
+ // websocket is connected
+ addStatus(\"websocket connected!\");
+ // send hello data to server.
+ ws.send(\"hello server!\");
+ addStatus(\"sent message to server: 'hello server'!\");
+ };
+ ws.onmessage = function (evt) {
+ var receivedMsg = evt.data;
+ addStatus(\"server sent the following: '\" + receivedMsg + \"'\");
+ };
+ ws.onclose = function() {
+ // websocket was closed
+ addStatus(\"websocket was closed\");
+ };
+ } else {
+ // browser does not support websockets
+ addStatus(\"sorry, your browser does not support websockets.\");
+ }
+ }
+ </script>
+ </head>
+ <body onload=\"ready();\">
+ <div id=\"status\"></div>
+ </body>
+ </html>"]).
+
+% callback on received websockets data
+handle_websocket(Ws) ->
+ receive
+ {browser, Data} ->
+ Ws:send(["received '", Data, "'"]),
+ handle_websocket(Ws);
+ closed ->
+ % IMPORTANT: since we specified the {ws_autoexit, false} option, we need to manually ensure that this process exists
+ % [otherwise it will become a zombie]
+ io:format("The WebSocket was CLOSED!~n"),
+ closed;
+ _Ignore ->
+ handle_websocket(Ws)
+ after 5000 ->
+ Ws:send("pushing!"),
+ handle_websocket(Ws)
+ end.
View
@@ -58,7 +58,8 @@
compress, % send compressed output if supported by browser
stream_support, % stream support option
loop, % the fun handling requests
- ws_loop % the fun handling websockets
+ ws_loop, % the loop handling websockets
+ ws_autoexit % true | false
}).
% Request
@@ -82,10 +83,13 @@
-record(ws, {
socket, % the socket handling the request
socket_mode, % http | ssl
+ ws_autoexit, % websocket process is automatically killed: true | false
peer_addr, % peer IP | undefined
peer_port, % peer port | undefined
peer_cert, % undefined | the DER encoded peer certificate that can be decoded with public_key:pkix_decode_cert/2
+ vsn, % {Maj,Min} | {'draft-hixie', Ver}
origin, % the originator
host, % the host
- path % the websocket GET request path
+ path, % the websocket GET request path
+ headers % [{Tag, Val}]
}).
View
@@ -46,7 +46,7 @@ FOR %%f in (examples\*.erl) DO erlc -W %command% -o ebin "%%f"
:COMPILE
mkdir ebin
FOR %%f in (src\*.erl) DO erlc -W %command% -o ebin "%%f"
-copy src\misultin.app ebin\misultin.app /Y
+copy src\misultin.app.src ebin\misultin.app /Y
GOTO END
:CLEAN
View
@@ -0,0 +1,9 @@
+{application, misultin,
+[
+ {description, "Lightweight HTTP(s) and Websockets Server Library"},
+ {vsn, "0.6.1"},
+ {modules, [misultin, misultin_req, misultin_socket, misultin_http, misultin_utility, misultin_websocket, misultin_ws]},
+ {registered, [misultin]},
+ {env, []},
+ {applications, [kernel, stdlib]}
+]}.
View
@@ -32,7 +32,7 @@
% ==========================================================================================================
-module(misultin).
-behaviour(gen_server).
--vsn("0.6.0").
+-vsn("0.6.1").
% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
@@ -107,7 +107,8 @@ init([Options]) ->
{compress, false, fun is_boolean/1, invalid_compress_option},
{stream_support, true, fun is_boolean/1, invalid_stream_support_option},
{loop, {error, undefined_loop}, fun is_function/1, loop_not_function},
- {ws_loop, none, fun is_function/1, ws_loop_not_function}
+ {ws_loop, none, fun is_function/1, ws_loop_not_function},
+ {ws_autoexit, true, fun is_boolean/1, invalid_ws_autoexit_option}
],
OptionsVerified = lists:foldl(fun(OptionName, Acc) -> [get_option(OptionName, Options)|Acc] end, [], OptionProps),
case proplists:get_value(error, OptionsVerified) of
@@ -124,6 +125,7 @@ init([Options]) ->
StreamSupport = proplists:get_value(stream_support, OptionsVerified),
Loop = proplists:get_value(loop, OptionsVerified),
WsLoop = proplists:get_value(ws_loop, OptionsVerified),
+ WsAutoExit = proplists:get_value(ws_autoexit, OptionsVerified),
% ipv6 support
?LOG_DEBUG("ip address is: ~p", [Ip]),
% set additional options according to socket mode if necessary
@@ -165,7 +167,7 @@ init([Options]) ->
% set options
OptionsTcp = [binary, {packet, raw}, {ip, Ip}, {reuseaddr, true}, {active, false}, {backlog, Backlog}|AdditionalOptions],
% build custom_opts
- CustomOpts = #custom_opts{compress = Compress, stream_support = StreamSupport, loop = Loop, ws_loop = WsLoop},
+ CustomOpts = #custom_opts{compress = Compress, stream_support = StreamSupport, loop = Loop, ws_loop = WsLoop, ws_autoexit = WsAutoExit},
% create listening socket and acceptor
case create_listener_and_acceptor(Port, OptionsTcp, RecvTimeout, SocketMode, CustomOpts) of
{ok, ListenSocket, AcceptorPid} ->
View
@@ -31,7 +31,7 @@
% POSSIBILITY OF SUCH DAMAGE.
% ==========================================================================================================
-module(misultin_http).
--vsn("0.6.0").
+-vsn("0.6.1").
% API
-export([handle_data/8]).
@@ -52,7 +52,8 @@
compress,
stream_support,
loop,
- ws_loop
+ ws_loop,
+ ws_autoexit
}).
% includes
@@ -64,7 +65,7 @@
% Callback from misultin_socket
handle_data(Sock, SocketMode, ListenPort, PeerAddr, PeerPort, PeerCert, RecvTimeout, CustomOpts) ->
% build connection & request records
- C = #c{sock = Sock, socket_mode = SocketMode, port = ListenPort, recv_timeout = RecvTimeout, compress = CustomOpts#custom_opts.compress, stream_support = CustomOpts#custom_opts.stream_support, loop = CustomOpts#custom_opts.loop, ws_loop = CustomOpts#custom_opts.ws_loop},
+ C = #c{sock = Sock, socket_mode = SocketMode, port = ListenPort, recv_timeout = RecvTimeout, compress = CustomOpts#custom_opts.compress, stream_support = CustomOpts#custom_opts.stream_support, loop = CustomOpts#custom_opts.loop, ws_loop = CustomOpts#custom_opts.ws_loop, ws_autoexit = CustomOpts#custom_opts.ws_autoexit},
Req = #req{socket = Sock, socket_mode = SocketMode, peer_addr = PeerAddr, peer_port = PeerPort, peer_cert = PeerCert},
% enter loop
request(C, Req).
@@ -138,14 +139,14 @@ headers(#c{sock = Sock, socket_mode = SocketMode, recv_timeout = RecvTimeout, ws
CheckWs = case WsLoop of
none -> false;
_Function -> misultin_websocket:check(Path, Headers)
- end,
+ end,
case CheckWs of
false ->
?LOG_DEBUG("normal http request received", []),
body(C, Req#req{headers = Headers});
- {true, Origin, Host, Path} ->
+ {true, Vsn} ->
?LOG_DEBUG("websocket request received", []),
- misultin_websocket:connect(#ws{socket = Sock, socket_mode = SocketMode, peer_addr = Req#req.peer_addr, peer_port = Req#req.peer_port, origin = Origin, host = Host, path = Path}, WsLoop)
+ misultin_websocket:connect(Req, #ws{vsn = Vsn, socket = Sock, socket_mode = SocketMode, peer_addr = Req#req.peer_addr, peer_port = Req#req.peer_port, path = Path, headers = Headers, ws_autoexit = C#c.ws_autoexit}, WsLoop)
end;
{SocketMode, Sock, _Other} ->
?LOG_DEBUG("tcp error treating headers: ~p, send bad request error back", [_Other]),
@@ -228,10 +229,10 @@ body(#c{sock = Sock, socket_mode = SocketMode, recv_timeout = RecvTimeout} = C,
request(C, #req{socket = Sock, socket_mode = SocketMode, peer_addr = Req#req.peer_addr, peer_port = Req#req.peer_port, peer_cert = Req#req.peer_cert})
end;
{error, timeout} ->
- ?LOG_DEBUG("request timeout, sending error", []),
+ ?LOG_WARNING("request timeout, sending error", []),
misultin_socket:send(Sock, misultin_utility:get_http_status_code(408), SocketMode);
_Other ->
- ?LOG_DEBUG("tcp error treating post data: ~p, send bad request error back", [_Other]),
+ ?LOG_ERROR("tcp error treating post data: ~p, send bad request error back", [_Other]),
misultin_socket:send(Sock, misultin_utility:get_http_status_code(400), SocketMode)
end
end;
View
@@ -32,7 +32,7 @@
% POSSIBILITY OF SUCH DAMAGE.
% ==========================================================================================================
-module(misultin_req, [Req, SocketPid]).
--vsn("0.6.0").
+-vsn("0.6.1").
% macros
-define(PERCENT, 37). % $\%
View
@@ -31,7 +31,7 @@
% POSSIBILITY OF SUCH DAMAGE.
% ==========================================================================================================
-module(misultin_socket).
--vsn("0.6.0").
+-vsn("0.6.1").
% API
-export([start_link/5]).
View
@@ -1,5 +1,5 @@
% ==========================================================================================================
-% MISULTIN - Main
+% MISULTIN - Various Utilities
%
% >-|-|-(°>
%
@@ -28,10 +28,10 @@
% POSSIBILITY OF SUCH DAMAGE.
% ==========================================================================================================
-module(misultin_utility).
--vsn("0.6.0").
+-vsn("0.6.1").
% API
--export([get_http_status_code/1, get_content_type/1, get_key_value/2]).
+-export([get_http_status_code/1, get_content_type/1, get_key_value/2, header_get_value/2]).
% ============================ \/ API ======================================================================
@@ -327,6 +327,27 @@ get_key_value(Key, List)->
{_K, Value}-> Value
end.
+% Function: Value | false
+% Description: Find atom Tag in Headers, Headers being both atoms [for known headers] and strings. Comparison on string Header Tags is case insensitive.
+header_get_value(Tag, Headers) when is_atom(Tag) ->
+ case lists:keyfind(Tag, 1, Headers) of
+ false ->
+ % header not found, test also conversion to string -> convert all string tags to lowercase (HTTP tags are case insensitive)
+ F = fun({HTag, HValue}) ->
+ case is_atom(HTag) of
+ true -> {HTag, HValue};
+ false -> {string:to_lower(HTag), HValue}
+ end
+ end,
+ HeadersStr = lists:map(F, Headers),
+ % test
+ case lists:keyfind(string:to_lower(atom_to_list(Tag)), 1, HeadersStr) of
+ false -> false;
+ {_, Value} -> Value
+ end;
+ {_, Value} -> Value
+ end.
+
% ============================ /\ API ======================================================================
Oops, something went wrong.

0 comments on commit 7374dac

Please sign in to comment.