diff options
author | wtc@google.com <wtc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-25 20:41:46 +0000 |
---|---|---|
committer | wtc@google.com <wtc@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-25 20:41:46 +0000 |
commit | c7af8b2475369ae8fd1f596c7265b08026c10cb4 (patch) | |
tree | b71ba9fabe98030c23c7c317f31283b7fdb816a2 | |
parent | 4132cc5ad7443aec1837c6501212906043ea1131 (diff) | |
download | chromium_src-c7af8b2475369ae8fd1f596c7265b08026c10cb4.zip chromium_src-c7af8b2475369ae8fd1f596c7265b08026c10cb4.tar.gz chromium_src-c7af8b2475369ae8fd1f596c7265b08026c10cb4.tar.bz2 |
Implement SSL tunneling through a proxy server.
Add several states to HttpNetworkTransaction for the HTTP
CONNECT method and the SSL Connect (handshake) after the
tunnel is connected.
Add the error code ERR_TUNNEL_CONNECTION_FAILED for failure
to connect a tunnel.
R=darin
BUG=1272555
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1329 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/base/client_socket_handle.h | 1 | ||||
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 183 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 24 |
4 files changed, 191 insertions, 20 deletions
diff --git a/net/base/client_socket_handle.h b/net/base/client_socket_handle.h index 7d8e896..f033d2a 100644 --- a/net/base/client_socket_handle.h +++ b/net/base/client_socket_handle.h @@ -60,6 +60,7 @@ class ClientSocketHandle { // These may only be used if is_initialized() is true. ClientSocket* socket() { return socket_->get(); } + ClientSocket* release_socket() { return socket_->release(); } void set_socket(ClientSocket* s) { socket_->reset(s); } private: diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index b35a939..c3b7a60 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -71,6 +71,9 @@ NET_ERROR(ADDRESS_UNREACHABLE, -109) // The server requested a client certificate for SSL client authentication. NET_ERROR(SSL_CLIENT_AUTH_CERT_NEEDED, -110) +// A tunnel connection through the proxy could not be established. +NET_ERROR(TUNNEL_CONNECTION_FAILED, -111) + // Certificate error codes // // The values of certificate error codes must be consecutive. diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 56604ee..6c90c66 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -153,8 +153,8 @@ void HttpNetworkTransaction::BuildRequestHeaders() { path = request_->url.PathForRequest(); } - request_headers_ = request_->method + " " + path + " HTTP/1.1\r\n" + - "Host: " + request_->url.host(); + request_headers_ = request_->method + " " + path + + " HTTP/1.1\r\nHost: " + request_->url.host(); if (request_->url.IntPort() != -1) request_headers_ += ":" + request_->url.port(); request_headers_ += "\r\n"; @@ -200,6 +200,34 @@ void HttpNetworkTransaction::BuildRequestHeaders() { request_headers_ += "\r\n"; } +// The HTTP CONNECT method for establishing a tunnel connection is documented +// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2717, Sections 5.2 and +// 5.3. +void HttpNetworkTransaction::BuildTunnelRequest() { + std::string port; + if (request_->url.has_port()) { + port = request_->url.port(); + } else { + port = "443"; + } + + // RFC 2616 Section 9 says the Host request-header field MUST accompany all + // HTTP/1.1 requests. + request_headers_ = "CONNECT " + request_->url.host() + ":" + port + + " HTTP/1.1\r\nHost: " + request_->url.host(); + if (request_->url.IntPort() != -1) + request_headers_ += ":" + request_->url.port(); + request_headers_ += "\r\n"; + + if (!request_->user_agent.empty()) + request_headers_ += "User-Agent: " + request_->user_agent + "\r\n"; + + // TODO(wtc): Add the Proxy-Authorization header, if necessary, to handle + // proxy authentication. + + request_headers_ += "\r\n"; +} + void HttpNetworkTransaction::DoCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(user_callback_); @@ -252,6 +280,27 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_CONNECT_COMPLETE: rv = DoConnectComplete(rv); break; + case STATE_WRITE_TUNNEL_REQUEST: + DCHECK(rv == OK); + rv = DoWriteTunnelRequest(); + break; + case STATE_WRITE_TUNNEL_REQUEST_COMPLETE: + rv = DoWriteTunnelRequestComplete(rv); + break; + case STATE_READ_TUNNEL_RESPONSE: + DCHECK(rv == OK); + rv = DoReadTunnelResponse(); + break; + case STATE_READ_TUNNEL_RESPONSE_COMPLETE: + rv = DoReadTunnelResponseComplete(rv); + break; + case STATE_SSL_CONNECT_OVER_TUNNEL: + DCHECK(rv == OK); + rv = DoSSLConnectOverTunnel(); + break; + case STATE_SSL_CONNECT_OVER_TUNNEL_COMPLETE: + rv = DoSSLConnectOverTunnelComplete(rv); + break; case STATE_WRITE_HEADERS: DCHECK(rv == OK); rv = DoWriteHeaders(); @@ -403,6 +452,80 @@ int HttpNetworkTransaction::DoConnect() { } int HttpNetworkTransaction::DoConnectComplete(int result) { + if (result == OK) { + if (using_tunnel_) { + next_state_ = STATE_WRITE_TUNNEL_REQUEST; + } else { + next_state_ = STATE_WRITE_HEADERS; + } + } + return result; +} + +int HttpNetworkTransaction::DoWriteTunnelRequest() { + next_state_ = STATE_WRITE_TUNNEL_REQUEST_COMPLETE; + + if (request_headers_.empty()) + BuildTunnelRequest(); + + return WriteRequestHeaders(); +} + +int HttpNetworkTransaction::DoWriteTunnelRequestComplete(int result) { + if (result < 0) + return result; + + request_headers_bytes_sent_ += result; + if (request_headers_bytes_sent_ < request_headers_.size()) { + next_state_ = STATE_WRITE_TUNNEL_REQUEST; + } else { + next_state_ = STATE_READ_TUNNEL_RESPONSE; + // Reset for writing the real request headers later. + request_headers_.clear(); + request_headers_bytes_sent_ = 0; + } + return OK; +} + +int HttpNetworkTransaction::DoReadTunnelResponse() { + next_state_ = STATE_READ_TUNNEL_RESPONSE_COMPLETE; + + return ReadResponseHeaders(); +} + +int HttpNetworkTransaction::DoReadTunnelResponseComplete(int result) { + if (result < 0) + return result; + if (result == 0) // The socket was closed before the tunnel is established. + return ERR_TUNNEL_CONNECTION_FAILED; + + header_buf_len_ += result; + DCHECK(header_buf_len_ <= header_buf_capacity_); + + int eoh = HttpUtil::LocateEndOfHeaders(header_buf_.get(), header_buf_len_); + if (eoh == -1) { + next_state_ = STATE_READ_TUNNEL_RESPONSE; // Read more. + return OK; + } + if (eoh != header_buf_len_) { + // The proxy sent extraneous data after the headers. + return ERR_TUNNEL_CONNECTION_FAILED; + } + + // And, we are done with the SSL tunnel CONNECT sequence. + return DidReadTunnelResponse(); +} + +int HttpNetworkTransaction::DoSSLConnectOverTunnel() { + next_state_ = STATE_SSL_CONNECT_OVER_TUNNEL_COMPLETE; + + ClientSocket* s = connection_.release_socket(); + s = socket_factory_->CreateSSLClientSocket(s, request_->url.host()); + connection_.set_socket(s); + return connection_.socket()->Connect(&io_callback_); +} + +int HttpNetworkTransaction::DoSSLConnectOverTunnelComplete(int result) { if (result == OK) next_state_ = STATE_WRITE_HEADERS; return result; @@ -421,12 +544,7 @@ int HttpNetworkTransaction::DoWriteHeaders() { if (request_headers_bytes_sent_ == 0) response_.request_time = Time::Now(); - const char* buf = request_headers_.data() + request_headers_bytes_sent_; - int buf_len = static_cast<int>(request_headers_.size() - - request_headers_bytes_sent_); - DCHECK(buf_len > 0); - - return connection_.socket()->Write(buf, buf_len, &io_callback_); + return WriteRequestHeaders(); } int HttpNetworkTransaction::DoWriteHeadersComplete(int result) { @@ -473,17 +591,7 @@ int HttpNetworkTransaction::DoWriteBodyComplete(int result) { int HttpNetworkTransaction::DoReadHeaders() { next_state_ = STATE_READ_HEADERS_COMPLETE; - // Grow the read buffer if necessary. - if (header_buf_len_ == header_buf_capacity_) { - header_buf_capacity_ += kHeaderBufInitialSize; - header_buf_.reset(static_cast<char*>( - realloc(header_buf_.release(), header_buf_capacity_))); - } - - char* buf = header_buf_.get() + header_buf_len_; - int buf_len = header_buf_capacity_ - header_buf_len_; - - return connection_.socket()->Read(buf, buf_len, &io_callback_); + return ReadResponseHeaders(); } int HttpNetworkTransaction::DoReadHeadersComplete(int result) { @@ -591,6 +699,43 @@ int HttpNetworkTransaction::DoReadBodyComplete(int result) { return result; } +int HttpNetworkTransaction::WriteRequestHeaders() { + const char* buf = request_headers_.data() + request_headers_bytes_sent_; + int buf_len = static_cast<int>(request_headers_.size() - + request_headers_bytes_sent_); + DCHECK(buf_len > 0); + + return connection_.socket()->Write(buf, buf_len, &io_callback_); +} + +int HttpNetworkTransaction::ReadResponseHeaders() { + // Grow the read buffer if necessary. + if (header_buf_len_ == header_buf_capacity_) { + header_buf_capacity_ += kHeaderBufInitialSize; + header_buf_.reset(static_cast<char*>( + realloc(header_buf_.release(), header_buf_capacity_))); + } + + char* buf = header_buf_.get() + header_buf_len_; + int buf_len = header_buf_capacity_ - header_buf_len_; + + return connection_.socket()->Read(buf, buf_len, &io_callback_); +} + +int HttpNetworkTransaction::DidReadTunnelResponse() { + // TODO(wtc): Require the "HTTP/1.x" status line. The HttpResponseHeaders + // constructor makes up an "HTTP/1.0 200 OK" status line if it is missing. + scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders( + HttpUtil::AssembleRawHeaders(header_buf_.get(), header_buf_len_)); + // TODO(wtc): Handle 407 proxy authentication challenge. + if (headers->response_code() != 200) + return ERR_TUNNEL_CONNECTION_FAILED; + + next_state_ = STATE_SSL_CONNECT_OVER_TUNNEL; + header_buf_len_ = 0; // Reset for reading the real response headers later. + return OK; +} + int HttpNetworkTransaction::DidReadResponseHeaders() { scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders( HttpUtil::AssembleRawHeaders(header_buf_.get(), header_buf_body_offset_)); diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 1d0673d..5386196 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -44,6 +44,7 @@ class HttpNetworkTransaction : public HttpTransaction { private: ~HttpNetworkTransaction(); void BuildRequestHeaders(); + void BuildTunnelRequest(); void DoCallback(int result); void OnIOComplete(int result); @@ -62,6 +63,12 @@ class HttpNetworkTransaction : public HttpTransaction { int DoResolveHostComplete(int result); int DoConnect(); int DoConnectComplete(int result); + int DoWriteTunnelRequest(); + int DoWriteTunnelRequestComplete(int result); + int DoReadTunnelResponse(); + int DoReadTunnelResponseComplete(int result); + int DoSSLConnectOverTunnel(); + int DoSSLConnectOverTunnelComplete(int result); int DoWriteHeaders(); int DoWriteHeadersComplete(int result); int DoWriteBody(); @@ -71,7 +78,16 @@ class HttpNetworkTransaction : public HttpTransaction { int DoReadBody(); int DoReadBodyComplete(int result); - // Called when read_buf_ contains the complete response headers. + // Called to write the request headers in request_headers_. + int WriteRequestHeaders(); + + // Called to read the response headers into header_buf_. + int ReadResponseHeaders(); + + // Called when header_buf_ contains the complete CONNECT response. + int DidReadTunnelResponse(); + + // Called when header_buf_ contains the complete response headers. int DidReadResponseHeaders(); // Called to possibly recover from the given error. Sets next_state_ and @@ -138,6 +154,12 @@ class HttpNetworkTransaction : public HttpTransaction { STATE_RESOLVE_HOST_COMPLETE, STATE_CONNECT, STATE_CONNECT_COMPLETE, + STATE_WRITE_TUNNEL_REQUEST, + STATE_WRITE_TUNNEL_REQUEST_COMPLETE, + STATE_READ_TUNNEL_RESPONSE, + STATE_READ_TUNNEL_RESPONSE_COMPLETE, + STATE_SSL_CONNECT_OVER_TUNNEL, + STATE_SSL_CONNECT_OVER_TUNNEL_COMPLETE, STATE_WRITE_HEADERS, STATE_WRITE_HEADERS_COMPLETE, STATE_WRITE_BODY, |