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:24 UTC

[29/50] mochiweb commit: updated refs/heads/import-upstream to 8eb1f22

tests for websocket handshake and frames parsing


Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/2eb1c563
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/2eb1c563
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/2eb1c563

Branch: refs/heads/import-upstream
Commit: 2eb1c563ca934b53a935c3b96ff5fba135cab9f8
Parents: cef2055
Author: Łukasz Lalik <lu...@gmail.com>
Authored: Wed Dec 25 01:25:24 2013 +0100
Committer: Łukasz Lalik <lu...@gmail.com>
Committed: Wed Dec 25 01:25:24 2013 +0100

----------------------------------------------------------------------
 src/mochiweb_websocket.erl       | 65 +++++++++++++++++---------------
 src/mochiweb_websocket_tests.erl | 70 +++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/2eb1c563/src/mochiweb_websocket.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_websocket.erl b/src/mochiweb_websocket.erl
index d1cc4b4..12bf84a 100644
--- a/src/mochiweb_websocket.erl
+++ b/src/mochiweb_websocket.erl
@@ -28,45 +28,47 @@
 -export([loop/4, upgrade_connection/1, request/4]).
 -export([after_response/4, reentry/1, send/3]).
 
--define(REQUEST_RECV_TIMEOUT, 3000000).   %% timeout waiting for request line
--define(HEADERS_RECV_TIMEOUT, 30000).    %% timeout waiting for headers
-
--define(MAX_HEADERS, 1000).
--define(DEFAULTS, [{name, ?MODULE},
-                   {port, 8888}]).
-
 loop(Socket, Body, State, WsVersion) ->
     ok = mochiweb_socket:setopts(Socket, [{packet, 0}, {active, once}]),
     proc_lib:hibernate(?MODULE, request, [Socket, Body, State, WsVersion]).
 
 upgrade_connection(Req) ->
-    % Headers for HYBI handshake
+    case make_handshake(Req) of
+        {Version, Response} ->
+            Req:respond(Response),
+            Version;
+
+        _ ->
+            mochiweb_socket:close(Req:get(socket)),
+            exit(normal)
+    end.
+   
+make_handshake(Req) ->
     SecKey  = Req:get_header_value("sec-websocket-key"),
     Sec1Key = Req:get_header_value("Sec-WebSocket-Key1"),
     Sec2Key = Req:get_header_value("Sec-WebSocket-Key2"),
     Origin = Req:get_header_value(origin),
-
     if not (SecKey == undefined) ->
-          hybi_handshake(Req, SecKey);
+          hybi_handshake(SecKey);
 
-      (not (Sec1Key == undefined)) and (not (Sec2Key == undefined)) ->
-          Body = Req:recv(8),
-          hixie_handshake(Req, Sec1Key, Sec2Key, Body, Origin);
+        (not (Sec1Key == undefined)) and (not (Sec2Key == undefined)) ->
+            Host = Req:get_header_value("Host"),
+            Path = Req:get(path),
+            Body = Req:recv(8),
+            hixie_handshake(Host, Path, Sec1Key, Sec2Key, Body, Origin);
 
        true ->
-          mochiweb_socket:close(Req:get(socket)),
-          exit(normal)
+          error
     end.
-   
 
-hybi_handshake(Req, SecKey) ->
+hybi_handshake(SecKey) ->
   Challenge = handshake_key(SecKey),
-  Req:respond({101, [{"Connection", "Upgrade"},
+  Response = {101, [{"Connection", "Upgrade"},
                      {"Upgrade", "websocket"},
-                     {"Sec-Websocket-Accept", Challenge}], ""}),
-  hybi.
+                     {"Sec-Websocket-Accept", Challenge}], ""},
+  {hybi, Response}.
 
-hixie_handshake(Req, Key1, Key2, Body, Origin) ->
+hixie_handshake(Host, Path, Key1, Key2, Body, Origin) ->
   Ikey1 = [D || D <- Key1, $0 =< D, D =< $9],
   Ikey2 = [D || D <- Key2, $0 =< D, D =< $9],
   Blank1 = length([D || D <- Key1, D =:= 32]),
@@ -76,18 +78,14 @@ hixie_handshake(Req, Key1, Key2, Body, Origin) ->
   Ckey = <<Part1:4/big-unsigned-integer-unit:8, Part2:4/big-unsigned-integer-unit:8, Body/binary>>,
   Challenge = erlang:md5(Ckey),
 
-  Location = lists:concat(["ws://", 
-                           Req:get_header_value("Host"),
-                           Req:get(path)]),
+  Location = lists:concat(["ws://", Host, Path]),
 
-  Req:respond({101, [{"Upgrade", "WebSocket"},
+  Response = {101, [{"Upgrade", "WebSocket"},
                      {"Connection", "Upgrade"},
                      {"Sec-WebSocket-Origin", Origin},
                      {"Sec-WebSocket-Location", Location}],
-                    Challenge}),
-  hixie.
-
-
+                    Challenge},
+  {hixie, Response}.
 
 send(Socket, Payload, hybi) ->
     Len = payload_length(iolist_size(Payload)),
@@ -121,6 +119,7 @@ request(Socket, Body, State, WsVersion) ->
                                 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
@@ -302,3 +301,11 @@ handle_frame(<<255, Rest/binary>>, Buffer) ->
   {Buffer, Rest};
 handle_frame(<<H, T/binary>>, Buffer) ->
   handle_frame(T, <<Buffer/binary, H>>).
+
+%%
+%% Tests
+%%
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-compile(export_all).
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/2eb1c563/src/mochiweb_websocket_tests.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_websocket_tests.erl b/src/mochiweb_websocket_tests.erl
new file mode 100644
index 0000000..272b188
--- /dev/null
+++ b/src/mochiweb_websocket_tests.erl
@@ -0,0 +1,70 @@
+-module(mochiweb_websocket_tests).
+-author('lukasz.lalik@zadane.pl').
+
+%% 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:
+
+%% 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.
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+make_handshake_for_correct_client_test() ->
+    %% Hybi handshake
+    Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Sec-WebSocket-Key", "Xn3fdKyc3qEXPuj2A3O+ZA=="}])),
+
+    {Version1, {HttpCode1, Headers1, _}} = mochiweb_websocket:make_handshake(Req1),
+    ?assertEqual(hybi, Version1),
+    ?assertEqual(101, HttpCode1),
+    ?assertEqual("Upgrade", proplists:get_value("Connection", Headers1)),
+    ?assertEqual(<<"BIFTHkJk4r5t8kuud82tZJaQsCE=">>, proplists:get_value("Sec-Websocket-Accept", Headers1)),
+
+    %% Hixie handshake
+    {Version2, {HttpCode2, Headers2, Body2}} = mochiweb_websocket:hixie_handshake(
+                                            "localhost", "/",
+                                            "33j284    9  z63 e 9 7",
+                                            "TF'3|6D12659H 7 70",
+                                            <<175,181,191,215,128,195,144,120>>,
+                                            "null"),
+    ?assertEqual(hixie, Version2),
+    ?assertEqual(101, HttpCode2),
+    ?assertEqual("null", proplists:get_value("Sec-WebSocket-Origin", Headers2)),
+    ?assertEqual("ws://localhost/", proplists:get_value("Sec-WebSocket-Location", Headers2)),
+    ?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>>, []),
+    ?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>>, []),
+    ?assertEqual([{1, <<"foo">>}, {1, <<"bar">>}], Resp2).
+
+hixie_frames_decode_test() ->
+    Resp1 = mochiweb_websocket:handle_frames(<<>>, []),
+    ?assertEqual([], Resp1),
+
+    Resp2 = mochiweb_websocket:handle_frames(<<0,102,111,111,255>>, []),
+    ?assertEqual([<<"foo">>], Resp2),
+
+    Resp3 = mochiweb_websocket:handle_frames(<<0,102,111,111,255,0,98,97,114,255>>, []),
+    ?assertEqual([<<"foo">>, <<"bar">>], Resp3).
+
+-endif.