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