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