Index: net/socket_stream/socket_stream.cc |
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7308b5380deef689091ded69b84c3429417dc539 |
--- /dev/null |
+++ b/net/socket_stream/socket_stream.cc |
@@ -0,0 +1,623 @@ |
+// Copyright (c) 2009 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. |
+// |
+// TODO(ukai): code is similar with http_network_transaction.cc. We should |
+// think about ways to share code, if possible. |
+ |
+#include "net/socket_stream/socket_stream.h" |
+ |
+#include <string> |
+ |
+#include "base/compiler_specific.h" |
+#include "base/logging.h" |
+#include "base/message_loop.h" |
+#include "base/string_util.h" |
+#include "net/base/host_resolver.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "net/base/net_util.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_util.h" |
+#include "net/socket/client_socket_factory.h" |
+#include "net/socket/ssl_client_socket.h" |
+#include "net/socket/socks5_client_socket.h" |
+#include "net/socket/socks_client_socket.h" |
+#include "net/socket/tcp_client_socket.h" |
+#include "net/url_request/url_request.h" |
+ |
+static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes. |
+static const int kReadBufferSize = 4096; |
+ |
+namespace net { |
+ |
+void SocketStream::ResponseHeaders::Realloc(size_t new_size) { |
+ headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size))); |
+} |
+ |
+SocketStream::SocketStream(const GURL& url, Delegate* delegate) |
+ : url_(url), |
+ delegate_(delegate), |
+ max_pending_send_allowed_(kMaxPendingSendAllowed), |
+ next_state_(STATE_NONE), |
+ host_resolver_(CreateSystemHostResolver()), |
eroman
2009/11/09 22:22:11
The HostResolver should be passed in as a dependen
eroman
2009/11/09 22:26:28
Also the HostResolver created in the chrome code i
|
+ factory_(ClientSocketFactory::GetDefaultFactory()), |
+ proxy_mode_(kDirectConnection), |
+ pac_request_(NULL), |
+ ALLOW_THIS_IN_INITIALIZER_LIST( |
+ io_callback_(this, &SocketStream::OnIOCompleted)), |
+ ALLOW_THIS_IN_INITIALIZER_LIST( |
+ read_callback_(this, &SocketStream::OnReadCompleted)), |
+ ALLOW_THIS_IN_INITIALIZER_LIST( |
+ write_callback_(this, &SocketStream::OnWriteCompleted)), |
+ read_buf_(NULL), |
+ write_buf_(NULL), |
+ current_write_buf_(NULL), |
+ write_buf_offset_(0), |
+ write_buf_size_(0) { |
+ DCHECK(MessageLoop::current()) << |
+ "The current MessageLoop must exist"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << |
+ "The current MessageLoop must be TYPE_IO"; |
+ DCHECK(delegate_); |
+} |
+ |
+SocketStream::~SocketStream() { |
+ DCHECK(!delegate_); |
+} |
+ |
+SocketStream::UserData* SocketStream::GetUserData( |
+ const void* key) const { |
+ UserDataMap::const_iterator found = user_data_.find(key); |
+ if (found != user_data_.end()) |
+ return found->second.get(); |
+ return NULL; |
+} |
+ |
+void SocketStream::SetUserData(const void* key, UserData* data) { |
+ user_data_[key] = linked_ptr<UserData>(data); |
+} |
+ |
+void SocketStream::set_context(URLRequestContext* context) { |
+ context_ = context; |
+} |
+ |
+void SocketStream::Connect() { |
+ DCHECK(MessageLoop::current()) << |
+ "The current MessageLoop must exist"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << |
+ "The current MessageLoop must be TYPE_IO"; |
+ ssl_config_service()->GetSSLConfig(&ssl_config_); |
+ |
+ AddRef(); // Released in Finish() |
+ // Open a connection asynchronously, so that delegate won't be called |
+ // back before returning Connect(). |
+ next_state_ = STATE_RESOLVE_PROXY; |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &SocketStream::DoLoop, OK)); |
+} |
+ |
+bool SocketStream::SendData(const char* data, int len) { |
+ DCHECK(MessageLoop::current()) << |
+ "The current MessageLoop must exist"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << |
+ "The current MessageLoop must be TYPE_IO"; |
+ if (!socket_.get() || !socket_->IsConnected()) |
+ return false; |
+ if (write_buf_) { |
+ int current_amount_send = write_buf_size_ - write_buf_offset_; |
+ for (PendingDataQueue::const_iterator iter = pending_write_bufs_.begin(); |
+ iter != pending_write_bufs_.end(); |
+ ++iter) |
+ current_amount_send += (*iter)->size(); |
+ |
+ current_amount_send += len; |
+ if (current_amount_send > max_pending_send_allowed_) |
+ return false; |
+ |
+ pending_write_bufs_.push_back(new IOBufferWithSize(len)); |
+ memcpy(pending_write_bufs_.back()->data(), data, len); |
+ return true; |
+ } |
+ DCHECK(!current_write_buf_); |
+ write_buf_ = new IOBuffer(len); |
+ memcpy(write_buf_->data(), data, len); |
+ write_buf_size_ = len; |
+ write_buf_offset_ = 0; |
+ // Send pending data asynchronously, so that delegate won't be called |
+ // back before returning SendData(). |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &SocketStream::DoLoop, OK)); |
+ return true; |
+} |
+ |
+void SocketStream::Close() { |
+ DCHECK(MessageLoop::current()) << |
+ "The current MessageLoop must exist"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << |
+ "The current MessageLoop must be TYPE_IO"; |
+ if (!socket_.get()) |
+ return; |
+ if (socket_->IsConnected()) |
+ socket_->Disconnect(); |
+ // Close asynchronously, so that delegate won't be called |
+ // back before returning Close(). |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ NewRunnableMethod(this, &SocketStream::DoLoop, OK)); |
+} |
+ |
+void SocketStream::DetachDelegate() { |
+ if (!delegate_) |
+ return; |
+ delegate_ = NULL; |
+ Close(); |
+} |
+ |
+void SocketStream::Finish() { |
+ DCHECK(MessageLoop::current()) << |
+ "The current MessageLoop must exist"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) << |
+ "The current MessageLoop must be TYPE_IO"; |
+ Delegate* delegate = delegate_; |
+ delegate_ = NULL; |
+ if (delegate) { |
+ delegate->OnClose(this); |
+ Release(); |
+ } |
+} |
+ |
+void SocketStream::SetHostResolver(HostResolver* host_resolver) { |
+ DCHECK(host_resolver); |
+ host_resolver_ = host_resolver; |
+} |
+ |
+void SocketStream::SetClientSocketFactory( |
+ ClientSocketFactory* factory) { |
+ DCHECK(factory); |
+ factory_ = factory; |
+} |
+ |
+void SocketStream::DidEstablishConnection() { |
+ if (!socket_.get() || !socket_->IsConnected()) { |
+ Finish(); |
+ return; |
+ } |
+ next_state_ = STATE_READ_WRITE; |
+ |
+ if (delegate_) |
+ delegate_->OnConnected(this, max_pending_send_allowed_); |
+ |
+ return; |
+} |
+ |
+void SocketStream::DidReceiveData(int result) { |
+ DCHECK(read_buf_); |
+ DCHECK(result > 0); |
+ if (!delegate_) |
+ return; |
+ // Notify recevied data to delegate. |
+ delegate_->OnReceivedData(this, read_buf_->data(), result); |
+ read_buf_ = NULL; |
+} |
+ |
+void SocketStream::DidSendData(int result) { |
+ current_write_buf_ = NULL; |
+ DCHECK(result > 0); |
+ if (!delegate_) |
+ return; |
+ |
+ delegate_->OnSentData(this, result); |
+ int remaining_size = write_buf_size_ - write_buf_offset_ - result; |
+ if (remaining_size == 0) { |
+ if (!pending_write_bufs_.empty()) { |
+ write_buf_size_ = pending_write_bufs_.front()->size(); |
+ write_buf_ = pending_write_bufs_.front(); |
+ pending_write_bufs_.pop_front(); |
+ } else { |
+ write_buf_size_ = 0; |
+ write_buf_ = NULL; |
+ } |
+ write_buf_offset_ = 0; |
+ } else { |
+ write_buf_offset_ += result; |
+ } |
+} |
+ |
+void SocketStream::OnIOCompleted(int result) { |
+ DoLoop(result); |
+ // TODO(ukai): notify error. |
+} |
+ |
+void SocketStream::OnReadCompleted(int result) { |
+ // TODO(ukai): notify error. |
+ if (result >= 0 && read_buf_) { |
+ DidReceiveData(result); |
+ result = OK; |
+ } |
+ DoLoop(result); |
+} |
+ |
+void SocketStream::OnWriteCompleted(int result) { |
+ // TODO(ukai): notify error. |
+ if (result >= 0 && write_buf_) { |
+ DidSendData(result); |
+ result = OK; |
+ } |
+ DoLoop(result); |
+} |
+ |
+int SocketStream::DoLoop(int result) { |
+ if (next_state_ == STATE_NONE) { |
+ Finish(); |
+ return ERR_CONNECTION_CLOSED; |
+ } |
+ |
+ do { |
+ State state = next_state_; |
+ next_state_ = STATE_NONE; |
+ switch (state) { |
+ case STATE_RESOLVE_PROXY: |
+ DCHECK_EQ(OK, result); |
+ result = DoResolveProxy(); |
+ break; |
+ case STATE_RESOLVE_PROXY_COMPLETE: |
+ result = DoResolveProxyComplete(result); |
+ break; |
+ case STATE_RESOLVE_HOST: |
+ DCHECK_EQ(OK, result); |
+ result = DoResolveHost(); |
+ break; |
+ case STATE_RESOLVE_HOST_COMPLETE: |
+ result = DoResolveHostComplete(result); |
+ break; |
+ case STATE_TCP_CONNECT: |
+ DCHECK_EQ(OK, result); |
+ result = DoTcpConnect(); |
+ break; |
+ case STATE_TCP_CONNECT_COMPLETE: |
+ result = DoTcpConnectComplete(result); |
+ break; |
+ case STATE_WRITE_TUNNEL_HEADERS: |
+ DCHECK_EQ(OK, result); |
+ result = DoWriteTunnelHeaders(); |
+ break; |
+ case STATE_WRITE_TUNNEL_HEADERS_COMPLETE: |
+ result = DoWriteTunnelHeadersComplete(result); |
+ break; |
+ case STATE_READ_TUNNEL_HEADERS: |
+ DCHECK_EQ(OK, result); |
+ result = DoReadTunnelHeaders(); |
+ break; |
+ case STATE_READ_TUNNEL_HEADERS_COMPLETE: |
+ result = DoReadTunnelHeadersComplete(result); |
+ break; |
+ case STATE_SOCKS_CONNECT: |
+ DCHECK_EQ(OK, result); |
+ result = DoSOCKSConnect(); |
+ break; |
+ case STATE_SOCKS_CONNECT_COMPLETE: |
+ result = DoSOCKSConnectComplete(result); |
+ break; |
+ case STATE_SSL_CONNECT: |
+ DCHECK_EQ(OK, result); |
+ result = DoSSLConnect(); |
+ break; |
+ case STATE_SSL_CONNECT_COMPLETE: |
+ result = DoSSLConnectComplete(result); |
+ break; |
+ case STATE_READ_WRITE: |
+ result = DoReadWrite(result); |
+ break; |
+ default: |
+ NOTREACHED() << "bad state"; |
+ result = ERR_UNEXPECTED; |
+ break; |
+ } |
+ } while (result != ERR_IO_PENDING && next_state_ != STATE_NONE); |
+ |
+ if (result != ERR_IO_PENDING) |
+ Finish(); |
+ |
+ return result; |
+} |
+ |
+int SocketStream::DoResolveProxy() { |
+ DCHECK(!pac_request_); |
+ next_state_ = STATE_RESOLVE_PROXY_COMPLETE; |
+ |
+ return proxy_service()->ResolveProxy( |
+ url_, &proxy_info_, &io_callback_, &pac_request_, NULL); |
+} |
+ |
+int SocketStream::DoResolveProxyComplete(int result) { |
+ next_state_ = STATE_RESOLVE_HOST; |
+ |
+ pac_request_ = NULL; |
+ if (result != OK) { |
+ LOG(ERROR) << "Failed to resolve proxy: " << result; |
+ proxy_info_.UseDirect(); |
+ } |
+ |
+ return OK; |
+} |
+ |
+int SocketStream::DoResolveHost() { |
+ next_state_ = STATE_RESOLVE_HOST_COMPLETE; |
+ |
+ if (proxy_info_.is_direct()) |
+ proxy_mode_ = kDirectConnection; |
+ else if (proxy_info_.proxy_server().is_socks()) |
+ proxy_mode_ = kSOCKSProxy; |
+ else |
+ proxy_mode_ = kTunnelProxy; |
+ |
+ // Determine the host and port to connect to. |
+ std::string host; |
+ int port; |
+ if (proxy_mode_ != kDirectConnection) { |
+ ProxyServer proxy_server = proxy_info_.proxy_server(); |
+ host = proxy_server.HostNoBrackets(); |
+ port = proxy_server.port(); |
+ } else { |
+ host = url_.HostNoBrackets(); |
+ port = url_.EffectiveIntPort(); |
+ } |
+ |
+ HostResolver::RequestInfo resolve_info(host, port); |
+ |
+ resolver_.reset(new SingleRequestHostResolver(host_resolver_.get())); |
+ return resolver_->Resolve(resolve_info, &addresses_, &io_callback_, NULL); |
+} |
+ |
+int SocketStream::DoResolveHostComplete(int result) { |
+ if (result == OK) |
+ next_state_ = STATE_TCP_CONNECT; |
+ return result; |
+} |
+ |
+int SocketStream::DoTcpConnect() { |
+ next_state_ = STATE_TCP_CONNECT_COMPLETE; |
+ DCHECK(factory_); |
+ socket_.reset(factory_->CreateTCPClientSocket(addresses_)); |
+ return socket_->Connect(&io_callback_); |
+} |
+ |
+int SocketStream::DoTcpConnectComplete(int result) { |
+ if (result != OK) |
+ return result; |
+ |
+ if (proxy_mode_ == kTunnelProxy) |
+ next_state_ = STATE_WRITE_TUNNEL_HEADERS; |
+ else if (proxy_mode_ == kSOCKSProxy) |
+ next_state_ = STATE_SOCKS_CONNECT; |
+ else if (is_secure()) { |
+ next_state_ = STATE_SSL_CONNECT; |
+ } else { |
+ DidEstablishConnection(); |
+ } |
+ return OK; |
+} |
+ |
+int SocketStream::DoWriteTunnelHeaders() { |
+ DCHECK_EQ(kTunnelProxy, proxy_mode_); |
+ |
+ next_state_ = STATE_WRITE_TUNNEL_HEADERS_COMPLETE; |
+ |
+ if (!tunnel_request_headers_.get()) { |
+ tunnel_request_headers_ = new RequestHeaders(); |
+ tunnel_request_headers_bytes_sent_ = 0; |
+ } |
+ if (tunnel_request_headers_->headers_.empty()) { |
+ tunnel_request_headers_->headers_ = StringPrintf( |
+ "CONNECT %s HTTP/1.1\r\n" |
+ "Host: %s\r\n" |
+ "Proxy-Connection: keep-alive\r\n", |
+ GetHostAndPort(url_).c_str(), |
+ GetHostAndOptionalPort(url_).c_str()); |
+ // TODO(ukai): set proxy auth if necessary. |
+ tunnel_request_headers_->headers_ += "\r\n"; |
+ } |
+ tunnel_request_headers_->SetDataOffset(tunnel_request_headers_bytes_sent_); |
+ int buf_len = static_cast<int>(tunnel_request_headers_->headers_.size() - |
+ tunnel_request_headers_bytes_sent_); |
+ DCHECK_GT(buf_len, 0); |
+ return socket_->Write(tunnel_request_headers_, buf_len, &io_callback_); |
+} |
+ |
+int SocketStream::DoWriteTunnelHeadersComplete(int result) { |
+ DCHECK_EQ(kTunnelProxy, proxy_mode_); |
+ |
+ if (result < 0) |
+ return result; |
+ |
+ tunnel_request_headers_bytes_sent_ += result; |
+ if (tunnel_request_headers_bytes_sent_ < |
+ tunnel_request_headers_->headers_.size()) |
+ next_state_ = STATE_WRITE_TUNNEL_HEADERS; |
+ else |
+ next_state_ = STATE_READ_TUNNEL_HEADERS; |
+ return OK; |
+} |
+ |
+int SocketStream::DoReadTunnelHeaders() { |
+ DCHECK_EQ(kTunnelProxy, proxy_mode_); |
+ |
+ next_state_ = STATE_READ_TUNNEL_HEADERS_COMPLETE; |
+ |
+ if (!tunnel_response_headers_.get()) { |
+ tunnel_response_headers_ = new ResponseHeaders(); |
+ tunnel_response_headers_capacity_ = kMaxTunnelResponseHeadersSize; |
+ tunnel_response_headers_->Realloc(tunnel_response_headers_capacity_); |
+ tunnel_response_headers_len_ = 0; |
+ } |
+ |
+ int buf_len = tunnel_response_headers_capacity_ - |
+ tunnel_response_headers_len_; |
+ tunnel_response_headers_->SetDataOffset(tunnel_response_headers_len_); |
+ CHECK(tunnel_response_headers_->data()); |
+ |
+ return socket_->Read(tunnel_response_headers_, buf_len, &io_callback_); |
+} |
+ |
+int SocketStream::DoReadTunnelHeadersComplete(int result) { |
+ DCHECK_EQ(kTunnelProxy, proxy_mode_); |
+ |
+ if (result < 0) |
+ return result; |
+ |
+ tunnel_response_headers_len_ += result; |
+ DCHECK(tunnel_response_headers_len_ <= tunnel_response_headers_capacity_); |
+ |
+ int eoh = HttpUtil::LocateEndOfHeaders( |
+ tunnel_response_headers_->headers(), tunnel_response_headers_len_, 0); |
+ if (eoh == -1) { |
+ if (tunnel_response_headers_len_ >= kMaxTunnelResponseHeadersSize) |
+ return ERR_RESPONSE_HEADERS_TOO_BIG; |
+ |
+ next_state_ = STATE_READ_TUNNEL_HEADERS; |
+ return OK; |
+ } |
+ // DidReadResponseHeaders |
+ scoped_refptr<HttpResponseHeaders> headers; |
+ headers = new HttpResponseHeaders( |
+ HttpUtil::AssembleRawHeaders(tunnel_response_headers_->headers(), eoh)); |
+ if (headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { |
+ // Require the "HTTP/1.x" status line. |
+ return ERR_TUNNEL_CONNECTION_FAILED; |
+ } |
+ switch (headers->response_code()) { |
+ case 200: // OK |
+ if (is_secure()) { |
+ DCHECK_EQ(eoh, tunnel_response_headers_len_); |
+ next_state_ = STATE_SSL_CONNECT; |
+ } else { |
+ DidEstablishConnection(); |
+ if ((eoh < tunnel_response_headers_len_) && delegate_) |
+ delegate_->OnReceivedData( |
+ this, tunnel_response_headers_->headers() + eoh, |
+ tunnel_response_headers_len_ - eoh); |
+ } |
+ return OK; |
+ case 407: // Proxy Authentication Required. |
+ // TODO(ukai): handle Proxy Authentication. |
+ break; |
+ default: |
+ break; |
+ } |
+ return ERR_TUNNEL_CONNECTION_FAILED; |
+} |
+ |
+int SocketStream::DoSOCKSConnect() { |
+ DCHECK_EQ(kSOCKSProxy, proxy_mode_); |
+ |
+ next_state_ = STATE_SOCKS_CONNECT_COMPLETE; |
+ |
+ ClientSocket* s = socket_.release(); |
+ HostResolver::RequestInfo req_info(url_.HostNoBrackets(), |
+ url_.EffectiveIntPort()); |
+ |
+ if (proxy_info_.proxy_server().scheme() == ProxyServer::SCHEME_SOCKS5) |
+ s = new SOCKS5ClientSocket(s, req_info, host_resolver_.get()); |
+ else |
+ s = new SOCKSClientSocket(s, req_info, host_resolver_.get()); |
+ socket_.reset(s); |
+ return socket_->Connect(&io_callback_); |
+} |
+ |
+int SocketStream::DoSOCKSConnectComplete(int result) { |
+ DCHECK_EQ(kSOCKSProxy, proxy_mode_); |
+ |
+ if (result == OK) { |
+ if (is_secure()) |
+ next_state_ = STATE_SSL_CONNECT; |
+ else |
+ DidEstablishConnection(); |
+ } |
+ return result; |
+} |
+ |
+int SocketStream::DoSSLConnect() { |
+ DCHECK(factory_); |
+ socket_.reset(factory_->CreateSSLClientSocket( |
+ socket_.release(), url_.HostNoBrackets(), ssl_config_)); |
+ next_state_ = STATE_SSL_CONNECT_COMPLETE; |
+ return socket_->Connect(&io_callback_); |
+} |
+ |
+int SocketStream::DoSSLConnectComplete(int result) { |
+ if (IsCertificateError(result)) |
+ result = HandleCertificateError(result); |
+ |
+ if (result == OK) |
+ DidEstablishConnection(); |
+ return result; |
+} |
+ |
+int SocketStream::DoReadWrite(int result) { |
+ if (result < OK) { |
+ Finish(); |
+ return result; |
+ } |
+ if (!socket_.get() || !socket_->IsConnected()) { |
+ Finish(); |
+ return ERR_CONNECTION_CLOSED; |
+ } |
+ |
+ next_state_ = STATE_READ_WRITE; |
+ |
+ if (!read_buf_) { |
+ read_buf_ = new IOBuffer(kReadBufferSize); |
+ result = socket_->Read(read_buf_, kReadBufferSize, &read_callback_); |
+ if (result > 0) { |
+ DidReceiveData(result); |
+ result = OK; |
+ } |
+ } |
+ if (write_buf_ && !current_write_buf_) { |
+ current_write_buf_ = new ReusedIOBuffer(write_buf_, write_buf_size_); |
+ current_write_buf_->SetOffset(write_buf_offset_); |
+ result = socket_->Write(current_write_buf_, |
+ write_buf_size_ - write_buf_offset_, |
+ &write_callback_); |
+ if (result > 0) { |
+ DidSendData(result); |
+ result = OK; |
+ } |
+ } |
+ |
+ // We arrived here when Write is performed and finished. |
+ if (result == OK) |
+ return ERR_IO_PENDING; |
+ return result; |
+} |
+ |
+int SocketStream::HandleCertificateError(int result) { |
+ // TODO(ukai): handle cert error properly. |
+ switch (result) { |
+ case ERR_CERT_COMMON_NAME_INVALID: |
+ case ERR_CERT_DATE_INVALID: |
+ case ERR_CERT_AUTHORITY_INVALID: |
+ result = OK; |
+ break; |
+ default: |
+ break; |
+ } |
+ return result; |
+} |
+ |
+bool SocketStream::is_secure() const { |
+ return url_.SchemeIs("wss"); |
+} |
+ |
+SSLConfigService* SocketStream::ssl_config_service() const { |
+ return context_->ssl_config_service(); |
+} |
+ |
+ProxyService* SocketStream::proxy_service() const { |
+ return context_->proxy_service(); |
+} |
+ |
+} // namespace net |