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.
5 #include "net/spdy/spdy_http_stream.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "net/base/host_port_pair.h"
15 #include "net/base/net_log.h"
16 #include "net/base/net_util.h"
17 #include "net/base/upload_data_stream.h"
18 #include "net/http/http_request_headers.h"
19 #include "net/http/http_request_info.h"
20 #include "net/http/http_response_info.h"
21 #include "net/spdy/spdy_header_block.h"
22 #include "net/spdy/spdy_http_utils.h"
23 #include "net/spdy/spdy_protocol.h"
24 #include "net/spdy/spdy_session.h"
28 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr
<SpdySession
>& spdy_session
,
30 : weak_factory_(this),
31 spdy_session_(spdy_session
),
32 is_reused_(spdy_session_
->IsReused()),
33 stream_closed_(false),
34 closed_stream_status_(ERR_FAILED
),
36 closed_stream_received_bytes_(0),
39 response_headers_status_(RESPONSE_HEADERS_ARE_INCOMPLETE
),
41 request_body_buf_size_(0),
42 buffered_read_callback_pending_(false),
43 more_read_data_pending_(false),
45 DCHECK(spdy_session_
.get());
48 SpdyHttpStream::~SpdyHttpStream() {
50 stream_
->DetachDelegate();
51 DCHECK(!stream_
.get());
55 int SpdyHttpStream::InitializeStream(const HttpRequestInfo
* request_info
,
56 RequestPriority priority
,
57 const BoundNetLog
& stream_net_log
,
58 const CompletionCallback
& callback
) {
61 return ERR_CONNECTION_CLOSED
;
63 request_info_
= request_info
;
64 if (request_info_
->method
== "GET") {
65 int error
= spdy_session_
->GetPushStream(request_info_
->url
, &stream_
,
70 // |stream_| may be NULL even if OK was returned.
72 DCHECK_EQ(stream_
->type(), SPDY_PUSH_STREAM
);
73 stream_
->SetDelegate(this);
78 int rv
= stream_request_
.StartRequest(
79 SPDY_REQUEST_RESPONSE_STREAM
, spdy_session_
, request_info_
->url
,
80 priority
, stream_net_log
,
81 base::Bind(&SpdyHttpStream::OnStreamCreated
,
82 weak_factory_
.GetWeakPtr(), callback
));
85 stream_
= stream_request_
.ReleaseStream();
86 stream_
->SetDelegate(this);
92 const HttpResponseInfo
* SpdyHttpStream::GetResponseInfo() const {
93 return response_info_
;
96 UploadProgress
SpdyHttpStream::GetUploadProgress() const {
97 if (!request_info_
|| !HasUploadData())
98 return UploadProgress();
100 return UploadProgress(request_info_
->upload_data_stream
->position(),
101 request_info_
->upload_data_stream
->size());
104 int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback
& callback
) {
105 CHECK(!callback
.is_null());
107 return closed_stream_status_
;
109 CHECK(stream_
.get());
111 // Check if we already have the response headers. If so, return synchronously.
112 if (response_headers_status_
== RESPONSE_HEADERS_ARE_COMPLETE
) {
113 CHECK(!stream_
->IsIdle());
117 // Still waiting for the response, return IO_PENDING.
118 CHECK(callback_
.is_null());
119 callback_
= callback
;
120 return ERR_IO_PENDING
;
123 int SpdyHttpStream::ReadResponseBody(
124 IOBuffer
* buf
, int buf_len
, const CompletionCallback
& callback
) {
126 CHECK(!stream_
->IsIdle());
130 CHECK(!callback
.is_null());
132 // If we have data buffered, complete the IO immediately.
133 if (!response_body_queue_
.IsEmpty()) {
134 return response_body_queue_
.Dequeue(buf
->data(), buf_len
);
135 } else if (stream_closed_
) {
136 return closed_stream_status_
;
139 CHECK(callback_
.is_null());
140 CHECK(!user_buffer_
.get());
141 CHECK_EQ(0, user_buffer_len_
);
143 callback_
= callback
;
145 user_buffer_len_
= buf_len
;
146 return ERR_IO_PENDING
;
149 void SpdyHttpStream::Close(bool not_reusable
) {
150 // Note: the not_reusable flag has no meaning for SPDY streams.
153 DCHECK(!stream_
.get());
156 HttpStream
* SpdyHttpStream::RenewStreamForAuth() {
160 bool SpdyHttpStream::IsResponseBodyComplete() const {
161 return stream_closed_
;
164 bool SpdyHttpStream::CanFindEndOfResponse() const {
168 bool SpdyHttpStream::IsConnectionReused() const {
172 void SpdyHttpStream::SetConnectionReused() {
173 // SPDY doesn't need an indicator here.
176 bool SpdyHttpStream::IsConnectionReusable() const {
177 // SPDY streams aren't considered reusable.
181 int64
SpdyHttpStream::GetTotalReceivedBytes() const {
183 return closed_stream_received_bytes_
;
188 return stream_
->raw_received_bytes();
191 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo
* load_timing_info
) const {
192 if (stream_closed_
) {
193 if (!closed_stream_has_load_timing_info_
)
195 *load_timing_info
= closed_stream_load_timing_info_
;
199 // If |stream_| has yet to be created, or does not yet have an ID, fail.
200 // The reused flag can only be correctly set once a stream has an ID. Streams
201 // get their IDs once the request has been successfully sent, so this does not
202 // behave that differently from other stream types.
203 if (!stream_
|| stream_
->stream_id() == 0)
206 return stream_
->GetLoadTimingInfo(load_timing_info
);
209 int SpdyHttpStream::SendRequest(const HttpRequestHeaders
& request_headers
,
210 HttpResponseInfo
* response
,
211 const CompletionCallback
& callback
) {
212 if (stream_closed_
) {
213 if (stream_
->type() == SPDY_PUSH_STREAM
)
214 return closed_stream_status_
;
216 return (closed_stream_status_
== OK
) ? ERR_FAILED
: closed_stream_status_
;
219 base::Time request_time
= base::Time::Now();
220 CHECK(stream_
.get());
222 stream_
->SetRequestTime(request_time
);
223 // This should only get called in the case of a request occurring
224 // during server push that has already begun but hasn't finished,
225 // so we set the response's request time to be the actual one
227 response_info_
->request_time
= request_time
;
229 CHECK(!request_body_buf_
.get());
230 if (HasUploadData()) {
231 // Use kMaxSpdyFrameChunkSize as the buffer size, since the request
232 // body data is written with this size at a time.
233 request_body_buf_
= new IOBufferWithSize(kMaxSpdyFrameChunkSize
);
234 // The request body buffer is empty at first.
235 request_body_buf_size_
= 0;
238 CHECK(!callback
.is_null());
241 // SendRequest can be called in two cases.
243 // a) A client initiated request. In this case, |response_info_| should be
244 // NULL to start with.
245 // b) A client request which matches a response that the server has already
247 if (push_response_info_
.get()) {
248 *response
= *(push_response_info_
.get());
249 push_response_info_
.reset();
251 DCHECK_EQ(static_cast<HttpResponseInfo
*>(NULL
), response_info_
);
254 response_info_
= response
;
256 // Put the peer's IP address and port into the response.
258 int result
= stream_
->GetPeerAddress(&address
);
261 response_info_
->socket_address
= HostPortPair::FromIPEndPoint(address
);
263 if (stream_
->type() == SPDY_PUSH_STREAM
) {
264 // Pushed streams do not send any data, and should always be
265 // idle. However, we still want to return ERR_IO_PENDING to mimic
266 // non-push behavior. The callback will be called when the
267 // response is received.
268 result
= ERR_IO_PENDING
;
270 scoped_ptr
<SpdyHeaderBlock
> headers(new SpdyHeaderBlock
);
271 CreateSpdyHeadersFromHttpRequest(
272 *request_info_
, request_headers
,
273 headers
.get(), stream_
->GetProtocolVersion(),
275 stream_
->net_log().AddEvent(
276 NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS
,
277 base::Bind(&SpdyHeaderBlockNetLogCallback
, headers
.get()));
279 stream_
->SendRequestHeaders(
281 HasUploadData() ? MORE_DATA_TO_SEND
: NO_MORE_DATA_TO_SEND
);
284 if (result
== ERR_IO_PENDING
) {
285 CHECK(callback_
.is_null());
286 callback_
= callback
;
291 void SpdyHttpStream::Cancel() {
295 DCHECK(!stream_
.get());
299 void SpdyHttpStream::OnRequestHeadersSent() {
300 if (!callback_
.is_null())
303 // TODO(akalin): Do this immediately after sending the request
306 ReadAndSendRequestBodyData();
309 SpdyResponseHeadersStatus
SpdyHttpStream::OnResponseHeadersUpdated(
310 const SpdyHeaderBlock
& response_headers
) {
311 CHECK_EQ(response_headers_status_
, RESPONSE_HEADERS_ARE_INCOMPLETE
);
313 if (!response_info_
) {
314 DCHECK_EQ(stream_
->type(), SPDY_PUSH_STREAM
);
315 push_response_info_
.reset(new HttpResponseInfo
);
316 response_info_
= push_response_info_
.get();
319 if (!SpdyHeadersToHttpResponse(
320 response_headers
, stream_
->GetProtocolVersion(), response_info_
)) {
321 // We do not have complete headers yet.
322 return RESPONSE_HEADERS_ARE_INCOMPLETE
;
325 response_info_
->response_time
= stream_
->response_time();
326 response_headers_status_
= RESPONSE_HEADERS_ARE_COMPLETE
;
327 // Don't store the SSLInfo in the response here, HttpNetworkTransaction
328 // will take care of that part.
330 NextProto protocol_negotiated
= kProtoUnknown
;
331 stream_
->GetSSLInfo(&ssl_info
,
332 &response_info_
->was_npn_negotiated
,
333 &protocol_negotiated
);
334 response_info_
->npn_negotiated_protocol
=
335 SSLClientSocket::NextProtoToString(protocol_negotiated
);
336 response_info_
->request_time
= stream_
->GetRequestTime();
337 response_info_
->connection_info
=
338 HttpResponseInfo::ConnectionInfoFromNextProto(stream_
->GetProtocol());
339 response_info_
->vary_data
340 .Init(*request_info_
, *response_info_
->headers
.get());
342 if (!callback_
.is_null())
345 return RESPONSE_HEADERS_ARE_COMPLETE
;
348 void SpdyHttpStream::OnDataReceived(scoped_ptr
<SpdyBuffer
> buffer
) {
349 CHECK_EQ(response_headers_status_
, RESPONSE_HEADERS_ARE_COMPLETE
);
351 // Note that data may be received for a SpdyStream prior to the user calling
352 // ReadResponseBody(), therefore user_buffer_ may be NULL. This may often
353 // happen for server initiated streams.
354 DCHECK(stream_
.get());
355 DCHECK(!stream_
->IsClosed() || stream_
->type() == SPDY_PUSH_STREAM
);
357 response_body_queue_
.Enqueue(buffer
.Pass());
359 if (user_buffer_
.get()) {
360 // Handing small chunks of data to the caller creates measurable overhead.
361 // We buffer data in short time-spans and send a single read notification.
362 ScheduleBufferedReadCallback();
367 void SpdyHttpStream::OnDataSent() {
368 request_body_buf_size_
= 0;
369 ReadAndSendRequestBodyData();
372 void SpdyHttpStream::OnClose(int status
) {
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();
382 bool invoked_callback
= false;
383 if (status
== net::OK
) {
384 // We need to complete any pending buffered read now.
385 invoked_callback
= DoBufferedReadCallback();
387 if (!invoked_callback
&& !callback_
.is_null())
391 bool SpdyHttpStream::HasUploadData() const {
392 CHECK(request_info_
);
394 request_info_
->upload_data_stream
&&
395 ((request_info_
->upload_data_stream
->size() > 0) ||
396 request_info_
->upload_data_stream
->is_chunked());
399 void SpdyHttpStream::OnStreamCreated(
400 const CompletionCallback
& callback
,
403 stream_
= stream_request_
.ReleaseStream();
404 stream_
->SetDelegate(this);
409 void SpdyHttpStream::ReadAndSendRequestBodyData() {
410 CHECK(HasUploadData());
411 CHECK_EQ(request_body_buf_size_
, 0);
413 if (request_info_
->upload_data_stream
->IsEOF())
416 // Read the data from the request body stream.
417 const int rv
= request_info_
->upload_data_stream
418 ->Read(request_body_buf_
.get(),
419 request_body_buf_
->size(),
420 base::Bind(&SpdyHttpStream::OnRequestBodyReadCompleted
,
421 weak_factory_
.GetWeakPtr()));
423 if (rv
!= ERR_IO_PENDING
) {
424 // ERR_IO_PENDING is the only possible error.
426 OnRequestBodyReadCompleted(rv
);
430 void SpdyHttpStream::OnRequestBodyReadCompleted(int status
) {
432 request_body_buf_size_
= status
;
433 const bool eof
= request_info_
->upload_data_stream
->IsEOF();
435 CHECK_GE(request_body_buf_size_
, 0);
437 CHECK_GT(request_body_buf_size_
, 0);
439 stream_
->SendData(request_body_buf_
.get(),
440 request_body_buf_size_
,
441 eof
? NO_MORE_DATA_TO_SEND
: MORE_DATA_TO_SEND
);
444 void SpdyHttpStream::ScheduleBufferedReadCallback() {
445 // If there is already a scheduled DoBufferedReadCallback, don't issue
446 // another one. Mark that we have received more data and return.
447 if (buffered_read_callback_pending_
) {
448 more_read_data_pending_
= true;
452 more_read_data_pending_
= false;
453 buffered_read_callback_pending_
= true;
454 const base::TimeDelta kBufferTime
= base::TimeDelta::FromMilliseconds(1);
455 base::MessageLoop::current()->PostDelayedTask(
457 base::Bind(base::IgnoreResult(&SpdyHttpStream::DoBufferedReadCallback
),
458 weak_factory_
.GetWeakPtr()),
462 // Checks to see if we should wait for more buffered data before notifying
463 // the caller. Returns true if we should wait, false otherwise.
464 bool SpdyHttpStream::ShouldWaitForMoreBufferedData() const {
465 // If the response is complete, there is no point in waiting.
469 DCHECK_GT(user_buffer_len_
, 0);
470 return response_body_queue_
.GetTotalSize() <
471 static_cast<size_t>(user_buffer_len_
);
474 bool SpdyHttpStream::DoBufferedReadCallback() {
475 buffered_read_callback_pending_
= false;
477 // If the transaction is cancelled or errored out, we don't need to complete
479 if (!stream_
.get() && !stream_closed_
)
483 stream_closed_
? closed_stream_status_
: stream_
->response_status();
484 if (stream_status
!= OK
)
487 // When more_read_data_pending_ is true, it means that more data has
488 // arrived since we started waiting. Wait a little longer and continue
490 if (more_read_data_pending_
&& ShouldWaitForMoreBufferedData()) {
491 ScheduleBufferedReadCallback();
496 if (user_buffer_
.get()) {
497 rv
= ReadResponseBody(user_buffer_
.get(), user_buffer_len_
, callback_
);
498 CHECK_NE(rv
, ERR_IO_PENDING
);
500 user_buffer_len_
= 0;
507 void SpdyHttpStream::DoCallback(int rv
) {
508 CHECK_NE(rv
, ERR_IO_PENDING
);
509 CHECK(!callback_
.is_null());
511 // Since Run may result in being called back, clear user_callback_ in advance.
512 CompletionCallback c
= callback_
;
517 void SpdyHttpStream::GetSSLInfo(SSLInfo
* ssl_info
) {
518 DCHECK(stream_
.get());
520 NextProto protocol_negotiated
= kProtoUnknown
;
521 stream_
->GetSSLInfo(ssl_info
, &using_npn
, &protocol_negotiated
);
524 void SpdyHttpStream::GetSSLCertRequestInfo(
525 SSLCertRequestInfo
* cert_request_info
) {
526 DCHECK(stream_
.get());
527 stream_
->GetSSLCertRequestInfo(cert_request_info
);
530 bool SpdyHttpStream::IsSpdyHttpStream() const {
534 void SpdyHttpStream::Drain(HttpNetworkSession
* session
) {
539 void SpdyHttpStream::SetPriority(RequestPriority priority
) {
540 // TODO(akalin): Plumb this through to |stream_request_| and