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
5 #include "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
6
7 #include <utility>
8
9 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h"
10 #include "net/third_party/quiche/src/quic/core/quic_connection.h"
11 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
12 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
13 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
14 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
15 #include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
16
17 namespace quic {
18
QuicSimpleServerSession(const QuicConfig & config,const ParsedQuicVersionVector & supported_versions,QuicConnection * connection,QuicSession::Visitor * visitor,QuicCryptoServerStreamBase::Helper * helper,const QuicCryptoServerConfig * crypto_config,QuicCompressedCertsCache * compressed_certs_cache,QuicSimpleServerBackend * quic_simple_server_backend)19 QuicSimpleServerSession::QuicSimpleServerSession(
20 const QuicConfig& config,
21 const ParsedQuicVersionVector& supported_versions,
22 QuicConnection* connection,
23 QuicSession::Visitor* visitor,
24 QuicCryptoServerStreamBase::Helper* helper,
25 const QuicCryptoServerConfig* crypto_config,
26 QuicCompressedCertsCache* compressed_certs_cache,
27 QuicSimpleServerBackend* quic_simple_server_backend)
28 : QuicServerSessionBase(config,
29 supported_versions,
30 connection,
31 visitor,
32 helper,
33 crypto_config,
34 compressed_certs_cache),
35 highest_promised_stream_id_(
36 QuicUtils::GetInvalidStreamId(connection->transport_version())),
37 quic_simple_server_backend_(quic_simple_server_backend) {
38 DCHECK(quic_simple_server_backend_);
39 }
40
~QuicSimpleServerSession()41 QuicSimpleServerSession::~QuicSimpleServerSession() {
42 DeleteConnection();
43 }
44
45 std::unique_ptr<QuicCryptoServerStreamBase>
CreateQuicCryptoServerStream(const QuicCryptoServerConfig * crypto_config,QuicCompressedCertsCache * compressed_certs_cache)46 QuicSimpleServerSession::CreateQuicCryptoServerStream(
47 const QuicCryptoServerConfig* crypto_config,
48 QuicCompressedCertsCache* compressed_certs_cache) {
49 return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this,
50 stream_helper());
51 }
52
OnStreamFrame(const QuicStreamFrame & frame)53 void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
54 if (!IsIncomingStream(frame.stream_id)) {
55 QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream";
56 connection()->CloseConnection(
57 QUIC_INVALID_STREAM_ID, "Client sent data on server push stream",
58 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
59 return;
60 }
61 QuicSpdySession::OnStreamFrame(frame);
62 }
63
PromisePushResources(const std::string & request_url,const std::list<QuicBackendResponse::ServerPushInfo> & resources,QuicStreamId original_stream_id,const spdy::SpdyStreamPrecedence & original_precedence,const spdy::Http2HeaderBlock & original_request_headers)64 void QuicSimpleServerSession::PromisePushResources(
65 const std::string& request_url,
66 const std::list<QuicBackendResponse::ServerPushInfo>& resources,
67 QuicStreamId original_stream_id,
68 const spdy::SpdyStreamPrecedence& original_precedence,
69 const spdy::Http2HeaderBlock& original_request_headers) {
70 if (!server_push_enabled()) {
71 return;
72 }
73
74 for (const QuicBackendResponse::ServerPushInfo& resource : resources) {
75 spdy::Http2HeaderBlock headers = SynthesizePushRequestHeaders(
76 request_url, resource, original_request_headers);
77 // TODO(b/136295430): Use sequential push IDs for IETF QUIC.
78 auto new_highest_promised_stream_id =
79 highest_promised_stream_id_ +
80 QuicUtils::StreamIdDelta(transport_version());
81 if (VersionUsesHttp3(transport_version()) &&
82 !CanCreatePushStreamWithId(new_highest_promised_stream_id)) {
83 return;
84 }
85 highest_promised_stream_id_ = new_highest_promised_stream_id;
86 SendPushPromise(original_stream_id, highest_promised_stream_id_,
87 headers.Clone());
88 promised_streams_.push_back(PromisedStreamInfo(
89 std::move(headers), highest_promised_stream_id_,
90 use_http2_priority_write_scheduler()
91 ? original_precedence
92 : spdy::SpdyStreamPrecedence(resource.priority)));
93 }
94
95 // Procese promised push request as many as possible.
96 HandlePromisedPushRequests();
97 }
98
CreateIncomingStream(QuicStreamId id)99 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
100 if (!ShouldCreateIncomingStream(id)) {
101 return nullptr;
102 }
103
104 QuicSpdyStream* stream = new QuicSimpleServerStream(
105 id, this, BIDIRECTIONAL, quic_simple_server_backend_);
106 ActivateStream(QuicWrapUnique(stream));
107 return stream;
108 }
109
CreateIncomingStream(PendingStream * pending)110 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(
111 PendingStream* pending) {
112 QuicSpdyStream* stream = new QuicSimpleServerStream(
113 pending, this, BIDIRECTIONAL, quic_simple_server_backend_);
114 ActivateStream(QuicWrapUnique(stream));
115 return stream;
116 }
117
118 QuicSimpleServerStream*
CreateOutgoingBidirectionalStream()119 QuicSimpleServerSession::CreateOutgoingBidirectionalStream() {
120 DCHECK(false);
121 return nullptr;
122 }
123
124 QuicSimpleServerStream*
CreateOutgoingUnidirectionalStream()125 QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
126 if (!ShouldCreateOutgoingUnidirectionalStream()) {
127 return nullptr;
128 }
129
130 QuicSimpleServerStream* stream = new QuicSimpleServerStream(
131 GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
132 quic_simple_server_backend_);
133 ActivateStream(QuicWrapUnique(stream));
134 return stream;
135 }
136
HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id)137 void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream(
138 QuicStreamId stream_id) {
139 // If this stream is a promised but not created stream (stream_id within the
140 // range of next_outgoing_stream_id_ and highes_promised_stream_id_),
141 // connection shouldn't be closed.
142 // Otherwise behave in the same way as base class.
143 if (highest_promised_stream_id_ ==
144 QuicUtils::GetInvalidStreamId(transport_version()) ||
145 stream_id > highest_promised_stream_id_) {
146 QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id);
147 }
148 }
149
HandleRstOnValidNonexistentStream(const QuicRstStreamFrame & frame)150 void QuicSimpleServerSession::HandleRstOnValidNonexistentStream(
151 const QuicRstStreamFrame& frame) {
152 QuicSession::HandleRstOnValidNonexistentStream(frame);
153 if (!IsClosedStream(frame.stream_id)) {
154 // If a nonexistent stream is not a closed stream and still valid, it must
155 // be a locally preserved stream. Resetting this kind of stream means
156 // cancelling the promised server push.
157 // Since PromisedStreamInfo are queued in sequence, the corresponding
158 // index for it in promised_streams_ can be calculated.
159 QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id();
160 if (VersionHasIetfQuicFrames(transport_version())) {
161 DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id, version()));
162 }
163 DCHECK_GE(frame.stream_id, next_stream_id);
164 size_t index = (frame.stream_id - next_stream_id) /
165 QuicUtils::StreamIdDelta(transport_version());
166 DCHECK_LE(index, promised_streams_.size());
167 promised_streams_[index].is_cancelled = true;
168 control_frame_manager().WriteOrBufferRstStream(frame.stream_id,
169 QUIC_RST_ACKNOWLEDGEMENT, 0);
170 connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT);
171 }
172 }
173
SynthesizePushRequestHeaders(std::string request_url,QuicBackendResponse::ServerPushInfo resource,const spdy::Http2HeaderBlock & original_request_headers)174 spdy::Http2HeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
175 std::string request_url,
176 QuicBackendResponse::ServerPushInfo resource,
177 const spdy::Http2HeaderBlock& original_request_headers) {
178 QuicUrl push_request_url = resource.request_url;
179
180 spdy::Http2HeaderBlock spdy_headers = original_request_headers.Clone();
181 // :authority could be different from original request.
182 spdy_headers[":authority"] = push_request_url.host();
183 spdy_headers[":path"] = push_request_url.path();
184 // Push request always use GET.
185 spdy_headers[":method"] = "GET";
186 spdy_headers["referer"] = request_url;
187 spdy_headers[":scheme"] = push_request_url.scheme();
188 // It is not possible to push a response to a request that includes a request
189 // body.
190 spdy_headers["content-length"] = "0";
191 // Remove "host" field as push request is a directly generated HTTP2 request
192 // which should use ":authority" instead of "host".
193 spdy_headers.erase("host");
194 return spdy_headers;
195 }
196
SendPushPromise(QuicStreamId original_stream_id,QuicStreamId promised_stream_id,spdy::Http2HeaderBlock headers)197 void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id,
198 QuicStreamId promised_stream_id,
199 spdy::Http2HeaderBlock headers) {
200 QUIC_DLOG(INFO) << "stream " << original_stream_id
201 << " send PUSH_PROMISE for promised stream "
202 << promised_stream_id;
203 WritePushPromise(original_stream_id, promised_stream_id, std::move(headers));
204 }
205
HandlePromisedPushRequests()206 void QuicSimpleServerSession::HandlePromisedPushRequests() {
207 while (!promised_streams_.empty() &&
208 ShouldCreateOutgoingUnidirectionalStream()) {
209 PromisedStreamInfo& promised_info = promised_streams_.front();
210 DCHECK_EQ(next_outgoing_unidirectional_stream_id(),
211 promised_info.stream_id);
212
213 if (promised_info.is_cancelled) {
214 // This stream has been reset by client. Skip this stream id.
215 promised_streams_.pop_front();
216 GetNextOutgoingUnidirectionalStreamId();
217 return;
218 }
219
220 QuicSimpleServerStream* promised_stream =
221 static_cast<QuicSimpleServerStream*>(
222 CreateOutgoingUnidirectionalStream());
223 DCHECK_NE(promised_stream, nullptr);
224 DCHECK_EQ(promised_info.stream_id, promised_stream->id());
225 QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
226
227 promised_stream->SetPriority(promised_info.precedence);
228
229 spdy::Http2HeaderBlock request_headers(
230 std::move(promised_info.request_headers));
231
232 promised_streams_.pop_front();
233 promised_stream->PushResponse(std::move(request_headers));
234 }
235 }
236
OnCanCreateNewOutgoingStream(bool unidirectional)237 void QuicSimpleServerSession::OnCanCreateNewOutgoingStream(
238 bool unidirectional) {
239 QuicSpdySession::OnCanCreateNewOutgoingStream(unidirectional);
240 if (unidirectional) {
241 HandlePromisedPushRequests();
242 }
243 }
244
MaybeInitializeHttp3UnidirectionalStreams()245 void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() {
246 size_t previous_static_stream_count = num_static_streams();
247 QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams();
248 size_t current_static_stream_count = num_static_streams();
249 DCHECK_GE(current_static_stream_count, previous_static_stream_count);
250 highest_promised_stream_id_ +=
251 QuicUtils::StreamIdDelta(transport_version()) *
252 (current_static_stream_count - previous_static_stream_count);
253 }
254 } // namespace quic
255