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::SpdyHeaderBlock & 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::SpdyHeaderBlock& original_request_headers) {
70 if (!server_push_enabled()) {
71 return;
72 }
73
74 for (QuicBackendResponse::ServerPushInfo resource : resources) {
75 spdy::SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
76 request_url, resource, original_request_headers);
77 // TODO(b/136295430): Use sequential push IDs for IETF QUIC.
78 // TODO(bnc): If |highest_promised_stream_id_| is too large, it will always
79 // be skipped. Fix it by not incrementing if CanCreatePushStreamWithId()
80 // returns false.
81 highest_promised_stream_id_ +=
82 QuicUtils::StreamIdDelta(transport_version());
83 if (VersionUsesHttp3(transport_version()) &&
84 !CanCreatePushStreamWithId(highest_promised_stream_id_)) {
85 return;
86 }
87 SendPushPromise(original_stream_id, highest_promised_stream_id_,
88 headers.Clone());
89 promised_streams_.push_back(PromisedStreamInfo(
90 std::move(headers), highest_promised_stream_id_,
91 use_http2_priority_write_scheduler()
92 ? original_precedence
93 : spdy::SpdyStreamPrecedence(resource.priority)));
94 }
95
96 // Procese promised push request as many as possible.
97 HandlePromisedPushRequests();
98 }
99
CreateIncomingStream(QuicStreamId id)100 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
101 if (!ShouldCreateIncomingStream(id)) {
102 return nullptr;
103 }
104
105 QuicSpdyStream* stream = new QuicSimpleServerStream(
106 id, this, BIDIRECTIONAL, quic_simple_server_backend_);
107 ActivateStream(QuicWrapUnique(stream));
108 return stream;
109 }
110
CreateIncomingStream(PendingStream * pending)111 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(
112 PendingStream* pending) {
113 QuicSpdyStream* stream = new QuicSimpleServerStream(
114 pending, this, BIDIRECTIONAL, quic_simple_server_backend_);
115 ActivateStream(QuicWrapUnique(stream));
116 return stream;
117 }
118
119 QuicSimpleServerStream*
CreateOutgoingBidirectionalStream()120 QuicSimpleServerSession::CreateOutgoingBidirectionalStream() {
121 DCHECK(false);
122 return nullptr;
123 }
124
125 QuicSimpleServerStream*
CreateOutgoingUnidirectionalStream()126 QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
127 if (!ShouldCreateOutgoingUnidirectionalStream()) {
128 return nullptr;
129 }
130
131 QuicSimpleServerStream* stream = new QuicSimpleServerStream(
132 GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
133 quic_simple_server_backend_);
134 ActivateStream(QuicWrapUnique(stream));
135 return stream;
136 }
137
HandleFrameOnNonexistentOutgoingStream(QuicStreamId stream_id)138 void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream(
139 QuicStreamId stream_id) {
140 // If this stream is a promised but not created stream (stream_id within the
141 // range of next_outgoing_stream_id_ and highes_promised_stream_id_),
142 // connection shouldn't be closed.
143 // Otherwise behave in the same way as base class.
144 if (highest_promised_stream_id_ ==
145 QuicUtils::GetInvalidStreamId(transport_version()) ||
146 stream_id > highest_promised_stream_id_) {
147 QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id);
148 }
149 }
150
HandleRstOnValidNonexistentStream(const QuicRstStreamFrame & frame)151 void QuicSimpleServerSession::HandleRstOnValidNonexistentStream(
152 const QuicRstStreamFrame& frame) {
153 QuicSession::HandleRstOnValidNonexistentStream(frame);
154 if (!IsClosedStream(frame.stream_id)) {
155 // If a nonexistent stream is not a closed stream and still valid, it must
156 // be a locally preserved stream. Resetting this kind of stream means
157 // cancelling the promised server push.
158 // Since PromisedStreamInfo are queued in sequence, the corresponding
159 // index for it in promised_streams_ can be calculated.
160 QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id();
161 if (VersionHasIetfQuicFrames(transport_version())) {
162 DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id));
163 }
164 DCHECK_GE(frame.stream_id, next_stream_id);
165 size_t index = (frame.stream_id - next_stream_id) /
166 QuicUtils::StreamIdDelta(transport_version());
167 DCHECK_LE(index, promised_streams_.size());
168 promised_streams_[index].is_cancelled = true;
169 control_frame_manager().WriteOrBufferRstStream(frame.stream_id,
170 QUIC_RST_ACKNOWLEDGEMENT, 0);
171 connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT);
172 }
173 }
174
SynthesizePushRequestHeaders(std::string request_url,QuicBackendResponse::ServerPushInfo resource,const spdy::SpdyHeaderBlock & original_request_headers)175 spdy::SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
176 std::string request_url,
177 QuicBackendResponse::ServerPushInfo resource,
178 const spdy::SpdyHeaderBlock& original_request_headers) {
179 QuicUrl push_request_url = resource.request_url;
180
181 spdy::SpdyHeaderBlock spdy_headers = original_request_headers.Clone();
182 // :authority could be different from original request.
183 spdy_headers[":authority"] = push_request_url.host();
184 spdy_headers[":path"] = push_request_url.path();
185 // Push request always use GET.
186 spdy_headers[":method"] = "GET";
187 spdy_headers["referer"] = request_url;
188 spdy_headers[":scheme"] = push_request_url.scheme();
189 // It is not possible to push a response to a request that includes a request
190 // body.
191 spdy_headers["content-length"] = "0";
192 // Remove "host" field as push request is a directly generated HTTP2 request
193 // which should use ":authority" instead of "host".
194 spdy_headers.erase("host");
195 return spdy_headers;
196 }
197
SendPushPromise(QuicStreamId original_stream_id,QuicStreamId promised_stream_id,spdy::SpdyHeaderBlock headers)198 void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id,
199 QuicStreamId promised_stream_id,
200 spdy::SpdyHeaderBlock headers) {
201 QUIC_DLOG(INFO) << "stream " << original_stream_id
202 << " send PUSH_PROMISE for promised stream "
203 << promised_stream_id;
204 WritePushPromise(original_stream_id, promised_stream_id, std::move(headers));
205 }
206
HandlePromisedPushRequests()207 void QuicSimpleServerSession::HandlePromisedPushRequests() {
208 while (!promised_streams_.empty() &&
209 ShouldCreateOutgoingUnidirectionalStream()) {
210 PromisedStreamInfo& promised_info = promised_streams_.front();
211 DCHECK_EQ(next_outgoing_unidirectional_stream_id(),
212 promised_info.stream_id);
213
214 if (promised_info.is_cancelled) {
215 // This stream has been reset by client. Skip this stream id.
216 promised_streams_.pop_front();
217 GetNextOutgoingUnidirectionalStreamId();
218 return;
219 }
220
221 QuicSimpleServerStream* promised_stream =
222 static_cast<QuicSimpleServerStream*>(
223 CreateOutgoingUnidirectionalStream());
224 DCHECK_NE(promised_stream, nullptr);
225 DCHECK_EQ(promised_info.stream_id, promised_stream->id());
226 QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
227
228 promised_stream->SetPriority(promised_info.precedence);
229
230 spdy::SpdyHeaderBlock request_headers(
231 std::move(promised_info.request_headers));
232
233 promised_streams_.pop_front();
234 promised_stream->PushResponse(std::move(request_headers));
235 }
236 }
237
OnCanCreateNewOutgoingStream(bool unidirectional)238 void QuicSimpleServerSession::OnCanCreateNewOutgoingStream(
239 bool unidirectional) {
240 QuicSpdySession::OnCanCreateNewOutgoingStream(unidirectional);
241 if (unidirectional) {
242 HandlePromisedPushRequests();
243 }
244 }
245
MaybeInitializeHttp3UnidirectionalStreams()246 void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() {
247 size_t previous_static_stream_count = num_outgoing_static_streams();
248 QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams();
249 size_t current_static_stream_count = num_outgoing_static_streams();
250 DCHECK_GE(current_static_stream_count, previous_static_stream_count);
251 highest_promised_stream_id_ +=
252 QuicUtils::StreamIdDelta(transport_version()) *
253 (current_static_stream_count - previous_static_stream_count);
254 }
255 } // namespace quic
256