| -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. |
| |
| -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("ws://", "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() -> |
| ?assertEqual([{1, <<"foo">>}], |
| (mochiweb_websocket:parse_hybi_frames(nil, |
| <<129, 131, 118, 21, 153, |
| 58, 16, 122, 246>>, |
| []))), |
| ?assertEqual([{1, <<"foo">>}, {1, <<"bar">>}], |
| (mochiweb_websocket:parse_hybi_frames(nil, |
| <<129, 131, 1, 225, 201, |
| 42, 103, 142, 166, 129, |
| 131, 93, 222, 214, 66, |
| 63, 191, 164>>, |
| []))). |
| |
| hixie_frames_decode_test() -> |
| ?assertEqual([], |
| (mochiweb_websocket:parse_hixie_frames(<<>>, []))), |
| ?assertEqual([<<"foo">>], |
| (mochiweb_websocket:parse_hixie_frames(<<0, 102, 111, |
| 111, 255>>, |
| []))), |
| ?assertEqual([<<"foo">>, <<"bar">>], |
| (mochiweb_websocket:parse_hixie_frames(<<0, 102, 111, |
| 111, 255, 0, 98, 97, |
| 114, 255>>, |
| []))). |
| |
| end_to_end_test_factory(ServerTransport) -> |
| mochiweb_test_util:with_server(ServerTransport, |
| fun end_to_end_server/1, |
| fun (Transport, Port) -> |
| end_to_end_client(mochiweb_test_util:sock_fun(Transport, |
| Port)) |
| end). |
| |
| end_to_end_server(Req) -> |
| ?assertEqual("Upgrade", |
| (mochiweb_request:get_header_value("connection", Req))), |
| ?assertEqual("websocket", |
| (mochiweb_request:get_header_value("upgrade", Req))), |
| {ReentryWs, _ReplyChannel} = |
| mochiweb_websocket:upgrade_connection(Req, |
| fun end_to_end_ws_loop/3), |
| ReentryWs(ok). |
| |
| end_to_end_ws_loop(Payload, State, ReplyChannel) -> |
| %% Echo server |
| lists:foreach(ReplyChannel, Payload), |
| State. |
| |
| end_to_end_client(S) -> |
| %% Key and Accept per https://tools.ietf.org/html/rfc6455 |
| UpgradeReq = string:join(["GET / HTTP/1.1", |
| "Host: localhost", "Upgrade: websocket", |
| "Connection: Upgrade", |
| "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==", "", |
| ""], |
| "\r\n"), |
| ok = S({send, UpgradeReq}), |
| {ok, {http_response, {1, 1}, 101, _}} = S(recv), |
| read_expected_headers(S, |
| [{'Upgrade', "websocket"}, {'Connection', "Upgrade"}, |
| {'Content-Length', "0"}, |
| {"Sec-Websocket-Accept", |
| "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}]), |
| %% The first message sent over telegraph :) |
| SmallMessage = <<"What hath God wrought?">>, |
| ok = S({send, |
| <<1:1, %% Fin |
| 0:1, %% Rsv1 |
| 0:1, %% Rsv2 |
| 0:1, %% Rsv3 |
| 2:4, %% Opcode, 1 = text frame |
| 1:1, %% Mask on |
| (byte_size(SmallMessage)):7, %% Length, <125 case |
| 0:32, %% Mask (trivial) |
| SmallMessage/binary>>}), |
| {ok, WsFrames} = S(recv), |
| <<1:1, %% Fin |
| 0:1, %% Rsv1 |
| 0:1, %% Rsv2 |
| 0:1, %% Rsv3 |
| 1:4, %% Opcode, text frame (all mochiweb suports for now) |
| MsgSize:8, %% Expecting small size |
| SmallMessage/binary>> = |
| WsFrames, |
| ?assertEqual(MsgSize, (byte_size(SmallMessage))), |
| ok. |
| |
| read_expected_headers(S, D) -> |
| Headers = mochiweb_test_util:read_server_headers(S), |
| lists:foreach(fun ({K, V}) -> |
| ?assertEqual(V, |
| (mochiweb_headers:get_value(K, Headers))) |
| end, |
| D). |
| |
| end_to_end_http_test() -> |
| end_to_end_test_factory(plain). |
| |
| end_to_end_https_test() -> end_to_end_test_factory(ssl). |