1%% Copyright (c) 2015-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_http2).
16
17%% Parsing.
18-export([parse_sequence/1]).
19-export([parse/1]).
20-export([parse/2]).
21-export([parse_settings_payload/1]).
22
23%% Building.
24-export([data/3]).
25-export([data_header/3]).
26-export([headers/3]).
27-export([priority/4]).
28-export([rst_stream/2]).
29-export([settings/1]).
30-export([settings_payload/1]).
31-export([settings_ack/0]).
32-export([push_promise/3]).
33-export([ping/1]).
34-export([ping_ack/1]).
35-export([goaway/3]).
36-export([window_update/1]).
37-export([window_update/2]).
38
39-type streamid() :: pos_integer().
40-export_type([streamid/0]).
41
42-type fin() :: fin | nofin.
43-export_type([fin/0]).
44
45-type head_fin() :: head_fin | head_nofin.
46-export_type([head_fin/0]).
47
48-type exclusive() :: exclusive | shared.
49-type weight() :: 1..256.
50-type settings() :: map().
51
52-type error() :: no_error
53	| protocol_error
54	| internal_error
55	| flow_control_error
56	| settings_timeout
57	| stream_closed
58	| frame_size_error
59	| refused_stream
60	| cancel
61	| compression_error
62	| connect_error
63	| enhance_your_calm
64	| inadequate_security
65	| http_1_1_required
66	| unknown_error.
67-export_type([error/0]).
68
69-type frame() :: {data, streamid(), fin(), binary()}
70	| {headers, streamid(), fin(), head_fin(), binary()}
71	| {headers, streamid(), fin(), head_fin(), exclusive(), streamid(), weight(), binary()}
72	| {priority, streamid(), exclusive(), streamid(), weight()}
73	| {rst_stream, streamid(), error()}
74	| {settings, settings()}
75	| settings_ack
76	| {push_promise, streamid(), head_fin(), streamid(), binary()}
77	| {ping, integer()}
78	| {ping_ack, integer()}
79	| {goaway, streamid(), error(), binary()}
80	| {window_update, non_neg_integer()}
81	| {window_update, streamid(), non_neg_integer()}
82	| {continuation, streamid(), head_fin(), binary()}.
83-export_type([frame/0]).
84
85%% Parsing.
86
87-spec parse_sequence(binary())
88	-> {ok, binary()} | more | {connection_error, error(), atom()}.
89parse_sequence(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", Rest/bits>>) ->
90	{ok, Rest};
91parse_sequence(Data) when byte_size(Data) >= 24 ->
92	{connection_error, protocol_error,
93		'The connection preface was invalid. (RFC7540 3.5)'};
94parse_sequence(Data) ->
95	Len = byte_size(Data),
96	<<Preface:Len/binary, _/bits>> = <<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>,
97	case Data of
98		Preface ->
99			more;
100		_ ->
101			{connection_error, protocol_error,
102				'The connection preface was invalid. (RFC7540 3.5)'}
103	end.
104
105parse(<< Len:24, _/bits >>, MaxFrameSize) when Len > MaxFrameSize ->
106	{connection_error, frame_size_error, 'The frame size exceeded SETTINGS_MAX_FRAME_SIZE. (RFC7540 4.2)'};
107parse(Data, _) ->
108	parse(Data).
109
110%%
111%% DATA frames.
112%%
113parse(<< _:24, 0:8, _:9, 0:31, _/bits >>) ->
114	{connection_error, protocol_error, 'DATA frames MUST be associated with a stream. (RFC7540 6.1)'};
115parse(<< 0:24, 0:8, _:4, 1:1, _:35, _/bits >>) ->
116	{connection_error, frame_size_error, 'DATA frames with padding flag MUST have a length > 0. (RFC7540 6.1)'};
117parse(<< Len0:24, 0:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 ->
118	{connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.1)'};
119%% No padding.
120parse(<< Len:24, 0:8, _:4, 0:1, _:2, FlagEndStream:1, _:1, StreamID:31, Data:Len/binary, Rest/bits >>) ->
121	{ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest};
122%% Padding.
123parse(<< Len0:24, 0:8, _:4, 1:1, _:2, FlagEndStream:1, _:1, StreamID:31, PadLen:8, Rest0/bits >>)
124		when byte_size(Rest0) >= Len0 - 1 ->
125	Len = Len0 - PadLen - 1,
126	case Rest0 of
127		<< Data:Len/binary, 0:PadLen/unit:8, Rest/bits >> ->
128			{ok, {data, StreamID, parse_fin(FlagEndStream), Data}, Rest};
129		_ ->
130			{connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.1)'}
131	end;
132%%
133%% HEADERS frames.
134%%
135parse(<< _:24, 1:8, _:9, 0:31, _/bits >>) ->
136	{connection_error, protocol_error, 'HEADERS frames MUST be associated with a stream. (RFC7540 6.2)'};
137parse(<< 0:24, 1:8, _:4, 1:1, _:35, _/bits >>) ->
138	{connection_error, frame_size_error, 'HEADERS frames with padding flag MUST have a length > 0. (RFC7540 6.1)'};
139parse(<< Len:24, 1:8, _:2, 1:1, _:37, _/bits >>) when Len < 5 ->
140	{connection_error, frame_size_error, 'HEADERS frames with priority flag MUST have a length >= 5. (RFC7540 6.1)'};
141parse(<< Len:24, 1:8, _:2, 1:1, _:1, 1:1, _:35, _/bits >>) when Len < 6 ->
142	{connection_error, frame_size_error, 'HEADERS frames with padding and priority flags MUST have a length >= 6. (RFC7540 6.1)'};
143parse(<< Len0:24, 1:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 ->
144	{connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'};
145parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 - 5 ->
146	{connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.2)'};
147%% No padding, no priority.
148parse(<< Len:24, 1:8, _:2, 0:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31,
149		HeaderBlockFragment:Len/binary, Rest/bits >>) ->
150	{ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest};
151%% Padding, no priority.
152parse(<< Len0:24, 1:8, _:2, 0:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31,
153		PadLen:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 1 ->
154	Len = Len0 - PadLen - 1,
155	case Rest0 of
156		<< HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> ->
157			{ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest};
158		_ ->
159			{connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'}
160	end;
161%% No padding, priority.
162parse(<< _:24, 1:8, _:2, 1:1, _:1, 0:1, _:4, StreamID:31, _:1, StreamID:31, _/bits >>) ->
163	{connection_error, protocol_error,
164		'HEADERS frames cannot define a stream that depends on itself. (RFC7540 5.3.1)'};
165parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 0:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31,
166		E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 5 ->
167	Len = Len0 - 5,
168	<< HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0,
169	{ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders),
170		parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest};
171%% Padding, priority.
172parse(<< _:24, 1:8, _:2, 1:1, _:1, 1:1, _:4, StreamID:31, _:9, StreamID:31, _/bits >>) ->
173	{connection_error, protocol_error,
174		'HEADERS frames cannot define a stream that depends on itself. (RFC7540 5.3.1)'};
175parse(<< Len0:24, 1:8, _:2, 1:1, _:1, 1:1, FlagEndHeaders:1, _:1, FlagEndStream:1, _:1, StreamID:31,
176		PadLen:8, E:1, DepStreamID:31, Weight:8, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 6 ->
177	Len = Len0 - PadLen - 6,
178	case Rest0 of
179		<< HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> ->
180			{ok, {headers, StreamID, parse_fin(FlagEndStream), parse_head_fin(FlagEndHeaders),
181				parse_exclusive(E), DepStreamID, Weight + 1, HeaderBlockFragment}, Rest};
182		_ ->
183			{connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.2)'}
184	end;
185%%
186%% PRIORITY frames.
187%%
188parse(<< 5:24, 2:8, _:9, 0:31, _/bits >>) ->
189	{connection_error, protocol_error, 'PRIORITY frames MUST be associated with a stream. (RFC7540 6.3)'};
190parse(<< 5:24, 2:8, _:9, StreamID:31, _:1, StreamID:31, _:8, Rest/bits >>) ->
191	{stream_error, StreamID, protocol_error,
192		'PRIORITY frames cannot make a stream depend on itself. (RFC7540 5.3.1)', Rest};
193parse(<< 5:24, 2:8, _:9, StreamID:31, E:1, DepStreamID:31, Weight:8, Rest/bits >>) ->
194	{ok, {priority, StreamID, parse_exclusive(E), DepStreamID, Weight + 1}, Rest};
195%% @todo figure out how to best deal with frame size errors; if we have everything fine
196%% if not we might want to inform the caller how much he should expect so that it can
197%% decide if it should just close the connection
198parse(<< BadLen:24, 2:8, _:9, StreamID:31, _:BadLen/binary, Rest/bits >>) ->
199	{stream_error, StreamID, frame_size_error, 'PRIORITY frames MUST be 5 bytes wide. (RFC7540 6.3)', Rest};
200%%
201%% RST_STREAM frames.
202%%
203parse(<< 4:24, 3:8, _:9, 0:31, _/bits >>) ->
204	{connection_error, protocol_error, 'RST_STREAM frames MUST be associated with a stream. (RFC7540 6.4)'};
205parse(<< 4:24, 3:8, _:9, StreamID:31, ErrorCode:32, Rest/bits >>) ->
206	{ok, {rst_stream, StreamID, parse_error_code(ErrorCode)}, Rest};
207%% @todo same as priority
208parse(<< _:24, 3:8, _:9, _:31, _/bits >>) ->
209	{connection_error, frame_size_error, 'RST_STREAM frames MUST be 4 bytes wide. (RFC7540 6.4)'};
210%%
211%% SETTINGS frames.
212%%
213parse(<< 0:24, 4:8, _:7, 1:1, _:1, 0:31, Rest/bits >>) ->
214	{ok, settings_ack, Rest};
215parse(<< _:24, 4:8, _:7, 1:1, _:1, 0:31, _/bits >>) ->
216	{connection_error, frame_size_error, 'SETTINGS frames with the ACK flag set MUST have a length of 0. (RFC7540 6.5)'};
217parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, _/bits >>) when Len rem 6 =/= 0 ->
218	{connection_error, frame_size_error, 'SETTINGS frames MUST have a length multiple of 6. (RFC7540 6.5)'};
219parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, Rest/bits >>) when byte_size(Rest) >= Len ->
220	parse_settings_payload(Rest, Len, #{});
221parse(<< _:24, 4:8, _:8, _:1, StreamID:31, _/bits >>) when StreamID =/= 0 ->
222	{connection_error, protocol_error, 'SETTINGS frames MUST NOT be associated with a stream. (RFC7540 6.5)'};
223%%
224%% PUSH_PROMISE frames.
225%%
226parse(<< Len:24, 5:8, _:40, _/bits >>) when Len < 4 ->
227	{connection_error, frame_size_error, 'PUSH_PROMISE frames MUST have a length >= 4. (RFC7540 4.2, RFC7540 6.6)'};
228parse(<< Len:24, 5:8, _:4, 1:1, _:35, _/bits >>) when Len < 5 ->
229	{connection_error, frame_size_error, 'PUSH_PROMISE frames with padding flag MUST have a length >= 5. (RFC7540 4.2, RFC7540 6.6)'};
230parse(<< _:24, 5:8, _:9, 0:31, _/bits >>) ->
231	{connection_error, protocol_error, 'PUSH_PROMISE frames MUST be associated with a stream. (RFC7540 6.6)'};
232parse(<< Len0:24, 5:8, _:4, 1:1, _:35, PadLen:8, _/bits >>) when PadLen >= Len0 - 4 ->
233	{connection_error, protocol_error, 'Length of padding MUST be less than length of payload. (RFC7540 6.6)'};
234parse(<< Len0:24, 5:8, _:4, 0:1, FlagEndHeaders:1, _:3, StreamID:31, _:1, PromisedStreamID:31, Rest0/bits >>)
235		when byte_size(Rest0) >= Len0 - 4 ->
236	Len = Len0 - 4,
237	<< HeaderBlockFragment:Len/binary, Rest/bits >> = Rest0,
238	{ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest};
239parse(<< Len0:24, 5:8, _:4, 1:1, FlagEndHeaders:1, _:2, StreamID:31, PadLen:8, _:1, PromisedStreamID:31, Rest0/bits >>)
240		when byte_size(Rest0) >= Len0 - 5 ->
241	Len = Len0 - 5,
242	case Rest0 of
243		<< HeaderBlockFragment:Len/binary, 0:PadLen/unit:8, Rest/bits >> ->
244			{ok, {push_promise, StreamID, parse_head_fin(FlagEndHeaders), PromisedStreamID, HeaderBlockFragment}, Rest};
245		_ ->
246			{connection_error, protocol_error, 'Padding octets MUST be set to zero. (RFC7540 6.6)'}
247	end;
248%%
249%% PING frames.
250%%
251parse(<< 8:24, 6:8, _:7, 1:1, _:1, 0:31, Opaque:64, Rest/bits >>) ->
252	{ok, {ping_ack, Opaque}, Rest};
253parse(<< 8:24, 6:8, _:7, 0:1, _:1, 0:31, Opaque:64, Rest/bits >>) ->
254	{ok, {ping, Opaque}, Rest};
255parse(<< 8:24, 6:8, _:104, _/bits >>) ->
256	{connection_error, protocol_error, 'PING frames MUST NOT be associated with a stream. (RFC7540 6.7)'};
257parse(<< Len:24, 6:8, _/bits >>) when Len =/= 8 ->
258	{connection_error, frame_size_error, 'PING frames MUST be 8 bytes wide. (RFC7540 6.7)'};
259%%
260%% GOAWAY frames.
261%%
262parse(<< Len0:24, 7:8, _:9, 0:31, _:1, LastStreamID:31, ErrorCode:32, Rest0/bits >>) when byte_size(Rest0) >= Len0 - 8 ->
263	Len = Len0 - 8,
264	<< DebugData:Len/binary, Rest/bits >> = Rest0,
265	{ok, {goaway, LastStreamID, parse_error_code(ErrorCode), DebugData}, Rest};
266parse(<< Len:24, 7:8, _:40, _/bits >>) when Len < 8 ->
267	{connection_error, frame_size_error, 'GOAWAY frames MUST have a length >= 8. (RFC7540 4.2, RFC7540 6.8)'};
268parse(<< _:24, 7:8, _:40, _/bits >>) ->
269	{connection_error, protocol_error, 'GOAWAY frames MUST NOT be associated with a stream. (RFC7540 6.8)'};
270%%
271%% WINDOW_UPDATE frames.
272%%
273parse(<< 4:24, 8:8, _:9, 0:31, _:1, 0:31, _/bits >>) ->
274	{connection_error, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)'};
275parse(<< 4:24, 8:8, _:9, 0:31, _:1, Increment:31, Rest/bits >>) ->
276	{ok, {window_update, Increment}, Rest};
277parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, 0:31, Rest/bits >>) ->
278	{stream_error, StreamID, protocol_error, 'WINDOW_UPDATE frames MUST have a non-zero increment. (RFC7540 6.9)', Rest};
279parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, Increment:31, Rest/bits >>) ->
280	{ok, {window_update, StreamID, Increment}, Rest};
281parse(<< Len:24, 8:8, _/bits >>) when Len =/= 4->
282	{connection_error, frame_size_error, 'WINDOW_UPDATE frames MUST be 4 bytes wide. (RFC7540 6.9)'};
283%%
284%% CONTINUATION frames.
285%%
286parse(<< _:24, 9:8, _:9, 0:31, _/bits >>) ->
287	{connection_error, protocol_error, 'CONTINUATION frames MUST be associated with a stream. (RFC7540 6.10)'};
288parse(<< Len:24, 9:8, _:5, FlagEndHeaders:1, _:3, StreamID:31, HeaderBlockFragment:Len/binary, Rest/bits >>) ->
289	{ok, {continuation, StreamID, parse_head_fin(FlagEndHeaders), HeaderBlockFragment}, Rest};
290%%
291%% Unknown frames are ignored.
292%%
293parse(<< Len:24, Type:8, _:40, _:Len/binary, Rest/bits >>) when Type > 9 ->
294	{ignore, Rest};
295%%
296%% Incomplete frames.
297%%
298parse(_) ->
299	more.
300
301-ifdef(TEST).
302parse_ping_test() ->
303	Ping = ping(1234567890),
304	_ = [more = parse(binary:part(Ping, 0, I)) || I <- lists:seq(1, byte_size(Ping) - 1)],
305	{ok, {ping, 1234567890}, <<>>} = parse(Ping),
306	{ok, {ping, 1234567890}, << 42 >>} = parse(<< Ping/binary, 42 >>),
307	ok.
308
309parse_windows_update_test() ->
310	WindowUpdate = << 4:24, 8:8, 0:9, 0:31, 0:1, 12345:31 >>,
311	_ = [more = parse(binary:part(WindowUpdate, 0, I)) || I <- lists:seq(1, byte_size(WindowUpdate) - 1)],
312	{ok, {window_update, 12345}, <<>>} = parse(WindowUpdate),
313	{ok, {window_update, 12345}, << 42 >>} = parse(<< WindowUpdate/binary, 42 >>),
314	ok.
315
316parse_settings_test() ->
317	more = parse(<< 0:24, 4:8, 1:8, 0:8 >>),
318	{ok, settings_ack, <<>>} = parse(<< 0:24, 4:8, 1:8, 0:32 >>),
319	{connection_error, protocol_error, _} = parse(<< 0:24, 4:8, 1:8, 0:1, 1:31 >>),
320	ok.
321-endif.
322
323parse_fin(0) -> nofin;
324parse_fin(1) -> fin.
325
326parse_head_fin(0) -> head_nofin;
327parse_head_fin(1) -> head_fin.
328
329parse_exclusive(0) -> shared;
330parse_exclusive(1) -> exclusive.
331
332parse_error_code( 0) -> no_error;
333parse_error_code( 1) -> protocol_error;
334parse_error_code( 2) -> internal_error;
335parse_error_code( 3) -> flow_control_error;
336parse_error_code( 4) -> settings_timeout;
337parse_error_code( 5) -> stream_closed;
338parse_error_code( 6) -> frame_size_error;
339parse_error_code( 7) -> refused_stream;
340parse_error_code( 8) -> cancel;
341parse_error_code( 9) -> compression_error;
342parse_error_code(10) -> connect_error;
343parse_error_code(11) -> enhance_your_calm;
344parse_error_code(12) -> inadequate_security;
345parse_error_code(13) -> http_1_1_required;
346parse_error_code(_) -> unknown_error.
347
348parse_settings_payload(SettingsPayload) ->
349	{ok, {settings, Settings}, <<>>}
350		= parse_settings_payload(SettingsPayload, byte_size(SettingsPayload), #{}),
351	Settings.
352
353parse_settings_payload(Rest, 0, Settings) ->
354	{ok, {settings, Settings}, Rest};
355%% SETTINGS_HEADER_TABLE_SIZE.
356parse_settings_payload(<< 1:16, Value:32, Rest/bits >>, Len, Settings) ->
357	parse_settings_payload(Rest, Len - 6, Settings#{header_table_size => Value});
358%% SETTINGS_ENABLE_PUSH.
359parse_settings_payload(<< 2:16, 0:32, Rest/bits >>, Len, Settings) ->
360	parse_settings_payload(Rest, Len - 6, Settings#{enable_push => false});
361parse_settings_payload(<< 2:16, 1:32, Rest/bits >>, Len, Settings) ->
362	parse_settings_payload(Rest, Len - 6, Settings#{enable_push => true});
363parse_settings_payload(<< 2:16, _:32, _/bits >>, _, _) ->
364	{connection_error, protocol_error, 'The SETTINGS_ENABLE_PUSH value MUST be 0 or 1. (RFC7540 6.5.2)'};
365%% SETTINGS_MAX_CONCURRENT_STREAMS.
366parse_settings_payload(<< 3:16, Value:32, Rest/bits >>, Len, Settings) ->
367	parse_settings_payload(Rest, Len - 6, Settings#{max_concurrent_streams => Value});
368%% SETTINGS_INITIAL_WINDOW_SIZE.
369parse_settings_payload(<< 4:16, Value:32, _/bits >>, _, _) when Value > 16#7fffffff ->
370	{connection_error, flow_control_error, 'The maximum SETTINGS_INITIAL_WINDOW_SIZE value is 0x7fffffff. (RFC7540 6.5.2)'};
371parse_settings_payload(<< 4:16, Value:32, Rest/bits >>, Len, Settings) ->
372	parse_settings_payload(Rest, Len - 6, Settings#{initial_window_size => Value});
373%% SETTINGS_MAX_FRAME_SIZE.
374parse_settings_payload(<< 5:16, Value:32, _/bits >>, _, _) when Value =< 16#3fff ->
375	{connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be > 0x3fff. (RFC7540 6.5.2)'};
376parse_settings_payload(<< 5:16, Value:32, Rest/bits >>, Len, Settings) when Value =< 16#ffffff ->
377	parse_settings_payload(Rest, Len - 6, Settings#{max_frame_size => Value});
378parse_settings_payload(<< 5:16, _:32, _/bits >>, _, _) ->
379	{connection_error, protocol_error, 'The SETTINGS_MAX_FRAME_SIZE value must be =< 0xffffff. (RFC7540 6.5.2)'};
380%% SETTINGS_MAX_HEADER_LIST_SIZE.
381parse_settings_payload(<< 6:16, Value:32, Rest/bits >>, Len, Settings) ->
382	parse_settings_payload(Rest, Len - 6, Settings#{max_header_list_size => Value});
383%% SETTINGS_ENABLE_CONNECT_PROTOCOL.
384parse_settings_payload(<< 8:16, 0:32, Rest/bits >>, Len, Settings) ->
385	parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => false});
386parse_settings_payload(<< 8:16, 1:32, Rest/bits >>, Len, Settings) ->
387	parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => true});
388parse_settings_payload(<< 8:16, _:32, _/bits >>, _, _) ->
389	{connection_error, protocol_error, 'The SETTINGS_ENABLE_CONNECT_PROTOCOL value MUST be 0 or 1. (draft-h2-websockets-01 3)'};
390%% Ignore unknown settings.
391parse_settings_payload(<< _:48, Rest/bits >>, Len, Settings) ->
392	parse_settings_payload(Rest, Len - 6, Settings).
393
394%% Building.
395
396data(StreamID, IsFin, Data) ->
397	[data_header(StreamID, IsFin, iolist_size(Data)), Data].
398
399data_header(StreamID, IsFin, Len) ->
400	FlagEndStream = flag_fin(IsFin),
401	<< Len:24, 0:15, FlagEndStream:1, 0:1, StreamID:31 >>.
402
403%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed.
404headers(StreamID, IsFin, HeaderBlock) ->
405	Len = iolist_size(HeaderBlock),
406	FlagEndStream = flag_fin(IsFin),
407	FlagEndHeaders = 1,
408	[<< Len:24, 1:8, 0:5, FlagEndHeaders:1, 0:1, FlagEndStream:1, 0:1, StreamID:31 >>, HeaderBlock].
409
410priority(StreamID, E, DepStreamID, Weight) ->
411	FlagExclusive = exclusive(E),
412	<< 5:24, 2:8, 0:9, StreamID:31, FlagExclusive:1, DepStreamID:31, Weight:8 >>.
413
414rst_stream(StreamID, Reason) ->
415	ErrorCode = error_code(Reason),
416	<< 4:24, 3:8, 0:9, StreamID:31, ErrorCode:32 >>.
417
418settings(Settings) ->
419	Payload = settings_payload(Settings),
420	Len = iolist_size(Payload),
421	[<< Len:24, 4:8, 0:40 >>, Payload].
422
423settings_payload(Settings) ->
424	[case Key of
425		header_table_size -> <<1:16, Value:32>>;
426		enable_push when Value -> <<2:16, 1:32>>;
427		enable_push -> <<2:16, 0:32>>;
428		max_concurrent_streams when Value =:= infinity -> <<>>;
429		max_concurrent_streams -> <<3:16, Value:32>>;
430		initial_window_size -> <<4:16, Value:32>>;
431		max_frame_size -> <<5:16, Value:32>>;
432		max_header_list_size when Value =:= infinity -> <<>>;
433		max_header_list_size -> <<6:16, Value:32>>;
434		enable_connect_protocol when Value -> <<8:16, 1:32>>;
435		enable_connect_protocol -> <<8:16, 0:32>>
436	end || {Key, Value} <- maps:to_list(Settings)].
437
438settings_ack() ->
439	<< 0:24, 4:8, 1:8, 0:32 >>.
440
441%% @todo Check size of HeaderBlock and use CONTINUATION frames if needed.
442push_promise(StreamID, PromisedStreamID, HeaderBlock) ->
443	Len = iolist_size(HeaderBlock) + 4,
444	FlagEndHeaders = 1,
445	[<< Len:24, 5:8, 0:5, FlagEndHeaders:1, 0:3, StreamID:31, 0:1, PromisedStreamID:31 >>, HeaderBlock].
446
447ping(Opaque) ->
448	<< 8:24, 6:8, 0:40, Opaque:64 >>.
449
450ping_ack(Opaque) ->
451	<< 8:24, 6:8, 0:7, 1:1, 0:32, Opaque:64 >>.
452
453goaway(LastStreamID, Reason, DebugData) ->
454	ErrorCode = error_code(Reason),
455	Len = iolist_size(DebugData) + 8,
456	[<< Len:24, 7:8, 0:41, LastStreamID:31, ErrorCode:32 >>, DebugData].
457
458window_update(Increment) ->
459	window_update(0, Increment).
460
461window_update(StreamID, Increment) when Increment =< 16#7fffffff ->
462	<< 4:24, 8:8, 0:8, StreamID:32, 0:1, Increment:31 >>.
463
464flag_fin(nofin) -> 0;
465flag_fin(fin) -> 1.
466
467exclusive(shared) -> 0;
468exclusive(exclusive) -> 1.
469
470error_code(no_error) -> 0;
471error_code(protocol_error) -> 1;
472error_code(internal_error) -> 2;
473error_code(flow_control_error) -> 3;
474error_code(settings_timeout) -> 4;
475error_code(stream_closed) -> 5;
476error_code(frame_size_error) -> 6;
477error_code(refused_stream) -> 7;
478error_code(cancel) -> 8;
479error_code(compression_error) -> 9;
480error_code(connect_error) -> 10;
481error_code(enhance_your_calm) -> 11;
482error_code(inadequate_security) -> 12;
483error_code(http_1_1_required) -> 13.
484