summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwtc@google.com <wtc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-25 20:41:46 +0000
committerwtc@google.com <wtc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-25 20:41:46 +0000
commitc7af8b2475369ae8fd1f596c7265b08026c10cb4 (patch)
treeb71ba9fabe98030c23c7c317f31283b7fdb816a2
parent4132cc5ad7443aec1837c6501212906043ea1131 (diff)
downloadchromium_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.h1
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/http/http_network_transaction.cc183
-rw-r--r--net/http/http_network_transaction.h24
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,