cow_spdy.erl 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. %% Copyright (c) 2013-2014, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(cow_spdy).
  15. %% Zstream.
  16. -export([deflate_init/0]).
  17. -export([inflate_init/0]).
  18. %% Parse.
  19. -export([split/1]).
  20. -export([parse/2]).
  21. %% Build.
  22. -export([data/3]).
  23. -export([syn_stream/12]).
  24. -export([syn_reply/6]).
  25. -export([rst_stream/2]).
  26. %% @todo settings
  27. -export([ping/1]).
  28. -export([goaway/2]).
  29. %% @todo headers
  30. %% @todo window_update
  31. -include("cow_spdy.hrl").
  32. %% Zstream.
  33. deflate_init() ->
  34. Zdef = zlib:open(),
  35. ok = zlib:deflateInit(Zdef),
  36. _ = zlib:deflateSetDictionary(Zdef, ?ZDICT),
  37. Zdef.
  38. inflate_init() ->
  39. Zinf = zlib:open(),
  40. ok = zlib:inflateInit(Zinf),
  41. Zinf.
  42. %% Parse.
  43. split(Data = << _:40, Length:24, _/bits >>)
  44. when byte_size(Data) >= Length + 8 ->
  45. Length2 = Length + 8,
  46. << Frame:Length2/binary, Rest/bits >> = Data,
  47. {true, Frame, Rest};
  48. split(_) ->
  49. false.
  50. parse(<< 0:1, StreamID:31, 0:7, IsFinFlag:1, _:24, Data/bits >>, _) ->
  51. {data, StreamID, from_flag(IsFinFlag), Data};
  52. parse(<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
  53. _:25, StreamID:31, _:1, AssocToStreamID:31, Priority:3, _:5,
  54. 0:8, Rest/bits >>, Zinf) ->
  55. case parse_headers(Rest, Zinf) of
  56. {ok, Headers, [{<<":host">>, Host}, {<<":method">>, Method},
  57. {<<":path">>, Path}, {<<":scheme">>, Scheme},
  58. {<<":version">>, Version}]} ->
  59. {syn_stream, StreamID, AssocToStreamID, from_flag(IsFinFlag),
  60. from_flag(IsUnidirectionalFlag), Priority, Method,
  61. Scheme, Host, Path, Version, Headers};
  62. _ ->
  63. {error, badprotocol}
  64. end;
  65. parse(<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, _:25,
  66. StreamID:31, Rest/bits >>, Zinf) ->
  67. case parse_headers(Rest, Zinf) of
  68. {ok, Headers, [{<<":status">>, Status}, {<<":version">>, Version}]} ->
  69. {syn_reply, StreamID, from_flag(IsFinFlag),
  70. Status, Version, Headers};
  71. _ ->
  72. {error, badprotocol}
  73. end;
  74. parse(<< 1:1, 3:15, 3:16, 0:8, _:56, StatusCode:32 >>, _)
  75. when StatusCode =:= 0; StatusCode > 11 ->
  76. {error, badprotocol};
  77. parse(<< 1:1, 3:15, 3:16, 0:8, _:25, StreamID:31, StatusCode:32 >>, _) ->
  78. Status = case StatusCode of
  79. 1 -> protocol_error;
  80. 2 -> invalid_stream;
  81. 3 -> refused_stream;
  82. 4 -> unsupported_version;
  83. 5 -> cancel;
  84. 6 -> internal_error;
  85. 7 -> flow_control_error;
  86. 8 -> stream_in_use;
  87. 9 -> stream_already_closed;
  88. 10 -> invalid_credentials;
  89. 11 -> frame_too_large
  90. end,
  91. {rst_stream, StreamID, Status};
  92. parse(<< 1:1, 3:15, 4:16, 0:7, ClearSettingsFlag:1, _:24,
  93. NbEntries:32, Rest/bits >>, _) ->
  94. try
  95. Settings = [begin
  96. Is0 = 0,
  97. Key = case ID of
  98. 1 -> upload_bandwidth;
  99. 2 -> download_bandwidth;
  100. 3 -> round_trip_time;
  101. 4 -> max_concurrent_streams;
  102. 5 -> current_cwnd;
  103. 6 -> download_retrans_rate;
  104. 7 -> initial_window_size;
  105. 8 -> client_certificate_vector_size
  106. end,
  107. {Key, Value, from_flag(PersistFlag), from_flag(WasPersistedFlag)}
  108. end || << Is0:6, WasPersistedFlag:1, PersistFlag:1,
  109. ID:24, Value:32 >> <= Rest],
  110. NbEntries = length(Settings),
  111. {settings, from_flag(ClearSettingsFlag), Settings}
  112. catch _:_ ->
  113. {error, badprotocol}
  114. end;
  115. parse(<< 1:1, 3:15, 6:16, 0:8, _:24, PingID:32 >>, _) ->
  116. {ping, PingID};
  117. parse(<< 1:1, 3:15, 7:16, 0:8, _:56, StatusCode:32 >>, _)
  118. when StatusCode > 2 ->
  119. {error, badprotocol};
  120. parse(<< 1:1, 3:15, 7:16, 0:8, _:25, LastGoodStreamID:31,
  121. StatusCode:32 >>, _) ->
  122. Status = case StatusCode of
  123. 0 -> ok;
  124. 1 -> protocol_error;
  125. 2 -> internal_error
  126. end,
  127. {goaway, LastGoodStreamID, Status};
  128. parse(<< 1:1, 3:15, 8:16, 0:7, IsFinFlag:1, _:25, StreamID:31,
  129. Rest/bits >>, Zinf) ->
  130. case parse_headers(Rest, Zinf) of
  131. {ok, Headers, []} ->
  132. {headers, StreamID, from_flag(IsFinFlag), Headers};
  133. _ ->
  134. {error, badprotocol}
  135. end;
  136. parse(<< 1:1, 3:15, 9:16, 0:8, _:57, 0:31 >>, _) ->
  137. {error, badprotocol};
  138. parse(<< 1:1, 3:15, 9:16, 0:8, _:25, StreamID:31,
  139. _:1, DeltaWindowSize:31 >>, _) ->
  140. {window_update, StreamID, DeltaWindowSize};
  141. parse(_, _) ->
  142. {error, badprotocol}.
  143. parse_headers(Data, Zinf) ->
  144. [<< NbHeaders:32, Rest/bits >>] = inflate(Zinf, Data),
  145. parse_headers(Rest, NbHeaders, [], []).
  146. parse_headers(<<>>, 0, Headers, SpHeaders) ->
  147. {ok, lists:reverse(Headers), lists:sort(SpHeaders)};
  148. parse_headers(<<>>, _, _, _) ->
  149. error;
  150. parse_headers(_, 0, _, _) ->
  151. error;
  152. parse_headers(<< 0:32, _/bits >>, _, _, _) ->
  153. error;
  154. parse_headers(<< L1:32, Key:L1/binary, L2:32, Value:L2/binary, Rest/bits >>,
  155. NbHeaders, Acc, SpAcc) ->
  156. case Key of
  157. << $:, _/bits >> ->
  158. parse_headers(Rest, NbHeaders - 1, Acc,
  159. lists:keystore(Key, 1, SpAcc, {Key, Value}));
  160. _ ->
  161. parse_headers(Rest, NbHeaders - 1, [{Key, Value}|Acc], SpAcc)
  162. end.
  163. inflate(Zinf, Data) ->
  164. try
  165. zlib:inflate(Zinf, Data)
  166. catch _:_ ->
  167. ok = zlib:inflateSetDictionary(Zinf, ?ZDICT),
  168. zlib:inflate(Zinf, <<>>)
  169. end.
  170. from_flag(0) -> false;
  171. from_flag(1) -> true.
  172. %% Build.
  173. data(StreamID, IsFin, Data) ->
  174. IsFinFlag = to_flag(IsFin),
  175. Length = iolist_size(Data),
  176. [<< 0:1, StreamID:31, 0:7, IsFinFlag:1, Length:24 >>, Data].
  177. syn_stream(Zdef, StreamID, AssocToStreamID, IsFin, IsUnidirectional,
  178. Priority, Method, Scheme, Host, Path, Version, Headers) ->
  179. IsFinFlag = to_flag(IsFin),
  180. IsUnidirectionalFlag = to_flag(IsUnidirectional),
  181. HeaderBlock = build_headers(Zdef, [
  182. {<<":method">>, Method},
  183. {<<":scheme">>, Scheme},
  184. {<<":host">>, Host},
  185. {<<":path">>, Path},
  186. {<<":version">>, Version}
  187. |Headers]),
  188. Length = 10 + iolist_size(HeaderBlock),
  189. [<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
  190. Length:24, 0:1, StreamID:31, 0:1, AssocToStreamID:31,
  191. Priority:3, 0:5, 0:8 >>, HeaderBlock].
  192. syn_reply(Zdef, StreamID, IsFin, Status, Version, Headers) ->
  193. IsFinFlag = to_flag(IsFin),
  194. HeaderBlock = build_headers(Zdef, [
  195. {<<":status">>, Status},
  196. {<<":version">>, Version}
  197. |Headers]),
  198. Length = 4 + iolist_size(HeaderBlock),
  199. [<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, Length:24,
  200. 0:1, StreamID:31 >>, HeaderBlock].
  201. rst_stream(StreamID, Status) ->
  202. StatusCode = case Status of
  203. protocol_error -> 1;
  204. invalid_stream -> 2;
  205. refused_stream -> 3;
  206. unsupported_version -> 4;
  207. cancel -> 5;
  208. internal_error -> 6;
  209. flow_control_error -> 7;
  210. stream_in_use -> 8;
  211. stream_already_closed -> 9;
  212. invalid_credentials -> 10;
  213. frame_too_large -> 11
  214. end,
  215. << 1:1, 3:15, 3:16, 0:8, 8:24,
  216. 0:1, StreamID:31, StatusCode:32 >>.
  217. %% @todo settings
  218. ping(PingID) ->
  219. << 1:1, 3:15, 6:16, 0:8, 4:24, PingID:32 >>.
  220. goaway(LastGoodStreamID, Status) ->
  221. StatusCode = case Status of
  222. ok -> 0;
  223. protocol_error -> 1;
  224. internal_error -> 2
  225. end,
  226. << 1:1, 3:15, 7:16, 0:8, 8:24,
  227. 0:1, LastGoodStreamID:31, StatusCode:32 >>.
  228. %% @todo headers
  229. %% @todo window_update
  230. build_headers(Zdef, Headers) ->
  231. DedupedHeaders = dedupe_headers(Headers, []),
  232. NbHeaders = length(DedupedHeaders),
  233. Headers2 = [begin
  234. L1 = iolist_size(Key),
  235. L2 = iolist_size(Value),
  236. [<< L1:32 >>, Key, << L2:32 >>, Value]
  237. end || {Key, Value} <- DedupedHeaders],
  238. zlib:deflate(Zdef, [<< NbHeaders:32 >>, Headers2], full).
  239. dedupe_headers([], Acc) ->
  240. lists:reverse(Acc);
  241. dedupe_headers([{Key, Value}|Headers], Acc) ->
  242. Acc2 = append_header_value(Key, Value, Acc, []),
  243. dedupe_headers(Headers, Acc2).
  244. append_header_value(Key, Value, [], Acc) ->
  245. [{Key, Value}|Acc];
  246. append_header_value(Key, Value, [{Key, PrevValue}|Rest], Acc) ->
  247. [{Key, [PrevValue, 0, Value]}|Rest] ++ Acc;
  248. append_header_value(Key, Value, [Header|Headers], Acc) ->
  249. append_header_value(Key, Value, Headers, [Header|Acc]).
  250. -ifdef(TEST).
  251. dedupe_headers_test_() ->
  252. Tests = [
  253. {[{<<"set-cookie">>, <<"session=123">>}, {<<"set-cookie">>, <<"other=456">>}, {<<"content-type">>, <<"text/html">>}],
  254. [{<<"set-cookie">>, [<<"session=123">>, 0, <<"other=456">>]}, {<<"content-type">>, <<"text/html">>}]}
  255. ],
  256. [fun() -> D = dedupe_headers(R, []) end || {R, D} <- Tests].
  257. -endif.
  258. to_flag(false) -> 0;
  259. to_flag(true) -> 1.