Project import generated by Copybara.

PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/tools/quic_simple_server_session.cc b/quic/tools/quic_simple_server_session.cc
new file mode 100644
index 0000000..7e2e3bf
--- /dev/null
+++ b/quic/tools/quic_simple_server_session.cc
@@ -0,0 +1,228 @@
+// 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 "net/third_party/quiche/src/quic/tools/quic_simple_server_session.h"
+
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/tools/quic_simple_server_stream.h"
+
+namespace quic {
+
+QuicSimpleServerSession::QuicSimpleServerSession(
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection,
+    QuicSession::Visitor* visitor,
+    QuicCryptoServerStream::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) {}
+
+QuicSimpleServerSession::~QuicSimpleServerSession() {
+  delete connection();
+}
+
+QuicCryptoServerStreamBase*
+QuicSimpleServerSession::CreateQuicCryptoServerStream(
+    const QuicCryptoServerConfig* crypto_config,
+    QuicCompressedCertsCache* compressed_certs_cache) {
+  return new QuicCryptoServerStream(
+      crypto_config, compressed_certs_cache,
+      GetQuicReloadableFlag(enable_quic_stateless_reject_support), this,
+      stream_helper());
+}
+
+void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
+  if (!IsIncomingStream(frame.stream_id)) {
+    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);
+}
+
+void QuicSimpleServerSession::PromisePushResources(
+    const QuicString& request_url,
+    const std::list<QuicBackendResponse::ServerPushInfo>& resources,
+    QuicStreamId original_stream_id,
+    const spdy::SpdyHeaderBlock& original_request_headers) {
+  if (!server_push_enabled()) {
+    return;
+  }
+
+  for (QuicBackendResponse::ServerPushInfo resource : resources) {
+    spdy::SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
+        request_url, resource, original_request_headers);
+    highest_promised_stream_id_ +=
+        QuicUtils::StreamIdDelta(connection()->transport_version());
+    SendPushPromise(original_stream_id, highest_promised_stream_id_,
+                    headers.Clone());
+    promised_streams_.push_back(PromisedStreamInfo(
+        std::move(headers), highest_promised_stream_id_, resource.priority));
+  }
+
+  // Procese promised push request as many as possible.
+  HandlePromisedPushRequests();
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(QuicStreamId id) {
+  if (!ShouldCreateIncomingStream(id)) {
+    return nullptr;
+  }
+
+  QuicSpdyStream* stream = new QuicSimpleServerStream(
+      id, this, BIDIRECTIONAL, quic_simple_server_backend_);
+  ActivateStream(QuicWrapUnique(stream));
+  return stream;
+}
+
+QuicSpdyStream* QuicSimpleServerSession::CreateIncomingStream(
+    PendingStream pending) {
+  QuicSpdyStream* stream = new QuicSimpleServerStream(
+      std::move(pending), this, BIDIRECTIONAL, quic_simple_server_backend_);
+  ActivateStream(QuicWrapUnique(stream));
+  return stream;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingBidirectionalStream() {
+  DCHECK(false);
+  return nullptr;
+}
+
+QuicSimpleServerStream*
+QuicSimpleServerSession::CreateOutgoingUnidirectionalStream() {
+  if (!ShouldCreateOutgoingUnidirectionalStream()) {
+    return nullptr;
+  }
+
+  QuicSimpleServerStream* stream = new QuicSimpleServerStream(
+      GetNextOutgoingUnidirectionalStreamId(), this, WRITE_UNIDIRECTIONAL,
+      quic_simple_server_backend_);
+  ActivateStream(QuicWrapUnique(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(connection()->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 (connection()->transport_version() == QUIC_VERSION_99) {
+      DCHECK(!QuicUtils::IsBidirectionalStreamId(frame.stream_id));
+    }
+    DCHECK_GE(frame.stream_id, next_stream_id);
+    size_t index = (frame.stream_id - next_stream_id) /
+                   QuicUtils::StreamIdDelta(connection()->transport_version());
+    DCHECK_LE(index, promised_streams_.size());
+    promised_streams_[index].is_cancelled = true;
+    control_frame_manager().WriteOrBufferRstStream(frame.stream_id,
+                                                   QUIC_RST_ACKNOWLEDGEMENT, 0);
+    connection()->OnStreamReset(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT);
+  }
+}
+
+spdy::SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
+    QuicString request_url,
+    QuicBackendResponse::ServerPushInfo resource,
+    const spdy::SpdyHeaderBlock& original_request_headers) {
+  QuicUrl push_request_url = resource.request_url;
+
+  spdy::SpdyHeaderBlock 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::SpdyHeaderBlock 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();
+    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());
+    DCHECK_NE(promised_stream, nullptr);
+    DCHECK_EQ(promised_info.stream_id, promised_stream->id());
+    QUIC_DLOG(INFO) << "created server push stream " << promised_stream->id();
+
+    promised_stream->SetPriority(promised_info.priority);
+
+    spdy::SpdyHeaderBlock request_headers(
+        std::move(promised_info.request_headers));
+
+    promised_streams_.pop_front();
+    promised_stream->PushResponse(std::move(request_headers));
+  }
+}
+
+void QuicSimpleServerSession::OnCanCreateNewOutgoingStream() {
+  HandlePromisedPushRequests();
+}
+}  // namespace quic