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