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