blob: 9991e3b1c918ac909d64fb588660d6d15e2da45e [file] [log] [blame]
%% @author Richard Jones <rj@metabrew.com>
%% @see http://www.whatwg.org/specs/web-socket-protocol/
%% As of August 2010
%%
%% However, at time of writing (Oct 8, 2010) Chrome 6 and Firefox 4 implement
%% an older version of the websocket spec, where messages are framed 0x00...0xFF
%% so the newer protocol with length headers has not been tested with a browser.
-module(mochiweb_wsrequest, [Socket, Path, Headers]).
-define(TIMEOUT, 999999). % TODO
-export([get/1, get_data/0, send/1]).
get(path) -> Path;
get(socket) -> Socket.
get_data() ->
% read FrameType byte
case mochiweb_socket:recv(Socket, 1, ?TIMEOUT) of
{error, closed} ->
closed;
{error, timeout} ->
timeout;
{ok, FrameType} ->
case FrameType of
<<255>> -> % Modern UTF-8 bytestream message with 64bit length
erlang:put(legacy, false),
{ok, <<Len:64/unsigned-integer>>} =
mochiweb_socket:recv(Socket, 8, ?TIMEOUT),
{ok, Frame} = mochiweb_socket:recv(Socket, Len, ?TIMEOUT),
{frame, Frame};
<<0>> -> % modern close request, or older no-length-frame msg
case mochiweb_socket:recv(Socket, 1, ?TIMEOUT) of
{ok, <<0>>} ->
% invalid for legacy protocol
% probably followed by 7 more 0 in modern
closing;
{ok, <<255>>} ->
% empty legacy frame.
erlang:put(legacy, true),
{frame, <<>>};
{ok, Byte2} ->
% Read up to the first 0xFF for the body
erlang:put(legacy, true),
Body = read_until_FF(Socket, Byte2),
{frame, Body}
end
end
end.
send(Body) ->
case erlang:get(legacy) of
true ->
% legacy spec, msgs are framed with 0x00..0xFF
mochiweb_socket:send(Socket, [0, Body, 255]);
_ ->
% header is 0xFF then 64bit big-endian int of the msg length
Len = iolist_size(Body),
mochiweb_socket:send(Socket, [255,
<<Len:64/unsigned-integer>>,
Body])
end.
read_until_FF(Socket, Acc) when is_binary(Acc) ->
case mochiweb_socket:recv(Socket, 1, ?TIMEOUT) of
{ok, <<255>>} -> Acc;
{ok, B} -> read_until_FF(Socket, <<Acc/binary, B/binary>>)
end.