OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/tools/quic/quic_simple_server_session.h" | 5 #include "net/tools/quic/quic_simple_server_session.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/stl_util.h" |
8 #include "net/quic/proto/cached_network_parameters.pb.h" | 9 #include "net/quic/proto/cached_network_parameters.pb.h" |
9 #include "net/quic/quic_connection.h" | 10 #include "net/quic/quic_connection.h" |
10 #include "net/quic/quic_flags.h" | 11 #include "net/quic/quic_flags.h" |
11 #include "net/quic/quic_spdy_session.h" | 12 #include "net/quic/quic_spdy_session.h" |
12 #include "net/quic/reliable_quic_stream.h" | 13 #include "net/quic/reliable_quic_stream.h" |
13 #include "net/tools/quic/quic_simple_server_stream.h" | 14 #include "net/tools/quic/quic_simple_server_stream.h" |
| 15 #include "url/gurl.h" |
14 | 16 |
15 namespace net { | 17 namespace net { |
16 namespace tools { | 18 namespace tools { |
17 | 19 |
18 QuicSimpleServerSession::QuicSimpleServerSession( | 20 QuicSimpleServerSession::QuicSimpleServerSession( |
19 const QuicConfig& config, | 21 const QuicConfig& config, |
20 QuicConnection* connection, | 22 QuicConnection* connection, |
21 QuicServerSessionVisitor* visitor, | 23 QuicServerSessionVisitor* visitor, |
22 const QuicCryptoServerConfig* crypto_config) | 24 const QuicCryptoServerConfig* crypto_config) |
23 : QuicServerSessionBase(config, connection, visitor, crypto_config) {} | 25 : QuicServerSessionBase(config, connection, visitor, crypto_config), |
| 26 highest_promised_stream_id_(0) {} |
24 | 27 |
25 QuicSimpleServerSession::~QuicSimpleServerSession() {} | 28 QuicSimpleServerSession::~QuicSimpleServerSession() {} |
26 | 29 |
27 QuicCryptoServerStreamBase* | 30 QuicCryptoServerStreamBase* |
28 QuicSimpleServerSession::CreateQuicCryptoServerStream( | 31 QuicSimpleServerSession::CreateQuicCryptoServerStream( |
29 const QuicCryptoServerConfig* crypto_config) { | 32 const QuicCryptoServerConfig* crypto_config) { |
30 return new QuicCryptoServerStream(crypto_config, this); | 33 return new QuicCryptoServerStream(crypto_config, this); |
31 } | 34 } |
32 | 35 |
| 36 void QuicSimpleServerSession::StreamDraining(QuicStreamId id) { |
| 37 QuicSpdySession::StreamDraining(id); |
| 38 if (!IsIncomingStream(id)) { |
| 39 HandlePromisedPushRequests(); |
| 40 } |
| 41 } |
| 42 |
| 43 void QuicSimpleServerSession::OnStreamFrame(const QuicStreamFrame& frame) { |
| 44 if (!IsIncomingStream(frame.stream_id)) { |
| 45 LOG(WARNING) << "Client shouldn't send data on server push stream"; |
| 46 connection()->SendConnectionCloseWithDetails( |
| 47 QUIC_INVALID_STREAM_ID, "Client sent data on server push stream"); |
| 48 return; |
| 49 } |
| 50 QuicSpdySession::OnStreamFrame(frame); |
| 51 } |
| 52 |
| 53 void QuicSimpleServerSession::PromisePushResources( |
| 54 const string& request_url, |
| 55 const list<QuicInMemoryCache::ServerPushInfo>& resources, |
| 56 QuicStreamId original_stream_id, |
| 57 const SpdyHeaderBlock& original_request_headers) { |
| 58 for (QuicInMemoryCache::ServerPushInfo resource : resources) { |
| 59 SpdyHeaderBlock headers = SynthesizePushRequestHeaders( |
| 60 request_url, resource, original_request_headers); |
| 61 highest_promised_stream_id_ += 2; |
| 62 SendPushPromise(original_stream_id, highest_promised_stream_id_, headers); |
| 63 promised_streams_.push_back(PromisedStreamInfo( |
| 64 headers, highest_promised_stream_id_, resource.priority)); |
| 65 } |
| 66 |
| 67 // Procese promised push request as many as possible. |
| 68 HandlePromisedPushRequests(); |
| 69 } |
| 70 |
33 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream( | 71 QuicSpdyStream* QuicSimpleServerSession::CreateIncomingDynamicStream( |
34 QuicStreamId id) { | 72 QuicStreamId id) { |
35 if (!ShouldCreateIncomingDynamicStream(id)) { | 73 if (!ShouldCreateIncomingDynamicStream(id)) { |
36 return nullptr; | 74 return nullptr; |
37 } | 75 } |
38 | 76 |
39 return new QuicSimpleServerStream(id, this); | 77 return new QuicSimpleServerStream(id, this); |
40 } | 78 } |
41 | 79 |
42 QuicSimpleServerStream* QuicSimpleServerSession::CreateOutgoingDynamicStream( | 80 QuicSimpleServerStream* QuicSimpleServerSession::CreateOutgoingDynamicStream( |
43 SpdyPriority priority) { | 81 SpdyPriority priority) { |
44 if (!ShouldCreateOutgoingDynamicStream()) { | 82 if (!ShouldCreateOutgoingDynamicStream()) { |
45 return nullptr; | 83 return nullptr; |
46 } | 84 } |
47 | 85 |
48 QuicSimpleServerStream* stream = | 86 QuicSimpleServerStream* stream = |
49 new QuicSimpleServerStream(GetNextOutgoingStreamId(), this); | 87 new QuicSimpleServerStream(GetNextOutgoingStreamId(), this); |
50 stream->SetPriority(priority); | 88 stream->SetPriority(priority); |
51 ActivateStream(stream); | 89 ActivateStream(stream); |
52 return stream; | 90 return stream; |
53 } | 91 } |
54 | 92 |
| 93 void QuicSimpleServerSession::CloseStreamInner(QuicStreamId stream_id, |
| 94 bool locally_reset) { |
| 95 QuicSpdySession::CloseStreamInner(stream_id, locally_reset); |
| 96 HandlePromisedPushRequests(); |
| 97 } |
| 98 |
| 99 void QuicSimpleServerSession::HandleFrameOnNonexistentOutgoingStream( |
| 100 QuicStreamId stream_id) { |
| 101 // If this stream is a promised but not created stream (stream_id within the |
| 102 // range of next_outgoing_stream_id_ and highes_promised_stream_id_), |
| 103 // connection shouldn't be closed. |
| 104 // Otherwise behave in the same way as base class. |
| 105 if (stream_id > highest_promised_stream_id_) { |
| 106 QuicSession::HandleFrameOnNonexistentOutgoingStream(stream_id); |
| 107 } |
| 108 } |
| 109 |
| 110 void QuicSimpleServerSession::HandleRstOnValidNonexistentStream( |
| 111 const QuicRstStreamFrame& frame) { |
| 112 QuicSession::HandleRstOnValidNonexistentStream(frame); |
| 113 if (!IsClosedStream(frame.stream_id)) { |
| 114 // If a nonexistent stream is not a closed stream and still valid, it must |
| 115 // be a locally preserved stream. Resetting this kind of stream means |
| 116 // cancelling the promised server push. |
| 117 // Since PromisedStreamInfo are queued in sequence, the corresponding |
| 118 // index for it in promised_streams_ can be calculated. |
| 119 DCHECK(frame.stream_id >= next_outgoing_stream_id()); |
| 120 size_t index = (frame.stream_id - next_outgoing_stream_id()) / 2; |
| 121 DCHECK(index <= promised_streams_.size()); |
| 122 promised_streams_[index].is_cancelled = true; |
| 123 connection()->SendRstStream(frame.stream_id, QUIC_RST_ACKNOWLEDGEMENT, 0); |
| 124 } |
| 125 } |
| 126 |
| 127 SpdyHeaderBlock QuicSimpleServerSession::SynthesizePushRequestHeaders( |
| 128 string request_url, |
| 129 QuicInMemoryCache::ServerPushInfo resource, |
| 130 const SpdyHeaderBlock& original_request_headers) { |
| 131 GURL push_request_url = resource.request_url; |
| 132 string path = push_request_url.path(); |
| 133 |
| 134 SpdyHeaderBlock spdy_headers = original_request_headers; |
| 135 // :authority could be different from original request. |
| 136 spdy_headers.ReplaceOrAppendHeader(":authority", push_request_url.host()); |
| 137 spdy_headers.ReplaceOrAppendHeader(":path", path); |
| 138 // Push request always use GET. |
| 139 spdy_headers.ReplaceOrAppendHeader(":method", "GET"); |
| 140 spdy_headers.ReplaceOrAppendHeader("referer", request_url); |
| 141 spdy_headers.ReplaceOrAppendHeader(":scheme", push_request_url.scheme()); |
| 142 // It is not possible to push a response to a request that includes a request |
| 143 // body. |
| 144 spdy_headers.ReplaceOrAppendHeader("content-length", "0"); |
| 145 // Remove "host" field as push request is a directly generated HTTP2 request |
| 146 // which should use ":authority" instead of "host". |
| 147 spdy_headers.erase("host"); |
| 148 return spdy_headers; |
| 149 } |
| 150 |
| 151 void QuicSimpleServerSession::SendPushPromise( |
| 152 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 |
| 161 void 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 |
| 180 promised_stream->PushResponse(promised_info.request_headers); |
| 181 |
| 182 promised_streams_.pop_front(); |
| 183 } |
| 184 } |
| 185 |
55 } // namespace tools | 186 } // namespace tools |
56 } // namespace net | 187 } // namespace net |
OLD | NEW |