cow_http2.erl (21529B)
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()}. 89 parse_sequence(<<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", Rest/bits>>) -> 90 {ok, Rest}; 91 parse_sequence(Data) when byte_size(Data) >= 24 -> 92 {connection_error, protocol_error, 93 'The connection preface was invalid. (RFC7540 3.5)'}; 94 parse_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 105 parse(<< Len:24, _/bits >>, MaxFrameSize) when Len > MaxFrameSize -> 106 {connection_error, frame_size_error, 'The frame size exceeded SETTINGS_MAX_FRAME_SIZE. (RFC7540 4.2)'}; 107 parse(Data, _) -> 108 parse(Data). 109 110 %% 111 %% DATA frames. 112 %% 113 parse(<< _:24, 0:8, _:9, 0:31, _/bits >>) -> 114 {connection_error, protocol_error, 'DATA frames MUST be associated with a stream. (RFC7540 6.1)'}; 115 parse(<< 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)'}; 117 parse(<< 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. 120 parse(<< 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. 123 parse(<< 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 %% 135 parse(<< _:24, 1:8, _:9, 0:31, _/bits >>) -> 136 {connection_error, protocol_error, 'HEADERS frames MUST be associated with a stream. (RFC7540 6.2)'}; 137 parse(<< 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)'}; 139 parse(<< 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)'}; 141 parse(<< 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)'}; 143 parse(<< 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)'}; 145 parse(<< 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. 148 parse(<< 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. 152 parse(<< 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. 162 parse(<< _: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)'}; 165 parse(<< 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. 172 parse(<< _: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)'}; 175 parse(<< 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 %% 188 parse(<< 5:24, 2:8, _:9, 0:31, _/bits >>) -> 189 {connection_error, protocol_error, 'PRIORITY frames MUST be associated with a stream. (RFC7540 6.3)'}; 190 parse(<< 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}; 193 parse(<< 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 198 parse(<< 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 %% 203 parse(<< 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)'}; 205 parse(<< 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 208 parse(<< _: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 %% 213 parse(<< 0:24, 4:8, _:7, 1:1, _:1, 0:31, Rest/bits >>) -> 214 {ok, settings_ack, Rest}; 215 parse(<< _: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)'}; 217 parse(<< 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)'}; 219 parse(<< Len:24, 4:8, _:7, 0:1, _:1, 0:31, Rest/bits >>) when byte_size(Rest) >= Len -> 220 parse_settings_payload(Rest, Len, #{}); 221 parse(<< _: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 %% 226 parse(<< 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)'}; 228 parse(<< 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)'}; 230 parse(<< _:24, 5:8, _:9, 0:31, _/bits >>) -> 231 {connection_error, protocol_error, 'PUSH_PROMISE frames MUST be associated with a stream. (RFC7540 6.6)'}; 232 parse(<< 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)'}; 234 parse(<< 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}; 239 parse(<< 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 %% 251 parse(<< 8:24, 6:8, _:7, 1:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> 252 {ok, {ping_ack, Opaque}, Rest}; 253 parse(<< 8:24, 6:8, _:7, 0:1, _:1, 0:31, Opaque:64, Rest/bits >>) -> 254 {ok, {ping, Opaque}, Rest}; 255 parse(<< 8:24, 6:8, _:104, _/bits >>) -> 256 {connection_error, protocol_error, 'PING frames MUST NOT be associated with a stream. (RFC7540 6.7)'}; 257 parse(<< 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 %% 262 parse(<< 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}; 266 parse(<< 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)'}; 268 parse(<< _: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 %% 273 parse(<< 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)'}; 275 parse(<< 4:24, 8:8, _:9, 0:31, _:1, Increment:31, Rest/bits >>) -> 276 {ok, {window_update, Increment}, Rest}; 277 parse(<< 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}; 279 parse(<< 4:24, 8:8, _:9, StreamID:31, _:1, Increment:31, Rest/bits >>) -> 280 {ok, {window_update, StreamID, Increment}, Rest}; 281 parse(<< 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 %% 286 parse(<< _:24, 9:8, _:9, 0:31, _/bits >>) -> 287 {connection_error, protocol_error, 'CONTINUATION frames MUST be associated with a stream. (RFC7540 6.10)'}; 288 parse(<< 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 %% 293 parse(<< Len:24, Type:8, _:40, _:Len/binary, Rest/bits >>) when Type > 9 -> 294 {ignore, Rest}; 295 %% 296 %% Incomplete frames. 297 %% 298 parse(_) -> 299 more. 300 301 -ifdef(TEST). 302 parse_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 309 parse_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 316 parse_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 323 parse_fin(0) -> nofin; 324 parse_fin(1) -> fin. 325 326 parse_head_fin(0) -> head_nofin; 327 parse_head_fin(1) -> head_fin. 328 329 parse_exclusive(0) -> shared; 330 parse_exclusive(1) -> exclusive. 331 332 parse_error_code( 0) -> no_error; 333 parse_error_code( 1) -> protocol_error; 334 parse_error_code( 2) -> internal_error; 335 parse_error_code( 3) -> flow_control_error; 336 parse_error_code( 4) -> settings_timeout; 337 parse_error_code( 5) -> stream_closed; 338 parse_error_code( 6) -> frame_size_error; 339 parse_error_code( 7) -> refused_stream; 340 parse_error_code( 8) -> cancel; 341 parse_error_code( 9) -> compression_error; 342 parse_error_code(10) -> connect_error; 343 parse_error_code(11) -> enhance_your_calm; 344 parse_error_code(12) -> inadequate_security; 345 parse_error_code(13) -> http_1_1_required; 346 parse_error_code(_) -> unknown_error. 347 348 parse_settings_payload(SettingsPayload) -> 349 {ok, {settings, Settings}, <<>>} 350 = parse_settings_payload(SettingsPayload, byte_size(SettingsPayload), #{}), 351 Settings. 352 353 parse_settings_payload(Rest, 0, Settings) -> 354 {ok, {settings, Settings}, Rest}; 355 %% SETTINGS_HEADER_TABLE_SIZE. 356 parse_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. 359 parse_settings_payload(<< 2:16, 0:32, Rest/bits >>, Len, Settings) -> 360 parse_settings_payload(Rest, Len - 6, Settings#{enable_push => false}); 361 parse_settings_payload(<< 2:16, 1:32, Rest/bits >>, Len, Settings) -> 362 parse_settings_payload(Rest, Len - 6, Settings#{enable_push => true}); 363 parse_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. 366 parse_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. 369 parse_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)'}; 371 parse_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. 374 parse_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)'}; 376 parse_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}); 378 parse_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. 381 parse_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. 384 parse_settings_payload(<< 8:16, 0:32, Rest/bits >>, Len, Settings) -> 385 parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => false}); 386 parse_settings_payload(<< 8:16, 1:32, Rest/bits >>, Len, Settings) -> 387 parse_settings_payload(Rest, Len - 6, Settings#{enable_connect_protocol => true}); 388 parse_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. 391 parse_settings_payload(<< _:48, Rest/bits >>, Len, Settings) -> 392 parse_settings_payload(Rest, Len - 6, Settings). 393 394 %% Building. 395 396 data(StreamID, IsFin, Data) -> 397 [data_header(StreamID, IsFin, iolist_size(Data)), Data]. 398 399 data_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. 404 headers(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 410 priority(StreamID, E, DepStreamID, Weight) -> 411 FlagExclusive = exclusive(E), 412 << 5:24, 2:8, 0:9, StreamID:31, FlagExclusive:1, DepStreamID:31, Weight:8 >>. 413 414 rst_stream(StreamID, Reason) -> 415 ErrorCode = error_code(Reason), 416 << 4:24, 3:8, 0:9, StreamID:31, ErrorCode:32 >>. 417 418 settings(Settings) -> 419 Payload = settings_payload(Settings), 420 Len = iolist_size(Payload), 421 [<< Len:24, 4:8, 0:40 >>, Payload]. 422 423 settings_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 438 settings_ack() -> 439 << 0:24, 4:8, 1:8, 0:32 >>. 440 441 %% @todo Check size of HeaderBlock and use CONTINUATION frames if needed. 442 push_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 447 ping(Opaque) -> 448 << 8:24, 6:8, 0:40, Opaque:64 >>. 449 450 ping_ack(Opaque) -> 451 << 8:24, 6:8, 0:7, 1:1, 0:32, Opaque:64 >>. 452 453 goaway(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 458 window_update(Increment) -> 459 window_update(0, Increment). 460 461 window_update(StreamID, Increment) when Increment =< 16#7fffffff -> 462 << 4:24, 8:8, 0:8, StreamID:32, 0:1, Increment:31 >>. 463 464 flag_fin(nofin) -> 0; 465 flag_fin(fin) -> 1. 466 467 exclusive(shared) -> 0; 468 exclusive(exclusive) -> 1. 469 470 error_code(no_error) -> 0; 471 error_code(protocol_error) -> 1; 472 error_code(internal_error) -> 2; 473 error_code(flow_control_error) -> 3; 474 error_code(settings_timeout) -> 4; 475 error_code(stream_closed) -> 5; 476 error_code(frame_size_error) -> 6; 477 error_code(refused_stream) -> 7; 478 error_code(cancel) -> 8; 479 error_code(compression_error) -> 9; 480 error_code(connect_error) -> 10; 481 error_code(enhance_your_calm) -> 11; 482 error_code(inadequate_security) -> 12; 483 error_code(http_1_1_required) -> 13.