You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by be...@apache.org on 2014/02/13 17:06:25 UTC
[30/50] mochiweb commit: updated refs/heads/import-upstream to 8eb1f22
simpler interface, refactor
Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/de79c0af
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/de79c0af
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/de79c0af
Branch: refs/heads/import-upstream
Commit: de79c0af59536dba1c536c2ce2661fb60d23138f
Parents: 2eb1c56
Author: Łukasz Lalik <lu...@gmail.com>
Authored: Wed Dec 25 12:28:44 2013 +0100
Committer: Łukasz Lalik <lu...@gmail.com>
Committed: Wed Dec 25 12:28:44 2013 +0100
----------------------------------------------------------------------
examples/websocket/websocket.erl | 19 ++-
src/mochiweb_websocket.erl | 224 ++++++++++++++++------------------
src/mochiweb_websocket_tests.erl | 10 +-
3 files changed, 121 insertions(+), 132 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/de79c0af/examples/websocket/websocket.erl
----------------------------------------------------------------------
diff --git a/examples/websocket/websocket.erl b/examples/websocket/websocket.erl
index a38efff..8d4eeb0 100644
--- a/examples/websocket/websocket.erl
+++ b/examples/websocket/websocket.erl
@@ -22,7 +22,7 @@
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
% THE SOFTWARE.
--export([start_link/0, ws_loop/4, loop/1]).
+-export([start_link/0, ws_loop/3, loop/1]).
start_link() ->
Loop = fun (Req) ->
@@ -35,15 +35,14 @@ start_link() ->
{port, 8080}
]).
-ws_loop(Socket, Payload, Sid, WsVersion) ->
- ReentryWs = mochiweb_websocket:reentry({?MODULE, ws_loop}),
+ws_loop(Payload, State, ReplyChannel) ->
io:format("Received data: ~p~n", [Payload]),
- ReentryWs(Socket, Sid, WsVersion).
+ Received = list_to_binary(Payload),
+ ReplyChannel(<<"Received ", Received/binary>>),
+ State.
loop(Req) ->
- ReentryWs = mochiweb_websocket:reentry({?MODULE, ws_loop}),
- WsVersion = mochiweb_websocket:upgrade_connection(Req),
-
- ReplySocket = Req:get(socket),
-
- ReentryWs(Req:get(socket), [], WsVersion).
+ {ReentryWs, ReplyChannel} = mochiweb_websocket:upgrade_connection(Req, {?MODULE, ws_loop}),
+ ReplyChannel(<<"Hello">>),
+ InitialState = [],
+ ReentryWs(InitialState).
http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/de79c0af/src/mochiweb_websocket.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_websocket.erl b/src/mochiweb_websocket.erl
index 12bf84a..677091b 100644
--- a/src/mochiweb_websocket.erl
+++ b/src/mochiweb_websocket.erl
@@ -25,18 +25,83 @@
%% @doc Websockets module for Mochiweb. Based on Misultin websockets module.
--export([loop/4, upgrade_connection/1, request/4]).
--export([after_response/4, reentry/1, send/3]).
+-export([loop/5, upgrade_connection/2, request/5]).
+-export([send/3]).
-loop(Socket, Body, State, WsVersion) ->
+loop(Socket, Body, State, WsVersion, ReplyChannel) ->
ok = mochiweb_socket:setopts(Socket, [{packet, 0}, {active, once}]),
- proc_lib:hibernate(?MODULE, request, [Socket, Body, State, WsVersion]).
+ proc_lib:hibernate(?MODULE, request, [Socket, Body, State, WsVersion, ReplyChannel]).
-upgrade_connection(Req) ->
+request(Socket, Body, State, WsVersion, ReplyChannel) ->
+ receive
+ {tcp_closed, _} ->
+ mochiweb_socket:close(Socket),
+ exit(normal);
+ {ssl_closed, _} ->
+ mochiweb_socket:close(Socket),
+ exit(normal);
+ {tcp_error, _, _} ->
+ mochiweb_socket:close(Socket),
+ exit(normal);
+
+ {tcp, _, WsFrames} ->
+ {M, F} = Body,
+ case WsVersion of
+ hybi ->
+ Reply = fun(close) ->
+ mochiweb_socket:close(Socket),
+ exit(normal);
+ (Payload) ->
+ NewState = M:F(Payload, State, ReplyChannel),
+ loop(Socket, Body, NewState, WsVersion, ReplyChannel)
+ end,
+
+ try parse_hybi_frames(Socket, WsFrames, []) of
+ Parsed -> process_frames(Parsed, Reply, [])
+ catch
+ _:_ -> Reply(close)
+ end;
+
+ hixie ->
+ try parse_hixie_frames(WsFrames, []) of
+ Payload ->
+ NewState = M:F(Payload, State),
+ loop(Socket, Body, NewState, WsVersion, ReplyChannel)
+ catch
+ _:_ ->
+ mochiweb_socket:close(Socket),
+ exit(normal)
+ end
+
+ end;
+
+ _ ->
+ mochiweb_socket:close(Socket),
+ exit(normal)
+ end.
+
+send(Socket, Payload, hybi) ->
+ Len = payload_length(iolist_size(Payload)),
+ Data = <<1:1, 0:3, 1:4, 0:1, Len/bits, Payload/binary>>,
+ mochiweb_socket:send(Socket, Data);
+
+send(Socket, Payload, hixie) ->
+ Data = <<0, Payload/binary, 255>>,
+ mochiweb_socket:send(Socket, Data).
+
+upgrade_connection(Req, Body) ->
case make_handshake(Req) of
{Version, Response} ->
Req:respond(Response),
- Version;
+
+ Socket = Req:get(socket),
+ ReplyChannel = fun(Payload) ->
+ ?MODULE:send(Socket, Payload, Version)
+ end,
+ Reentry = fun (State) ->
+ ?MODULE:loop(Socket, Body, State, Version, ReplyChannel)
+ end,
+ {Reentry, ReplyChannel};
_ ->
mochiweb_socket:close(Req:get(socket)),
@@ -62,11 +127,14 @@ make_handshake(Req) ->
end.
hybi_handshake(SecKey) ->
- Challenge = handshake_key(SecKey),
- Response = {101, [{"Connection", "Upgrade"},
- {"Upgrade", "websocket"},
- {"Sec-Websocket-Accept", Challenge}], ""},
- {hybi, Response}.
+ BinKey = list_to_binary(SecKey),
+ Bin = <<BinKey/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>,
+ Challenge = base64:encode(crypto:hash(sha, Bin)),
+
+ Response = {101, [{"Connection", "Upgrade"},
+ {"Upgrade", "websocket"},
+ {"Sec-Websocket-Accept", Challenge}], ""},
+ {hybi, Response}.
hixie_handshake(Host, Path, Key1, Key2, Body, Origin) ->
Ikey1 = [D || D <- Key1, $0 =< D, D =< $9],
@@ -87,60 +155,9 @@ hixie_handshake(Host, Path, Key1, Key2, Body, Origin) ->
Challenge},
{hixie, Response}.
-send(Socket, Payload, hybi) ->
- Len = payload_length(iolist_size(Payload)),
- Data = <<1:1, 0:3, 1:4, 0:1, Len/bits, Payload/binary>>,
- mochiweb_socket:send(Socket, Data);
-
-send(Socket, Payload, hixie) ->
- Data = <<0, Payload/binary, 255>>,
- mochiweb_socket:send(Socket, Data).
-
-request(Socket, Body, State, WsVersion) ->
- receive
- {tcp_closed, _} ->
- mochiweb_socket:close(Socket),
- exit(normal);
- {ssl_closed, _} ->
- mochiweb_socket:close(Socket),
- exit(normal);
- {tcp_error, _, _} ->
- mochiweb_socket:close(Socket),
- exit(normal);
-
- {tcp, _, WsFrames} ->
- {M, F} = Body,
- case WsVersion of
- hybi ->
- Reply = fun(close) ->
- mochiweb_socket:close(Socket),
- exit(normal);
- (Payload) ->
- M:F(Socket, Payload, State, WsVersion)
- end,
-
- io:format("Handle: ~p~n", [WsFrames]),
- try parse_frames(Socket, WsFrames, []) of
- Parsed -> process_frames(Parsed, Reply, [])
- catch
- _:_ -> Reply(close)
- end;
-
- hixie ->
- try handle_frames(WsFrames, []) of
- Payload -> M:F(Socket, Payload, State, WsVersion)
- catch
- _:_ ->
- mochiweb_socket:close(Socket),
- exit(normal)
- end
-
- end;
-
- _ ->
- handle_invalid_request(Socket)
- end.
-
+%%
+%% Websockets internal functions for RFC6455 and hybi draft
+%%
process_frames([], Reply, Acc) ->
Reply(lists:reverse(Acc));
@@ -154,33 +171,10 @@ process_frames([{Opcode, Payload} | Rest], Reply, Acc) ->
process_frames(Rest, Reply, [Payload | Acc])
end.
-reentry(Body) ->
- fun (Socket, State, WsVersion) ->
- ?MODULE:after_response(Body, Socket, State, WsVersion)
- end.
-
--spec handle_invalid_request(term()) -> no_return().
-handle_invalid_request(Socket) ->
- mochiweb_socket:close(Socket),
- exit(normal).
-
-after_response(Body, Socket, State, WsVersion) ->
- ?MODULE:loop(Socket, Body, State, WsVersion).
-
-%%
-%% Websockets internal functions for RFC6455 (hybi)
-%%
-
-% RFC6455 Handshake
-handshake_key(Key) ->
- BinKey = list_to_binary(Key),
- Bin = <<BinKey/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>,
- base64:encode(crypto:hash(sha, Bin)).
-
-parse_frames(_, <<>>, Acc) ->
+parse_hybi_frames(_, <<>>, Acc) ->
lists:reverse(Acc);
-parse_frames(S, <<_Fin:1,
+parse_hybi_frames(S, <<_Fin:1,
_Rsv:3,
Opcode:4,
_Mask:1,
@@ -190,10 +184,10 @@ parse_frames(S, <<_Fin:1,
Rest/binary>>,
Acc) when PayloadLen < 126 ->
- Payload2 = extract_payload(MaskKey, Payload),
- parse_frames(S, Rest, [{Opcode, Payload2} | Acc]);
+ Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
+ parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]);
-parse_frames(S, <<_Fin:1,
+parse_hybi_frames(S, <<_Fin:1,
_Rsv:3,
Opcode:4,
_Mask:1,
@@ -204,10 +198,10 @@ parse_frames(S, <<_Fin:1,
Rest/binary>>,
Acc) ->
- Payload2 = extract_payload(MaskKey, Payload),
- parse_frames(S, Rest, [{Opcode, Payload2} | Acc]);
+ Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
+ parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]);
-parse_frames(Socket, <<_Fin:1,
+parse_hybi_frames(Socket, <<_Fin:1,
_Rsv:3,
_Opcode:4,
_Mask:1,
@@ -230,7 +224,7 @@ parse_frames(Socket, <<_Fin:1,
exit(normal);
{tcp, _, Continuation} ->
- parse_frames(Socket, <<PartFrame/binary, Continuation/binary>>, Acc);
+ parse_hybi_frames(Socket, <<PartFrame/binary, Continuation/binary>>, Acc);
_ ->
mochiweb_socket:close(Socket),
@@ -241,7 +235,7 @@ parse_frames(Socket, <<_Fin:1,
exit(normal)
end;
-parse_frames(S, <<_Fin:1,
+parse_hybi_frames(S, <<_Fin:1,
_Rsv:3,
Opcode:4,
_Mask:1,
@@ -253,30 +247,27 @@ parse_frames(S, <<_Fin:1,
Rest/binary>>,
Acc) ->
- Payload2 = extract_payload(MaskKey, Payload),
- parse_frames(S, Rest, [{Opcode, Payload2} | Acc]).
-
-extract_payload(MaskKey, Payload) ->
- websocket_unmask(Payload, MaskKey, <<>>).
+ Payload2 = hybi_unmask(Payload, MaskKey, <<>>),
+ parse_hybi_frames(S, Rest, [{Opcode, Payload2} | Acc]).
% Unmasks RFC 6455 message
-websocket_unmask(<<O:32, Rest/bits>>, MaskKey, Acc) ->
+hybi_unmask(<<O:32, Rest/bits>>, MaskKey, Acc) ->
<<MaskKey2:32>> = MaskKey,
T = O bxor MaskKey2,
- websocket_unmask(Rest, MaskKey, <<Acc/binary, T:32>>);
-websocket_unmask(<<O:24>>, MaskKey, Acc) ->
+ hybi_unmask(Rest, MaskKey, <<Acc/binary, T:32>>);
+hybi_unmask(<<O:24>>, MaskKey, Acc) ->
<<MaskKey2:24, _:8>> = MaskKey,
T = O bxor MaskKey2,
<<Acc/binary, T:24>>;
-websocket_unmask(<<O:16>>, MaskKey, Acc) ->
+hybi_unmask(<<O:16>>, MaskKey, Acc) ->
<<MaskKey2:16, _:16>> = MaskKey,
T = O bxor MaskKey2,
<<Acc/binary, T:16>>;
-websocket_unmask(<<O:8>>, MaskKey, Acc) ->
+hybi_unmask(<<O:8>>, MaskKey, Acc) ->
<<MaskKey2:8, _:24>> = MaskKey,
T = O bxor MaskKey2,
<<Acc/binary, T:8>>;
-websocket_unmask(<<>>, _MaskKey, Acc) ->
+hybi_unmask(<<>>, _MaskKey, Acc) ->
Acc.
payload_length(N) ->
@@ -290,17 +281,16 @@ payload_length(N) ->
%%
%% Websockets internal functions for hixie-76 websocket version
%%
-
-handle_frames(<<>>, Frames) ->
+parse_hixie_frames(<<>>, Frames) ->
lists:reverse(Frames);
-handle_frames(<<0, T/binary>>, Frames) ->
- {Frame, Rest} = handle_frame(T, <<>>),
- handle_frames(Rest, [Frame | Frames]).
+parse_hixie_frames(<<0, T/binary>>, Frames) ->
+ {Frame, Rest} = parse_hixie(T, <<>>),
+ parse_hixie_frames(Rest, [Frame | Frames]).
-handle_frame(<<255, Rest/binary>>, Buffer) ->
+parse_hixie(<<255, Rest/binary>>, Buffer) ->
{Buffer, Rest};
-handle_frame(<<H, T/binary>>, Buffer) ->
- handle_frame(T, <<Buffer/binary, H>>).
+parse_hixie(<<H, T/binary>>, Buffer) ->
+ parse_hixie(T, <<Buffer/binary, H>>).
%%
%% Tests
http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/de79c0af/src/mochiweb_websocket_tests.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_websocket_tests.erl b/src/mochiweb_websocket_tests.erl
index 272b188..a98a9ea 100644
--- a/src/mochiweb_websocket_tests.erl
+++ b/src/mochiweb_websocket_tests.erl
@@ -51,20 +51,20 @@ make_handshake_for_correct_client_test() ->
?assertEqual(<<230,144,237,94,84,214,41,69,244,150,134,167,221,103,239,246>>, Body2).
hybi_frames_decode_test() ->
- Resp1 = mochiweb_websocket:parse_frames(nil, <<129,131,118,21,153,58,16,122,246>>, []),
+ Resp1 = mochiweb_websocket:parse_hybi_frames(nil, <<129,131,118,21,153,58,16,122,246>>, []),
?assertEqual([{1, <<"foo">>}], Resp1),
- Resp2 = mochiweb_websocket:parse_frames(nil, <<129,131,1,225,201,42,103,142,166,129,131,93,222,214,66,63,191,164>>, []),
+ Resp2 = mochiweb_websocket:parse_hybi_frames(nil, <<129,131,1,225,201,42,103,142,166,129,131,93,222,214,66,63,191,164>>, []),
?assertEqual([{1, <<"foo">>}, {1, <<"bar">>}], Resp2).
hixie_frames_decode_test() ->
- Resp1 = mochiweb_websocket:handle_frames(<<>>, []),
+ Resp1 = mochiweb_websocket:parse_hixie_frames(<<>>, []),
?assertEqual([], Resp1),
- Resp2 = mochiweb_websocket:handle_frames(<<0,102,111,111,255>>, []),
+ Resp2 = mochiweb_websocket:parse_hixie_frames(<<0,102,111,111,255>>, []),
?assertEqual([<<"foo">>], Resp2),
- Resp3 = mochiweb_websocket:handle_frames(<<0,102,111,111,255,0,98,97,114,255>>, []),
+ Resp3 = mochiweb_websocket:parse_hixie_frames(<<0,102,111,111,255,0,98,97,114,255>>, []),
?assertEqual([<<"foo">>, <<"bar">>], Resp3).
-endif.