blob: e72383a29de6551d0564c980da0df91a4b402d89 [file] [log] [blame]
jokulikc971baf92016-01-06 00:36:391// 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/tools/quic/quic_simple_server_session.h"
6
7#include "base/logging.h"
zhongyi4a9d27b2016-01-12 20:08:318#include "base/stl_util.h"
jokulikc971baf92016-01-06 00:36:399#include "net/quic/proto/cached_network_parameters.pb.h"
10#include "net/quic/quic_connection.h"
11#include "net/quic/quic_flags.h"
12#include "net/quic/quic_spdy_session.h"
13#include "net/quic/reliable_quic_stream.h"
14#include "net/tools/quic/quic_simple_server_stream.h"
zhongyi4a9d27b2016-01-12 20:08:3115#include "url/gurl.h"
jokulikc971baf92016-01-06 00:36:3916
17namespace net {
jokulikc971baf92016-01-06 00:36:3918
19QuicSimpleServerSession::QuicSimpleServerSession(
20 const QuicConfig& config,
21 QuicConnection* connection,
22 QuicServerSessionVisitor* visitor,
23 const QuicCryptoServerConfig* crypto_config)
zhongyi4a9d27b2016-01-12 20:08:3124 : QuicServerSessionBase(config, connection, visitor, crypto_config),
25 highest_promised_stream_id_(0) {}
jokulikc971baf92016-01-06 00:36:3926
27QuicSimpleServerSession::~QuicSimpleServerSession() {}
28
29QuicCryptoServerStreamBase*
30QuicSimpleServerSession::CreateQuicCryptoServerStream(
31 const QuicCryptoServerConfig* crypto_config) {
32 return new QuicCryptoServerStream(crypto_config, this);
33}
34
zhongyi4a9d27b2016-01-12 20:08:3135void QuicSimpleServerSession::StreamDraining(QuicStreamId id) {
36 QuicSpdySession::StreamDraining(id);
37 if (!IsIncomingStream(id)) {
38 HandlePromisedPushRequests();
39 }
40}
41
42void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) {
43 if (!IsIncomingStream(frame.stream_id)) {
44 LOG(WARNING) << "Client shouldn't send data on server push stream";
45 connection()->SendConnectionCloseWithDetails(
46 QUIC_INVALID_STREAM_ID, "Client sent data on server push stream");
47 return;
48 }
49 QuicSpdySession::OnStreamFrame(frame);
50}
51
52void QuicSimpleServerSession::PromisePushResources(
53 const string& request_url,
54 const list<QuicInMemoryCache::ServerPushInfo>& resources,
55 QuicStreamId original_stream_id,
56 const SpdyHeaderBlock& original_request_headers) {
57 for (QuicInMemoryCache::ServerPushInfo resource : resources) {
58 SpdyHeaderBlock headers = SynthesizePushRequestHeaders(
59 request_url, resource, original_request_headers);
60 highest_promised_stream_id_ += 2;
61 SendPushPromise(original_stream_id, highest_promised_stream_id_, headers);
62 promised_streams_.push_back(PromisedStreamInfo(
63 headers, highest_promised_stream_id_, resource.priority));
64 }
65
66 // Procese promised push request as many as possible.
67 HandlePromisedPushRequests();
68}
69
jokulikc971baf92016-01-06 00:36:3970QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream(
71 QuicStreamId id) {
72 if (!ShouldCreateIncomingDynamicStream(id)) {
73 return nullptr;
74 }
75
danzh33407f12016-03-04 21:58:1676 QuicSpdyStream* stream = new QuicSimpleServerStream(id, this);
77 ActivateStream(stream);
78 return stream;
jokulikc971baf92016-01-06 00:36:3979}
80
81QuicSimpleServerStream* QuicSimpleServerSession::CreateOutgoingDynamicStream(
82 SpdyPriority priority) {
83 if (!ShouldCreateOutgoingDynamicStream()) {
84 return nullptr;
85 }
86
87 QuicSimpleServerStream* stream =
88 new QuicSimpleServerStream(GetNextOutgoingStreamId(), this);
89 stream->SetPriority(priority);
90 ActivateStream(stream);
91 return stream;
92}
93
zhongyi4a9d27b2016-01-12 20:08:3194void QuicSimpleServerSession::CloseStreamInner(QuicStreamId stream_id,
95 bool locally_reset) {
96 QuicSpdySession::CloseStreamInner(stream_id, locally_reset);
97 HandlePromisedPushRequests();
98}
99
100void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream(
101 QuicStreamId stream_id) {
102 // If this stream is a promised but not created stream (stream_id within the
103 // range of next_outgoing_stream_id_ and highes_promised_stream_id_),
104 // connection shouldn't be closed.
105 // Otherwise behave in the same way as base class.
106 if (stream_id > highest_promised_stream_id_) {
107 QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id);
108 }
109}
110
111void QuicSimpleServerSession::HandleRstOnValidNonexistentStream(
112 const QuicRstStreamFrame& frame) {
113 QuicSession::HandleRstOnValidNonexistentStream(frame);
114 if (!IsClosedStream(frame.stream_id)) {
115 // If a nonexistent stream is not a closed stream and still valid, it must
116 // be a locally preserved stream. Resetting this kind of stream means
117 // cancelling the promised server push.
118 // Since PromisedStreamInfo are queued in sequence, the corresponding
119 // index for it in promised_streams_ can be calculated.
120 DCHECK(frame.stream_id >= next_outgoing_stream_id());
121 size_t index = (frame.stream_id - next_outgoing_stream_id()) / 2;
122 DCHECK(index <= promised_streams_.size());
123 promised_streams_[index].is_cancelled = true;
124 connection()->SendRstStream(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0);
125 }
126}
127
128SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders(
129 string request_url,
130 QuicInMemoryCache::ServerPushInfo resource,
131 const SpdyHeaderBlock& original_request_headers) {
132 GURL push_request_url = resource.request_url;
133 string path = push_request_url.path();
134
135 SpdyHeaderBlock spdy_headers = original_request_headers;
136 // :authority could be different from original request.
137 spdy_headers.ReplaceOrAppendHeader(":authority", push_request_url.host());
138 spdy_headers.ReplaceOrAppendHeader(":path", path);
139 // Push request always use GET.
140 spdy_headers.ReplaceOrAppendHeader(":method", "GET");
141 spdy_headers.ReplaceOrAppendHeader("referer", request_url);
142 spdy_headers.ReplaceOrAppendHeader(":scheme", push_request_url.scheme());
143 // It is not possible to push a response to a request that includes a request
144 // body.
145 spdy_headers.ReplaceOrAppendHeader("content-length", "0");
146 // Remove "host" field as push request is a directly generated HTTP2 request
147 // which should use ":authority" instead of "host".
148 spdy_headers.erase("host");
149 return spdy_headers;
150}
151
152void QuicSimpleServerSession::SendPushPromise(QuicStreamId original_stream_id,
153 QuicStreamId promised_stream_id,
154 const SpdyHeaderBlock& headers) {
155 DVLOG(1) << "stream " << original_stream_id
156 << " send PUSH_PROMISE for promised stream " << promised_stream_id;
157 headers_stream()->WritePushPromise(original_stream_id, promised_stream_id,
158 headers, nullptr);
159}
160
161void QuicSimpleServerSession::HandlePromisedPushRequests() {
162 while (!promised_streams_.empty() && ShouldCreateOutgoingDynamicStream()) {
163 const PromisedStreamInfo& promised_info = promised_streams_.front();
164 DCHECK_EQ(next_outgoing_stream_id(), promised_info.stream_id);
165
166 if (promised_info.is_cancelled) {
167 // This stream has been reset by client. Skip this stream id.
168 promised_streams_.pop_front();
169 GetNextOutgoingStreamId();
170 return;
171 }
172
173 QuicSimpleServerStream* promised_stream =
174 static_cast<QuicSimpleServerStream*>(
175 CreateOutgoingDynamicStream(promised_info.priority));
176 DCHECK(promised_stream != nullptr);
177 DCHECK_EQ(promised_info.stream_id, promised_stream->id());
178 DVLOG(1) << "created server push stream " << promised_stream->id();
179
ckrasic244375a32016-02-04 21:21:22180 const SpdyHeaderBlock request_headers(promised_info.request_headers);
zhongyi4a9d27b2016-01-12 20:08:31181
182 promised_streams_.pop_front();
ckrasic244375a32016-02-04 21:21:22183 promised_stream->PushResponse(request_headers);
zhongyi4a9d27b2016-01-12 20:08:31184 }
185}
186
jokulikc971baf92016-01-06 00:36:39187} // namespace net