QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright (c) 2012 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 | |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 5 | #include "quic/tools/quic_simple_server_session.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 6 | |
| 7 | #include <utility> |
| 8 | |
bnc | 38b5aed | 2021-04-02 10:57:41 -0700 | [diff] [blame] | 9 | #include "absl/memory/memory.h" |
vasilvv | 20d73c4 | 2021-04-06 20:14:52 -0700 | [diff] [blame] | 10 | #include "quic/core/http/quic_server_initiated_spdy_stream.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 11 | #include "quic/core/http/quic_spdy_session.h" |
| 12 | #include "quic/core/quic_connection.h" |
| 13 | #include "quic/core/quic_utils.h" |
| 14 | #include "quic/platform/api/quic_flags.h" |
| 15 | #include "quic/platform/api/quic_logging.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 16 | #include "quic/tools/quic_simple_server_stream.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 17 | |
| 18 | namespace quic { |
| 19 | |
| 20 | QuicSimpleServerSession::QuicSimpleServerSession( |
| 21 | const QuicConfig& config, |
| 22 | const ParsedQuicVersionVector& supported_versions, |
| 23 | QuicConnection* connection, |
| 24 | QuicSession::Visitor* visitor, |
nharper | 5f23a2d | 2020-02-20 10:44:09 -0800 | [diff] [blame] | 25 | QuicCryptoServerStreamBase::Helper* helper, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 26 | const QuicCryptoServerConfig* crypto_config, |
| 27 | QuicCompressedCertsCache* compressed_certs_cache, |
| 28 | QuicSimpleServerBackend* quic_simple_server_backend) |
| 29 | : QuicServerSessionBase(config, |
| 30 | supported_versions, |
| 31 | connection, |
| 32 | visitor, |
| 33 | helper, |
| 34 | crypto_config, |
| 35 | compressed_certs_cache), |
| 36 | highest_promised_stream_id_( |
renjietang | 1a33a0c | 2019-08-12 12:05:31 -0700 | [diff] [blame] | 37 | QuicUtils::GetInvalidStreamId(connection->transport_version())), |
rch | d7d9d78 | 2019-05-02 18:18:03 -0700 | [diff] [blame] | 38 | quic_simple_server_backend_(quic_simple_server_backend) { |
vasilvv | f803516 | 2021-02-01 14:49:14 -0800 | [diff] [blame] | 39 | QUICHE_DCHECK(quic_simple_server_backend_); |
rch | d7d9d78 | 2019-05-02 18:18:03 -0700 | [diff] [blame] | 40 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 41 | |
| 42 | QuicSimpleServerSession::~QuicSimpleServerSession() { |
ianswett | 6aefa0b | 2019-12-10 07:26:15 -0800 | [diff] [blame] | 43 | DeleteConnection(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 44 | } |
| 45 | |
nharper | e5e28f9 | 2020-01-03 14:10:07 -0800 | [diff] [blame] | 46 | std::unique_ptr<QuicCryptoServerStreamBase> |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 47 | QuicSimpleServerSession::CreateQuicCryptoServerStream( |
| 48 | const QuicCryptoServerConfig* crypto_config, |
| 49 | QuicCompressedCertsCache* compressed_certs_cache) { |
nharper | e5e28f9 | 2020-01-03 14:10:07 -0800 | [diff] [blame] | 50 | return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this, |
| 51 | stream_helper()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) { |
vasilvv | 20d73c4 | 2021-04-06 20:14:52 -0700 | [diff] [blame] | 55 | if (!IsIncomingStream(frame.stream_id) && !WillNegotiateWebTransport()) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 56 | QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream"; |
| 57 | connection()->CloseConnection( |
| 58 | QUIC_INVALID_STREAM_ID, "Client sent data on server push stream", |
| 59 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 60 | return; |
| 61 | } |
| 62 | QuicSpdySession::OnStreamFrame(frame); |
| 63 | } |
| 64 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 65 | QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) { |
| 66 | if (!ShouldCreateIncomingStream(id)) { |
| 67 | return nullptr; |
| 68 | } |
| 69 | |
| 70 | QuicSpdyStream* stream = new QuicSimpleServerStream( |
| 71 | id, this, BIDIRECTIONAL, quic_simple_server_backend_); |
bnc | 38b5aed | 2021-04-02 10:57:41 -0700 | [diff] [blame] | 72 | ActivateStream(absl::WrapUnique(stream)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 73 | return stream; |
| 74 | } |
| 75 | |
| 76 | QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream( |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 77 | PendingStream* pending) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 78 | QuicSpdyStream* stream = new QuicSimpleServerStream( |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 79 | pending, this, BIDIRECTIONAL, quic_simple_server_backend_); |
bnc | 38b5aed | 2021-04-02 10:57:41 -0700 | [diff] [blame] | 80 | ActivateStream(absl::WrapUnique(stream)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 81 | return stream; |
| 82 | } |
| 83 | |
vasilvv | 20d73c4 | 2021-04-06 20:14:52 -0700 | [diff] [blame] | 84 | QuicSpdyStream* QuicSimpleServerSession::CreateOutgoingBidirectionalStream() { |
| 85 | if (!WillNegotiateWebTransport()) { |
| 86 | QUIC_BUG(QuicSimpleServerSession CreateOutgoingBidirectionalStream without |
| 87 | WebTransport support) |
| 88 | << "QuicSimpleServerSession::CreateOutgoingBidirectionalStream called " |
| 89 | "in a session without WebTransport support."; |
| 90 | return nullptr; |
| 91 | } |
| 92 | if (!ShouldCreateOutgoingBidirectionalStream()) { |
| 93 | return nullptr; |
| 94 | } |
| 95 | |
| 96 | QuicServerInitiatedSpdyStream* stream = new QuicServerInitiatedSpdyStream( |
| 97 | GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); |
| 98 | ActivateStream(absl::WrapUnique(stream)); |
| 99 | return stream; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | QuicSimpleServerStream* |
| 103 | QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() { |
| 104 | if (!ShouldCreateOutgoingUnidirectionalStream()) { |
| 105 | return nullptr; |
| 106 | } |
| 107 | |
| 108 | QuicSimpleServerStream* stream = new QuicSimpleServerStream( |
| 109 | GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, |
| 110 | quic_simple_server_backend_); |
bnc | 38b5aed | 2021-04-02 10:57:41 -0700 | [diff] [blame] | 111 | ActivateStream(absl::WrapUnique(stream)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 112 | return stream; |
| 113 | } |
| 114 | |
| 115 | void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream( |
| 116 | QuicStreamId stream_id) { |
| 117 | // If this stream is a promised but not created stream (stream_id within the |
| 118 | // range of next_outgoing_stream_id_ and highes_promised_stream_id_), |
| 119 | // connection shouldn't be closed. |
| 120 | // Otherwise behave in the same way as base class. |
| 121 | if (highest_promised_stream_id_ == |
renjietang | d1d0085 | 2019-09-06 10:43:12 -0700 | [diff] [blame] | 122 | QuicUtils::GetInvalidStreamId(transport_version()) || |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 123 | stream_id > highest_promised_stream_id_) { |
| 124 | QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | void QuicSimpleServerSession::HandleRstOnValidNonexistentStream( |
| 129 | const QuicRstStreamFrame& frame) { |
| 130 | QuicSession::HandleRstOnValidNonexistentStream(frame); |
| 131 | if (!IsClosedStream(frame.stream_id)) { |
| 132 | // If a nonexistent stream is not a closed stream and still valid, it must |
| 133 | // be a locally preserved stream. Resetting this kind of stream means |
| 134 | // cancelling the promised server push. |
| 135 | // Since PromisedStreamInfo are queued in sequence, the corresponding |
| 136 | // index for it in promised_streams_ can be calculated. |
| 137 | QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id(); |
dschinazi | 1de5819 | 2020-11-25 14:46:08 -0800 | [diff] [blame] | 138 | if ((!version().HasIetfQuicFrames() || |
| 139 | !QuicUtils::IsBidirectionalStreamId(frame.stream_id, version())) && |
| 140 | frame.stream_id >= next_stream_id) { |
| 141 | size_t index = (frame.stream_id - next_stream_id) / |
| 142 | QuicUtils::StreamIdDelta(transport_version()); |
| 143 | if (index <= promised_streams_.size()) { |
| 144 | promised_streams_[index].is_cancelled = true; |
| 145 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 146 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 147 | control_frame_manager().WriteOrBufferRstStream(frame.stream_id, |
| 148 | QUIC_RST_ACKNOWLEDGEMENT, 0); |
| 149 | connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT); |
| 150 | } |
| 151 | } |
| 152 | |
QUICHE team | 8210352 | 2020-10-22 08:15:09 -0700 | [diff] [blame] | 153 | spdy::Http2HeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders( |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 154 | std::string request_url, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 155 | QuicBackendResponse::ServerPushInfo resource, |
QUICHE team | 8210352 | 2020-10-22 08:15:09 -0700 | [diff] [blame] | 156 | const spdy::Http2HeaderBlock& original_request_headers) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 157 | QuicUrl push_request_url = resource.request_url; |
| 158 | |
QUICHE team | 8210352 | 2020-10-22 08:15:09 -0700 | [diff] [blame] | 159 | spdy::Http2HeaderBlock spdy_headers = original_request_headers.Clone(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 160 | // :authority could be different from original request. |
| 161 | spdy_headers[":authority"] = push_request_url.host(); |
| 162 | spdy_headers[":path"] = push_request_url.path(); |
| 163 | // Push request always use GET. |
| 164 | spdy_headers[":method"] = "GET"; |
| 165 | spdy_headers["referer"] = request_url; |
| 166 | spdy_headers[":scheme"] = push_request_url.scheme(); |
| 167 | // It is not possible to push a response to a request that includes a request |
| 168 | // body. |
| 169 | spdy_headers["content-length"] = "0"; |
| 170 | // Remove "host" field as push request is a directly generated HTTP2 request |
| 171 | // which should use ":authority" instead of "host". |
| 172 | spdy_headers.erase("host"); |
| 173 | return spdy_headers; |
| 174 | } |
| 175 | |
| 176 | void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id, |
| 177 | QuicStreamId promised_stream_id, |
QUICHE team | 8210352 | 2020-10-22 08:15:09 -0700 | [diff] [blame] | 178 | spdy::Http2HeaderBlock headers) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 179 | QUIC_DLOG(INFO) << "stream " << original_stream_id |
| 180 | << " send PUSH_PROMISE for promised stream " |
| 181 | << promised_stream_id; |
| 182 | WritePushPromise(original_stream_id, promised_stream_id, std::move(headers)); |
| 183 | } |
| 184 | |
| 185 | void QuicSimpleServerSession::HandlePromisedPushRequests() { |
| 186 | while (!promised_streams_.empty() && |
| 187 | ShouldCreateOutgoingUnidirectionalStream()) { |
| 188 | PromisedStreamInfo& promised_info = promised_streams_.front(); |
vasilvv | f803516 | 2021-02-01 14:49:14 -0800 | [diff] [blame] | 189 | QUICHE_DCHECK_EQ(next_outgoing_unidirectional_stream_id(), |
| 190 | promised_info.stream_id); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 191 | |
| 192 | if (promised_info.is_cancelled) { |
| 193 | // This stream has been reset by client. Skip this stream id. |
| 194 | promised_streams_.pop_front(); |
| 195 | GetNextOutgoingUnidirectionalStreamId(); |
| 196 | return; |
| 197 | } |
| 198 | |
| 199 | QuicSimpleServerStream* promised_stream = |
| 200 | static_cast<QuicSimpleServerStream*>( |
| 201 | CreateOutgoingUnidirectionalStream()); |
vasilvv | f803516 | 2021-02-01 14:49:14 -0800 | [diff] [blame] | 202 | QUICHE_DCHECK_NE(promised_stream, nullptr); |
| 203 | QUICHE_DCHECK_EQ(promised_info.stream_id, promised_stream->id()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 204 | QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id(); |
| 205 | |
fayang | 9a42376 | 2019-07-31 08:12:58 -0700 | [diff] [blame] | 206 | promised_stream->SetPriority(promised_info.precedence); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 207 | |
QUICHE team | 8210352 | 2020-10-22 08:15:09 -0700 | [diff] [blame] | 208 | spdy::Http2HeaderBlock request_headers( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 209 | std::move(promised_info.request_headers)); |
| 210 | |
| 211 | promised_streams_.pop_front(); |
| 212 | promised_stream->PushResponse(std::move(request_headers)); |
| 213 | } |
| 214 | } |
| 215 | |
fkastenholz | 8556dc2 | 2019-07-18 12:42:38 -0700 | [diff] [blame] | 216 | void QuicSimpleServerSession::OnCanCreateNewOutgoingStream( |
renjietang | de12d3d | 2019-07-19 10:57:42 -0700 | [diff] [blame] | 217 | bool unidirectional) { |
renjietang | c04c85f | 2019-07-25 14:07:27 -0700 | [diff] [blame] | 218 | QuicSpdySession::OnCanCreateNewOutgoingStream(unidirectional); |
renjietang | de12d3d | 2019-07-19 10:57:42 -0700 | [diff] [blame] | 219 | if (unidirectional) { |
| 220 | HandlePromisedPushRequests(); |
| 221 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 222 | } |
renjietang | 1a33a0c | 2019-08-12 12:05:31 -0700 | [diff] [blame] | 223 | |
| 224 | void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() { |
fayang | 116fa4c | 2020-06-02 08:10:49 -0700 | [diff] [blame] | 225 | size_t previous_static_stream_count = num_static_streams(); |
renjietang | 1a33a0c | 2019-08-12 12:05:31 -0700 | [diff] [blame] | 226 | QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams(); |
fayang | 116fa4c | 2020-06-02 08:10:49 -0700 | [diff] [blame] | 227 | size_t current_static_stream_count = num_static_streams(); |
vasilvv | f803516 | 2021-02-01 14:49:14 -0800 | [diff] [blame] | 228 | QUICHE_DCHECK_GE(current_static_stream_count, previous_static_stream_count); |
renjietang | 1a33a0c | 2019-08-12 12:05:31 -0700 | [diff] [blame] | 229 | highest_promised_stream_id_ += |
| 230 | QuicUtils::StreamIdDelta(transport_version()) * |
| 231 | (current_static_stream_count - previous_static_stream_count); |
| 232 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 233 | } // namespace quic |