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