| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "quic/tools/quic_simple_server_session.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "absl/memory/memory.h" | 
 | #include "quic/core/http/quic_server_initiated_spdy_stream.h" | 
 | #include "quic/core/http/quic_spdy_session.h" | 
 | #include "quic/core/quic_connection.h" | 
 | #include "quic/core/quic_types.h" | 
 | #include "quic/core/quic_utils.h" | 
 | #include "quic/platform/api/quic_flags.h" | 
 | #include "quic/platform/api/quic_logging.h" | 
 | #include "quic/tools/quic_simple_server_stream.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | QuicSimpleServerSession::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) | 
 |     : QuicServerSessionBase(config, supported_versions, connection, visitor, | 
 |                             helper, crypto_config, compressed_certs_cache), | 
 |       highest_promised_stream_id_( | 
 |           QuicUtils::GetInvalidStreamId(connection->transport_version())), | 
 |       quic_simple_server_backend_(quic_simple_server_backend) { | 
 |   QUICHE_DCHECK(quic_simple_server_backend_); | 
 | } | 
 |  | 
 | QuicSimpleServerSession::~QuicSimpleServerSession() { DeleteConnection(); } | 
 |  | 
 | std::unique_ptr<QuicCryptoServerStreamBase> | 
 | QuicSimpleServerSession::CreateQuicCryptoServerStream( | 
 |     const QuicCryptoServerConfig* crypto_config, | 
 |     QuicCompressedCertsCache* compressed_certs_cache) { | 
 |   return CreateCryptoServerStream(crypto_config, compressed_certs_cache, this, | 
 |                                   stream_helper()); | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) { | 
 |   if (!IsIncomingStream(frame.stream_id) && !WillNegotiateWebTransport()) { | 
 |     QUIC_LOG(WARNING) << "Client shouldn't send data on server push stream"; | 
 |     connection()->CloseConnection( | 
 |         QUIC_INVALID_STREAM_ID, "Client sent data on server push stream", | 
 |         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | 
 |     return; | 
 |   } | 
 |   QuicSpdySession::OnStreamFrame(frame); | 
 | } | 
 |  | 
 | QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) { | 
 |   if (!ShouldCreateIncomingStream(id)) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   QuicSpdyStream* stream = new QuicSimpleServerStream( | 
 |       id, this, BIDIRECTIONAL, quic_simple_server_backend_); | 
 |   ActivateStream(absl::WrapUnique(stream)); | 
 |   return stream; | 
 | } | 
 |  | 
 | QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream( | 
 |     PendingStream* pending) { | 
 |   QuicSpdyStream* stream = | 
 |       new QuicSimpleServerStream(pending, this, quic_simple_server_backend_); | 
 |   ActivateStream(absl::WrapUnique(stream)); | 
 |   return stream; | 
 | } | 
 |  | 
 | QuicSpdyStream* QuicSimpleServerSession::CreateOutgoingBidirectionalStream() { | 
 |   if (!WillNegotiateWebTransport()) { | 
 |     QUIC_BUG(QuicSimpleServerSession CreateOutgoingBidirectionalStream without | 
 |                  WebTransport support) | 
 |         << "QuicSimpleServerSession::CreateOutgoingBidirectionalStream called " | 
 |            "in a session without WebTransport support."; | 
 |     return nullptr; | 
 |   } | 
 |   if (!ShouldCreateOutgoingBidirectionalStream()) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   QuicServerInitiatedSpdyStream* stream = new QuicServerInitiatedSpdyStream( | 
 |       GetNextOutgoingBidirectionalStreamId(), this, BIDIRECTIONAL); | 
 |   ActivateStream(absl::WrapUnique(stream)); | 
 |   return stream; | 
 | } | 
 |  | 
 | QuicSimpleServerStream* | 
 | QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() { | 
 |   if (!ShouldCreateOutgoingUnidirectionalStream()) { | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   QuicSimpleServerStream* stream = new QuicSimpleServerStream( | 
 |       GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL, | 
 |       quic_simple_server_backend_); | 
 |   ActivateStream(absl::WrapUnique(stream)); | 
 |   return stream; | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream( | 
 |     QuicStreamId stream_id) { | 
 |   // If this stream is a promised but not created stream (stream_id within the | 
 |   // range of next_outgoing_stream_id_ and highes_promised_stream_id_), | 
 |   // connection shouldn't be closed. | 
 |   // Otherwise behave in the same way as base class. | 
 |   if (highest_promised_stream_id_ == | 
 |           QuicUtils::GetInvalidStreamId(transport_version()) || | 
 |       stream_id > highest_promised_stream_id_) { | 
 |     QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id); | 
 |   } | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::HandleRstOnValidNonexistentStream( | 
 |     const QuicRstStreamFrame& frame) { | 
 |   QuicSession::HandleRstOnValidNonexistentStream(frame); | 
 |   if (!IsClosedStream(frame.stream_id)) { | 
 |     // If a nonexistent stream is not a closed stream and still valid, it must | 
 |     // be a locally preserved stream. Resetting this kind of stream means | 
 |     // cancelling the promised server push. | 
 |     // Since PromisedStreamInfo are queued in sequence, the corresponding | 
 |     // index for it in promised_streams_ can be calculated. | 
 |     QuicStreamId next_stream_id = next_outgoing_unidirectional_stream_id(); | 
 |     if ((!version().HasIetfQuicFrames() || | 
 |          !QuicUtils::IsBidirectionalStreamId(frame.stream_id, version())) && | 
 |         frame.stream_id >= next_stream_id) { | 
 |       size_t index = (frame.stream_id - next_stream_id) / | 
 |                      QuicUtils::StreamIdDelta(transport_version()); | 
 |       if (index <= promised_streams_.size()) { | 
 |         promised_streams_[index].is_cancelled = true; | 
 |       } | 
 |     } | 
 |     control_frame_manager().WriteOrBufferRstStream( | 
 |         frame.stream_id, | 
 |         QuicResetStreamError::FromInternal(QUIC_RST_ACKNOWLEDGEMENT), 0); | 
 |     connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT); | 
 |   } | 
 | } | 
 |  | 
 | spdy::Http2HeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders( | 
 |     std::string request_url, QuicBackendResponse::ServerPushInfo resource, | 
 |     const spdy::Http2HeaderBlock& original_request_headers) { | 
 |   QuicUrl push_request_url = resource.request_url; | 
 |  | 
 |   spdy::Http2HeaderBlock spdy_headers = original_request_headers.Clone(); | 
 |   // :authority could be different from original request. | 
 |   spdy_headers[":authority"] = push_request_url.host(); | 
 |   spdy_headers[":path"] = push_request_url.path(); | 
 |   // Push request always use GET. | 
 |   spdy_headers[":method"] = "GET"; | 
 |   spdy_headers["referer"] = request_url; | 
 |   spdy_headers[":scheme"] = push_request_url.scheme(); | 
 |   // It is not possible to push a response to a request that includes a request | 
 |   // body. | 
 |   spdy_headers["content-length"] = "0"; | 
 |   // Remove "host" field as push request is a directly generated HTTP2 request | 
 |   // which should use ":authority" instead of "host". | 
 |   spdy_headers.erase("host"); | 
 |   return spdy_headers; | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id, | 
 |                                               QuicStreamId promised_stream_id, | 
 |                                               spdy::Http2HeaderBlock headers) { | 
 |   QUIC_DLOG(INFO) << "stream " << original_stream_id | 
 |                   << " send PUSH_PROMISE for promised stream " | 
 |                   << promised_stream_id; | 
 |   WritePushPromise(original_stream_id, promised_stream_id, std::move(headers)); | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::HandlePromisedPushRequests() { | 
 |   while (!promised_streams_.empty() && | 
 |          ShouldCreateOutgoingUnidirectionalStream()) { | 
 |     PromisedStreamInfo& promised_info = promised_streams_.front(); | 
 |     QUICHE_DCHECK_EQ(next_outgoing_unidirectional_stream_id(), | 
 |                      promised_info.stream_id); | 
 |  | 
 |     if (promised_info.is_cancelled) { | 
 |       // This stream has been reset by client. Skip this stream id. | 
 |       promised_streams_.pop_front(); | 
 |       GetNextOutgoingUnidirectionalStreamId(); | 
 |       return; | 
 |     } | 
 |  | 
 |     QuicSimpleServerStream* promised_stream = | 
 |         static_cast<QuicSimpleServerStream*>( | 
 |             CreateOutgoingUnidirectionalStream()); | 
 |     QUICHE_DCHECK_NE(promised_stream, nullptr); | 
 |     QUICHE_DCHECK_EQ(promised_info.stream_id, promised_stream->id()); | 
 |     QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id(); | 
 |  | 
 |     promised_stream->SetPriority(promised_info.precedence); | 
 |  | 
 |     spdy::Http2HeaderBlock request_headers( | 
 |         std::move(promised_info.request_headers)); | 
 |  | 
 |     promised_streams_.pop_front(); | 
 |     promised_stream->PushResponse(std::move(request_headers)); | 
 |   } | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::OnCanCreateNewOutgoingStream( | 
 |     bool unidirectional) { | 
 |   QuicSpdySession::OnCanCreateNewOutgoingStream(unidirectional); | 
 |   if (unidirectional) { | 
 |     HandlePromisedPushRequests(); | 
 |   } | 
 | } | 
 |  | 
 | void QuicSimpleServerSession::MaybeInitializeHttp3UnidirectionalStreams() { | 
 |   size_t previous_static_stream_count = num_static_streams(); | 
 |   QuicSpdySession::MaybeInitializeHttp3UnidirectionalStreams(); | 
 |   size_t current_static_stream_count = num_static_streams(); | 
 |   QUICHE_DCHECK_GE(current_static_stream_count, previous_static_stream_count); | 
 |   highest_promised_stream_id_ += | 
 |       QuicUtils::StreamIdDelta(transport_version()) * | 
 |       (current_static_stream_count - previous_static_stream_count); | 
 | } | 
 | }  // namespace quic |