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