zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cow_spdy.erl (9353B)


      1 %% Copyright (c) 2013-2018, 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 
     15 -module(cow_spdy).
     16 
     17 %% Zstream.
     18 -export([deflate_init/0]).
     19 -export([inflate_init/0]).
     20 
     21 %% Parse.
     22 -export([split/1]).
     23 -export([parse/2]).
     24 
     25 %% Build.
     26 -export([data/3]).
     27 -export([syn_stream/12]).
     28 -export([syn_reply/6]).
     29 -export([rst_stream/2]).
     30 -export([settings/2]).
     31 -export([ping/1]).
     32 -export([goaway/2]).
     33 %% @todo headers
     34 %% @todo window_update
     35 
     36 -include("cow_spdy.hrl").
     37 
     38 %% Zstream.
     39 
     40 deflate_init() ->
     41 	Zdef = zlib:open(),
     42 	ok = zlib:deflateInit(Zdef),
     43 	_ = zlib:deflateSetDictionary(Zdef, ?ZDICT),
     44 	Zdef.
     45 
     46 inflate_init() ->
     47 	Zinf = zlib:open(),
     48 	ok = zlib:inflateInit(Zinf),
     49 	Zinf.
     50 
     51 %% Parse.
     52 
     53 split(Data = << _:40, Length:24, _/bits >>)
     54 		when byte_size(Data) >= Length + 8 ->
     55 	Length2 = Length + 8,
     56 	<< Frame:Length2/binary, Rest/bits >> = Data,
     57 	{true, Frame, Rest};
     58 split(_) ->
     59 	false.
     60 
     61 parse(<< 0:1, StreamID:31, 0:7, IsFinFlag:1, _:24, Data/bits >>, _) ->
     62 	{data, StreamID, from_flag(IsFinFlag), Data};
     63 parse(<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
     64 		_:25, StreamID:31, _:1, AssocToStreamID:31, Priority:3, _:5,
     65 		0:8, Rest/bits >>, Zinf) ->
     66 	case parse_headers(Rest, Zinf) of
     67 		{ok, Headers, [{<<":host">>, Host}, {<<":method">>, Method},
     68 				{<<":path">>, Path}, {<<":scheme">>, Scheme},
     69 				{<<":version">>, Version}]} ->
     70 			{syn_stream, StreamID, AssocToStreamID, from_flag(IsFinFlag),
     71 				from_flag(IsUnidirectionalFlag), Priority, Method,
     72 				Scheme, Host, Path, Version, Headers};
     73 		_ ->
     74 			{error, badprotocol}
     75 	end;
     76 parse(<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, _:25,
     77 		StreamID:31, Rest/bits >>, Zinf) ->
     78 	case parse_headers(Rest, Zinf) of
     79 		{ok, Headers, [{<<":status">>, Status}, {<<":version">>, Version}]} ->
     80 			{syn_reply, StreamID, from_flag(IsFinFlag),
     81 				Status, Version, Headers};
     82 		_ ->
     83 			{error, badprotocol}
     84 	end;
     85 parse(<< 1:1, 3:15, 3:16, 0:8, _:56, StatusCode:32 >>, _)
     86 		when StatusCode =:= 0; StatusCode > 11 ->
     87 	{error, badprotocol};
     88 parse(<< 1:1, 3:15, 3:16, 0:8, _:25, StreamID:31, StatusCode:32 >>, _) ->
     89 	Status = case StatusCode of
     90 		1 -> protocol_error;
     91 		2 -> invalid_stream;
     92 		3 -> refused_stream;
     93 		4 -> unsupported_version;
     94 		5 -> cancel;
     95 		6 -> internal_error;
     96 		7 -> flow_control_error;
     97 		8 -> stream_in_use;
     98 		9 -> stream_already_closed;
     99 		10 -> invalid_credentials;
    100 		11 -> frame_too_large
    101 	end,
    102 	{rst_stream, StreamID, Status};
    103 parse(<< 1:1, 3:15, 4:16, 0:7, ClearSettingsFlag:1, _:24,
    104 		NbEntries:32, Rest/bits >>, _) ->
    105 	try
    106 		Settings = [begin
    107 			Is0 = 0,
    108 			Key = case ID of
    109 				1 -> upload_bandwidth;
    110 				2 -> download_bandwidth;
    111 				3 -> round_trip_time;
    112 				4 -> max_concurrent_streams;
    113 				5 -> current_cwnd;
    114 				6 -> download_retrans_rate;
    115 				7 -> initial_window_size;
    116 				8 -> client_certificate_vector_size
    117 			end,
    118 			{Key, Value, from_flag(PersistFlag), from_flag(WasPersistedFlag)}
    119 		end || << Is0:6, WasPersistedFlag:1, PersistFlag:1,
    120 			ID:24, Value:32 >> <= Rest],
    121 		NbEntries = length(Settings),
    122 		{settings, from_flag(ClearSettingsFlag), Settings}
    123 	catch _:_ ->
    124 		{error, badprotocol}
    125 	end;
    126 parse(<< 1:1, 3:15, 6:16, 0:8, _:24, PingID:32 >>, _) ->
    127 	{ping, PingID};
    128 parse(<< 1:1, 3:15, 7:16, 0:8, _:56, StatusCode:32 >>, _)
    129 		when StatusCode > 2 ->
    130 	{error, badprotocol};
    131 parse(<< 1:1, 3:15, 7:16, 0:8, _:25, LastGoodStreamID:31,
    132 		StatusCode:32 >>, _) ->
    133 	Status = case StatusCode of
    134 		0 -> ok;
    135 		1 -> protocol_error;
    136 		2 -> internal_error
    137 	end,
    138 	{goaway, LastGoodStreamID, Status};
    139 parse(<< 1:1, 3:15, 8:16, 0:7, IsFinFlag:1, _:25, StreamID:31,
    140 		Rest/bits >>, Zinf) ->
    141 	case parse_headers(Rest, Zinf) of
    142 		{ok, Headers, []} ->
    143 			{headers, StreamID, from_flag(IsFinFlag), Headers};
    144 		_ ->
    145 			{error, badprotocol}
    146 	end;
    147 parse(<< 1:1, 3:15, 9:16, 0:8, _:57, 0:31 >>, _) ->
    148 	{error, badprotocol};
    149 parse(<< 1:1, 3:15, 9:16, 0:8, _:25, StreamID:31,
    150 		_:1, DeltaWindowSize:31 >>, _) ->
    151 	{window_update, StreamID, DeltaWindowSize};
    152 parse(_, _) ->
    153 	{error, badprotocol}.
    154 
    155 parse_headers(Data, Zinf) ->
    156 	[<< NbHeaders:32, Rest/bits >>] = inflate(Zinf, Data),
    157 	parse_headers(Rest, NbHeaders, [], []).
    158 
    159 parse_headers(<<>>, 0, Headers, SpHeaders) ->
    160 	{ok, lists:reverse(Headers), lists:sort(SpHeaders)};
    161 parse_headers(<<>>, _, _, _) ->
    162 	error;
    163 parse_headers(_, 0, _, _) ->
    164 	error;
    165 parse_headers(<< 0:32, _/bits >>, _, _, _) ->
    166 	error;
    167 parse_headers(<< L1:32, Key:L1/binary, L2:32, Value:L2/binary, Rest/bits >>,
    168 		NbHeaders, Acc, SpAcc) ->
    169 	case Key of
    170 		<< $:, _/bits >> ->
    171 			parse_headers(Rest, NbHeaders - 1, Acc,
    172 				lists:keystore(Key, 1, SpAcc, {Key, Value}));
    173 		_ ->
    174 			parse_headers(Rest, NbHeaders - 1, [{Key, Value}|Acc], SpAcc)
    175 	end.
    176 
    177 inflate(Zinf, Data) ->
    178 	try
    179 		zlib:inflate(Zinf, Data)
    180 	catch _:_ ->
    181 		ok = zlib:inflateSetDictionary(Zinf, ?ZDICT),
    182 		zlib:inflate(Zinf, <<>>)
    183 	end.
    184 
    185 from_flag(0) -> false;
    186 from_flag(1) -> true.
    187 
    188 %% Build.
    189 
    190 data(StreamID, IsFin, Data) ->
    191 	IsFinFlag = to_flag(IsFin),
    192 	Length = iolist_size(Data),
    193 	[<< 0:1, StreamID:31, 0:7, IsFinFlag:1, Length:24 >>, Data].
    194 
    195 syn_stream(Zdef, StreamID, AssocToStreamID, IsFin, IsUnidirectional,
    196 		Priority, Method, Scheme, Host, Path, Version, Headers) ->
    197 	IsFinFlag = to_flag(IsFin),
    198 	IsUnidirectionalFlag = to_flag(IsUnidirectional),
    199 	HeaderBlock = build_headers(Zdef, [
    200 		{<<":method">>, Method},
    201 		{<<":scheme">>, Scheme},
    202 		{<<":host">>, Host},
    203 		{<<":path">>, Path},
    204 		{<<":version">>, Version}
    205 		|Headers]),
    206 	Length = 10 + iolist_size(HeaderBlock),
    207 	[<< 1:1, 3:15, 1:16, 0:6, IsUnidirectionalFlag:1, IsFinFlag:1,
    208 		Length:24, 0:1, StreamID:31, 0:1, AssocToStreamID:31,
    209 		Priority:3, 0:5, 0:8 >>, HeaderBlock].
    210 
    211 syn_reply(Zdef, StreamID, IsFin, Status, Version, Headers) ->
    212 	IsFinFlag = to_flag(IsFin),
    213 	HeaderBlock = build_headers(Zdef, [
    214 		{<<":status">>, Status},
    215 		{<<":version">>, Version}
    216 		|Headers]),
    217 	Length = 4 + iolist_size(HeaderBlock),
    218 	[<< 1:1, 3:15, 2:16, 0:7, IsFinFlag:1, Length:24,
    219 		0:1, StreamID:31 >>, HeaderBlock].
    220 
    221 rst_stream(StreamID, Status) ->
    222 	StatusCode = case Status of
    223 		protocol_error -> 1;
    224 		invalid_stream -> 2;
    225 		refused_stream -> 3;
    226 		unsupported_version -> 4;
    227 		cancel -> 5;
    228 		internal_error -> 6;
    229 		flow_control_error -> 7;
    230 		stream_in_use -> 8;
    231 		stream_already_closed -> 9;
    232 		invalid_credentials -> 10;
    233 		frame_too_large -> 11
    234 	end,
    235 	<< 1:1, 3:15, 3:16, 0:8, 8:24,
    236 		0:1, StreamID:31, StatusCode:32 >>.
    237 
    238 settings(ClearSettingsFlag, Settings) ->
    239 	IsClearSettingsFlag = to_flag(ClearSettingsFlag),
    240 	NbEntries = length(Settings),
    241 	Entries = [begin
    242 		IsWasPersistedFlag = to_flag(WasPersistedFlag),
    243 		IsPersistFlag = to_flag(PersistFlag),
    244 		ID = case Key of
    245 			upload_bandwidth -> 1;
    246 			download_bandwidth -> 2;
    247 			round_trip_time -> 3;
    248 			max_concurrent_streams -> 4;
    249 			current_cwnd -> 5;
    250 			download_retrans_rate -> 6;
    251 			initial_window_size -> 7;
    252 			client_certificate_vector_size -> 8
    253 		end,
    254 		<< 0:6, IsWasPersistedFlag:1, IsPersistFlag:1, ID:24, Value:32 >>
    255 	end || {Key, Value, WasPersistedFlag, PersistFlag} <- Settings],
    256 	Length = 4 + iolist_size(Entries),
    257 	[<< 1:1, 3:15, 4:16, 0:7, IsClearSettingsFlag:1, Length:24,
    258 		NbEntries:32 >>, Entries].
    259 
    260 -ifdef(TEST).
    261 settings_frame_test() ->
    262 	ClearSettingsFlag = false,
    263 	Settings = [{max_concurrent_streams,1000,false,false},
    264 				{initial_window_size,10485760,false,false}],
    265 	Bin = list_to_binary(cow_spdy:settings(ClearSettingsFlag, Settings)),
    266 	P = cow_spdy:parse(Bin, undefined),
    267 	P = {settings, ClearSettingsFlag, Settings},
    268 	ok.
    269 -endif.
    270 
    271 ping(PingID) ->
    272 	<< 1:1, 3:15, 6:16, 0:8, 4:24, PingID:32 >>.
    273 
    274 goaway(LastGoodStreamID, Status) ->
    275 	StatusCode = case Status of
    276 		ok -> 0;
    277 		protocol_error -> 1;
    278 		internal_error -> 2
    279 	end,
    280 	<< 1:1, 3:15, 7:16, 0:8, 8:24,
    281 		0:1, LastGoodStreamID:31, StatusCode:32 >>.
    282 
    283 %% @todo headers
    284 %% @todo window_update
    285 
    286 build_headers(Zdef, Headers) ->
    287 	Headers1 = merge_headers(lists:sort(Headers), []),
    288 	NbHeaders = length(Headers1),
    289 	Headers2 = [begin
    290 		L1 = iolist_size(Key),
    291 		L2 = iolist_size(Value),
    292 		[<< L1:32 >>, Key, << L2:32 >>, Value]
    293 	end || {Key, Value} <- Headers1],
    294 	zlib:deflate(Zdef, [<< NbHeaders:32 >>, Headers2], full).
    295 
    296 merge_headers([], Acc) ->
    297 	lists:reverse(Acc);
    298 merge_headers([{Name, Value1}, {Name, Value2}|Tail], Acc) ->
    299 	merge_headers([{Name, [Value1, 0, Value2]}|Tail], Acc);
    300 merge_headers([Head|Tail], Acc) ->
    301 	merge_headers(Tail, [Head|Acc]).
    302 
    303 -ifdef(TEST).
    304 merge_headers_test_() ->
    305 	Tests = [
    306 		{[{<<"set-cookie">>, <<"session=123">>}, {<<"set-cookie">>, <<"other=456">>}, {<<"content-type">>, <<"text/html">>}],
    307 		 [{<<"set-cookie">>, [<<"session=123">>, 0, <<"other=456">>]}, {<<"content-type">>, <<"text/html">>}]}
    308 	],
    309 	[fun() -> D = merge_headers(R, []) end || {R, D} <- Tests].
    310 -endif.
    311 
    312 to_flag(false) -> 0;
    313 to_flag(true) -> 1.