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 /* Ignore because this can cause issues with reverse proxies
418  * case CONNECTION:
419 			case KEEP_ALIVE:
420 			case PROXY_CONNECTION:
421 			case TRANSFER_ENCODING:
422 			case UPGRADE:
423 				return false;*/
424 			case TE:
425 				import std.string : icmp;
426 				if (icmp(value, "trailers") != 0)
427 					return false;
428 				break;
429 			default:
430 				if (name[0] == ':')
431 					return false;
432 		}
433 		if (name[0] != ':')
434 			stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PSEUDO_HEADER_DISALLOWED);		
435 		
436 		return true;
437 	}
438 
439 	bool validateResponseHeader(Stream stream, int trailer) 
440 	{
441 		import std.conv : parse;
442 		int token;
443 		
444 		if (name[0] == ':') {
445 			if (trailer || (stream.httpFlags & HTTPFlags.PSEUDO_HEADER_DISALLOWED)) {
446 				return false;
447 			}
448 		}
449 		
450 		token = parseToken(name);
451 		
452 		with(Token) switch (token) {
453 			case _STATUS: {
454 				if (!validatePseudoHeader(stream, HTTPFlags._STATUS)) {
455 					return false;
456 				}
457 				if (value.length != 3) {
458 					return false;
459 				}
460 				stream.statusCode = cast(short)parse!uint(value);
461 				if (stream.statusCode == -1)
462 					return false;
463 				break;
464 			}
465 			case CONTENT_LENGTH: {
466 				if (stream.contentLength != -1) {
467 					return false;
468 				}
469 				stream.contentLength = parse!long(value);
470 				if (stream.contentLength == -1) {
471 					return false;
472 				}
473 				break;
474 			}
475 				/* disallowed header fields */
476 			case CONNECTION:
477 			case KEEP_ALIVE:
478 			case PROXY_CONNECTION:
479 			case TRANSFER_ENCODING:
480 			case UPGRADE:
481 				return false;
482 			case TE:
483 				import std.string : icmp;
484 				if (icmp("trailers", value) != 0) {
485 					return false;
486 				}
487 				break;
488 			default:
489 				if (name[0] == ':') {
490 					return false;
491 				}
492 		}
493 		
494 		if (name[0] != ':') {
495 			stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | HTTPFlags.PSEUDO_HEADER_DISALLOWED);
496 		}
497 		
498 		return true;
499 	}
500 private:	
501 	bool validatePseudoHeader(Stream stream, HTTPFlags flag) {
502 		if (stream.httpFlags & flag) {
503 			return false;
504 		}
505 		if (lws(value)) {
506 			return false;
507 		}
508 		stream.httpFlags = cast(HTTPFlags)(stream.httpFlags | flag);
509 		return true;
510 	}
511 
512 	bool lws(in string s) {
513 		size_t i;
514 		for (i = 0; i < s.length; ++i)
515 			if (s.ptr[i] != ' ' && s.ptr[i] != '\t')
516 				return false;
517 		return true;
518 	}
519 }
520 
521 
522 /// The frame types in HTTP/2 specification.
523 enum FrameType : ubyte
524 {
525 	DATA = 0,
526 	HEADERS = 0x01,
527     PRIORITY = 0x02,
528 	RST_STREAM = 0x03,
529 	SETTINGS = 0x04,
530 	PUSH_PROMISE = 0x05,
531     PING = 0x06,
532 	GOAWAY = 0x07,
533 	WINDOW_UPDATE = 0x08,
534 	CONTINUATION = 0x09
535 }
536 
537 /// The flags for HTTP/2 frames.  This enum defines all flags for all frames.
538 enum FrameFlags : ubyte 
539 {
540 	NONE = 0,
541 	END_STREAM = 0x01,
542 	END_HEADERS = 0x04,
543 	ACK = 0x01,
544 	PADDED = 0x08,
545 	PRIORITY = 0x20
546 }
547 
548 
549 /// The status codes for the RST_STREAM and GOAWAY frames.
550 enum FrameError : uint
551 {
552 	NO_ERROR = 0x00,
553 	PROTOCOL_ERROR = 0x01,
554 	INTERNAL_ERROR = 0x02,
555 	FLOW_CONTROL_ERROR = 0x03,
556 	TIMEOUT = 0x04,
557 	STREAM_CLOSED = 0x05,
558 	FRAME_SIZE_ERROR = 0x06,
559 	REFUSED_STREAM = 0x07,
560 	CANCEL = 0x08,
561 	COMPRESSION_ERROR = 0x09,
562 	CONNECT_ERROR = 0x0a,
563 	ENHANCE_YOUR_CALM = 0x0b,
564 	INADEQUATE_SECURITY = 0x0c,
565 	HTTP_1_1_REQUIRED = 0x0d
566 }
567 
568 /**
569  * Callback function invoked when the library wants to read data from
570  * the source.  The read data is sent in the stream |stream_id|.
571  * The implementation of this function must read at most |length|
572  * bytes of data from |source| (or possibly other places) and store
573  * them in |buf| and return number of data stored in |buf|.  If EOF is
574  * reached, set $(D DataFlags.EOF)
575  *
576  * Sometime it is desirable to avoid copying data into |buf| and let
577  * application to send data directly.  To achieve this, set
578  * `DataFlags.NO_COPY` to |data_flags| (and possibly
579  * other flags, just like when we do copy), and return the number of
580  * bytes to send without copying data into |buf|.  The library, seeing
581  * `DataFlags.NO_COPY`, will invoke `Connector.writeData`.  
582  * The application must send complete DATA frame in that callback.
583  *
584  * If the application wants to postpone DATA frames (e.g.,
585  * asynchronous I/O, or reading data blocks for long time), it is
586  * achieved by setting $(D pause) without reading
587  * any data in this invocation.  The library removes DATA frame from
588  * the outgoing queue temporarily.  To move back deferred DATA frame
589  * to outgoing queue, call `resumeData()`.  
590  * 
591  * In case of error, there are 2 choices.
592  * Setting $(D rst_stream=true) will close the stream by issuing RST_STREAM with 
593  * $(D FrameError.INTERNAL_ERROR).  If a different error code is desirable, 
594  * use `http2_submit_rst_stream()`  with a desired error code and then
595  * set $(D rst_stream) to true. 
596  * 
597  * Returning false will signal $(D Error.CALLBACK_FAILURE), aborting the entire session.
598  */
599 alias DataProvider = int delegate(ubyte[] buf, ref DataFlags data_flags);
600 
601 /// The flags used to set in |data_flags| output parameter in DataSource.read_callback
602 enum DataFlags : ubyte
603 {
604 	/// No flag set.
605 	NONE = 0,
606 
607 	/// Indicates EOF was sensed.
608 	EOF = 0x01,
609 	/// Indicates that END_STREAM flag must not be set 
610 	/// even if EOF is set. Usually this flag is used to send
611 	/// trailer header fields with `submitRequest()` or `submitResponse()`
612 	/// Note: unused at the moment
613 	NO_END_STREAM = 0x02,
614 	/// Indicates that application will send complete DATA frame
615 	/// in `Connector.writeData`
616 	NO_COPY = 0x04
617 }
618 
619 /**
620  * The category of HEADERS, which indicates the role of the frame.  In
621  * HTTP/2 spec, request, response, push response and other arbitrary
622  * headers (e.g., trailers) are all called just HEADERS.  To give the
623  * application the role of incoming HEADERS frame, we define several
624  * categories.
625  */
626 enum HeadersCategory : ubyte
627 {
628 	/// The HEADERS frame is opening new stream, which is analogous to SYN_STREAM in SPDY.
629 	REQUEST = 0,
630 
631 	/// The HEADERS frame is the first response headers, which is analogous to SYN_REPLY in SPDY.
632 	RESPONSE = 1,
633 
634 	/// The HEADERS frame is the first headers sent against reserved stream.
635 	PUSH_RESPONSE = 2,
636 
637 	/**
638     * The HEADERS frame which does not apply for the above categories,
639     * which is analogous to HEADERS in SPDY.  If non-final response
640     * (e.g., status 1xx) is used, final response HEADERS frame will be
641     * categorized here.
642     */
643 	HEADERS = 3
644 }
645 
646 
647 /// nghttp2_stream_state
648 /**
649  * If local peer is stream initiator:
650  * OPENING : upon sending request HEADERS
651  * OPENED : upon receiving response HEADERS
652  * CLOSING : upon queuing RST_STREAM
653  *
654  * If remote peer is stream initiator:
655  * OPENING : upon receiving request HEADERS
656  * OPENED : upon sending response HEADERS
657  * CLOSING : upon queuing RST_STREAM
658  */
659 enum StreamState : ubyte {
660 	/// Initial state
661 	INITIAL,
662 	
663 	/// For stream initiator: request HEADERS has been sent, but response HEADERS has not been received yet. 
664 	/// For receiver: request HEADERS has been received, but it does not send response HEADERS yet. 
665 	OPENING,
666 	
667 	/// For stream initiator: response HEADERS is received. For receiver: response HEADERS is sent.
668 	OPENED,
669 	
670 	/// RST_STREAM is received, but somehow we need to keep stream in memory.
671 	CLOSING,
672 	
673 	/// PUSH_PROMISE is received or sent
674 	RESERVED,
675 	
676 	/// Stream is created in this state if it is used as anchor in dependency tree.
677 	IDLE
678 }
679 
680 enum ShutdownFlag {
681 	NONE = 0,
682 	
683 	/// Indicates further receptions will be disallowed.
684 	RD = 0x01,
685 	
686 	/// Indicates further transmissions will be disallowed.
687 	WR = 0x02,
688 	
689 	/// Indicates both further receptions and transmissions will be disallowed.
690 	RDWR = RD | WR
691 }
692 
693 enum StreamFlags : ubyte {
694 	NONE = 0,
695 	
696 	/// Indicates that this stream is pushed stream and not opened yet.
697 	PUSH = 0x01,
698 	
699 	/// Indicates that this stream was closed
700 	CLOSED = 0x02,
701 	
702 	/// Indicates the item is deferred due to flow control.
703 	DEFERRED_FLOW_CONTROL = 0x04,
704 	
705 	/// Indicates the item is deferred by user callback
706 	DEFERRED_USER = 0x08,
707 	
708 	/// bitwise OR of DEFERRED_FLOW_CONTROL and DEFERRED_USER. */
709 	DEFERRED_ALL = 0x0c    
710 }
711 
712 /// HTTP related flags to enforce HTTP semantics
713 enum HTTPFlags {
714 	NONE = 0,
715 	
716 	/// header field seen so far 
717 	_AUTHORITY = 1,
718 	_PATH = 1 << 1,
719 	_METHOD = 1 << 2,
720 	_SCHEME = 1 << 3,
721 	
722 	/// host is not pseudo header, but we require either host or :authority 
723 	HOST = 1 << 4,
724 	_STATUS = 1 << 5,
725 	
726 	/// required header fields for HTTP request except for CONNECT method.
727 	REQ_HEADERS = _METHOD | _PATH | _SCHEME,
728 	PSEUDO_HEADER_DISALLOWED = 1 << 6,
729 	
730 	/* HTTP method flags */
731 	METH_CONNECT = 1 << 7,
732 	METH_HEAD = 1 << 8,
733 	METH_OPTIONS = 1 << 9,
734 	METH_ALL = METH_CONNECT | METH_HEAD | METH_OPTIONS,
735 	/* :path category */
736 	/* path starts with "/" */
737 	PATH_REGULAR = 1 << 10,
738 	/* path "*" */
739 	PATH_ASTERISK = 1 << 11,
740 	/* scheme */
741 	/* "http" or "https" scheme */
742 	SCHEME_HTTP = 1 << 12,
743 
744 	/* set if final response is expected */
745 	EXPECT_FINAL_RESPONSE = 1 << 13,
746 }
747 
748 enum StreamDPRI {
749 	NONE = 0,
750 	NO_ITEM = 0x01,
751 	TOP = 0x02,
752 	REST = 0x04
753 }
754 
755 /// HTTP Tokens
756 enum Token : int {
757 	ERROR = -1,
758 	_AUTHORITY = 0,
759 	_METHOD,
760 	_PATH,
761 	_SCHEME,
762 	_STATUS,
763 	CONNECTION,
764 	CONTENT_LENGTH,
765 	HOST,
766 	KEEP_ALIVE,
767 	PROXY_CONNECTION,
768 	TE,
769 	TRANSFER_ENCODING,
770 	UPGRADE,
771 	MAXIDX,
772 }
773 
774 struct Setting {
775 	alias SettingCode = ushort;
776 	/// Notes: If we add SETTINGS, update the capacity of HTTP2_INBOUND_NUM_IV as well
777 	enum : SettingCode {
778 		HEADER_TABLE_SIZE = 0x01,
779 		ENABLE_PUSH = 0x02,
780 		MAX_CONCURRENT_STREAMS = 0x03,
781 		INITIAL_WINDOW_SIZE = 0x04,
782 		MAX_FRAME_SIZE = 0x05,
783 		MAX_HEADER_LIST_SIZE = 0x06
784 	}
785 	
786 	/// The SETTINGS ID.
787 	SettingCode id;
788 	uint value;
789 
790 	void unpack(in ubyte[] payload) {
791 		id = read!ushort(payload);
792 		value = read!uint(payload[2 .. $]);
793 	}
794 }
795 
796 alias SettingsID = Setting.SettingCode;
797