Index: net/http/http_network_transaction.cc |
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc |
index 3e878ca7929a0445222803aaea40876df7779e29..3e74ea89460442e84efc6400115f9597960bab1e 100644 |
--- a/net/http/http_network_transaction.cc |
+++ b/net/http/http_network_transaction.cc |
@@ -31,6 +31,98 @@ using base::Time; |
namespace net { |
+namespace { |
+ |
+void BuildRequestHeaders(const HttpRequestInfo* request_info, |
+ const std::string& authorization_headers, |
+ const UploadDataStream* upload_data_stream, |
+ bool using_proxy, |
+ std::string* request_headers) { |
+ const std::string path = using_proxy ? |
+ HttpUtil::SpecForRequest(request_info->url) : |
+ HttpUtil::PathForRequest(request_info->url); |
+ *request_headers = |
+ StringPrintf("%s %s HTTP/1.1\r\nHost: %s", |
+ request_info->method.c_str(), path.c_str(), |
+ request_info->url.host().c_str()); |
+ if (request_info->url.IntPort() != -1) |
+ *request_headers += ":" + request_info->url.port(); |
+ *request_headers += "\r\n"; |
+ |
+ // For compat with HTTP/1.0 servers and proxies: |
+ if (using_proxy) |
+ *request_headers += "Proxy-"; |
+ *request_headers += "Connection: keep-alive\r\n"; |
+ |
+ if (!request_info->user_agent.empty()) { |
+ StringAppendF(request_headers, "User-Agent: %s\r\n", |
+ request_info->user_agent.c_str()); |
+ } |
+ |
+ // Our consumer should have made sure that this is a safe referrer. See for |
+ // instance WebCore::FrameLoader::HideReferrer. |
+ if (request_info->referrer.is_valid()) |
+ StringAppendF(request_headers, "Referer: %s\r\n", |
+ request_info->referrer.spec().c_str()); |
+ |
+ // Add a content length header? |
+ if (upload_data_stream) { |
+ StringAppendF(request_headers, "Content-Length: %llu\r\n", |
wtc
2009/04/30 18:57:20
I just learned that we're supposed to use the PRIu
willchan no longer on Chromium
2009/04/30 19:32:40
That's for uint64_t. We're using uint64 here. ui
|
+ upload_data_stream->size()); |
+ } else if (request_info->method == "POST" || request_info->method == "PUT" || |
+ request_info->method == "HEAD") { |
+ // An empty POST/PUT request still needs a content length. As for HEAD, |
+ // IE and Safari also add a content length header. Presumably it is to |
+ // support sending a HEAD request to an URL that only expects to be sent a |
+ // POST or some other method that normally would have a message body. |
+ *request_headers += "Content-Length: 0\r\n"; |
+ } |
+ |
+ // Honor load flags that impact proxy caches. |
+ if (request_info->load_flags & LOAD_BYPASS_CACHE) { |
+ *request_headers += "Pragma: no-cache\r\nCache-Control: no-cache\r\n"; |
+ } else if (request_info->load_flags & LOAD_VALIDATE_CACHE) { |
+ *request_headers += "Cache-Control: max-age=0\r\n"; |
+ } |
+ |
+ if (!authorization_headers.empty()) { |
+ *request_headers += authorization_headers; |
+ } |
+ |
+ // TODO(darin): Need to prune out duplicate headers. |
+ |
+ *request_headers += request_info->extra_headers; |
+ *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 2817, Sections 5.2 and |
+// 5.3. |
+void BuildTunnelRequest(const HttpRequestInfo* request_info, |
+ const std::string& authorization_headers, |
+ std::string* request_headers) { |
+ // RFC 2616 Section 9 says the Host request-header field MUST accompany all |
+ // HTTP/1.1 requests. |
+ *request_headers = StringPrintf("CONNECT %s:%d HTTP/1.1\r\n", |
+ request_info->url.host().c_str(), request_info->url.EffectiveIntPort()); |
+ *request_headers += "Host: " + request_info->url.host(); |
+ if (request_info->url.has_port()) |
+ *request_headers += ":" + request_info->url.port(); |
+ *request_headers += "\r\n"; |
+ |
+ if (!request_info->user_agent.empty()) |
+ StringAppendF(request_headers, "User-Agent: %s\r\n", |
+ request_info->user_agent.c_str()); |
+ |
+ if (!authorization_headers.empty()) { |
+ *request_headers += authorization_headers; |
+ } |
+ |
+ *request_headers += "\r\n"; |
+} |
+ |
+} // namespace |
+ |
//----------------------------------------------------------------------------- |
HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session, |
@@ -291,83 +383,6 @@ HttpNetworkTransaction::~HttpNetworkTransaction() { |
session_->proxy_service()->CancelPacRequest(pac_request_); |
} |
-void HttpNetworkTransaction::BuildRequestHeaders() { |
- // For proxy use the full url. Otherwise just the absolute path. |
- // This strips out any reference/username/password. |
- std::string path = using_proxy_ ? |
- HttpUtil::SpecForRequest(request_->url) : |
- HttpUtil::PathForRequest(request_->url); |
- |
- 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"; |
- |
- // For compat with HTTP/1.0 servers and proxies: |
- if (using_proxy_) |
- request_headers_ += "Proxy-"; |
- request_headers_ += "Connection: keep-alive\r\n"; |
- |
- if (!request_->user_agent.empty()) |
- request_headers_ += "User-Agent: " + request_->user_agent + "\r\n"; |
- |
- // Our consumer should have made sure that this is a safe referrer. See for |
- // instance WebCore::FrameLoader::HideReferrer. |
- if (request_->referrer.is_valid()) |
- request_headers_ += "Referer: " + request_->referrer.spec() + "\r\n"; |
- |
- // Add a content length header? |
- if (request_->upload_data) { |
- request_body_stream_.reset(new UploadDataStream(request_->upload_data)); |
- request_headers_ += |
- "Content-Length: " + Uint64ToString(request_body_stream_->size()) + |
- "\r\n"; |
- } else if (request_->method == "POST" || request_->method == "PUT" || |
- request_->method == "HEAD") { |
- // An empty POST/PUT request still needs a content length. As for HEAD, |
- // IE and Safari also add a content length header. Presumably it is to |
- // support sending a HEAD request to an URL that only expects to be sent a |
- // POST or some other method that normally would have a message body. |
- request_headers_ += "Content-Length: 0\r\n"; |
- } |
- |
- // Honor load flags that impact proxy caches. |
- if (request_->load_flags & LOAD_BYPASS_CACHE) { |
- request_headers_ += "Pragma: no-cache\r\nCache-Control: no-cache\r\n"; |
- } else if (request_->load_flags & LOAD_VALIDATE_CACHE) { |
- request_headers_ += "Cache-Control: max-age=0\r\n"; |
- } |
- |
- ApplyAuth(); |
- |
- // TODO(darin): Need to prune out duplicate headers. |
- |
- request_headers_ += request_->extra_headers; |
- 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 2817, Sections 5.2 and |
-// 5.3. |
-void HttpNetworkTransaction::BuildTunnelRequest() { |
- // RFC 2616 Section 9 says the Host request-header field MUST accompany all |
- // HTTP/1.1 requests. |
- request_headers_ = StringPrintf("CONNECT %s:%d HTTP/1.1\r\n", |
- request_->url.host().c_str(), request_->url.EffectiveIntPort()); |
- request_headers_ += "Host: " + request_->url.host(); |
- if (request_->url.has_port()) |
- request_headers_ += ":" + request_->url.port(); |
- request_headers_ += "\r\n"; |
- |
- if (!request_->user_agent.empty()) |
- request_headers_ += "User-Agent: " + request_->user_agent + "\r\n"; |
- |
- ApplyAuth(); |
- |
- request_headers_ += "\r\n"; |
-} |
- |
void HttpNetworkTransaction::DoCallback(int rv) { |
DCHECK(rv != ERR_IO_PENDING); |
DCHECK(user_callback_); |
@@ -655,10 +670,36 @@ int HttpNetworkTransaction::DoWriteHeaders() { |
// This is constructed lazily (instead of within our Start method), so that |
// we have proxy info available. |
if (request_headers_.empty()) { |
+ // Figure out if we can/should add Proxy-Authentication & Authentication |
+ // headers. |
+ bool have_proxy_auth = |
+ ShouldApplyProxyAuth() && |
+ (HaveAuth(HttpAuth::AUTH_PROXY) || |
+ SelectPreemptiveAuth(HttpAuth::AUTH_PROXY)); |
+ bool have_server_auth = |
+ ShouldApplyServerAuth() && |
+ (HaveAuth(HttpAuth::AUTH_SERVER) || |
+ SelectPreemptiveAuth(HttpAuth::AUTH_SERVER)); |
+ |
+ std::string authorization_headers; |
+ |
+ if (have_proxy_auth) |
+ authorization_headers.append( |
+ BuildAuthorizationHeader(HttpAuth::AUTH_PROXY)); |
+ if (have_server_auth) |
+ authorization_headers.append( |
+ BuildAuthorizationHeader(HttpAuth::AUTH_SERVER)); |
+ |
if (establishing_tunnel_) { |
- BuildTunnelRequest(); |
+ BuildTunnelRequest(request_, authorization_headers, &request_headers_); |
} else { |
- BuildRequestHeaders(); |
+ if (request_->upload_data) |
+ request_body_stream_.reset(new UploadDataStream(request_->upload_data)); |
+ BuildRequestHeaders(request_, |
+ authorization_headers, |
+ request_body_stream_.get(), |
+ using_proxy_, |
+ &request_headers_); |
} |
} |
@@ -671,7 +712,7 @@ int HttpNetworkTransaction::DoWriteHeaders() { |
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); |
+ DCHECK_GT(buf_len, 0); |
return connection_.socket()->Write(buf, buf_len, &io_callback_); |
} |
@@ -765,8 +806,10 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { |
if (result < 0) |
return HandleIOError(result); |
- if (result == 0 && ShouldResendRequest()) |
+ if (result == 0 && ShouldResendRequest()) { |
+ ResetConnectionAndRequestForResend(); |
return result; |
+ } |
// Record our best estimate of the 'response time' as the time when we read |
// the first bytes of the response headers. |
@@ -1257,8 +1300,10 @@ int HttpNetworkTransaction::HandleIOError(int error) { |
case ERR_CONNECTION_RESET: |
case ERR_CONNECTION_CLOSED: |
case ERR_CONNECTION_ABORTED: |
- if (ShouldResendRequest()) |
+ if (ShouldResendRequest()) { |
+ ResetConnectionAndRequestForResend(); |
error = OK; |
+ } |
break; |
} |
return error; |
@@ -1282,7 +1327,7 @@ void HttpNetworkTransaction::ResetStateForRestart() { |
response_ = HttpResponseInfo(); |
} |
-bool HttpNetworkTransaction::ShouldResendRequest() { |
+bool HttpNetworkTransaction::ShouldResendRequest() const { |
// NOTE: we resend a request only if we reused a keep-alive connection. |
// This automatically prevents an infinite resend loop because we'll run |
// out of the cached keep-alive connections eventually. |
@@ -1291,6 +1336,10 @@ bool HttpNetworkTransaction::ShouldResendRequest() { |
header_buf_len_) { // We have received some response headers. |
return false; |
} |
+ return true; |
+} |
+ |
+void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { |
connection_.set_socket(NULL); |
connection_.Reset(); |
// There are two reasons we need to clear request_headers_. 1) It contains |
@@ -1301,7 +1350,6 @@ bool HttpNetworkTransaction::ShouldResendRequest() { |
request_headers_.clear(); |
request_headers_bytes_sent_ = 0; |
next_state_ = STATE_INIT_CONNECTION; // Resend the request. |
- return true; |
} |
int HttpNetworkTransaction::ReconsiderProxyAfterError(int error) { |
@@ -1348,12 +1396,16 @@ int HttpNetworkTransaction::ReconsiderProxyAfterError(int error) { |
return rv; |
} |
-void HttpNetworkTransaction::AddAuthorizationHeader(HttpAuth::Target target) { |
- // If we have no authentication information, check if we can select |
- // a cache entry preemptively (based on the path). |
- if (!HaveAuth(target) && !SelectPreemptiveAuth(target)) |
- return; |
+bool HttpNetworkTransaction::ShouldApplyProxyAuth() const { |
+ return using_proxy_ || establishing_tunnel_; |
+} |
+bool HttpNetworkTransaction::ShouldApplyServerAuth() const { |
+ return !establishing_tunnel_; |
+} |
+ |
+std::string HttpNetworkTransaction::BuildAuthorizationHeader( |
+ HttpAuth::Target target) const { |
DCHECK(HaveAuth(target)); |
// Add a Authorization/Proxy-Authorization header line. |
@@ -1362,24 +1414,9 @@ void HttpNetworkTransaction::AddAuthorizationHeader(HttpAuth::Target target) { |
auth_identity_[target].password, |
request_, |
&proxy_info_); |
- request_headers_ += HttpAuth::GetAuthorizationHeaderName(target) + |
- ": " + credentials + "\r\n"; |
-} |
- |
-void HttpNetworkTransaction::ApplyAuth() { |
- // We expect using_proxy_ and using_tunnel_ to be mutually exclusive. |
- DCHECK(!using_proxy_ || !using_tunnel_); |
- |
- // Don't send proxy auth after tunnel has been established. |
- bool should_apply_proxy_auth = using_proxy_ || establishing_tunnel_; |
- // Don't send origin server auth while establishing tunnel. |
- bool should_apply_server_auth = !establishing_tunnel_; |
- |
- if (should_apply_proxy_auth) |
- AddAuthorizationHeader(HttpAuth::AUTH_PROXY); |
- if (should_apply_server_auth) |
- AddAuthorizationHeader(HttpAuth::AUTH_SERVER); |
+ return HttpAuth::GetAuthorizationHeaderName(target) + |
+ ": " + credentials + "\r\n"; |
} |
GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const { |