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