cow_http2.erl 19 KB

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