1 /**
2  * Session Tests
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.session_tests;
13 
14 import libhttp2.constants;
15 static if (TEST_ALL):
16 
17 import libhttp2.session;
18 import libhttp2.connector;
19 import libhttp2.frame;
20 import libhttp2.types;
21 import libhttp2.buffers;
22 import libhttp2.deflater;
23 import libhttp2.inflater;
24 import libhttp2.stream;
25 import libhttp2.tests;
26 import libhttp2.huffman;
27 import libhttp2.helpers;
28 
29 import memutils.refcounted;
30 
31 import std.algorithm : min, max;
32 import std.functional : toDelegate;
33 
34 struct Accumulator {
35 	ubyte[] opSlice() { return buf[0 .. length]; }
36 
37 	ubyte[65535] buf;
38 	size_t length;
39 }
40 
41 struct ScriptedDataFeed {
42 	ubyte[8192] data;
43 	ubyte* datamark;
44 	ubyte* datalimit;
45 	size_t[8192] feedseq;
46 	size_t seqidx;
47 
48 	this(Buffers bufs) 
49 	{
50 		Buffers.Chain ci;
51 		Buffer* buf;
52 		ubyte* ptr;
53 		size_t len;
54 
55 		ptr = data.ptr;
56 		len = 0;
57 		
58 		for (ci = bufs.head; ci; ci = ci.next) {
59 			buf = &ci.buf;
60 			memcpy(ptr, buf.pos, buf.length);
61 			ptr += buf.length;
62 			len += buf.length;
63 		}
64 		
65 		datamark = data.ptr;
66 		datalimit = data.ptr + len;
67 		feedseq[0] = len;
68 	}
69 }
70 
71 struct MyUserData {
72 	this(Session* sess)
73 	{
74 		cb_handlers = MyCallbacks(sess, &this);
75 		datasrc = MyDataSource(&this);
76 	}
77 	void opAssign(ref MyUserData other) {
78 		cb_handlers = other.cb_handlers;
79 		cb_handlers.user_data = &this;
80 		datasrc.user_data = &this;
81 	}
82 
83 	MyCallbacks cb_handlers;
84 	MyDataSource datasrc;
85 	Accumulator* acc;
86 	ScriptedDataFeed *df;
87 	int frame_recv_cb_called, invalid_frame_recv_cb_called;
88 	FrameType recv_frame_type;
89 	int frame_send_cb_called;
90 	FrameType sent_frame_type;
91 	int frame_not_send_cb_called;
92 	ubyte not_sent_frame_type;
93 	ErrorCode not_sent_error;
94 	int stream_close_cb_called;
95 	FrameError stream_close_error_code;
96 	int data_source_length;
97 	int stream_id;
98 	size_t block_count;
99 	int data_chunk_recv_cb_called;
100 	const(Frame)* frame;
101 	size_t fixed_sendlen;
102 	int header_cb_called;
103 	int begin_headers_cb_called;
104 	HeaderField hf;
105 	size_t data_chunk_len;
106 	size_t padlen;
107 	int begin_frame_cb_called;
108 }
109 
110 const HeaderField[] reqhf = [HeaderField(":method", "GET"), HeaderField(":path", "/"), HeaderField(":scheme", "https"), HeaderField(":authority", "localhost")];
111 
112 const HeaderField[] reshf = [HeaderField(":status", "200")];
113 
114 alias Callbacks = RefCounted!CallbackConnector;
115 
116 struct MyCallbacks {
117 	Session* session;
118 	MyUserData* user_data;
119 
120 	static int writeNull(in ubyte[] data) 
121 	{
122 		return cast(int)data.length;
123 	}
124 	
125 	static int writeFailure(in ubyte[] data) 
126 	{
127 		return ErrorCode.CALLBACK_FAILURE;
128 	}
129 	
130 	int writeFixedBytes(in ubyte[] data) 
131 	{
132 		size_t fixed_sendlen = user_data.fixed_sendlen;
133 		return cast(int)(fixed_sendlen < data.length ? fixed_sendlen : data.length);
134 	}
135 	
136 	int writeToAccumulator(in ubyte[] data) 
137 	{
138 		Accumulator *acc = user_data.acc;
139 		assert(acc.length + data.length < acc.buf.length);
140 		acc.buf[acc.length .. acc.length + data.length] = data[0 .. $];
141 		acc.length += data.length;
142 		return cast(int)data.length;
143 	}
144 
145 	int writeWouldBlock(in ubyte[] data) 
146 	{
147 		int r;
148 		if (user_data.block_count == 0) {
149 			r = ErrorCode.WOULDBLOCK;
150 		} else {
151 			--user_data.block_count;
152 			r = cast(int)data.length;
153 		}
154 		return r;
155 	}
156 
157 	ErrorCode writeData(in Frame frame, ubyte[] framehd, uint length)
158 	{
159 		Accumulator *acc = user_data.acc;
160 
161 		FrameHeader hd;
162 		hd.unpack(framehd);
163 		acc.buf[acc.length .. acc.length + framehd.length] = framehd[0 .. $];
164 
165 		hd.unpack(acc.buf[acc.length .. acc.length + framehd.length]);
166 
167 		acc.length += framehd.length; // FRAME_HDLEN
168 
169 		if (frame.data.padlen)
170 			acc.buf[acc.length++] = cast(ubyte)(frame.data.padlen - 1);
171 		acc.length += length;
172 
173 		if (frame.data.padlen)
174 			acc.length += frame.data.padlen - 1;
175 
176 		return ErrorCode.OK;
177 
178 	}
179 		
180 	int readScripted(ubyte[] data) 
181 	{
182 		ScriptedDataFeed* df = user_data.df;
183 		size_t wlen = df.feedseq[df.seqidx] > data.length ? data.length : df.feedseq[df.seqidx];
184 		data[0 .. wlen] = df.datamark[0 .. wlen];
185 		df.datamark += wlen;
186 		df.feedseq[df.seqidx] -= wlen;
187 		if (df.feedseq[df.seqidx] == 0) {
188 			++df.seqidx;
189 		}
190 		return cast(int)wlen;
191 	}
192 
193 	static int readEOF(ubyte[] data) 
194 	{
195 		return ErrorCode.EOF;
196 	}
197 
198 	bool onFrameHeader(in FrameHeader hd) 
199 	{
200 		++user_data.begin_frame_cb_called;
201 		return true;
202 	}
203 
204 	bool onFrame(in Frame frame) 
205 	{
206 		++user_data.frame_recv_cb_called;
207 		user_data.recv_frame_type = frame.hd.type;
208 		return true;
209 	}
210 
211 	bool onInvalidFrame(in Frame frame, FrameError error_code) 
212 	{
213 		++user_data.invalid_frame_recv_cb_called;
214 		return true;
215 	}
216 
217 	bool onFrameSent(in Frame frame) 
218 	{
219 		++user_data.frame_send_cb_called;
220 		user_data.sent_frame_type = frame.hd.type;
221 		return true;
222 	}
223 
224 	bool onFrameSentTwice(in Frame frame)
225 	{
226 		static bool called;
227 		ErrorCode rv;
228 		DataProvider data_prd;
229 		
230 		if (!called) {
231 			called = true;
232 			
233 			data_prd = toDelegate(&MyDataSource.readTwice);
234 
235 			rv = submitData(*session, FrameFlags.END_STREAM, frame.hd.stream_id, data_prd);
236 			assert(0 == rv);
237 		}
238 		
239 		return true;
240 	}
241 
242 
243 	bool onFrameFailure(in Frame frame, ErrorCode lib_error)
244 	{
245 		++user_data.frame_not_send_cb_called;
246 		user_data.not_sent_frame_type = frame.hd.type;
247 		user_data.not_sent_error = lib_error;
248 		return true;
249 	}
250 
251 	bool onDataChunk(FrameFlags flags, int stream_id, in ubyte[] data, ref bool pause) 
252 	{
253 		++user_data.data_chunk_recv_cb_called;
254 		user_data.data_chunk_len = data.length;
255 		return true;
256 	}
257 
258 	bool onDataChunkPause(FrameFlags flags, int stream_id, in ubyte[] data, ref bool pause) 
259 	{
260 		++user_data.data_chunk_recv_cb_called;
261 		pause = true;
262 		return true;
263 	}
264 
265 	int selectPaddingLength(in Frame frame, int max_payloadlen) 
266 	{
267 		return cast(int) min(max_payloadlen, frame.hd.length + user_data.padlen);
268 	}
269 
270 	static int tooLargeMaxFrameSize(FrameType frame_type, int stream_id, int session_remote_window_size, int stream_remote_window_size, uint remote_max_frame_size)
271 	{
272 		return MAX_FRAME_SIZE_MAX + 1;
273 	}
274 
275 	static int smallestMaxFrameSize(FrameType frame_type, int stream_id, int session_remote_window_size, int stream_remote_window_size, uint remote_max_frame_size)
276 	{
277 		return 1;
278 	}
279 
280 	bool onHeaderField(in Frame frame, in HeaderField hf, ref bool pause, ref bool rst_stream) 
281 	{
282 		++user_data.header_cb_called;
283 		user_data.hf = hf;
284 		user_data.frame = &frame;
285 		return true;
286 	}
287 	
288 	bool onHeaderFieldPause(in Frame frame, in HeaderField hf, ref bool pause, ref bool rst_stream) {
289 		pause = true;
290 		return onHeaderField(frame, hf, pause, rst_stream);
291 	}
292 	
293 	bool onHeaderFieldRstStream(in Frame frame, in HeaderField hf, ref bool pause, ref bool rst_stream) {
294 		rst_stream = true;
295 		return onHeaderField(frame, hf, pause, rst_stream);
296 	}
297 	
298 	bool onHeaders(in Frame frame) {
299 		++user_data.begin_headers_cb_called;
300 		return true;
301 	}
302 	
303 	bool onStreamExit(int stream_id, FrameError error_code)
304 	{
305 		++user_data.stream_close_cb_called;
306 		user_data.stream_close_error_code = error_code;
307 		
308 		return true;
309 	}
310 		
311 }
312 
313 struct MyDataSource
314 {
315 	MyUserData* user_data;
316 
317 	int readFixedLength(ubyte[] buf, ref DataFlags data_flags) 
318 	{
319 		size_t wlen;
320 		if (buf.length < user_data.data_source_length) {
321 			wlen = buf.length;
322 		} else {
323 			wlen = user_data.data_source_length;
324 		}
325 		user_data.data_source_length -= wlen;
326 		assert(user_data.data_source_length >= 0);
327 		if (user_data.data_source_length == 0) {
328 			data_flags |= DataFlags.EOF;
329 		}
330 		return cast(int)wlen;
331 	}
332 
333 	int readNoCopy(ubyte[] buf, ref DataFlags data_flags)
334 	{
335 		size_t wlen;
336 		if (buf.length < user_data.data_source_length)
337 			wlen = buf.length;
338 		else
339 			wlen = user_data.data_source_length;
340 
341 		user_data.data_source_length -= wlen;
342 		data_flags |= DataFlags.NO_COPY;
343 		if (user_data.data_source_length == 0)
344 			data_flags |= DataFlags.EOF;
345 		return cast(int)wlen;
346 	}
347 
348 	static int readRstStream(ubyte[] buf, ref DataFlags data_flags)
349 	{
350 		return ErrorCode.TEMPORAL_CALLBACK_FAILURE;
351 	}
352 
353 	static int readFailure(ubyte[] buf, ref DataFlags data_flags)
354 	{
355 		return ErrorCode.CALLBACK_FAILURE;
356 	}
357 
358 	static int readDeferred(ubyte[] buf, ref DataFlags data_flags)
359 	{
360 		return ErrorCode.DEFERRED;
361 	}
362 
363 	static int readTwice(ubyte[] buf, ref DataFlags data_flags) 
364 	{
365 		data_flags |= DataFlags.EOF;
366 		return min(buf.length, 16);
367 	}
368 
369 }
370 
371 private immutable PrioritySpec pri_spec_default;
372 
373 void test_session_read() {
374 	Session session;
375 	Callbacks callbacks;
376 	ScriptedDataFeed df;
377 	MyUserData user_data = MyUserData(&session);
378 	Buffers bufs = framePackBuffers();
379 	size_t framelen;
380 	Frame frame;
381 	size_t i;
382 	OutboundItem item;
383 	HeaderField[] hfa;
384 	Deflater deflater;
385 	int rv;
386 	
387 	
388 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
389 	callbacks.read_cb = &user_data.cb_handlers.readScripted;
390 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
391 	callbacks.on_frame_header_cb = &user_data.cb_handlers.onFrameHeader;
392 	
393 	user_data.df = &df;
394 	
395 	session = new Session(SERVER, *callbacks);
396 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
397 		
398 	hfa = reqhf.copy();
399 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
400 	rv = frame.headers.pack(bufs, deflater);
401 
402 	assert(0 == rv);
403 	
404 	df = ScriptedDataFeed(bufs);
405 	
406 	framelen = bufs.length;
407 	
408 	/* Send 1 byte per each read */
409 	for (i = 0; i < framelen; ++i) {
410 		df.feedseq[i] = 1;
411 	}
412 	
413 	frame.headers.free();
414 	
415 	user_data.frame_recv_cb_called = 0;
416 	user_data.begin_frame_cb_called = 0;
417 	while (cast(size_t)df.seqidx < framelen) {
418 		assert(0 == session.recv());
419 	}
420 	assert(1 == user_data.frame_recv_cb_called);
421 	assert(1 == user_data.begin_frame_cb_called);
422 	
423 	bufs.reset();
424 	
425 	/* Receive PRIORITY */
426 	frame.priority = Priority(5, pri_spec_default);
427 	
428 	frame.priority.pack(bufs);
429 	
430 	assert(0 == rv);
431 	
432 	frame.priority.free();
433 	
434 	df = ScriptedDataFeed(bufs);
435 	
436 	user_data.frame_recv_cb_called = 0;
437 	user_data.begin_frame_cb_called = 0;
438 	
439 	assert(0 == session.recv());
440 	assert(1 == user_data.frame_recv_cb_called);
441 	assert(1 == user_data.begin_frame_cb_called);
442 	
443 	bufs.reset();
444 	
445 	deflater.free();
446 	session.free();
447 	
448 	/* Some tests for frame too large */
449 	session = new Session(SERVER, *callbacks);
450 	
451 	/* Receive PING with too large payload */
452 	frame.ping = Ping(FrameFlags.NONE, null);
453 	
454 	frame.ping.pack(bufs);
455 	
456 	assert(0 == rv);
457 	
458 	/* Add extra 16 bytes */
459 	bufs.seekLastPresent();
460 	assert(bufs.cur.buf.length >= 16);
461 	
462 	bufs.cur.buf.last += 16;
463 	write!uint(bufs.cur.buf.pos, cast(uint)(((frame.hd.length + 16) << 8) + bufs.cur.buf.pos[3]));
464 	
465 	frame.ping.free();
466 	
467 	df = ScriptedDataFeed(bufs);
468 	user_data.frame_recv_cb_called = 0;
469 	user_data.begin_frame_cb_called = 0;
470 	
471 	assert(0 == session.recv());
472 	assert(0 == user_data.frame_recv_cb_called);
473 	assert(0 == user_data.begin_frame_cb_called);
474 	
475 	item = session.getNextOutboundItem();
476 	assert(FrameType.GOAWAY == item.frame.hd.type);
477 	assert(FrameError.FRAME_SIZE_ERROR == item.frame.goaway.error_code);
478 	assert(0 == session.send());
479 	
480 	bufs.free();
481 	session.free();
482 }
483 
484 void test_session_read_invalid_stream_id() {
485 	Session session;
486 	Callbacks callbacks;
487 	ScriptedDataFeed df;
488 	MyUserData user_data = MyUserData(&session);
489 	Buffers bufs = framePackBuffers();
490 	Frame frame;
491 	Deflater deflater;
492 	int rv;
493 
494 	HeaderField[] hfa;	
495 
496 	callbacks.read_cb = &user_data.cb_handlers.readScripted;
497 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
498 	
499 	user_data.df = &df;
500 	user_data.invalid_frame_recv_cb_called = 0;
501 	session = new Session(SERVER, *callbacks);
502 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
503 
504 	
505 	hfa = reqhf.copy();
506 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.HEADERS, pri_spec_default, hfa);
507 	rv = frame.headers.pack(bufs, deflater);
508 	
509 	assert(0 == rv);
510 	assert(bufs.length > 0);
511 	
512 	df = ScriptedDataFeed(bufs);
513 	frame.headers.free();
514 	
515 	assert(0 == session.recv());
516 	assert(1 == user_data.invalid_frame_recv_cb_called);
517 	
518 	bufs.free();
519 	deflater.free();
520 	session.free();
521 }
522 
523 void test_session_read_invalid_frame() {
524 	Session session;
525 	Callbacks callbacks;
526 	ScriptedDataFeed df;
527 	MyUserData user_data = MyUserData(&session);
528 	Buffers bufs = framePackBuffers();
529 	Frame frame;
530 	HeaderField[] hfa;
531 	Deflater deflater;
532 	int rv;	
533 
534 	callbacks.read_cb = &user_data.cb_handlers.readScripted;
535 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
536 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
537 	
538 	user_data.df = &df;
539 	user_data.frame_send_cb_called = 0;
540 	session = new Session(SERVER, *callbacks);
541 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
542 	
543 	hfa = reqhf.copy();
544 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
545 	rv = frame.headers.pack(bufs, deflater);
546 	
547 	assert(0 == rv);
548 	assert(bufs.length > 0);
549 	
550 	df = ScriptedDataFeed(bufs);
551 	
552 	assert(0 == session.recv());
553 	assert(0 == session.send());
554 	assert(0 == user_data.frame_send_cb_called);
555 	
556 	/* Receive exactly same bytes of HEADERS is treated as error, because it has
557    * pseudo headers and without END_STREAM flag set */
558 	df = ScriptedDataFeed(bufs);
559 	
560 	assert(0 == session.recv());
561 	assert(0 == session.send());
562 	assert(1 == user_data.frame_send_cb_called);
563 	assert(FrameType.RST_STREAM == user_data.sent_frame_type);
564 	
565 	bufs.free();
566 	frame.headers.free();
567 	
568 	deflater.free();
569 	session.free();
570 }
571 
572 void test_session_read_eof() {
573 	Session session;
574 	Callbacks callbacks;
575 	
576 	
577 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
578 	callbacks.read_cb = toDelegate(&MyCallbacks.readEOF);
579 	
580 	session = new Session(CLIENT, *callbacks);
581 	assert(ErrorCode.EOF == session.recv());
582 	
583 	session.free();
584 }
585 
586 void test_session_read_data() {
587 	Session session;
588 	Callbacks callbacks;
589 	MyUserData user_data = MyUserData(&session);
590 	ubyte[8092] data;
591 	int rv;
592 	OutboundItem item;
593 	Stream stream;
594 	FrameHeader hd;
595 	int i;
596 		
597 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
598 	callbacks.on_data_chunk_cb = &user_data.cb_handlers.onDataChunk;
599 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
600 	
601 	session = new Session(CLIENT, *callbacks);
602 	
603 	/* Create DATA frame with length 4KiB */
604 	
605 	hd.length = 4096;
606 	hd.type = FrameType.DATA;
607 	hd.flags = FrameFlags.NONE;
608 	hd.stream_id = 1;
609 	hd.pack(data[0 .. $]);
610 	
611 	/* stream 1 is not opened, so it must be responded with connection
612      error.  This is not mandated by the spec */
613 	user_data.data_chunk_recv_cb_called = 0;
614 	user_data.frame_recv_cb_called = 0;
615 	rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
616 	assert(FRAME_HDLEN + 4096 == rv);
617 	
618 	assert(0 == user_data.data_chunk_recv_cb_called);
619 	assert(0 == user_data.frame_recv_cb_called);
620 	item = session.getNextOutboundItem();
621 	assert(FrameType.GOAWAY == item.frame.hd.type);
622 	
623 	session.free();
624 	
625 	session = new Session(CLIENT, *callbacks);
626 	
627 	/* Create stream 1 with CLOSING state. DATA is ignored. */
628 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.CLOSING, null);
629 
630 	/* Set initial window size 16383 to check stream flow control,
631      isolating it from the conneciton flow control */
632 	stream.localWindowSize = 16383;
633 	
634 	user_data.data_chunk_recv_cb_called = 0;
635 	user_data.frame_recv_cb_called = 0;
636 	rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
637 	assert(FRAME_HDLEN + 4096 == rv);
638 	
639 	assert(0 == user_data.data_chunk_recv_cb_called);
640 	assert(0 == user_data.frame_recv_cb_called);
641 	item = session.getNextOutboundItem();
642 	assert(!item);
643 	
644 	/* This is normal case. DATA is acceptable. */
645 	stream.state = StreamState.OPENED;
646 	
647 	user_data.data_chunk_recv_cb_called = 0;
648 	user_data.frame_recv_cb_called = 0;
649 	rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
650 	assert(FRAME_HDLEN + 4096 == rv);
651 	
652 	assert(1 == user_data.data_chunk_recv_cb_called);
653 	assert(1 == user_data.frame_recv_cb_called);
654 	
655 	//assert(!session.getNextOutboundItem());
656 	
657 	user_data.data_chunk_recv_cb_called = 0;
658 	user_data.frame_recv_cb_called = 0;
659 	rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
660 	assert(FRAME_HDLEN + 4096 == rv);
661 	
662 	/* Now we got data more than initial-window-size / 2, WINDOW_UPDATE
663      must be queued */
664 	assert(1 == user_data.data_chunk_recv_cb_called);
665 	assert(1 == user_data.frame_recv_cb_called);
666 	item = session.getNextOutboundItem();
667 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
668 	assert(1 == item.frame.window_update.hd.stream_id);
669 	assert(0 == session.send());
670 	
671 	/* Set initial window size to 1MiB, so that we can check connection
672      flow control individually */
673 	stream.localWindowSize = 1 << 20;
674 	/* Connection flow control takes into account DATA which is received
675      in the error condition. We have received 4096 * 4 bytes of
676      DATA. Additional 4 DATA frames, connection flow control will kick
677      in. */
678 	for (i = 0; i < 5; ++i) {
679 		rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
680 		assert(FRAME_HDLEN + 4096 == rv);
681 	}
682 	item = session.getNextOutboundItem();
683 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
684 	assert(0 == item.frame.window_update.hd.stream_id);
685 	assert(0 == session.send());
686 	
687 	/* Reception of DATA with stream ID = 0 causes connection error */
688 	hd.length = 4096;
689 	hd.type = FrameType.DATA;
690 	hd.flags = FrameFlags.NONE;
691 	hd.stream_id = 0;
692 	hd.pack(data[0 .. $]);
693 	
694 	user_data.data_chunk_recv_cb_called = 0;
695 	user_data.frame_recv_cb_called = 0;
696 	rv = session.memRecv(data[0 .. FRAME_HDLEN + 4096]);
697 	assert(FRAME_HDLEN + 4096 == rv);
698 	
699 	assert(0 == user_data.data_chunk_recv_cb_called);
700 	assert(0 == user_data.frame_recv_cb_called);
701 	item = session.getNextOutboundItem();
702 	assert(FrameType.GOAWAY == item.frame.hd.type);
703 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
704 	
705 	session.free();
706 }
707 
708 void test_session_read_continuation() {
709 	Session session;
710 	Callbacks callbacks;
711 	HeaderField[] hfa;
712 	Frame frame;
713 	Buffers bufs = framePackBuffers();
714 	Buffer* buf;
715 	int rv;
716 	MyUserData user_data = MyUserData(&session);
717 	Deflater deflater;
718 	ubyte[1024] data;
719 	size_t datalen;
720 	FrameHeader cont_hd;
721 	PrioritySpec pri_spec;
722 	
723 	callbacks.on_header_field_cb = &user_data.cb_handlers.onHeaderField;
724 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
725 	callbacks.on_frame_header_cb = &user_data.cb_handlers.onFrameHeader;
726 	
727 	session = new Session(SERVER, *callbacks);
728 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
729 	
730 	/* Make 1 HEADERS and insert CONTINUATION header */	
731 	hfa = reqhf.copy();
732 	frame.headers = Headers(FrameFlags.NONE, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
733 	rv = frame.headers.pack(bufs, deflater);
734 	
735 	assert(0 == rv);
736 	assert(bufs.length > 0);
737 	
738 	/* make sure that all data is in the first buf */
739 	buf = &bufs.head.buf;
740 	assert(bufs.length == buf.length);
741 	
742 	frame.headers.free();
743 	
744 	/* HEADERS's payload is 1 byte */
745 	memcpy(data.ptr, buf.pos, FRAME_HDLEN + 1);
746 	datalen = FRAME_HDLEN + 1;
747 	buf.pos += FRAME_HDLEN + 1;
748 	
749 	write!uint(data.ptr, (1 << 8) + data[3]);
750 	
751 	/* First CONTINUATION, 2 bytes */
752 	cont_hd = FrameHeader(2, FrameType.CONTINUATION, FrameFlags.NONE, 1);
753 	
754 	cont_hd.pack(data[datalen .. $]);
755 	datalen += FRAME_HDLEN;
756 	
757 	data[datalen .. cont_hd.length + datalen] = buf.pos[0 .. cont_hd.length];
758 	datalen += cont_hd.length;
759 	buf.pos += cont_hd.length;
760 	
761 	/* Second CONTINUATION, rest of the bytes */
762 	cont_hd = FrameHeader(buf.length, FrameType.CONTINUATION, FrameFlags.END_HEADERS, 1);
763 	
764 	cont_hd.pack(data[datalen .. $]);
765 	datalen += FRAME_HDLEN;
766 	
767 	data[datalen .. datalen + cont_hd.length] = buf.pos[0 .. cont_hd.length];
768 	datalen += cont_hd.length;
769 	buf.pos += cont_hd.length;
770 	
771 	assert(0 == buf.length);
772 	
773 	user_data.header_cb_called = 0;
774 	user_data.begin_frame_cb_called = 0;
775 	
776 	rv = session.memRecv(data[0 .. datalen]);
777 	assert(cast(size_t)datalen == rv);
778 	assert(4 == user_data.header_cb_called, "headers called times: " ~ user_data.header_cb_called.to!string);
779 	assert(3 == user_data.begin_frame_cb_called);
780 	
781 	deflater.free();
782 	session.free();
783 	
784 	/* Expecting CONTINUATION, but get the other frame */
785 	session = new Session(SERVER, *callbacks);
786 	
787 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
788 
789 	/* HEADERS without END_HEADERS flag */
790 	hfa = reqhf.copy();
791 	frame.headers = Headers(FrameFlags.NONE, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
792 	bufs.reset();
793 	rv = frame.headers.pack(bufs, deflater);
794 	
795 	assert(0 == rv);
796 	assert(bufs.length > 0);
797 	
798 	frame.headers.free();
799 	
800 	/* make sure that all data is in the first buf */
801 	buf = &bufs.head.buf;
802 	assert(bufs.length == buf.length);
803 	
804 	memcpy(data.ptr, buf.pos, buf.length);
805 	datalen = buf.length;
806 	
807 	/* Followed by PRIORITY */
808 	pri_spec = pri_spec_default;
809 	
810 	frame.priority = Priority(1, pri_spec);
811 	bufs.reset();
812 	
813 	frame.priority.pack(bufs);
814 
815 	assert(bufs.length > 0);
816 	
817 	memcpy(data.ptr + datalen, buf.pos, buf.length);
818 	datalen += buf.length;
819 	
820 	user_data.begin_headers_cb_called = 0;
821 	rv = session.memRecv(data[0 .. datalen]);
822 	assert(cast(size_t)datalen == rv);
823 	
824 	assert(1 == user_data.begin_headers_cb_called);
825 	assert(FrameType.GOAWAY == session.getNextOutboundItem().frame.hd.type);
826 	
827 	bufs.free();
828 	deflater.free();
829 	session.free();
830 }
831 
832 void test_session_read_headers_with_priority() {
833 	Session session;
834 	Callbacks callbacks;
835 	HeaderField[] hfa;
836 	Frame frame;
837 	Buffers bufs = framePackBuffers();
838 	Buffer* buf;
839 	int rv;
840 	MyUserData user_data = MyUserData(&session);
841 	Deflater deflater;
842 	OutboundItem item;
843 	PrioritySpec pri_spec;
844 	Stream stream;
845 
846 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
847 	
848 	session = new Session(SERVER, *callbacks);
849 	
850 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
851 
852 	openStream(session, 1);
853 	
854 	/* With FrameFlags.PRIORITY without exclusive flag set */
855 	
856 	hfa = reqhf.copy();
857 	pri_spec = PrioritySpec(1, 99, 0);
858 
859 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 3, HeadersCategory.HEADERS, pri_spec, hfa);
860 	
861 	rv = frame.headers.pack(bufs, deflater);
862 	
863 	assert(0 == rv);
864 	assert(bufs.length > 0);
865 	
866 	frame.headers.free();
867 	
868 	buf = &bufs.head.buf;
869 	assert(bufs.length == buf.length);
870 	
871 	user_data.frame_recv_cb_called = 0;
872 	
873 	rv = session.memRecv((*buf)[]);
874 	
875 	assert(buf.length == rv);
876 	assert(1 == user_data.frame_recv_cb_called);
877 	
878 	stream = session.getStream(3);
879 	
880 	assert(99 == stream.weight);
881 	assert(1 == stream.depPrev.id);
882 	
883 	bufs.reset();
884 	
885 	/* With FrameFlags.PRIORITY, but cut last 1 byte to make it invalid. */
886 	
887 	hfa = reqhf.copy();
888 	
889 	pri_spec = PrioritySpec(0, 99, 0);
890 	
891 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 5, HeadersCategory.HEADERS, pri_spec, hfa);
892 	
893 	rv = frame.headers.pack(bufs, deflater);
894 	
895 	assert(0 == rv);
896 	assert(bufs.length > FRAME_HDLEN + 5);
897 	
898 	frame.headers.free();
899 	
900 	buf = &bufs.head.buf;
901 	/* Make payload shorter than required length to store priority
902      group */
903 	write!uint(buf.pos, (4 << 8) + buf.pos[3]);
904 	
905 	user_data.frame_recv_cb_called = 0;
906 	
907 	rv = session.memRecv((*buf)[]);
908 	
909 	assert(buf.length == rv);
910 	assert(0 == user_data.frame_recv_cb_called);
911 	
912 	stream = session.getStream(5);
913 	
914 	assert(!stream);
915 	
916 	item = session.getNextOutboundItem();
917 	assert(item);
918 	assert(FrameType.GOAWAY == item.frame.hd.type);
919 	assert(FrameError.FRAME_SIZE_ERROR == item.frame.goaway.error_code);
920 	
921 	bufs.reset();
922 	
923 	deflater.free();
924 	session.free();
925 	
926 	/* Check dep_stream_id == stream_id */
927 	session = new Session(SERVER, *callbacks);
928 	
929 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
930 	
931 	
932 	hfa = reqhf.copy();
933 	
934 	pri_spec = PrioritySpec(1, 0, 0);
935 	
936 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 1, HeadersCategory.HEADERS, pri_spec, hfa);
937 	
938 	rv = frame.headers.pack(bufs, deflater);
939 	
940 	assert(0 == rv);
941 	assert(bufs.length > 0);
942 	
943 	frame.headers.free();
944 	
945 	buf = &bufs.head.buf;
946 	assert(bufs.length == buf.length);
947 	
948 	user_data.frame_recv_cb_called = 0;
949 	
950 	rv = session.memRecv((*buf)[]);
951 	
952 	assert(buf.length == rv);
953 	assert(0 == user_data.frame_recv_cb_called);
954 	
955 	stream = session.getStream(1);
956 	
957 	assert(!stream);
958 	
959 	item = session.getNextOutboundItem();
960 	assert(item);
961 	assert(FrameType.GOAWAY == item.frame.hd.type);
962 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
963 	
964 	bufs.reset();
965 	
966 	bufs.free();
967 	deflater.free();
968 	session.free();
969 }
970 
971 void test_session_read_premature_headers() {
972 	Session session;
973 	Callbacks callbacks;
974 	HeaderField[] hfa;
975 	Frame frame;
976 	Buffers bufs = framePackBuffers();
977 	Buffer* buf;
978 	int rv;
979 	MyUserData user_data = MyUserData(&session);
980 	Deflater deflater;
981 	OutboundItem item;
982 
983 	
984 	session = new Session(SERVER, *callbacks);
985 	
986 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
987 	
988 	
989 	hfa = reqhf.copy();
990 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
991 	rv = frame.headers.pack(bufs, deflater);
992 	
993 	assert(0 == rv);
994 	assert(bufs.length > 0);
995 	
996 	frame.headers.free();
997 	
998 	buf = &bufs.head.buf;
999 	assert(bufs.length == buf.length);
1000 	
1001 	/* Intentionally feed payload cutting last 1 byte off */
1002 	write!uint(buf.pos,cast(uint)(((frame.hd.length - 1) << 8) + buf.pos[3]));
1003 	rv = session.memRecv(buf.pos[0 .. buf.length - 1]);
1004 
1005 	assert(cast(size_t)(buf.length - 1) == rv);
1006 	
1007 	item = session.getNextOutboundItem();
1008 	assert(item);
1009 	assert(FrameType.RST_STREAM == item.frame.hd.type);
1010 	assert(FrameError.COMPRESSION_ERROR == item.frame.rst_stream.error_code);
1011 	
1012 	bufs.free();
1013 	deflater.free();
1014 	session.free();
1015 }
1016 
1017 void test_session_read_unknown_frame() {
1018 	Session session;
1019 	Callbacks callbacks;
1020 	MyUserData user_data = MyUserData(&session);
1021 	ubyte[16384] data;
1022 	size_t datalen;
1023 	FrameHeader hd;
1024 	int rv;
1025 	
1026 	hd = FrameHeader(16000, cast(FrameType)99, FrameFlags.NONE, 0);
1027 
1028 	hd.pack(data[0 .. $]);
1029 	datalen = FRAME_HDLEN + hd.length;
1030 	
1031 	
1032 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
1033 	
1034 	session = new Session(SERVER, *callbacks);
1035 	
1036 	user_data.frame_recv_cb_called = 0;
1037 	
1038 	/* Unknown frame must be ignored */
1039 	rv = session.memRecv(data[0 .. datalen]);
1040 	
1041 	assert(rv == datalen);
1042 	assert(0 == user_data.frame_recv_cb_called);
1043 	assert(!session.getNextOutboundItem());
1044 	
1045 	session.free();
1046 }
1047 
1048 void test_session_read_unexpected_continuation() {
1049 	Session session;
1050 	Callbacks callbacks;
1051 	MyUserData user_data = MyUserData(&session);
1052 	ubyte[16384] data;
1053 	size_t datalen;
1054 	FrameHeader hd;
1055 	int rv;
1056 	OutboundItem item;
1057 	
1058 	hd = FrameHeader(16000, FrameType.CONTINUATION,	FrameFlags.END_HEADERS, 1);
1059 	
1060 	hd.pack(data[0 .. $]);
1061 	datalen = FRAME_HDLEN + hd.length;
1062 	
1063 	
1064 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
1065 	
1066 	session = new Session(SERVER, *callbacks);
1067 	
1068 	openStream(session, 1);
1069 	
1070 	user_data.frame_recv_cb_called = 0;
1071 	
1072 	/* unexpected CONTINUATION must be treated as connection error */
1073 	rv = session.memRecv(data[0 .. datalen]);
1074 	
1075 	assert(rv == cast(size_t)datalen);
1076 	assert(0 == user_data.frame_recv_cb_called);
1077 	
1078 	item = session.getNextOutboundItem();
1079 	
1080 	assert(FrameType.GOAWAY == item.frame.hd.type);
1081 	
1082 	session.free();
1083 }
1084 
1085 void test_session_read_settings_header_table_size() {
1086 	Session session;
1087 	Callbacks callbacks;
1088 	Frame frame;
1089 	Buffers bufs = framePackBuffers();
1090 	Buffer* buf;
1091 	int rv;
1092 	MyUserData user_data = MyUserData(&session);
1093 	Setting[3] iva;
1094 	HeaderField hf = HeaderField(":authority", "example.org");
1095 
1096 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
1097 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
1098 	
1099 	session = new Session(CLIENT, *callbacks);
1100 	
1101 	iva[0].id = Setting.HEADER_TABLE_SIZE;
1102 	iva[0].value = 3000;
1103 	
1104 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
1105 	iva[1].value = 16384;
1106 
1107 	frame.settings = Settings(FrameFlags.NONE, iva[0 .. 2].copy());
1108 	
1109 	frame.settings.pack(bufs);
1110 
1111 	assert(bufs.length > 0);
1112 	
1113 	frame.settings.free();
1114 	
1115 	buf = &bufs.head.buf;
1116 	assert(bufs.length == buf.length);
1117 	
1118 	user_data.frame_recv_cb_called = 0;
1119 	
1120 	rv = session.memRecv((*buf)[]);
1121 	
1122 	assert(rv == buf.length);
1123 	assert(1 == user_data.frame_recv_cb_called);
1124 	
1125 	assert(3000 == session.remote_settings.header_table_size);
1126 	assert(16384 == session.remote_settings.initial_window_size);
1127 	
1128 	bufs.reset();
1129 	
1130 	/* 2 SettingsID.HEADER_TABLE_SIZE */
1131 	iva[0].id = Setting.HEADER_TABLE_SIZE;
1132 	iva[0].value = 3001;
1133 	
1134 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
1135 	iva[1].value = 16383;
1136 	
1137 	iva[2].id = Setting.HEADER_TABLE_SIZE;
1138 	iva[2].value = 3001;
1139 	
1140 	frame.settings = Settings(FrameFlags.NONE, iva.copy());
1141 	
1142 	frame.settings.pack(bufs);
1143 
1144 	assert(bufs.length > 0);
1145 	
1146 	frame.settings.free();
1147 	
1148 	buf = &bufs.head.buf;
1149 	assert(bufs.length == buf.length);
1150 	
1151 	user_data.frame_recv_cb_called = 0;
1152 	
1153 	rv = session.memRecv((*buf)[]);
1154 	
1155 	assert(rv == buf.length);
1156 	assert(1 == user_data.frame_recv_cb_called);
1157 	
1158 	assert(3001 == session.remote_settings.header_table_size);
1159 	assert(16383 == session.remote_settings.initial_window_size);
1160 	
1161 	bufs.reset();
1162 	
1163 	/* 2 SettingsID.HEADER_TABLE_SIZE; first entry clears dynamic header table. */	
1164 	submitRequest(session, pri_spec_default, (&hf)[0 .. 1], DataProvider.init, null);
1165 	session.send();
1166 	
1167 	assert(0 < session.hd_deflater.ctx.hd_table.length);
1168 	
1169 	iva[0].id = Setting.HEADER_TABLE_SIZE;
1170 	iva[0].value = 0;
1171 	
1172 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
1173 	iva[1].value = 16382;
1174 	
1175 	iva[2].id = Setting.HEADER_TABLE_SIZE;
1176 	iva[2].value = 4096;
1177 	
1178 	frame.settings = Settings(FrameFlags.NONE, iva.copy());
1179 	
1180 	frame.settings.pack(bufs);
1181 
1182 	assert(bufs.length > 0);
1183 	
1184 	frame.settings.free();
1185 	
1186 	buf = &bufs.head.buf;
1187 	assert(bufs.length == buf.length);
1188 	
1189 	user_data.frame_recv_cb_called = 0;
1190 	
1191 	rv = session.memRecv((*buf)[]);
1192 	
1193 	assert(rv == buf.length);
1194 	assert(1 == user_data.frame_recv_cb_called);
1195 	
1196 	assert(4096 == session.remote_settings.header_table_size);
1197 	assert(16382 == session.remote_settings.initial_window_size);
1198 	assert(0 == session.hd_deflater.ctx.hd_table.length);
1199 	
1200 	bufs.reset();
1201 	
1202 	/* 2 SettingsID.HEADER_TABLE_SIZE; second entry clears dynamic header table. */
1203 	
1204 	submitRequest(session, pri_spec_default, (&hf)[0 .. 1], DataProvider.init, null);
1205 	session.send();
1206 	
1207 	assert(0 < session.hd_deflater.ctx.hd_table.length);
1208 	
1209 	iva[0].id = Setting.HEADER_TABLE_SIZE;
1210 	iva[0].value = 3000;
1211 
1212 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
1213 	iva[1].value = 16381;
1214 	
1215 	iva[2].id = Setting.HEADER_TABLE_SIZE;
1216 	iva[2].value = 0;
1217 	
1218 	frame.settings = Settings(FrameFlags.NONE, iva.copy());
1219 	
1220 	frame.settings.pack(bufs);
1221 
1222 	assert(bufs.length > 0);
1223 	
1224 	frame.settings.free();
1225 	
1226 	buf = &bufs.head.buf;
1227 	assert(bufs.length == buf.length);
1228 	
1229 	user_data.frame_recv_cb_called = 0;
1230 	
1231 	rv = session.memRecv((*buf)[]);
1232 	
1233 	assert(rv == buf.length);
1234 	assert(1 == user_data.frame_recv_cb_called);
1235 	
1236 	assert(0 == session.remote_settings.header_table_size);
1237 	assert(16381 == session.remote_settings.initial_window_size);
1238 	assert(0 == session.hd_deflater.ctx.hd_table.length);
1239 	
1240 	bufs.reset();
1241 	
1242 	bufs.free();
1243 	session.free();
1244 }
1245 
1246 void test_session_read_too_large_frame_length() {
1247 	Session session;
1248 	Callbacks callbacks;
1249 	ubyte[FRAME_HDLEN] buf;
1250 	OutboundItem item;
1251 	FrameHeader hd;
1252 	
1253 	/* Initial max frame size is MAX_FRAME_SIZE_MIN */
1254 	hd = FrameHeader(MAX_FRAME_SIZE_MIN + 1, FrameType.HEADERS, FrameFlags.NONE, 1);
1255 
1256 	session = new Session(SERVER, *callbacks);
1257 	
1258 	hd.pack(buf);
1259 	
1260 	assert(buf.length == session.memRecv(buf));
1261 	
1262 	item = session.getNextOutboundItem();
1263 	
1264 	assert(item);
1265 	assert(FrameType.GOAWAY == item.frame.hd.type);
1266 	
1267 	session.free();
1268 }
1269 
1270 void test_session_continue() {
1271 	Session session;
1272 	Callbacks callbacks;
1273 	MyUserData user_data = MyUserData(&session);
1274 	const HeaderField[] hf1 = [HeaderField(":method", "GET"), HeaderField(":path", "/")];
1275 	const HeaderField[] hf2 = [HeaderField("user-agent", "nghttp2/1.0.0"), HeaderField("alpha", "bravo")];
1276 	Buffers bufs = framePackBuffers();
1277 	Buffer* buf;
1278 	size_t framelen1, framelen2;
1279 	int rv;
1280 	ubyte[4096] buffer;
1281 	Buffer databuf;
1282 	Frame frame;
1283 	HeaderField[] hfa;
1284 	
1285 	Frame* recv_frame;
1286 	FrameHeader data_hd;
1287 	Deflater deflater;
1288 
1289 	databuf = Buffer(buffer);
1290 
1291 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
1292 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
1293 	callbacks.on_data_chunk_cb = &user_data.cb_handlers.onDataChunkPause;
1294 	callbacks.on_header_field_cb = &user_data.cb_handlers.onHeaderFieldPause;
1295 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
1296 	
1297 	session = new Session(SERVER, *callbacks);
1298 	/* disable strict HTTP layering checks */
1299 	session.opt_flags |= OptionsMask.NO_HTTP_MESSAGING;
1300 	
1301 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
1302 	
1303 	/* Make 2 HEADERS frames */
1304 	hfa = hf1.copy();
1305 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
1306 	rv = frame.headers.pack(bufs, deflater);
1307 	
1308 	assert(0 == rv);
1309 	assert(bufs.length > 0);
1310 	
1311 	frame.headers.free();
1312 	
1313 	buf = &bufs.head.buf;
1314 	assert(bufs.length == buf.length);
1315 	
1316 	framelen1 = buf.length;
1317 	memcpy(databuf.last, buf.pos, buf.length);
1318 	databuf.last += buf.length;
1319 	
1320 	hfa = hf2.copy();
1321 	frame.headers = Headers(FrameFlags.END_HEADERS, 3, HeadersCategory.HEADERS, pri_spec_default, hfa);
1322 	bufs.reset();
1323 	rv = frame.headers.pack(bufs, deflater);
1324 	
1325 	assert(0 == rv);
1326 	assert(bufs.length > 0);
1327 	
1328 	frame.headers.free();
1329 	
1330 	assert(bufs.length == buf.length);
1331 	
1332 	framelen2 = buf.length;
1333 	memcpy(databuf.last, buf.pos, buf.length);
1334 	databuf.last += buf.length;
1335 
1336 	/* Receive 1st HEADERS and pause */
1337 	user_data.begin_headers_cb_called = 0;
1338 	user_data.header_cb_called = 0;
1339 	rv = session.memRecv(databuf[]);
1340 	
1341 	assert(rv >= 0);
1342 	databuf.pos += rv;
1343 	
1344 	recv_frame = cast(Frame*)user_data.frame;
1345 	assert(FrameType.HEADERS == recv_frame.hd.type);
1346 	assert(framelen1 - FRAME_HDLEN == recv_frame.hd.length);
1347 	
1348 	assert(1 == user_data.begin_headers_cb_called);
1349 	assert(1 == user_data.header_cb_called);
1350 	assert(hf1[0] == user_data.hf);
1351 	
1352 	/* get 2nd header field */
1353 	user_data.begin_headers_cb_called = 0;
1354 	user_data.header_cb_called = 0;
1355 	rv = session.memRecv(databuf[]);
1356 	
1357 	assert(rv >= 0);
1358 	databuf.pos += rv;
1359 	
1360 	assert(0 == user_data.begin_headers_cb_called);
1361 	assert(1 == user_data.header_cb_called);
1362 	
1363 	assert(hf1[1] == user_data.hf);
1364 	
1365 	/* will call end_headers_callback and receive 2nd HEADERS and pause */
1366 	user_data.begin_headers_cb_called = 0;
1367 	user_data.header_cb_called = 0;
1368 	rv = session.memRecv(databuf[]);
1369 	
1370 	assert(rv >= 0);
1371 	databuf.pos += rv;
1372 	
1373 	recv_frame = cast(Frame*) user_data.frame;
1374 	assert(FrameType.HEADERS == recv_frame.hd.type);
1375 	assert(framelen2 - FRAME_HDLEN == recv_frame.hd.length);
1376 	
1377 	assert(1 == user_data.begin_headers_cb_called);
1378 	assert(1 == user_data.header_cb_called);
1379 	
1380 	assert(hf2[0] == user_data.hf);
1381 	
1382 	/* get 2nd header field */
1383 	user_data.begin_headers_cb_called = 0;
1384 	user_data.header_cb_called = 0;
1385 	rv = session.memRecv(databuf[]);
1386 	
1387 	assert(rv >= 0);
1388 	databuf.pos += rv;
1389 	
1390 	assert(0 == user_data.begin_headers_cb_called);
1391 	assert(1 == user_data.header_cb_called);
1392 	
1393 	assert(hf2[1] == user_data.hf);
1394 	
1395 	/* No input data, frame_read_cb is called */
1396 	user_data.begin_headers_cb_called = 0;
1397 	user_data.header_cb_called = 0;
1398 	user_data.frame_recv_cb_called = 0;
1399 	rv = session.memRecv(databuf[]);
1400 	
1401 	assert(rv >= 0);
1402 	databuf.pos += rv;
1403 	
1404 	assert(0 == user_data.begin_headers_cb_called);
1405 	assert(0 == user_data.header_cb_called);
1406 	assert(1 == user_data.frame_recv_cb_called);
1407 	
1408 	/* Receive DATA */
1409 	data_hd = FrameHeader(16, FrameType.DATA, FrameFlags.NONE, 1);
1410 	
1411 	databuf.reset();
1412 	data_hd.pack(databuf.pos[0 .. databuf.available]);
1413 	
1414 	/* Intentionally specify larger buffer size to see pause is kicked in. */
1415 	databuf.last = databuf.end;
1416 	
1417 	user_data.frame_recv_cb_called = 0;
1418 	rv = session.memRecv(databuf[]);
1419 	
1420 	assert(16 + FRAME_HDLEN == rv);
1421 	assert(0 == user_data.frame_recv_cb_called);
1422 	
1423 	/* Next Session.memRecv invokes on_frame_cb and
1424        pause again in on_data_chunk_cb since we pass same
1425        DATA frame. */
1426 	user_data.frame_recv_cb_called = 0;
1427 	rv = session.memRecv(databuf[]);
1428 	assert(16 + FRAME_HDLEN == rv);
1429 	assert(1 == user_data.frame_recv_cb_called);
1430 	
1431 	/* And finally call on_frame_cb with 0 size input */
1432 	user_data.frame_recv_cb_called = 0;
1433 	rv = session.memRecv(null);
1434 	assert(0 == rv);
1435 	assert(1 == user_data.frame_recv_cb_called);
1436 	
1437 	bufs.free();
1438 	deflater.free();
1439 	session.free();
1440 }
1441 
1442 void test_session_add_frame() {
1443 	Session session;
1444 	Callbacks callbacks;
1445 	Accumulator acc;
1446 	MyUserData user_data = MyUserData(&session);
1447 	OutboundItem item;
1448 	Frame* frame;
1449 	HeaderField[] hfa;
1450 
1451 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
1452 	
1453 	acc.length = 0;
1454 	user_data.acc = &acc;
1455 	
1456 	session = new Session(CLIENT, *callbacks);
1457 	
1458 	item = Mem.alloc!OutboundItem(session);
1459 	
1460 	frame = &item.frame;
1461 	
1462 	
1463 	hfa = reqhf.copy();
1464 	
1465 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), session.next_stream_id, HeadersCategory.REQUEST, pri_spec_default, hfa);
1466 	
1467 	session.next_stream_id += 2;
1468 	
1469 	assert(0 == session.addItem(item));
1470 	assert(0 == session.ob_ss_pq.empty);
1471 	assert(0 == session.send());
1472 	assert(FrameType.HEADERS == acc.buf[3]);
1473 	assert(cast(ubyte)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY) == acc.buf[4]);
1474 	/* check stream id */
1475 	assert(1 == read!uint(&acc.buf[5]));
1476 	session.free();
1477 }
1478 
1479 void test_session_on_request_headers_received() {
1480 	Session session;
1481 	Callbacks callbacks;
1482 	MyUserData user_data = MyUserData(&session);
1483 	Frame frame;
1484 	Stream stream;
1485 	int stream_id = 1;
1486 	HeaderField[] malformed_hfa = [HeaderField(":path", "\x01")];
1487 	HeaderField[] hfa;
1488 	
1489 	PrioritySpec pri_spec;
1490 
1491 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
1492 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
1493 	
1494 	session = new Session(SERVER, *callbacks);
1495 	
1496 	pri_spec = PrioritySpec(0, 255, 0);
1497 
1498 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), stream_id, HeadersCategory.REQUEST, pri_spec, null);
1499 	
1500 	user_data.begin_headers_cb_called = 0;
1501 	user_data.invalid_frame_recv_cb_called = 0;
1502 	
1503 	assert(0 == session.onRequestHeaders(frame));
1504 	assert(1 == user_data.begin_headers_cb_called);
1505 	stream = session.getStream(stream_id);
1506 	assert(StreamState.OPENING == stream.state);
1507 	assert(255 == stream.weight);
1508 	
1509 	frame.headers.free();
1510 	
1511 	/* More than un-ACKed max concurrent streams leads REFUSED_STREAM */
1512 	session.pending_local_max_concurrent_stream = 1;
1513 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 3, HeadersCategory.HEADERS, pri_spec_default, null);
1514 	user_data.invalid_frame_recv_cb_called = 0;
1515 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1516 	assert(1 == user_data.invalid_frame_recv_cb_called);
1517 	assert(0 == (session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
1518 	
1519 	frame.headers.free();
1520 	session.local_settings.max_concurrent_streams = INITIAL_MAX_CONCURRENT_STREAMS;
1521 
1522 	/* Stream ID less than or equal to the previouly received request HEADERS is just ignored due to race condition */
1523 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 3, HeadersCategory.HEADERS, pri_spec_default, null);
1524 	user_data.invalid_frame_recv_cb_called = 0;
1525 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1526 	assert(0 == user_data.invalid_frame_recv_cb_called);
1527 	assert(0 == (session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
1528 	
1529 	frame.headers.free();
1530 	
1531 	/* Stream ID is our side and it is idle stream ID, then treat it as connection error */
1532 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 2, HeadersCategory.HEADERS, pri_spec_default, null);
1533 	user_data.invalid_frame_recv_cb_called = 0;
1534 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1535 	assert(1 == user_data.invalid_frame_recv_cb_called);
1536 	assert(session.goaway_flags & GoAwayFlags.TERM_ON_SEND);
1537 	
1538 	frame.headers.free();
1539 	
1540 	session.free();
1541 	
1542 	/* Check malformed headers. The library accept it. */
1543 	session = new Session(SERVER, *callbacks);
1544 	
1545 	hfa = malformed_hfa.copy();
1546 
1547 	frame.headers = Headers(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.PRIORITY), 1, HeadersCategory.HEADERS, pri_spec_default, hfa);
1548 	user_data.begin_headers_cb_called = 0;
1549 	user_data.invalid_frame_recv_cb_called = 0;
1550 	assert(0 == session.onRequestHeaders(frame));
1551 	assert(1 == user_data.begin_headers_cb_called);
1552 	assert(0 == user_data.invalid_frame_recv_cb_called);
1553 	
1554 	frame.headers.free();
1555 	
1556 	session.free();
1557 	
1558 	/* Check client side */
1559 	session = new Session(CLIENT, *callbacks);
1560 	
1561 	/* Receiving peer's idle stream ID is subject to connection error */
1562 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.REQUEST, pri_spec_default, null);
1563 	
1564 	user_data.invalid_frame_recv_cb_called = 0;
1565 	assert(ErrorCode.IGN_HEADER_BLOCK ==
1566 		session.onRequestHeaders(frame));
1567 	assert(1 == user_data.invalid_frame_recv_cb_called);
1568 	assert(session.goaway_flags & GoAwayFlags.TERM_ON_SEND);
1569 	
1570 	frame.headers.free();
1571 	
1572 	session.free();
1573 	
1574 	session = new Session(CLIENT, *callbacks);
1575 	
1576 	/* Receiving our's idle stream ID is subject to connection error */
1577 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.REQUEST, pri_spec_default, null);
1578 	
1579 	user_data.invalid_frame_recv_cb_called = 0;
1580 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1581 	assert(1 == user_data.invalid_frame_recv_cb_called);
1582 	assert(session.goaway_flags & GoAwayFlags.TERM_ON_SEND);
1583 	
1584 	frame.headers.free();
1585 	
1586 	session.free();
1587 	
1588 	session = new Session(CLIENT, *callbacks);
1589 	
1590 	session.next_stream_id = 5;
1591 	
1592 	/* Stream ID which is not idle and not in stream map is just ignored */
1593 	frame.headers = Headers(FrameFlags.END_HEADERS, 3, HeadersCategory.REQUEST, pri_spec_default, null);
1594 	
1595 	user_data.invalid_frame_recv_cb_called = 0;
1596 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1597 	assert(0 == user_data.invalid_frame_recv_cb_called);
1598 	assert(0 == (session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
1599 	
1600 	frame.headers.free();
1601 	
1602 	session.free();
1603 	
1604 	session = new Session(SERVER, *callbacks);
1605 	
1606 	/* Stream ID which is equal to local_last_stream_id is ok. */
1607 	session.local_last_stream_id = 3;
1608 	
1609 	frame.headers = Headers(FrameFlags.END_HEADERS, 3, HeadersCategory.REQUEST, pri_spec_default, null);
1610 	
1611 	assert(0 == session.onRequestHeaders(frame));
1612 	
1613 	frame.headers.free();
1614 	
1615 	/* If GOAWAY has been sent, new stream is ignored */
1616 	frame.headers = Headers(FrameFlags.END_HEADERS, 5, HeadersCategory.REQUEST, pri_spec_default, null);
1617 	
1618 	session.goaway_flags |= GoAwayFlags.SENT;
1619 	user_data.invalid_frame_recv_cb_called = 0;
1620 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
1621 	assert(0 == user_data.invalid_frame_recv_cb_called);
1622 	assert(0 == (session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
1623 	
1624 	frame.headers.free();
1625 	
1626 	session.free();
1627 }
1628 
1629 void test_session_on_response_headers_received() {
1630 	Session session;
1631 	Callbacks callbacks;
1632 	MyUserData user_data = MyUserData(&session);
1633 	Frame frame;
1634 	Stream stream;
1635 
1636 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
1637 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
1638 	
1639 	session = new Session(CLIENT, *callbacks);
1640 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1641 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, null);
1642 	
1643 	user_data.begin_headers_cb_called = 0;
1644 	user_data.invalid_frame_recv_cb_called = 0;
1645 
1646 	assert(0 == session.onResponseHeaders(frame, stream));
1647 	assert(1 == user_data.begin_headers_cb_called);
1648 	assert(StreamState.OPENED == stream.state);
1649 	
1650 	frame.headers.free();
1651 	session.free();
1652 }
1653 
1654 void test_session_on_headers_received() {
1655 	Session session;
1656 	Callbacks callbacks;
1657 	MyUserData user_data = MyUserData(&session);
1658 	Frame frame;
1659 	Stream stream;
1660 
1661 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
1662 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
1663 	
1664 	session = new Session(CLIENT, *callbacks);
1665 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
1666 	stream.shutdown(ShutdownFlag.WR);
1667 	frame.headers = Headers(FrameFlags.END_HEADERS, 1, HeadersCategory.HEADERS, pri_spec_default, null);
1668 	
1669 	user_data.begin_headers_cb_called = 0;
1670 	user_data.invalid_frame_recv_cb_called = 0;
1671 	
1672 	assert(0 == session.onHeaders(frame, stream));
1673 	assert(1 == user_data.begin_headers_cb_called);
1674 	assert(StreamState.OPENED == stream.state);
1675 	
1676 	/* stream closed */
1677 	frame.hd.flags |= FrameFlags.END_STREAM;
1678 	
1679 	assert(0 == session.onHeaders(frame, stream));
1680 	assert(2 == user_data.begin_headers_cb_called);
1681 	
1682 	/* Check to see when StreamState.CLOSING, incoming HEADERS is discarded. */
1683 	stream = session.openStream(3, StreamFlags.NONE, pri_spec_default, StreamState.CLOSING, null);
1684 	frame.hd.stream_id = 3;
1685 	frame.hd.flags = FrameFlags.END_HEADERS;
1686 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onHeaders(frame, stream));
1687 	/* See no counters are updated */
1688 	assert(2 == user_data.begin_headers_cb_called);
1689 	assert(0 == user_data.invalid_frame_recv_cb_called);
1690 	
1691 	/* Server initiated stream */
1692 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1693 
1694 	/* half closed (remote) */
1695 	frame.hd.flags = cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM);
1696 	frame.hd.stream_id = 2;
1697 	
1698 	assert(0 == session.onHeaders(frame, stream));
1699 	assert(3 == user_data.begin_headers_cb_called);
1700 	assert(StreamState.OPENING == stream.state);
1701 	
1702 	stream.shutdown(ShutdownFlag.RD);
1703 	
1704 	/* Further reception of HEADERS is subject to stream error */
1705 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onHeaders(frame, stream));
1706 	assert(1 == user_data.invalid_frame_recv_cb_called);
1707 	
1708 	frame.headers.free();
1709 	
1710 	session.free();
1711 }
1712 
1713 void test_session_on_push_response_headers_received() {
1714 	Session session;
1715 	Callbacks callbacks;
1716 	MyUserData user_data = MyUserData(&session);
1717 	Frame frame;
1718 	Stream stream;
1719 	OutboundItem item;
1720 
1721 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
1722 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
1723 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
1724 	
1725 	session = new Session(CLIENT, *callbacks);
1726 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
1727 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.HEADERS, pri_spec_default, null);
1728 	/* session.onPushResponseHeaders assumes stream's state is StreamState.RESERVED and session.server is 0. */
1729 	
1730 	user_data.begin_headers_cb_called = 0;
1731 	user_data.invalid_frame_recv_cb_called = 0;
1732 
1733 	assert(0 == session.onPushResponseHeaders(frame, stream));
1734 	assert(1 == user_data.begin_headers_cb_called);
1735 	assert(StreamState.OPENED == stream.state);
1736 	assert(1 == session.num_incoming_streams);
1737 	assert(0 == (stream.flags & StreamFlags.PUSH));
1738 	
1739 	/* If un-ACKed max concurrent streams limit is exceeded, RST_STREAMed */
1740 	session.pending_local_max_concurrent_stream = 1;
1741 	stream = session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
1742 	frame.hd.stream_id = 4;
1743 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushResponseHeaders(frame, stream));
1744 	item = session.getNextOutboundItem();
1745 	assert(FrameType.RST_STREAM == item.frame.hd.type);
1746 	assert(FrameError.REFUSED_STREAM == item.frame.rst_stream.error_code);
1747 	assert(1 == session.num_incoming_streams);
1748 	
1749 	assert(0 == session.send());
1750 	assert(1 == session.num_incoming_streams);
1751 	
1752 	/* If ACKed max concurrent streams limit is exceeded, GOAWAY is issued */
1753 	session.local_settings.max_concurrent_streams = 1;
1754 	
1755 	stream = session.openStream(6, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
1756 	frame.hd.stream_id = 6;
1757 	
1758 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushResponseHeaders(frame, stream));
1759 	item = session.getNextOutboundItem();
1760 	assert(FrameType.GOAWAY == item.frame.hd.type);
1761 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
1762 	assert(1 == session.num_incoming_streams);
1763 	
1764 	frame.headers.free();
1765 	session.free();
1766 }
1767 
1768 void test_session_on_priority_received() {
1769 	Session session;
1770 	Callbacks callbacks;
1771 	MyUserData user_data = MyUserData(&session);
1772 	Frame frame;
1773 	Stream stream, dep_stream;
1774 	PrioritySpec pri_spec;
1775 	OutboundItem item;
1776 
1777 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
1778 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
1779 	
1780 	session = new Session(SERVER, *callbacks);
1781 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1782 	
1783 	pri_spec = PrioritySpec(0, 2, 0);
1784 	
1785 	frame.priority = Priority(1, pri_spec);
1786 	
1787 	/* depend on stream 0 */
1788 	assert(0 == session.onPriority(frame));
1789 	
1790 	assert(2 == stream.weight);
1791 	
1792 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1793 	
1794 	dep_stream = session.openStream(3, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1795 	
1796 	frame.hd.stream_id = 2;
1797 	
1798 	/* using dependency stream */
1799 	frame.priority.pri_spec = PrioritySpec(3, 1, 0);
1800 	
1801 	assert(0 == session.onPriority(frame));
1802 	assert(dep_stream == stream.depPrev);
1803 	
1804 	/* PRIORITY against idle stream */
1805 	frame.hd.stream_id = 100;
1806 	
1807 	assert(0 == session.onPriority(frame));
1808 	
1809 	stream = session.getStreamRaw(frame.hd.stream_id);
1810 	
1811 	assert(StreamState.IDLE == stream.state);
1812 	assert(dep_stream == stream.depPrev);
1813 	
1814 	frame.priority.free();
1815 	session.free();
1816 	
1817 	/* Check dep_stream_id == stream_id case */
1818 	session = new Session(SERVER, *callbacks);
1819 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
1820 	
1821 	pri_spec = PrioritySpec(1, 0, 0);
1822 	
1823 	frame.priority = Priority(1, pri_spec);
1824 	
1825 	assert(0 == session.onPriority(frame));
1826 	
1827 	item = session.getNextOutboundItem();
1828 	
1829 	assert(FrameType.GOAWAY == item.frame.hd.type);
1830 	
1831 	frame.priority.free();
1832 	session.free();
1833 }
1834 
1835 void test_session_on_rst_stream_received() {
1836 	Session session;
1837 	Callbacks callbacks;
1838 	MyUserData user_data = MyUserData(&session);
1839 	Frame frame;
1840 	
1841 	session = new Session(SERVER, *callbacks);
1842 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1843 	
1844 	frame.rst_stream = RstStream(1, FrameError.PROTOCOL_ERROR);
1845 	
1846 	assert(0 == session.onRstStream(frame));
1847 	assert(!session.getStream(1));
1848 	
1849 	frame.rst_stream.free();
1850 	session.free();
1851 }
1852 
1853 void test_session_on_settings_received() {
1854 	Session session;
1855 	Callbacks callbacks;
1856 	MyUserData user_data = MyUserData(&session);
1857 	Stream stream1, stream2;
1858 	Frame frame;
1859 	Setting[255] iva;
1860 	OutboundItem item;
1861 	HeaderField hf = HeaderField(":authority", "example.org");
1862 
1863 	iva[0].id = Setting.MAX_CONCURRENT_STREAMS;
1864 	iva[0].value = 50;
1865 	
1866 	iva[1].id = Setting.MAX_CONCURRENT_STREAMS;
1867 	iva[1].value = 1000000009;
1868 	
1869 	iva[2].id = Setting.INITIAL_WINDOW_SIZE;
1870 	iva[2].value = 64 * 1024;
1871 	
1872 	iva[3].id = Setting.HEADER_TABLE_SIZE;
1873 	iva[3].value = 1024;
1874 	
1875 	iva[4].id = Setting.ENABLE_PUSH;
1876 	iva[4].value = 0;
1877 		
1878 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
1879 	
1880 	session = new Session(CLIENT, *callbacks);
1881 	session.remote_settings.initial_window_size = 16 * 1024;
1882 	
1883 	stream1 = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1884 	stream2 = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
1885 	/* Set window size for each streams and will see how settings updates these values */
1886 	stream1.remoteWindowSize = 16 * 1024;
1887 	stream2.remoteWindowSize = -48 * 1024;
1888 	
1889 	frame.settings = Settings(FrameFlags.NONE, iva[0 .. 5].copy());
1890 	
1891 	assert(0 == session.onSettings(frame, false));
1892 	assert(1000000009 == session.remote_settings.max_concurrent_streams);
1893 	assert(64 * 1024 == session.remote_settings.initial_window_size);
1894 	assert(1024 == session.remote_settings.header_table_size);
1895 	assert(0 == session.remote_settings.enable_push);
1896 	
1897 	assert(64 * 1024 == stream1.remoteWindowSize);
1898 	assert(0 == stream2.remoteWindowSize);
1899 	
1900 	frame.settings.iva[2].value = 16 * 1024;
1901 	
1902 	assert(0 == session.onSettings(frame, false));
1903 	
1904 	assert(16 * 1024 == stream1.remoteWindowSize);
1905 	assert(-48 * 1024 == stream2.remoteWindowSize);
1906 	
1907 	assert(16 * 1024 == session.getStreamRemoteWindowSize(stream1.id));
1908 	assert(0 == session.getStreamRemoteWindowSize(stream2.id));
1909 	
1910 	frame.settings.free();
1911 	
1912 	session.free();
1913 	
1914 	/* Check ACK with niv > 0 */
1915 	session = new Session(SERVER, *callbacks);
1916 	frame.settings = Settings(FrameFlags.ACK, iva[0 .. 1].copy());
1917 	/* Specify inflight_ivadeliberately */
1918 	session.inflight_iva = frame.settings.iva;
1919 	
1920 	assert(0 == session.onSettings(frame, false));
1921 	item = session.getNextOutboundItem();
1922 	assert(item);
1923 	assert(FrameType.GOAWAY == item.frame.hd.type);
1924 	
1925 	session.inflight_iva = null;
1926 	
1927 	frame.settings.free();
1928 	session.free();
1929 	
1930 	/* Check ACK against no inflight SETTINGS */
1931 	session = new Session(SERVER, *callbacks);
1932 	frame.settings = Settings(FrameFlags.ACK, null);
1933 	
1934 	assert(0 == session.onSettings(frame, false));
1935 	item = session.getNextOutboundItem();
1936 	assert(item);
1937 	assert(FrameType.GOAWAY == item.frame.hd.type);
1938 	
1939 	frame.settings.free();
1940 	session.free();
1941 	
1942 	/* Check that 2 SettingsID.HEADER_TABLE_SIZE 0 and 4096 are included
1943      and header table size is once cleared to 0. */
1944 	session = new Session(CLIENT, *callbacks);
1945 	
1946 	submitRequest(session, pri_spec_default, (&hf)[0 .. 1], DataProvider.init, null);
1947 
1948 	session.send();
1949 	
1950 	assert(session.hd_deflater.ctx.hd_table.length > 0);
1951 	
1952 	iva[0].id = Setting.HEADER_TABLE_SIZE;
1953 	iva[0].value = 0;
1954 	
1955 	iva[1].id = Setting.HEADER_TABLE_SIZE;
1956 	iva[1].value = 2048;
1957 	
1958 	frame.settings = Settings(FrameFlags.NONE, iva[0 .. 2].copy());
1959 	
1960 	assert(0 == session.onSettings(frame, false));
1961 	
1962 	assert(0 == session.hd_deflater.ctx.hd_table.length);
1963 	assert(2048 == session.hd_deflater.ctx.hd_table_bufsize_max);
1964 	assert(2048 == session.remote_settings.header_table_size);
1965 	
1966 	frame.settings.free();
1967 	session.free();
1968 	
1969 	/* Check too large SettingsID.MAX_FRAME_SIZE */
1970 	session = new Session(SERVER, *callbacks);
1971 	
1972 	iva[0].id = Setting.MAX_FRAME_SIZE;
1973 	iva[0].value = MAX_FRAME_SIZE_MAX + 1;
1974 	
1975 	frame.settings = Settings(FrameFlags.NONE, iva[0 .. 1].copy());
1976 	
1977 	assert(0 == session.onSettings(frame, false));
1978 
1979 	item = session.getNextOutboundItem();
1980 	
1981 	assert(item);
1982 	assert(FrameType.GOAWAY == item.frame.hd.type);
1983 	
1984 	frame.settings.free();
1985 	session.free();
1986 }
1987 
1988 void test_session_on_push_promise_received() {
1989 	Session session;
1990 	Callbacks callbacks;
1991 	MyUserData user_data = MyUserData(&session);
1992 	Frame frame;
1993 	Stream stream, promised_stream;
1994 	OutboundItem item;
1995 	HeaderField[] malformed_hfa = [HeaderField(":path", "\x01")];
1996 	HeaderField[] hfa;
1997 	Setting iv = Setting(Setting.ENABLE_PUSH, 0);
1998 
1999 	
2000 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2001 	callbacks.on_headers_cb = &user_data.cb_handlers.onHeaders;
2002 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
2003 	
2004 	session = new Session(CLIENT, *callbacks);
2005 	
2006 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2007 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, 2, null);
2008 	
2009 	user_data.begin_headers_cb_called = 0;
2010 	user_data.invalid_frame_recv_cb_called = 0;
2011 	
2012 	assert(0 == session.onPushPromise(frame));
2013 	
2014 	assert(1 == user_data.begin_headers_cb_called);
2015 	promised_stream = session.getStream(2);
2016 	assert(StreamState.RESERVED == promised_stream.state);
2017 	assert(2 == session.last_recv_stream_id);
2018 	
2019 	/* Attempt to PUSH_PROMISE against half close (remote) */
2020 	stream.shutdown(ShutdownFlag.RD);
2021 	frame.push_promise.promised_stream_id = 4;
2022 	
2023 	user_data.begin_headers_cb_called = 0;
2024 	user_data.invalid_frame_recv_cb_called = 0;
2025 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2026 	
2027 	assert(0 == user_data.begin_headers_cb_called);
2028 	assert(1 == user_data.invalid_frame_recv_cb_called);
2029 	assert(!session.getStream(4));
2030 	item = session.getNextOutboundItem();
2031 	assert(FrameType.RST_STREAM == item.frame.hd.type);
2032 	assert(4 == item.frame.hd.stream_id);
2033 	assert(FrameError.PROTOCOL_ERROR == item.frame.rst_stream.error_code);
2034 	assert(0 == session.send());
2035 	assert(4 == session.last_recv_stream_id);
2036 	
2037 	/* Attempt to PUSH_PROMISE against stream in closing state */
2038 	stream.shutFlags = ShutdownFlag.NONE;
2039 	stream.state = StreamState.CLOSING;
2040 	frame.push_promise.promised_stream_id = 6;
2041 	
2042 	user_data.begin_headers_cb_called = 0;
2043 	user_data.invalid_frame_recv_cb_called = 0;
2044 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2045 	
2046 	assert(0 == user_data.begin_headers_cb_called);
2047 	assert(!session.getStream(6));
2048 	item = session.getNextOutboundItem();
2049 	assert(FrameType.RST_STREAM == item.frame.hd.type);
2050 	assert(6 == item.frame.hd.stream_id);
2051 	assert(FrameError.REFUSED_STREAM == item.frame.rst_stream.error_code);
2052 	assert(0 == session.send());
2053 	
2054 	/* Attempt to PUSH_PROMISE against non-existent stream */
2055 	frame.hd.stream_id = 3;
2056 	frame.push_promise.promised_stream_id = 8;
2057 	
2058 	user_data.begin_headers_cb_called = 0;
2059 	user_data.invalid_frame_recv_cb_called = 0;
2060 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2061 	
2062 	assert(0 == user_data.begin_headers_cb_called);
2063 	assert(!session.getStream(8));
2064 	item = session.getNextOutboundItem();
2065 	assert(FrameType.GOAWAY == item.frame.hd.type);
2066 	assert(0 == item.frame.hd.stream_id);
2067 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
2068 	assert(0 == session.send());
2069 	
2070 	session.free();
2071 
2072 	session = new Session(CLIENT, *callbacks);
2073 	
2074 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2075 	
2076 	/* Same ID twice */
2077 	stream.state = StreamState.OPENING;
2078 	
2079 	user_data.begin_headers_cb_called = 0;
2080 	user_data.invalid_frame_recv_cb_called = 0;
2081 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2082 	
2083 	assert(0 == user_data.begin_headers_cb_called);
2084 	assert(!session.getStream(8));
2085 	item = session.getNextOutboundItem();
2086 	assert(FrameType.GOAWAY == item.frame.hd.type);
2087 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
2088 	assert(0 == session.send());
2089 	
2090 	/* After GOAWAY, PUSH_PROMISE will be discarded */
2091 	frame.push_promise.promised_stream_id = 10;
2092 	
2093 	user_data.begin_headers_cb_called = 0;
2094 	user_data.invalid_frame_recv_cb_called = 0;
2095 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2096 	
2097 	assert(0 == user_data.begin_headers_cb_called);
2098 	assert(!session.getStream(10));
2099 	assert(!session.getNextOutboundItem());
2100 	
2101 	frame.push_promise.free();
2102 	session.free();
2103 	
2104 	session = new Session(CLIENT, *callbacks);
2105 	
2106 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
2107 	/* Attempt to PUSH_PROMISE against reserved (remote) stream */
2108 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 2, 4, null);
2109 	
2110 	user_data.begin_headers_cb_called = 0;
2111 	user_data.invalid_frame_recv_cb_called = 0;
2112 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2113 	
2114 	assert(0 == user_data.begin_headers_cb_called);
2115 	assert(1 == user_data.invalid_frame_recv_cb_called);
2116 	
2117 	frame.push_promise.free();
2118 	session.free();
2119 	
2120 	/* Disable PUSH */
2121 	session = new Session(CLIENT, *callbacks);
2122 	
2123 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2124 	
2125 	session.local_settings.enable_push = 0;
2126 	
2127 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, 2, null);
2128 	
2129 	user_data.begin_headers_cb_called = 0;
2130 	user_data.invalid_frame_recv_cb_called = 0;
2131 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2132 	
2133 	assert(0 == user_data.begin_headers_cb_called);
2134 	assert(1 == user_data.invalid_frame_recv_cb_called);
2135 	
2136 	frame.push_promise.free();
2137 	session.free();
2138 	
2139 	/* Check malformed headers. We accept malformed headers */
2140 	session = new Session(CLIENT, *callbacks);
2141 	
2142 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2143 	hfa = malformed_hfa.copy();
2144 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, 2, hfa);
2145 	user_data.begin_headers_cb_called = 0;
2146 	user_data.invalid_frame_recv_cb_called = 0;
2147 	assert(0 == session.onPushPromise(frame));
2148 	
2149 	assert(1 == user_data.begin_headers_cb_called);
2150 	assert(0 == user_data.invalid_frame_recv_cb_called);
2151 	
2152 	frame.push_promise.free();
2153 	session.free();
2154 
2155 	// If local_settings.enable_push = 0 is pending, but not acked from peer, incoming
2156 	// PUSH_PROMISE is rejected
2157 
2158 	session = new Session(CLIENT, *callbacks);
2159 	
2160 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2161 	// Submit settings with ENABLE_PUSH = 0 (thus disabling push)
2162 	submitSettings(session, (&iv)[0 .. 1]);
2163 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, 2, null);
2164 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onPushPromise(frame));
2165 
2166 	frame.push_promise.free();
2167 	session.free();
2168 
2169 }
2170 
2171 void test_session_on_ping_received() {
2172 	Session session;
2173 	Callbacks callbacks;
2174 	MyUserData user_data = MyUserData(&session);
2175 	Frame frame;
2176 	OutboundItem top;
2177 	string opaque_data = "01234567";
2178 	
2179 	user_data.frame_recv_cb_called = 0;
2180 	user_data.invalid_frame_recv_cb_called = 0;
2181 	
2182 	
2183 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
2184 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
2185 	
2186 	session = new Session(CLIENT, *callbacks);
2187 	frame.ping = Ping(FrameFlags.ACK, cast(ubyte[])opaque_data.ptr[0 .. 8]);
2188 	
2189 	assert(0 == session.onPing(frame));
2190 	assert(1 == user_data.frame_recv_cb_called);
2191 	
2192 	/* Since this ping frame has PONG flag set, no further action is
2193      performed. */
2194 	assert(!session.ob_pq_top);
2195 	
2196 	/* Clear the flag, and receive it again */
2197 	frame.hd.flags = FrameFlags.NONE;
2198 	
2199 	assert(0 == session.onPing(frame));
2200 	assert(2 == user_data.frame_recv_cb_called);
2201 	top = session.ob_pq_top;
2202 	assert(FrameType.PING == top.frame.hd.type);
2203 	assert(FrameFlags.ACK == top.frame.hd.flags);
2204 	assert(opaque_data == top.frame.ping.opaque_data);
2205 	
2206 	frame.ping.free();
2207 	session.free();
2208 }
2209 
2210 void test_session_on_goaway_received() {
2211 	Session session;
2212 	Callbacks callbacks;
2213 	MyUserData user_data = MyUserData(&session);
2214 	Frame frame;
2215 	int i;
2216 
2217 	user_data.frame_recv_cb_called = 0;
2218 	user_data.invalid_frame_recv_cb_called = 0;
2219 	
2220 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
2221 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
2222 	callbacks.on_stream_exit_cb = &user_data.cb_handlers.onStreamExit;
2223 	
2224 	session = new Session(CLIENT, *callbacks);
2225 	
2226 	for (i = 1; i <= 7; ++i) {
2227 		openStream(session, i);
2228 	}
2229 	
2230 	frame.goaway = GoAway(3, FrameError.PROTOCOL_ERROR, null);
2231 	
2232 	user_data.stream_close_cb_called = 0;
2233 	
2234 	assert(0 == session.onGoAway(frame));
2235 	
2236 	assert(1 == user_data.frame_recv_cb_called);
2237 	assert(3 == session.remote_last_stream_id);
2238 	/* on_stream_close should be callsed for 2 times (stream 5 and 7) */
2239 	assert(2 == user_data.stream_close_cb_called);
2240 	
2241 	assert(session.getStream(1));
2242 	assert(session.getStream(2));
2243 	assert(session.getStream(3));
2244 	assert(session.getStream(4));
2245 	assert(!session.getStream(5));
2246 	assert(session.getStream(6));
2247 	assert(!session.getStream(7));
2248 	
2249 	frame.goaway.free();
2250 	session.free();
2251 
2252 }
2253 
2254 void test_session_on_window_update_received() {
2255 	Session session;
2256 	Callbacks callbacks;
2257 	MyUserData user_data = MyUserData(&session);
2258 	Frame frame;
2259 	Stream stream;
2260 	OutboundItem data_item;
2261 	
2262 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
2263 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
2264 	user_data.frame_recv_cb_called = 0;
2265 	user_data.invalid_frame_recv_cb_called = 0;
2266 	
2267 	session = new Session(CLIENT, *callbacks);
2268 	
2269 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
2270 	
2271 	data_item = createDataOutboundItem();
2272 	
2273 	stream.attachItem(data_item, session);
2274 	
2275 	frame.window_update = WindowUpdate(FrameFlags.NONE, 1, 16 * 1024);
2276 
2277 	assert(0 == session.onWindowUpdate(frame));
2278 	assert(1 == user_data.frame_recv_cb_called);
2279 	assert(INITIAL_WINDOW_SIZE + 16 * 1024 == stream.remoteWindowSize);
2280 	
2281 	stream.deferItem(StreamFlags.DEFERRED_FLOW_CONTROL, session);
2282 	
2283 	assert(0 == session.onWindowUpdate(frame));
2284 	assert(2 == user_data.frame_recv_cb_called);
2285 	assert(INITIAL_WINDOW_SIZE + 16 * 1024 * 2 == stream.remoteWindowSize);
2286 	assert(0 == (stream.flags & StreamFlags.DEFERRED_ALL));
2287 	
2288 	frame.window_update.free();
2289 	
2290 	/* Receiving WINDOW_UPDATE on reserved (remote) stream is a connection error */
2291 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
2292 	
2293 	frame.window_update = WindowUpdate(FrameFlags.NONE, 2, 4096);
2294 	
2295 	assert(!(session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
2296 	assert(0 == session.onWindowUpdate(frame));
2297 	assert(session.goaway_flags & GoAwayFlags.TERM_ON_SEND);
2298 	
2299 	frame.window_update.free();
2300 	session.free();
2301 	/* Receiving WINDOW_UPDATE on reserved (local) stream is allowed */
2302 	session = new Session(SERVER, *callbacks);
2303 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
2304 
2305 	frame.window_update = WindowUpdate(FrameFlags.NONE, 2, 4096);
2306 
2307 	assert(0 == session.onWindowUpdate(frame));
2308 	assert(!(session.goaway_flags & GoAwayFlags.TERM_ON_SEND));
2309 	
2310 	assert(INITIAL_WINDOW_SIZE + 4096 == stream.remoteWindowSize);
2311 	
2312 	frame.window_update.free();
2313 	
2314 	session.free();
2315 }
2316 
2317 void test_session_on_data_received() {
2318 	Session session;
2319 	Callbacks callbacks;
2320 	MyUserData user_data = MyUserData(&session);
2321 	OutboundItem top;
2322 	Stream stream;
2323 	Frame frame;
2324 
2325 	session = new Session(CLIENT, *callbacks);
2326 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2327 	
2328 	frame.hd = FrameHeader(4096, FrameType.DATA, FrameFlags.NONE, 2);
2329 	
2330 	assert(0 == session.onData(frame));
2331 	assert(0 == stream.shutFlags);
2332 	
2333 	frame.hd.flags = FrameFlags.END_STREAM;
2334 	
2335 	assert(0 == session.onData(frame));
2336 	assert(ShutdownFlag.RD == stream.shutFlags);
2337 	
2338 	/* If StreamState.CLOSING state, DATA frame is discarded. */
2339 	stream = session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.CLOSING, null);
2340 	
2341 	frame.hd.flags = FrameFlags.NONE;
2342 	frame.hd.stream_id = 4;
2343 	
2344 	assert(0 == session.onData(frame));
2345 	assert(!session.ob_pq_top);
2346 	
2347 	/* Check INVALID_STREAM case: DATA frame with stream ID which does not exist. */
2348 	
2349 	frame.hd.stream_id = 6;
2350 	
2351 	assert(0 == session.onData(frame));
2352 	top = session.ob_pq_top;
2353 	/* DATA against nonexistent stream is just ignored for now */
2354 	assert(!top);
2355 	/* assert(FrameType.RST_STREAM == top.frame.hd.type); */
2356 	/* assert(FrameError.PROTOCOL_ERROR == top.frame.rst_stream.error_code);
2357 	 */
2358 	session.free();
2359 }
2360 
2361 void test_session_write_headers_start_stream() {
2362 	Session session;
2363 	Callbacks callbacks;
2364 	OutboundItem item;
2365 	Frame* frame;
2366 	Stream stream;
2367 	
2368 	
2369 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2370 	
2371 	session = new Session(CLIENT, *callbacks);
2372 	
2373 	item = Mem.alloc!OutboundItem(session);
2374 
2375 	frame = &item.frame;
2376 	
2377 	frame.headers = Headers(FrameFlags.END_HEADERS,	session.next_stream_id, HeadersCategory.REQUEST, pri_spec_default, null);
2378 	session.next_stream_id += 2;
2379 	
2380 	session.addItem(item);
2381 	assert(0 == session.send());
2382 	stream = session.getStream(1);
2383 	assert(StreamState.OPENING == stream.state);
2384 	
2385 	session.free();
2386 }
2387 
2388 void test_session_write_headers_reply() {
2389 	Session session;
2390 	Callbacks callbacks;
2391 	OutboundItem item;
2392 	Frame* frame;
2393 	Stream stream;
2394 	
2395 	
2396 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2397 	
2398 	session = new Session(CLIENT, *callbacks);
2399 	session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2400 	
2401 	item = Mem.alloc!OutboundItem(session);
2402 	
2403 	frame = &item.frame;
2404 	
2405 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.HEADERS, pri_spec_default, null);
2406 	session.addItem(item);
2407 	assert(0 == session.send());
2408 	stream = session.getStream(2);
2409 	assert(StreamState.OPENED == stream.state);
2410 	
2411 	session.free();
2412 }
2413 
2414 void test_session_write_headers_frame_size_error() {
2415 	import std.c.string : memset;
2416 
2417 	Session session;
2418 	Callbacks callbacks;
2419 	OutboundItem item;
2420 	Frame* frame;	
2421 	size_t vallen = MAX_HF_LEN;
2422 	HeaderField[] hfa_copy;
2423 	HeaderField[28] hfa;
2424 	MyUserData user_data = MyUserData(&session);
2425 
2426 	foreach(size_t i, ref hf; hfa) 
2427 	{
2428 		hf.name = "header";
2429 		char[] value = Mem.alloc!(char[])(vallen + 1);
2430 		memset(value.ptr, '0' + cast(int)i, value.length);
2431 		value[$-1] = '\0';
2432 		hf.value = cast(string)value;
2433 		hf.flag = HeaderFlag.NONE;
2434 	}
2435 	
2436 	
2437 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2438 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
2439 	
2440 	session = new Session(CLIENT, *callbacks);
2441 	hfa_copy = hfa.ptr[0 .. hfa.length].copy();
2442 	item = Mem.alloc!OutboundItem(session);
2443 	
2444 	frame = &item.frame;
2445 	
2446 	frame.headers = Headers(cast(FrameFlags)FrameFlags.END_HEADERS,	session.next_stream_id, HeadersCategory.REQUEST, pri_spec_default, hfa_copy);
2447 	
2448 	session.next_stream_id += 2;
2449 	
2450 	session.addItem(item);
2451 	
2452 	user_data.frame_not_send_cb_called = 0;
2453 	
2454 	assert(0 == session.send());
2455 	
2456 	assert(1 == user_data.frame_not_send_cb_called);
2457 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
2458 	assert(ErrorCode.FRAME_SIZE_ERROR == user_data.not_sent_error);
2459 	
2460 	foreach(ref hf; hfa)
2461 	{
2462 		Mem.free(hf.value);
2463 	}
2464 	frame.headers.free();
2465 	session.free();
2466 }
2467 
2468 void test_session_write_headers_push_reply() {
2469 	Session session;
2470 	Callbacks callbacks;
2471 	OutboundItem item;
2472 	Frame* frame;
2473 	Stream stream;
2474 
2475 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2476 	
2477 	session = new Session(SERVER, *callbacks);
2478 	session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
2479 	
2480 	item = Mem.alloc!OutboundItem(session);
2481 	
2482 	frame = &item.frame;
2483 	
2484 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.HEADERS, pri_spec_default, null);
2485 	session.addItem(item);
2486 	assert(0 == session.num_outgoing_streams);
2487 	assert(0 == session.send());
2488 	assert(1 == session.num_outgoing_streams);
2489 	stream = session.getStream(2);
2490 	assert(StreamState.OPENED == stream.state);
2491 	assert(0 == (stream.flags & StreamFlags.PUSH));
2492 	session.free();
2493 }
2494 
2495 void test_session_write_rst_stream() {
2496 	Session session;
2497 	Callbacks callbacks;
2498 	MyUserData user_data = MyUserData(&session);
2499 	OutboundItem item;
2500 	Frame* frame;
2501 
2502 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2503 	session = new Session(CLIENT, *callbacks);
2504 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2505 	
2506 	item = Mem.alloc!OutboundItem(session);
2507 	
2508 	frame = &item.frame;
2509 	
2510 	frame.rst_stream = RstStream(1, FrameError.PROTOCOL_ERROR);
2511 	session.addItem(item);
2512 	assert(0 == session.send());
2513 	
2514 	assert(!session.getStream(1));
2515 	
2516 	session.free();
2517 }
2518 
2519 void test_session_write_push_promise() {
2520 	Session session;
2521 	Callbacks callbacks;
2522 	OutboundItem item;
2523 	Frame* frame;
2524 	Stream stream;
2525 	Setting iv;
2526 	MyUserData user_data = MyUserData(&session);
2527 
2528 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2529 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
2530 	
2531 	session = new Session(SERVER, *callbacks);
2532 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2533 	
2534 	item = Mem.alloc!OutboundItem(session);
2535 	
2536 	frame = &item.frame;
2537 	
2538 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, session.next_stream_id, null);
2539 	
2540 	session.next_stream_id += 2;
2541 	
2542 	session.addItem(item);
2543 	
2544 	assert(0 == session.send());
2545 	stream = session.getStream(2);
2546 	assert(StreamState.RESERVED == stream.state);
2547 	
2548 	/* Received ENABLE_PUSH = 0 */
2549 	iv.id = Setting.ENABLE_PUSH;
2550 	iv.value = 0;
2551 	frame = Mem.alloc!Frame();
2552 	frame.settings = Settings(FrameFlags.NONE, (&iv)[0 .. 1].copy());
2553 	session.onSettings(*frame, true);
2554 	frame.settings.free();
2555 	Mem.free(frame);
2556 	
2557 	item = Mem.alloc!OutboundItem(session);
2558 	
2559 	frame = &item.frame;
2560 	
2561 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, -1, null);
2562 	session.addItem(item);
2563 	
2564 	user_data.frame_not_send_cb_called = 0;
2565 	assert(0 == session.send());
2566 	
2567 	assert(1 == user_data.frame_not_send_cb_called);
2568 	assert(FrameType.PUSH_PROMISE == user_data.not_sent_frame_type);
2569 	assert(ErrorCode.PUSH_DISABLED == user_data.not_sent_error);
2570 	
2571 	session.free();
2572 	
2573 	/* PUSH_PROMISE from client is error */
2574 	session = new Session(CLIENT, *callbacks);
2575 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2576 	item = Mem.alloc!OutboundItem(session);
2577 	
2578 	frame = &item.frame;
2579 	
2580 	frame.push_promise = PushPromise(FrameFlags.END_HEADERS, 1, -1, null);
2581 	session.addItem(item);
2582 	
2583 	assert(0 == session.send());
2584 	assert(!session.getStream(3));
2585 
2586 	session.free();
2587 }
2588 
2589 void test_session_is_my_stream_id() {
2590 	Session session;
2591 	Callbacks callbacks;
2592 	
2593 	session = new Session(SERVER, *callbacks);
2594 	
2595 	assert(0 == session.isMyStreamId(0));
2596 	assert(0 == session.isMyStreamId(1));
2597 	assert(1 == session.isMyStreamId(2));
2598 	
2599 	session.free();
2600 	
2601 	session = new Session(CLIENT, *callbacks);
2602 	
2603 	assert(0 == session.isMyStreamId(0));
2604 	assert(1 == session.isMyStreamId(1));
2605 	assert(0 == session.isMyStreamId(2));
2606 	
2607 	session.free();
2608 }
2609 
2610 void test_session_upgrade() {
2611 	Session session;
2612 	Callbacks callbacks;
2613 	ubyte[128] settings_payload;
2614 	int settings_payloadlen;
2615 	Setting[16] iva;
2616 	Stream stream;
2617 	OutboundItem item;
2618 
2619 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2620 	iva[0].id = Setting.MAX_CONCURRENT_STREAMS;
2621 	iva[0].value = 1;
2622 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
2623 	iva[1].value = 4095;
2624 	settings_payloadlen = packSettingsPayload(settings_payload.ptr[0 .. 128], iva[0 .. 2]);
2625 	
2626 	/* Check client side */
2627 	session = new Session(CLIENT, *callbacks);
2628 	assert(0 == session.upgrade(settings_payload[0 .. settings_payloadlen], &*callbacks));
2629 	stream = session.getStream(1);
2630 	assert(stream !is null);
2631 	assert(&*callbacks == session.getStreamUserData(stream.id));
2632 	assert(ShutdownFlag.WR == stream.shutFlags);
2633 	item = session.getNextOutboundItem();
2634 	assert(FrameType.SETTINGS == item.frame.hd.type);
2635 	assert(2 == item.frame.settings.iva.length);
2636 	assert(Setting.MAX_CONCURRENT_STREAMS == item.frame.settings.iva[0].id);
2637 	assert(1 == item.frame.settings.iva[0].value);
2638 	assert(Setting.INITIAL_WINDOW_SIZE == item.frame.settings.iva[1].id);
2639 	assert(4095 == item.frame.settings.iva[1].value);
2640 	
2641 	/* Call upgrade() again is error */
2642 	assert(ErrorCode.PROTO == session.upgrade(settings_payload[0 .. settings_payloadlen], &*callbacks));
2643 	session.free();
2644 
2645 	/* Check server side */
2646 	session = new Session(SERVER, *callbacks);
2647 	assert(0 == session.upgrade(settings_payload[0 .. settings_payloadlen], &*callbacks));
2648 	stream = session.getStream(1);
2649 	assert(stream);
2650 	//assert(!session.getStreamUserData(stream.id));
2651 	assert(ShutdownFlag.RD == stream.shutFlags);
2652 	assert(!session.getNextOutboundItem());
2653 	assert(1 == session.remote_settings.max_concurrent_streams);
2654 	assert(4095 == session.remote_settings.initial_window_size);
2655 	/* Call upgrade() again is error */
2656 	assert(ErrorCode.PROTO == session.upgrade(settings_payload[0 .. settings_payloadlen], &*callbacks));
2657 	session.free();
2658 
2659 	/* Empty SETTINGS is OK */
2660 	settings_payloadlen = packSettingsPayload(settings_payload[0 .. 0], null);
2661 	
2662 	session = new Session(CLIENT, *callbacks);
2663 	assert(0 == session.upgrade(settings_payload[0 .. settings_payloadlen]));
2664 	session.free();
2665 }
2666 
2667 void test_session_reprioritize_stream() {
2668 	Session session;
2669 	Callbacks callbacks;
2670 	MyUserData user_data = MyUserData(&session);
2671 	Stream stream;
2672 	Stream dep_stream;
2673 	PrioritySpec pri_spec;
2674 
2675 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
2676 	
2677 	session = new Session(SERVER, *callbacks);
2678 	
2679 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2680 	
2681 	pri_spec = PrioritySpec(0, 10, 0);
2682 	
2683 	session.reprioritizeStream(stream, pri_spec);
2684 	
2685 	assert(10 == stream.weight);
2686 	assert(!stream.depPrev);
2687 	
2688 	/* If depenency to idle stream which is not in depdenency tree yet */
2689 	
2690 	pri_spec = PrioritySpec(3, 99, 0);
2691 	
2692 	session.reprioritizeStream(stream, pri_spec);
2693 	
2694 	assert(99 == stream.weight);
2695 	assert(3 == stream.depPrev.id);
2696 	
2697 	dep_stream = session.getStreamRaw(3);
2698 	
2699 	assert(DEFAULT_WEIGHT == dep_stream.weight);
2700 	
2701 	dep_stream = openStream(session, 3);
2702 	
2703 	/* Change weight */
2704 	pri_spec.weight = 128;
2705 	
2706 	session.reprioritizeStream(stream, pri_spec);
2707 	
2708 	assert(128 == stream.weight);
2709 	assert(dep_stream == stream.depPrev);
2710 	
2711 	/* Test circular dependency; stream 1 is first removed and becomes
2712      root.  Then stream 3 depends on it. */
2713 	pri_spec = PrioritySpec(1, 1, 0);
2714 	
2715 	session.reprioritizeStream(dep_stream, pri_spec);
2716 	
2717 	assert(1 == dep_stream.weight);
2718 	assert(stream == dep_stream.depPrev);
2719 	
2720 	/* Making priority to closed stream will result in default
2721      priority */
2722 	session.last_recv_stream_id = 9;
2723 	
2724 	pri_spec = PrioritySpec(5, 5, 0);
2725 	
2726 	session.reprioritizeStream(stream, pri_spec);
2727 	
2728 	assert(DEFAULT_WEIGHT == stream.weight);
2729 	
2730 	session.free();
2731 }
2732 
2733 void test_session_reprioritize_stream_with_idle_stream_dep() {
2734 	Session session;
2735 	Callbacks callbacks;
2736 	MyUserData user_data = MyUserData(&session);
2737 	Stream stream;
2738 	PrioritySpec pri_spec;
2739 
2740 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
2741 	
2742 	session = new Session(SERVER, *callbacks);
2743 	
2744 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2745 	
2746 	session.pending_local_max_concurrent_stream = 1;
2747 	
2748 	pri_spec = PrioritySpec(101, 10, 0);
2749 	
2750 	session.reprioritizeStream(stream, pri_spec);
2751 	
2752 	/* idle stream is not counteed to max concurrent streams */
2753 	
2754 	assert(10 == stream.weight);
2755 	assert(101 == stream.depPrev.id);
2756 	
2757 	stream = session.getStreamRaw(101);
2758 	
2759 	assert(DEFAULT_WEIGHT == stream.weight);
2760 	
2761 	session.free();
2762 }
2763 
2764 void test_submit_data() {
2765 	Session session;
2766 	Callbacks callbacks;
2767 	DataProvider data_prd;
2768 	MyUserData user_data = MyUserData(&session);
2769 	Frame* frame;
2770 	FrameHeader hd;
2771 	ActiveOutboundItem* aob;
2772 	Buffers framebufs;
2773 	Buffer* buf;
2774 
2775 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
2776 	
2777 	data_prd = &user_data.datasrc.readFixedLength;
2778 	user_data.data_source_length = DATA_PAYLOADLEN * 2;
2779 	session = new Session(CLIENT, *callbacks);
2780 	aob = &session.aob;
2781 	framebufs = aob.framebufs;
2782 	
2783 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2784 	assert(0 == submitData(session, FrameFlags.END_STREAM, 1, data_prd));
2785 	
2786 	user_data.block_count = 0;
2787 	assert(0 == session.send());
2788 	frame = &aob.item.frame;
2789 	
2790 	buf = &framebufs.head.buf;
2791 	hd.unpack((*buf)[]);
2792 	
2793 	assert(FrameFlags.NONE == hd.flags);
2794 	assert(FrameFlags.NONE == frame.hd.flags);
2795 	/* aux_data.data.flags has these flags */
2796 	assert(FrameFlags.END_STREAM == aob.item.aux_data.data.flags);
2797 	
2798 	session.free();
2799 }
2800 
2801 void test_submit_data_read_length_too_large() {
2802 	Session session;
2803 	Callbacks callbacks;
2804 	DataProvider data_prd;
2805 	MyUserData user_data = MyUserData(&session);
2806 	Frame* frame;
2807 	FrameHeader hd;
2808 	ActiveOutboundItem* aob;
2809 	Buffers framebufs;
2810 	Buffer* buf;
2811 	size_t payloadlen;
2812 	
2813 	
2814 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
2815 	callbacks.max_frame_size_cb = toDelegate(&MyCallbacks.tooLargeMaxFrameSize);
2816 	
2817 	data_prd = &user_data.datasrc.readFixedLength;
2818 	user_data.data_source_length = DATA_PAYLOADLEN * 2;
2819 	session = new Session(CLIENT, *callbacks);
2820 	aob = &session.aob;
2821 	framebufs = aob.framebufs;
2822 	
2823 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2824 	assert(0 == submitData(session, FrameFlags.END_STREAM, 1, data_prd));
2825 	
2826 	user_data.block_count = 0;
2827 	assert(0 == session.send());
2828 	frame = &aob.item.frame;
2829 	
2830 	buf = &framebufs.head.buf;
2831 	hd.unpack((*buf)[]);
2832 	
2833 	assert(FrameFlags.NONE == hd.flags);
2834 	assert(FrameFlags.NONE == frame.hd.flags);
2835 	assert(16384 == hd.length);
2836 	/* aux_data.data.flags has these flags */
2837 	assert(FrameFlags.END_STREAM == aob.item.aux_data.data.flags);
2838 	
2839 	session.free();
2840 	
2841 	/* Check that buffers are expanded */
2842 	session = new Session(CLIENT, *callbacks);
2843 	
2844 	user_data.data_source_length = MAX_FRAME_SIZE_MAX;
2845 	
2846 	session.remote_settings.max_frame_size = MAX_FRAME_SIZE_MAX;
2847 	
2848 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2849 	assert(0 == submitData(session, FrameFlags.END_STREAM, 1, data_prd));
2850 	
2851 	user_data.block_count = 0;
2852 	assert(0 == session.send());
2853 	
2854 	aob = &session.aob;
2855 	
2856 	frame = &aob.item.frame;
2857 	
2858 	framebufs = aob.framebufs;
2859 	
2860 	buf = &framebufs.head.buf;
2861 	hd.unpack((*buf)[]);
2862 	
2863 	payloadlen = min(INITIAL_CONNECTION_WINDOW_SIZE, INITIAL_WINDOW_SIZE);
2864 	
2865 	assert(FRAME_HDLEN + 1 + payloadlen == cast(size_t)buf.capacity, "Capacity error, got payloadlen " ~ payloadlen.to!string ~ " and capacity: " ~ buf.capacity.to!string);
2866 	assert(FrameFlags.NONE == hd.flags, "Flag error");
2867 	assert(FrameFlags.NONE == frame.hd.flags);
2868 	assert(payloadlen == hd.length);
2869 	/* aux_data.data.flags has these flags */
2870 	assert(FrameFlags.END_STREAM == aob.item.aux_data.data.flags);
2871 	
2872 	session.free();
2873 }
2874 
2875 void test_submit_data_read_length_smallest() {
2876 	Session session;
2877 	Callbacks callbacks;
2878 	DataProvider data_prd;
2879 	MyUserData user_data = MyUserData(&session);
2880 	Frame* frame;
2881 	FrameHeader hd;
2882 	ActiveOutboundItem* aob;
2883 	Buffers framebufs;
2884 	Buffer* buf;
2885 
2886 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
2887 	callbacks.max_frame_size_cb = toDelegate(&MyCallbacks.smallestMaxFrameSize);
2888 	
2889 	data_prd = &user_data.datasrc.readFixedLength;
2890 	user_data.data_source_length = DATA_PAYLOADLEN * 2;
2891 	session = new Session(CLIENT, *callbacks);
2892 	aob = &session.aob;
2893 	framebufs = aob.framebufs;
2894 	
2895 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2896 	assert(0 == submitData(session, FrameFlags.END_STREAM, 1, data_prd));
2897 	
2898 	user_data.block_count = 0;
2899 	assert(0 == session.send());
2900 	frame = &aob.item.frame;
2901 	
2902 	buf = &framebufs.head.buf;
2903 	hd.unpack((*buf)[]);
2904 	
2905 	assert(FrameFlags.NONE == hd.flags);
2906 	assert(FrameFlags.NONE == frame.hd.flags);
2907 	assert(1 == hd.length);
2908 	/* aux_data.data.flags has these flags */
2909 	assert(FrameFlags.END_STREAM == aob.item.aux_data.data.flags);
2910 	
2911 	session.free();
2912 }
2913 
2914 void test_submit_data_twice() {
2915 	Session session;
2916 	Callbacks callbacks;
2917 	DataProvider data_prd;
2918 	MyUserData user_data = MyUserData(&session);
2919 	Accumulator acc;
2920 	
2921 	
2922 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
2923 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSentTwice;
2924 	
2925 	data_prd = toDelegate(&MyDataSource.readTwice);
2926 	
2927 	acc.length = 0;
2928 	user_data.acc = &acc;
2929 	
2930 	session = new Session(CLIENT, *callbacks);
2931 	
2932 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
2933 	
2934 	assert(0 == submitData(session, FrameFlags.NONE, 1, data_prd));
2935 	
2936 	assert(0 == session.send());
2937 	
2938 	/* We should have sent 2 DATA frame with 16 bytes payload each */
2939 	assert(FRAME_HDLEN * 2 + 16 * 2 == acc.length);
2940 	
2941 	session.free();
2942 }
2943 
2944 void test_submit_request_with_data() {
2945 	Session session;
2946 	Callbacks callbacks;
2947 	DataProvider data_prd;
2948 	MyUserData user_data = MyUserData(&session);
2949 	OutboundItem item;
2950 
2951 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
2952 	
2953 	data_prd = &user_data.datasrc.readFixedLength;
2954 	user_data.data_source_length = 64 * 1024 - 1;
2955 	session = new Session(CLIENT, *callbacks);
2956 	assert(1 == submitRequest(session, pri_spec_default, reqhf, data_prd, null));
2957 	item = session.getNextOutboundItem();
2958 	assert(reqhf.length == item.frame.headers.hfa.length);
2959 	assert(reqhf.equals(item.frame.headers.hfa));
2960 	assert(0 == session.send());
2961 	assert(0 == user_data.data_source_length);
2962 	
2963 	session.free();
2964 }
2965 
2966 void test_submit_request_without_data() {
2967 	Session session;
2968 	Callbacks callbacks;
2969 	Accumulator acc;
2970 	DataProvider data_prd = null;
2971 	OutboundItem item;
2972 	MyUserData user_data = MyUserData(&session);
2973 	Frame frame;
2974 	Inflater inflater = Inflater(true);
2975 	HeaderFields output;
2976 	Buffers bufs = framePackBuffers();
2977 
2978 	acc.length = 0;
2979 	user_data.acc = &acc;
2980 	
2981 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
2982 	session = new Session(CLIENT, *callbacks);
2983 
2984 	assert(1 == submitRequest(session, pri_spec_default, reqhf, data_prd, null));
2985 	item = session.getNextOutboundItem();
2986 	assert(reqhf.length == item.frame.headers.hfa.length);
2987 	assert(reqhf.equals(item.frame.headers.hfa));
2988 	assert(item.frame.hd.flags & FrameFlags.END_STREAM);
2989 	
2990 	assert(0 == session.send());
2991 	frame.unpack(acc[]);
2992 
2993 	bufs.add(cast(string) acc[]);
2994 	output.inflate(inflater, bufs, FRAME_HDLEN);
2995 	
2996 	assert(reqhf.length == output.length);
2997 	assert(reqhf.equals(output[]));
2998 	frame.headers.free();
2999 	output.reset();
3000 	
3001 	bufs.free();
3002 	inflater.free();
3003 	session.free();
3004 }
3005 
3006 void test_submit_response_with_data() {
3007 	Session session;
3008 	Callbacks callbacks;
3009 	DataProvider data_prd;
3010 	MyUserData user_data = MyUserData(&session);
3011 	OutboundItem item;
3012 
3013 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3014 	
3015 	data_prd = &user_data.datasrc.readFixedLength;
3016 	user_data.data_source_length = 64 * 1024 - 1;
3017 	session = new Session(SERVER, *callbacks);
3018 	session.openStream(1, StreamFlags.PUSH, pri_spec_default, StreamState.OPENING, null);
3019 	assert(0 == submitResponse(session, 1, reshf, data_prd));
3020 	item = session.getNextOutboundItem();
3021 	assert(reshf.length == item.frame.headers.hfa.length);
3022 	assert(reshf.equals(item.frame.headers.hfa));
3023 	assert(0 == session.send());
3024 	assert(0 == user_data.data_source_length);
3025 	
3026 	session.free();
3027 }
3028 
3029 void test_submit_response_without_data() {
3030 	Session session;
3031 	Callbacks callbacks;
3032 	Accumulator acc;
3033 	DataProvider data_prd = null;
3034 	OutboundItem item;
3035 	MyUserData user_data = MyUserData(&session);
3036 	Frame frame;
3037 	Inflater inflater = Inflater(true);
3038 	HeaderFields output;
3039 	Buffers bufs = framePackBuffers();
3040 
3041 	acc.length = 0;
3042 	user_data.acc = &acc;
3043 	
3044 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
3045 	session = new Session(SERVER, *callbacks);
3046 
3047 	session.openStream(1, StreamFlags.PUSH, pri_spec_default, StreamState.OPENING, null);
3048 	assert(0 == submitResponse(session, 1, reshf, data_prd));
3049 	item = session.getNextOutboundItem();
3050 	assert(reshf.length == item.frame.headers.hfa.length);
3051 	assert(reshf.equals(item.frame.headers.hfa));
3052 	assert(item.frame.hd.flags & FrameFlags.END_STREAM);
3053 	
3054 	assert(0 == session.send());
3055 	frame.unpack(acc[]);
3056 	
3057 	bufs.add(cast(string)acc[]);
3058 	output.inflate(inflater, bufs, FRAME_HDLEN);
3059 	
3060 	assert(reshf.length == output.length);
3061 	assert(reshf.equals(output[]));
3062 	
3063 	output.reset();
3064 	bufs.free();
3065 	frame.headers.free();
3066 	inflater.free();
3067 	session.free();
3068 }
3069 
3070 void test_submit_headers_start_stream() {
3071 	Session session;
3072 	Callbacks callbacks;
3073 	OutboundItem item;
3074 
3075 	session = new Session(CLIENT, *callbacks);
3076 	assert(1 == submitHeaders(session, FrameFlags.END_STREAM, -1, pri_spec_default, reqhf));
3077 	item = session.getNextOutboundItem();
3078 	assert(reqhf.length == item.frame.headers.hfa.length);
3079 	assert(reqhf.equals(item.frame.headers.hfa));
3080 	assert(cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM) == item.frame.hd.flags);
3081 	assert(0 == (item.frame.hd.flags & FrameFlags.PRIORITY));
3082 	
3083 	session.free();
3084 }
3085 
3086 void test_submit_headers_reply() {
3087 	Session session;
3088 	Callbacks callbacks;
3089 	MyUserData user_data = MyUserData(&session);
3090 	OutboundItem item;
3091 	Stream stream;
3092 
3093 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3094 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3095 	
3096 	session = new Session(SERVER, *callbacks);
3097 	submitHeaders(session, FrameFlags.END_STREAM, 1, pri_spec_default, reshf);
3098 	item = session.getNextOutboundItem();
3099 	assert(reshf.length == item.frame.headers.hfa.length);
3100 	assert(reshf.equals(item.frame.headers.hfa));
3101 	assert(cast(FrameFlags)(FrameFlags.END_STREAM | FrameFlags.END_HEADERS) == item.frame.hd.flags);
3102 	
3103 	user_data.frame_send_cb_called = 0;
3104 	user_data.sent_frame_type = FrameType.init;
3105 	/* The transimission will be canceled because the stream 1 is not
3106      open. */
3107 	assert(0 == session.send());
3108 	assert(0 == user_data.frame_send_cb_called);
3109 	
3110 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3111 	
3112 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 1, pri_spec_default, reshf));
3113 	assert(0 == session.send());
3114 	assert(1 == user_data.frame_send_cb_called);
3115 	assert(FrameType.HEADERS == user_data.sent_frame_type);
3116 	assert(stream.shutFlags & ShutdownFlag.WR);
3117 	
3118 	session.free();
3119 }
3120 
3121 void test_submit_headers_push_reply() {
3122 	Session session;
3123 	Callbacks callbacks;
3124 	MyUserData user_data = MyUserData(&session);
3125 	Stream stream;
3126 	int foo;
3127 
3128 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3129 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3130 	
3131 	session = new Session(SERVER, *callbacks);
3132 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
3133 	assert(0 == submitHeaders(session, FrameFlags.NONE, 2, pri_spec_default, reshf, &foo));
3134 	
3135 	user_data.frame_send_cb_called = 0;
3136 	user_data.sent_frame_type = FrameType.init;
3137 	assert(0 == session.send());
3138 	assert(1 == user_data.frame_send_cb_called);
3139 	assert(FrameType.HEADERS == user_data.sent_frame_type);
3140 	assert(StreamState.OPENED == stream.state);
3141 	assert(&foo == session.getStreamUserData(stream.id));
3142 
3143 	session.free();
3144 	
3145 	/* Sending HEADERS from client against stream in reserved state is
3146      error */
3147 	session = new Session(CLIENT, *callbacks);
3148 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
3149 	assert(0 == submitHeaders(session, FrameFlags.NONE, 2, pri_spec_default, reqhf, null));
3150 	
3151 	user_data.frame_send_cb_called = 0;
3152 	user_data.sent_frame_type = FrameType.init;
3153 	assert(0 == session.send());
3154 	assert(0 == user_data.frame_send_cb_called);
3155 	
3156 	session.free();
3157 }
3158 
3159 void test_submit_headers() {
3160 	Session session;
3161 	Callbacks callbacks;
3162 	MyUserData user_data = MyUserData(&session);
3163 	OutboundItem item;
3164 	Stream stream;
3165 	Accumulator acc;
3166 	Frame frame;
3167 	Inflater inflater = Inflater(true);
3168 	HeaderFields output;
3169 	Buffers bufs = framePackBuffers();
3170 
3171 	acc.length = 0;
3172 	user_data.acc = &acc;
3173 	
3174 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
3175 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3176 	
3177 	session = new Session(CLIENT, *callbacks);
3178 
3179 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 1, pri_spec_default, reqhf));
3180 	item = session.getNextOutboundItem();
3181 	assert(reqhf.length == item.frame.headers.hfa.length);
3182 	assert(reqhf.equals(item.frame.headers.hfa));
3183 	assert(cast(FrameFlags)(FrameFlags.END_STREAM | FrameFlags.END_HEADERS) == item.frame.hd.flags);
3184 	
3185 	user_data.frame_send_cb_called = 0;
3186 	user_data.sent_frame_type = FrameType.init;
3187 	/* The transimission will be canceled because the stream 1 is not open. */
3188 	assert(0 == session.send());
3189 	assert(0 == user_data.frame_send_cb_called);
3190 	
3191 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3192 	
3193 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 1, pri_spec_default, reqhf));
3194 	assert(0 == session.send());
3195 	assert(1 == user_data.frame_send_cb_called);
3196 	assert(FrameType.HEADERS == user_data.sent_frame_type);
3197 	assert(stream.shutFlags & ShutdownFlag.WR);
3198 	
3199 	frame.unpack(acc[]);
3200 	
3201 	bufs.add(cast(string)acc[]);
3202 	output.inflate(inflater, bufs, FRAME_HDLEN);
3203 	
3204 	assert(reqhf.length == output.length);
3205 	assert(reqhf.equals(output[]));
3206 	
3207 	output.reset();
3208 	bufs.free();
3209 	frame.headers.free();
3210 	
3211 	inflater.free();
3212 	session.free();
3213 }
3214 
3215 void test_submit_headers_continuation() {
3216 	Session session;
3217 	Callbacks callbacks;
3218 	HeaderField[] hfa = [HeaderField("h1", ""), HeaderField("h1", ""), HeaderField("h1", ""), HeaderField("h1", ""), 
3219 		HeaderField("h1", ""), HeaderField("h1", ""), HeaderField("h1", "")];
3220 	OutboundItem item;
3221 	ubyte[4096] data;
3222 	size_t i;
3223 	MyUserData user_data = MyUserData(&session);
3224 
3225 	foreach(ref hf; hfa)
3226 		hf.value = cast(string)data;
3227 	
3228 	
3229 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3230 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3231 	
3232 	session = new Session(CLIENT, *callbacks);
3233 	assert(1 == submitHeaders(session, FrameFlags.END_STREAM, -1, pri_spec_default, hfa, null));
3234 	item = session.getNextOutboundItem();
3235 	assert(FrameType.HEADERS == item.frame.hd.type);
3236 	assert(cast(FrameFlags)(FrameFlags.END_STREAM | FrameFlags.END_HEADERS) == item.frame.hd.flags);
3237 	assert(0 == (item.frame.hd.flags & FrameFlags.PRIORITY));
3238 	
3239 	user_data.frame_send_cb_called = 0;
3240 	assert(0 == session.send());
3241 	assert(1 == user_data.frame_send_cb_called);
3242 	
3243 	session.free();
3244 }
3245 
3246 void test_submit_priority() {
3247 	Session session;
3248 	Callbacks callbacks;
3249 	Stream stream;
3250 	MyUserData user_data = MyUserData(&session);
3251 	PrioritySpec pri_spec;
3252 	
3253 	
3254 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3255 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3256 	
3257 	session = new Session(CLIENT, *callbacks);
3258 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3259 	
3260 	pri_spec = PrioritySpec(0, 3, 0);
3261 	
3262 	/* depends on stream 0 */
3263 	assert(0 == submitPriority(session, 1, pri_spec));
3264 	assert(0 == session.send());
3265 	assert(3 == stream.weight);
3266 	
3267 	/* submit against idle stream */
3268 	assert(0 == submitPriority(session, 3, pri_spec));
3269 	
3270 	user_data.frame_send_cb_called = 0;
3271 	assert(0 == session.send());
3272 	assert(1 == user_data.frame_send_cb_called);
3273 	
3274 	session.free();
3275 }
3276 
3277 void test_submit_settings() {
3278 	Session session;
3279 	Callbacks callbacks;
3280 	MyUserData user_data = MyUserData(&session);
3281 	OutboundItem item;
3282 	Frame* frame;
3283 	Setting[7] iva;
3284 	Frame ack_frame;
3285 	const int UNKNOWN_ID = 1000000007;
3286  
3287 	iva[0].id = Setting.MAX_CONCURRENT_STREAMS;
3288 	iva[0].value = 5;
3289 	
3290 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
3291 	iva[1].value = 16 * 1024;
3292 	
3293 	iva[2].id = Setting.MAX_CONCURRENT_STREAMS;
3294 	iva[2].value = 50;
3295 	
3296 	iva[3].id = Setting.HEADER_TABLE_SIZE;
3297 	iva[3].value = 0;
3298 	
3299 	iva[4].id = cast(ushort)UNKNOWN_ID;
3300 	iva[4].value = 999;
3301 	
3302 	iva[5].id = Setting.INITIAL_WINDOW_SIZE;
3303 	iva[5].value = cast(uint)MAX_WINDOW_SIZE + 1;
3304 	
3305 	
3306 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3307 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3308 	session = new Session(SERVER, *callbacks);
3309 
3310 	assert(ErrorCode.INVALID_ARGUMENT == submitSettings(session, iva[0 .. 6]));
3311 	
3312 	/* Make sure that local settings are not changed */
3313 	assert(INITIAL_MAX_CONCURRENT_STREAMS == session.local_settings.max_concurrent_streams);
3314 	assert(INITIAL_WINDOW_SIZE == session.local_settings.initial_window_size);
3315 	
3316 	/* Now sends without 6th one */
3317 	assert(0 == submitSettings(session, iva[0 .. 5]));
3318 	
3319 	item = session.getNextOutboundItem();
3320 	
3321 	assert(FrameType.SETTINGS == item.frame.hd.type);
3322 
3323 	frame = &item.frame;
3324 	assert(5 == frame.settings.iva.length);
3325 	assert(5 == frame.settings.iva[0].value);
3326 	assert(Setting.MAX_CONCURRENT_STREAMS == frame.settings.iva[0].id);
3327 	
3328 	assert(16 * 1024 == frame.settings.iva[1].value);
3329 	assert(Setting.INITIAL_WINDOW_SIZE == frame.settings.iva[1].id);
3330 	
3331 	assert(cast(ushort)UNKNOWN_ID == frame.settings.iva[4].id);
3332 	assert(999 == frame.settings.iva[4].value);
3333 	
3334 	user_data.frame_send_cb_called = 0;
3335 	assert(0 == session.send());
3336 	assert(1 == user_data.frame_send_cb_called);
3337 	
3338 	assert(50 == session.pending_local_max_concurrent_stream);
3339 	
3340 	ack_frame.settings = Settings(FrameFlags.ACK, null);
3341 	assert(0 == session.onSettings(ack_frame, false));
3342 	ack_frame.settings.free();
3343 	
3344 	assert(16 * 1024 == session.local_settings.initial_window_size);
3345 	assert(0 == session.hd_inflater.ctx.hd_table_bufsize_max);
3346 	assert(50 == session.local_settings.max_concurrent_streams);
3347 	assert(INITIAL_MAX_CONCURRENT_STREAMS == session.pending_local_max_concurrent_stream);
3348 	
3349 	session.free();
3350 }
3351 
3352 void test_submit_settings_update_local_window_size() {
3353 	Session session;
3354 	Callbacks callbacks;
3355 	OutboundItem item;
3356 	Setting[4] iva;
3357 	Stream stream;
3358 	Frame ack_frame;
3359 
3360 	ack_frame.settings = Settings(FrameFlags.ACK, null);
3361 	
3362 	iva[0].id = Setting.INITIAL_WINDOW_SIZE;
3363 	iva[0].value = 16 * 1024;
3364 	
3365 	
3366 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3367 	
3368 	session = new Session(SERVER, *callbacks);
3369 	
3370 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3371 	stream.localWindowSize = INITIAL_WINDOW_SIZE + 100;
3372 	stream.recvWindowSize = 32768;
3373 
3374 	stream = session.openStream(3, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3375 	
3376 	assert(0 == submitSettings(session, iva[0 .. 1]));
3377 	assert(0 == session.send());
3378 	assert(0 == session.onSettings(ack_frame, false));
3379 	
3380 	stream = session.getStream(1);
3381 	assert(0 == stream.recvWindowSize);
3382 	assert(16 * 1024 + 100 == stream.localWindowSize);
3383 	
3384 	stream = session.getStream(3);
3385 	assert(16 * 1024 == stream.localWindowSize);
3386 	
3387 	item = session.getNextOutboundItem();
3388 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3389 	assert(32768 == item.frame.window_update.window_size_increment);
3390 	
3391 	session.free();
3392 	
3393 	/* Check overflow case */
3394 	iva[0].value = 128 * 1024;
3395 	session = new Session(SERVER, *callbacks);
3396 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3397 	stream.localWindowSize = MAX_WINDOW_SIZE;
3398 	
3399 	assert(0 == submitSettings(session, iva[0 .. 1]));
3400 	assert(0 == session.send());
3401 	assert(0 == session.onSettings(ack_frame, false));
3402 	
3403 	item = session.getNextOutboundItem();
3404 	assert(FrameType.GOAWAY == item.frame.hd.type, "Expected GoAway frame type, got " ~ item.frame.hd.type.to!string);
3405 	assert(FrameError.FLOW_CONTROL_ERROR == item.frame.goaway.error_code, "Expected a Flow control error, got " ~ item.frame.goaway.error_code.to!string);
3406 	
3407 	session.free();
3408 	ack_frame.settings.free();
3409 }
3410 
3411 void test_submit_push_promise() {
3412 	Session session;
3413 	Callbacks callbacks;
3414 	MyUserData user_data = MyUserData(&session);
3415 	Stream stream;
3416 	
3417 	
3418 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3419 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3420 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
3421 	
3422 	session = new Session(SERVER, *callbacks);
3423 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3424 	assert(2 == submitPushPromise(session, 1, reqhf, &user_data));
3425 	
3426 	user_data.frame_send_cb_called = 0;
3427 	user_data.sent_frame_type = FrameType.init;
3428 	assert(0 == session.send());
3429 	assert(1 == user_data.frame_send_cb_called);
3430 	assert(FrameType.PUSH_PROMISE == user_data.sent_frame_type);
3431 	stream = session.getStream(2);
3432 	assert(StreamState.RESERVED == stream.state);
3433 	assert(&user_data == session.getStreamUserData(2));
3434 	
3435 	/* submit PUSH_PROMISE while associated stream is not opened */
3436 	assert(4 == submitPushPromise(session, 3, reqhf, &user_data));
3437 	
3438 	user_data.frame_not_send_cb_called = 0;
3439 	
3440 	assert(0 == session.send());
3441 	assert(1 == user_data.frame_not_send_cb_called);
3442 	assert(FrameType.PUSH_PROMISE == user_data.not_sent_frame_type);
3443 	
3444 	stream = session.getStream(4);
3445 	
3446 	assert(!stream);
3447 	
3448 	session.free();
3449 }
3450 
3451 void test_submit_window_update() {
3452 	Session session;
3453 	Callbacks callbacks;
3454 	MyUserData user_data = MyUserData(&session);
3455 	OutboundItem item;
3456 	Stream stream;
3457 	
3458 
3459 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3460 	
3461 	session = new Session(CLIENT, *callbacks);
3462 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3463 	stream.recvWindowSize = 4096;
3464 	
3465 	assert(0 == submitWindowUpdate(session, 2, 1024));
3466 	item = session.getNextOutboundItem();
3467 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3468 	assert(1024 == item.frame.window_update.window_size_increment);
3469 	assert(0 == session.send());
3470 	assert(3072 == stream.recvWindowSize);
3471 	
3472 	assert(0 == submitWindowUpdate(session, 2, 4096));
3473 	item = session.getNextOutboundItem();
3474 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3475 	assert(4096 == item.frame.window_update.window_size_increment);
3476 	assert(0 == session.send());
3477 	assert(0 == stream.recvWindowSize);
3478 	
3479 	assert(0 == submitWindowUpdate(session, 2, 4096));
3480 	item = session.getNextOutboundItem();
3481 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3482 	assert(4096 == item.frame.window_update.window_size_increment);
3483 	assert(0 == session.send());
3484 	assert(0 == stream.recvWindowSize);
3485 	
3486 	assert(0 == submitWindowUpdate(session, 2, 0));
3487 	/* It is ok if stream is closed or does not exist at the call
3488      time */
3489 	assert(0 == submitWindowUpdate(session, 4, 4096));
3490 	
3491 	session.free();
3492 }
3493 
3494 void test_submit_window_update_local_window_size() {
3495 	Session session;
3496 	Callbacks callbacks;
3497 	OutboundItem item;
3498 	Stream stream;
3499 
3500 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3501 	
3502 	session = new Session(CLIENT, *callbacks);
3503 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3504 	stream.recvWindowSize = 4096;
3505 	
3506 	assert(0 == submitWindowUpdate(session, 2, stream.recvWindowSize + 1));
3507 	assert(INITIAL_WINDOW_SIZE + 1 == stream.localWindowSize);
3508 	assert(0 == stream.recvWindowSize);
3509 	item = session.getNextOutboundItem();
3510 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3511 	assert(4097 == item.frame.window_update.window_size_increment);
3512 	
3513 	assert(0 == session.send());
3514 	
3515 	/* Let's decrement local window size */
3516 	stream.recvWindowSize = 4096;
3517 	assert(0 == submitWindowUpdate(session, 2, -stream.localWindowSize / 2));
3518 	assert(32768 == stream.localWindowSize);
3519 	assert(-28672 == stream.recvWindowSize);
3520 	assert(32768 == stream.recvReduction);
3521 
3522 	item = session.getNextOutboundItem();
3523 	assert(!item);
3524 	
3525 	/* Increase local window size */
3526 	assert(0 == submitWindowUpdate(session, 2, 16384));
3527 	assert(49152 == stream.localWindowSize);
3528 	assert(-12288 == stream.recvWindowSize);
3529 	assert(16384 == stream.recvReduction);
3530 	assert(!session.getNextOutboundItem());
3531 	
3532 	assert(ErrorCode.FLOW_CONTROL == submitWindowUpdate(session, 2, MAX_WINDOW_SIZE));
3533 	
3534 	assert(0 == session.send());
3535 	
3536 	/* Check connection-level flow control */
3537 	session.recv_window_size = 4096;
3538 	assert(0 == submitWindowUpdate(session, 0,	session.recv_window_size + 1));
3539 	assert(INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
3540 		session.local_window_size);
3541 	assert(0 == session.recv_window_size);
3542 	item = session.getNextOutboundItem();
3543 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
3544 	assert(4097 == item.frame.window_update.window_size_increment);
3545 	
3546 	assert(0 == session.send());
3547 	
3548 	/* Go decrement part */
3549 	session.recv_window_size = 4096;
3550 	assert(0 == submitWindowUpdate(session, 0, -session.local_window_size / 2));
3551 	assert(32768 == session.local_window_size);
3552 	assert(-28672 == session.recv_window_size);
3553 	assert(32768 == session.recv_reduction);
3554 	item = session.getNextOutboundItem();
3555 	assert(!item);
3556 	
3557 	/* Increase local window size */
3558 	assert(0 == submitWindowUpdate(session, 0, 16384));
3559 	assert(49152 == session.local_window_size);
3560 	assert(-12288 == session.recv_window_size);
3561 	assert(16384 == session.recv_reduction);
3562 	assert(!session.getNextOutboundItem());
3563 	
3564 	assert(ErrorCode.FLOW_CONTROL == submitWindowUpdate(session, 0, MAX_WINDOW_SIZE));
3565 	
3566 	session.free();
3567 }
3568 
3569 void test_submit_shutdown_notice() {
3570 	Session session;
3571 	Callbacks callbacks;
3572 	MyUserData user_data = MyUserData(&session);
3573 
3574 	
3575 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3576 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3577 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
3578 	
3579 	session = new Session(SERVER, *callbacks);
3580 	
3581 	assert(0 == submitShutdownNotice(session));
3582 	
3583 	user_data.frame_send_cb_called = 0;
3584 	
3585 	session.send();
3586 	
3587 	assert(1 == user_data.frame_send_cb_called);
3588 	assert(FrameType.GOAWAY == user_data.sent_frame_type);
3589 	assert((1u << 31) - 1 == session.local_last_stream_id);
3590 	
3591 	/* After another GOAWAY, submitShutdownNotice() is noop. */
3592 	assert(0 == session.terminateSession(FrameError.NO_ERROR));
3593 	
3594 	user_data.frame_send_cb_called = 0;
3595 	
3596 	session.send();
3597 	
3598 	assert(1 == user_data.frame_send_cb_called);
3599 	assert(FrameType.GOAWAY == user_data.sent_frame_type);
3600 	assert(0 == session.local_last_stream_id);
3601 	
3602 	assert(0 == submitShutdownNotice(session));
3603 	
3604 	user_data.frame_send_cb_called = 0;
3605 	user_data.frame_not_send_cb_called = 0;
3606 	
3607 	session.send();
3608 	
3609 	assert(0 == user_data.frame_send_cb_called);
3610 	assert(0 == user_data.frame_not_send_cb_called);
3611 	
3612 	session.free();
3613 	
3614 	/* Using submitShutdownNotice() with client side session is error */
3615 	session = new Session(CLIENT, *callbacks);
3616 	
3617 	assert(ErrorCode.INVALID_STATE == submitShutdownNotice(session));
3618 	
3619 	session.free();
3620 }
3621 
3622 void test_submit_invalid_hf() {
3623 	Session session;
3624 	Callbacks callbacks;
3625 	HeaderField[] empty_name_hfa = [HeaderField("Version", "HTTP/1.1"), HeaderField("", "empty name")];
3626 	
3627 	/* Now invalid header field from HTTP/1.1 is accepted in libhttp2 */
3628 	session = new Session(SERVER, *callbacks);
3629 
3630 	/* submitRequest */
3631 	assert(0 < submitRequest(session, pri_spec_default, empty_name_hfa, DataProvider.init, null));
3632 	
3633 	/* submitResponse */
3634 	assert(0 == submitResponse(session, 2, empty_name_hfa, DataProvider.init));
3635 	
3636 	/* submitHeaders */
3637 	assert(0 < submitHeaders(session, FrameFlags.NONE, -1, pri_spec_default, empty_name_hfa));
3638 	
3639 	/* submitPushPromise */
3640 	openStream(session, 1);
3641 	
3642 	assert(0 < submitPushPromise(session, 1, empty_name_hfa, null));
3643 	
3644 	session.free();
3645 }
3646 
3647 void test_session_open_stream() {
3648 	Session session;
3649 	Callbacks callbacks;
3650 	Stream stream;
3651 	PrioritySpec pri_spec;
3652 	
3653 	
3654 	session = new Session(SERVER, *callbacks);
3655 	
3656 	pri_spec = PrioritySpec(0, 245, 0);
3657 	
3658 	stream = session.openStream(1, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3659 	assert(1 == session.num_incoming_streams);
3660 	assert(0 == session.num_outgoing_streams);
3661 	assert(StreamState.OPENED == stream.state);
3662 	assert(245 == stream.weight);
3663 	assert(!stream.depPrev);
3664 	assert(ShutdownFlag.NONE == stream.shutFlags);
3665 	
3666 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3667 	assert(1 == session.num_incoming_streams);
3668 	assert(1 == session.num_outgoing_streams);
3669 	assert(!stream.depPrev);
3670 	assert(DEFAULT_WEIGHT == stream.weight);
3671 	assert(ShutdownFlag.NONE == stream.shutFlags);
3672 
3673 	stream = session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
3674 	assert(1 == session.num_incoming_streams);
3675 	assert(1 == session.num_outgoing_streams);
3676 	assert(!stream.depPrev);
3677 	assert(DEFAULT_WEIGHT == stream.weight);
3678 	assert(ShutdownFlag.RD == stream.shutFlags);
3679 	
3680 	pri_spec = PrioritySpec(1, 17, 1);
3681 	
3682 	stream = session.openStream(3, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3683 	assert(17 == stream.weight);
3684 	assert(1 == stream.depPrev.id);
3685 	
3686 	/* Dependency to idle stream */
3687 	pri_spec = PrioritySpec(1000000007, 240, 1);
3688 
3689 	stream = session.openStream(5, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3690 	assert(240 == stream.weight);
3691 	assert(1000000007 == stream.depPrev.id);
3692 	
3693 	stream = session.getStreamRaw(1000000007);
3694 	
3695 	assert(DEFAULT_WEIGHT == stream.weight);
3696 	assert(stream.rootNext);
3697 	
3698 	/* Dependency to closed stream which is not in dependency tree */
3699 	session.last_recv_stream_id = 7;
3700 	
3701 	pri_spec = PrioritySpec(7, 10, 0);
3702 	
3703 	stream = session.openStream(9, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3704 	
3705 	assert(DEFAULT_WEIGHT == stream.weight);
3706 	
3707 	session.free();
3708 	
3709 	session = new Session(CLIENT, *callbacks);
3710 	stream = session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
3711 	assert(0 == session.num_incoming_streams);
3712 	assert(0 == session.num_outgoing_streams);
3713 	assert(!stream.depPrev);
3714 	assert(DEFAULT_WEIGHT == stream.weight);
3715 	assert(ShutdownFlag.WR == stream.shutFlags);
3716 	
3717 	session.free();
3718 }
3719 
3720 void test_session_open_stream_with_idle_stream_dep() {
3721 	Session session;
3722 	Callbacks callbacks;
3723 	Stream stream;
3724 	PrioritySpec pri_spec;
3725 	
3726 	
3727 	session = new Session(SERVER, *callbacks);
3728 	
3729 	/* Dependency to idle stream */
3730 	pri_spec = PrioritySpec(101, 245, 0);
3731 
3732 	
3733 	stream = session.openStream(1, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3734 	
3735 	assert(245 == stream.weight);
3736 	assert(101 == stream.depPrev.id);
3737 	
3738 	stream = session.getStreamRaw(101);
3739 	
3740 	assert(StreamState.IDLE == stream.state);
3741 	assert(DEFAULT_WEIGHT == stream.weight);
3742 	
3743 	pri_spec = PrioritySpec(211, 1, 0);
3744 
3745 	/* stream 101 was already created as idle. */
3746 	stream = session.openStream(101, StreamFlags.NONE, pri_spec, StreamState.OPENED, null);
3747 	
3748 	assert(1 == stream.weight);
3749 	assert(211 == stream.depPrev.id);
3750 
3751 	stream = session.getStreamRaw(211);
3752 	
3753 	assert(StreamState.IDLE == stream.state);
3754 	assert(DEFAULT_WEIGHT == stream.weight);
3755 	
3756 	session.free();
3757 }
3758 
3759 void test_session_get_next_ob_item() {
3760 	Session session;
3761 	Callbacks callbacks;
3762 	PrioritySpec pri_spec;
3763 	
3764 	
3765 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3766 	
3767 	session = new Session(SERVER, *callbacks);
3768 	session.remote_settings.max_concurrent_streams = 2;
3769 	
3770 	assert(!session.getNextOutboundItem());
3771 	submitPing(session, null);
3772 	assert(FrameType.PING == session.getNextOutboundItem().frame.hd.type);
3773 	
3774 	submitRequest(session, pri_spec_default, null, DataProvider.init, null);
3775 	assert(FrameType.PING == session.getNextOutboundItem().frame.hd.type);
3776 	
3777 	assert(0 == session.send());
3778 	assert(!session.getNextOutboundItem());
3779 	
3780 	/* Incoming stream does not affect the number of outgoing max
3781      concurrent streams. */
3782 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3783 	
3784 	pri_spec = PrioritySpec(0, MAX_WEIGHT, 0);
3785 
3786 	submitRequest(session, pri_spec, null, DataProvider.init, null);
3787 	assert(FrameType.HEADERS == session.getNextOutboundItem().frame.hd.type);
3788 	assert(0 == session.send());
3789 	
3790 	submitRequest(session, pri_spec, null, DataProvider.init, null);
3791 	assert(!session.getNextOutboundItem());
3792 	
3793 	session.remote_settings.max_concurrent_streams = 3;
3794 	
3795 	assert(FrameType.HEADERS == session.getNextOutboundItem().frame.hd.type);
3796 
3797 	session.free();
3798 }
3799 
3800 void test_session_pop_next_ob_item() {
3801 	Session session;
3802 	Callbacks callbacks;
3803 	OutboundItem item;
3804 	PrioritySpec pri_spec;
3805 	Stream stream;
3806 		
3807 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3808 	
3809 	session = new Session(SERVER, *callbacks);
3810 	session.remote_settings.max_concurrent_streams = 1;
3811 	
3812 	assert(!session.popNextOutboundItem());
3813 	
3814 	submitPing(session, null);
3815 	
3816 	pri_spec = PrioritySpec(0, 254, 0);
3817 	
3818 	submitRequest(session, pri_spec, null, DataProvider.init, null);
3819 	
3820 	item = session.popNextOutboundItem();
3821 	assert(FrameType.PING == item.frame.hd.type);
3822 	item.free();
3823 	Mem.free(item);
3824 	
3825 	item = session.popNextOutboundItem();
3826 	assert(FrameType.HEADERS == item.frame.hd.type);
3827 	item.free();
3828 	Mem.free(item);
3829 	
3830 	assert(!session.popNextOutboundItem());
3831 	
3832 	/* Incoming stream does not affect the number of outgoing max concurrent streams. */
3833 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3834 	/* In-flight outgoing stream */
3835 	session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3836 
3837 	pri_spec = PrioritySpec(0, MAX_WEIGHT, 0);
3838 	
3839 	submitRequest(session, pri_spec, null, DataProvider.init, null);
3840 	submitResponse(session, 1, null, DataProvider.init);
3841 	
3842 	item = session.popNextOutboundItem();
3843 	assert(FrameType.HEADERS == item.frame.hd.type);
3844 	assert(1 == item.frame.hd.stream_id);
3845 	
3846 	stream = session.getStream(1);
3847 	
3848 	stream.detachItem(session);
3849 	
3850 	item.free();
3851 	Mem.free(item);
3852 	
3853 	assert(!session.popNextOutboundItem());
3854 	
3855 	session.remote_settings.max_concurrent_streams = 2;
3856 	
3857 	item = session.popNextOutboundItem();
3858 	assert(FrameType.HEADERS == item.frame.hd.type);
3859 	item.free();
3860 	Mem.free(item);
3861 	
3862 	session.free();
3863 	
3864 	/* Check that push reply HEADERS are queued into ob_ss_pq */
3865 	session = new Session(SERVER, *callbacks);
3866 	session.remote_settings.max_concurrent_streams = 0;
3867 	session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
3868 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 2));
3869 	assert(!session.popNextOutboundItem());
3870 	assert(1 == session.ob_ss_pq.length);
3871 	session.free();
3872 }
3873 
3874 void test_session_reply_fail() {
3875 	Session session;
3876 	Callbacks callbacks;
3877 	DataProvider data_prd;
3878 	MyUserData user_data = MyUserData(&session);
3879 
3880 	callbacks.write_cb = toDelegate(&MyCallbacks.writeFailure);
3881 	
3882 	data_prd = &user_data.datasrc.readFixedLength;
3883 	user_data.data_source_length = 4 * 1024;
3884 	session = new Session(SERVER, *callbacks);
3885 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3886 	assert(0 == submitResponse(session, 1, null, data_prd));
3887 	assert(ErrorCode.CALLBACK_FAILURE == session.send());
3888 	session.free();
3889 }
3890 
3891 void test_session_max_concurrent_streams() {
3892 	Session session;
3893 	Callbacks callbacks;
3894 	Frame frame;
3895 	OutboundItem item;
3896 
3897 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
3898 	
3899 	session = new Session(SERVER, *callbacks);
3900 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
3901 	
3902 	/* Check un-ACKed Setting.MAX_CONCURRENT_STREAMS */
3903 	frame.headers = Headers(FrameFlags.END_HEADERS, 3, HeadersCategory.HEADERS, pri_spec_default, null);
3904 	session.pending_local_max_concurrent_stream = 1;
3905 	
3906 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
3907 
3908 	item = session.ob_pq_top;
3909 	assert(FrameType.RST_STREAM == item.frame.hd.type);
3910 	assert(FrameError.REFUSED_STREAM == item.frame.rst_stream.error_code);
3911 	
3912 	assert(0 == session.send());
3913 	
3914 	/* Check ACKed Setting.MAX_CONCURRENT_STREAMS */
3915 	session.local_settings.max_concurrent_streams = 1;
3916 	frame.hd.stream_id = 5;
3917 	
3918 	assert(ErrorCode.IGN_HEADER_BLOCK == session.onRequestHeaders(frame));
3919 	
3920 	item = session.ob_pq_top;
3921 	assert(FrameType.GOAWAY == item.frame.hd.type);
3922 	assert(FrameError.PROTOCOL_ERROR == item.frame.goaway.error_code);
3923 	
3924 	frame.headers.free();
3925 	session.free();
3926 }
3927 
3928 void test_session_stop_data_with_rst_stream() {
3929 	Session session;
3930 	Callbacks callbacks;
3931 	MyUserData user_data = MyUserData(&session);
3932 	DataProvider data_prd;
3933 	Frame frame;
3934 
3935 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3936 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
3937 	data_prd = &user_data.datasrc.readFixedLength;
3938 	
3939 	user_data.frame_send_cb_called = 0;
3940 	user_data.data_source_length = DATA_PAYLOADLEN * 4;
3941 
3942 	session = new Session(SERVER, *callbacks);
3943 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3944 	submitResponse(session, 1, null, data_prd);
3945 	
3946 	user_data.block_count = 2;
3947 	/* Sends response HEADERS + DATA[0] */
3948 	assert(0 == session.send());
3949 	assert(FrameType.DATA == user_data.sent_frame_type);
3950 	/* data for DATA[1] is read from data_prd but it is not sent */
3951 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 2);
3952 	
3953 	frame.rst_stream = RstStream(1, FrameError.CANCEL);
3954 	assert(0 == session.onRstStream(frame));
3955 	frame.rst_stream.free();
3956 	
3957 	/* Big enough number to send all DATA frames potentially. */
3958 	user_data.block_count = 100;
3959 	/* Nothing will be sent in the following call. */
3960 	assert(0 == session.send());
3961 	/* With RST_STREAM, stream is canceled and further DATA on that stream are not sent. */
3962 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 2);
3963 	
3964 	assert(!session.getStream(1));
3965 	
3966 	session.free();
3967 }
3968 
3969 void test_session_defer_data() {
3970 	Session session;
3971 	Callbacks callbacks;
3972 	MyUserData user_data = MyUserData(&session);
3973 	DataProvider data_prd;
3974 	OutboundItem item;
3975 	Stream stream;
3976 		
3977 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
3978 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
3979 	data_prd = toDelegate(&MyDataSource.readDeferred);
3980 	
3981 	user_data.frame_send_cb_called = 0;
3982 	user_data.data_source_length = DATA_PAYLOADLEN * 4;
3983 
3984 	session = new Session(SERVER, *callbacks);
3985 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
3986 	
3987 	session.remote_window_size = 1 << 20;
3988 	stream.remoteWindowSize = 1 << 20;
3989 	
3990 	submitResponse(session, 1, null, data_prd);
3991 	
3992 	user_data.block_count = 1;
3993 	/* Sends HEADERS reply */
3994 	assert(0 == session.send());
3995 	assert(FrameType.HEADERS == user_data.sent_frame_type);
3996 	/* No data is read */
3997 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 4);
3998 	
3999 	user_data.block_count = 1;
4000 	submitPing(session, null);
4001 	/* Sends PING */
4002 	assert(0 == session.send());
4003 	assert(FrameType.PING == user_data.sent_frame_type);
4004 	
4005 	/* Resume deferred DATA */
4006 	assert(0 == session.resumeData(1));
4007 	item = session.ob_da_pq.top();
4008 	item.aux_data.data.data_prd = &user_data.datasrc.readFixedLength;
4009 	user_data.block_count = 1;
4010 	/* Reads 2 DATA chunks */
4011 	assert(0 == session.send());
4012 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 2);
4013 	
4014 	/* Deferred again */
4015 	item.aux_data.data.data_prd = toDelegate(&MyDataSource.readDeferred);
4016 	/* This is needed since 16KiB block is already read and waiting to be
4017      sent. No read_callback invocation. */
4018 	user_data.block_count = 1;
4019 	assert(0 == session.send());
4020 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 2);
4021 	
4022 	/* Resume deferred DATA */
4023 	assert(0 == session.resumeData(1));
4024 	item = session.ob_da_pq.top();
4025 	item.aux_data.data.data_prd = &user_data.datasrc.readFixedLength;
4026 	user_data.block_count = 1;
4027 	/* Reads 2 16KiB blocks */
4028 	assert(0 == session.send());
4029 	assert(user_data.data_source_length == 0);
4030 	
4031 	session.free();
4032 }
4033 
4034 void test_session_flow_control() {
4035 	Session session;
4036 	Callbacks callbacks;
4037 	MyUserData user_data = MyUserData(&session);
4038 	DataProvider data_prd;
4039 	Frame frame;
4040 	Stream stream;
4041 	int new_initial_window_size;
4042 	Setting[1] iva;
4043 	Frame settings_frame;
4044 		
4045 	callbacks.write_cb = &user_data.cb_handlers.writeFixedBytes;
4046 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
4047 	data_prd = &user_data.datasrc.readFixedLength;
4048 	
4049 	user_data.frame_send_cb_called = 0;
4050 	user_data.data_source_length = 128 * 1024;
4051 	/* Use smaller emission count so that we can check outbound flow
4052      control window calculation is correct. */
4053 	user_data.fixed_sendlen = 2 * 1024;
4054 	
4055 	/* Initial window size to 64KiB - 1*/
4056 	session = new Session(CLIENT, *callbacks);
4057 	/* Change it to 64KiB for easy calculation */
4058 	session.remote_window_size = 64 * 1024;
4059 	session.remote_settings.initial_window_size = 64 * 1024;
4060 	
4061 	submitRequest(session, pri_spec_default, null, data_prd, null);
4062 
4063 	/* Sends 64KiB - 1 data */
4064 	assert(0 == session.send());
4065 	assert(64 * 1024 == user_data.data_source_length, "Wrong data source length: " ~ user_data.data_source_length.to!string);
4066 	
4067 	/* Back 32KiB in stream window */
4068 	frame.window_update = WindowUpdate(FrameFlags.NONE, 1, 32 * 1024);
4069 	session.onWindowUpdate(frame);
4070 	
4071 	/* Send nothing because of connection-level window */
4072 	assert(0 == session.send());
4073 	assert(64 * 1024 == user_data.data_source_length);
4074 	
4075 	/* Back 32KiB in connection-level window */
4076 	frame.hd.stream_id = 0;
4077 	session.onWindowUpdate(frame);
4078 	
4079 	/* Sends another 32KiB data */
4080 	assert(0 == session.send());
4081 	assert(32 * 1024 == user_data.data_source_length);
4082 	
4083 	stream = session.getStream(1);
4084 	/* Change initial window size to 16KiB. The window_size becomes
4085      negative. */
4086 	new_initial_window_size = 16 * 1024;
4087 	stream.remoteWindowSize = new_initial_window_size - (session.remote_settings.initial_window_size - stream.remoteWindowSize);
4088 	session.remote_settings.initial_window_size = new_initial_window_size;
4089 	assert(-48 * 1024 == stream.remoteWindowSize);
4090 	
4091 	/* Back 48KiB to stream window */
4092 	frame.hd.stream_id = 1;
4093 	frame.window_update.window_size_increment = 48 * 1024;
4094 	session.onWindowUpdate(frame);
4095 	
4096 	/* Nothing is sent because window_size is 0 */
4097 	assert(0 == session.send());
4098 	assert(32 * 1024 == user_data.data_source_length);
4099 	
4100 	/* Back 16KiB in stream window */
4101 	frame.hd.stream_id = 1;
4102 	frame.window_update.window_size_increment = 16 * 1024;
4103 	session.onWindowUpdate(frame);
4104 	
4105 	/* Back 24KiB in connection-level window */
4106 	frame.hd.stream_id = 0;
4107 	frame.window_update.window_size_increment = 24 * 1024;
4108 	session.onWindowUpdate(frame);
4109 	
4110 	/* Sends another 16KiB data */
4111 	assert(0 == session.send());
4112 	assert(16 * 1024 == user_data.data_source_length);
4113 	
4114 	/* Increase initial window size to 32KiB */
4115 	iva[0].id = Setting.INITIAL_WINDOW_SIZE;
4116 	iva[0].value = 32 * 1024;
4117 	
4118 	settings_frame.settings = Settings(FrameFlags.NONE, iva[0 .. 1].copy());
4119 	session.onSettings(settings_frame, true);
4120 	settings_frame.settings.free();
4121 	
4122 	/* Sends another 8KiB data */
4123 	assert(0 == session.send());
4124 	assert(8 * 1024 == user_data.data_source_length);
4125 	
4126 	/* Back 8KiB in connection-level window */
4127 	frame.hd.stream_id = 0;
4128 	frame.window_update.window_size_increment = 8 * 1024;
4129 	session.onWindowUpdate(frame);
4130 	
4131 	/* Sends last 8KiB data */
4132 	assert(0 == session.send());
4133 	assert(0 == user_data.data_source_length);
4134 	assert(session.getStream(1).shutFlags & ShutdownFlag.WR);
4135 	
4136 	frame.window_update.free();
4137 	session.free();
4138 }
4139 
4140 void test_session_flow_control_data_recv() {
4141 	Session session;
4142 	Callbacks callbacks;
4143 	ubyte[64 * 1024 + 16] data;
4144 	FrameHeader hd;
4145 	OutboundItem item;
4146 	Stream stream;
4147 
4148 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
4149 	
4150 	/* Initial window size to 64KiB - 1*/
4151 	session = new Session(CLIENT, *callbacks);
4152 	
4153 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
4154 	
4155 	session.next_stream_id = 3;
4156 	
4157 	stream.shutdown(ShutdownFlag.WR);
4158 	
4159 	session.local_window_size = MAX_PAYLOADLEN;
4160 	stream.localWindowSize = MAX_PAYLOADLEN;
4161 	
4162 	/* Create DATA frame */
4163 	
4164 	hd = FrameHeader(MAX_PAYLOADLEN, FrameType.DATA, FrameFlags.END_STREAM, 1);
4165 	
4166 	hd.pack(data[0 .. $]);
4167 	assert(MAX_PAYLOADLEN + FRAME_HDLEN == session.memRecv(data[0 .. MAX_PAYLOADLEN + FRAME_HDLEN]));
4168 	
4169 	item = session.getNextOutboundItem();
4170 	/* Since this is the last frame, stream-level WINDOW_UPDATE is not
4171      issued, but connection-level is. */
4172 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
4173 	assert(0 == item.frame.hd.stream_id);
4174 	assert(MAX_PAYLOADLEN == item.frame.window_update.window_size_increment);
4175 	
4176 	assert(0 == session.send());
4177 
4178 	/* Receive DATA for closed stream. They are still subject to under
4179      connection-level flow control, since this situation arises when
4180      RST_STREAM is issued by the remote, but the local side keeps
4181      sending DATA frames. Without calculating connection-level window,
4182      the subsequent flow control gets confused. */
4183 	assert(MAX_PAYLOADLEN + FRAME_HDLEN == session.memRecv(data[0 .. MAX_PAYLOADLEN + FRAME_HDLEN]));
4184 	
4185 	item = session.getNextOutboundItem();
4186 	assert(FrameType.WINDOW_UPDATE == item.frame.hd.type);
4187 	assert(0 == item.frame.hd.stream_id);
4188 	assert(MAX_PAYLOADLEN == item.frame.window_update.window_size_increment);
4189 	
4190 	session.free();
4191 }
4192 
4193 void test_session_flow_control_data_with_padding_recv() {
4194 	Session session;
4195 	Callbacks callbacks;
4196 	ubyte[1024] data;
4197 	FrameHeader hd;
4198 	Stream stream;
4199 	Options options;
4200 	
4201 	
4202 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
4203 	
4204 	/* Disable auto window update so that we can check padding is consumed automatically */
4205 	options.setNoAutoWindowUpdate(1);
4206 	
4207 	/* Initial window size to 64KiB - 1*/
4208 	session = new Session(CLIENT, *callbacks, options);
4209 	
4210 	
4211 	
4212 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
4213 	
4214 	/* Create DATA frame */
4215 	
4216 	hd = FrameHeader(357, FrameType.DATA, cast(FrameFlags)(FrameFlags.END_STREAM | FrameFlags.PADDED), 1);
4217 	
4218 	hd.pack(data[0 .. $]);
4219 	/* Set Pad Length field, which itself is padding */
4220 	data[FRAME_HDLEN] = 255;
4221 	
4222 	assert(cast(size_t)(FRAME_HDLEN + hd.length) == session.memRecv(data[0 .. FRAME_HDLEN + hd.length]));
4223 	
4224 	assert(cast(int)hd.length == session.recv_window_size);
4225 	assert(cast(int)hd.length == stream.recvWindowSize);
4226 	assert(256 == session.consumed_size);
4227 	assert(256 == stream.consumedSize);
4228 	
4229 	session.free();
4230 }
4231 
4232 void test_session_data_read_temporal_failure() {
4233 	Session session;
4234 	Callbacks callbacks;
4235 	MyUserData user_data = MyUserData(&session);
4236 	DataProvider data_prd;
4237 	Frame frame;
4238 	Stream stream;
4239 	int data_size = 128 * 1024;
4240 	
4241 	
4242 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
4243 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
4244 	data_prd = &user_data.datasrc.readFixedLength;
4245 	
4246 	user_data.data_source_length = data_size;
4247 	
4248 	/* Initial window size is 64KiB - 1 */
4249 	session = new Session(CLIENT, *callbacks);
4250 	submitRequest(session, pri_spec_default, null, data_prd, null);
4251 	
4252 	/* Sends INITIAL_WINDOW_SIZE data, assuming, it is equal to
4253      or smaller than INITIAL_CONNECTION_WINDOW_SIZE */
4254 	assert(0 == session.send());
4255 	assert(data_size - INITIAL_WINDOW_SIZE == user_data.data_source_length);
4256 	
4257 	stream = session.getStream(1);
4258 	assert(stream.isDeferredByFlowControl());
4259 	assert(FrameType.DATA == stream.item.frame.hd.type);
4260 	
4261 	stream.item.aux_data.data.data_prd = toDelegate(&MyDataSource.readRstStream);
4262 	
4263 	/* Back INITIAL_WINDOW_SIZE to both connection-level and
4264      stream-wise window */
4265 	frame.window_update = WindowUpdate(FrameFlags.NONE, 1, INITIAL_WINDOW_SIZE);
4266 	session.onWindowUpdate(frame);
4267 	frame.hd.stream_id = 0;
4268 	session.onWindowUpdate(frame);
4269 	frame.window_update.free();
4270 
4271 	/* Sending data will fail (soft fail) and treated as stream error */
4272 	user_data.frame_send_cb_called = 0;
4273 	assert(0 == session.send());
4274 	assert(data_size - INITIAL_WINDOW_SIZE == user_data.data_source_length);
4275 	
4276 	assert(1 == user_data.frame_send_cb_called);
4277 	assert(FrameType.RST_STREAM == user_data.sent_frame_type);
4278 	
4279 	data_prd = toDelegate(&MyDataSource.readFailure);
4280 
4281 	submitRequest(session, pri_spec_default, null, data_prd, null);
4282 	/* Sending data will fail (hard fail) and session tear down */
4283 	auto send_ret = session.send();
4284 	assert(ErrorCode.CALLBACK_FAILURE == send_ret, "send returned: "~ send_ret.to!string);
4285 	
4286 	session.free();
4287 }
4288 
4289 void test_session_on_stream_close() {
4290 	Session session;
4291 	Callbacks callbacks;
4292 	MyUserData user_data = MyUserData(&session);
4293 	Stream stream;
4294 
4295 	callbacks.on_stream_exit_cb = &user_data.cb_handlers.onStreamExit;
4296 	user_data.stream_close_cb_called = 0;
4297 	
4298 	session = new Session(CLIENT, *callbacks);
4299 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, &user_data);
4300 	assert(stream);
4301 	assert(session.closeStream(1, FrameError.NO_ERROR) == 0);
4302 	assert(user_data.stream_close_cb_called == 1);
4303 	session.free();
4304 }
4305 
4306 void test_session_on_ctrl_not_send() {
4307 	Session session;
4308 	Callbacks callbacks;
4309 	MyUserData user_data = MyUserData(&session);
4310 	Stream stream;
4311 	
4312 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
4313 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
4314 	user_data.frame_not_send_cb_called = 0;
4315 	user_data.not_sent_frame_type = FrameType.init;
4316 	user_data.not_sent_error = ErrorCode.OK;
4317 	
4318 	session = new Session(SERVER, *callbacks);
4319 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, &user_data);
4320 	
4321 	/* Check response HEADERS */
4322 	/* Send bogus stream ID */
4323 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 3));
4324 	assert(0 == session.send());
4325 	assert(1 == user_data.frame_not_send_cb_called);
4326 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
4327 	assert(ErrorCode.STREAM_CLOSED == user_data.not_sent_error);
4328 	
4329 	user_data.frame_not_send_cb_called = 0;
4330 	/* Shutdown transmission */
4331 	stream.shutFlags = cast(ShutdownFlag)(stream.shutFlags | ShutdownFlag.WR);
4332 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 1));
4333 	assert(0 == session.send());
4334 	assert(1 == user_data.frame_not_send_cb_called);
4335 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
4336 	assert(ErrorCode.STREAM_SHUT_WR == user_data.not_sent_error);
4337 	
4338 	stream.shutFlags = ShutdownFlag.NONE;
4339 	user_data.frame_not_send_cb_called = 0;
4340 	/* Queue RST_STREAM */
4341 	assert(0 == submitHeaders(session, FrameFlags.END_STREAM, 1));
4342 	assert(0 == submitRstStream(session, 1, FrameError.INTERNAL_ERROR));
4343 	assert(0 == session.send());
4344 	assert(1 == user_data.frame_not_send_cb_called);
4345 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
4346 	assert(ErrorCode.STREAM_CLOSING == user_data.not_sent_error);
4347 	
4348 	session.free();
4349 	
4350 	/* Check request HEADERS */
4351 	user_data.frame_not_send_cb_called = 0;
4352 	session = new Session(CLIENT, *callbacks);
4353 	/* Maximum Stream ID is reached */
4354 	session.next_stream_id = (1u << 31) + 1;
4355 	assert(ErrorCode.STREAM_ID_NOT_AVAILABLE == submitHeaders(session, FrameFlags.END_STREAM));
4356 	
4357 	user_data.frame_not_send_cb_called = 0;
4358 	/* GOAWAY received */
4359 	session.goaway_flags |= GoAwayFlags.RECV;
4360 	session.next_stream_id = 9;
4361 	
4362 	assert(0 < submitHeaders(session, FrameFlags.END_STREAM));
4363 	assert(0 == session.send());
4364 	assert(1 == user_data.frame_not_send_cb_called);
4365 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
4366 	assert(ErrorCode.START_STREAM_NOT_ALLOWED == user_data.not_sent_error);
4367 
4368 	session.free();
4369 }
4370 
4371 void test_session_get_outbound_queue_size() {
4372 	Session session;
4373 	Callbacks callbacks;
4374 
4375 	session = new Session(CLIENT, *callbacks);
4376 	assert(0 == session.getOutboundQueueSize());
4377 
4378 	submitPing(session, null);
4379 	assert(1 == session.getOutboundQueueSize());
4380 	
4381 	assert(0 == submitGoAway(session, 2, FrameError.NO_ERROR, null));
4382 	assert(2 == session.getOutboundQueueSize());
4383 	
4384 	session.free();
4385 }
4386 
4387 void test_session_get_effective_local_window_size() {
4388 	Session session;
4389 	Callbacks callbacks;
4390 	Stream stream;
4391 	
4392 	session = new Session(CLIENT, *callbacks);
4393 	
4394 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENED, null);
4395 	
4396 	assert(INITIAL_CONNECTION_WINDOW_SIZE == session.getEffectiveLocalWindowSize());
4397 	assert(0 == session.getEffectiveRecvDataLength());
4398 	
4399 	assert(INITIAL_WINDOW_SIZE == session.getStreamEffectiveLocalWindowSize(1));
4400 	assert(0 == session.getStreamEffectiveRecvDataLength(1));
4401 	
4402 	/* Check connection flow control */
4403 	session.recv_window_size = 100;
4404 	submitWindowUpdate(session, 0, 1100);
4405 	
4406 	assert(INITIAL_CONNECTION_WINDOW_SIZE + 1000 == session.getEffectiveLocalWindowSize());
4407 	assert(0 == session.getEffectiveRecvDataLength());
4408 	
4409 	submitWindowUpdate(session, 0, -50);
4410 	/* Now session.recv_window_size = -50 */
4411 	assert(INITIAL_CONNECTION_WINDOW_SIZE + 950 == session.getEffectiveLocalWindowSize());
4412 	assert(0 == session.getEffectiveRecvDataLength());
4413 
4414 	session.recv_window_size += 50;
4415 	/* Now session.recv_window_size = 0 */
4416 	submitWindowUpdate(session, 0, 100);
4417 	assert(INITIAL_CONNECTION_WINDOW_SIZE + 1050 == session.getEffectiveLocalWindowSize());
4418 	assert(50 == session.getEffectiveRecvDataLength());
4419 	
4420 	/* Check stream flow control */
4421 	stream.recvWindowSize = 100;
4422 	submitWindowUpdate(session, 1, 1100);
4423 	
4424 	assert(INITIAL_WINDOW_SIZE + 1000 == session.getStreamEffectiveLocalWindowSize(1));
4425 	assert(0 == session.getStreamEffectiveRecvDataLength(1));
4426 
4427 	submitWindowUpdate(session, 1, -50);
4428 	/* Now stream.recvWindowSize = -50 */
4429 	assert(INITIAL_WINDOW_SIZE + 950 == session.getStreamEffectiveLocalWindowSize(1));
4430 	assert(0 == session.getStreamEffectiveRecvDataLength(1));
4431 	
4432 	stream.recvWindowSize += 50;
4433 	/* Now stream.recvWindowSize = 0 */
4434 	submitWindowUpdate(session, 1, 100);
4435 	assert(INITIAL_WINDOW_SIZE + 1050 == session.getStreamEffectiveLocalWindowSize(1));
4436 	assert(50 == session.getStreamEffectiveRecvDataLength(1));
4437 	
4438 	session.free();
4439 }
4440 
4441 void test_session_set_option() {
4442 	Session session;
4443 	Callbacks callbacks;
4444 	Options options;
4445 
4446 	options.setNoAutoWindowUpdate(1);
4447 	
4448 	
4449 	session = new Session(CLIENT, *callbacks, options);
4450 	
4451 	assert(session.opt_flags & OptionsMask.NO_AUTO_WINDOW_UPDATE);
4452 	
4453 	session.free();
4454 	
4455 	options.setPeerMaxConcurrentStreams(100);
4456 	
4457 	session = new Session(CLIENT, *callbacks, options);
4458 	
4459 	assert(100 == session.remote_settings.max_concurrent_streams);
4460 	session.free();
4461 
4462 }
4463 
4464 void test_session_data_backoff_by_high_pri_frame() {
4465 	Session session;
4466 	Callbacks callbacks;
4467 	MyUserData user_data = MyUserData(&session);
4468 	DataProvider data_prd;
4469 	Stream stream;
4470 	
4471 	
4472 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
4473 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
4474 	data_prd = &user_data.datasrc.readFixedLength;
4475 	
4476 	user_data.frame_send_cb_called = 0;
4477 	user_data.data_source_length = DATA_PAYLOADLEN * 4;
4478 	
4479 	session = new Session(CLIENT, *callbacks);
4480 	submitRequest(session, pri_spec_default, null, data_prd, null);
4481 	
4482 	session.remote_window_size = 1 << 20;
4483 	
4484 	user_data.block_count = 2;
4485 	/* Sends request HEADERS + DATA[0] */
4486 	assert(0 == session.send());
4487 	
4488 	stream = session.getStream(1);
4489 	stream.remoteWindowSize = 1 << 20;
4490 	
4491 	assert(FrameType.DATA == user_data.sent_frame_type);
4492 	/* data for DATA[1] is read from data_prd but it is not sent */
4493 	assert(user_data.data_source_length == DATA_PAYLOADLEN * 2);
4494 	
4495 	submitPing(session, null);
4496 	user_data.block_count = 2;
4497 	/* Sends DATA[1] + PING, PING is interleaved in DATA sequence */
4498 	assert(0 == session.send());
4499 	assert(FrameType.PING == user_data.sent_frame_type);
4500 	/* data for DATA[2] is read from data_prd but it is not sent */
4501 	assert(user_data.data_source_length == DATA_PAYLOADLEN);
4502 	
4503 	user_data.block_count = 2;
4504 	/* Sends DATA[2..3] */
4505 	assert(0 == session.send());
4506 	
4507 	assert(stream.shutFlags & ShutdownFlag.WR);
4508 	
4509 	session.free();
4510 }
4511 
4512 private void check_session_read_data_with_padding(Buffers bufs, size_t datalen) {
4513 	Session session;
4514 	MyUserData user_data = MyUserData(&session);
4515 	Callbacks callbacks;
4516 	ubyte[] input;
4517 	
4518 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
4519 	callbacks.on_data_chunk_cb = &user_data.cb_handlers.onDataChunk;
4520 	session = new Session(SERVER, *callbacks);
4521 
4522 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
4523 	
4524 	input = bufs.remove();
4525 	
4526 	user_data.frame_recv_cb_called = 0;
4527 	user_data.data_chunk_len = 0;
4528 	
4529 	assert(cast(size_t)input.length == session.memRecv(input));
4530 	
4531 	assert(1 == user_data.frame_recv_cb_called);
4532 	assert(datalen == user_data.data_chunk_len);
4533 	
4534 	Mem.free(input);
4535 	session.free();
4536 }
4537 
4538 void test_session_pack_data_with_padding() {
4539 	Session session;
4540 	MyUserData user_data = MyUserData(&session);
4541 	Callbacks callbacks;
4542 	DataProvider data_prd;
4543 	Frame* frame;
4544 	int datalen = 55;
4545 	
4546 	
4547 	callbacks.write_cb = &user_data.cb_handlers.writeWouldBlock;
4548 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
4549 	callbacks.select_padding_length_cb = &user_data.cb_handlers.selectPaddingLength;
4550 	
4551 	data_prd = &user_data.datasrc.readFixedLength;
4552 	
4553 	session = new Session(CLIENT, *callbacks);
4554 	
4555 	user_data.padlen = 63;
4556 	
4557 	submitRequest(session, pri_spec_default, null, data_prd, null);
4558 	user_data.block_count = 1;
4559 	user_data.data_source_length = datalen;
4560 	/* Sends HEADERS */
4561 	assert(0 == session.send());
4562 	assert(FrameType.HEADERS == user_data.sent_frame_type);
4563 	
4564 	frame = &session.aob.item.frame;
4565 	
4566 	assert(user_data.padlen == frame.data.padlen);
4567 	assert(frame.hd.flags & FrameFlags.PADDED);
4568 	
4569 	/* Check reception of this DATA frame */
4570 	check_session_read_data_with_padding(session.aob.framebufs, datalen);
4571 	
4572 	session.free();
4573 }
4574 
4575 void test_session_pack_headers_with_padding() {
4576 	Session session, sv_session;
4577 	Accumulator acc;
4578 	MyUserData user_data = MyUserData(&session);
4579 	Callbacks callbacks;
4580 	
4581 	
4582 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
4583 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
4584 	callbacks.select_padding_length_cb = &user_data.cb_handlers.selectPaddingLength;
4585 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
4586 	
4587 	acc.length = 0;
4588 	user_data.acc = &acc;
4589 	
4590 	session = new Session(CLIENT, *callbacks);
4591 	sv_session = new Session(SERVER, *callbacks);
4592 	
4593 	user_data.padlen = 163;
4594 
4595 	assert(1 == submitRequest(session, pri_spec_default, reqhf, DataProvider.init, null));
4596 	assert(0 == session.send());
4597 	
4598 	assert(acc.length < MAX_PAYLOADLEN);
4599 	user_data.frame_recv_cb_called = 0;
4600 	assert(cast(int)acc.length == sv_session.memRecv(acc[]), "memRecv");
4601 	assert(1 == user_data.frame_recv_cb_called, "frame_recv_cb_called");
4602 	assert(!sv_session.getNextOutboundItem());
4603 	
4604 	sv_session.free();
4605 	session.free();
4606 }
4607 
4608 void test_session_pack_settings_payload() {
4609 	Setting[2] iva;
4610 	ubyte[64] buf;
4611 	int len;
4612 	Setting[] resiva;
4613 
4614 	iva[0].id = Setting.HEADER_TABLE_SIZE;
4615 	iva[0].value = 1023;
4616 	iva[1].id = Setting.INITIAL_WINDOW_SIZE;
4617 	iva[1].value = 4095;
4618 	
4619 	len = packSettingsPayload(buf.ptr[0 .. 64], iva);
4620 	assert(2 * FRAME_SETTINGS_ENTRY_LENGTH == len);
4621 	Settings.unpack(resiva, buf[0 .. len]);
4622 	assert(2 == resiva.length);
4623 	assert(Setting.HEADER_TABLE_SIZE == resiva[0].id);
4624 	assert(1023 == resiva[0].value);
4625 	assert(Setting.INITIAL_WINDOW_SIZE == resiva[1].id);
4626 	assert(4095 == resiva[1].value);
4627 	
4628 	Mem.free(resiva);
4629 	
4630 	len = packSettingsPayload(buf[0 .. 9] /* too small */, iva);
4631 	assert(ErrorCode.INSUFF_BUFSIZE == len);
4632 }
4633 
4634 void checkStreamDependencySiblings(Stream stream, Stream dep_prev, Stream dep_next, Stream sib_prev, Stream sib_next) {
4635 	assert(dep_prev == stream.depPrev);
4636 	assert(dep_next == stream.depNext);
4637 	assert(sib_prev == stream.sibPrev);
4638 	assert(sib_next == stream.sibNext);
4639 }
4640 
4641 /* http2_stream_dep_add() and its families functions should be
4642    tested in http2_stream_test.c, but it is easier to use
4643    http2_session_open_stream().  Therefore, we test them here. */
4644 void test_session_stream_dep_add() {
4645 	Session session;
4646 	Callbacks callbacks;
4647 	Stream a, b, c, d, e;
4648 	
4649 	session = new Session(SERVER, *callbacks);
4650 	
4651 	a = openStream(session, 1);
4652 	
4653 	c = openStreamWithDep(session, 5, a);
4654 	b = openStreamWithDep(session, 3, a);
4655 	d = openStreamWithDep(session, 7, c);
4656 	
4657 	/* a
4658    * |
4659    * b--c
4660    *    |
4661    *    d
4662    */
4663 	
4664 	assert(4 == a.subStreams);
4665 	assert(1 == b.subStreams);
4666 	assert(2 == c.subStreams);
4667 	assert(1 == d.subStreams);
4668 	
4669 	assert(DEFAULT_WEIGHT * 2 == a.sumDepWeight);
4670 	assert(0 == b.sumDepWeight);
4671 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
4672 	assert(0 == d.sumDepWeight);
4673 	
4674 	checkStreamDependencySiblings(a, null, b, null, null);
4675 	checkStreamDependencySiblings(b, a, null, null, c);
4676 	checkStreamDependencySiblings(c, null, d, b, null);
4677 	checkStreamDependencySiblings(d, c, null, null, null);
4678 	
4679 	assert(4 == session.roots.num_streams);
4680 	assert(a == session.roots.head);
4681 	assert(!a.rootNext);
4682 	
4683 	e = openStreamWithDepExclusive(session, 9, a);
4684 	
4685 	/* a
4686    * |
4687    * e
4688    * |
4689    * b--c
4690    *    |
4691    *    d
4692    */
4693 	
4694 	assert(5 == a.subStreams);
4695 	assert(4 == e.subStreams);
4696 	assert(1 == b.subStreams);
4697 	assert(2 == c.subStreams);
4698 	assert(1 == d.subStreams);
4699 	
4700 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
4701 	assert(DEFAULT_WEIGHT * 2 == e.sumDepWeight);
4702 	assert(0 == b.sumDepWeight);
4703 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
4704 	assert(0 == d.sumDepWeight);
4705 	
4706 	checkStreamDependencySiblings(a, null, e, null, null);
4707 	checkStreamDependencySiblings(e, a, b, null, null);
4708 	checkStreamDependencySiblings(b, e, null, null, c);
4709 	checkStreamDependencySiblings(c, null, d, b, null);
4710 	checkStreamDependencySiblings(d, c, null, null, null);
4711 	
4712 	assert(5 == session.roots.num_streams);
4713 	assert(a == session.roots.head);
4714 	assert(!a.rootNext);
4715 	
4716 	session.free();
4717 }
4718 
4719 void test_session_stream_dep_remove() {
4720 	Session session;
4721 	Callbacks callbacks;
4722 	Stream a, b, c, d, e, f;
4723 
4724 	/* Remove root */
4725 	session = new Session(SERVER, *callbacks);
4726 	
4727 	a = openStream(session, 1);
4728 	b = openStreamWithDep(session, 3, a);
4729 	c = openStreamWithDep(session, 5, a);
4730 	d = openStreamWithDep(session, 7, c);
4731 	
4732 	/* a
4733    * |
4734    * c--b
4735    * |
4736    * d
4737    */
4738 	
4739 	a.remove();
4740 	
4741 	/* becomes:
4742    * b    c
4743    *      |
4744    *      d
4745    */
4746 	
4747 	assert(1 == a.subStreams);
4748 	assert(1 == b.subStreams);
4749 	assert(2 == c.subStreams);
4750 	assert(1 == d.subStreams);
4751 	
4752 	assert(0 == a.sumDepWeight);
4753 	assert(0 == b.sumDepWeight);
4754 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
4755 	assert(0 == d.sumDepWeight);
4756 	
4757 	checkStreamDependencySiblings(a, null, null, null, null);
4758 	checkStreamDependencySiblings(b, null, null, null, null);
4759 	checkStreamDependencySiblings(c, null, d, null, null);
4760 	checkStreamDependencySiblings(d, c, null, null, null);
4761 	
4762 	assert(3 == session.roots.num_streams);
4763 	assert(b == session.roots.head);
4764 	assert(c == b.rootNext);
4765 	assert(!c.rootNext);
4766 	
4767 	session.free();
4768 	
4769 	/* Remove left most stream */
4770 	session = new Session(SERVER, *callbacks);
4771 	
4772 	a = openStream(session, 1);
4773 	b = openStreamWithDep(session, 3, a);
4774 	c = openStreamWithDep(session, 5, a);
4775 	d = openStreamWithDep(session, 7, c);
4776 	
4777 	/* a
4778    * |
4779    * c--b
4780    * |
4781    * d
4782    */
4783 	
4784 	b.remove();
4785 	
4786 	/* becomes:
4787    * a
4788    * |
4789    * c
4790    * |
4791    * d
4792    */
4793 	
4794 	assert(3 == a.subStreams, "substreams is: " ~ a.subStreams.to!string);
4795 	assert(1 == b.subStreams);
4796 	assert(2 == c.subStreams);
4797 	assert(1 == d.subStreams);
4798 	
4799 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
4800 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
4801 	assert(0 == d.sumDepWeight);
4802 	assert(0 == b.sumDepWeight);
4803 	
4804 	checkStreamDependencySiblings(a, null, c, null, null);
4805 	checkStreamDependencySiblings(b, null, null, null, null);
4806 	checkStreamDependencySiblings(c, a, d, null, null);
4807 	checkStreamDependencySiblings(d, c, null, null, null);
4808 	
4809 	assert(3 == session.roots.num_streams);
4810 	assert(a == session.roots.head);
4811 	assert(!a.rootNext);
4812 	
4813 	session.free();
4814 	
4815 	/* Remove right most stream */
4816 	session = new Session(SERVER, *callbacks);
4817 	
4818 	a = openStream(session, 1);
4819 	b = openStreamWithDep(session, 3, a);
4820 	c = openStreamWithDep(session, 5, a);
4821 	d = openStreamWithDep(session, 7, c);
4822 	
4823 	/* a
4824    * |
4825    * c--b
4826    * |
4827    * d
4828    */
4829 	
4830 	c.remove();
4831 	
4832 	/* becomes:
4833    * a
4834    * |
4835    * d--b
4836    */
4837 	
4838 	assert(3 == a.subStreams);
4839 	assert(1 == b.subStreams);
4840 	assert(1 == c.subStreams);
4841 	assert(1 == d.subStreams);
4842 	
4843 	assert(DEFAULT_WEIGHT * 2 == a.sumDepWeight);
4844 	assert(0 == b.sumDepWeight);
4845 	assert(0 == d.sumDepWeight);
4846 	assert(0 == c.sumDepWeight);
4847 	
4848 	checkStreamDependencySiblings(a, null, d, null, null);
4849 	checkStreamDependencySiblings(b, null, null, d, null);
4850 	checkStreamDependencySiblings(c, null, null, null, null);
4851 	checkStreamDependencySiblings(d, a, null, null, b);
4852 	
4853 	session.free();
4854 	
4855 	/* Remove middle stream */
4856 	session = new Session(SERVER, *callbacks);
4857 	
4858 	a = openStream(session, 1);
4859 	b = openStreamWithDep(session, 3, a);
4860 	c = openStreamWithDep(session, 5, a);
4861 	d = openStreamWithDep(session, 7, a);
4862 	e = openStreamWithDep(session, 9, c);
4863 	f = openStreamWithDep(session, 11, c);
4864 	
4865 	/* a
4866    * |
4867    * d--c--b
4868    *    |
4869    *    f--e
4870    */
4871 	
4872 	assert(6 == a.subStreams);
4873 	assert(1 == b.subStreams);
4874 	assert(3 == c.subStreams);
4875 	assert(1 == d.subStreams);
4876 	assert(1 == e.subStreams);
4877 	assert(1 == f.subStreams);
4878 	
4879 	assert(DEFAULT_WEIGHT * 3 == a.sumDepWeight);
4880 	assert(0 == b.sumDepWeight);
4881 	assert(DEFAULT_WEIGHT * 2 == c.sumDepWeight);
4882 	assert(0 == d.sumDepWeight);
4883 	assert(0 == e.sumDepWeight);
4884 	assert(0 == f.sumDepWeight);
4885 	
4886 	c.remove();
4887 	
4888 	/* becomes:
4889    * a
4890    * |
4891    * d--f--e--b
4892    */
4893 	
4894 	assert(5 == a.subStreams);
4895 	assert(1 == b.subStreams);
4896 	assert(1 == c.subStreams);
4897 	assert(1 == d.subStreams);
4898 	assert(1 == e.subStreams);
4899 	assert(1 == f.subStreams);
4900 	
4901 	/* c's weight 16 is distributed evenly to e and f.  Each weight of e
4902      and f becomes 8. */
4903 	assert(DEFAULT_WEIGHT * 2 + 8 * 2 == a.sumDepWeight);
4904 	assert(0 == b.sumDepWeight);
4905 	assert(0 == c.sumDepWeight);
4906 	assert(0 == d.sumDepWeight);
4907 	assert(0 == e.sumDepWeight);
4908 	assert(0 == f.sumDepWeight);
4909 	
4910 	checkStreamDependencySiblings(a, null, d, null, null);
4911 	checkStreamDependencySiblings(b, null, null, e, null);
4912 	checkStreamDependencySiblings(c, null, null, null, null);
4913 	checkStreamDependencySiblings(e, null, null, f, b);
4914 	checkStreamDependencySiblings(f, null, null, d, e);
4915 	checkStreamDependencySiblings(d, a, null, null, f);
4916 	
4917 	session.free();
4918 }
4919 
4920 void test_session_stream_dep_add_subtree() {
4921 	Session session;
4922 	Callbacks callbacks;
4923 	Stream a, b, c, d, e, f;
4924 	
4925 	
4926 	
4927 	/* dep_stream has dep_next */
4928 	session = new Session(SERVER, *callbacks);
4929 	
4930 	a = openStream(session, 1);
4931 	b = openStreamWithDep(session, 3, a);
4932 	c = openStreamWithDep(session, 5, a);
4933 	d = openStreamWithDep(session, 7, c);
4934 	
4935 	e = openStream(session, 9);
4936 	f = openStreamWithDep(session, 11, e);
4937 	
4938 	/* a         e
4939    * |         |
4940    * c--b      f
4941    * |
4942    * d
4943    */
4944 	
4945 	a.addSubtree(e, session);
4946 	
4947 	/* becomes
4948    * a
4949    * |
4950    * e--c--b
4951    * |  |
4952    * f  d
4953    */
4954 	
4955 	assert(6 == a.subStreams);
4956 	assert(1 == b.subStreams);
4957 	assert(2 == c.subStreams);
4958 	assert(1 == d.subStreams);
4959 	assert(2 == e.subStreams);
4960 	assert(1 == f.subStreams);
4961 	
4962 	assert(DEFAULT_WEIGHT * 3 == a.sumDepWeight);
4963 	assert(0 == b.sumDepWeight);
4964 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
4965 	assert(0 == d.sumDepWeight);
4966 	assert(DEFAULT_WEIGHT == e.sumDepWeight);
4967 	assert(0 == f.sumDepWeight);
4968 	
4969 	checkStreamDependencySiblings(a, null, e, null, null);
4970 	checkStreamDependencySiblings(b, null, null, c, null);
4971 	checkStreamDependencySiblings(c, null, d, e, b);
4972 	checkStreamDependencySiblings(d, c, null, null, null);
4973 	checkStreamDependencySiblings(e, a, f, null, c);
4974 	checkStreamDependencySiblings(f, e, null, null, null);
4975 	
4976 	session.free();
4977 	
4978 	/* dep_stream has dep_next and now we insert subtree */
4979 	session = new Session(SERVER, *callbacks);
4980 	
4981 	a = openStream(session, 1);
4982 	b = openStreamWithDep(session, 3, a);
4983 	c = openStreamWithDep(session, 5, a);
4984 	d = openStreamWithDep(session, 7, c);
4985 	
4986 	e = openStream(session, 9);
4987 	f = openStreamWithDep(session, 11, e);
4988 	
4989 	/* a         e
4990    * |         |
4991    * c--b      f
4992    * |
4993    * d
4994    */
4995 	
4996 	a.insertSubtree(e, session);
4997 	
4998 	/* becomes
4999    * a
5000    * |
5001    * e
5002    * |
5003    * f--c--b
5004    *    |
5005    *    d
5006    */
5007 	
5008 	assert(6 == a.subStreams);
5009 	assert(1 == b.subStreams);
5010 	assert(2 == c.subStreams);
5011 	assert(1 == d.subStreams);
5012 	assert(5 == e.subStreams);
5013 	assert(1 == f.subStreams);
5014 	
5015 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
5016 	assert(0 == b.sumDepWeight);
5017 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
5018 	assert(0 == d.sumDepWeight);
5019 	assert(DEFAULT_WEIGHT * 3 == e.sumDepWeight);
5020 	assert(0 == f.sumDepWeight);
5021 	
5022 	checkStreamDependencySiblings(a, null, e, null, null);
5023 	checkStreamDependencySiblings(e, a, f, null, null);
5024 	checkStreamDependencySiblings(f, e, null, null, c);
5025 	checkStreamDependencySiblings(b, null, null, c, null);
5026 	checkStreamDependencySiblings(c, null, d, f, b);
5027 	checkStreamDependencySiblings(d, c, null, null, null);
5028 	
5029 	session.free();
5030 }
5031 
5032 void test_session_stream_dep_remove_subtree() {
5033 	Session session;
5034 	Callbacks callbacks;
5035 	Stream a, b, c, d, e;
5036 
5037 	/* Remove left most stream */
5038 	session = new Session(SERVER, *callbacks);
5039 	
5040 	a = openStream(session, 1);
5041 	b = openStreamWithDep(session, 3, a);
5042 	c = openStreamWithDep(session, 5, a);
5043 	d = openStreamWithDep(session, 7, c);
5044 	
5045 	/* a
5046    * |
5047    * c--b
5048    * |
5049    * d
5050    */
5051 	
5052 	c.removeSubtree();
5053 	
5054 	/* becomes
5055    * a  c
5056    * |  |
5057    * b  d
5058    */
5059 	
5060 	assert(2 == a.subStreams);
5061 	assert(1 == b.subStreams);
5062 	assert(2 == c.subStreams);
5063 	assert(1 == d.subStreams);
5064 	
5065 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
5066 	assert(0 == b.sumDepWeight);
5067 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
5068 	assert(0 == d.sumDepWeight);
5069 	
5070 	checkStreamDependencySiblings(a, null, b, null, null);
5071 	checkStreamDependencySiblings(b, a, null, null, null);
5072 	checkStreamDependencySiblings(c, null, d, null, null);
5073 	checkStreamDependencySiblings(d, c, null, null, null);
5074 	
5075 	session.free();
5076 	
5077 	/* Remove right most stream */
5078 	session = new Session(SERVER, *callbacks);
5079 	
5080 	a = openStream(session, 1);
5081 	b = openStreamWithDep(session, 3, a);
5082 	c = openStreamWithDep(session, 5, a);
5083 	d = openStreamWithDep(session, 7, c);
5084 	
5085 	/* a
5086    * |
5087    * c--b
5088    * |
5089    * d
5090    */
5091 	
5092 	b.removeSubtree();
5093 	
5094 	/* becomes
5095    * a  b
5096    * |
5097    * c
5098    * |
5099    * d
5100    */
5101 	
5102 	assert(3 == a.subStreams);
5103 	assert(1 == b.subStreams);
5104 	assert(2 == c.subStreams);
5105 	assert(1 == d.subStreams);
5106 	
5107 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
5108 	assert(0 == b.sumDepWeight);
5109 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
5110 	assert(0 == d.sumDepWeight);
5111 	
5112 	checkStreamDependencySiblings(a, null, c, null, null);
5113 	checkStreamDependencySiblings(c, a, d, null, null);
5114 	checkStreamDependencySiblings(d, c, null, null, null);
5115 	checkStreamDependencySiblings(b, null, null, null, null);
5116 	
5117 	session.free();
5118 	
5119 	/* Remove middle stream */
5120 	session = new Session(SERVER, *callbacks);
5121 	
5122 	a = openStream(session, 1);
5123 	e = openStreamWithDep(session, 9, a);
5124 	c = openStreamWithDep(session, 5, a);
5125 	b = openStreamWithDep(session, 3, a);
5126 	d = openStreamWithDep(session, 7, c);
5127 	
5128 	/* a
5129    * |
5130    * b--c--e
5131    *    |
5132    *    d
5133    */
5134 	
5135 	c.removeSubtree();
5136 	
5137 	/* becomes
5138    * a     c
5139    * |     |
5140    * b--e  d
5141    */
5142 	
5143 	assert(3 == a.subStreams);
5144 	assert(1 == b.subStreams);
5145 	assert(1 == e.subStreams);
5146 	assert(2 == c.subStreams);
5147 	assert(1 == d.subStreams);
5148 	
5149 	assert(DEFAULT_WEIGHT * 2 == a.sumDepWeight);
5150 	assert(0 == b.sumDepWeight);
5151 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
5152 	assert(0 == d.sumDepWeight);
5153 	assert(0 == e.sumDepWeight);
5154 	
5155 	checkStreamDependencySiblings(a, null, b, null, null);
5156 	checkStreamDependencySiblings(b, a, null, null, e);
5157 	checkStreamDependencySiblings(e, null, null, b, null);
5158 	checkStreamDependencySiblings(c, null, d, null, null);
5159 	checkStreamDependencySiblings(d, c, null, null, null);
5160 	
5161 	session.free();
5162 }
5163 
5164 void test_session_stream_dep_make_head_root() {
5165 	Session session;
5166 	Callbacks callbacks;
5167 	Stream a, b, c, d;
5168 
5169 	session = new Session(SERVER, *callbacks);
5170 	
5171 	a = openStream(session, 1);
5172 	b = openStreamWithDep(session, 3, a);
5173 	
5174 	c = openStream(session, 5);
5175 	
5176 	/* a     c
5177    * |
5178    * b
5179    */
5180 	
5181 	c.removeSubtree();
5182 	c.makeTopmostRoot(session);
5183 	
5184 	/*
5185    * c
5186    * |
5187    * a
5188    * |
5189    * b
5190    */
5191 	
5192 	assert(3 == c.subStreams);
5193 	assert(2 == a.subStreams);
5194 	assert(1 == b.subStreams);
5195 	
5196 	assert(DEFAULT_WEIGHT == c.sumDepWeight);
5197 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
5198 	assert(0 == b.sumDepWeight);
5199 	
5200 	checkStreamDependencySiblings(c, null, a, null, null);
5201 	checkStreamDependencySiblings(a, c, b, null, null);
5202 	checkStreamDependencySiblings(b, a, null, null, null);
5203 	
5204 	session.free();
5205 	
5206 	session = new Session(SERVER, *callbacks);
5207 	
5208 	a = openStream(session, 1);
5209 	
5210 	b = openStream(session, 3);
5211 	
5212 	c = openStream(session, 5);
5213 	
5214 	/*
5215    * a  b   c
5216    */
5217 	
5218 	c.removeSubtree();
5219 	c.makeTopmostRoot(session);
5220 	
5221 	/*
5222    * c
5223    * |
5224    * b--a
5225    */
5226 	
5227 	assert(3 == c.subStreams);
5228 	assert(1 == a.subStreams);
5229 	assert(1 == b.subStreams);
5230 	
5231 	assert(DEFAULT_WEIGHT * 2 == c.sumDepWeight);
5232 	assert(0 == b.sumDepWeight);
5233 	assert(0 == a.sumDepWeight);
5234 	
5235 	checkStreamDependencySiblings(c, null, b, null, null);
5236 	checkStreamDependencySiblings(b, c, null, null, a);
5237 	checkStreamDependencySiblings(a, null, null, b, null);
5238 	
5239 	session.free();
5240 	
5241 	session = new Session(SERVER, *callbacks);
5242 	
5243 	a = openStream(session, 1);
5244 	b = openStreamWithDep(session, 3, a);
5245 	
5246 	c = openStream(session, 5);
5247 	d = openStreamWithDep(session, 7, c);
5248 	
5249 	/* a     c
5250    * |     |
5251    * b     d
5252    */
5253 	
5254 	c.removeSubtree();
5255 	c.makeTopmostRoot(session);
5256 	
5257 	/*
5258    * c
5259    * |
5260    * a--d
5261    * |
5262    * b
5263    */
5264 	
5265 	assert(4 == c.subStreams);
5266 	assert(1 == d.subStreams);
5267 	assert(2 == a.subStreams);
5268 	assert(1 == b.subStreams);
5269 	
5270 	assert(DEFAULT_WEIGHT * 2 == c.sumDepWeight);
5271 	assert(0 == d.sumDepWeight);
5272 	assert(DEFAULT_WEIGHT == a.sumDepWeight);
5273 	assert(0 == b.sumDepWeight);
5274 	
5275 	checkStreamDependencySiblings(c, null, a, null, null);
5276 	checkStreamDependencySiblings(d, null, null, a, null);
5277 	checkStreamDependencySiblings(a, c, b, null, d);
5278 	checkStreamDependencySiblings(b, a, null, null, null);
5279 	
5280 	session.free();
5281 }
5282 
5283 void test_session_stream_attach_item() {
5284 	Session session;
5285 	Callbacks callbacks;
5286 	Stream a, b, c, d;
5287 	OutboundItem da, db, dc, dd;
5288 	
5289 	
5290 	
5291 	session = new Session(SERVER, *callbacks);
5292 	
5293 	a = openStream(session, 1);
5294 	b = openStreamWithDep(session, 3, a);
5295 	c = openStreamWithDep(session, 5, a);
5296 	d = openStreamWithDep(session, 7, c);
5297 	
5298 	/* a
5299    * |
5300    * c--b
5301    * |
5302    * d
5303    */
5304 	
5305 	db = createDataOutboundItem();
5306 	
5307 	b.attachItem(db, session);
5308 	
5309 	assert(StreamDPRI.NO_ITEM == a.dpri);
5310 	assert(StreamDPRI.TOP == b.dpri);
5311 	assert(StreamDPRI.NO_ITEM == c.dpri);
5312 	assert(StreamDPRI.NO_ITEM == d.dpri);
5313 	
5314 	assert(16 == b.effectiveWeight);
5315 	
5316 	assert(16 == a.sumNorestWeight);
5317 	
5318 	assert(1 == db.queued);
5319 	
5320 	dc = createDataOutboundItem();
5321 	
5322 	c.attachItem(dc, session);
5323 	
5324 	assert(StreamDPRI.NO_ITEM == a.dpri);
5325 	assert(StreamDPRI.TOP == b.dpri);
5326 	assert(StreamDPRI.TOP == c.dpri);
5327 	assert(StreamDPRI.NO_ITEM == d.dpri);
5328 	
5329 	assert(16 * 16 / 32 == b.effectiveWeight);
5330 	assert(16 * 16 / 32 == c.effectiveWeight);
5331 	
5332 	assert(32 == a.sumNorestWeight);
5333 	
5334 	assert(1 == dc.queued);
5335 	
5336 	da = createDataOutboundItem();
5337 	
5338 	a.attachItem(da, session);
5339 	
5340 	assert(StreamDPRI.TOP == a.dpri);
5341 	assert(StreamDPRI.REST == b.dpri);
5342 	assert(StreamDPRI.REST == c.dpri);
5343 	assert(StreamDPRI.NO_ITEM == d.dpri);
5344 	
5345 	assert(16 == a.effectiveWeight);
5346 	
5347 	assert(1 == da.queued);
5348 	
5349 	a.detachItem(session);
5350 	
5351 	assert(StreamDPRI.NO_ITEM == a.dpri);
5352 	assert(StreamDPRI.TOP == b.dpri);
5353 	assert(StreamDPRI.TOP == c.dpri);
5354 	assert(StreamDPRI.NO_ITEM == d.dpri);
5355 	
5356 	assert(16 * 16 / 32 == b.effectiveWeight);
5357 	assert(16 * 16 / 32 == c.effectiveWeight);
5358 	
5359 	dd = createDataOutboundItem();
5360 	
5361 	d.attachItem(dd, session);
5362 	
5363 	assert(StreamDPRI.NO_ITEM == a.dpri);
5364 	assert(StreamDPRI.TOP == b.dpri);
5365 	assert(StreamDPRI.TOP == c.dpri);
5366 	assert(StreamDPRI.REST == d.dpri);
5367 	
5368 	assert(16 * 16 / 32 == b.effectiveWeight);
5369 	assert(16 * 16 / 32 == c.effectiveWeight);
5370 	
5371 	assert(0 == dd.queued);
5372 	
5373 	c.detachItem(session);
5374 	
5375 	assert(StreamDPRI.NO_ITEM == a.dpri);
5376 	assert(StreamDPRI.TOP == b.dpri);
5377 	assert(StreamDPRI.NO_ITEM == c.dpri);
5378 	assert(StreamDPRI.REST == d.dpri);
5379 	
5380 	assert(16 * 16 / 16 == b.effectiveWeight);
5381 	
5382 	assert(0 == dd.queued);
5383 	
5384 	b.detachItem(session);
5385 	
5386 	assert(StreamDPRI.NO_ITEM == a.dpri);
5387 	assert(StreamDPRI.NO_ITEM == b.dpri);
5388 	assert(StreamDPRI.NO_ITEM == c.dpri);
5389 	assert(StreamDPRI.TOP == d.dpri);
5390 	
5391 	assert(16 * 16 / 16 == d.effectiveWeight);
5392 	
5393 	assert(1 == dd.queued);
5394 	
5395 	session.free();
5396 }
5397 
5398 void test_session_stream_attach_item_subtree() {
5399 	Session session;
5400 	Callbacks callbacks;
5401 	Stream a, b, c, d, e, f;
5402 	OutboundItem db, dd, de;
5403 	
5404 	session = new Session(SERVER, *callbacks);
5405 	
5406 	a = openStream(session, 1);
5407 	b = openStreamWithDep(session, 3, a);
5408 	c = openStreamWithDep(session, 5, a);
5409 	d = openStreamWithDep(session, 7, c);
5410 	
5411 	e = openStream(session, 9);
5412 	f = openStreamWithDep(session, 11, e);
5413 	/*
5414    * a        e
5415    * |        |
5416    * c--b     f
5417    * |
5418    * d
5419    */
5420 	
5421 	de = createDataOutboundItem();
5422 	
5423 	e.attachItem(de, session);
5424 	
5425 	db = createDataOutboundItem();
5426 	
5427 	b.attachItem(db, session);
5428 	
5429 	assert(StreamDPRI.NO_ITEM == a.dpri);
5430 	assert(StreamDPRI.TOP == b.dpri);
5431 	assert(StreamDPRI.NO_ITEM == c.dpri);
5432 	assert(StreamDPRI.NO_ITEM == d.dpri);
5433 	assert(StreamDPRI.TOP == e.dpri);
5434 	assert(StreamDPRI.NO_ITEM == f.dpri);
5435 	
5436 	assert(16 == b.effectiveWeight);
5437 	assert(16 == e.effectiveWeight);
5438 	
5439 	/* Insert subtree e under a */
5440 	
5441 	e.removeSubtree();
5442 	a.insertSubtree(e, session);
5443 	
5444 	/*
5445    * a
5446    * |
5447    * e
5448    * |
5449    * f--c--b
5450    *    |
5451    *    d
5452    */
5453 	
5454 	assert(StreamDPRI.NO_ITEM == a.dpri);
5455 	assert(StreamDPRI.REST == b.dpri);
5456 	assert(StreamDPRI.NO_ITEM == c.dpri);
5457 	assert(StreamDPRI.NO_ITEM == d.dpri);
5458 	assert(StreamDPRI.TOP == e.dpri);
5459 	assert(StreamDPRI.NO_ITEM == f.dpri);
5460 	
5461 	assert(16 == e.effectiveWeight);
5462 	
5463 	/* Remove subtree b */
5464 	
5465 	b.removeSubtree();
5466 	
5467 	b.makeRoot(session);
5468 	
5469 	/*
5470    * a       b
5471    * |
5472    * e
5473    * |
5474    * f--c
5475    *    |
5476 	 *    d
5477    */
5478 	
5479 	assert(StreamDPRI.NO_ITEM == a.dpri);
5480 	assert(StreamDPRI.TOP == b.dpri);
5481 	assert(StreamDPRI.NO_ITEM == c.dpri);
5482 	assert(StreamDPRI.NO_ITEM == d.dpri);
5483 	assert(StreamDPRI.TOP == e.dpri);
5484 	assert(StreamDPRI.NO_ITEM == f.dpri);
5485 	
5486 	assert(16 == b.effectiveWeight);
5487 	assert(16 == e.effectiveWeight);
5488 	
5489 	/* Remove subtree a */
5490 	
5491 	a.removeSubtree();
5492 	
5493 	a.makeRoot(session);
5494 	
5495 	assert(StreamDPRI.NO_ITEM == a.dpri);
5496 	assert(StreamDPRI.TOP == b.dpri);
5497 	assert(StreamDPRI.NO_ITEM == c.dpri);
5498 	assert(StreamDPRI.NO_ITEM == d.dpri);
5499 	assert(StreamDPRI.TOP == e.dpri);
5500 	assert(StreamDPRI.NO_ITEM == f.dpri);
5501 	
5502 	/* Remove subtree c */
5503 	
5504 	c.removeSubtree();
5505 	
5506 	c.makeRoot(session);
5507 	
5508 	/*
5509    * a       b     c
5510    * |             |
5511    * e             d
5512    * |
5513    * f
5514    */
5515 	
5516 	assert(StreamDPRI.NO_ITEM == a.dpri);
5517 	assert(StreamDPRI.TOP == b.dpri);
5518 	assert(StreamDPRI.NO_ITEM == c.dpri);
5519 	assert(StreamDPRI.NO_ITEM == d.dpri);
5520 	assert(StreamDPRI.TOP == e.dpri);
5521 	assert(StreamDPRI.NO_ITEM == f.dpri);
5522 	
5523 	dd = createDataOutboundItem();
5524 	
5525 	d.attachItem(dd, session);
5526 	
5527 	/* Add subtree c to a */
5528 	
5529 	c.removeSubtree();
5530 	a.addSubtree(c, session);
5531 	
5532 	/*
5533    * a       b
5534    * |
5535    * c--e
5536    * |  |
5537    * d  f
5538    */
5539 	
5540 	assert(StreamDPRI.NO_ITEM == a.dpri);
5541 	assert(StreamDPRI.TOP == b.dpri);
5542 	assert(StreamDPRI.NO_ITEM == c.dpri);
5543 	assert(StreamDPRI.REST == d.dpri);
5544 	assert(StreamDPRI.TOP == e.dpri);
5545 	assert(StreamDPRI.NO_ITEM == f.dpri);
5546 	
5547 	assert(16 == b.effectiveWeight);
5548 	assert(16 * 16 / 16 == e.effectiveWeight);
5549 	
5550 	assert(32 == a.sumNorestWeight);
5551 	assert(16 == c.sumNorestWeight);
5552 	
5553 	/* Insert b under a */
5554 	
5555 	b.removeSubtree();
5556 	a.insertSubtree(b, session);
5557 	
5558 	/*
5559    * a
5560    * |
5561    * b
5562    * |
5563    * e--c
5564    * |  |
5565    * f  d
5566    */
5567 	
5568 	assert(StreamDPRI.NO_ITEM == a.dpri);
5569 	assert(StreamDPRI.TOP == b.dpri);
5570 	assert(StreamDPRI.NO_ITEM == c.dpri);
5571 	assert(StreamDPRI.REST == d.dpri);
5572 	assert(StreamDPRI.REST == e.dpri);
5573 	assert(StreamDPRI.NO_ITEM == f.dpri);
5574 	
5575 	assert(16 == b.effectiveWeight);
5576 	
5577 	assert(16 == a.sumNorestWeight);
5578 	assert(0 == b.sumNorestWeight);
5579 	
5580 	/* Remove subtree b */
5581 	
5582 	b.removeSubtree();
5583 	b.makeRoot(session);
5584 	
5585 	/*
5586    * b       a
5587    * |
5588    * e--c
5589    * |  |
5590    * f  d
5591    */
5592 	
5593 	assert(StreamDPRI.NO_ITEM == a.dpri);
5594 	assert(StreamDPRI.TOP == b.dpri);
5595 	assert(StreamDPRI.NO_ITEM == c.dpri);
5596 	assert(StreamDPRI.REST == d.dpri);
5597 	assert(StreamDPRI.REST == e.dpri);
5598 	assert(StreamDPRI.NO_ITEM == f.dpri);
5599 	
5600 	assert(0 == a.sumNorestWeight);
5601 	assert(0 == b.sumNorestWeight);
5602 	
5603 	session.free();
5604 }
5605 
5606 void test_session_keep_closed_stream() {
5607 	Session session;
5608 	Callbacks callbacks;
5609 	const size_t max_concurrent_streams = 5;
5610 	Setting iv = Setting(Setting.MAX_CONCURRENT_STREAMS, max_concurrent_streams);
5611 	size_t i;
5612 	
5613 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
5614 	
5615 	session = new Session(SERVER, *callbacks);
5616 	
5617 	submitSettings(session, (&iv)[0 .. 1]);
5618 	
5619 	for (i = 0; i < max_concurrent_streams; ++i) {
5620 		openStream(session, cast(int)i * 2 + 1);
5621 	}
5622 	
5623 	assert(0 == session.num_closed_streams);
5624 	
5625 	session.closeStream(1, FrameError.NO_ERROR);
5626 	
5627 	assert(1 == session.num_closed_streams);
5628 	assert(1 == session.closed_stream_tail.id);
5629 	assert(session.closed_stream_tail == session.closed_stream_head);
5630 	
5631 	session.closeStream(5, FrameError.NO_ERROR);
5632 	
5633 	assert(2 == session.num_closed_streams);
5634 	assert(5 == session.closed_stream_tail.id);
5635 	assert(1 == session.closed_stream_head.id);
5636 	assert(session.closed_stream_head == session.closed_stream_tail.closedPrev);
5637 	assert(!session.closed_stream_tail.closedNext);
5638 	assert(session.closed_stream_tail == session.closed_stream_head.closedNext);
5639 	assert(!session.closed_stream_head.closedPrev);
5640 	
5641 	openStream(session, 11);
5642 	
5643 	assert(1 == session.num_closed_streams);
5644 	assert(5 == session.closed_stream_tail.id);
5645 	assert(session.closed_stream_tail == session.closed_stream_head);
5646 	assert(!session.closed_stream_head.closedPrev);
5647 	assert(!session.closed_stream_head.closedNext);
5648 	
5649 	openStream(session, 13);
5650 	
5651 	assert(0 == session.num_closed_streams);
5652 	assert(!session.closed_stream_tail);
5653 	assert(!session.closed_stream_head);
5654 	
5655 	session.free();
5656 }
5657 
5658 void test_session_keep_idle_stream() {
5659 	Session session;
5660 	Callbacks callbacks;
5661 	const size_t max_concurrent_streams = 1;
5662 	Setting iv = Setting(Setting.MAX_CONCURRENT_STREAMS, max_concurrent_streams);
5663 	int i;
5664 	
5665 	
5666 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
5667 	
5668 	session = new Session(SERVER, *callbacks);
5669 	
5670 	submitSettings(session, (&iv)[0 .. 1]);
5671 	
5672 	/* We at least allow 2 idle streams even if max concurrent streams
5673      is very low. */
5674 	for (i = 0; i < 2; ++i) {
5675 		session.openStream(i * 2 + 1, StreamFlags.NONE, pri_spec_default, StreamState.IDLE, null);
5676 	}
5677 
5678 	assert(2 == session.num_idle_streams);
5679 	
5680 	assert(1 == session.idle_stream_head.id);
5681 	assert(3 == session.idle_stream_tail.id);
5682 	
5683 	session.openStream(5, StreamFlags.NONE, pri_spec_default, StreamState.IDLE, null);
5684 	
5685 	assert(2 == session.num_idle_streams);
5686 	
5687 	assert(3 == session.idle_stream_head.id);
5688 	assert(5 == session.idle_stream_tail.id);
5689 	
5690 	session.free();
5691 }
5692 
5693 void test_session_detach_idle_stream() {
5694 	Session session;
5695 	Callbacks callbacks;
5696 	int i;
5697 	Stream stream;
5698 	
5699 	
5700 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
5701 	
5702 	session = new Session(SERVER, *callbacks);
5703 	
5704 	for (i = 1; i <= 3; ++i) {
5705 		session.openStream(i, StreamFlags.NONE, pri_spec_default, StreamState.IDLE, null);
5706 	}
5707 	
5708 	assert(3 == session.num_idle_streams);
5709 	
5710 	/* Detach middle stream */
5711 	stream = session.getStreamRaw(2);
5712 	
5713 	assert(session.idle_stream_head == stream.closedPrev);
5714 	assert(session.idle_stream_tail == stream.closedNext);
5715 	assert(stream == session.idle_stream_head.closedNext);
5716 	assert(stream == session.idle_stream_tail.closedPrev);
5717 	
5718 	session.detachIdleStream(stream);
5719 	
5720 	assert(2 == session.num_idle_streams);
5721 	
5722 	assert(!stream.closedPrev);
5723 	assert(!stream.closedNext);
5724 	
5725 	assert(session.idle_stream_head == session.idle_stream_tail.closedPrev);
5726 	assert(session.idle_stream_tail == session.idle_stream_head.closedNext);
5727 	
5728 	/* Detach head stream */
5729 	stream = session.idle_stream_head;
5730 	
5731 	session.detachIdleStream(stream);
5732 	
5733 	assert(1 == session.num_idle_streams);
5734 	
5735 	assert(session.idle_stream_head == session.idle_stream_tail);
5736 	assert(!session.idle_stream_head.closedPrev);
5737 	assert(!session.idle_stream_head.closedNext);
5738 	
5739 	/* Detach last stream */
5740 	
5741 	stream = session.idle_stream_head;
5742 	
5743 	session.detachIdleStream(stream);
5744 	
5745 	assert(0 == session.num_idle_streams);
5746 	
5747 	assert(!session.idle_stream_head);
5748 	assert(!session.idle_stream_tail);
5749 	
5750 	for (i = 4; i <= 5; ++i) {
5751 		session.openStream(i, StreamFlags.NONE, pri_spec_default, StreamState.IDLE, null);
5752 	}
5753 	
5754 	assert(2 == session.num_idle_streams);
5755 	
5756 	/* Detach tail stream */
5757 	
5758 	stream = session.idle_stream_tail;
5759 	
5760 	session.detachIdleStream(stream);
5761 	
5762 	assert(1 == session.num_idle_streams);
5763 	
5764 	assert(session.idle_stream_head == session.idle_stream_tail);
5765 	assert(!session.idle_stream_head.closedPrev);
5766 	assert(!session.idle_stream_head.closedNext);
5767 	
5768 	session.free();
5769 }
5770 
5771 void test_session_large_dep_tree() {
5772 	Session session;
5773 	Callbacks callbacks;
5774 	size_t i;
5775 	Stream dep_stream;
5776 	Stream root_stream;
5777 	int stream_id;
5778 	
5779 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
5780 	
5781 	session = new Session(SERVER, *callbacks);
5782 	
5783 	stream_id = 1;
5784 	for (i = 0; i < MAX_DEP_TREE_LENGTH; ++i) {
5785 		dep_stream = session.openStreamWithDep(stream_id, dep_stream);
5786 		stream_id += 2;
5787 	}
5788 	
5789 	root_stream = session.getStream(1);
5790 	
5791 	/* Check that last dep_stream must be part of tree */
5792 	assert(root_stream.subtreeContains(dep_stream));
5793 	
5794 	dep_stream = session.openStreamWithDep(stream_id, dep_stream);
5795 	
5796 	/* We exceeded MAX_DEP_TREE_LENGTH limit.  dep_stream is now
5797      root node and has no descendants. */
5798 	assert(!root_stream.subtreeContains(dep_stream));
5799 	assert(dep_stream.inDepTree());
5800 	
5801 	session.free();
5802 }
5803 
5804 void test_session_graceful_shutdown() {
5805 	Session session;
5806 	Callbacks callbacks;
5807 	MyUserData user_data = MyUserData(&session);
5808 	
5809 	
5810 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
5811 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
5812 	callbacks.on_stream_exit_cb = &user_data.cb_handlers.onStreamExit;
5813 	
5814 	session = new Session(SERVER, *callbacks);
5815 	
5816 	openStream(session, 301);
5817 	openStream(session, 302);
5818 	openStream(session, 309);
5819 	openStream(session, 311);
5820 	openStream(session, 319);
5821 	
5822 	assert(0 == submitShutdownNotice(session));
5823 	
5824 	user_data.frame_send_cb_called = 0;
5825 	
5826 	assert(0 == session.send());
5827 	
5828 	assert(1 == user_data.frame_send_cb_called);
5829 	assert((1u << 31) - 1 == session.local_last_stream_id);
5830 	
5831 	assert(0 == submitGoAway(session, 311, FrameError.NO_ERROR, null));
5832 	
5833 	user_data.frame_send_cb_called = 0;
5834 	user_data.stream_close_cb_called = 0;
5835 	
5836 	assert(0 == session.send());
5837 	
5838 	assert(1 == user_data.frame_send_cb_called);
5839 	assert(311 == session.local_last_stream_id);
5840 	assert(1 == user_data.stream_close_cb_called);
5841 
5842 	assert(0 == session.terminateSession(301, FrameError.NO_ERROR));
5843 	
5844 	user_data.frame_send_cb_called = 0;
5845 	user_data.stream_close_cb_called = 0;
5846 	
5847 	assert(0 == session.send());
5848 	
5849 	assert(1 == user_data.frame_send_cb_called);
5850 	assert(301 == session.local_last_stream_id);
5851 	assert(2 == user_data.stream_close_cb_called);
5852 	
5853 	assert(session.getStream(301));
5854 	assert(session.getStream(302));
5855 	assert(!session.getStream(309));
5856 	assert(!session.getStream(311));
5857 	assert(!session.getStream(319));
5858 	
5859 	session.free();
5860 }
5861 
5862 void test_session_on_header_temporal_failure() {
5863 	Session session;
5864 	Callbacks callbacks;
5865 	MyUserData user_data = MyUserData(&session);
5866 	Buffers bufs = framePackBuffers();
5867 	Buffer* buf;
5868 	Deflater deflater;
5869 	HeaderField[] hfa = [HeaderField("alpha", "bravo"), HeaderField("charlie", "delta")];
5870 	HeaderField[] hfa_copy;
5871 	size_t hdpos;
5872 	int rv;
5873 	Frame frame;
5874 	FrameHeader hd;
5875 	OutboundItem item;
5876 		
5877 	callbacks.on_header_field_cb = &user_data.cb_handlers.onHeaderFieldRstStream;
5878 	
5879 	session = new Session(SERVER, *callbacks);
5880 		
5881 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
5882 
5883 	hfa_copy = reqhf.copy();
5884 	
5885 	frame.headers = Headers(FrameFlags.END_STREAM, 1, HeadersCategory.REQUEST, pri_spec_default, hfa_copy);
5886 	frame.headers.pack(bufs, deflater);
5887 	frame.headers.free();
5888 	
5889 	/* We are going to create CONTINUATION.  First serialize header
5890      block, and then frame header. */
5891 	hdpos = bufs.length;
5892 	
5893 	buf = &bufs.head.buf;
5894 	buf.last += FRAME_HDLEN;
5895 	
5896 	deflater.deflate(bufs, hfa[1 .. 2]);
5897 	
5898 	hd = FrameHeader(cast(int)(bufs.length - hdpos - FRAME_HDLEN), FrameType.CONTINUATION, FrameFlags.END_HEADERS, 1);
5899 	
5900 	hd.pack(buf.pos[hdpos .. buf.available]);
5901 	
5902 	rv = session.memRecv((*buf)[]);
5903 	
5904 	assert(rv == bufs.length);
5905 	
5906 	item = session.getNextOutboundItem();
5907 	
5908 	assert(FrameType.RST_STREAM == item.frame.hd.type);
5909 	
5910 	/* Make sure no header decompression error occurred */
5911 	assert(GoAwayFlags.NONE == session.goaway_flags);
5912 	
5913 	bufs.free();
5914 	
5915 	deflater.free();
5916 	session.free();
5917 }
5918 
5919 void test_session_read_client_preface() {
5920 	Session session;
5921 	Callbacks callbacks;
5922 	Options options;
5923 	int rv;
5924 	Frame ping_frame;
5925 	ubyte[16] buf;
5926 
5927 	options.setRecvClientPreface(1);
5928 	
5929 	/* Check success case */
5930 	session = new Session(SERVER, *callbacks, options);
5931 	
5932 	assert(session.opt_flags & OptionsMask.RECV_CLIENT_PREFACE);
5933 	
5934 	rv = session.memRecv(cast(ubyte[])CLIENT_CONNECTION_PREFACE);
5935 	
5936 	assert(rv == CLIENT_CONNECTION_PREFACE.length);
5937 	assert(InboundState.READ_FIRST_SETTINGS == session.iframe.state);
5938 	
5939 	/* Receiving PING is error because we want SETTINGS. */
5940 	ping_frame.ping = Ping(FrameFlags.NONE, null);
5941 	
5942 	ping_frame.ping.hd.pack(buf[0 .. $]);
5943 	
5944 	rv = session.memRecv(buf[0 .. FRAME_HDLEN]);
5945 	assert(FRAME_HDLEN == rv);
5946 	assert(InboundState.IGN_ALL == session.iframe.state);
5947 	assert(0 == session.iframe.payloadleft);
5948 	
5949 	ping_frame.ping.free();
5950 	
5951 	session.free();
5952 	
5953 	/* Check bad case */
5954 	session = new Session(SERVER, *callbacks, options);
5955 	
5956 	/* Feed preface with one byte less */
5957 	rv = session.memRecv(cast(ubyte[])CLIENT_CONNECTION_PREFACE[0 .. $-1]);
5958 	
5959 	assert(rv == CLIENT_CONNECTION_PREFACE.length - 1);
5960 	assert(InboundState.READ_CLIENT_PREFACE == session.iframe.state);
5961 	assert(1 == session.iframe.payloadleft);
5962 	
5963 	rv = session.memRecv(cast(ubyte[])"\0");
5964 	
5965 	assert(ErrorCode.BAD_PREFACE == rv);
5966 	
5967 	session.free();	
5968 }
5969 
5970 void test_session_delete_data_item() {
5971 	Session session;
5972 	Callbacks callbacks;
5973 	Stream a;
5974 	DataProvider prd = toDelegate(&MyDataSource.readFailure);
5975 		
5976 	session = new Session(SERVER, *callbacks);
5977 	
5978 	a = openStream(session, 1);
5979 	openStreamWithDep(session, 3, a);
5980 	
5981 	/* We don't care about these members, since we won't send data */
5982 	
5983 	/* This data item will be marked as TOP */
5984 	assert(0 == submitData(session, FrameFlags.NONE, 1, prd));
5985 	/* This data item will be marked as REST */
5986 	assert(0 == submitData(session, FrameFlags.NONE, 3, prd));
5987 	
5988 	session.free();
5989 }
5990 
5991 void test_session_open_idle_stream() {
5992 	Session session;
5993 	Callbacks callbacks;
5994 	Stream stream;
5995 	Stream opened_stream;
5996 	PrioritySpec pri_spec;
5997 	Frame frame;
5998 	
5999 	session = new Session(SERVER, *callbacks);
6000 	
6001 	pri_spec = PrioritySpec(0, 3, 0);
6002 	
6003 	frame.priority = Priority(1, pri_spec);
6004 	
6005 	assert(0 == session.onPriority(frame));
6006 	
6007 	stream = session.getStreamRaw(1);
6008 	
6009 	assert(StreamState.IDLE == stream.state);
6010 	assert(!stream.closedPrev);
6011 	assert(!stream.closedNext);
6012 	assert(1 == session.num_idle_streams);
6013 	assert(session.idle_stream_head == stream);
6014 	assert(session.idle_stream_tail == stream);
6015 
6016 	opened_stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6017 	
6018 	assert(stream == opened_stream);
6019 	assert(StreamState.OPENING == stream.state);
6020 	assert(0 == session.num_idle_streams);
6021 	assert(!session.idle_stream_head);
6022 	assert(!session.idle_stream_tail);
6023 	
6024 	frame.priority.free();
6025 	
6026 	session.free();
6027 }
6028 
6029 void test_session_cancel_reserved_remote() {
6030 	Session session;
6031 	Callbacks callbacks;
6032 	Stream stream;
6033 	Frame frame;
6034 	HeaderField[] hfa;
6035 	
6036 	Deflater deflater;
6037 
6038 	Buffers bufs = framePackBuffers();
6039 	int rv;	
6040 	
6041 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6042 	
6043 	session = new Session(CLIENT, *callbacks);
6044 	
6045 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6046 	
6047 	stream = session.openStream(2, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
6048 	
6049 	session.last_recv_stream_id = 2;
6050 	
6051 	submitRstStream(session, 2, FrameError.CANCEL);
6052 	
6053 	assert(StreamState.CLOSING == stream.state);
6054 	
6055 	assert(0 == session.send());
6056 	
6057 	hfa = reshf.copy();
6058 	
6059 	frame.headers = Headers(FrameFlags.END_HEADERS, 2, HeadersCategory.PUSH_RESPONSE, pri_spec_default, hfa);
6060 	rv = frame.headers.pack(bufs, deflater);
6061 	
6062 	assert(0 == rv);
6063 	
6064 	rv = session.memRecv(bufs.head.buf[]);
6065 
6066 	assert(bufs.head.buf.length == rv);
6067 	
6068 	/* stream is not dangling, so assign null */
6069 	stream = null;
6070 	
6071 	/* No RST_STREAM or GOAWAY is generated since stream should be in
6072      StreamState.CLOSING and push response should be ignored. */
6073 	assert(0 == session.ob_pq.length);
6074 	
6075 	/* Check that we can receive push response HEADERS while RST_STREAM
6076      is just queued. */
6077 	session.openStream(4, StreamFlags.NONE, pri_spec_default, StreamState.RESERVED, null);
6078 	
6079 	session.last_recv_stream_id = 4;
6080 	
6081 	submitRstStream(session, 2, FrameError.CANCEL);
6082 	
6083 	bufs.reset();
6084 	
6085 	frame.hd.stream_id = 4;
6086 	rv = frame.headers.pack(bufs, deflater);
6087 	
6088 	assert(0 == rv);
6089 	
6090 	rv = session.memRecv(bufs.head.buf[]);
6091 	
6092 	assert(bufs.head.buf.length == rv);
6093 	
6094 	assert(1 == session.ob_pq.length);
6095 	
6096 	frame.headers.free();
6097 	
6098 	deflater.free();
6099 	
6100 	session.free();
6101 	
6102 	bufs.free();
6103 }
6104 
6105 void test_session_reset_pending_headers() {
6106 	Session session;
6107 	Callbacks callbacks;
6108 	Stream stream;
6109 	int stream_id;
6110 	MyUserData user_data = MyUserData(&session);
6111 		
6112 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6113 	callbacks.on_frame_sent_cb = &user_data.cb_handlers.onFrameSent;
6114 	callbacks.on_frame_failure_cb = &user_data.cb_handlers.onFrameFailure;
6115 	callbacks.on_stream_exit_cb = &user_data.cb_handlers.onStreamExit;
6116 	
6117 	session = new Session(CLIENT, *callbacks);
6118 	
6119 	stream_id = submitRequest(session, pri_spec_default, null, DataProvider.init, null);
6120 	assert(stream_id >= 1);
6121 	
6122 	submitRstStream(session, stream_id, FrameError.CANCEL);
6123 	
6124 	session.remote_settings.max_concurrent_streams = 0;
6125 	
6126 	/* RST_STREAM cancels pending HEADERS and is not actually sent. */
6127 	user_data.frame_send_cb_called = 0;
6128 	assert(0 == session.send());
6129 	
6130 	assert(0 == user_data.frame_send_cb_called);
6131 	
6132 	stream = session.getStream(stream_id);
6133 	
6134 	assert(!stream);
6135 	
6136 	/* See HEADERS is not sent.  on_stream_close is called just like
6137      transmission failure. */
6138 	session.remote_settings.max_concurrent_streams = 1;
6139 	
6140 	user_data.frame_not_send_cb_called = 0;
6141 	user_data.stream_close_error_code = FrameError.NO_ERROR;
6142 	assert(0 == session.send());
6143 	
6144 	assert(1 == user_data.frame_not_send_cb_called);
6145 	assert(FrameType.HEADERS == user_data.not_sent_frame_type);
6146 	assert(FrameError.CANCEL == user_data.stream_close_error_code);
6147 	
6148 	stream = session.getStream(stream_id);
6149 	
6150 	assert(!stream);
6151 	
6152 	session.free();
6153 }
6154 
6155 
6156 void test_session_send_data_callback() {
6157 	Session session;
6158 	Callbacks callbacks;
6159 	Accumulator acc;
6160 	MyUserData user_data = MyUserData(&session);
6161 	FrameHeader hd;
6162 
6163 	callbacks.write_cb = &user_data.cb_handlers.writeToAccumulator;
6164 	callbacks.write_data_cb = &user_data.cb_handlers.writeData;
6165 	DataProvider data_prd = &user_data.datasrc.readNoCopy;
6166 
6167 	acc.length = 0;
6168 	user_data.acc = &acc;
6169 
6170 	user_data.data_source_length = DATA_PAYLOADLEN * 2;
6171 
6172 	session = new Session(CLIENT, *callbacks);
6173 
6174 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6175 
6176 	submitData(session, FrameFlags.END_STREAM, 1, data_prd);
6177 
6178 	assert(0 == session.send());
6179 	assert((FRAME_HDLEN + DATA_PAYLOADLEN) * 2 == acc.length, "Accumulator length was: " ~ acc.length.to!string);
6180 
6181 	hd.unpack(acc[]);
6182 
6183 	assert(16384 == hd.length);
6184 	assert(FrameType.DATA == hd.type);
6185 	assert(FrameFlags.NONE == hd.flags, "Frame flag was: " ~ hd.flags.to!string);
6186 
6187 	hd.unpack(acc.buf[FRAME_HDLEN + hd.length .. acc.buf.length]);
6188 
6189 	assert(16384 == hd.length);
6190 	assert(FrameType.DATA == hd.type);
6191 	assert(FrameFlags.END_STREAM == hd.flags);
6192 
6193 	session.free();
6194 }
6195 
6196 private void check_http_recv_headers_fail(Session session, ref MyUserData user_data, ref Deflater deflater, int stream_id, int stream_state, in HeaderField[] hfa) 
6197 {
6198 	
6199 	int rv;
6200 	OutboundItem item;
6201 	Buffers bufs = framePackBuffers();
6202 	if (stream_state != -1) 
6203 		session.openStream(stream_id, StreamFlags.NONE, pri_spec_default, cast(StreamState)stream_state, null);
6204 	
6205 	packHeaders(bufs, deflater, stream_id, FrameFlags.END_HEADERS, hfa);
6206 	
6207 	user_data.invalid_frame_recv_cb_called = 0;
6208 	
6209 	rv = session.memRecv(bufs.head.buf[]);
6210 	
6211 	assert(bufs.head.buf.length == rv);
6212 	
6213 	item = session.getNextOutboundItem();
6214 	
6215 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6216 	assert(1 == user_data.invalid_frame_recv_cb_called, user_data.invalid_frame_recv_cb_called.to!string);
6217 	assert(0 == session.send());
6218 	
6219 	bufs.free();
6220 }
6221 
6222 
6223 private void check_http_recv_headers_ok(Session session, ref MyUserData user_data, ref Deflater deflater, int stream_id, int stream_state, in HeaderField[] hfa) 
6224 {
6225 	
6226 	int rv;
6227 	Buffers bufs = framePackBuffers();
6228 
6229 	if (stream_state != -1) 
6230 		session.openStream(stream_id, StreamFlags.NONE, pri_spec_default, cast(StreamState)stream_state, null);
6231 	
6232 	packHeaders(bufs, deflater, stream_id, FrameFlags.END_HEADERS, hfa);
6233 	
6234 	user_data.frame_recv_cb_called = 0;
6235 	
6236 	rv = session.memRecv(bufs.head.buf[]);
6237 	
6238 	assert(bufs.head.buf.length == rv);	
6239 
6240 	assert(!session.getNextOutboundItem());
6241 
6242 	assert(1 == user_data.frame_recv_cb_called);
6243 	
6244 	bufs.free();
6245 }
6246 
6247 void test_http_mandatory_headers() 
6248 {
6249 	Session session;
6250 	Callbacks callbacks;
6251 	MyUserData user_data = MyUserData(&session);
6252 	callbacks.on_invalid_frame_cb = &user_data.cb_handlers.onInvalidFrame;
6253 	callbacks.on_frame_cb = &user_data.cb_handlers.onFrame;
6254 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6255 	Deflater deflater;
6256 
6257 	/* test case for response */
6258 	const HeaderField[] nostatus_reshf = [HeaderField("server", "foo")];
6259 	const HeaderField[] dupstatus_reshf = [HeaderField(":status", "200"), HeaderField(":status", "200")];
6260 	const HeaderField[] badpseudo_reshf = [HeaderField(":status", "200"), HeaderField(":scheme", "https")];
6261 	const HeaderField[] latepseudo_reshf = [HeaderField("server", "foo"), HeaderField(":status", "200")];
6262 	const HeaderField[] badstatus_reshf = [HeaderField(":status", "2000")];
6263 	const HeaderField[] badcl_reshf = [HeaderField(":status", "200"), HeaderField("content-length", "-1")];
6264 	const HeaderField[] dupcl_reshf = [HeaderField(":status", "200"), HeaderField("content-length", "0"), HeaderField("content-length", "0")];
6265 	const HeaderField[] badhd_reshf = [HeaderField(":status", "200"), HeaderField("connection", "close")];
6266 
6267 	/* test case for request */
6268 	const HeaderField[] nopath_reqhf = [
6269 		HeaderField(":scheme", "https"), 		HeaderField(":method", "GET"), 
6270 		HeaderField(":authority", "localhost")
6271 	];
6272 	const HeaderField[] earlyconnect_reqhf = [
6273 		HeaderField(":method", "CONNECT"), 		HeaderField(":scheme", "https"), 
6274 		HeaderField(":path", "/"), 				HeaderField(":authority", "localhost")];
6275 	const HeaderField[] lateconnect_reqhf = [
6276 		HeaderField(":scheme", "https"), 		HeaderField(":path", "/"), 
6277 		HeaderField(":method", "CONNECT"), 		HeaderField(":authority", "localhost")];
6278 	const HeaderField[] duppath_reqhf = [
6279 		HeaderField(":scheme", "https"), 		HeaderField(":method", "GET"),
6280 		HeaderField(":authority", "localhost"), HeaderField(":path", "/"),
6281 		HeaderField(":path", "/")];
6282 	const HeaderField[] badcl_reqhf = [
6283 		HeaderField(":scheme", "https"), 		HeaderField(":method", "POST"),
6284 		HeaderField(":authority", "localhost"), HeaderField(":path", "/"),
6285 		HeaderField("content-length", "-1")];
6286 	const HeaderField[] dupcl_reqhf = [
6287 		HeaderField(":scheme", "https"), 		HeaderField(":method", "POST"),
6288 		HeaderField(":authority", "localhost"), HeaderField(":path", "/"),
6289 		HeaderField("content-length", "0"), 	HeaderField("content-length", "0")];
6290 	const HeaderField[] badhd_reqhf = [
6291 		HeaderField(":scheme", "https"), 		HeaderField(":method", "GET"), 
6292 		HeaderField(":authority", "localhost"), HeaderField(":path", "/"),
6293 		HeaderField("connection", "close")];
6294 	const HeaderField[] badauthority_reqhf = [
6295 		HeaderField(":scheme", "https"), 		HeaderField(":method", "POST"),
6296 		HeaderField(":authority", "\x0d\x0alocalhost"), HeaderField(":path", "/")];
6297 	const HeaderField[] badhdbtw_reqhf = [
6298 		HeaderField(":scheme", "https"), 		HeaderField(":method", "GET"), 
6299 		HeaderField("foo", "\x0d\x0a"), HeaderField(":authority", "localhost"),
6300 		HeaderField(":path", "/")];
6301 	const HeaderField[] asteriskget1_reqhf = [
6302 		HeaderField(":path", "*"), HeaderField(":scheme", "https"),
6303 		HeaderField(":authority", "localhost"), HeaderField(":method", "GET")];
6304 	const HeaderField[] asteriskget2_reqhf = [
6305 		HeaderField(":scheme", "https"), HeaderField(":authority", "localhost"),
6306 		HeaderField(":method", "GET"), HeaderField(":path", "*")];
6307 	const HeaderField[] asteriskoptions1_reqhf = [
6308 		HeaderField(":path", "*"), HeaderField(":scheme", "https"),
6309 		HeaderField(":authority", "localhost"), HeaderField(":method", "OPTIONS")];
6310 	const HeaderField[] asteriskoptions2_reqhf = [
6311 		HeaderField(":scheme", "https"), HeaderField(":authority", "localhost"),
6312 		HeaderField(":method", "OPTIONS"), HeaderField(":path", "*")];
6313 		
6314 	session = new Session(CLIENT, *callbacks);
6315 	
6316 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6317 	
6318 	/* response header lacks :status */
6319 	check_http_recv_headers_fail(session, user_data, deflater, 1, StreamState.OPENING, nostatus_reshf);
6320 	
6321 	/* response header has 2 :status */
6322 	check_http_recv_headers_fail(session, user_data, deflater, 3, StreamState.OPENING, dupstatus_reshf);
6323 	
6324 	/* response header has bad pseudo header :scheme */
6325 	check_http_recv_headers_fail(session, user_data, deflater, 5, StreamState.OPENING, badpseudo_reshf);
6326 	
6327 	/* response header has :status after regular header field */
6328 	check_http_recv_headers_fail(session, user_data, deflater, 7, StreamState.OPENING, latepseudo_reshf);
6329 	
6330 	/* response header has bad status code */
6331 	check_http_recv_headers_fail(session, user_data, deflater, 9, StreamState.OPENING, badstatus_reshf);
6332 	
6333 	/* response header has bad content-length */
6334 	check_http_recv_headers_fail(session, user_data, deflater, 11, StreamState.OPENING, badcl_reshf);
6335 	
6336 	/* response header has multiple content-length */
6337 	check_http_recv_headers_fail(session, user_data, deflater, 13, StreamState.OPENING, dupcl_reshf);
6338 	
6339 	/* response header has disallowed header field */
6340 	check_http_recv_headers_fail(session, user_data, deflater, 15, StreamState.OPENING, badhd_reshf);
6341 	
6342 	deflater.free();
6343 	
6344 	session.free();
6345 	
6346 	/* check server side */
6347 	session = new Session(SERVER, *callbacks);
6348 	
6349 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6350 	
6351 	/* request header has no :path */
6352 	check_http_recv_headers_fail(session, user_data, deflater, 1, -1, nopath_reqhf);
6353 	
6354 	/* request header has CONNECT method, but followed by :path */
6355 	check_http_recv_headers_fail(session, user_data, deflater, 3, -1, earlyconnect_reqhf);
6356 	
6357 	/* request header has CONNECT method following :path */
6358 	check_http_recv_headers_fail(session, user_data, deflater, 5, -1, lateconnect_reqhf);
6359 	
6360 	/* request header has multiple :path */
6361 	check_http_recv_headers_fail(session, user_data, deflater, 7, -1, duppath_reqhf);
6362 	
6363 	/* request header has bad content-length */
6364 	check_http_recv_headers_fail(session, user_data, deflater, 9, -1, badcl_reqhf);
6365 	
6366 	/* request header has multiple content-length */
6367 	check_http_recv_headers_fail(session, user_data, deflater, 11, -1, dupcl_reqhf);
6368 
6369 	/* request header has disallowed header field */
6370 	check_http_recv_headers_fail(session, user_data, deflater, 13, -1, badhd_reqhf);
6371 
6372 	/* request header has :authority header field containing illegal characters */
6373 	check_http_recv_headers_fail(session, user_data, deflater, 15, -1, badauthority_reqhf);
6374 
6375 	/*  request header has regular header field containing illegal 
6376 	 * character before all mandatory header fields are seen. */
6377 	check_http_recv_headers_fail(session, user_data, deflater, 17, -1, badhdbtw_reqhf);
6378 
6379 	/* request header has "*" in :path header field while method is GET.
6380      :path is received before :method */
6381 	check_http_recv_headers_fail(session, user_data, deflater, 19, -1, asteriskget1_reqhf);
6382 	
6383 	/* request header has "*" in :path header field while method is GET.
6384      :method is received before :path */
6385 	check_http_recv_headers_fail(session, user_data, deflater, 21, -1, asteriskget2_reqhf);
6386 	
6387 	/* OPTIONS method can include "*" in :path header field.  :path is
6388      received before :method. */
6389 	check_http_recv_headers_ok(session, user_data, deflater, 23, -1, asteriskoptions1_reqhf);
6390 	
6391 	/* OPTIONS method can include "*" in :path header field.  :method is
6392      received before :path. */
6393 	check_http_recv_headers_ok(session, user_data, deflater, 25, -1, asteriskoptions2_reqhf);
6394 
6395 	deflater.free();
6396 	
6397 	session.free();
6398 }
6399 
6400 void test_http_content_length() {
6401 	Session session;
6402 	Callbacks callbacks;
6403 	Deflater deflater;
6404 
6405 	Buffers bufs = framePackBuffers();
6406 	int rv;
6407 	Stream stream;
6408 	const HeaderField[] cl_reshf = [HeaderField(":status", "200"),
6409 		HeaderField("te", "trailers"),
6410 		HeaderField("content-length", "9000000000")];
6411 	const HeaderField[] cl_reqhf = [
6412 		HeaderField(":path", "/"),        HeaderField(":method", "PUT"),
6413 		HeaderField(":scheme", "https"),  HeaderField("te", "trailers"),
6414 		HeaderField("host", "localhost"), HeaderField("content-length", "9000000000")];
6415 
6416 	
6417 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6418 	
6419 	session = new Session(CLIENT, *callbacks);
6420 	
6421 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6422 	
6423 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6424 	
6425 	packHeaders(bufs, deflater, 1, FrameFlags.END_HEADERS, cl_reshf);
6426 	
6427 	rv = session.memRecv(bufs.head.buf[]);
6428 	
6429 	assert(bufs.head.buf.length == rv);
6430 	assert(!session.getNextOutboundItem());
6431 	assert(9000000000L == stream.contentLength);
6432 	assert(200 == stream.statusCode);
6433 	
6434 	deflater.free();
6435 	
6436 	session.free();
6437 	
6438 	bufs.reset();
6439 	
6440 	/* check server side */
6441 	session = new Session(SERVER, *callbacks);
6442 	
6443 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6444 	
6445 	packHeaders(bufs, deflater, 1, FrameFlags.END_HEADERS, cl_reqhf);
6446 
6447 	rv = session.memRecv(bufs.head.buf[]);
6448 	
6449 	assert(bufs.head.buf.length == rv);
6450 	
6451 	stream = session.getStream(1);
6452 	
6453 	assert(!session.getNextOutboundItem());
6454 	assert(9000000000L == stream.contentLength);
6455 	
6456 	deflater.free();
6457 	
6458 	session.free();
6459 	
6460 	bufs.free();
6461 }
6462 
6463 void test_http_content_length_mismatch() {
6464 	Session session;
6465 	Callbacks callbacks;
6466 	Deflater deflater;
6467 
6468 	Buffers bufs = framePackBuffers();
6469 	int rv;
6470 	const HeaderField[] cl_reqhf = [
6471 		HeaderField(":path", "/"), HeaderField(":method", "PUT"),
6472 		HeaderField(":authority", "localhost"), HeaderField(":scheme", "https"),
6473 		HeaderField("content-length", "20")];
6474 	OutboundItem item;
6475 	FrameHeader hd;
6476 	
6477 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6478 	
6479 	session = new Session(SERVER, *callbacks);
6480 	
6481 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6482 	
6483 	/* header says content-length: 20, but HEADERS has END_STREAM flag set */
6484 	packHeaders(bufs, deflater, 1, cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM), cl_reqhf);
6485 
6486 	rv = session.memRecv(bufs.head.buf[]);
6487 	
6488 	assert(bufs.head.buf.length == rv);
6489 	
6490 	item = session.getNextOutboundItem();
6491 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6492 	
6493 	assert(0 == session.send());
6494 	
6495 	bufs.reset();
6496 	
6497 	/* header says content-length: 20, but DATA has 0 byte */
6498 	packHeaders(bufs, deflater, 3, FrameFlags.END_HEADERS, cl_reqhf);
6499 
6500 	hd = FrameHeader(0, FrameType.DATA, FrameFlags.END_STREAM, 3);
6501 	hd.pack(bufs.head.buf.last[0 .. bufs.head.buf.available]);
6502 	bufs.head.buf.last += FRAME_HDLEN;
6503 	
6504 	rv = session.memRecv(bufs.head.buf[]);
6505 	
6506 	assert(bufs.head.buf.length == rv);
6507 	
6508 	item = session.getNextOutboundItem();
6509 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6510 	
6511 	assert(0 == session.send());
6512 	
6513 	bufs.reset();
6514 	
6515 	/* header says content-length: 20, but DATA has 21 bytes */
6516 	packHeaders(bufs, deflater, 5, FrameFlags.END_HEADERS, cl_reqhf);
6517 
6518 	hd = FrameHeader(21, FrameType.DATA, FrameFlags.END_STREAM, 5);
6519 	hd.pack(bufs.head.buf.last[0 .. bufs.head.buf.available]);
6520 	bufs.head.buf.last += FRAME_HDLEN + 21;
6521 	
6522 	rv = session.memRecv(bufs.head.buf[]);
6523 	
6524 	assert(bufs.head.buf.length == rv);
6525 	
6526 	item = session.getNextOutboundItem();
6527 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6528 	
6529 	assert(0 == session.send());
6530 	
6531 	bufs.reset();
6532 	
6533 	deflater.free();
6534 	
6535 	session.free();
6536 	
6537 	bufs.free();
6538 }
6539 
6540 void test_http_non_final_response() {
6541 	Session session;
6542 	Callbacks callbacks;
6543 	Deflater deflater;
6544 
6545 	Buffers bufs = framePackBuffers();
6546 	int rv;
6547 	const HeaderField[] nonfinal_reshf = [HeaderField(":status", "100")];
6548 	OutboundItem item;
6549 	FrameHeader hd;
6550 	Stream stream;
6551 		
6552 	
6553 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6554 	
6555 	session = new Session(CLIENT, *callbacks);
6556 	
6557 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6558 	
6559 	/* non-final HEADERS with END_STREAM is illegal */
6560 	stream = session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6561 	
6562 	packHeaders(bufs, deflater, 1, cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM), nonfinal_reshf);
6563 
6564 	rv = session.memRecv(bufs.head.buf[]);
6565 	
6566 	assert(bufs.head.buf.length == rv);
6567 	
6568 	item = session.getNextOutboundItem();
6569 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6570 	
6571 	assert(0 == session.send());
6572 	
6573 	bufs.reset();
6574 	
6575 	/* non-final HEADERS followed by non-empty DATA is illegal */
6576 	stream = session.openStream(3, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6577 	
6578 	packHeaders(bufs, deflater, 3, FrameFlags.END_HEADERS, nonfinal_reshf);
6579 
6580 	hd = FrameHeader(10, FrameType.DATA, FrameFlags.END_STREAM, 3);
6581 	hd.pack(bufs.head.buf.last[0 .. bufs.head.buf.available]);
6582 	bufs.head.buf.last += FRAME_HDLEN + 10;
6583 	
6584 	rv = session.memRecv(bufs.head.buf[]);
6585 	
6586 	assert(bufs.head.buf.length == rv);
6587 	
6588 	item = session.getNextOutboundItem();
6589 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6590 	
6591 	assert(0 == session.send());
6592 	
6593 	bufs.reset();
6594 	
6595 	/* non-final HEADERS followed by empty DATA (without END_STREAM) is
6596      ok */
6597 	stream = session.openStream(5, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6598 	
6599 	packHeaders(bufs, deflater, 5, FrameFlags.END_HEADERS, nonfinal_reshf);
6600 
6601 	hd = FrameHeader(0, FrameType.DATA, FrameFlags.NONE, 5);
6602 	hd.pack(bufs.head.buf.last[0 .. bufs.head.buf.available]);
6603 	bufs.head.buf.last += FRAME_HDLEN;
6604 	
6605 	rv = session.memRecv(bufs.head.buf[]);
6606 	
6607 	assert(bufs.head.buf.length == rv);
6608 	
6609 	assert(!session.getNextOutboundItem());
6610 	
6611 	bufs.reset();
6612 	
6613 	/* non-final HEADERS followed by empty DATA (with END_STREAM) is
6614      illegal */
6615 	stream = session.openStream(7, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6616 	
6617 	packHeaders(bufs, deflater, 7, FrameFlags.END_HEADERS, nonfinal_reshf);
6618 
6619 	hd = FrameHeader(0, FrameType.DATA, FrameFlags.END_STREAM, 7);
6620 	hd.pack(bufs.head.buf.last[0 .. bufs.head.buf.available]);
6621 	bufs.head.buf.last += FRAME_HDLEN;
6622 	
6623 	rv = session.memRecv(bufs.head.buf[]);
6624 	
6625 	assert(bufs.head.buf.length == rv);
6626 	
6627 	item = session.getNextOutboundItem();
6628 	
6629 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6630 	
6631 	assert(0 == session.send());
6632 	
6633 	bufs.reset();
6634 	
6635 	/* non-final HEADERS followed by final HEADERS is OK */
6636 	stream = session.openStream(9, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6637 	
6638 	packHeaders(bufs, deflater, 9, FrameFlags.END_HEADERS, nonfinal_reshf);
6639 
6640 	rv = session.memRecv(bufs.head.buf[]);
6641 	
6642 	assert(bufs.head.buf.length == rv);
6643 	
6644 	bufs.reset();
6645 	
6646 	packHeaders(bufs, deflater, 9, FrameFlags.END_HEADERS, reshf);
6647 
6648 	rv = session.memRecv(bufs.head.buf[]);
6649 	
6650 	assert(bufs.head.buf.length == rv);
6651 	
6652 	assert(!session.getNextOutboundItem());
6653 	
6654 	bufs.reset();
6655 	
6656 	deflater.free();
6657 	
6658 	session.free();
6659 	
6660 	bufs.free();
6661 }
6662 
6663 void test_http_trailer_headers() {
6664 	Session session;
6665 	Callbacks callbacks;
6666 	Deflater deflater;
6667 
6668 	Buffers bufs = framePackBuffers();
6669 	int rv;
6670 	const HeaderField[] trailer_reqhf = [
6671 		HeaderField("foo", "bar"),
6672 	];
6673 	OutboundItem item;
6674 		
6675 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6676 	
6677 	session = new Session(SERVER, *callbacks);
6678 	
6679 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6680 	
6681 	/* good trailer header */
6682 	packHeaders(bufs, deflater, 1, FrameFlags.END_HEADERS, reqhf);
6683 
6684 	rv = session.memRecv(bufs.head.buf[]);
6685 	
6686 	assert(bufs.head.buf.length == rv);
6687 	
6688 	bufs.reset();
6689 	
6690 	packHeaders(bufs, deflater, 1, cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM), trailer_reqhf);
6691 
6692 	rv = session.memRecv(bufs.head.buf[]);
6693 	
6694 	assert(bufs.head.buf.length == rv);
6695 	
6696 	assert(!session.getNextOutboundItem());
6697 	
6698 	bufs.reset();
6699 	
6700 	/* trailer header without END_STREAM is illegal */
6701 	packHeaders(bufs, deflater, 3, FrameFlags.END_HEADERS, reqhf);
6702 	
6703 	rv = session.memRecv(bufs.head.buf[]);
6704 	
6705 	assert(bufs.head.buf.length == rv);
6706 	
6707 	bufs.reset();
6708 	
6709 	packHeaders(bufs, deflater, 3, FrameFlags.END_HEADERS, trailer_reqhf);
6710 
6711 	rv = session.memRecv(bufs.head.buf[]);
6712 	
6713 	assert(bufs.head.buf.length == rv);
6714 	
6715 	item = session.getNextOutboundItem();
6716 	
6717 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6718 	
6719 	assert(0 == session.send());
6720 	
6721 	bufs.reset();
6722 	
6723 	/* trailer header including pseudo header field is illegal */
6724 	packHeaders(bufs, deflater, 5, FrameFlags.END_HEADERS, reqhf);
6725 
6726 	rv = session.memRecv(bufs.head.buf[]);
6727 	
6728 	assert(bufs.head.buf.length == rv);
6729 	
6730 	bufs.reset();
6731 	
6732 	packHeaders(bufs, deflater, 5, FrameFlags.END_HEADERS, reqhf);
6733 	rv = session.memRecv(bufs.head.buf[]);
6734 	
6735 	assert(bufs.head.buf.length == rv);
6736 	
6737 	item = session.getNextOutboundItem();
6738 	
6739 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6740 	
6741 	assert(0 == session.send());
6742 	
6743 	bufs.reset();
6744 	
6745 	deflater.free();
6746 	
6747 	session.free();
6748 	
6749 	bufs.free();
6750 }
6751 
6752 void test_http_ignore_content_length() {
6753 	Session session;
6754 	Callbacks callbacks;
6755 	Deflater deflater;
6756 
6757 	Buffers bufs = framePackBuffers();
6758 	int rv;
6759 	const HeaderField[] cl_reshf = [HeaderField(":status", "304"), HeaderField("content-length", "20")];
6760 	const HeaderField[] conn_reqhf = [HeaderField(":authority", "localhost"), HeaderField(":method", "CONNECT"), HeaderField("content-length", "999999")];
6761 	Stream stream;
6762 
6763 	
6764 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6765 	
6766 	session = new Session(CLIENT, *callbacks);
6767 	
6768 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6769 	
6770 	/* If status 304, content-length must be ignored */
6771 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6772 	
6773 	packHeaders(bufs, deflater, 1, cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM), cl_reshf);
6774 	
6775 	rv = session.memRecv(bufs.head.buf[]);
6776 	
6777 	assert(bufs.head.buf.length == rv);
6778 	
6779 	assert(!session.getNextOutboundItem());
6780 	
6781 	bufs.reset();
6782 	
6783 	deflater.free();
6784 	session.free();
6785 	
6786 	/* If request method is CONNECT, content-length must be ignored */
6787 	session = new Session(SERVER, *callbacks);
6788 	
6789 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6790 	
6791 	packHeaders(bufs, deflater, 1, FrameFlags.END_HEADERS, conn_reqhf);
6792 	
6793 	rv = session.memRecv(bufs.head.buf[]);
6794 	
6795 	assert(bufs.head.buf.length == rv);
6796 	
6797 	assert(!session.getNextOutboundItem());
6798 	
6799 	stream = session.getStream(1);
6800 	
6801 	assert(-1 == stream.contentLength);
6802 	assert((stream.httpFlags & HTTPFlags.METH_CONNECT) > 0);
6803 	
6804 	deflater.free();
6805 	session.free();
6806 	bufs.free();
6807 }
6808 
6809 void test_http_ignore_regular_header() {
6810 	Session session;
6811 	Callbacks callbacks;
6812 	MyUserData user_data = MyUserData(&session);
6813 
6814 	const HeaderField[] bad_reqhf = [
6815 		HeaderField(":authority", "localhost"), HeaderField(":scheme", "https"),
6816 		HeaderField(":path", "/"),              HeaderField(":method", "GET"),
6817 		HeaderField("foo", "\x00"),           HeaderField("bar", "buzz")
6818 	];
6819 	const HeaderField[] bad_reshf = [
6820 		HeaderField(":authority", "localhost"), HeaderField(":scheme", "https"),
6821 		HeaderField(":path", "/"), HeaderField(":method", "GET"), HeaderField("bar", "buzz")
6822 	];
6823 	
6824 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6825 	callbacks.on_header_field_cb = &user_data.cb_handlers.onHeaderFieldPause;
6826 
6827 	int rv;
6828 	Buffers bufs = framePackBuffers();
6829 	Deflater deflater;
6830 
6831 	int proclen;
6832 	size_t i;
6833 
6834 	session = new Session(SERVER, *callbacks);
6835 
6836 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6837 
6838 	packHeaders(bufs, deflater, 1, cast(FrameFlags)(FrameFlags.END_HEADERS | FrameFlags.END_STREAM), bad_reqhf);
6839 
6840 	for(i = 0; i < 4; ++i) {
6841 		rv = session.memRecv(bufs.head.buf.pos[proclen .. bufs.head.buf.length]);
6842 		assert(rv > 0);
6843 		proclen += rv;
6844 		assert(bad_reshf[i] == user_data.hf);
6845 	}
6846 
6847 	rv = session.memRecv(bufs.head.buf.pos[proclen .. bufs.head.buf.length]);
6848 
6849 	assert(rv > 0);
6850 	/* header field "foo" must be ignored because it has illegal value.
6851 	 * So we have "bar" header field for 5th header. */
6852 	assert(bad_reshf[4] == user_data.hf);
6853 
6854 	proclen += rv;
6855 
6856 	assert(bufs.head.buf.length == proclen);
6857 
6858 	deflater.free();
6859 	session.free();
6860 	bufs.free();
6861 }
6862 
6863 void test_http_record_request_method() {
6864 	Session session;
6865 	Callbacks callbacks;
6866 	const HeaderField[] conn_reqhf = [HeaderField(":method", "CONNECT"), HeaderField(":authority", "localhost")];
6867 	const HeaderField[] conn_reshf = [HeaderField(":status", "200"), HeaderField("content-length", "9999")];
6868 	Stream stream;
6869 	int rv;
6870 	Buffers bufs = framePackBuffers();
6871 	Deflater deflater;
6872 	
6873 	
6874 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6875 	
6876 	session = new Session(CLIENT, *callbacks);
6877 	
6878 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6879 	
6880 	assert(1 == submitRequest(session, pri_spec_default, conn_reqhf, DataProvider.init, null));
6881 	
6882 	assert(0 == session.send());
6883 	
6884 	stream = session.getStream(1);
6885 	
6886 	assert(HTTPFlags.METH_CONNECT == stream.httpFlags);
6887 	
6888 	packHeaders(bufs, deflater, 1, FrameFlags.END_HEADERS, conn_reshf);
6889 	
6890 	rv = session.memRecv(bufs.head.buf[]);
6891 	
6892 	assert(bufs.head.buf.length == rv);
6893 	
6894 	assert((HTTPFlags.METH_CONNECT & stream.httpFlags) > 0);
6895 	assert(-1 == stream.contentLength);
6896 	
6897 	deflater.free();
6898 	session.free();
6899 	bufs.free();
6900 }
6901 
6902 void test_http_push_promise() {
6903 	Session session;
6904 	Callbacks callbacks;
6905 	Deflater deflater;
6906 
6907 	Buffers bufs = framePackBuffers();
6908 	int rv;
6909 	Stream stream;
6910 	const HeaderField[] bad_reqhf = [HeaderField(":method", "GET")];
6911 	OutboundItem item;
6912 
6913 	
6914 	callbacks.write_cb = toDelegate(&MyCallbacks.writeNull);
6915 	
6916 	/* good PUSH_PROMISE case */
6917 	session = new Session(CLIENT, *callbacks);
6918 	
6919 	deflater = Deflater(DEFAULT_MAX_DEFLATE_BUFFER_SIZE);
6920 
6921 	session.openStream(1, StreamFlags.NONE, pri_spec_default, StreamState.OPENING, null);
6922 	
6923 	packPushPromise(bufs, deflater, 1, FrameFlags.END_HEADERS, 2, reqhf);
6924 
6925 	rv = session.memRecv(bufs.head.buf[]);
6926 	
6927 	assert(bufs.head.buf.length == rv);
6928 	
6929 	assert(!session.getNextOutboundItem());
6930 	
6931 	stream = session.getStream(2);
6932 	assert(stream);
6933 	
6934 	bufs.reset();
6935 	
6936 	packHeaders(bufs, deflater, 2, FrameFlags.END_HEADERS, reshf);
6937 		
6938 	rv = session.memRecv(bufs.head.buf[]);
6939 	
6940 	assert(bufs.head.buf.length == rv);
6941 	
6942 	assert(!session.getNextOutboundItem());
6943 	
6944 	assert(200 == stream.statusCode);
6945 	
6946 	bufs.reset();
6947 	
6948 	/* PUSH_PROMISE lacks mandatory header */
6949 	packPushPromise(bufs, deflater, 1, FrameFlags.END_HEADERS, 4, bad_reqhf);
6950 
6951 	rv = session.memRecv(bufs.head.buf[]);
6952 	
6953 	assert(bufs.head.buf.length == rv);
6954 	
6955 	item = session.getNextOutboundItem();
6956 	
6957 	assert(FrameType.RST_STREAM == item.frame.hd.type);
6958 	assert(4 == item.frame.hd.stream_id);
6959 	
6960 	bufs.reset();
6961 	
6962 	deflater.free();
6963 	session.free();
6964 	bufs.free();
6965 }
6966 
6967 unittest {
6968 
6969 	import memutils.allocators;
6970 	enum Debugger = 0x02;
6971 	assert(0 == getAllocator!Debugger().bytesAllocated());
6972 
6973 	test_session_read();
6974 	test_session_read_invalid_stream_id();
6975 	test_session_read_invalid_frame();
6976 	test_session_read_eof();
6977 	test_session_read_data();
6978 	test_session_read_continuation();
6979 	test_session_read_headers_with_priority();
6980 	test_session_read_premature_headers();
6981 	test_session_read_unknown_frame();
6982 	test_session_read_unexpected_continuation();
6983 	test_session_read_settings_header_table_size();
6984 	test_session_read_too_large_frame_length();
6985 	test_session_continue();
6986 	test_session_add_frame();
6987 	test_session_on_request_headers_received();
6988 	test_session_on_response_headers_received();
6989 	test_session_on_headers_received();
6990 	test_session_on_push_response_headers_received();
6991 	test_session_on_priority_received();
6992 	test_session_on_rst_stream_received();
6993 	test_session_on_settings_received();
6994 	test_session_on_push_promise_received();
6995 	test_session_on_ping_received();
6996 	test_session_on_goaway_received();
6997 	test_session_on_window_update_received();
6998 	test_session_on_data_received();
6999 	test_session_write_headers_start_stream();
7000 	test_session_write_headers_reply();
7001 	test_session_write_headers_frame_size_error();
7002 	test_session_write_headers_push_reply();
7003 	test_session_write_rst_stream();
7004 	test_session_write_push_promise();
7005 	test_session_is_my_stream_id();
7006 	test_session_upgrade();
7007 	test_session_reprioritize_stream();
7008 	test_session_reprioritize_stream_with_idle_stream_dep();
7009 	test_submit_data();
7010 	test_submit_data_read_length_too_large();
7011 	test_submit_data_read_length_smallest();
7012 	test_submit_data_twice();
7013 	test_submit_request_with_data();
7014 	test_submit_request_without_data();
7015 	test_submit_response_with_data();
7016 	test_submit_response_without_data();
7017 	test_submit_headers_start_stream();
7018 	test_submit_headers_reply();
7019 	test_submit_headers_push_reply();
7020 	test_submit_headers();
7021 	test_submit_headers_continuation();
7022 	test_submit_priority();
7023 	test_submit_settings();
7024 	test_submit_settings_update_local_window_size();
7025 	test_submit_push_promise();
7026 	test_submit_window_update();
7027 	test_submit_window_update_local_window_size();
7028 	test_submit_shutdown_notice();
7029 	test_submit_invalid_hf();
7030 	test_session_open_stream();
7031 	test_session_open_stream_with_idle_stream_dep();
7032 	test_session_get_next_ob_item();
7033 	test_session_pop_next_ob_item();
7034 	test_session_reply_fail();
7035 	test_session_max_concurrent_streams();
7036 	test_session_stop_data_with_rst_stream();
7037 	test_session_defer_data();
7038 	test_session_flow_control();
7039 	test_session_flow_control_data_recv();
7040 	test_session_flow_control_data_with_padding_recv();
7041 	test_session_data_read_temporal_failure();
7042 	test_session_on_stream_close();
7043 	test_session_on_ctrl_not_send();
7044 	test_session_get_outbound_queue_size();
7045 	test_session_get_effective_local_window_size();
7046 	test_session_set_option();
7047 	test_session_data_backoff_by_high_pri_frame();
7048 	test_session_pack_data_with_padding();
7049 	test_session_pack_headers_with_padding();
7050 	test_session_pack_settings_payload();
7051 	test_session_stream_dep_add();
7052 	test_session_stream_dep_remove();
7053 	test_session_stream_dep_add_subtree();
7054 	test_session_stream_dep_remove_subtree();
7055 	test_session_stream_dep_make_head_root();
7056 	test_session_stream_attach_item();
7057 	test_session_stream_attach_item_subtree();
7058 	test_session_keep_closed_stream();
7059 	test_session_keep_idle_stream();
7060 	test_session_detach_idle_stream();
7061 	test_session_large_dep_tree();
7062 	test_session_graceful_shutdown();
7063 	test_session_on_header_temporal_failure();
7064 	test_session_read_client_preface();
7065 	test_session_delete_data_item();
7066 	test_session_open_idle_stream();
7067 	test_session_cancel_reserved_remote();
7068 	test_session_reset_pending_headers();
7069 	test_session_send_data_callback();
7070 	test_http_mandatory_headers();
7071 	test_http_content_length();
7072 	test_http_content_length_mismatch();
7073 	test_http_non_final_response();
7074 	test_http_trailer_headers();
7075 	test_http_ignore_content_length();
7076 	test_http_ignore_regular_header();
7077 	test_http_record_request_method();
7078 	test_http_push_promise();
7079 	//getAllocator!Debugger().printMap();
7080 	assert(0 == getAllocator!Debugger().bytesAllocated());
7081 }