QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/third_party/quiche/src/quic/core/http/quic_spdy_stream.h" |
| 6 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 7 | #include <limits> |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame] | 8 | #include <string> |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 9 | #include <utility> |
| 10 | |
bnc | 3fc60df | 2019-07-17 11:55:10 -0700 | [diff] [blame] | 11 | #include "net/third_party/quiche/src/quic/core/http/http_constants.h" |
renjietang | 7d4f913 | 2019-06-20 15:04:34 -0700 | [diff] [blame] | 12 | #include "net/third_party/quiche/src/quic/core/http/http_decoder.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 13 | #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" |
| 14 | #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 15 | #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" |
| 16 | #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 17 | #include "net/third_party/quiche/src/quic/core/quic_utils.h" |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 18 | #include "net/third_party/quiche/src/quic/core/quic_versions.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 19 | #include "net/third_party/quiche/src/quic/core/quic_write_blocked_list.h" |
| 20 | #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" |
| 21 | #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" |
| 22 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| 23 | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| 24 | #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice_storage.h" |
bnc | 4e9283d | 2019-12-17 07:08:57 -0800 | [diff] [blame] | 25 | #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 26 | #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" |
| 27 | #include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 28 | #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" |
| 29 | |
| 30 | using spdy::SpdyHeaderBlock; |
| 31 | using spdy::SpdyPriority; |
| 32 | |
| 33 | namespace quic { |
| 34 | |
| 35 | // Visitor of HttpDecoder that passes data frame to QuicSpdyStream and closes |
| 36 | // the connection on unexpected frames. |
| 37 | class QuicSpdyStream::HttpDecoderVisitor : public HttpDecoder::Visitor { |
| 38 | public: |
| 39 | explicit HttpDecoderVisitor(QuicSpdyStream* stream) : stream_(stream) {} |
| 40 | HttpDecoderVisitor(const HttpDecoderVisitor&) = delete; |
| 41 | HttpDecoderVisitor& operator=(const HttpDecoderVisitor&) = delete; |
| 42 | |
bnc | dfabdfb | 2020-01-17 17:46:40 -0800 | [diff] [blame] | 43 | void OnError(HttpDecoder* decoder) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 44 | stream_->session()->connection()->CloseConnection( |
bnc | dfabdfb | 2020-01-17 17:46:40 -0800 | [diff] [blame] | 45 | decoder->error(), decoder->error_detail(), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 46 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 47 | } |
| 48 | |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 49 | bool OnCancelPushFrame(const CancelPushFrame& /*frame*/) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 50 | CloseConnectionOnWrongFrame("Cancel Push"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 51 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 52 | } |
| 53 | |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 54 | bool OnMaxPushIdFrame(const MaxPushIdFrame& /*frame*/) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 55 | CloseConnectionOnWrongFrame("Max Push Id"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 56 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 57 | } |
| 58 | |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 59 | bool OnGoAwayFrame(const GoAwayFrame& /*frame*/) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 60 | CloseConnectionOnWrongFrame("Goaway"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 61 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 62 | } |
| 63 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 64 | bool OnSettingsFrameStart(QuicByteCount /*header_length*/) override { |
renjietang | f41bf64 | 2019-04-02 11:45:34 -0700 | [diff] [blame] | 65 | CloseConnectionOnWrongFrame("Settings"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 66 | return false; |
renjietang | f41bf64 | 2019-04-02 11:45:34 -0700 | [diff] [blame] | 67 | } |
| 68 | |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 69 | bool OnSettingsFrame(const SettingsFrame& /*frame*/) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 70 | CloseConnectionOnWrongFrame("Settings"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 71 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 72 | } |
| 73 | |
dschinazi | 17d4242 | 2019-06-18 16:35:07 -0700 | [diff] [blame] | 74 | bool OnDuplicatePushFrame(const DuplicatePushFrame& /*frame*/) override { |
bnc | bf3dbe5 | 2019-07-17 05:17:41 -0700 | [diff] [blame] | 75 | // TODO(b/137554973): Consume frame. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 76 | CloseConnectionOnWrongFrame("Duplicate Push"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 77 | return false; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 78 | } |
| 79 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 80 | bool OnDataFrameStart(QuicByteCount header_length) override { |
| 81 | return stream_->OnDataFrameStart(header_length); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 82 | } |
| 83 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 84 | bool OnDataFramePayload(quiche::QuicheStringPiece payload) override { |
bnc | 7091426 | 2019-03-16 12:49:50 -0700 | [diff] [blame] | 85 | DCHECK(!payload.empty()); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 86 | return stream_->OnDataFramePayload(payload); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 87 | } |
| 88 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 89 | bool OnDataFrameEnd() override { return stream_->OnDataFrameEnd(); } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 90 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 91 | bool OnHeadersFrameStart(QuicByteCount header_length) override { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 92 | if (!VersionUsesHttp3(stream_->transport_version())) { |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 93 | CloseConnectionOnWrongFrame("Headers"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 94 | return false; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 95 | } |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 96 | return stream_->OnHeadersFrameStart(header_length); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 97 | } |
| 98 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 99 | bool OnHeadersFramePayload(quiche::QuicheStringPiece payload) override { |
bnc | 7091426 | 2019-03-16 12:49:50 -0700 | [diff] [blame] | 100 | DCHECK(!payload.empty()); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 101 | if (!VersionUsesHttp3(stream_->transport_version())) { |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 102 | CloseConnectionOnWrongFrame("Headers"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 103 | return false; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 104 | } |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 105 | return stream_->OnHeadersFramePayload(payload); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 106 | } |
| 107 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 108 | bool OnHeadersFrameEnd() override { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 109 | if (!VersionUsesHttp3(stream_->transport_version())) { |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 110 | CloseConnectionOnWrongFrame("Headers"); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 111 | return false; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 112 | } |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 113 | return stream_->OnHeadersFrameEnd(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 114 | } |
| 115 | |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 116 | bool OnPushPromiseFrameStart(QuicByteCount header_length) override { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 117 | if (!VersionUsesHttp3(stream_->transport_version())) { |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 118 | CloseConnectionOnWrongFrame("Push Promise"); |
| 119 | return false; |
| 120 | } |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 121 | return stream_->OnPushPromiseFrameStart(header_length); |
| 122 | } |
| 123 | |
| 124 | bool OnPushPromiseFramePushId(PushId push_id, |
| 125 | QuicByteCount push_id_length) override { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 126 | if (!VersionUsesHttp3(stream_->transport_version())) { |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 127 | CloseConnectionOnWrongFrame("Push Promise"); |
| 128 | return false; |
| 129 | } |
| 130 | return stream_->OnPushPromiseFramePushId(push_id, push_id_length); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 131 | } |
| 132 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 133 | bool OnPushPromiseFramePayload(quiche::QuicheStringPiece payload) override { |
bnc | 7091426 | 2019-03-16 12:49:50 -0700 | [diff] [blame] | 134 | DCHECK(!payload.empty()); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 135 | if (!VersionUsesHttp3(stream_->transport_version())) { |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 136 | CloseConnectionOnWrongFrame("Push Promise"); |
| 137 | return false; |
| 138 | } |
| 139 | return stream_->OnPushPromiseFramePayload(payload); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 140 | } |
| 141 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 142 | bool OnPushPromiseFrameEnd() override { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 143 | if (!VersionUsesHttp3(stream_->transport_version())) { |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 144 | CloseConnectionOnWrongFrame("Push Promise"); |
| 145 | return false; |
| 146 | } |
| 147 | return stream_->OnPushPromiseFrameEnd(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 148 | } |
| 149 | |
bnc | 51e8962 | 2020-01-10 10:40:32 -0800 | [diff] [blame] | 150 | bool OnPriorityUpdateFrameStart(QuicByteCount /*header_length*/) override { |
| 151 | CloseConnectionOnWrongFrame("Priority update"); |
| 152 | return false; |
| 153 | } |
| 154 | |
| 155 | bool OnPriorityUpdateFrame(const PriorityUpdateFrame& /*frame*/) override { |
| 156 | CloseConnectionOnWrongFrame("Priority update"); |
| 157 | return false; |
| 158 | } |
| 159 | |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 160 | bool OnUnknownFrameStart(uint64_t frame_type, |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 161 | QuicByteCount header_length) override { |
| 162 | return stream_->OnUnknownFrameStart(frame_type, header_length); |
bnc | bf3dbe5 | 2019-07-17 05:17:41 -0700 | [diff] [blame] | 163 | } |
| 164 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 165 | bool OnUnknownFramePayload(quiche::QuicheStringPiece payload) override { |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 166 | return stream_->OnUnknownFramePayload(payload); |
bnc | bf3dbe5 | 2019-07-17 05:17:41 -0700 | [diff] [blame] | 167 | } |
| 168 | |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 169 | bool OnUnknownFrameEnd() override { return stream_->OnUnknownFrameEnd(); } |
bnc | bf3dbe5 | 2019-07-17 05:17:41 -0700 | [diff] [blame] | 170 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 171 | private: |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 172 | void CloseConnectionOnWrongFrame(quiche::QuicheStringPiece frame_type) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 173 | stream_->session()->connection()->CloseConnection( |
bnc | dfabdfb | 2020-01-17 17:46:40 -0800 | [diff] [blame] | 174 | QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM, |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 175 | quiche::QuicheStrCat(frame_type, " frame received on data stream"), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 176 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 177 | } |
| 178 | |
| 179 | QuicSpdyStream* stream_; |
| 180 | }; |
| 181 | |
| 182 | #define ENDPOINT \ |
| 183 | (session()->perspective() == Perspective::IS_SERVER ? "Server: " \ |
| 184 | : "Client:" \ |
| 185 | " ") |
| 186 | |
| 187 | QuicSpdyStream::QuicSpdyStream(QuicStreamId id, |
| 188 | QuicSpdySession* spdy_session, |
| 189 | StreamType type) |
| 190 | : QuicStream(id, spdy_session, /*is_static=*/false, type), |
| 191 | spdy_session_(spdy_session), |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 192 | on_body_available_called_because_sequencer_is_closed_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 193 | visitor_(nullptr), |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 194 | blocked_on_decoding_headers_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 195 | headers_decompressed_(false), |
bnc | 446887e | 2019-11-27 13:08:28 -0800 | [diff] [blame] | 196 | header_list_size_limit_exceeded_(false), |
renjietang | 6f572a8 | 2019-07-23 16:33:13 -0700 | [diff] [blame] | 197 | headers_payload_length_(0), |
| 198 | trailers_payload_length_(0), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 199 | trailers_decompressed_(false), |
| 200 | trailers_consumed_(false), |
renjietang | 7498c8c | 2019-07-02 19:28:42 -0700 | [diff] [blame] | 201 | priority_sent_(false), |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 202 | http_decoder_visitor_(std::make_unique<HttpDecoderVisitor>(this)), |
bnc | a9bb469 | 2019-07-09 17:29:48 -0700 | [diff] [blame] | 203 | decoder_(http_decoder_visitor_.get()), |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 204 | sequencer_offset_(0), |
| 205 | is_decoder_processing_input_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 206 | ack_listener_(nullptr) { |
bnc | fa1de5f | 2019-07-09 17:53:56 -0700 | [diff] [blame] | 207 | DCHECK_EQ(session()->connection(), spdy_session->connection()); |
renjietang | d1d0085 | 2019-09-06 10:43:12 -0700 | [diff] [blame] | 208 | DCHECK_EQ(transport_version(), spdy_session->transport_version()); |
bnc | fa1de5f | 2019-07-09 17:53:56 -0700 | [diff] [blame] | 209 | DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id)); |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 210 | DCHECK_EQ(0u, sequencer()->NumBytesConsumed()); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 211 | // If headers are sent on the headers stream, then do not receive any |
| 212 | // callbacks from the sequencer until headers are complete. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 213 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 214 | sequencer()->SetBlockedUntilFlush(); |
| 215 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 216 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 217 | if (VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 218 | sequencer()->set_level_triggered(true); |
| 219 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 220 | } |
| 221 | |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 222 | QuicSpdyStream::QuicSpdyStream(PendingStream* pending, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 223 | QuicSpdySession* spdy_session, |
| 224 | StreamType type) |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 225 | : QuicStream(pending, type, /*is_static=*/false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 226 | spdy_session_(spdy_session), |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 227 | on_body_available_called_because_sequencer_is_closed_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 228 | visitor_(nullptr), |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 229 | blocked_on_decoding_headers_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 230 | headers_decompressed_(false), |
bnc | 446887e | 2019-11-27 13:08:28 -0800 | [diff] [blame] | 231 | header_list_size_limit_exceeded_(false), |
renjietang | 6f572a8 | 2019-07-23 16:33:13 -0700 | [diff] [blame] | 232 | headers_payload_length_(0), |
| 233 | trailers_payload_length_(0), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 234 | trailers_decompressed_(false), |
| 235 | trailers_consumed_(false), |
renjietang | 7498c8c | 2019-07-02 19:28:42 -0700 | [diff] [blame] | 236 | priority_sent_(false), |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 237 | http_decoder_visitor_(std::make_unique<HttpDecoderVisitor>(this)), |
bnc | a9bb469 | 2019-07-09 17:29:48 -0700 | [diff] [blame] | 238 | decoder_(http_decoder_visitor_.get()), |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 239 | sequencer_offset_(sequencer()->NumBytesConsumed()), |
| 240 | is_decoder_processing_input_(false), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 241 | ack_listener_(nullptr) { |
bnc | fa1de5f | 2019-07-09 17:53:56 -0700 | [diff] [blame] | 242 | DCHECK_EQ(session()->connection(), spdy_session->connection()); |
renjietang | d1d0085 | 2019-09-06 10:43:12 -0700 | [diff] [blame] | 243 | DCHECK_EQ(transport_version(), spdy_session->transport_version()); |
bnc | fa1de5f | 2019-07-09 17:53:56 -0700 | [diff] [blame] | 244 | DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id())); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 245 | // If headers are sent on the headers stream, then do not receive any |
| 246 | // callbacks from the sequencer until headers are complete. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 247 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 248 | sequencer()->SetBlockedUntilFlush(); |
| 249 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 250 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 251 | if (VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 252 | sequencer()->set_level_triggered(true); |
| 253 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | QuicSpdyStream::~QuicSpdyStream() {} |
| 257 | |
| 258 | size_t QuicSpdyStream::WriteHeaders( |
| 259 | SpdyHeaderBlock header_block, |
| 260 | bool fin, |
| 261 | QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { |
fayang | a4b37b2 | 2019-06-18 13:37:47 -0700 | [diff] [blame] | 262 | QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 263 | // Send stream type for server push stream |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 264 | if (VersionUsesHttp3(transport_version()) && type() == WRITE_UNIDIRECTIONAL && |
| 265 | send_buffer().stream_offset() == 0) { |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 266 | char data[sizeof(kServerPushStream)]; |
bnc | 4e9283d | 2019-12-17 07:08:57 -0800 | [diff] [blame] | 267 | QuicDataWriter writer(QUICHE_ARRAYSIZE(data), data); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 268 | writer.WriteVarInt62(kServerPushStream); |
| 269 | |
| 270 | // Similar to frame headers, stream type byte shouldn't be exposed to upper |
| 271 | // layer applications. |
| 272 | unacked_frame_headers_offsets_.Add(0, writer.length()); |
| 273 | |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 274 | QUIC_LOG(INFO) << ENDPOINT << "Stream " << id() |
| 275 | << " is writing type as server push"; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 276 | WriteOrBufferData(quiche::QuicheStringPiece(writer.data(), writer.length()), |
| 277 | false, nullptr); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 278 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 279 | size_t bytes_written = |
| 280 | WriteHeadersImpl(std::move(header_block), fin, std::move(ack_listener)); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 281 | if (!VersionUsesHttp3(transport_version()) && fin) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 282 | // If HEADERS are sent on the headers stream, then |fin_sent_| needs to be |
| 283 | // set and write side needs to be closed without actually sending a FIN on |
| 284 | // this stream. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 285 | // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent. |
| 286 | set_fin_sent(true); |
| 287 | CloseWriteSide(); |
| 288 | } |
| 289 | return bytes_written; |
| 290 | } |
| 291 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 292 | void QuicSpdyStream::WriteOrBufferBody(quiche::QuicheStringPiece data, |
| 293 | bool fin) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 294 | if (!VersionUsesHttp3(transport_version()) || data.length() == 0) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 295 | WriteOrBufferData(data, fin, nullptr); |
| 296 | return; |
| 297 | } |
fayang | a4b37b2 | 2019-06-18 13:37:47 -0700 | [diff] [blame] | 298 | QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 299 | |
| 300 | // Write frame header. |
| 301 | std::unique_ptr<char[]> buffer; |
| 302 | QuicByteCount header_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 303 | HttpEncoder::SerializeDataFrameHeader(data.length(), &buffer); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 304 | unacked_frame_headers_offsets_.Add( |
| 305 | send_buffer().stream_offset(), |
| 306 | send_buffer().stream_offset() + header_length); |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 307 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 308 | << " is writing DATA frame header of length " |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 309 | << header_length; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 310 | WriteOrBufferData(quiche::QuicheStringPiece(buffer.get(), header_length), |
| 311 | false, nullptr); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 312 | |
| 313 | // Write body. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 314 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 315 | << " is writing DATA frame payload of length " |
renjietang | dbe9834 | 2019-10-18 11:00:57 -0700 | [diff] [blame] | 316 | << data.length() << " with fin " << fin; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 317 | WriteOrBufferData(data, fin, nullptr); |
| 318 | } |
| 319 | |
| 320 | size_t QuicSpdyStream::WriteTrailers( |
| 321 | SpdyHeaderBlock trailer_block, |
| 322 | QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { |
| 323 | if (fin_sent()) { |
| 324 | QUIC_BUG << "Trailers cannot be sent after a FIN, on stream " << id(); |
| 325 | return 0; |
| 326 | } |
| 327 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 328 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 329 | // The header block must contain the final offset for this stream, as the |
| 330 | // trailers may be processed out of order at the peer. |
| 331 | const QuicStreamOffset final_offset = |
| 332 | stream_bytes_written() + BufferedDataBytes(); |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 333 | QUIC_DLOG(INFO) << ENDPOINT << "Inserting trailer: (" |
| 334 | << kFinalOffsetHeaderKey << ", " << final_offset << ")"; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 335 | trailer_block.insert( |
| 336 | std::make_pair(kFinalOffsetHeaderKey, |
| 337 | quiche::QuicheTextUtils::Uint64ToString(final_offset))); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 338 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 339 | |
| 340 | // Write the trailing headers with a FIN, and close stream for writing: |
| 341 | // trailers are the last thing to be sent on a stream. |
| 342 | const bool kFin = true; |
| 343 | size_t bytes_written = |
| 344 | WriteHeadersImpl(std::move(trailer_block), kFin, std::move(ack_listener)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 345 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 346 | // If trailers are sent on the headers stream, then |fin_sent_| needs to be |
| 347 | // set without actually sending a FIN on this stream. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 348 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 349 | set_fin_sent(kFin); |
| 350 | |
| 351 | // Also, write side of this stream needs to be closed. However, only do |
| 352 | // this if there is no more buffered data, otherwise it will never be sent. |
| 353 | if (BufferedDataBytes() == 0) { |
| 354 | CloseWriteSide(); |
| 355 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | return bytes_written; |
| 359 | } |
| 360 | |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 361 | void QuicSpdyStream::WritePushPromise(const PushPromiseFrame& frame) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 362 | DCHECK(VersionUsesHttp3(transport_version())); |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 363 | std::unique_ptr<char[]> push_promise_frame_with_id; |
| 364 | const size_t push_promise_frame_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 365 | HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 366 | frame, &push_promise_frame_with_id); |
| 367 | |
| 368 | unacked_frame_headers_offsets_.Add(send_buffer().stream_offset(), |
| 369 | send_buffer().stream_offset() + |
| 370 | push_promise_frame_length + |
| 371 | frame.headers.length()); |
| 372 | |
| 373 | // Write Push Promise frame header and push id. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 374 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 375 | << " is writing Push Promise frame header of length " |
| 376 | << push_promise_frame_length << " , with promised id " |
| 377 | << frame.push_id; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 378 | WriteOrBufferData(quiche::QuicheStringPiece(push_promise_frame_with_id.get(), |
| 379 | push_promise_frame_length), |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 380 | /* fin = */ false, /* ack_listener = */ nullptr); |
| 381 | |
| 382 | // Write response headers. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 383 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 384 | << " is writing Push Promise request header of length " |
| 385 | << frame.headers.length(); |
| 386 | WriteOrBufferData(frame.headers, /* fin = */ false, |
| 387 | /* ack_listener = */ nullptr); |
| 388 | } |
| 389 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 390 | QuicConsumedData QuicSpdyStream::WritevBody(const struct iovec* iov, |
| 391 | int count, |
| 392 | bool fin) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 393 | QuicMemSliceStorage storage( |
| 394 | iov, count, |
| 395 | session()->connection()->helper()->GetStreamSendBufferAllocator(), |
| 396 | GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size)); |
| 397 | return WriteBodySlices(storage.ToSpan(), fin); |
| 398 | } |
| 399 | |
| 400 | QuicConsumedData QuicSpdyStream::WriteBodySlices(QuicMemSliceSpan slices, |
| 401 | bool fin) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 402 | if (!VersionUsesHttp3(transport_version()) || slices.empty()) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 403 | return WriteMemSlices(slices, fin); |
| 404 | } |
| 405 | |
| 406 | std::unique_ptr<char[]> buffer; |
| 407 | QuicByteCount header_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 408 | HttpEncoder::SerializeDataFrameHeader(slices.total_length(), &buffer); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 409 | if (!CanWriteNewDataAfterData(header_length)) { |
| 410 | return {0, false}; |
| 411 | } |
| 412 | |
fayang | a4b37b2 | 2019-06-18 13:37:47 -0700 | [diff] [blame] | 413 | QuicConnection::ScopedPacketFlusher flusher(spdy_session_->connection()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 414 | |
| 415 | // Write frame header. |
| 416 | struct iovec header_iov = {static_cast<void*>(buffer.get()), header_length}; |
| 417 | QuicMemSliceStorage storage( |
| 418 | &header_iov, 1, |
| 419 | spdy_session_->connection()->helper()->GetStreamSendBufferAllocator(), |
| 420 | GetQuicFlag(FLAGS_quic_send_buffer_max_data_slice_size)); |
| 421 | unacked_frame_headers_offsets_.Add( |
| 422 | send_buffer().stream_offset(), |
| 423 | send_buffer().stream_offset() + header_length); |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 424 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 425 | << " is writing DATA frame header of length " |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 426 | << header_length; |
| 427 | WriteMemSlices(storage.ToSpan(), false); |
| 428 | |
| 429 | // Write body. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 430 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 431 | << " is writing DATA frame payload of length " |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 432 | << slices.total_length(); |
| 433 | return WriteMemSlices(slices, fin); |
| 434 | } |
| 435 | |
| 436 | size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) { |
| 437 | DCHECK(FinishedReadingHeaders()); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 438 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 439 | return sequencer()->Readv(iov, iov_len); |
| 440 | } |
bnc | c901949 | 2019-07-23 06:44:39 -0700 | [diff] [blame] | 441 | size_t bytes_read = 0; |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 442 | sequencer()->MarkConsumed(body_manager_.ReadBody(iov, iov_len, &bytes_read)); |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 443 | |
renjietang | bd1a039 | 2019-05-31 11:36:24 -0700 | [diff] [blame] | 444 | return bytes_read; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 445 | } |
| 446 | |
| 447 | int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const { |
| 448 | DCHECK(FinishedReadingHeaders()); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 449 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 450 | return sequencer()->GetReadableRegions(iov, iov_len); |
| 451 | } |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 452 | return body_manager_.PeekBody(iov, iov_len); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 453 | } |
| 454 | |
| 455 | void QuicSpdyStream::MarkConsumed(size_t num_bytes) { |
| 456 | DCHECK(FinishedReadingHeaders()); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 457 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 458 | sequencer()->MarkConsumed(num_bytes); |
| 459 | return; |
| 460 | } |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 461 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 462 | sequencer()->MarkConsumed(body_manager_.OnBodyConsumed(num_bytes)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 463 | } |
| 464 | |
| 465 | bool QuicSpdyStream::IsDoneReading() const { |
| 466 | bool done_reading_headers = FinishedReadingHeaders(); |
| 467 | bool done_reading_body = sequencer()->IsClosed(); |
| 468 | bool done_reading_trailers = FinishedReadingTrailers(); |
| 469 | return done_reading_headers && done_reading_body && done_reading_trailers; |
| 470 | } |
| 471 | |
| 472 | bool QuicSpdyStream::HasBytesToRead() const { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 473 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 474 | return sequencer()->HasBytesToRead(); |
| 475 | } |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 476 | return body_manager_.HasBytesToRead(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 477 | } |
| 478 | |
| 479 | void QuicSpdyStream::MarkTrailersConsumed() { |
| 480 | trailers_consumed_ = true; |
| 481 | } |
| 482 | |
| 483 | uint64_t QuicSpdyStream::total_body_bytes_read() const { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 484 | if (VersionUsesHttp3(transport_version())) { |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 485 | return body_manager_.total_body_bytes_received(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 486 | } |
| 487 | return sequencer()->NumBytesConsumed(); |
| 488 | } |
| 489 | |
| 490 | void QuicSpdyStream::ConsumeHeaderList() { |
| 491 | header_list_.Clear(); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 492 | |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 493 | if (!FinishedReadingHeaders()) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 494 | return; |
| 495 | } |
| 496 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 497 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 498 | sequencer()->SetUnblocked(); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 499 | return; |
| 500 | } |
| 501 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 502 | if (body_manager_.HasBytesToRead()) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 503 | OnBodyAvailable(); |
| 504 | return; |
| 505 | } |
| 506 | |
| 507 | if (sequencer()->IsClosed() && |
| 508 | !on_body_available_called_because_sequencer_is_closed_) { |
| 509 | on_body_available_called_because_sequencer_is_closed_ = true; |
| 510 | OnBodyAvailable(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 511 | } |
| 512 | } |
| 513 | |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 514 | void QuicSpdyStream::OnStreamHeadersPriority( |
| 515 | const spdy::SpdyStreamPrecedence& precedence) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 516 | DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 517 | SetPriority(precedence); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 518 | } |
| 519 | |
bnc | 6270680 | 2019-06-26 12:37:46 -0700 | [diff] [blame] | 520 | void QuicSpdyStream::OnStreamHeaderList(bool fin, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 521 | size_t frame_len, |
| 522 | const QuicHeaderList& header_list) { |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 523 | // TODO(b/134706391): remove |fin| argument. |
bnc | 446887e | 2019-11-27 13:08:28 -0800 | [diff] [blame] | 524 | // When using Google QUIC, an empty header list indicates that the size limit |
| 525 | // has been exceeded. |
| 526 | // When using IETF QUIC, there is an explicit signal from |
| 527 | // QpackDecodedHeadersAccumulator. |
| 528 | if ((VersionUsesHttp3(transport_version()) && |
| 529 | header_list_size_limit_exceeded_) || |
| 530 | (!VersionUsesHttp3(transport_version()) && header_list.empty())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 531 | OnHeadersTooLarge(); |
| 532 | if (IsDoneReading()) { |
bnc | 6270680 | 2019-06-26 12:37:46 -0700 | [diff] [blame] | 533 | return; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 534 | } |
| 535 | } |
| 536 | if (!headers_decompressed_) { |
| 537 | OnInitialHeadersComplete(fin, frame_len, header_list); |
| 538 | } else { |
| 539 | OnTrailingHeadersComplete(fin, frame_len, header_list); |
| 540 | } |
| 541 | } |
| 542 | |
bnc | b7ac104 | 2019-12-06 10:32:23 -0800 | [diff] [blame] | 543 | void QuicSpdyStream::OnHeadersDecoded(QuicHeaderList headers, |
| 544 | bool header_list_size_limit_exceeded) { |
| 545 | header_list_size_limit_exceeded_ = header_list_size_limit_exceeded; |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 546 | qpack_decoded_headers_accumulator_.reset(); |
| 547 | |
| 548 | QuicSpdySession::LogHeaderCompressionRatioHistogram( |
| 549 | /* using_qpack = */ true, |
| 550 | /* is_sent = */ false, headers.compressed_header_bytes(), |
| 551 | headers.uncompressed_header_bytes()); |
| 552 | |
| 553 | if (spdy_session_->promised_stream_id() == |
| 554 | QuicUtils::GetInvalidStreamId(session()->transport_version())) { |
| 555 | const QuicByteCount frame_length = headers_decompressed_ |
| 556 | ? trailers_payload_length_ |
| 557 | : headers_payload_length_; |
| 558 | OnStreamHeaderList(/* fin = */ false, frame_length, headers); |
| 559 | } else { |
| 560 | spdy_session_->OnHeaderList(headers); |
| 561 | } |
| 562 | |
| 563 | if (blocked_on_decoding_headers_) { |
| 564 | blocked_on_decoding_headers_ = false; |
| 565 | // Continue decoding HTTP/3 frames. |
| 566 | OnDataAvailable(); |
| 567 | } |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 568 | } |
| 569 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 570 | void QuicSpdyStream::OnHeaderDecodingError( |
| 571 | quiche::QuicheStringPiece error_message) { |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 572 | qpack_decoded_headers_accumulator_.reset(); |
| 573 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 574 | std::string connection_close_error_message = quiche::QuicheStrCat( |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 575 | "Error decoding ", headers_decompressed_ ? "trailers" : "headers", |
| 576 | " on stream ", id(), ": ", error_message); |
| 577 | CloseConnectionWithDetails(QUIC_QPACK_DECOMPRESSION_FAILED, |
| 578 | connection_close_error_message); |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 579 | } |
| 580 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 581 | void QuicSpdyStream::OnHeadersTooLarge() { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 582 | if (VersionUsesHttp3(transport_version())) { |
bnc | 446887e | 2019-11-27 13:08:28 -0800 | [diff] [blame] | 583 | // TODO(b/124216424): Reset stream with H3_REQUEST_CANCELLED (if client) |
| 584 | // or with H3_REQUEST_REJECTED (if server). |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 585 | std::string error_message = |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 586 | quiche::QuicheStrCat("Too large headers received on stream ", id()); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 587 | CloseConnectionWithDetails(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE, |
| 588 | error_message); |
| 589 | } else { |
| 590 | Reset(QUIC_HEADERS_TOO_LARGE); |
| 591 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 592 | } |
| 593 | |
| 594 | void QuicSpdyStream::OnInitialHeadersComplete( |
| 595 | bool fin, |
| 596 | size_t /*frame_len*/, |
| 597 | const QuicHeaderList& header_list) { |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 598 | // TODO(b/134706391): remove |fin| argument. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 599 | headers_decompressed_ = true; |
| 600 | header_list_ = header_list; |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 601 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 602 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 603 | if (fin) { |
| 604 | OnStreamFrame( |
| 605 | QuicStreamFrame(id(), /* fin = */ true, |
| 606 | flow_controller()->highest_received_byte_offset(), |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 607 | quiche::QuicheStringPiece())); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 608 | } |
| 609 | return; |
| 610 | } |
| 611 | |
bnc | 70f78fc | 2019-08-26 10:12:31 -0700 | [diff] [blame] | 612 | if (fin && !rst_sent()) { |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 613 | OnStreamFrame(QuicStreamFrame(id(), fin, /* offset = */ 0, |
| 614 | quiche::QuicheStringPiece())); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 615 | } |
| 616 | if (FinishedReadingHeaders()) { |
| 617 | sequencer()->SetUnblocked(); |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | void QuicSpdyStream::OnPromiseHeaderList( |
| 622 | QuicStreamId /* promised_id */, |
| 623 | size_t /* frame_len */, |
| 624 | const QuicHeaderList& /*header_list */) { |
| 625 | // To be overridden in QuicSpdyClientStream. Not supported on |
| 626 | // server side. |
| 627 | session()->connection()->CloseConnection( |
| 628 | QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server", |
| 629 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 630 | } |
| 631 | |
| 632 | void QuicSpdyStream::OnTrailingHeadersComplete( |
| 633 | bool fin, |
| 634 | size_t /*frame_len*/, |
| 635 | const QuicHeaderList& header_list) { |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 636 | // TODO(b/134706391): remove |fin| argument. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 637 | DCHECK(!trailers_decompressed_); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 638 | if (!VersionUsesHttp3(transport_version()) && fin_received()) { |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 639 | QUIC_DLOG(INFO) << ENDPOINT |
| 640 | << "Received Trailers after FIN, on stream: " << id(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 641 | session()->connection()->CloseConnection( |
| 642 | QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin", |
| 643 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 644 | return; |
| 645 | } |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 646 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 647 | if (!VersionUsesHttp3(transport_version()) && !fin) { |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 648 | QUIC_DLOG(INFO) << ENDPOINT |
| 649 | << "Trailers must have FIN set, on stream: " << id(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 650 | session()->connection()->CloseConnection( |
| 651 | QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers", |
| 652 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 653 | return; |
| 654 | } |
| 655 | |
| 656 | size_t final_byte_offset = 0; |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 657 | const bool expect_final_byte_offset = !VersionUsesHttp3(transport_version()); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 658 | if (!SpdyUtils::CopyAndValidateTrailers(header_list, expect_final_byte_offset, |
bnc | 5231ee2 | 2019-04-15 19:02:13 -0700 | [diff] [blame] | 659 | &final_byte_offset, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 660 | &received_trailers_)) { |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 661 | QUIC_DLOG(ERROR) << ENDPOINT << "Trailers for stream " << id() |
| 662 | << " are malformed."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 663 | session()->connection()->CloseConnection( |
| 664 | QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed", |
| 665 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 666 | return; |
| 667 | } |
| 668 | trailers_decompressed_ = true; |
bnc | ab33c71 | 2019-06-28 15:42:15 -0700 | [diff] [blame] | 669 | if (fin) { |
| 670 | const QuicStreamOffset offset = |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 671 | VersionUsesHttp3(transport_version()) |
bnc | ab33c71 | 2019-06-28 15:42:15 -0700 | [diff] [blame] | 672 | ? flow_controller()->highest_received_byte_offset() |
| 673 | : final_byte_offset; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 674 | OnStreamFrame( |
| 675 | QuicStreamFrame(id(), fin, offset, quiche::QuicheStringPiece())); |
bnc | ab33c71 | 2019-06-28 15:42:15 -0700 | [diff] [blame] | 676 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 677 | } |
| 678 | |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 679 | void QuicSpdyStream::OnPriorityFrame( |
| 680 | const spdy::SpdyStreamPrecedence& precedence) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 681 | DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 682 | SetPriority(precedence); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 683 | } |
| 684 | |
| 685 | void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { |
| 686 | if (frame.error_code != QUIC_STREAM_NO_ERROR) { |
bnc | 84aa7e5 | 2019-12-13 05:25:21 -0800 | [diff] [blame] | 687 | if (VersionUsesHttp3(transport_version()) && !fin_received() && |
| 688 | spdy_session_->qpack_decoder()) { |
| 689 | spdy_session_->qpack_decoder()->OnStreamReset(id()); |
| 690 | } |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 691 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 692 | QuicStream::OnStreamReset(frame); |
| 693 | return; |
| 694 | } |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 695 | |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 696 | QUIC_DVLOG(1) << ENDPOINT |
| 697 | << "Received QUIC_STREAM_NO_ERROR, not discarding response"; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 698 | set_rst_received(true); |
| 699 | MaybeIncreaseHighestReceivedOffset(frame.byte_offset); |
| 700 | set_stream_error(frame.error_code); |
| 701 | CloseWriteSide(); |
| 702 | } |
| 703 | |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 704 | void QuicSpdyStream::Reset(QuicRstStreamErrorCode error) { |
bnc | 84aa7e5 | 2019-12-13 05:25:21 -0800 | [diff] [blame] | 705 | if (VersionUsesHttp3(transport_version()) && !fin_received() && |
| 706 | spdy_session_->qpack_decoder()) { |
| 707 | spdy_session_->qpack_decoder()->OnStreamReset(id()); |
| 708 | } |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 709 | |
| 710 | QuicStream::Reset(error); |
| 711 | } |
| 712 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 713 | void QuicSpdyStream::OnDataAvailable() { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 714 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 715 | // Sequencer must be blocked until headers are consumed. |
| 716 | DCHECK(FinishedReadingHeaders()); |
| 717 | } |
bnc | fa0c90c | 2019-03-13 14:14:28 -0700 | [diff] [blame] | 718 | |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 719 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 720 | OnBodyAvailable(); |
| 721 | return; |
| 722 | } |
| 723 | |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 724 | if (is_decoder_processing_input_) { |
| 725 | // Let the outermost nested OnDataAvailable() call do the work. |
| 726 | return; |
| 727 | } |
| 728 | |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 729 | if (blocked_on_decoding_headers_) { |
| 730 | return; |
| 731 | } |
| 732 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 733 | iovec iov; |
bnc | ef51815 | 2019-07-18 05:36:44 -0700 | [diff] [blame] | 734 | while (session()->connection()->connected() && !reading_stopped() && |
| 735 | decoder_.error() == QUIC_NO_ERROR) { |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 736 | DCHECK_GE(sequencer_offset_, sequencer()->NumBytesConsumed()); |
| 737 | if (!sequencer()->PeekRegion(sequencer_offset_, &iov)) { |
| 738 | break; |
| 739 | } |
| 740 | |
bnc | d48a92e | 2019-06-18 19:24:58 -0700 | [diff] [blame] | 741 | DCHECK(!sequencer()->IsClosed()); |
bnc | 8aadca7 | 2019-06-28 11:09:39 -0700 | [diff] [blame] | 742 | is_decoder_processing_input_ = true; |
| 743 | QuicByteCount processed_bytes = decoder_.ProcessInput( |
| 744 | reinterpret_cast<const char*>(iov.iov_base), iov.iov_len); |
| 745 | is_decoder_processing_input_ = false; |
| 746 | sequencer_offset_ += processed_bytes; |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 747 | if (blocked_on_decoding_headers_) { |
| 748 | return; |
| 749 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 750 | } |
| 751 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 752 | // Do not call OnBodyAvailable() until headers are consumed. |
| 753 | if (!FinishedReadingHeaders()) { |
| 754 | return; |
| 755 | } |
| 756 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 757 | if (body_manager_.HasBytesToRead()) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 758 | OnBodyAvailable(); |
| 759 | return; |
| 760 | } |
| 761 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 762 | if (sequencer()->IsClosed() && |
| 763 | !on_body_available_called_because_sequencer_is_closed_) { |
| 764 | on_body_available_called_because_sequencer_is_closed_ = true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 765 | OnBodyAvailable(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 766 | } |
| 767 | } |
| 768 | |
| 769 | void QuicSpdyStream::OnClose() { |
| 770 | QuicStream::OnClose(); |
| 771 | |
| 772 | if (visitor_) { |
| 773 | Visitor* visitor = visitor_; |
| 774 | // Calling Visitor::OnClose() may result the destruction of the visitor, |
| 775 | // so we need to ensure we don't call it again. |
| 776 | visitor_ = nullptr; |
| 777 | visitor->OnClose(this); |
| 778 | } |
| 779 | } |
| 780 | |
| 781 | void QuicSpdyStream::OnCanWrite() { |
| 782 | QuicStream::OnCanWrite(); |
| 783 | |
| 784 | // Trailers (and hence a FIN) may have been sent ahead of queued body bytes. |
| 785 | if (!HasBufferedData() && fin_sent()) { |
| 786 | CloseWriteSide(); |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | bool QuicSpdyStream::FinishedReadingHeaders() const { |
| 791 | return headers_decompressed_ && header_list_.empty(); |
| 792 | } |
| 793 | |
bnc | d011990 | 2019-06-26 12:12:33 -0700 | [diff] [blame] | 794 | // static |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 795 | bool QuicSpdyStream::ParseHeaderStatusCode(const SpdyHeaderBlock& header, |
bnc | d011990 | 2019-06-26 12:12:33 -0700 | [diff] [blame] | 796 | int* status_code) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 797 | SpdyHeaderBlock::const_iterator it = header.find(spdy::kHttp2StatusHeader); |
| 798 | if (it == header.end()) { |
| 799 | return false; |
| 800 | } |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 801 | const quiche::QuicheStringPiece status(it->second); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 802 | if (status.size() != 3) { |
| 803 | return false; |
| 804 | } |
| 805 | // First character must be an integer in range [1,5]. |
| 806 | if (status[0] < '1' || status[0] > '5') { |
| 807 | return false; |
| 808 | } |
| 809 | // The remaining two characters must be integers. |
| 810 | if (!isdigit(status[1]) || !isdigit(status[2])) { |
| 811 | return false; |
| 812 | } |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 813 | return quiche::QuicheTextUtils::StringToInt(status, status_code); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 814 | } |
| 815 | |
| 816 | bool QuicSpdyStream::FinishedReadingTrailers() const { |
| 817 | // If no further trailing headers are expected, and the decompressed trailers |
| 818 | // (if any) have been consumed, then reading of trailers is finished. |
| 819 | if (!fin_received()) { |
| 820 | return false; |
| 821 | } else if (!trailers_decompressed_) { |
| 822 | return true; |
| 823 | } else { |
| 824 | return trailers_consumed_; |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | void QuicSpdyStream::ClearSession() { |
| 829 | spdy_session_ = nullptr; |
| 830 | } |
| 831 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 832 | bool QuicSpdyStream::OnDataFrameStart(QuicByteCount header_length) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 833 | DCHECK(VersionUsesHttp3(transport_version())); |
bnc | 519216c | 2019-07-09 05:03:48 -0700 | [diff] [blame] | 834 | if (!headers_decompressed_ || trailers_decompressed_) { |
| 835 | // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME. |
| 836 | session()->connection()->CloseConnection( |
| 837 | QUIC_INVALID_HEADERS_STREAM_DATA, "Unexpected DATA frame received.", |
| 838 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 839 | return false; |
| 840 | } |
| 841 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 842 | sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); |
bnc | e5f9c03 | 2019-07-25 11:30:40 -0700 | [diff] [blame] | 843 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 844 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 845 | } |
| 846 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 847 | bool QuicSpdyStream::OnDataFramePayload(quiche::QuicheStringPiece payload) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 848 | DCHECK(VersionUsesHttp3(transport_version())); |
bnc | fa0c90c | 2019-03-13 14:14:28 -0700 | [diff] [blame] | 849 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 850 | body_manager_.OnBody(payload); |
bnc | e5f9c03 | 2019-07-25 11:30:40 -0700 | [diff] [blame] | 851 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 852 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 853 | } |
| 854 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 855 | bool QuicSpdyStream::OnDataFrameEnd() { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 856 | DCHECK(VersionUsesHttp3(transport_version())); |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 857 | QUIC_DVLOG(1) << ENDPOINT |
| 858 | << "Reaches the end of a data frame. Total bytes received are " |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 859 | << body_manager_.total_body_bytes_received(); |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 860 | return true; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 861 | } |
| 862 | |
| 863 | bool QuicSpdyStream::OnStreamFrameAcked(QuicStreamOffset offset, |
| 864 | QuicByteCount data_length, |
| 865 | bool fin_acked, |
| 866 | QuicTime::Delta ack_delay_time, |
| 867 | QuicByteCount* newly_acked_length) { |
| 868 | const bool new_data_acked = QuicStream::OnStreamFrameAcked( |
| 869 | offset, data_length, fin_acked, ack_delay_time, newly_acked_length); |
| 870 | |
| 871 | const QuicByteCount newly_acked_header_length = |
| 872 | GetNumFrameHeadersInInterval(offset, data_length); |
| 873 | DCHECK_LE(newly_acked_header_length, *newly_acked_length); |
| 874 | unacked_frame_headers_offsets_.Difference(offset, offset + data_length); |
| 875 | if (ack_listener_ != nullptr && new_data_acked) { |
| 876 | ack_listener_->OnPacketAcked( |
| 877 | *newly_acked_length - newly_acked_header_length, ack_delay_time); |
| 878 | } |
| 879 | return new_data_acked; |
| 880 | } |
| 881 | |
| 882 | void QuicSpdyStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, |
| 883 | QuicByteCount data_length, |
| 884 | bool fin_retransmitted) { |
| 885 | QuicStream::OnStreamFrameRetransmitted(offset, data_length, |
| 886 | fin_retransmitted); |
| 887 | |
| 888 | const QuicByteCount retransmitted_header_length = |
| 889 | GetNumFrameHeadersInInterval(offset, data_length); |
| 890 | DCHECK_LE(retransmitted_header_length, data_length); |
| 891 | |
| 892 | if (ack_listener_ != nullptr) { |
| 893 | ack_listener_->OnPacketRetransmitted(data_length - |
| 894 | retransmitted_header_length); |
| 895 | } |
| 896 | } |
| 897 | |
| 898 | QuicByteCount QuicSpdyStream::GetNumFrameHeadersInInterval( |
| 899 | QuicStreamOffset offset, |
| 900 | QuicByteCount data_length) const { |
| 901 | QuicByteCount header_acked_length = 0; |
| 902 | QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); |
| 903 | newly_acked.Intersection(unacked_frame_headers_offsets_); |
| 904 | for (const auto& interval : newly_acked) { |
| 905 | header_acked_length += interval.Length(); |
| 906 | } |
| 907 | return header_acked_length; |
| 908 | } |
| 909 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 910 | bool QuicSpdyStream::OnHeadersFrameStart(QuicByteCount header_length) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 911 | DCHECK(VersionUsesHttp3(transport_version())); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 912 | DCHECK(!qpack_decoded_headers_accumulator_); |
| 913 | |
bnc | 519216c | 2019-07-09 05:03:48 -0700 | [diff] [blame] | 914 | if (trailers_decompressed_) { |
| 915 | // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME. |
| 916 | session()->connection()->CloseConnection( |
| 917 | QUIC_INVALID_HEADERS_STREAM_DATA, |
| 918 | "HEADERS frame received after trailing HEADERS.", |
| 919 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 920 | return false; |
| 921 | } |
| 922 | |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 923 | sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 924 | |
| 925 | qpack_decoded_headers_accumulator_ = |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 926 | std::make_unique<QpackDecodedHeadersAccumulator>( |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 927 | id(), spdy_session_->qpack_decoder(), this, |
bnc | bdd303e | 2019-07-09 05:33:17 -0700 | [diff] [blame] | 928 | spdy_session_->max_inbound_header_list_size()); |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 929 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 930 | return true; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 931 | } |
| 932 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 933 | bool QuicSpdyStream::OnHeadersFramePayload(quiche::QuicheStringPiece payload) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 934 | DCHECK(VersionUsesHttp3(transport_version())); |
bnc | fcd4235 | 2019-09-20 17:55:47 -0700 | [diff] [blame] | 935 | DCHECK(qpack_decoded_headers_accumulator_); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 936 | |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 937 | if (headers_decompressed_) { |
| 938 | trailers_payload_length_ += payload.length(); |
| 939 | } else { |
| 940 | headers_payload_length_ += payload.length(); |
| 941 | } |
| 942 | |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 943 | qpack_decoded_headers_accumulator_->Decode(payload); |
bnc | 8d04130 | 2019-06-10 10:19:04 -0700 | [diff] [blame] | 944 | |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 945 | // |qpack_decoded_headers_accumulator_| is reset if an error is detected. |
bnc | f9ff46b | 2019-11-26 10:32:09 -0800 | [diff] [blame] | 946 | if (!qpack_decoded_headers_accumulator_) { |
| 947 | return false; |
| 948 | } |
| 949 | |
| 950 | sequencer()->MarkConsumed(body_manager_.OnNonBody(payload.size())); |
| 951 | return true; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 952 | } |
| 953 | |
renjietang | 546a628 | 2019-06-03 10:21:21 -0700 | [diff] [blame] | 954 | bool QuicSpdyStream::OnHeadersFrameEnd() { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 955 | DCHECK(VersionUsesHttp3(transport_version())); |
bnc | fcd4235 | 2019-09-20 17:55:47 -0700 | [diff] [blame] | 956 | DCHECK(qpack_decoded_headers_accumulator_); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 957 | |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 958 | qpack_decoded_headers_accumulator_->EndHeaderBlock(); |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 959 | |
bnc | 232ff9b | 2019-11-13 18:38:22 -0800 | [diff] [blame] | 960 | // If decoding is complete or an error is detected, then |
| 961 | // |qpack_decoded_headers_accumulator_| is already reset. |
| 962 | if (qpack_decoded_headers_accumulator_) { |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 963 | blocked_on_decoding_headers_ = true; |
| 964 | return false; |
| 965 | } |
| 966 | |
bnc | c057c01 | 2019-07-02 11:13:22 -0700 | [diff] [blame] | 967 | return !sequencer()->IsClosed() && !reading_stopped(); |
| 968 | } |
| 969 | |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 970 | bool QuicSpdyStream::OnPushPromiseFrameStart(QuicByteCount header_length) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 971 | DCHECK(VersionUsesHttp3(transport_version())); |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 972 | DCHECK(!qpack_decoded_headers_accumulator_); |
| 973 | |
| 974 | sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); |
| 975 | |
| 976 | return true; |
| 977 | } |
| 978 | |
| 979 | bool QuicSpdyStream::OnPushPromiseFramePushId(PushId push_id, |
| 980 | QuicByteCount push_id_length) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 981 | DCHECK(VersionUsesHttp3(transport_version())); |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 982 | DCHECK(!qpack_decoded_headers_accumulator_); |
| 983 | |
| 984 | // TODO(renjietang): Check max push id and handle errors. |
| 985 | spdy_session_->OnPushPromise(id(), push_id); |
bnc | f0db654 | 2019-09-23 11:18:28 -0700 | [diff] [blame] | 986 | sequencer()->MarkConsumed(body_manager_.OnNonBody(push_id_length)); |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 987 | |
| 988 | qpack_decoded_headers_accumulator_ = |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 989 | std::make_unique<QpackDecodedHeadersAccumulator>( |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 990 | id(), spdy_session_->qpack_decoder(), this, |
| 991 | spdy_session_->max_inbound_header_list_size()); |
| 992 | |
| 993 | return true; |
| 994 | } |
| 995 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 996 | bool QuicSpdyStream::OnPushPromiseFramePayload( |
| 997 | quiche::QuicheStringPiece payload) { |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 998 | spdy_session_->OnCompressedFrameSize(payload.length()); |
| 999 | return OnHeadersFramePayload(payload); |
| 1000 | } |
| 1001 | |
| 1002 | bool QuicSpdyStream::OnPushPromiseFrameEnd() { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1003 | DCHECK(VersionUsesHttp3(transport_version())); |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 1004 | |
renjietang | 6fbdaf4 | 2019-07-29 10:50:54 -0700 | [diff] [blame] | 1005 | return OnHeadersFrameEnd(); |
renjietang | 3c3dfb7 | 2019-07-26 11:55:52 -0700 | [diff] [blame] | 1006 | } |
| 1007 | |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1008 | bool QuicSpdyStream::OnUnknownFrameStart(uint64_t frame_type, |
bnc | a2b13be | 2019-07-31 12:04:20 -0700 | [diff] [blame] | 1009 | QuicByteCount header_length) { |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1010 | // Ignore unknown frames, but consume frame header. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 1011 | QUIC_DVLOG(1) << ENDPOINT << "Discarding " << header_length |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1012 | << " byte long frame header of frame of unknown type " |
| 1013 | << frame_type << "."; |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 1014 | sequencer()->MarkConsumed(body_manager_.OnNonBody(header_length)); |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1015 | return true; |
| 1016 | } |
| 1017 | |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 1018 | bool QuicSpdyStream::OnUnknownFramePayload(quiche::QuicheStringPiece payload) { |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1019 | // Ignore unknown frames, but consume frame payload. |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 1020 | QUIC_DVLOG(1) << ENDPOINT << "Discarding " << payload.size() |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1021 | << " bytes of payload of frame of unknown type."; |
bnc | 0b83e0c | 2019-08-16 15:56:32 -0700 | [diff] [blame] | 1022 | sequencer()->MarkConsumed(body_manager_.OnNonBody(payload.size())); |
bnc | 25827c4 | 2019-07-29 08:57:24 -0700 | [diff] [blame] | 1023 | return true; |
| 1024 | } |
| 1025 | |
| 1026 | bool QuicSpdyStream::OnUnknownFrameEnd() { |
| 1027 | return true; |
| 1028 | } |
| 1029 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1030 | size_t QuicSpdyStream::WriteHeadersImpl( |
| 1031 | spdy::SpdyHeaderBlock header_block, |
| 1032 | bool fin, |
| 1033 | QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1034 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1035 | return spdy_session_->WriteHeadersOnHeadersStream( |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 1036 | id(), std::move(header_block), fin, precedence(), |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1037 | std::move(ack_listener)); |
| 1038 | } |
| 1039 | |
bnc | cd3c4b5 | 2020-01-10 11:13:18 -0800 | [diff] [blame] | 1040 | if (!priority_sent_) { |
| 1041 | PriorityUpdateFrame priority_update; |
| 1042 | priority_update.prioritized_element_type = REQUEST_STREAM; |
| 1043 | priority_update.prioritized_element_id = id(); |
| 1044 | // Value between 0 and 7, inclusive. Lower value means higher priority. |
| 1045 | int urgency = precedence().spdy3_priority(); |
| 1046 | priority_update.priority_field_value = quiche::QuicheStrCat("u=", urgency); |
| 1047 | spdy_session_->WriteHttp3PriorityUpdate(priority_update); |
| 1048 | priority_sent_ = true; |
| 1049 | } |
renjietang | 7498c8c | 2019-07-02 19:28:42 -0700 | [diff] [blame] | 1050 | |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1051 | // Encode header list. |
bnc | 609c24e | 2019-09-10 05:24:32 -0700 | [diff] [blame] | 1052 | QuicByteCount encoder_stream_sent_byte_count; |
bnc | f21c1ad | 2019-06-20 20:09:50 -0700 | [diff] [blame] | 1053 | std::string encoded_headers = |
bnc | 609c24e | 2019-09-10 05:24:32 -0700 | [diff] [blame] | 1054 | spdy_session_->qpack_encoder()->EncodeHeaderList( |
| 1055 | id(), header_block, &encoder_stream_sent_byte_count); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1056 | |
| 1057 | // Write HEADERS frame. |
| 1058 | std::unique_ptr<char[]> headers_frame_header; |
| 1059 | const size_t headers_frame_header_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 1060 | HttpEncoder::SerializeHeadersFrameHeader(encoded_headers.size(), |
| 1061 | &headers_frame_header); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1062 | unacked_frame_headers_offsets_.Add( |
| 1063 | send_buffer().stream_offset(), |
| 1064 | send_buffer().stream_offset() + headers_frame_header_length); |
| 1065 | |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 1066 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1067 | << " is writing HEADERS frame header of length " |
| 1068 | << headers_frame_header_length; |
dmcardle | ba2fb7e | 2019-12-13 07:44:34 -0800 | [diff] [blame] | 1069 | WriteOrBufferData(quiche::QuicheStringPiece(headers_frame_header.get(), |
| 1070 | headers_frame_header_length), |
| 1071 | /* fin = */ false, /* ack_listener = */ nullptr); |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1072 | |
dschinazi | 88bd5b0 | 2019-10-10 00:52:20 -0700 | [diff] [blame] | 1073 | QUIC_DLOG(INFO) << ENDPOINT << "Stream " << id() |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1074 | << " is writing HEADERS frame payload of length " |
renjietang | dbe9834 | 2019-10-18 11:00:57 -0700 | [diff] [blame] | 1075 | << encoded_headers.length() << " with fin " << fin; |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1076 | WriteOrBufferData(encoded_headers, fin, nullptr); |
| 1077 | |
bnc | 20a9ad9 | 2019-10-01 10:29:14 -0700 | [diff] [blame] | 1078 | QuicSpdySession::LogHeaderCompressionRatioHistogram( |
| 1079 | /* using_qpack = */ true, |
| 1080 | /* is_sent = */ true, |
| 1081 | encoded_headers.size() + encoder_stream_sent_byte_count, |
| 1082 | header_block.TotalBytesUsed()); |
| 1083 | |
bnc | 609c24e | 2019-09-10 05:24:32 -0700 | [diff] [blame] | 1084 | return encoded_headers.size() + encoder_stream_sent_byte_count; |
bnc | 62446bc | 2019-03-14 06:11:25 -0700 | [diff] [blame] | 1085 | } |
| 1086 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1087 | #undef ENDPOINT // undef for jumbo builds |
| 1088 | } // namespace quic |