Index: net/spdy/spdy_proxy_client_socket.cc |
=================================================================== |
--- net/spdy/spdy_proxy_client_socket.cc (revision 60753) |
+++ net/spdy/spdy_proxy_client_socket.cc (working copy) |
@@ -1,437 +0,0 @@ |
-// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/spdy/spdy_proxy_client_socket.h" |
- |
-#include <algorithm> // min |
- |
-#include "base/logging.h" |
-#include "base/string_util.h" |
-#include "googleurl/src/gurl.h" |
-#include "net/base/auth.h" |
-#include "net/base/io_buffer.h" |
-#include "net/base/net_util.h" |
-#include "net/http/http_auth_cache.h" |
-#include "net/http/http_auth_handler_factory.h" |
-#include "net/http/http_net_log_params.h" |
-#include "net/http/http_proxy_utils.h" |
-#include "net/spdy/spdy_http_utils.h" |
- |
-namespace net { |
- |
-SpdyProxyClientSocket::SpdyProxyClientSocket( |
- SpdyStream* spdy_stream, |
- const std::string& user_agent, |
- const HostPortPair& endpoint, |
- const GURL& url, |
- const HostPortPair& proxy_server, |
- HttpAuthCache* auth_cache, |
- HttpAuthHandlerFactory* auth_handler_factory) |
- : ALLOW_THIS_IN_INITIALIZER_LIST( |
- io_callback_(this, &SpdyProxyClientSocket::OnIOComplete)), |
- next_state_(STATE_NONE), |
- spdy_stream_(spdy_stream), |
- read_callback_(NULL), |
- write_callback_(NULL), |
- endpoint_(endpoint), |
- auth_( |
- new HttpAuthController(HttpAuth::AUTH_PROXY, |
- GURL("http://" + proxy_server.ToString()), |
- auth_cache, |
- auth_handler_factory)), |
- user_buffer_(NULL), |
- write_buffer_len_(0), |
- write_bytes_outstanding_(0), |
- eof_has_been_read_(false), |
- net_log_(spdy_stream->net_log()) { |
- request_.method = "CONNECT"; |
- request_.url = url; |
- if (!user_agent.empty()) |
- request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, |
- user_agent); |
- spdy_stream_->SetDelegate(this); |
- was_ever_used_ = spdy_stream_->WasEverUsed(); |
-} |
- |
-SpdyProxyClientSocket::~SpdyProxyClientSocket() { |
- Disconnect(); |
-} |
- |
-// Sends a SYN_STREAM frame to the proxy with a CONNECT request |
-// for the specified endpoint. Waits for the server to send back |
-// a SYN_REPLY frame. OK will be returned if the status is 200. |
-// ERR_TUNNEL_CONNECTION_FAILED will be returned for any other status. |
-// In any of these cases, Read() may be called to retrieve the HTTP |
-// response body. Any other return values should be considered fatal. |
-// TODO(rch): handle 407 proxy auth requested correctly, perhaps |
-// by creating a new stream for the subsequent request. |
-// TODO(rch): create a more appropriate error code to disambiguate |
-// the HTTPS Proxy tunnel failure from an HTTP Proxy tunnel failure. |
-int SpdyProxyClientSocket::Connect(CompletionCallback* callback) { |
- DCHECK(!read_callback_); |
- if (next_state_ == STATE_DONE) |
- return OK; |
- |
- DCHECK_EQ(STATE_NONE, next_state_); |
- next_state_ = STATE_GENERATE_AUTH_TOKEN; |
- |
- int rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) |
- read_callback_ = callback; |
- return rv; |
-} |
- |
-void SpdyProxyClientSocket::Disconnect() { |
- next_state_ = STATE_NONE; |
- if (spdy_stream_) |
- // This will cause OnClose to be invoked, which takes care of |
- // cleaning up all the internal state. |
- spdy_stream_->Cancel(); |
-} |
- |
-bool SpdyProxyClientSocket::IsConnected() const { |
- return next_state_ == STATE_DONE && spdy_stream_ != NULL && |
- !spdy_stream_->closed(); |
-} |
- |
-bool SpdyProxyClientSocket::IsConnectedAndIdle() const { |
- return IsConnected() && !spdy_stream_->is_idle(); |
-} |
- |
-void SpdyProxyClientSocket::SetSubresourceSpeculation() { |
- // TODO(rch): what should this implementation be? |
-} |
- |
-void SpdyProxyClientSocket::SetOmniboxSpeculation() { |
- // TODO(rch): what should this implementation be? |
-} |
- |
-bool SpdyProxyClientSocket::WasEverUsed() const { |
- return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed()); |
-} |
- |
-int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, |
- CompletionCallback* callback) { |
- DCHECK(!read_callback_); |
- DCHECK(!user_buffer_); |
- |
- if (!spdy_stream_) { |
- if (eof_has_been_read_) |
- return ERR_CONNECTION_CLOSED; |
- eof_has_been_read_ = true; |
- return 0; |
- } |
- |
- DCHECK(next_state_ == STATE_DONE); |
- DCHECK(buf); |
- user_buffer_ = new DrainableIOBuffer(buf, buf_len); |
- int result = PopulateUserReadBuffer(); |
- if (result == 0) { |
- DCHECK(callback); |
- read_callback_ = callback; |
- return ERR_IO_PENDING; |
- } |
- user_buffer_ = NULL; |
- return result; |
-} |
- |
-int SpdyProxyClientSocket::PopulateUserReadBuffer() { |
- if (!user_buffer_) |
- return ERR_IO_PENDING; |
- |
- while (!read_buffer_.empty() && user_buffer_->BytesRemaining() > 0) { |
- scoped_refptr<DrainableIOBuffer> data = read_buffer_.front(); |
- const int bytes_to_copy = std::min(user_buffer_->BytesRemaining(), |
- data->size()); |
- memcpy(user_buffer_->data(), data->data(), bytes_to_copy); |
- user_buffer_->DidConsume(bytes_to_copy); |
- if (bytes_to_copy == data->size()) { |
- // Consumed all data from this buffer |
- read_buffer_.pop_front(); |
- } else { |
- data->DidConsume(bytes_to_copy); |
- } |
- } |
- |
- return user_buffer_->BytesConsumed(); |
-} |
- |
-int SpdyProxyClientSocket::Write(IOBuffer* buf, int buf_len, |
- CompletionCallback* callback) { |
- DCHECK(!write_callback_); |
- if (!spdy_stream_) |
- return ERR_CONNECTION_CLOSED; |
- |
- write_bytes_outstanding_= buf_len; |
- if (buf_len <= kMaxSpdyFrameChunkSize) { |
- int rv = spdy_stream_->WriteStreamData(buf, buf_len, spdy::DATA_FLAG_NONE); |
- if (rv == ERR_IO_PENDING) { |
- write_callback_ = callback; |
- write_buffer_len_ = buf_len; |
- } |
- return rv; |
- } |
- |
- // Since a SPDY Data frame can only include kMaxSpdyFrameChunkSize bytes |
- // we need to send multiple data frames |
- for (int i = 0; i < buf_len; i += kMaxSpdyFrameChunkSize) { |
- int len = std::min(kMaxSpdyFrameChunkSize, buf_len - i); |
- scoped_refptr<DrainableIOBuffer> iobuf(new DrainableIOBuffer(buf, i + len)); |
- iobuf->SetOffset(i); |
- int rv = spdy_stream_->WriteStreamData(iobuf, len, spdy::DATA_FLAG_NONE); |
- if (rv > 0) { |
- write_bytes_outstanding_ -= rv; |
- } else if (rv != ERR_IO_PENDING) { |
- return rv; |
- } |
- } |
- if (write_bytes_outstanding_ > 0) { |
- write_callback_ = callback; |
- write_buffer_len_ = buf_len; |
- return ERR_IO_PENDING; |
- } else { |
- return buf_len; |
- } |
-} |
- |
-bool SpdyProxyClientSocket::SetReceiveBufferSize(int32 size) { |
- // Since this ClientSocket sits on top of a shared SpdySession, it |
- // is not safe for callers to set change this underlying socket. |
- return false; |
-} |
- |
-bool SpdyProxyClientSocket::SetSendBufferSize(int32 size) { |
- // Since this ClientSocket sits on top of a shared SpdySession, it |
- // is not safe for callers to set change this underlying socket. |
- return false; |
-} |
- |
-int SpdyProxyClientSocket::GetPeerAddress(AddressList* address) const { |
- if (!IsConnected()) |
- return ERR_UNEXPECTED; |
- return spdy_stream_->GetPeerAddress(address); |
-} |
- |
-void SpdyProxyClientSocket::OnIOComplete(int result) { |
- DCHECK_NE(STATE_NONE, next_state_); |
- int rv = DoLoop(result); |
- if (rv != ERR_IO_PENDING) { |
- CompletionCallback* c = read_callback_; |
- read_callback_ = NULL; |
- c->Run(rv); |
- } |
-} |
- |
-int SpdyProxyClientSocket::DoLoop(int last_io_result) { |
- DCHECK_NE(next_state_, STATE_NONE); |
- int rv = last_io_result; |
- do { |
- State state = next_state_; |
- next_state_ = STATE_NONE; |
- switch (state) { |
- case STATE_GENERATE_AUTH_TOKEN: |
- DCHECK_EQ(OK, rv); |
- rv = DoGenerateAuthToken(); |
- break; |
- case STATE_GENERATE_AUTH_TOKEN_COMPLETE: |
- rv = DoGenerateAuthTokenComplete(rv); |
- break; |
- case STATE_SEND_REQUEST: |
- DCHECK_EQ(OK, rv); |
- net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, |
- NULL); |
- rv = DoSendRequest(); |
- break; |
- case STATE_SEND_REQUEST_COMPLETE: |
- rv = DoSendRequestComplete(rv); |
- net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, |
- NULL); |
- break; |
- case STATE_READ_REPLY_COMPLETE: |
- rv = DoReadReplyComplete(rv); |
- net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, |
- NULL); |
- break; |
- default: |
- NOTREACHED() << "bad state"; |
- rv = ERR_UNEXPECTED; |
- break; |
- } |
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE && |
- next_state_ != STATE_DONE); |
- return rv; |
-} |
- |
-int SpdyProxyClientSocket::DoGenerateAuthToken() { |
- next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE; |
- return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_); |
-} |
- |
-int SpdyProxyClientSocket::DoGenerateAuthTokenComplete(int result) { |
- DCHECK_NE(ERR_IO_PENDING, result); |
- if (result == OK) |
- next_state_ = STATE_SEND_REQUEST; |
- return result; |
-} |
- |
-int SpdyProxyClientSocket::DoSendRequest() { |
- next_state_ = STATE_SEND_REQUEST_COMPLETE; |
- |
- // Add Proxy-Authentication header if necessary. |
- HttpRequestHeaders authorization_headers; |
- if (auth_->HaveAuth()) { |
- auth_->AddAuthorizationHeader(&authorization_headers); |
- } |
- |
- std::string request_line; |
- HttpRequestHeaders request_headers; |
- BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line, |
- &request_headers); |
- if (net_log_.IsLoggingAll()) { |
- net_log_.AddEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, |
- new NetLogHttpRequestParameter( |
- request_line, request_headers)); |
- } |
- |
- request_.extra_headers.MergeFrom(request_headers); |
- linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock()); |
- CreateSpdyHeadersFromHttpRequest(request_, headers.get(), true); |
- // Reset the URL to be the endpoint of the connection |
- (*headers)["url"] = endpoint_.ToString(); |
- headers->erase("scheme"); |
- spdy_stream_->set_spdy_headers(headers); |
- |
- return spdy_stream_->SendRequest(true); |
-} |
- |
-int SpdyProxyClientSocket::DoSendRequestComplete(int result) { |
- if (result < 0) |
- return result; |
- |
- // Wait for SYN_REPLY frame from the server |
- next_state_ = STATE_READ_REPLY_COMPLETE; |
- return ERR_IO_PENDING; |
-} |
- |
-int SpdyProxyClientSocket::DoReadReplyComplete(int result) { |
- // We enter this method directly from DoSendRequestComplete, since |
- // we are notified by a callback when the SYN_REPLY frame arrives |
- |
- if (result < 0) |
- return result; |
- |
- next_state_ = STATE_DONE; |
- // Require the "HTTP/1.x" status line for SSL CONNECT. |
- if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- |
- if (net_log_.IsLoggingAll()) { |
- net_log_.AddEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, |
- new NetLogHttpResponseParameter(response_.headers)); |
- } |
- |
- if (response_.headers->response_code() == 200) |
- return OK; |
- else |
- return ERR_TUNNEL_CONNECTION_FAILED; |
-} |
- |
-// SpdyStream::Delegate methods: |
-// Called when SYN frame has been sent. |
-// Returns true if no more data to be sent after SYN frame. |
-bool SpdyProxyClientSocket::OnSendHeadersComplete(int status) { |
- DCHECK_EQ(next_state_, STATE_SEND_REQUEST_COMPLETE); |
- |
- OnIOComplete(status); |
- |
- // We return true here so that we send |spdy_stream_| into |
- // STATE_OPEN (ala WebSockets). |
- return true; |
-} |
- |
-int SpdyProxyClientSocket::OnSendBody() { |
- // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets) |
- // OnSendBody() should never be called. |
- NOTREACHED(); |
- return ERR_UNEXPECTED; |
-} |
- |
-bool SpdyProxyClientSocket::OnSendBodyComplete(int status) { |
- // Because we use |spdy_stream_| via STATE_OPEN (ala WebSockets) |
- // OnSendBodyComplete() should never be called. |
- NOTREACHED(); |
- return false; |
-} |
- |
-int SpdyProxyClientSocket::OnResponseReceived( |
- const spdy::SpdyHeaderBlock& response, |
- base::Time response_time, |
- int status) { |
- // Save the response |
- SpdyHeadersToHttpResponse(response, &response_); |
- |
- DCHECK_EQ(next_state_, STATE_READ_REPLY_COMPLETE); |
- |
- OnIOComplete(status); |
- |
- return OK; |
-} |
- |
-// Called when data is received. |
-void SpdyProxyClientSocket::OnDataReceived(const char* data, int length) { |
- if (length > 0) { |
- // Save the received data. |
- scoped_refptr<IOBuffer> io_buffer = new IOBuffer(length); |
- memcpy(io_buffer->data(), data, length); |
- read_buffer_.push_back(new DrainableIOBuffer(io_buffer, length)); |
- } |
- |
- if (read_callback_) { |
- int rv = PopulateUserReadBuffer(); |
- CompletionCallback* c = read_callback_; |
- read_callback_ = NULL; |
- user_buffer_ = NULL; |
- c->Run(rv); |
- } |
-} |
- |
-void SpdyProxyClientSocket::OnDataSent(int length) { |
- DCHECK(write_callback_); |
- |
- write_bytes_outstanding_ -= length; |
- |
- DCHECK_GE(write_bytes_outstanding_, 0); |
- |
- if (write_bytes_outstanding_ == 0) { |
- int rv = write_buffer_len_; |
- write_buffer_len_ = 0; |
- write_bytes_outstanding_ = 0; |
- CompletionCallback* c = write_callback_; |
- write_callback_ = NULL; |
- c->Run(rv); |
- } |
-} |
- |
-void SpdyProxyClientSocket::OnClose(int status) { |
- DCHECK(spdy_stream_); |
- // If we're in the middle of connecting, we need to make sure |
- // we invoke the connect callback. |
- CompletionCallback* connect_callback = NULL; |
- if (next_state_ != STATE_NONE && next_state_ != STATE_DONE) { |
- DCHECK(read_callback_); |
- connect_callback = read_callback_; |
- } |
- was_ever_used_ = spdy_stream_->WasEverUsed(); |
- spdy_stream_ = NULL; |
- read_callback_ = NULL; |
- write_callback_ = NULL; |
- user_buffer_ = NULL; |
- read_buffer_.empty(); |
- if (connect_callback) |
- connect_callback->Run(status); |
-} |
- |
-} // namespace net |