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