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