Skip to content
Newer
Older
100644 183 lines (163 sloc) 5.96 KB
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
1 %%%----------------------------------------------------------------------
2 %%% File : yaws_websockets.erl
bd0bf89 @klacke no utf8 in the author name
authored Jan 12, 2010
3 %%% Author : Davide Marques <nesrait@gmail.com>
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
4 %%% Purpose :
bd0bf89 @klacke no utf8 in the author name
authored Jan 12, 2010
5 %%% Created : 18 Dec 2009 by Davide Marques <nesrait@gmail.com>
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
6 %%% Modified:
7 %%%----------------------------------------------------------------------
8
9 -module(yaws_websockets).
10 -author('nesrait@gmail.com').
11
12 -include("../include/yaws.hrl").
13 -include("../include/yaws_api.hrl").
14 -include("yaws_debug.hrl").
15
16 -include_lib("kernel/include/file.hrl").
17 -export([handshake/3, unframe_one/1, unframe_all/2]).
18
19 handshake(Arg, ContentPid, SocketMode) ->
20 CliSock = Arg#arg.clisock,
5a70e44 @schemeway Reverted some names to ease merge.
schemeway authored Aug 26, 2010
21 case get_origin_header(Arg#arg.headers) of
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
22 undefined ->
23 %% Yaws will take care of closing the socket
24 ContentPid ! discard;
25 Origin ->
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 26, 2010
26 ProtocolVersion = ws_version(Arg#arg.headers),
5a70e44 @schemeway Reverted some names to ease merge.
schemeway authored Aug 27, 2010
27 Protocol = get_protocol_header(Arg#arg.headers),
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
28 Host = (Arg#arg.headers)#headers.host,
29 {abs_path, Path} = (Arg#arg.req)#http_request.path,
30 SC = get(sc),
5a70e44 @schemeway Reverted some names to ease merge.
schemeway authored Aug 27, 2010
31 WebSocketLocation =
32 case SC#sconf.ssl of
33 undefined -> "ws://" ++ Host ++ Path;
34 _ -> "wss://" ++ Host ++ Path
35 end,
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 27, 2010
36 Handshake = handshake(ProtocolVersion, Arg, CliSock,
37 WebSocketLocation, Origin, Protocol),
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
38 case SC#sconf.ssl of
39 undefined ->
40 gen_tcp:send(CliSock, Handshake),
41 inet:setopts(CliSock, [{packet, raw}, {active, SocketMode}]),
42 TakeOverResult =
43 gen_tcp:controlling_process(CliSock, ContentPid);
44 _ ->
45 ssl:send(CliSock, Handshake),
46 ssl:setopts(CliSock, [{packet, raw}, {active, SocketMode}]),
47 TakeOverResult =
48 ssl:controlling_process(CliSock, ContentPid)
49 end,
50 case TakeOverResult of
51 ok ->
52 ContentPid ! {ok, CliSock};
53 {error, Reason} ->
54 ContentPid ! discard,
55 exit({websocket, Reason})
56 end
57 end,
58 exit(normal).
59
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
60 handshake(ws_76, Arg, CliSock, WebSocketLocation, Origin, Protocol) ->
61 {ok, Challenge} = gen_tcp:recv(CliSock, 8),
62 Key1 = secret_key("sec-websocket-key1", Arg#arg.headers),
63 Key2 = secret_key("sec-websocket-key2", Arg#arg.headers),
64 ChallengeResponse = challenge(Key1, Key2, binary_to_list(Challenge)),
65 ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n",
66 "Upgrade: WebSocket\r\n",
67 "Connection: Upgrade\r\n",
68 "Sec-WebSocket-Origin: ", Origin, "\r\n",
69 "Sec-WebSocket-Location: ", WebSocketLocation, "\r\n",
70 "Sec-WebSocket-Protocol: ", Protocol, "\r\n",
71 "\r\n", ChallengeResponse];
72
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 28, 2010
73 handshake(ws_75, _Arg, _CliSock, WebSocketLocation, Origin, _Protocol) ->
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
74 ["HTTP/1.1 101 Web Socket Protocol Handshake\r\n",
75 "Upgrade: WebSocket\r\n",
76 "Connection: Upgrade\r\n",
77 "WebSocket-Origin: ", Origin, "\r\n",
78 "WebSocket-Location: ", WebSocketLocation, "\r\n",
79 "\r\n"].
80
81 ws_version(Headers) ->
82 case query_header("sec-websocket-key1", Headers) of
83 undefined -> ws_75;
84 _ -> ws_76
85 end.
86
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
87 %% This should take care of all the Data Framing scenarios specified in
88 %% http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-66#page-26
89 unframe_one(DataFrames) ->
90 <<Type, _/bitstring>> = DataFrames,
91 case Type of
92 T when (T =< 127) ->
93 %% {ok, FF_Ended_Frame} = re:compile("^.(.*)\\xFF(.*?)", [ungreedy]),
94 FF_Ended_Frame = {re_pattern,2,0,
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 28, 2010
95 <<69,82,67,80,71,0,0,0,16,2,0,0,5,0,0,0,2,0,0,0,
96 0,0,255,2,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,
97 27,25,12,94,0,7,0,1,57,12,84,0,7,27,255,94,0,7,
98 0,2,56,12,84,0,7,84,0,27,0>>},
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
99 {match, [Data, NextFrame]} =
100 re:run(DataFrames, FF_Ended_Frame,
101 [{capture, all_but_first, binary}]),
102 {ok, Data, NextFrame};
103
104 _ -> %% Type band 16#80 =:= 16#80
105 {Length, LenBytes} = unpack_length(DataFrames, 0, 0),
106 <<_, _:LenBytes/bytes, Data:Length/bytes,
107 NextFrame/bitstring>> = DataFrames,
108 {ok, Data, NextFrame}
109 end.
110
111 unframe_all(<<>>, Acc) ->
112 lists:reverse(Acc);
113 unframe_all(DataFramesBin, Acc) ->
114 {ok, Msg, Rem} = unframe_one(DataFramesBin),
115 unframe_all(Rem, [Msg|Acc]).
116
117
118 %% Internal functions
5a70e44 @schemeway Reverted some names to ease merge.
schemeway authored Aug 27, 2010
119 get_origin_header(Headers) ->
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
120 query_header("origin", Headers).
121
5a70e44 @schemeway Reverted some names to ease merge.
schemeway authored Aug 27, 2010
122 get_protocol_header(Headers) ->
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
123 query_header("sec-websocket-protocol", Headers, "unknown").
124
125 query_header(HeaderName, Headers) ->
126 query_header(HeaderName, Headers, undefined).
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 28, 2010
127
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
128 query_header(Header, #headers{other=L}, Default) ->
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
129 lists:foldl(fun({http_header,_,K0,_,V}, undefined) ->
130 K = case is_atom(K0) of
131 true ->
132 atom_to_list(K0);
133 false ->
134 K0
135 end,
136 case string:to_lower(K) of
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
137 Header ->
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
138 V;
139 _ ->
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
140 Default
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
141 end;
142 (_, Acc) ->
143 Acc
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
144 end, Default, L).
145
146 secret_key(KeyName, Headers) ->
147 case query_header(KeyName, Headers) of
148 undefined ->
149 0;
150 Key ->
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 28, 2010
151 Digits = lists:filter(fun(C) -> C >= 48 andalso C =< 57 end, Key),
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
152 NumberOfSpaces = length(lists:filter(fun(C) -> C == 32 end, Key)),
153 list_to_integer(Digits) div NumberOfSpaces
154 end.
155
156 challenge(Key1, Key2, Challenge) ->
157 BinaryAnswer =
158 crypto:md5_final(
159 crypto:md5_update(
160 crypto:md5_init(),
161 digits32(Key1) ++ digits32(Key2) ++ Challenge)),
162 BinaryAnswer.
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
163
fe2e42a @schemeway Added support for websockets v76.
schemeway authored Aug 27, 2010
164 digits32(Num) ->
165 Digit4 = Num rem 256,
166 Num2 = Num div 256,
167 Digit3 = Num2 rem 256,
168 Num3 = Num2 div 256,
169 Digit2 = Num3 rem 256,
170 Digit1 = Num3 div 256,
171 [Digit1, Digit2, Digit3, Digit4].
4ebda22 @vinoski minor code cleanup
vinoski authored Aug 28, 2010
172
7fdc54a @davide HTML5 Web Sockets support.
davide authored Dec 18, 2009
173 unpack_length(Binary, LenBytes, Length) ->
174 <<_, _:LenBytes/bytes, B, _/bitstring>> = Binary,
175 B_v = B band 16#7F,
176 NewLength = (Length * 128) + B_v,
177 case B band 16#80 of
178 16#80 ->
179 unpack_length(Binary, LenBytes + 1, NewLength);
180 0 ->
181 {NewLength, LenBytes + 1}
182 end.
Something went wrong with that request. Please try again.