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