Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: net/spdy/spdy_http_stream.cc

Issue 2832973003: Split net/spdy into core and chromium subdirectories. (Closed)
Patch Set: Fix some more build rules. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/spdy_http_stream.h ('k') | net/spdy/spdy_http_stream_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "net/spdy/spdy_http_stream.h"
6
7 #include <algorithm>
8 #include <list>
9 #include <memory>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/values.h"
19 #include "net/base/host_port_pair.h"
20 #include "net/base/upload_data_stream.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_info.h"
24 #include "net/log/net_log_event_type.h"
25 #include "net/log/net_log_with_source.h"
26 #include "net/spdy/platform/api/spdy_string.h"
27 #include "net/spdy/spdy_header_block.h"
28 #include "net/spdy/spdy_http_utils.h"
29 #include "net/spdy/spdy_protocol.h"
30 #include "net/spdy/spdy_session.h"
31
32 namespace net {
33
34 const size_t SpdyHttpStream::kRequestBodyBufferSize = 1 << 14; // 16KB
35
36 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
37 bool direct,
38 NetLogSource source_dependency)
39 : MultiplexedHttpStream(MultiplexedSessionHandle(spdy_session)),
40 spdy_session_(spdy_session),
41 is_reused_(spdy_session_->IsReused()),
42 source_dependency_(source_dependency),
43 stream_(nullptr),
44 stream_closed_(false),
45 closed_stream_status_(ERR_FAILED),
46 closed_stream_id_(0),
47 closed_stream_received_bytes_(0),
48 closed_stream_sent_bytes_(0),
49 request_info_(NULL),
50 response_info_(NULL),
51 response_headers_complete_(false),
52 user_buffer_len_(0),
53 request_body_buf_size_(0),
54 buffered_read_callback_pending_(false),
55 more_read_data_pending_(false),
56 direct_(direct),
57 was_alpn_negotiated_(false),
58 weak_factory_(this) {
59 DCHECK(spdy_session_.get());
60 }
61
62 SpdyHttpStream::~SpdyHttpStream() {
63 if (stream_) {
64 stream_->DetachDelegate();
65 DCHECK(!stream_);
66 }
67 }
68
69 int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info,
70 RequestPriority priority,
71 const NetLogWithSource& stream_net_log,
72 const CompletionCallback& callback) {
73 DCHECK(!stream_);
74 if (!spdy_session_)
75 return ERR_CONNECTION_CLOSED;
76
77 request_info_ = request_info;
78 if (request_info_->method == "GET") {
79 int error = spdy_session_->GetPushStream(request_info_->url, priority,
80 &stream_, stream_net_log);
81 if (error != OK)
82 return error;
83
84 // |stream_| may be NULL even if OK was returned.
85 if (stream_) {
86 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
87 InitializeStreamHelper();
88 return OK;
89 }
90 }
91
92 int rv = stream_request_.StartRequest(
93 SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
94 priority, stream_net_log,
95 base::Bind(&SpdyHttpStream::OnStreamCreated,
96 weak_factory_.GetWeakPtr(), callback));
97
98 if (rv == OK) {
99 stream_ = stream_request_.ReleaseStream().get();
100 InitializeStreamHelper();
101 }
102
103 return rv;
104 }
105
106 int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
107 CHECK(!callback.is_null());
108 if (stream_closed_)
109 return closed_stream_status_;
110
111 CHECK(stream_);
112
113 // Check if we already have the response headers. If so, return synchronously.
114 if (response_headers_complete_) {
115 CHECK(!stream_->IsIdle());
116 return OK;
117 }
118
119 // Still waiting for the response, return IO_PENDING.
120 CHECK(response_callback_.is_null());
121 response_callback_ = callback;
122 return ERR_IO_PENDING;
123 }
124
125 int SpdyHttpStream::ReadResponseBody(
126 IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
127 // Invalidate HttpRequestInfo pointer. This is to allow the stream to be
128 // shared across multiple transactions which might require this
129 // stream to outlive the request_'s owner.
130 // Only allowed when Reading of response body starts. It is safe to reset it
131 // at this point since request_->upload_data_stream is also not needed
132 // anymore.
133 request_info_ = nullptr;
134
135 if (stream_)
136 CHECK(!stream_->IsIdle());
137
138 CHECK(buf);
139 CHECK(buf_len);
140 CHECK(!callback.is_null());
141
142 // If we have data buffered, complete the IO immediately.
143 if (!response_body_queue_.IsEmpty()) {
144 return response_body_queue_.Dequeue(buf->data(), buf_len);
145 } else if (stream_closed_) {
146 return closed_stream_status_;
147 }
148
149 CHECK(response_callback_.is_null());
150 CHECK(!user_buffer_.get());
151 CHECK_EQ(0, user_buffer_len_);
152
153 response_callback_ = callback;
154 user_buffer_ = buf;
155 user_buffer_len_ = buf_len;
156 return ERR_IO_PENDING;
157 }
158
159 void SpdyHttpStream::Close(bool not_reusable) {
160 // Note: the not_reusable flag has no meaning for SPDY streams.
161
162 Cancel();
163 DCHECK(!stream_);
164 }
165
166 bool SpdyHttpStream::IsResponseBodyComplete() const {
167 return stream_closed_;
168 }
169
170 bool SpdyHttpStream::IsConnectionReused() const {
171 return is_reused_;
172 }
173
174 int64_t SpdyHttpStream::GetTotalReceivedBytes() const {
175 if (stream_closed_)
176 return closed_stream_received_bytes_;
177
178 if (!stream_)
179 return 0;
180
181 return stream_->raw_received_bytes();
182 }
183
184 int64_t SpdyHttpStream::GetTotalSentBytes() const {
185 if (stream_closed_)
186 return closed_stream_sent_bytes_;
187
188 if (!stream_)
189 return 0;
190
191 return stream_->raw_sent_bytes();
192 }
193
194 bool SpdyHttpStream::GetAlternativeService(
195 AlternativeService* alternative_service) const {
196 return false;
197 }
198
199 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
200 if (stream_closed_) {
201 if (!closed_stream_has_load_timing_info_)
202 return false;
203 *load_timing_info = closed_stream_load_timing_info_;
204 return true;
205 }
206
207 // If |stream_| has yet to be created, or does not yet have an ID, fail.
208 // The reused flag can only be correctly set once a stream has an ID. Streams
209 // get their IDs once the request has been successfully sent, so this does not
210 // behave that differently from other stream types.
211 if (!stream_ || stream_->stream_id() == 0)
212 return false;
213
214 return stream_->GetLoadTimingInfo(load_timing_info);
215 }
216
217 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
218 HttpResponseInfo* response,
219 const CompletionCallback& callback) {
220 if (stream_closed_) {
221 return closed_stream_status_;
222 }
223
224 base::Time request_time = base::Time::Now();
225 CHECK(stream_);
226
227 stream_->SetRequestTime(request_time);
228 // This should only get called in the case of a request occurring
229 // during server push that has already begun but hasn't finished,
230 // so we set the response's request time to be the actual one
231 if (response_info_)
232 response_info_->request_time = request_time;
233
234 CHECK(!request_body_buf_.get());
235 if (HasUploadData()) {
236 request_body_buf_ = new IOBufferWithSize(kRequestBodyBufferSize);
237 // The request body buffer is empty at first.
238 request_body_buf_size_ = 0;
239 }
240
241 CHECK(!callback.is_null());
242 CHECK(response);
243
244 // SendRequest can be called in two cases.
245 //
246 // a) A client initiated request. In this case, |response_info_| should be
247 // NULL to start with.
248 // b) A client request which matches a response that the server has already
249 // pushed.
250 if (push_response_info_.get()) {
251 *response = *(push_response_info_.get());
252 push_response_info_.reset();
253 } else {
254 DCHECK_EQ(static_cast<HttpResponseInfo*>(NULL), response_info_);
255 }
256
257 response_info_ = response;
258
259 // Put the peer's IP address and port into the response.
260 IPEndPoint address;
261 int result = stream_->GetPeerAddress(&address);
262 if (result != OK)
263 return result;
264 response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
265
266 if (stream_->type() == SPDY_PUSH_STREAM) {
267 // Pushed streams do not send any data, and should always be
268 // idle. However, we still want to return ERR_IO_PENDING to mimic
269 // non-push behavior. The callback will be called when the
270 // response is received.
271 CHECK(response_callback_.is_null());
272 response_callback_ = callback;
273 return ERR_IO_PENDING;
274 }
275
276 SpdyHeaderBlock headers;
277 CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, direct_,
278 &headers);
279 stream_->net_log().AddEvent(
280 NetLogEventType::HTTP_TRANSACTION_HTTP2_SEND_REQUEST_HEADERS,
281 base::Bind(&SpdyHeaderBlockNetLogCallback, &headers));
282 result = stream_->SendRequestHeaders(
283 std::move(headers),
284 HasUploadData() ? MORE_DATA_TO_SEND : NO_MORE_DATA_TO_SEND);
285
286 if (result == ERR_IO_PENDING) {
287 CHECK(request_callback_.is_null());
288 request_callback_ = callback;
289 }
290 return result;
291 }
292
293 void SpdyHttpStream::Cancel() {
294 request_callback_.Reset();
295 response_callback_.Reset();
296 if (stream_) {
297 stream_->Cancel();
298 DCHECK(!stream_);
299 }
300 }
301
302 void SpdyHttpStream::OnHeadersSent() {
303 if (HasUploadData()) {
304 ReadAndSendRequestBodyData();
305 } else {
306 MaybePostRequestCallback(OK);
307 }
308 }
309
310 void SpdyHttpStream::OnHeadersReceived(
311 const SpdyHeaderBlock& response_headers) {
312 DCHECK(!response_headers_complete_);
313 response_headers_complete_ = true;
314
315 if (!response_info_) {
316 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
317 push_response_info_.reset(new HttpResponseInfo);
318 response_info_ = push_response_info_.get();
319 }
320
321 const bool headers_valid =
322 SpdyHeadersToHttpResponse(response_headers, response_info_);
323 DCHECK(headers_valid);
324
325 response_info_->response_time = stream_->response_time();
326 // Don't store the SSLInfo in the response here, HttpNetworkTransaction
327 // will take care of that part.
328 response_info_->was_alpn_negotiated = was_alpn_negotiated_;
329 response_info_->request_time = stream_->GetRequestTime();
330 response_info_->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
331 response_info_->alpn_negotiated_protocol =
332 HttpResponseInfo::ConnectionInfoToString(response_info_->connection_info);
333 response_info_->vary_data
334 .Init(*request_info_, *response_info_->headers.get());
335
336 if (!response_callback_.is_null()) {
337 DoResponseCallback(OK);
338 }
339 }
340
341 void SpdyHttpStream::OnDataReceived(std::unique_ptr<SpdyBuffer> buffer) {
342 DCHECK(response_headers_complete_);
343
344 // Note that data may be received for a SpdyStream prior to the user calling
345 // ReadResponseBody(), therefore user_buffer_ may be NULL. This may often
346 // happen for server initiated streams.
347 DCHECK(stream_);
348 DCHECK(!stream_->IsClosed() || stream_->type() == SPDY_PUSH_STREAM);
349 if (buffer) {
350 response_body_queue_.Enqueue(std::move(buffer));
351
352 if (user_buffer_.get()) {
353 // Handing small chunks of data to the caller creates measurable overhead.
354 // We buffer data in short time-spans and send a single read notification.
355 ScheduleBufferedReadCallback();
356 }
357 }
358 }
359
360 void SpdyHttpStream::OnDataSent() {
361 request_body_buf_size_ = 0;
362 ReadAndSendRequestBodyData();
363 }
364
365 // TODO(xunjieli): Maybe do something with the trailers. crbug.com/422958.
366 void SpdyHttpStream::OnTrailers(const SpdyHeaderBlock& trailers) {}
367
368 void SpdyHttpStream::OnClose(int status) {
369 // Cancel any pending reads from the upload data stream.
370 if (request_info_ && request_info_->upload_data_stream)
371 request_info_->upload_data_stream->Reset();
372
373 if (stream_) {
374 stream_closed_ = true;
375 closed_stream_status_ = status;
376 closed_stream_id_ = stream_->stream_id();
377 closed_stream_has_load_timing_info_ =
378 stream_->GetLoadTimingInfo(&closed_stream_load_timing_info_);
379 closed_stream_received_bytes_ = stream_->raw_received_bytes();
380 closed_stream_sent_bytes_ = stream_->raw_sent_bytes();
381 }
382 stream_ = nullptr;
383
384 // Callbacks might destroy |this|.
385 base::WeakPtr<SpdyHttpStream> self = weak_factory_.GetWeakPtr();
386
387 if (!request_callback_.is_null()) {
388 DoRequestCallback(status);
389 if (!self)
390 return;
391 }
392
393 if (status == OK) {
394 // We need to complete any pending buffered read now.
395 DoBufferedReadCallback();
396 if (!self)
397 return;
398 }
399
400 if (!response_callback_.is_null()) {
401 DoResponseCallback(status);
402 }
403 }
404
405 NetLogSource SpdyHttpStream::source_dependency() const {
406 return source_dependency_;
407 }
408
409 bool SpdyHttpStream::HasUploadData() const {
410 CHECK(request_info_);
411 return
412 request_info_->upload_data_stream &&
413 ((request_info_->upload_data_stream->size() > 0) ||
414 request_info_->upload_data_stream->is_chunked());
415 }
416
417 void SpdyHttpStream::OnStreamCreated(
418 const CompletionCallback& callback,
419 int rv) {
420 if (rv == OK) {
421 stream_ = stream_request_.ReleaseStream().get();
422 InitializeStreamHelper();
423 }
424 callback.Run(rv);
425 }
426
427 void SpdyHttpStream::ReadAndSendRequestBodyData() {
428 CHECK(HasUploadData());
429 CHECK_EQ(request_body_buf_size_, 0);
430 if (request_info_->upload_data_stream->IsEOF()) {
431 MaybePostRequestCallback(OK);
432 return;
433 }
434
435 // Read the data from the request body stream.
436 const int rv = request_info_->upload_data_stream
437 ->Read(request_body_buf_.get(),
438 request_body_buf_->size(),
439 base::Bind(&SpdyHttpStream::OnRequestBodyReadCompleted,
440 weak_factory_.GetWeakPtr()));
441
442 if (rv != ERR_IO_PENDING)
443 OnRequestBodyReadCompleted(rv);
444 }
445
446 void SpdyHttpStream::InitializeStreamHelper() {
447 stream_->SetDelegate(this);
448 was_alpn_negotiated_ = stream_->WasAlpnNegotiated();
449 }
450
451 void SpdyHttpStream::ResetStreamInternal() {
452 spdy_session_->ResetStream(stream()->stream_id(), ERROR_CODE_INTERNAL_ERROR,
453 SpdyString());
454 }
455
456 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
457 if (status < 0) {
458 DCHECK_NE(ERR_IO_PENDING, status);
459 // Post |request_callback_| with received error. This should be posted
460 // before ResetStreamInternal, because the latter would call
461 // |request_callback_| via OnClose with an error code potentially different
462 // from |status|.
463 MaybePostRequestCallback(status);
464
465 base::ThreadTaskRunnerHandle::Get()->PostTask(
466 FROM_HERE, base::Bind(&SpdyHttpStream::ResetStreamInternal,
467 weak_factory_.GetWeakPtr()));
468
469 return;
470 }
471
472 CHECK_GE(status, 0);
473 request_body_buf_size_ = status;
474 const bool eof = request_info_->upload_data_stream->IsEOF();
475 // Only the final frame may have a length of 0.
476 if (eof) {
477 CHECK_GE(request_body_buf_size_, 0);
478 } else {
479 CHECK_GT(request_body_buf_size_, 0);
480 }
481 stream_->SendData(request_body_buf_.get(),
482 request_body_buf_size_,
483 eof ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
484 }
485
486 void SpdyHttpStream::ScheduleBufferedReadCallback() {
487 // If there is already a scheduled DoBufferedReadCallback, don't issue
488 // another one. Mark that we have received more data and return.
489 if (buffered_read_callback_pending_) {
490 more_read_data_pending_ = true;
491 return;
492 }
493
494 more_read_data_pending_ = false;
495 buffered_read_callback_pending_ = true;
496 const base::TimeDelta kBufferTime = base::TimeDelta::FromMilliseconds(1);
497 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
498 FROM_HERE, base::Bind(&SpdyHttpStream::DoBufferedReadCallback,
499 weak_factory_.GetWeakPtr()),
500 kBufferTime);
501 }
502
503 // Checks to see if we should wait for more buffered data before notifying
504 // the caller. Returns true if we should wait, false otherwise.
505 bool SpdyHttpStream::ShouldWaitForMoreBufferedData() const {
506 // If the response is complete, there is no point in waiting.
507 if (stream_closed_)
508 return false;
509
510 DCHECK_GT(user_buffer_len_, 0);
511 return response_body_queue_.GetTotalSize() <
512 static_cast<size_t>(user_buffer_len_);
513 }
514
515 void SpdyHttpStream::DoBufferedReadCallback() {
516 buffered_read_callback_pending_ = false;
517
518 // If the transaction is cancelled or errored out, we don't need to complete
519 // the read.
520 if (!stream_ && !stream_closed_)
521 return;
522
523 int stream_status =
524 stream_closed_ ? closed_stream_status_ : stream_->response_status();
525 if (stream_status != OK)
526 return;
527
528 // When more_read_data_pending_ is true, it means that more data has
529 // arrived since we started waiting. Wait a little longer and continue
530 // to buffer.
531 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
532 ScheduleBufferedReadCallback();
533 return;
534 }
535
536 int rv = 0;
537 if (user_buffer_.get()) {
538 rv = ReadResponseBody(user_buffer_.get(), user_buffer_len_,
539 response_callback_);
540 CHECK_NE(rv, ERR_IO_PENDING);
541 user_buffer_ = NULL;
542 user_buffer_len_ = 0;
543 DoResponseCallback(rv);
544 }
545 }
546
547 void SpdyHttpStream::DoRequestCallback(int rv) {
548 CHECK_NE(rv, ERR_IO_PENDING);
549 CHECK(!request_callback_.is_null());
550 // Since Run may result in being called back, reset request_callback_ in
551 // advance.
552 base::ResetAndReturn(&request_callback_).Run(rv);
553 }
554
555 void SpdyHttpStream::MaybeDoRequestCallback(int rv) {
556 CHECK_NE(ERR_IO_PENDING, rv);
557 if (request_callback_)
558 base::ResetAndReturn(&request_callback_).Run(rv);
559 }
560
561 void SpdyHttpStream::MaybePostRequestCallback(int rv) {
562 CHECK_NE(ERR_IO_PENDING, rv);
563 if (request_callback_)
564 base::ThreadTaskRunnerHandle::Get()->PostTask(
565 FROM_HERE, base::Bind(&SpdyHttpStream::MaybeDoRequestCallback,
566 weak_factory_.GetWeakPtr(), rv));
567 }
568
569 void SpdyHttpStream::DoResponseCallback(int rv) {
570 CHECK_NE(rv, ERR_IO_PENDING);
571 CHECK(!response_callback_.is_null());
572
573 // Since Run may result in being called back, reset response_callback_ in
574 // advance.
575 base::ResetAndReturn(&response_callback_).Run(rv);
576 }
577
578 bool SpdyHttpStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
579 if (!spdy_session_)
580 return false;
581
582 return spdy_session_->GetPeerAddress(endpoint) == OK;
583 }
584
585 void SpdyHttpStream::PopulateNetErrorDetails(NetErrorDetails* details) {
586 details->connection_info = HttpResponseInfo::CONNECTION_INFO_HTTP2;
587 return;
588 }
589
590 void SpdyHttpStream::SetPriority(RequestPriority priority) {
591 // TODO(akalin): Plumb this through to |stream_request_| and
592 // |stream_|.
593 }
594
595 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/spdy_http_stream.h ('k') | net/spdy/spdy_http_stream_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698