WiFiServiceImpl (Windows): Fixed wrong authentication type with WEP-PSK.
[chromium-blink-merge.git] / net / spdy / spdy_http_stream.cc
blobf3da77bc803e419b8c7b0df76128d37652199234
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"
7 #include <algorithm>
8 #include <list>
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/values.h"
17 #include "net/base/host_port_pair.h"
18 #include "net/base/net_util.h"
19 #include "net/base/upload_data_stream.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_info.h"
23 #include "net/log/net_log.h"
24 #include "net/spdy/spdy_header_block.h"
25 #include "net/spdy/spdy_http_utils.h"
26 #include "net/spdy/spdy_protocol.h"
27 #include "net/spdy/spdy_session.h"
29 namespace net {
31 SpdyHttpStream::SpdyHttpStream(const base::WeakPtr<SpdySession>& spdy_session,
32 bool direct)
33 : spdy_session_(spdy_session),
34 is_reused_(spdy_session_->IsReused()),
35 stream_closed_(false),
36 closed_stream_status_(ERR_FAILED),
37 closed_stream_id_(0),
38 closed_stream_received_bytes_(0),
39 request_info_(NULL),
40 response_info_(NULL),
41 response_headers_status_(RESPONSE_HEADERS_ARE_INCOMPLETE),
42 user_buffer_len_(0),
43 request_body_buf_size_(0),
44 buffered_read_callback_pending_(false),
45 more_read_data_pending_(false),
46 direct_(direct),
47 weak_factory_(this) {
48 DCHECK(spdy_session_.get());
51 SpdyHttpStream::~SpdyHttpStream() {
52 if (stream_.get()) {
53 stream_->DetachDelegate();
54 DCHECK(!stream_.get());
58 int SpdyHttpStream::InitializeStream(const HttpRequestInfo* request_info,
59 RequestPriority priority,
60 const BoundNetLog& stream_net_log,
61 const CompletionCallback& callback) {
62 DCHECK(!stream_);
63 if (!spdy_session_)
64 return ERR_CONNECTION_CLOSED;
66 request_info_ = request_info;
67 if (request_info_->method == "GET") {
68 int error = spdy_session_->GetPushStream(request_info_->url, &stream_,
69 stream_net_log);
70 if (error != OK)
71 return error;
73 // |stream_| may be NULL even if OK was returned.
74 if (stream_.get()) {
75 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
76 stream_->SetDelegate(this);
77 return OK;
81 int rv = stream_request_.StartRequest(
82 SPDY_REQUEST_RESPONSE_STREAM, spdy_session_, request_info_->url,
83 priority, stream_net_log,
84 base::Bind(&SpdyHttpStream::OnStreamCreated,
85 weak_factory_.GetWeakPtr(), callback));
87 if (rv == OK) {
88 stream_ = stream_request_.ReleaseStream();
89 stream_->SetDelegate(this);
92 return rv;
95 UploadProgress SpdyHttpStream::GetUploadProgress() const {
96 if (!request_info_ || !HasUploadData())
97 return UploadProgress();
99 return UploadProgress(request_info_->upload_data_stream->position(),
100 request_info_->upload_data_stream->size());
103 int SpdyHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
104 CHECK(!callback.is_null());
105 if (stream_closed_)
106 return closed_stream_status_;
108 CHECK(stream_.get());
110 // Check if we already have the response headers. If so, return synchronously.
111 if (response_headers_status_ == RESPONSE_HEADERS_ARE_COMPLETE) {
112 CHECK(!stream_->IsIdle());
113 return OK;
116 // Still waiting for the response, return IO_PENDING.
117 CHECK(callback_.is_null());
118 callback_ = callback;
119 return ERR_IO_PENDING;
122 int SpdyHttpStream::ReadResponseBody(
123 IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
124 if (stream_.get())
125 CHECK(!stream_->IsIdle());
127 CHECK(buf);
128 CHECK(buf_len);
129 CHECK(!callback.is_null());
131 // If we have data buffered, complete the IO immediately.
132 if (!response_body_queue_.IsEmpty()) {
133 return response_body_queue_.Dequeue(buf->data(), buf_len);
134 } else if (stream_closed_) {
135 return closed_stream_status_;
138 CHECK(callback_.is_null());
139 CHECK(!user_buffer_.get());
140 CHECK_EQ(0, user_buffer_len_);
142 callback_ = callback;
143 user_buffer_ = buf;
144 user_buffer_len_ = buf_len;
145 return ERR_IO_PENDING;
148 void SpdyHttpStream::Close(bool not_reusable) {
149 // Note: the not_reusable flag has no meaning for SPDY streams.
151 Cancel();
152 DCHECK(!stream_.get());
155 HttpStream* SpdyHttpStream::RenewStreamForAuth() {
156 return NULL;
159 bool SpdyHttpStream::IsResponseBodyComplete() const {
160 return stream_closed_;
163 bool SpdyHttpStream::CanFindEndOfResponse() const {
164 return true;
167 bool SpdyHttpStream::IsConnectionReused() const {
168 return is_reused_;
171 void SpdyHttpStream::SetConnectionReused() {
172 // SPDY doesn't need an indicator here.
175 bool SpdyHttpStream::IsConnectionReusable() const {
176 // SPDY streams aren't considered reusable.
177 return false;
180 int64 SpdyHttpStream::GetTotalReceivedBytes() const {
181 if (stream_closed_)
182 return closed_stream_received_bytes_;
184 if (!stream_)
185 return 0;
187 return stream_->raw_received_bytes();
190 bool SpdyHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
191 if (stream_closed_) {
192 if (!closed_stream_has_load_timing_info_)
193 return false;
194 *load_timing_info = closed_stream_load_timing_info_;
195 return true;
198 // If |stream_| has yet to be created, or does not yet have an ID, fail.
199 // The reused flag can only be correctly set once a stream has an ID. Streams
200 // get their IDs once the request has been successfully sent, so this does not
201 // behave that differently from other stream types.
202 if (!stream_ || stream_->stream_id() == 0)
203 return false;
205 return stream_->GetLoadTimingInfo(load_timing_info);
208 int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
209 HttpResponseInfo* response,
210 const CompletionCallback& callback) {
211 if (stream_closed_) {
212 return closed_stream_status_;
215 base::Time request_time = base::Time::Now();
216 CHECK(stream_.get());
218 stream_->SetRequestTime(request_time);
219 // This should only get called in the case of a request occurring
220 // during server push that has already begun but hasn't finished,
221 // so we set the response's request time to be the actual one
222 if (response_info_)
223 response_info_->request_time = request_time;
225 CHECK(!request_body_buf_.get());
226 if (HasUploadData()) {
227 // Use kMaxSpdyFrameChunkSize as the buffer size, since the request
228 // body data is written with this size at a time.
229 request_body_buf_ = new IOBufferWithSize(kMaxSpdyFrameChunkSize);
230 // The request body buffer is empty at first.
231 request_body_buf_size_ = 0;
234 CHECK(!callback.is_null());
235 CHECK(response);
237 // SendRequest can be called in two cases.
239 // a) A client initiated request. In this case, |response_info_| should be
240 // NULL to start with.
241 // b) A client request which matches a response that the server has already
242 // pushed.
243 if (push_response_info_.get()) {
244 *response = *(push_response_info_.get());
245 push_response_info_.reset();
246 } else {
247 DCHECK_EQ(static_cast<HttpResponseInfo*>(NULL), response_info_);
250 response_info_ = response;
252 // Put the peer's IP address and port into the response.
253 IPEndPoint address;
254 int result = stream_->GetPeerAddress(&address);
255 if (result != OK)
256 return result;
257 response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
259 if (stream_->type() == SPDY_PUSH_STREAM) {
260 // Pushed streams do not send any data, and should always be
261 // idle. However, we still want to return ERR_IO_PENDING to mimic
262 // non-push behavior. The callback will be called when the
263 // response is received.
264 result = ERR_IO_PENDING;
265 } else {
266 scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
267 CreateSpdyHeadersFromHttpRequest(
268 *request_info_, request_headers,
269 stream_->GetProtocolVersion(), direct_,
270 headers.get());
271 stream_->net_log().AddEvent(
272 NetLog::TYPE_HTTP_TRANSACTION_HTTP2_SEND_REQUEST_HEADERS,
273 base::Bind(&SpdyHeaderBlockNetLogCallback, headers.get()));
274 result =
275 stream_->SendRequestHeaders(
276 headers.Pass(),
277 HasUploadData() ? MORE_DATA_TO_SEND : NO_MORE_DATA_TO_SEND);
280 if (result == ERR_IO_PENDING) {
281 CHECK(callback_.is_null());
282 callback_ = callback;
284 return result;
287 void SpdyHttpStream::Cancel() {
288 callback_.Reset();
289 if (stream_.get()) {
290 stream_->Cancel();
291 DCHECK(!stream_.get());
295 void SpdyHttpStream::OnRequestHeadersSent() {
296 if (!callback_.is_null())
297 DoCallback(OK);
299 // TODO(akalin): Do this immediately after sending the request
300 // headers.
301 if (HasUploadData())
302 ReadAndSendRequestBodyData();
305 SpdyResponseHeadersStatus SpdyHttpStream::OnResponseHeadersUpdated(
306 const SpdyHeaderBlock& response_headers) {
307 CHECK_EQ(response_headers_status_, RESPONSE_HEADERS_ARE_INCOMPLETE);
309 if (!response_info_) {
310 DCHECK_EQ(stream_->type(), SPDY_PUSH_STREAM);
311 push_response_info_.reset(new HttpResponseInfo);
312 response_info_ = push_response_info_.get();
315 if (!SpdyHeadersToHttpResponse(
316 response_headers, stream_->GetProtocolVersion(), response_info_)) {
317 // We do not have complete headers yet.
318 return RESPONSE_HEADERS_ARE_INCOMPLETE;
321 response_info_->response_time = stream_->response_time();
322 response_headers_status_ = RESPONSE_HEADERS_ARE_COMPLETE;
323 // Don't store the SSLInfo in the response here, HttpNetworkTransaction
324 // will take care of that part.
325 SSLInfo ssl_info;
326 NextProto protocol_negotiated = kProtoUnknown;
327 stream_->GetSSLInfo(&ssl_info,
328 &response_info_->was_npn_negotiated,
329 &protocol_negotiated);
330 response_info_->npn_negotiated_protocol =
331 SSLClientSocket::NextProtoToString(protocol_negotiated);
332 response_info_->request_time = stream_->GetRequestTime();
333 response_info_->connection_info =
334 HttpResponseInfo::ConnectionInfoFromNextProto(stream_->GetProtocol());
335 response_info_->vary_data
336 .Init(*request_info_, *response_info_->headers.get());
338 if (!callback_.is_null())
339 DoCallback(OK);
341 return RESPONSE_HEADERS_ARE_COMPLETE;
344 void SpdyHttpStream::OnDataReceived(scoped_ptr<SpdyBuffer> buffer) {
345 CHECK_EQ(response_headers_status_, RESPONSE_HEADERS_ARE_COMPLETE);
347 // Note that data may be received for a SpdyStream prior to the user calling
348 // ReadResponseBody(), therefore user_buffer_ may be NULL. This may often
349 // happen for server initiated streams.
350 DCHECK(stream_.get());
351 DCHECK(!stream_->IsClosed() || stream_->type() == SPDY_PUSH_STREAM);
352 if (buffer) {
353 response_body_queue_.Enqueue(buffer.Pass());
355 if (user_buffer_.get()) {
356 // Handing small chunks of data to the caller creates measurable overhead.
357 // We buffer data in short time-spans and send a single read notification.
358 ScheduleBufferedReadCallback();
363 void SpdyHttpStream::OnDataSent() {
364 request_body_buf_size_ = 0;
365 ReadAndSendRequestBodyData();
368 void SpdyHttpStream::OnClose(int status) {
369 if (stream_.get()) {
370 stream_closed_ = true;
371 closed_stream_status_ = status;
372 closed_stream_id_ = stream_->stream_id();
373 closed_stream_has_load_timing_info_ =
374 stream_->GetLoadTimingInfo(&closed_stream_load_timing_info_);
375 closed_stream_received_bytes_ = stream_->raw_received_bytes();
377 stream_.reset();
378 bool invoked_callback = false;
379 if (status == OK) {
380 // We need to complete any pending buffered read now.
381 invoked_callback = DoBufferedReadCallback();
383 if (!invoked_callback && !callback_.is_null())
384 DoCallback(status);
387 bool SpdyHttpStream::HasUploadData() const {
388 CHECK(request_info_);
389 return
390 request_info_->upload_data_stream &&
391 ((request_info_->upload_data_stream->size() > 0) ||
392 request_info_->upload_data_stream->is_chunked());
395 void SpdyHttpStream::OnStreamCreated(
396 const CompletionCallback& callback,
397 int rv) {
398 if (rv == OK) {
399 stream_ = stream_request_.ReleaseStream();
400 stream_->SetDelegate(this);
402 callback.Run(rv);
405 void SpdyHttpStream::ReadAndSendRequestBodyData() {
406 CHECK(HasUploadData());
407 CHECK_EQ(request_body_buf_size_, 0);
409 if (request_info_->upload_data_stream->IsEOF())
410 return;
412 // Read the data from the request body stream.
413 const int rv = request_info_->upload_data_stream
414 ->Read(request_body_buf_.get(),
415 request_body_buf_->size(),
416 base::Bind(&SpdyHttpStream::OnRequestBodyReadCompleted,
417 weak_factory_.GetWeakPtr()));
419 if (rv != ERR_IO_PENDING) {
420 // ERR_IO_PENDING is the only possible error.
421 CHECK_GE(rv, 0);
422 OnRequestBodyReadCompleted(rv);
426 void SpdyHttpStream::OnRequestBodyReadCompleted(int status) {
427 CHECK_GE(status, 0);
428 request_body_buf_size_ = status;
429 const bool eof = request_info_->upload_data_stream->IsEOF();
430 // Only the final fame may have a length of 0.
431 if (eof) {
432 CHECK_GE(request_body_buf_size_, 0);
433 } else {
434 CHECK_GT(request_body_buf_size_, 0);
436 stream_->SendData(request_body_buf_.get(),
437 request_body_buf_size_,
438 eof ? NO_MORE_DATA_TO_SEND : MORE_DATA_TO_SEND);
441 void SpdyHttpStream::ScheduleBufferedReadCallback() {
442 // If there is already a scheduled DoBufferedReadCallback, don't issue
443 // another one. Mark that we have received more data and return.
444 if (buffered_read_callback_pending_) {
445 more_read_data_pending_ = true;
446 return;
449 more_read_data_pending_ = false;
450 buffered_read_callback_pending_ = true;
451 const base::TimeDelta kBufferTime = base::TimeDelta::FromMilliseconds(1);
452 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
453 FROM_HERE,
454 base::Bind(base::IgnoreResult(&SpdyHttpStream::DoBufferedReadCallback),
455 weak_factory_.GetWeakPtr()),
456 kBufferTime);
459 // Checks to see if we should wait for more buffered data before notifying
460 // the caller. Returns true if we should wait, false otherwise.
461 bool SpdyHttpStream::ShouldWaitForMoreBufferedData() const {
462 // If the response is complete, there is no point in waiting.
463 if (stream_closed_)
464 return false;
466 DCHECK_GT(user_buffer_len_, 0);
467 return response_body_queue_.GetTotalSize() <
468 static_cast<size_t>(user_buffer_len_);
471 bool SpdyHttpStream::DoBufferedReadCallback() {
472 buffered_read_callback_pending_ = false;
474 // If the transaction is cancelled or errored out, we don't need to complete
475 // the read.
476 if (!stream_.get() && !stream_closed_)
477 return false;
479 int stream_status =
480 stream_closed_ ? closed_stream_status_ : stream_->response_status();
481 if (stream_status != OK)
482 return false;
484 // When more_read_data_pending_ is true, it means that more data has
485 // arrived since we started waiting. Wait a little longer and continue
486 // to buffer.
487 if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
488 ScheduleBufferedReadCallback();
489 return false;
492 int rv = 0;
493 if (user_buffer_.get()) {
494 rv = ReadResponseBody(user_buffer_.get(), user_buffer_len_, callback_);
495 CHECK_NE(rv, ERR_IO_PENDING);
496 user_buffer_ = NULL;
497 user_buffer_len_ = 0;
498 DoCallback(rv);
499 return true;
501 return false;
504 void SpdyHttpStream::DoCallback(int rv) {
505 CHECK_NE(rv, ERR_IO_PENDING);
506 CHECK(!callback_.is_null());
508 // Since Run may result in being called back, clear user_callback_ in advance.
509 CompletionCallback c = callback_;
510 callback_.Reset();
511 c.Run(rv);
514 void SpdyHttpStream::GetSSLInfo(SSLInfo* ssl_info) {
515 DCHECK(stream_.get());
516 bool using_npn;
517 NextProto protocol_negotiated = kProtoUnknown;
518 stream_->GetSSLInfo(ssl_info, &using_npn, &protocol_negotiated);
521 void SpdyHttpStream::GetSSLCertRequestInfo(
522 SSLCertRequestInfo* cert_request_info) {
523 // A SPDY stream cannot request client certificates. Client authentication may
524 // only occur during the initial SSL handshake.
525 NOTREACHED();
528 bool SpdyHttpStream::IsSpdyHttpStream() const {
529 return true;
532 void SpdyHttpStream::Drain(HttpNetworkSession* session) {
533 Close(false);
534 delete this;
537 void SpdyHttpStream::SetPriority(RequestPriority priority) {
538 // TODO(akalin): Plumb this through to |stream_request_| and
539 // |stream_|.
542 } // namespace net