OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/quic/quic_spdy_stream.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "net/quic/quic_bug_tracker.h" | |
12 #include "net/quic/quic_spdy_session.h" | |
13 #include "net/quic/quic_utils.h" | |
14 #include "net/quic/quic_write_blocked_list.h" | |
15 #include "net/quic/spdy_utils.h" | |
16 | |
17 using base::IntToString; | |
18 using base::StringPiece; | |
19 using std::min; | |
20 using std::string; | |
21 | |
22 namespace net { | |
23 | |
24 #define ENDPOINT \ | |
25 (session()->perspective() == Perspective::IS_SERVER ? "Server: " : "Client:" \ | |
26 " ") | |
27 | |
28 QuicSpdyStream::QuicSpdyStream(QuicStreamId id, QuicSpdySession* spdy_session) | |
29 : ReliableQuicStream(id, spdy_session), | |
30 spdy_session_(spdy_session), | |
31 visitor_(nullptr), | |
32 headers_decompressed_(false), | |
33 priority_(kDefaultPriority), | |
34 trailers_decompressed_(false), | |
35 trailers_delivered_(false) { | |
36 DCHECK_NE(kCryptoStreamId, id); | |
37 // Don't receive any callbacks from the sequencer until headers | |
38 // are complete. | |
39 sequencer()->SetBlockedUntilFlush(); | |
40 spdy_session_->RegisterStreamPriority(id, priority_); | |
41 } | |
42 | |
43 QuicSpdyStream::~QuicSpdyStream() { | |
44 if (spdy_session_ != nullptr) { | |
45 spdy_session_->UnregisterStreamPriority(id()); | |
46 } | |
47 } | |
48 | |
49 void QuicSpdyStream::CloseWriteSide() { | |
50 if (!fin_received() && !rst_received() && sequencer()->ignore_read_data() && | |
51 !rst_sent()) { | |
52 DCHECK(fin_sent()); | |
53 // Tell the peer to stop sending further data. | |
54 DVLOG(1) << ENDPOINT << "Send QUIC_STREAM_NO_ERROR on stream " << id(); | |
55 Reset(QUIC_STREAM_NO_ERROR); | |
56 } | |
57 | |
58 ReliableQuicStream::CloseWriteSide(); | |
59 } | |
60 | |
61 void QuicSpdyStream::StopReading() { | |
62 if (!fin_received() && !rst_received() && write_side_closed() && | |
63 !rst_sent()) { | |
64 DCHECK(fin_sent()); | |
65 // Tell the peer to stop sending further data. | |
66 DVLOG(1) << ENDPOINT << "Send QUIC_STREAM_NO_ERROR on stream " << id(); | |
67 Reset(QUIC_STREAM_NO_ERROR); | |
68 } | |
69 ReliableQuicStream::StopReading(); | |
70 } | |
71 | |
72 size_t QuicSpdyStream::WriteHeaders( | |
73 SpdyHeaderBlock header_block, | |
74 bool fin, | |
75 QuicAckListenerInterface* ack_notifier_delegate) { | |
76 size_t bytes_written = spdy_session_->WriteHeaders( | |
77 id(), std::move(header_block), fin, priority_, ack_notifier_delegate); | |
78 if (fin) { | |
79 // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent. | |
80 set_fin_sent(true); | |
81 CloseWriteSide(); | |
82 } | |
83 return bytes_written; | |
84 } | |
85 | |
86 void QuicSpdyStream::WriteOrBufferBody( | |
87 const string& data, | |
88 bool fin, | |
89 QuicAckListenerInterface* ack_notifier_delegate) { | |
90 WriteOrBufferData(data, fin, ack_notifier_delegate); | |
91 } | |
92 | |
93 size_t QuicSpdyStream::WriteTrailers( | |
94 SpdyHeaderBlock trailer_block, | |
95 QuicAckListenerInterface* ack_notifier_delegate) { | |
96 if (fin_sent()) { | |
97 QUIC_BUG << "Trailers cannot be sent after a FIN."; | |
98 return 0; | |
99 } | |
100 | |
101 // The header block must contain the final offset for this stream, as the | |
102 // trailers may be processed out of order at the peer. | |
103 DVLOG(1) << "Inserting trailer: (" << kFinalOffsetHeaderKey << ", " | |
104 << stream_bytes_written() + queued_data_bytes() << ")"; | |
105 trailer_block.insert(std::make_pair( | |
106 kFinalOffsetHeaderKey, | |
107 IntToString(stream_bytes_written() + queued_data_bytes()))); | |
108 | |
109 // Write the trailing headers with a FIN, and close stream for writing: | |
110 // trailers are the last thing to be sent on a stream. | |
111 const bool kFin = true; | |
112 size_t bytes_written = spdy_session_->WriteHeaders( | |
113 id(), std::move(trailer_block), kFin, priority_, ack_notifier_delegate); | |
114 set_fin_sent(kFin); | |
115 | |
116 // Trailers are the last thing to be sent on a stream, but if there is still | |
117 // queued data then CloseWriteSide() will cause it never to be sent. | |
118 if (queued_data_bytes() == 0) { | |
119 CloseWriteSide(); | |
120 } | |
121 | |
122 return bytes_written; | |
123 } | |
124 | |
125 size_t QuicSpdyStream::Readv(const struct iovec* iov, size_t iov_len) { | |
126 DCHECK(FinishedReadingHeaders()); | |
127 return sequencer()->Readv(iov, iov_len); | |
128 } | |
129 | |
130 int QuicSpdyStream::GetReadableRegions(iovec* iov, size_t iov_len) const { | |
131 DCHECK(FinishedReadingHeaders()); | |
132 return sequencer()->GetReadableRegions(iov, iov_len); | |
133 } | |
134 | |
135 void QuicSpdyStream::MarkConsumed(size_t num_bytes) { | |
136 DCHECK(FinishedReadingHeaders()); | |
137 return sequencer()->MarkConsumed(num_bytes); | |
138 } | |
139 | |
140 bool QuicSpdyStream::IsDoneReading() const { | |
141 bool done_reading_headers = FinishedReadingHeaders(); | |
142 bool done_reading_body = sequencer()->IsClosed(); | |
143 bool done_reading_trailers = FinishedReadingTrailers(); | |
144 return done_reading_headers && done_reading_body && done_reading_trailers; | |
145 } | |
146 | |
147 bool QuicSpdyStream::HasBytesToRead() const { | |
148 bool headers_to_read = !decompressed_headers_.empty(); | |
149 bool body_to_read = sequencer()->HasBytesToRead(); | |
150 bool trailers_to_read = !decompressed_trailers_.empty(); | |
151 return headers_to_read || body_to_read || trailers_to_read; | |
152 } | |
153 | |
154 void QuicSpdyStream::MarkHeadersConsumed(size_t bytes_consumed) { | |
155 decompressed_headers_.erase(0, bytes_consumed); | |
156 if (FinishedReadingHeaders()) { | |
157 sequencer()->SetUnblocked(); | |
158 } | |
159 } | |
160 | |
161 void QuicSpdyStream::MarkTrailersConsumed(size_t bytes_consumed) { | |
162 decompressed_trailers_.erase(0, bytes_consumed); | |
163 } | |
164 | |
165 void QuicSpdyStream::MarkTrailersDelivered() { | |
166 trailers_delivered_ = true; | |
167 } | |
168 | |
169 void QuicSpdyStream::ConsumeHeaderList() { | |
170 header_list_.Clear(); | |
171 if (FinishedReadingHeaders()) { | |
172 sequencer()->SetUnblocked(); | |
173 } | |
174 } | |
175 | |
176 void QuicSpdyStream::SetPriority(SpdyPriority priority) { | |
177 DCHECK_EQ(0u, stream_bytes_written()); | |
178 spdy_session_->UpdateStreamPriority(id(), priority); | |
179 priority_ = priority; | |
180 } | |
181 | |
182 void QuicSpdyStream::OnStreamHeaders(StringPiece headers_data) { | |
183 if (!headers_decompressed_) { | |
184 headers_data.AppendToString(&decompressed_headers_); | |
185 } else { | |
186 DCHECK(!trailers_decompressed_); | |
187 headers_data.AppendToString(&decompressed_trailers_); | |
188 } | |
189 } | |
190 | |
191 void QuicSpdyStream::OnStreamHeadersPriority(SpdyPriority priority) { | |
192 DCHECK_EQ(Perspective::IS_SERVER, session()->connection()->perspective()); | |
193 SetPriority(priority); | |
194 } | |
195 | |
196 void QuicSpdyStream::OnStreamHeadersComplete(bool fin, size_t frame_len) { | |
197 if (!headers_decompressed_) { | |
198 OnInitialHeadersComplete(fin, frame_len); | |
199 } else { | |
200 OnTrailingHeadersComplete(fin, frame_len); | |
201 } | |
202 } | |
203 | |
204 void QuicSpdyStream::OnStreamHeaderList(bool fin, | |
205 size_t frame_len, | |
206 const QuicHeaderList& header_list) { | |
207 if (!headers_decompressed_) { | |
208 OnInitialHeadersComplete(fin, frame_len, header_list); | |
209 } else { | |
210 OnTrailingHeadersComplete(fin, frame_len, header_list); | |
211 } | |
212 } | |
213 | |
214 void QuicSpdyStream::OnInitialHeadersComplete(bool fin, size_t /*frame_len*/) { | |
215 headers_decompressed_ = true; | |
216 if (fin) { | |
217 OnStreamFrame(QuicStreamFrame(id(), fin, 0, StringPiece())); | |
218 } | |
219 if (FinishedReadingHeaders()) { | |
220 sequencer()->SetUnblocked(); | |
221 } | |
222 } | |
223 | |
224 void QuicSpdyStream::OnInitialHeadersComplete( | |
225 bool fin, | |
226 size_t /*frame_len*/, | |
227 const QuicHeaderList& header_list) { | |
228 headers_decompressed_ = true; | |
229 header_list_ = header_list; | |
230 if (fin) { | |
231 OnStreamFrame(QuicStreamFrame(id(), fin, 0, StringPiece())); | |
232 } | |
233 if (FinishedReadingHeaders()) { | |
234 sequencer()->SetUnblocked(); | |
235 } | |
236 } | |
237 | |
238 void QuicSpdyStream::OnPromiseHeaders(StringPiece headers_data) { | |
239 headers_data.AppendToString(&decompressed_headers_); | |
240 } | |
241 | |
242 void QuicSpdyStream::OnPromiseHeadersComplete( | |
243 QuicStreamId /* promised_stream_id */, | |
244 size_t /* frame_len */) { | |
245 // To be overridden in QuicSpdyClientStream. Not supported on | |
246 // server side. | |
247 session()->connection()->CloseConnection( | |
248 QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server", | |
249 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
250 return; | |
251 } | |
252 | |
253 void QuicSpdyStream::OnPromiseHeaderList( | |
254 QuicStreamId /* promised_id */, | |
255 size_t /* frame_len */, | |
256 const QuicHeaderList& /*header_list */) { | |
257 // To be overridden in QuicSpdyClientStream. Not supported on | |
258 // server side. | |
259 session()->connection()->CloseConnection( | |
260 QUIC_INVALID_HEADERS_STREAM_DATA, "Promise headers received by server", | |
261 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
262 return; | |
263 } | |
264 | |
265 void QuicSpdyStream::OnTrailingHeadersComplete(bool fin, size_t /*frame_len*/) { | |
266 DCHECK(!trailers_decompressed_); | |
267 if (fin_received()) { | |
268 DLOG(ERROR) << "Received Trailers after FIN, on stream: " << id(); | |
269 session()->connection()->CloseConnection( | |
270 QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin", | |
271 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
272 return; | |
273 } | |
274 if (!fin) { | |
275 DLOG(ERROR) << "Trailers must have FIN set, on stream: " << id(); | |
276 session()->connection()->CloseConnection( | |
277 QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers", | |
278 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
279 return; | |
280 } | |
281 | |
282 size_t final_byte_offset = 0; | |
283 if (!SpdyUtils::ParseTrailers(decompressed_trailers().data(), | |
284 decompressed_trailers().length(), | |
285 &final_byte_offset, &received_trailers_)) { | |
286 DLOG(ERROR) << "Trailers are malformed: " << id(); | |
287 session()->connection()->CloseConnection( | |
288 QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed", | |
289 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
290 return; | |
291 } | |
292 | |
293 // The data on this stream ends at |final_byte_offset|. | |
294 DVLOG(1) << "Stream ends at byte offset: " << final_byte_offset | |
295 << " currently read: " << stream_bytes_read(); | |
296 | |
297 OnStreamFrame(QuicStreamFrame(id(), fin, final_byte_offset, StringPiece())); | |
298 trailers_decompressed_ = true; | |
299 } | |
300 | |
301 void QuicSpdyStream::OnTrailingHeadersComplete( | |
302 bool fin, | |
303 size_t /*frame_len*/, | |
304 const QuicHeaderList& header_list) { | |
305 DCHECK(!trailers_decompressed_); | |
306 if (fin_received()) { | |
307 DLOG(ERROR) << "Received Trailers after FIN, on stream: " << id(); | |
308 session()->connection()->CloseConnection( | |
309 QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers after fin", | |
310 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
311 return; | |
312 } | |
313 if (!fin) { | |
314 DLOG(ERROR) << "Trailers must have FIN set, on stream: " << id(); | |
315 session()->connection()->CloseConnection( | |
316 QUIC_INVALID_HEADERS_STREAM_DATA, "Fin missing from trailers", | |
317 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
318 return; | |
319 } | |
320 | |
321 size_t final_byte_offset = 0; | |
322 if (!SpdyUtils::CopyAndValidateTrailers(header_list, &final_byte_offset, | |
323 &received_trailers_)) { | |
324 DLOG(ERROR) << "Trailers are malformed: " << id(); | |
325 session()->connection()->CloseConnection( | |
326 QUIC_INVALID_HEADERS_STREAM_DATA, "Trailers are malformed", | |
327 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | |
328 return; | |
329 } | |
330 OnStreamFrame(QuicStreamFrame(id(), fin, final_byte_offset, StringPiece())); | |
331 trailers_decompressed_ = true; | |
332 } | |
333 | |
334 void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) { | |
335 if (frame.error_code != QUIC_STREAM_NO_ERROR) { | |
336 ReliableQuicStream::OnStreamReset(frame); | |
337 return; | |
338 } | |
339 DVLOG(1) << "Received QUIC_STREAM_NO_ERROR, not discarding response"; | |
340 set_rst_received(true); | |
341 MaybeIncreaseHighestReceivedOffset(frame.byte_offset); | |
342 set_stream_error(frame.error_code); | |
343 CloseWriteSide(); | |
344 } | |
345 | |
346 void QuicSpdyStream::OnClose() { | |
347 ReliableQuicStream::OnClose(); | |
348 | |
349 if (visitor_) { | |
350 Visitor* visitor = visitor_; | |
351 // Calling Visitor::OnClose() may result the destruction of the visitor, | |
352 // so we need to ensure we don't call it again. | |
353 visitor_ = nullptr; | |
354 visitor->OnClose(this); | |
355 } | |
356 } | |
357 | |
358 bool QuicSpdyStream::FinishedReadingHeaders() const { | |
359 return headers_decompressed_ && decompressed_headers_.empty() && | |
360 header_list_.empty(); | |
361 } | |
362 | |
363 bool QuicSpdyStream::ParseHeaderStatusCode(const SpdyHeaderBlock& header, | |
364 int* status_code) const { | |
365 SpdyHeaderBlock::const_iterator it = header.find(":status"); | |
366 if (it == header.end()) { | |
367 return false; | |
368 } | |
369 const StringPiece status(it->second); | |
370 if (status.size() != 3) { | |
371 return false; | |
372 } | |
373 // First character must be an integer in range [1,5]. | |
374 if (status[0] < '1' || status[0] > '5') { | |
375 return false; | |
376 } | |
377 // The remaining two characters must be integers. | |
378 if (!isdigit(status[1]) || !isdigit(status[2])) { | |
379 return false; | |
380 } | |
381 return StringToInt(status, status_code); | |
382 } | |
383 | |
384 bool QuicSpdyStream::FinishedReadingTrailers() const { | |
385 // If no further trailing headers are expected, and the decompressed trailers | |
386 // (if any) have been consumed, then reading of trailers is finished. | |
387 if (!fin_received()) { | |
388 return false; | |
389 } else if (!trailers_decompressed_) { | |
390 return true; | |
391 } else { | |
392 return trailers_delivered_ && decompressed_trailers_.empty(); | |
393 } | |
394 } | |
395 | |
396 SpdyPriority QuicSpdyStream::priority() const { | |
397 return priority_; | |
398 } | |
399 | |
400 void QuicSpdyStream::ClearSession() { | |
401 spdy_session_ = nullptr; | |
402 } | |
403 | |
404 QuicConsumedData QuicSpdyStream::WritevDataInner( | |
405 QuicIOVector iov, | |
406 QuicStreamOffset offset, | |
407 bool fin, | |
408 QuicAckListenerInterface* ack_notifier_delegate) { | |
409 if (spdy_session_->headers_stream() != nullptr && | |
410 spdy_session_->force_hol_blocking()) { | |
411 return spdy_session_->headers_stream()->WritevStreamData( | |
412 id(), iov, offset, fin, ack_notifier_delegate); | |
413 } | |
414 return ReliableQuicStream::WritevDataInner(iov, offset, fin, | |
415 ack_notifier_delegate); | |
416 } | |
417 | |
418 } // namespace net | |
OLD | NEW |