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