Permalink
Browse files

Very basic hybi 8 message sending support.

  • Loading branch information...
1 parent 0653171 commit 05956b9316c5aa515fe3d8d6721d5ff9c505be2f @jbothma committed Oct 2, 2011
Showing with 72 additions and 59 deletions.
  1. +9 −8 src/yaws_api.erl
  2. +55 −43 src/yaws_websockets.erl
  3. +8 −8 www/websockets_example_endpoint.yaws
View
@@ -37,8 +37,8 @@
stream_chunk_end/1]).
-export([stream_process_deliver/2, stream_process_deliver_chunk/2,
stream_process_deliver_final_chunk/2, stream_process_end/2]).
--export([websocket_send/2, websocket_receive/1,
- websocket_unframe_data/1, websocket_setopts/2]).
+-export([websocket_send/3, websocket_receive/2,
+ websocket_unframe_data/2, websocket_setopts/2]).
-export([new_cookie_session/1, new_cookie_session/2, new_cookie_session/3,
cookieval_to_opaque/1, request_url/1,
print_cookie_sessions/0,
@@ -976,16 +976,16 @@ stream_process_end(Sock, YawsPid) ->
YawsPid ! endofstreamcontent.
-websocket_send(Socket, IoList) ->
- DataFrame = [0, IoList, 255],
+websocket_send(Socket, ProtocolVersion, IoList) ->
+ DataFrame = yaws_websockets:frame(ProtocolVersion, IoList),
case Socket of
{sslsocket,_,_} ->
ssl:send(Socket, DataFrame);
_ ->
gen_tcp:send(Socket, DataFrame)
end.
-websocket_receive(Socket) ->
+websocket_receive(Socket, ProtocolVersion) ->
R = case Socket of
{sslsocket,_,_} ->
ssl:recv(Socket, 0);
@@ -994,13 +994,14 @@ websocket_receive(Socket) ->
end,
case R of
{ok, DataFrames} ->
- ReceivedMsgs = yaws_websockets:unframe_all(DataFrames, []),
+ ReceivedMsgs = yaws_websockets:unframe_all(ProtocolVersion,
+ DataFrames, []),
{ok, ReceivedMsgs};
_ -> R
end.
-websocket_unframe_data(DataFrameBin) ->
- {ok, Msg, <<>>} = yaws_websockets:unframe_one(DataFrameBin),
+websocket_unframe_data(ProtocolVersion, DataFrameBin) ->
+ {ok, Msg, <<>>} = yaws_websockets:unframe_one(ProtocolVersion, DataFrameBin),
Msg.
websocket_setopts({sslsocket,_,_}=Socket, Opts) ->
View
@@ -14,13 +14,15 @@
-include("yaws_debug.hrl").
-include_lib("kernel/include/file.hrl").
--export([handshake/3, unframe_one/1, unframe_all/2]).
+-export([handshake/3, unframe_one/2, unframe_all/3, frame/2]).
handshake(Arg, ContentPid, SocketMode) ->
CliSock = Arg#arg.clisock,
%jdtodo io:format("CliSock ~p~n",[CliSock]),
case get_origin_header(Arg#arg.headers) of
undefined ->
+ %% TODO: Lack of origin header is allowed for non-browser clients
+ %% in hybi 17 but for simplicity the connection is closed for now.
%jdtodo io:format("No origin header.~n"),
%% Yaws will take care of closing the socket
ContentPid ! discard;
@@ -51,26 +53,16 @@ handshake(Arg, ContentPid, SocketMode) ->
end,
case TakeOverResult of
ok ->
- ContentPid ! {ok, CliSock};
+ ContentPid ! {ok, CliSock, ProtocolVersion};
{error, Reason} ->
ContentPid ! discard,
exit({websocket, Reason})
end
end,
exit(normal).
-handshake(ws_hy10, Arg, _CliSock, _WebSocketLocation, _Origin, _Protocol) ->
- io:format("ws_hy10!~n",[]),
- Key = get_nonce_header(Arg#arg.headers),
- AcceptHash = hash_nonce(Key),
- ["HTTP/1.1 101 Switching Protocols\r\n",
- "Upgrade: websocket\r\n",
- "Connection: Upgrade\r\n",
- "Sec-WebSocket-Accept: ", AcceptHash , "\r\n",
- "\r\n"];
-
-handshake(ws_76, Arg, CliSock, WebSocketLocation, Origin, Protocol) ->
- io:format("ws_76!~n",[]),
+handshake(hi_76, Arg, CliSock, WebSocketLocation, Origin, Protocol) ->
+ io:format("hi_76!~n",[]),
{ok, Challenge} = case CliSock of
{sslsocket, _, _} ->
ssl:recv(CliSock, 8);
@@ -88,39 +80,41 @@ handshake(ws_76, Arg, CliSock, WebSocketLocation, Origin, Protocol) ->
"Sec-WebSocket-Protocol: ", Protocol, "\r\n",
"\r\n", ChallengeResponse];
-handshake(ws_75, _Arg, _CliSock, WebSocketLocation, Origin, _Protocol) ->
- io:format("ws_75!~n",[]),
+handshake(hi_75, _Arg, _CliSock, WebSocketLocation, Origin, _Protocol) ->
+ io:format("hi_75!~n",[]),
["HTTP/1.1 101 Web Socket Protocol Handshake\r\n",
"Upgrade: WebSocket\r\n",
"Connection: Upgrade\r\n",
"WebSocket-Origin: ", Origin, "\r\n",
"WebSocket-Location: ", WebSocketLocation, "\r\n",
+ "\r\n"];
+
+handshake(_Ver, Arg, _CliSock, _WebSocketLocation, _Origin, _Protocol) ->
+ io:format("10!~n",[]),
+ Key = get_nonce_header(Arg#arg.headers),
+ AcceptHash = hash_nonce(Key),
+ ["HTTP/1.1 101 Switching Protocols\r\n",
+ "Upgrade: websocket\r\n",
+ "Connection: Upgrade\r\n",
+ "Sec-WebSocket-Accept: ", AcceptHash , "\r\n",
"\r\n"].
ws_version(Headers) ->
- %jdtodo io:format("IDing Websocket Version...~n~p~n",[Headers]),
- case query_header("sec-websocket-key1", Headers) of
- undefined ->
- case get_nonce_header(Headers) of
- undefined -> ws_75;
- _ -> ws_hy10
- end;
- _ -> ws_76
- end.
-
-%% This should take care of all the Data Framing scenarios specified in
-%% http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-66#page-26
-unframe_one(DataFrames) ->
- % Try to decode as a hybi 10 frame.
- % Fall back to the old way if it seems wrong.
- case unmask_one_hy10(DataFrames) of
- undefined ->
- io:format("unframe_one falling back to hixie 76 format.~n", []),
- unmask_one_hi76(DataFrames);
- Else -> Else
+ io:format("IDing Websocket Version...~n~p~n",[Headers]),
+ case query_header("sec-websocket-version", Headers) of
+ "8" -> 8;
+ "17" -> 17;
+ _ ->
+ case query_header("sec-websocket-key1", Headers) of
+ undefined ->
+ %% might be hixie 75 but...
+ %% TODO: should really negotiate a hybi version here
+ hi_75;
+ _ -> hi_76
+ end
end.
-unmask_one_hy10(DataFrames) ->
+unframe_one(8, DataFrames) ->
Frame = binary_to_list(DataFrames),
First = lists:nth(1, Frame),
Opcode = First band 15,
@@ -141,9 +135,12 @@ unmask_one_hy10(DataFrames) ->
{unmaskeddata, UnmaskedData}],
io:format("~p~n", [JDUnframed]),
{ok, list_to_binary(UnmaskedData), <<>>} %jdtodo baaaaad
- end.
+ end;
-unmask_one_hi76(DataFrames) ->
+%% This should take care of all the Data Framing scenarios specified in
+%% http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-66#page-26
+unframe_one(hi_75, DataFrames) -> unframe_one(hi_76, DataFrames);
+unframe_one(hi_76, DataFrames) ->
<<Type, _/bitstring>> = DataFrames,
case Type of
T when (T =< 127) ->
@@ -165,11 +162,26 @@ unmask_one_hi76(DataFrames) ->
{ok, Data, NextFrame}
end.
-unframe_all(<<>>, Acc) ->
+unframe_all(_, <<>>, Acc) ->
lists:reverse(Acc);
-unframe_all(DataFramesBin, Acc) ->
- {ok, Msg, Rem} = unframe_one(DataFramesBin),
- unframe_all(Rem, [Msg|Acc]).
+unframe_all(ProtocolVersion, DataFramesBin, Acc) ->
+ {ok, Msg, Rem} = unframe_one(ProtocolVersion, DataFramesBin),
+ unframe_all(ProtocolVersion, Rem, [Msg|Acc]).
+
+frame(hi_75, IoList) ->
+ frame(hi_76, IoList);
+frame(hi_76, IoList) ->
+ [0, IoList, 255];
+frame(_, Data) ->
+ %FIN=true because we're not fragmenting.
+ %OPCODE=1 for text
+ FirstByte = 128 bor 1,
+ ByteList = binary_to_list(Data),
+ Length = length(ByteList),
+ SecondByte = Length, % server to client is not masked
+ << FirstByte, SecondByte, Data:Length/binary >>.
+
+
%%
%% mask(Pos, Mask, Data)
@@ -16,36 +16,36 @@ spawn_ws_owner() ->
websocket_owner() ->
receive
- {ok, WebSocket} ->
+ {ok, WebSocket, ProtocolVersion} ->
%% This is how we read messages (plural!!) from websockets on passive mode
- case yaws_api:websocket_receive(WebSocket) of
+ case yaws_api:websocket_receive(WebSocket, ProtocolVersion) of
{error,closed} ->
io:format("The websocket got disconnected right from the start. "
"This wasn't supposed to happen!!~n");
{ok, Messages} ->
case Messages of
[<<"client-connected">>] ->
yaws_api:websocket_setopts(WebSocket, [{active, true}]),
- echo_server(WebSocket);
+ echo_server(WebSocket, ProtocolVersion);
Other ->
io:format("websocket_owner got: ~p. Terminating~n", [Other])
end
end;
_ -> ok
end.
-echo_server(WebSocket) ->
+echo_server(WebSocket, ProtocolVersion) ->
receive
{tcp, WebSocket, DataFrame} ->
- Data = yaws_api:websocket_unframe_data(DataFrame),
+ Data = yaws_api:websocket_unframe_data(ProtocolVersion, DataFrame),
io:format("Got data from Websocket: ~p~n", [Data]),
- yaws_api:websocket_send(WebSocket, Data),
- echo_server(WebSocket);
+ yaws_api:websocket_send(WebSocket, ProtocolVersion, Data),
+ echo_server(WebSocket, ProtocolVersion);
{tcp_closed, WebSocket} ->
io:format("Websocket closed. Terminating echo_server...~n");
Any ->
io:format("echo_server received msg:~p~n", [Any]),
- echo_server(WebSocket)
+ echo_server(WebSocket, ProtocolVersion)
end.
get_upgrade_header(#headers{other=L}) ->

0 comments on commit 05956b9

Please sign in to comment.