1 /** 2 * Types 3 * 4 * Copyright: 5 * (C) 2012-2015 Tatsuhiro Tsujikawa 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Distributed under the terms of the MIT license with an additional section 1.2 of the curl/libcurl project. 10 * Consult the provided LICENSE.md file for details 11 */ 12 module libhttp2.types; 13 14 import libhttp2.constants; 15 import libhttp2.helpers; 16 import std.conv : to; 17 import memutils.refcounted; 18 import memutils.utils; 19 20 alias Mem = ThreadMem; 21 void LOGF(ARGS...)(lazy ARGS args) { 22 import std.stdio: writefln; 23 static if (DEBUG) 24 writefln(args); 25 } 26 27 void logDebug(ARGS...)(lazy ARGS args) { 28 import std.stdio: writeln; 29 static if (DEBUG) 30 writeln("D: ", args); 31 } 32 33 /// Return values used in this library. The code range is [-999, -500], inclusive. 34 enum ErrorCode : int { 35 OK = 0, 36 37 ERROR = -1, 38 CREDENTIAL_PENDING = -101, 39 IGN_HEADER_BLOCK = -103, 40 41 /* 42 * Invalid HTTP header field was received but it can be treated as 43 * if it was not received because of compatibility reasons. 44 */ 45 IGN_HTTP_HEADER = -105, 46 47 IGN_PAYLOAD = -104, 48 /// Invalid argument passed. 49 INVALID_ARGUMENT = -501, 50 51 /// Out of buffer space. 52 BUFFER_ERROR = -502, 53 54 /// The specified protocol version is not supported. 55 UNSUPPORTED_VERSION = -503, 56 57 /// Used as a return value from http2_send_callback and http2_recv_callback 58 /// to indicate that the operation would block. 59 WOULDBLOCK = -504, 60 61 /// General protocol error 62 PROTO = -505, 63 64 /// The frame is invalid. 65 INVALID_FRAME = -506, 66 67 /// The peer performed a shutdown on the connection. 68 EOF = -507, 69 70 /// Used as a return value from DataProvider to indicate that data 71 /// transfer is postponed. See DataProvider for details. 72 DEFERRED = -508, 73 74 /// Stream ID has reached the maximum value. Therefore no stream ID is available. 75 STREAM_ID_NOT_AVAILABLE = -509, 76 77 /// The stream is already closed; or the stream ID is invalid. 78 STREAM_CLOSED = -510, 79 80 /// RST_STREAM has been added to the outbound queue. The stream is in closing state. 81 STREAM_CLOSING = -511, 82 83 /// The transmission is not allowed for this stream (e.g., a frame with END_STREAM flag set has already sent). 84 STREAM_SHUT_WR = -512, 85 86 /// The stream ID is invalid. 87 INVALID_STREAM_ID = -513, 88 89 /// The state of the stream is not valid (e.g., DATA cannot be sent to the stream if response HEADERS has not been sent). 90 INVALID_STREAM_STATE = -514, 91 /// Another DATA frame has already been deferred. 92 /// 93 DEFERRED_DATA_EXIST = -515, 94 95 /// Starting new stream is not allowed (e.g., GOAWAY has been sent and/or received). 96 START_STREAM_NOT_ALLOWED = -516, 97 /** 98 * GOAWAY has already been sent. 99 */ 100 GOAWAY_ALREADY_SENT = -517, 101 102 /** 103 * The received frame contains the invalid header block (e.g., There 104 * are duplicate header names; or the header names are not encoded 105 * in US-ASCII character set and not lower cased; or the header name 106 * is zero-length string; or the header value contains multiple 107 * in-sequence NUL bytes). 108 */ 109 INVALID_HEADER_BLOCK = -518, 110 111 /// Indicates that the context is not suitable to perform the requested operation. 112 INVALID_STATE = -519, 113 114 /// The user callback function failed due to the temporal error. 115 TEMPORAL_CALLBACK_FAILURE = -521, 116 117 /// The length of the frame is invalid, either too large or too small. 118 FRAME_SIZE_ERROR = -522, 119 120 /// Header block inflate/deflate error. 121 HEADER_COMP = -523, 122 123 /// Flow control error 124 FLOW_CONTROL = -524, 125 126 /// Insufficient buffer size given to function. 127 INSUFF_BUFSIZE = -525, 128 129 /// Callback was paused by the application 130 PAUSE = -526, 131 132 /// There are too many in-flight SETTING frame and no more transmission of SETTINGS is allowed. 133 TOO_MANY_INFLIGHT_SETTINGS = -527, 134 135 /// The server push is disabled. 136 PUSH_DISABLED = -528, 137 138 /// DATA frame for a given stream has been already submitted and has not been fully processed yet. 139 DATA_EXIST = -529, 140 141 /// The current session is closing due to a connection error or http2_session_terminate_session() is called. 142 SESSION_CLOSING = -530, 143 144 /// Invalid HTTP header field was received and stream is going to be closed. 145 HTTP_HEADER = -531, 146 147 /** 148 * The errors < FATAL mean that the library is under unexpected condition and processing was terminated (e.g., 149 * out of memory). If application receives this error code, it must stop using that $(D Session) object and only allowed 150 * operation for that object is deallocate it using http2_session_del(). 151 */ 152 FATAL = -900, 153 154 /// Out of memory. This is a fatal error. 155 NOMEM = -901, 156 157 /// The user callback function failed. This is a fatal error. 158 CALLBACK_FAILURE = -902, 159 160 /// Invalid connection preface was received and further processing is not possible. 161 BAD_PREFACE = -903 162 } 163 164 /* 165 * Returns string describing the |error_code|. The |error_code| must be one of the $(D ErrorCode). 166 */ 167 string toString(ErrorCode error_code) { 168 with(ErrorCode) switch (error_code) { 169 case OK: 170 return "Success"; 171 case ERROR: 172 return "Unknown error"; 173 case INVALID_ARGUMENT: 174 return "Invalid argument"; 175 case BUFFER_ERROR: 176 return "Out of buffer space"; 177 case UNSUPPORTED_VERSION: 178 return "Unsupported SPDY version"; 179 case WOULDBLOCK: 180 return "Operation would block"; 181 case PROTO: 182 return "Protocol error"; 183 case INVALID_FRAME: 184 return "Invalid frame octets"; 185 case EOF: 186 return "EOF"; 187 case DEFERRED: 188 return "Data transfer deferred"; 189 case STREAM_ID_NOT_AVAILABLE: 190 return "No more Stream ID available"; 191 case STREAM_CLOSED: 192 return "Stream was already closed or invalid"; 193 case STREAM_CLOSING: 194 return "Stream is closing"; 195 case STREAM_SHUT_WR: 196 return "The transmission is not allowed for this stream"; 197 case INVALID_STREAM_ID: 198 return "Stream ID is invalid"; 199 case INVALID_STREAM_STATE: 200 return "Invalid stream state"; 201 case DEFERRED_DATA_EXIST: 202 return "Another DATA frame has already been deferred"; 203 case SESSION_CLOSING: 204 return "The current session is closing"; 205 case START_STREAM_NOT_ALLOWED: 206 return "request HEADERS is not allowed"; 207 case GOAWAY_ALREADY_SENT: 208 return "GOAWAY has already been sent"; 209 case INVALID_HEADER_BLOCK: 210 return "Invalid header block"; 211 case INVALID_STATE: 212 return "Invalid state"; 213 case TEMPORAL_CALLBACK_FAILURE: 214 return "The user callback function failed due to the temporal error"; 215 case FRAME_SIZE_ERROR: 216 return "The length of the frame is invalid"; 217 case HEADER_COMP: 218 return "Header compression/decompression error"; 219 case FLOW_CONTROL: 220 return "Flow control error"; 221 case INSUFF_BUFSIZE: 222 return "Insufficient buffer size given to function"; 223 case PAUSE: 224 return "Callback was paused by the application"; 225 case TOO_MANY_INFLIGHT_SETTINGS: 226 return "Too many inflight SETTINGS"; 227 case PUSH_DISABLED: 228 return "Server push is disabled by peer"; 229 case DATA_EXIST: 230 return "DATA frame already exists"; 231 case NOMEM: 232 return "Out of memory"; 233 case CALLBACK_FAILURE: 234 return "The user callback function failed"; 235 case BAD_PREFACE: 236 return "Received bad connection preface"; 237 default: return error_code.to!string; 238 } 239 } 240 241 /// The flag for a header field. 242 enum HeaderFlag : ubyte 243 { 244 /// No flag set. 245 NONE = 0, 246 247 /** 248 * Indicates that this header field must not be indexed ("Literal 249 * Header Field never Indexed" representation must be used in HPACK 250 * encoding). Other implementation calls this bit as "sensitive". 251 */ 252 NO_INDEX = 0x01 253 } 254 255 /// The header field, which mainly used to represent HTTP headers. 256 struct HeaderField 257 { 258 immutable(char)[] name; 259 immutable(char)[] value; 260 261 HeaderFlag flag = HeaderFlag.NONE; 262 263 bool opEquals()(auto ref HeaderField other) const { 264 return name == other.name && value == other.value; 265 } 266 267 void free() { 268 if (name.length > 0) 269 Mem.free(name); 270 if (value.length > 0) 271 Mem.free(value); 272 name = null; 273 value = null; 274 } 275 276 /** 277 * Returns true if HTTP header field name |name| of length |len| is 278 * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 279 * 280 * Because this is a header field name in HTTP2, the upper cased alphabet 281 * is treated as error. 282 */ 283 bool validateName() { 284 immutable(char)* pos = name.ptr; 285 size_t len = name.length; 286 if (len == 0) 287 return false; 288 289 if (*pos == ':') { 290 if (name.length == 1) 291 return false; 292 ++pos; 293 --len; 294 } 295 296 for (const immutable(char)* last = pos + len; pos != last; ++pos) { 297 if (!VALID_HD_NAME_CHARS[*pos]) { 298 return false; 299 } 300 } 301 return true; 302 } 303 304 /* 305 * Returns true if HTTP header field value |value| of length |len| 306 * is valid according to http://tools.ietf.org/html/rfc7230#section-3.2 307 */ 308 bool validateValue() { 309 immutable(char)* pos = value.ptr; 310 size_t len = value.length; 311 312 for (const immutable(char)* last = pos + len; pos != last; ++pos) { 313 if (!VALID_HD_VALUE_CHARS[*pos]) { 314 return false; 315 } 316 } 317 return true; 318 } 319 320 import libhttp2.stream : Stream; 321 /// Validate a request header 322 bool validateRequestHeader(Stream stream, bool trailer) { 323 int token; 324 325 if (name[0] == ':') 326 if (trailer || (stream.httpFlags & HTTPFlags.PSEUDO_HEADER_DISALLOWED)) 327 return false; 328 329 token = parseToken(name); 330 331 with(Token) switch (token) { 332 case _AUTHORITY: 333 if (!validatePseudoHeader(stream, HTTPFlags._AUTHORITY)) 334 return false; 335 336 break; 337 case _METHOD: 338 if (!validatePseudoHeader(stream, HTTPFlags._METHOD)) 339 return false; 340 341 switch (value.length) 342 { 343 case 4: 344 345 if (value == "HEAD") { 346 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.METH_HEAD); 347 } 348 break; 349 case 7: 350 switch (value[6]) 351 { 352 case 'T': 353 if (value == "CONNECT") { 354 if (stream.id % 2 == 0) { 355 /* we won't allow CONNECT for push */ 356 return false; 357 } 358 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.METH_CONNECT); 359 if (stream.httpFlags & (HTTPFlags._PATH | HTTPFlags._SCHEME)) 360 return false; 361 } 362 break; 363 364 case 'S': 365 if (value == "OPTIONS") { 366 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.METH_OPTIONS); 367 } 368 break; 369 default: 370 break; 371 } 372 break; 373 default: 374 break; 375 } 376 break; 377 case _PATH: 378 if (stream.httpFlags & HTTPFlags.METH_CONNECT) { 379 return false; 380 } 381 if (!validatePseudoHeader(stream, HTTPFlags._PATH)) { 382 return false; 383 } 384 if (value[0] == '/') { 385 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PATH_REGULAR); 386 } else if (value.length == 1 && value[0] == '*') { 387 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PATH_ASTERISK); 388 } 389 break; 390 case _SCHEME: 391 if (stream.httpFlags & HTTPFlags.METH_CONNECT) { 392 return false; 393 } 394 if (!validatePseudoHeader(stream, HTTPFlags._SCHEME)) { 395 return false; 396 } 397 if ((value.length == 4 && memieq("http".ptr, value.ptr, 4)) || 398 (value.length == 5 && memieq("https".ptr, value.ptr, 5))) { 399 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.SCHEME_HTTP); 400 } 401 break; 402 case HOST: 403 if (!validatePseudoHeader(stream, HTTPFlags.HOST)) { 404 return false; 405 } 406 break; 407 case CONTENT_LENGTH: { 408 if (stream.contentLength != -1) 409 return false; 410 import std.conv : parse; 411 stream.contentLength = parse!long(value); 412 if (stream.contentLength == -1) 413 return false; 414 break; 415 } 416 /* disallowed header fields */ 417 case CONNECTION: 418 case KEEP_ALIVE: 419 case PROXY_CONNECTION: 420 case TRANSFER_ENCODING: 421 case UPGRADE: 422 return false; 423 case TE: 424 import std.string : icmp; 425 if (icmp(value, "trailers") != 0) 426 return false; 427 break; 428 default: 429 if (name[0] == ':') 430 return false; 431 } 432 if (name[0] != ':') 433 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PSEUDO_HEADER_DISALLOWED); 434 435 return true; 436 } 437 438 bool validateResponseHeader(Stream stream, int trailer) 439 { 440 import std.conv : parse; 441 int token; 442 443 if (name[0] == ':') { 444 if (trailer || (stream.httpFlags & HTTPFlags.PSEUDO_HEADER_DISALLOWED)) { 445 return false; 446 } 447 } 448 449 token = parseToken(name); 450 451 with(Token) switch (token) { 452 case _STATUS: { 453 if (!validatePseudoHeader(stream, HTTPFlags._STATUS)) { 454 return false; 455 } 456 if (value.length != 3) { 457 return false; 458 } 459 stream.statusCode = cast(short)parse!uint(value); 460 if (stream.statusCode == -1) 461 return false; 462 break; 463 } 464 case CONTENT_LENGTH: { 465 if (stream.contentLength != -1) { 466 return false; 467 } 468 stream.contentLength = parse!long(value); 469 if (stream.contentLength == -1) { 470 return false; 471 } 472 break; 473 } 474 /* disallowed header fields */ 475 case CONNECTION: 476 case KEEP_ALIVE: 477 case PROXY_CONNECTION: 478 case TRANSFER_ENCODING: 479 case UPGRADE: 480 return false; 481 case TE: 482 import std.string : icmp; 483 if (icmp("trailers", value) != 0) { 484 return false; 485 } 486 break; 487 default: 488 if (name[0] == ':') { 489 return false; 490 } 491 } 492 493 if (name[0] != ':') { 494 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PSEUDO_HEADER_DISALLOWED); 495 } 496 497 return true; 498 } 499 private: 500 bool validatePseudoHeader(Stream stream, HTTPFlags flag) { 501 if (stream.httpFlags & flag) { 502 return false; 503 } 504 if (lws(value)) { 505 return false; 506 } 507 stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | flag); 508 return true; 509 } 510 511 bool lws(in string s) { 512 size_t i; 513 for (i = 0; i < s.length; ++i) 514 if (s.ptr[i] != ' ' && s.ptr[i] != '\t') 515 return false; 516 return true; 517 } 518 } 519 520 521 /// The frame types in HTTP/2 specification. 522 enum FrameType : ubyte 523 { 524 DATA = 0, 525 HEADERS = 0x01, 526 PRIORITY = 0x02, 527 RST_STREAM = 0x03, 528 SETTINGS = 0x04, 529 PUSH_PROMISE = 0x05, 530 PING = 0x06, 531 GOAWAY = 0x07, 532 WINDOW_UPDATE = 0x08, 533 CONTINUATION = 0x09 534 } 535 536 /// The flags for HTTP/2 frames. This enum defines all flags for all frames. 537 enum FrameFlags : ubyte 538 { 539 NONE = 0, 540 END_STREAM = 0x01, 541 END_HEADERS = 0x04, 542 ACK = 0x01, 543 PADDED = 0x08, 544 PRIORITY = 0x20 545 } 546 547 548 /// The status codes for the RST_STREAM and GOAWAY frames. 549 enum FrameError : uint 550 { 551 NO_ERROR = 0x00, 552 PROTOCOL_ERROR = 0x01, 553 INTERNAL_ERROR = 0x02, 554 FLOW_CONTROL_ERROR = 0x03, 555 TIMEOUT = 0x04, 556 STREAM_CLOSED = 0x05, 557 FRAME_SIZE_ERROR = 0x06, 558 REFUSED_STREAM = 0x07, 559 CANCEL = 0x08, 560 COMPRESSION_ERROR = 0x09, 561 CONNECT_ERROR = 0x0a, 562 ENHANCE_YOUR_CALM = 0x0b, 563 INADEQUATE_SECURITY = 0x0c, 564 HTTP_1_1_REQUIRED = 0x0d 565 } 566 567 /** 568 * Callback function invoked when the library wants to read data from 569 * the source. The read data is sent in the stream |stream_id|. 570 * The implementation of this function must read at most |length| 571 * bytes of data from |source| (or possibly other places) and store 572 * them in |buf| and return number of data stored in |buf|. If EOF is 573 * reached, set $(D DataFlags.EOF) 574 * 575 * Sometime it is desirable to avoid copying data into |buf| and let 576 * application to send data directly. To achieve this, set 577 * `DataFlags.NO_COPY` to |data_flags| (and possibly 578 * other flags, just like when we do copy), and return the number of 579 * bytes to send without copying data into |buf|. The library, seeing 580 * `DataFlags.NO_COPY`, will invoke `Connector.writeData`. 581 * The application must send complete DATA frame in that callback. 582 * 583 * If the application wants to postpone DATA frames (e.g., 584 * asynchronous I/O, or reading data blocks for long time), it is 585 * achieved by setting $(D pause) without reading 586 * any data in this invocation. The library removes DATA frame from 587 * the outgoing queue temporarily. To move back deferred DATA frame 588 * to outgoing queue, call `resumeData()`. 589 * 590 * In case of error, there are 2 choices. 591 * Setting $(D rst_stream=true) will close the stream by issuing RST_STREAM with 592 * $(D FrameError.INTERNAL_ERROR). If a different error code is desirable, 593 * use `http2_submit_rst_stream()` with a desired error code and then 594 * set $(D rst_stream) to true. 595 * 596 * Returning false will signal $(D Error.CALLBACK_FAILURE), aborting the entire session. 597 */ 598 alias DataProvider = int delegate(ubyte[] buf, ref DataFlags data_flags); 599 600 /// The flags used to set in |data_flags| output parameter in DataSource.read_callback 601 enum DataFlags : ubyte 602 { 603 /// No flag set. 604 NONE = 0, 605 606 /// Indicates EOF was sensed. 607 EOF = 0x01, 608 /// Indicates that END_STREAM flag must not be set 609 /// even if EOF is set. Usually this flag is used to send 610 /// trailer header fields with `submitRequest()` or `submitResponse()` 611 /// Note: unused at the moment 612 NO_END_STREAM = 0x02, 613 /// Indicates that application will send complete DATA frame 614 /// in `Connector.writeData` 615 NO_COPY = 0x04 616 } 617 618 /** 619 * The category of HEADERS, which indicates the role of the frame. In 620 * HTTP/2 spec, request, response, push response and other arbitrary 621 * headers (e.g., trailers) are all called just HEADERS. To give the 622 * application the role of incoming HEADERS frame, we define several 623 * categories. 624 */ 625 enum HeadersCategory : ubyte 626 { 627 /// The HEADERS frame is opening new stream, which is analogous to SYN_STREAM in SPDY. 628 REQUEST = 0, 629 630 /// The HEADERS frame is the first response headers, which is analogous to SYN_REPLY in SPDY. 631 RESPONSE = 1, 632 633 /// The HEADERS frame is the first headers sent against reserved stream. 634 PUSH_RESPONSE = 2, 635 636 /** 637 * The HEADERS frame which does not apply for the above categories, 638 * which is analogous to HEADERS in SPDY. If non-final response 639 * (e.g., status 1xx) is used, final response HEADERS frame will be 640 * categorized here. 641 */ 642 HEADERS = 3 643 } 644 645 646 /// nghttp2_stream_state 647 /** 648 * If local peer is stream initiator: 649 * OPENING : upon sending request HEADERS 650 * OPENED : upon receiving response HEADERS 651 * CLOSING : upon queuing RST_STREAM 652 * 653 * If remote peer is stream initiator: 654 * OPENING : upon receiving request HEADERS 655 * OPENED : upon sending response HEADERS 656 * CLOSING : upon queuing RST_STREAM 657 */ 658 enum StreamState : ubyte { 659 /// Initial state 660 INITIAL, 661 662 /// For stream initiator: request HEADERS has been sent, but response HEADERS has not been received yet. 663 /// For receiver: request HEADERS has been received, but it does not send response HEADERS yet. 664 OPENING, 665 666 /// For stream initiator: response HEADERS is received. For receiver: response HEADERS is sent. 667 OPENED, 668 669 /// RST_STREAM is received, but somehow we need to keep stream in memory. 670 CLOSING, 671 672 /// PUSH_PROMISE is received or sent 673 RESERVED, 674 675 /// Stream is created in this state if it is used as anchor in dependency tree. 676 IDLE 677 } 678 679 enum ShutdownFlag { 680 NONE = 0, 681 682 /// Indicates further receptions will be disallowed. 683 RD = 0x01, 684 685 /// Indicates further transmissions will be disallowed. 686 WR = 0x02, 687 688 /// Indicates both further receptions and transmissions will be disallowed. 689 RDWR = RD | WR 690 } 691 692 enum StreamFlags : ubyte { 693 NONE = 0, 694 695 /// Indicates that this stream is pushed stream and not opened yet. 696 PUSH = 0x01, 697 698 /// Indicates that this stream was closed 699 CLOSED = 0x02, 700 701 /// Indicates the item is deferred due to flow control. 702 DEFERRED_FLOW_CONTROL = 0x04, 703 704 /// Indicates the item is deferred by user callback 705 DEFERRED_USER = 0x08, 706 707 /// bitwise OR of DEFERRED_FLOW_CONTROL and DEFERRED_USER. */ 708 DEFERRED_ALL = 0x0c 709 } 710 711 /// HTTP related flags to enforce HTTP semantics 712 enum HTTPFlags { 713 NONE = 0, 714 715 /// header field seen so far 716 _AUTHORITY = 1, 717 _PATH = 1 << 1, 718 _METHOD = 1 << 2, 719 _SCHEME = 1 << 3, 720 721 /// host is not pseudo header, but we require either host or :authority 722 HOST = 1 << 4, 723 _STATUS = 1 << 5, 724 725 /// required header fields for HTTP request except for CONNECT method. 726 REQ_HEADERS = _METHOD | _PATH | _SCHEME, 727 PSEUDO_HEADER_DISALLOWED = 1 << 6, 728 729 /* HTTP method flags */ 730 METH_CONNECT = 1 << 7, 731 METH_HEAD = 1 << 8, 732 METH_OPTIONS = 1 << 9, 733 METH_ALL = METH_CONNECT | METH_HEAD | METH_OPTIONS, 734 /* :path category */ 735 /* path starts with "/" */ 736 PATH_REGULAR = 1 << 10, 737 /* path "*" */ 738 PATH_ASTERISK = 1 << 11, 739 /* scheme */ 740 /* "http" or "https" scheme */ 741 SCHEME_HTTP = 1 << 12, 742 743 /* set if final response is expected */ 744 EXPECT_FINAL_RESPONSE = 1 << 13, 745 } 746 747 enum StreamDPRI { 748 NONE = 0, 749 NO_ITEM = 0x01, 750 TOP = 0x02, 751 REST = 0x04 752 } 753 754 /// HTTP Tokens 755 enum Token : int { 756 ERROR = -1, 757 _AUTHORITY = 0, 758 _METHOD, 759 _PATH, 760 _SCHEME, 761 _STATUS, 762 CONNECTION, 763 CONTENT_LENGTH, 764 HOST, 765 KEEP_ALIVE, 766 PROXY_CONNECTION, 767 TE, 768 TRANSFER_ENCODING, 769 UPGRADE, 770 MAXIDX, 771 } 772 773 struct Setting { 774 alias SettingCode = ushort; 775 /// Notes: If we add SETTINGS, update the capacity of HTTP2_INBOUND_NUM_IV as well 776 enum : SettingCode { 777 HEADER_TABLE_SIZE = 0x01, 778 ENABLE_PUSH = 0x02, 779 MAX_CONCURRENT_STREAMS = 0x03, 780 INITIAL_WINDOW_SIZE = 0x04, 781 MAX_FRAME_SIZE = 0x05, 782 MAX_HEADER_LIST_SIZE = 0x06 783 } 784 785 /// The SETTINGS ID. 786 SettingCode id; 787 uint value; 788 789 void unpack(in ubyte[] payload) { 790 id = read!ushort(payload); 791 value = read!uint(payload[2 .. $]); 792 } 793 } 794 795 alias SettingsID = Setting.SettingCode; 796