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.