| Index: net/tools/quic/quic_simple_server_session.cc
|
| diff --git a/net/tools/quic/quic_simple_server_session.cc b/net/tools/quic/quic_simple_server_session.cc
|
| index 0c6aeb876852064b61e85d2b06469539df5a6017..9b4257615e401f8e7f16a3462baeb798a5c5a64d 100644
|
| --- a/net/tools/quic/quic_simple_server_session.cc
|
| +++ b/net/tools/quic/quic_simple_server_session.cc
|
| @@ -5,12 +5,14 @@
|
| #include "net/tools/quic/quic_simple_server_session.h"
|
|
|
| #include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| #include "net/quic/proto/cached_network_parameters.pb.h"
|
| #include "net/quic/quic_connection.h"
|
| #include "net/quic/quic_flags.h"
|
| #include "net/quic/quic_spdy_session.h"
|
| #include "net/quic/reliable_quic_stream.h"
|
| #include "net/tools/quic/quic_simple_server_stream.h"
|
| +#include "url/gurl.h"
|
|
|
| namespace net {
|
| namespace tools {
|
| @@ -20,7 +22,8 @@ QuicSimpleServerSession::QuicSimpleServerSession(
|
| QuicConnection* connection,
|
| QuicServerSessionVisitor* visitor,
|
| const QuicCryptoServerConfig* crypto_config)
|
| - : QuicServerSessionBase(config, connection, visitor, crypto_config) {}
|
| + : QuicServerSessionBase(config, connection, visitor, crypto_config),
|
| + highest_promised_stream_id_(0) {}
|
|
|
| QuicSimpleServerSession::~QuicSimpleServerSession() {}
|
|
|
| @@ -30,6 +33,41 @@ QuicSimpleServerSession::CreateQuicCryptoServerStream(
|
| return new QuicCryptoServerStream(crypto_config, this);
|
| }
|
|
|
| +void QuicSimpleServerSession::StreamDraining(QuicStreamId id) {
|
| + QuicSpdySession::StreamDraining(id);
|
| + if (!IsIncomingStream(id)) {
|
| + HandlePromisedPushRequests();
|
| + }
|
| +}
|
| +
|
| +void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
|
| + if (!IsIncomingStream(frame.stream_id)) {
|
| + LOG(WARNING) << "Client shouldn't send data on server push stream";
|
| + connection()->SendConnectionCloseWithDetails(
|
| + QUIC_INVALID_STREAM_ID, "Client sent data on server push stream");
|
| + return;
|
| + }
|
| + QuicSpdySession::OnStreamFrame(frame);
|
| +}
|
| +
|
| +void QuicSimpleServerSession::PromisePushResources(
|
| + const string& request_url,
|
| + const list<QuicInMemoryCache::ServerPushInfo>& resources,
|
| + QuicStreamId original_stream_id,
|
| + const SpdyHeaderBlock& original_request_headers) {
|
| + for (QuicInMemoryCache::ServerPushInfo resource : resources) {
|
| + SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
|
| + request_url, resource, original_request_headers);
|
| + highest_promised_stream_id_ += 2;
|
| + SendPushPromise(original_stream_id, highest_promised_stream_id_, headers);
|
| + promised_streams_.push_back(PromisedStreamInfo(
|
| + headers, highest_promised_stream_id_, resource.priority));
|
| + }
|
| +
|
| + // Procese promised push request as many as possible.
|
| + HandlePromisedPushRequests();
|
| +}
|
| +
|
| QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream(
|
| QuicStreamId id) {
|
| if (!ShouldCreateIncomingDynamicStream(id)) {
|
| @@ -52,5 +90,98 @@ QuicSimpleServerStream* QuicSimpleServerSession::CreateOutgoingDynamicStream(
|
| return stream;
|
| }
|
|
|
| +void QuicSimpleServerSession::CloseStreamInner(QuicStreamId stream_id,
|
| + bool locally_reset) {
|
| + QuicSpdySession::CloseStreamInner(stream_id, locally_reset);
|
| + HandlePromisedPushRequests();
|
| +}
|
| +
|
| +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 (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.
|
| + DCHECK(frame.stream_id >= next_outgoing_stream_id());
|
| + size_t index = (frame.stream_id - next_outgoing_stream_id()) / 2;
|
| + DCHECK(index <= promised_streams_.size());
|
| + promised_streams_[index].is_cancelled = true;
|
| + connection()->SendRstStream(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0);
|
| + }
|
| +}
|
| +
|
| +SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
|
| + string request_url,
|
| + QuicInMemoryCache::ServerPushInfo resource,
|
| + const SpdyHeaderBlock& original_request_headers) {
|
| + GURL push_request_url = resource.request_url;
|
| + string path = push_request_url.path();
|
| +
|
| + SpdyHeaderBlock spdy_headers = original_request_headers;
|
| + // :authority could be different from original request.
|
| + spdy_headers.ReplaceOrAppendHeader(":authority", push_request_url.host());
|
| + spdy_headers.ReplaceOrAppendHeader(":path", path);
|
| + // Push request always use GET.
|
| + spdy_headers.ReplaceOrAppendHeader(":method", "GET");
|
| + spdy_headers.ReplaceOrAppendHeader("referer", request_url);
|
| + spdy_headers.ReplaceOrAppendHeader(":scheme", push_request_url.scheme());
|
| + // It is not possible to push a response to a request that includes a request
|
| + // body.
|
| + spdy_headers.ReplaceOrAppendHeader("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,
|
| + const SpdyHeaderBlock& headers) {
|
| + DVLOG(1) << "stream " << original_stream_id
|
| + << " send PUSH_PROMISE for promised stream " << promised_stream_id;
|
| + headers_stream()->WritePushPromise(original_stream_id, promised_stream_id,
|
| + headers, nullptr);
|
| +}
|
| +
|
| +void QuicSimpleServerSession::HandlePromisedPushRequests() {
|
| + while (!promised_streams_.empty() && ShouldCreateOutgoingDynamicStream()) {
|
| + const PromisedStreamInfo& promised_info = promised_streams_.front();
|
| + DCHECK_EQ(next_outgoing_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();
|
| + GetNextOutgoingStreamId();
|
| + return;
|
| + }
|
| +
|
| + QuicSimpleServerStream* promised_stream =
|
| + static_cast<QuicSimpleServerStream*>(
|
| + CreateOutgoingDynamicStream(promised_info.priority));
|
| + DCHECK(promised_stream != nullptr);
|
| + DCHECK_EQ(promised_info.stream_id, promised_stream->id());
|
| + DVLOG(1) << "created server push stream " << promised_stream->id();
|
| +
|
| + promised_stream->PushResponse(promised_info.request_headers);
|
| +
|
| + promised_streams_.pop_front();
|
| + }
|
| +}
|
| +
|
| } // namespace tools
|
| } // namespace net
|
|
|