Index: net/http/http_network_transaction.cc |
=================================================================== |
--- net/http/http_network_transaction.cc (revision 29289) |
+++ net/http/http_network_transaction.cc (working copy) |
@@ -25,6 +25,7 @@ |
#include "net/http/http_network_session.h" |
#include "net/http/http_request_info.h" |
#include "net/http/http_response_headers.h" |
+#include "net/http/http_response_info.h" |
#include "net/http/http_util.h" |
#include "net/socket/client_socket_factory.h" |
#include "net/socket/socks5_client_socket.h" |
@@ -35,10 +36,6 @@ |
namespace net { |
-void HttpNetworkTransaction::ResponseHeaders::Realloc(size_t new_size) { |
- headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size))); |
-} |
- |
namespace { |
void BuildRequestHeaders(const HttpRequestInfo* request_info, |
@@ -138,20 +135,12 @@ |
request_(NULL), |
pac_request_(NULL), |
reused_socket_(false), |
+ headers_valid_(false), |
+ logged_response_time(false), |
using_ssl_(false), |
proxy_mode_(kDirectConnection), |
establishing_tunnel_(false), |
- reading_body_from_socket_(false), |
embedded_identity_used_(false), |
- request_headers_(new RequestHeaders()), |
- request_headers_bytes_sent_(0), |
- header_buf_(new ResponseHeaders()), |
- header_buf_capacity_(0), |
- header_buf_len_(0), |
- header_buf_body_offset_(-1), |
- header_buf_http_offset_(-1), |
- response_body_length_(-1), // -1 means unspecified. |
- response_body_read_(0), |
read_buf_len_(0), |
next_state_(STATE_NONE) { |
session->ssl_config_service()->GetSSLConfig(&ssl_config_); |
@@ -176,7 +165,7 @@ |
int HttpNetworkTransaction::RestartIgnoringLastError( |
CompletionCallback* callback) { |
if (connection_.socket()->IsConnected()) { |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
} else { |
connection_.socket()->Disconnect(); |
connection_.Reset(); |
@@ -266,19 +255,19 @@ |
} |
bool keep_alive = false; |
- if (response_.headers->IsKeepAlive()) { |
- // If there is a response body of known length, we need to drain it first. |
- if (response_body_length_ > 0 || chunked_decoder_.get()) { |
+ // Even if the server says the connection is keep-alive, we have to be |
+ // able to find the end of each response in order to reuse the connection. |
+ if (GetResponseHeaders()->IsKeepAlive() && |
+ http_stream_->CanFindEndOfResponse()) { |
+ // If the response body hasn't been completely read, we need to drain |
+ // it first. |
+ if (!http_stream_->IsResponseBodyComplete()) { |
next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; |
- read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket |
+ read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket. |
read_buf_len_ = kDrainBodyBufferSize; |
return; |
} |
- if (response_body_length_ == 0) // No response body to drain. |
- keep_alive = true; |
- // response_body_length_ is -1 and we're not using chunked encoding. We |
- // don't know the length of the response body, so we can't reuse this |
- // connection even though the server says it's keep-alive. |
+ keep_alive = true; |
} |
// We don't need to drain the response body, so we act as if we had drained |
@@ -288,7 +277,7 @@ |
void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { |
if (keep_alive) { |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
reused_socket_ = true; |
} else { |
next_state_ = STATE_INIT_CONNECTION; |
@@ -302,7 +291,8 @@ |
int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, |
CompletionCallback* callback) { |
- DCHECK(response_.headers); |
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); |
+ DCHECK(headers.get()); |
DCHECK(buf); |
DCHECK_LT(0, buf_len); |
@@ -317,8 +307,8 @@ |
// network attacker can already control HTTP sessions. |
// We reach this case when the user cancels a 407 proxy auth prompt. |
// See http://crbug.com/8473 |
- DCHECK_EQ(407, response_.headers->response_code()); |
- LogBlockedTunnelResponse(response_.headers->response_code()); |
+ DCHECK_EQ(407, headers->response_code()); |
+ LogBlockedTunnelResponse(headers->response_code()); |
return ERR_TUNNEL_CONNECTION_FAILED; |
} |
@@ -338,8 +328,9 @@ |
} |
const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const { |
- return (response_.headers || response_.ssl_info.cert || |
- response_.cert_request_info) ? &response_ : NULL; |
+ const HttpResponseInfo* response = http_stream_->GetResponseInfo(); |
+ return ((headers_valid_ && response->headers) || response->ssl_info.cert || |
+ response->cert_request_info) ? response : NULL; |
} |
LoadState HttpNetworkTransaction::GetLoadState() const { |
@@ -350,8 +341,7 @@ |
return LOAD_STATE_RESOLVING_PROXY_FOR_URL; |
case STATE_INIT_CONNECTION_COMPLETE: |
return connection_.GetLoadState(); |
- case STATE_WRITE_HEADERS_COMPLETE: |
- case STATE_WRITE_BODY_COMPLETE: |
+ case STATE_SEND_REQUEST_COMPLETE: |
return LOAD_STATE_SENDING_REQUEST; |
case STATE_READ_HEADERS_COMPLETE: |
return LOAD_STATE_WAITING_FOR_RESPONSE; |
@@ -363,10 +353,10 @@ |
} |
uint64 HttpNetworkTransaction::GetUploadProgress() const { |
- if (!request_body_stream_.get()) |
+ if (!http_stream_.get()) |
return 0; |
- return request_body_stream_->position(); |
+ return http_stream_->GetUploadProgress(); |
} |
HttpNetworkTransaction::~HttpNetworkTransaction() { |
@@ -439,24 +429,15 @@ |
rv = DoSSLConnectComplete(rv); |
TRACE_EVENT_END("http.ssl_connect", request_, request_->url.spec()); |
break; |
- case STATE_WRITE_HEADERS: |
+ case STATE_SEND_REQUEST: |
DCHECK_EQ(OK, rv); |
- TRACE_EVENT_BEGIN("http.write_headers", request_, request_->url.spec()); |
- rv = DoWriteHeaders(); |
+ TRACE_EVENT_BEGIN("http.send_request", request_, request_->url.spec()); |
+ rv = DoSendRequest(); |
break; |
- case STATE_WRITE_HEADERS_COMPLETE: |
- rv = DoWriteHeadersComplete(rv); |
- TRACE_EVENT_END("http.write_headers", request_, request_->url.spec()); |
+ case STATE_SEND_REQUEST_COMPLETE: |
+ rv = DoSendRequestComplete(rv); |
+ TRACE_EVENT_END("http.send_request", request_, request_->url.spec()); |
break; |
- case STATE_WRITE_BODY: |
- DCHECK_EQ(OK, rv); |
- TRACE_EVENT_BEGIN("http.write_body", request_, request_->url.spec()); |
- rv = DoWriteBody(); |
- break; |
- case STATE_WRITE_BODY_COMPLETE: |
- rv = DoWriteBodyComplete(rv); |
- TRACE_EVENT_END("http.write_body", request_, request_->url.spec()); |
- break; |
case STATE_READ_HEADERS: |
DCHECK_EQ(OK, rv); |
TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec()); |
@@ -611,7 +592,7 @@ |
// trying to reuse a keep-alive connection. |
reused_socket_ = connection_.is_reused(); |
if (reused_socket_) { |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
} else { |
// Now we have a TCP connected socket. Perform other connection setup as |
// needed. |
@@ -620,11 +601,12 @@ |
else if (using_ssl_ && proxy_mode_ == kDirectConnection) { |
next_state_ = STATE_SSL_CONNECT; |
} else { |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
if (proxy_mode_ == kHTTPProxyUsingTunnel) |
establishing_tunnel_ = true; |
} |
} |
+ headers_valid_ = false; |
http_stream_.reset(new HttpBasicStream(&connection_)); |
return OK; |
} |
@@ -655,7 +637,7 @@ |
if (using_ssl_) { |
next_state_ = STATE_SSL_CONNECT; |
} else { |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
} |
} else { |
result = ReconsiderProxyAfterError(result); |
@@ -694,7 +676,7 @@ |
base::TimeDelta::FromMinutes(10), |
100); |
- next_state_ = STATE_WRITE_HEADERS; |
+ next_state_ = STATE_SEND_REQUEST; |
} else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { |
result = HandleCertificateRequest(result); |
} else { |
@@ -703,12 +685,16 @@ |
return result; |
} |
-int HttpNetworkTransaction::DoWriteHeaders() { |
- next_state_ = STATE_WRITE_HEADERS_COMPLETE; |
+int HttpNetworkTransaction::DoSendRequest() { |
+ next_state_ = STATE_SEND_REQUEST_COMPLETE; |
+ UploadDataStream* request_body = NULL; |
+ if (!establishing_tunnel_ && request_->upload_data) |
+ request_body = new UploadDataStream(request_->upload_data); |
+ |
// This is constructed lazily (instead of within our Start method), so that |
// we have proxy info available. |
- if (request_headers_->headers_.empty()) { |
+ if (request_headers_.empty()) { |
// Figure out if we can/should add Proxy-Authentication & Authentication |
// headers. |
bool have_proxy_auth = |
@@ -733,91 +719,30 @@ |
BuildAuthorizationHeader(HttpAuth::AUTH_SERVER)); |
if (establishing_tunnel_) { |
- BuildTunnelRequest(request_, authorization_headers, |
- &request_headers_->headers_); |
+ BuildTunnelRequest(request_, authorization_headers, &request_headers_); |
} else { |
- if (request_->upload_data) |
- request_body_stream_.reset(new UploadDataStream(request_->upload_data)); |
- BuildRequestHeaders(request_, authorization_headers, |
- request_body_stream_.get(), |
- proxy_mode_ == kHTTPProxy, |
- &request_headers_->headers_); |
+ BuildRequestHeaders(request_, authorization_headers, request_body, |
+ proxy_mode_ == kHTTPProxy, &request_headers_); |
} |
} |
- // Record our best estimate of the 'request time' as the time when we send |
- // out the first bytes of the request headers. |
- if (request_headers_bytes_sent_ == 0) { |
- response_.request_time = Time::Now(); |
- } |
- |
- request_headers_->SetDataOffset(request_headers_bytes_sent_); |
- int buf_len = static_cast<int>(request_headers_->headers_.size() - |
- request_headers_bytes_sent_); |
- DCHECK_GT(buf_len, 0); |
- |
- return http_stream_->Write(request_headers_, buf_len, &io_callback_); |
+ return http_stream_->SendRequest(request_, request_headers_, request_body, |
+ &io_callback_); |
} |
-int HttpNetworkTransaction::DoWriteHeadersComplete(int result) { |
+int HttpNetworkTransaction::DoSendRequestComplete(int result) { |
if (result < 0) |
return HandleIOError(result); |
- request_headers_bytes_sent_ += result; |
- if (request_headers_bytes_sent_ < request_headers_->headers_.size()) { |
- next_state_ = STATE_WRITE_HEADERS; |
- } else if (!establishing_tunnel_ && request_body_stream_.get() && |
- request_body_stream_->size()) { |
- next_state_ = STATE_WRITE_BODY; |
- } else { |
- next_state_ = STATE_READ_HEADERS; |
- } |
- return OK; |
-} |
+ next_state_ = STATE_READ_HEADERS; |
-int HttpNetworkTransaction::DoWriteBody() { |
- next_state_ = STATE_WRITE_BODY_COMPLETE; |
- |
- DCHECK(request_body_stream_.get()); |
- DCHECK(request_body_stream_->size()); |
- |
- int buf_len = static_cast<int>(request_body_stream_->buf_len()); |
- |
- return http_stream_->Write(request_body_stream_->buf(), buf_len, |
- &io_callback_); |
-} |
- |
-int HttpNetworkTransaction::DoWriteBodyComplete(int result) { |
- if (result < 0) |
- return HandleIOError(result); |
- |
- request_body_stream_->DidConsume(result); |
- |
- if (request_body_stream_->position() < request_body_stream_->size()) { |
- next_state_ = STATE_WRITE_BODY; |
- } else { |
- next_state_ = STATE_READ_HEADERS; |
- } |
return OK; |
} |
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_->Realloc(header_buf_capacity_); |
- } |
- |
- int buf_len = header_buf_capacity_ - header_buf_len_; |
- header_buf_->set_data(header_buf_len_); |
- |
- // http://crbug.com/16371: We're seeing |user_buf_->data()| return NULL. |
- // See if the user is passing in an IOBuffer with a NULL |data_|. |
- CHECK(header_buf_->data()); |
- |
- return http_stream_->Read(header_buf_, buf_len, &io_callback_); |
+ return http_stream_->ReadResponseHeaders(&io_callback_); |
} |
int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { |
@@ -826,24 +751,12 @@ |
return ERR_TUNNEL_CONNECTION_FAILED; |
} |
- if (has_found_status_line_start()) { |
- // Assume EOF is end-of-headers. |
- header_buf_body_offset_ = header_buf_len_; |
- return OK; |
- } |
- |
- // No status line was matched yet. Could have been a HTTP/0.9 response, or |
- // a partial HTTP/1.x response. |
- |
- if (header_buf_len_ == 0) { |
+ if (!http_stream_->GetResponseInfo()->headers) { |
// The connection was closed before any data was sent. Likely an error |
// rather than empty HTTP/0.9 response. |
return ERR_EMPTY_RESPONSE; |
} |
- // Assume everything else is a HTTP/0.9 response (including responses |
- // of 'h', 'ht', 'htt'). |
- header_buf_body_offset_ = 0; |
return OK; |
} |
@@ -865,99 +778,115 @@ |
} |
} |
- if (result < 0) |
+ if (result < 0 && result != ERR_CONNECTION_CLOSED) |
return HandleIOError(result); |
- if (result == 0 && ShouldResendRequest(result)) { |
+ if (result == ERR_CONNECTION_CLOSED && ShouldResendRequest(result)) { |
ResetConnectionAndRequestForResend(); |
- return result; |
+ return OK; |
} |
- // Record our best estimate of the 'response time' as the time when we read |
- // the first bytes of the response headers. |
- if (header_buf_len_ == 0) { |
- // After we call RestartWithAuth header_buf_len will be zero again, and |
- // we need to be cautious about incorrectly logging the duration across the |
- // authentication activitiy. |
- bool first_response = response_.response_time == Time(); |
- response_.response_time = Time::Now(); |
- if (first_response) |
- LogTransactionConnectedMetrics(); |
+ // After we call RestartWithAuth a new response_time will be recorded, and |
+ // we need to be cautious about incorrectly logging the duration across the |
+ // authentication activity. |
+ HttpResponseInfo* response = http_stream_->GetResponseInfo(); |
+ if (!logged_response_time) { |
+ LogTransactionConnectedMetrics(); |
+ logged_response_time = true; |
} |
- // The socket was closed before we found end-of-headers. |
- if (result == 0) { |
+ if (result == ERR_CONNECTION_CLOSED) { |
int rv = HandleConnectionClosedBeforeEndOfHeaders(); |
if (rv != OK) |
return rv; |
- } else { |
- header_buf_len_ += result; |
- DCHECK(header_buf_len_ <= header_buf_capacity_); |
+ // TODO(wtc): Traditionally this code has returned 0 when reading a closed |
+ // socket. That is partially corrected in classes that we call, but |
+ // callers need to be updated. |
+ result = 0; |
+ } |
- // Look for the start of the status line, if it hasn't been found yet. |
- if (!has_found_status_line_start()) { |
- header_buf_http_offset_ = HttpUtil::LocateStartOfStatusLine( |
- header_buf_->headers(), header_buf_len_); |
- } |
+ if (response->headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { |
+ // Require the "HTTP/1.x" status line for SSL CONNECT. |
+ if (establishing_tunnel_) |
+ return ERR_TUNNEL_CONNECTION_FAILED; |
- if (has_found_status_line_start()) { |
- int eoh = HttpUtil::LocateEndOfHeaders( |
- header_buf_->headers(), header_buf_len_, header_buf_http_offset_); |
- if (eoh == -1) { |
- // Prevent growing the headers buffer indefinitely. |
- if (header_buf_len_ >= kMaxHeaderBufSize) |
- return ERR_RESPONSE_HEADERS_TOO_BIG; |
+ // HTTP/0.9 doesn't support the PUT method, so lack of response headers |
+ // indicates a buggy server. See: |
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=193921 |
+ if (request_->method == "PUT") |
+ return ERR_METHOD_NOT_SUPPORTED; |
+ } |
- // Haven't found the end of headers yet, keep reading. |
- next_state_ = STATE_READ_HEADERS; |
+ if (establishing_tunnel_) { |
+ switch (response->headers->response_code()) { |
+ case 200: // OK |
+ if (http_stream_->IsMoreDataBuffered()) { |
+ // The proxy sent extraneous data after the headers. |
+ return ERR_TUNNEL_CONNECTION_FAILED; |
+ } |
+ next_state_ = STATE_SSL_CONNECT; |
+ // Reset for the real request and response headers. |
+ request_headers_.clear(); |
+ http_stream_.reset(new HttpBasicStream(&connection_)); |
+ headers_valid_ = false; |
+ establishing_tunnel_ = false; |
return OK; |
- } |
- header_buf_body_offset_ = eoh; |
- } else if (header_buf_len_ < 8) { |
- // Not enough data to decide whether this is HTTP/0.9 yet. |
- // 8 bytes = (4 bytes of junk) + "http".length() |
- next_state_ = STATE_READ_HEADERS; |
- return OK; |
- } else { |
- // Enough data was read -- there is no status line. |
- header_buf_body_offset_ = 0; |
+ |
+ // We aren't able to CONNECT to the remote host through the proxy. We |
+ // need to be very suspicious about the response because an active network |
+ // attacker can force us into this state by masquerading as the proxy. |
+ // The only safe thing to do here is to fail the connection because our |
+ // client is expecting an SSL protected response. |
+ // See http://crbug.com/7338. |
+ case 407: // Proxy Authentication Required |
+ // We need this status code to allow proxy authentication. Our |
+ // authentication code is smart enough to avoid being tricked by an |
+ // active network attacker. |
+ break; |
+ default: |
+ // For all other status codes, we conservatively fail the CONNECT |
+ // request. |
+ // We lose something by doing this. We have seen proxy 403, 404, and |
+ // 501 response bodies that contain a useful error message. For |
+ // example, Squid uses a 404 response to report the DNS error: "The |
+ // domain name does not exist." |
+ LogBlockedTunnelResponse(response->headers->response_code()); |
+ return ERR_TUNNEL_CONNECTION_FAILED; |
} |
} |
- // And, we are done with the Start or the SSL tunnel CONNECT sequence. |
- return DidReadResponseHeaders(); |
+ // Check for an intermediate 100 Continue response. An origin server is |
+ // allowed to send this response even if we didn't ask for it, so we just |
+ // need to skip over it. |
+ // We treat any other 1xx in this same way (although in practice getting |
+ // a 1xx that isn't a 100 is rare). |
+ if (response->headers->response_code() / 100 == 1) { |
+ next_state_ = STATE_READ_HEADERS; |
+ return OK; |
+ } |
+ |
+ int rv = HandleAuthChallenge(); |
+ if (rv != OK) |
+ return rv; |
+ |
+ if (using_ssl_ && !establishing_tunnel_) { |
+ SSLClientSocket* ssl_socket = |
+ reinterpret_cast<SSLClientSocket*>(connection_.socket()); |
+ ssl_socket->GetSSLInfo(&response->ssl_info); |
+ } |
+ |
+ headers_valid_ = true; |
+ return OK; |
} |
int HttpNetworkTransaction::DoReadBody() { |
DCHECK(read_buf_); |
DCHECK_GT(read_buf_len_, 0); |
DCHECK(connection_.is_initialized()); |
- DCHECK(!header_buf_->headers() || header_buf_body_offset_ >= 0); |
next_state_ = STATE_READ_BODY_COMPLETE; |
- |
- // We may have already consumed the indicated content length. |
- if (response_body_length_ != -1 && |
- response_body_read_ >= response_body_length_) |
- return 0; |
- |
- // We may have some data remaining in the header buffer. |
- if (header_buf_->headers() && header_buf_body_offset_ < header_buf_len_) { |
- int n = std::min(read_buf_len_, header_buf_len_ - header_buf_body_offset_); |
- memcpy(read_buf_->data(), header_buf_->headers() + header_buf_body_offset_, |
- n); |
- header_buf_body_offset_ += n; |
- if (header_buf_body_offset_ == header_buf_len_) { |
- header_buf_->Reset(); |
- header_buf_capacity_ = 0; |
- header_buf_len_ = 0; |
- header_buf_body_offset_ = -1; |
- } |
- return n; |
- } |
- |
- reading_body_from_socket_ = true; |
- return http_stream_->Read(read_buf_, read_buf_len_, &io_callback_); |
+ return http_stream_->ReadResponseBody(read_buf_, read_buf_len_, |
+ &io_callback_); |
} |
int HttpNetworkTransaction::DoReadBodyComplete(int result) { |
@@ -965,39 +894,18 @@ |
DCHECK(!establishing_tunnel_) << |
"We should never read a response body of a tunnel."; |
- bool unfiltered_eof = (result == 0 && reading_body_from_socket_); |
- reading_body_from_socket_ = false; |
- |
- // Filter incoming data if appropriate. FilterBuf may return an error. |
- if (result > 0 && chunked_decoder_.get()) { |
- result = chunked_decoder_->FilterBuf(read_buf_->data(), result); |
- if (result == 0 && !chunked_decoder_->reached_eof()) { |
- // Don't signal completion of the Read call yet or else it'll look like |
- // we received end-of-file. Wait for more data. |
- next_state_ = STATE_READ_BODY; |
- return OK; |
- } |
- } |
- |
bool done = false, keep_alive = false; |
if (result < 0) { |
- // Error while reading the socket. |
+ // Error or closed connection while reading the socket. |
done = true; |
- } else { |
- response_body_read_ += result; |
- if (unfiltered_eof || |
- (response_body_length_ != -1 && |
- response_body_read_ >= response_body_length_) || |
- (chunked_decoder_.get() && chunked_decoder_->reached_eof())) { |
- done = true; |
- keep_alive = response_.headers->IsKeepAlive(); |
- // We can't reuse the connection if we read more than the advertised |
- // content length. |
- if (unfiltered_eof || |
- (response_body_length_ != -1 && |
- response_body_read_ > response_body_length_)) |
- keep_alive = false; |
- } |
+ // TODO(wtc): Traditionally this code has returned 0 when reading a closed |
+ // socket. That is partially corrected in classes that we call, but |
+ // callers need to be updated. |
+ if (result == ERR_CONNECTION_CLOSED) |
+ result = 0; |
+ } else if (http_stream_->IsResponseBodyComplete()) { |
+ done = true; |
+ keep_alive = GetResponseHeaders()->IsKeepAlive(); |
} |
// Clean up connection_ if we are done. |
@@ -1026,45 +934,18 @@ |
return rv; |
} |
-// TODO(wtc): The first two thirds of this method and the DoReadBodyComplete |
-// method are almost the same. Figure out a good way for these two methods |
-// to share code. |
+// TODO(wtc): This method and the DoReadBodyComplete method are almost |
+// the same. Figure out a good way for these two methods to share code. |
int HttpNetworkTransaction::DoDrainBodyForAuthRestartComplete(int result) { |
- bool unfiltered_eof = (result == 0 && reading_body_from_socket_); |
- reading_body_from_socket_ = false; |
- |
- // Filter incoming data if appropriate. FilterBuf may return an error. |
- if (result > 0 && chunked_decoder_.get()) { |
- result = chunked_decoder_->FilterBuf(read_buf_->data(), result); |
- if (result == 0 && !chunked_decoder_->reached_eof()) { |
- // Don't signal completion of the Read call yet or else it'll look like |
- // we received end-of-file. Wait for more data. |
- next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; |
- return OK; |
- } |
- } |
- |
// keep_alive defaults to true because the very reason we're draining the |
// response body is to reuse the connection for auth restart. |
bool done = false, keep_alive = true; |
if (result < 0) { |
- // Error while reading the socket. |
+ // Error or closed connection while reading the socket. |
done = true; |
keep_alive = false; |
- } else { |
- response_body_read_ += result; |
- if (unfiltered_eof || |
- (response_body_length_ != -1 && |
- response_body_read_ >= response_body_length_) || |
- (chunked_decoder_.get() && chunked_decoder_->reached_eof())) { |
- done = true; |
- // We can't reuse the connection if we read more than the advertised |
- // content length. |
- if (unfiltered_eof || |
- (response_body_length_ != -1 && |
- response_body_read_ > response_body_length_)) |
- keep_alive = false; |
- } |
+ } else if (http_stream_->IsResponseBodyComplete()) { |
+ done = true; |
} |
if (done) { |
@@ -1213,7 +1094,8 @@ |
} |
void HttpNetworkTransaction::LogTransactionConnectedMetrics() const { |
- base::TimeDelta total_duration = response_.response_time - start_time_; |
+ base::TimeDelta total_duration = |
+ http_stream_->GetResponseInfo()->response_time - start_time_; |
UMA_HISTOGRAM_CLIPPED_TIMES( |
"Net.Transaction_Connected_Under_10", |
@@ -1272,7 +1154,8 @@ |
} |
void HttpNetworkTransaction::LogTransactionMetrics() const { |
- base::TimeDelta duration = base::Time::Now() - response_.request_time; |
+ base::TimeDelta duration = base::Time::Now() - |
+ http_stream_->GetResponseInfo()->request_time; |
if (60 < duration.InMinutes()) |
return; |
@@ -1300,143 +1183,6 @@ |
<< GetHostAndPort(request_->url) << "."; |
} |
-int HttpNetworkTransaction::DidReadResponseHeaders() { |
- DCHECK_GE(header_buf_body_offset_, 0); |
- |
- scoped_refptr<HttpResponseHeaders> headers; |
- if (has_found_status_line_start()) { |
- headers = new HttpResponseHeaders( |
- HttpUtil::AssembleRawHeaders( |
- header_buf_->headers(), header_buf_body_offset_)); |
- } else { |
- // Fabricate a status line to to preserve the HTTP/0.9 version. |
- // (otherwise HttpResponseHeaders will default it to HTTP/1.0). |
- headers = new HttpResponseHeaders(std::string("HTTP/0.9 200 OK")); |
- } |
- |
- if (headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { |
- // Require the "HTTP/1.x" status line for SSL CONNECT. |
- if (establishing_tunnel_) |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- |
- // HTTP/0.9 doesn't support the PUT method, so lack of response headers |
- // indicates a buggy server. See: |
- // https://bugzilla.mozilla.org/show_bug.cgi?id=193921 |
- if (request_->method == "PUT") |
- return ERR_METHOD_NOT_SUPPORTED; |
- } |
- |
- if (establishing_tunnel_) { |
- switch (headers->response_code()) { |
- case 200: // OK |
- if (header_buf_body_offset_ != header_buf_len_) { |
- // The proxy sent extraneous data after the headers. |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
- next_state_ = STATE_SSL_CONNECT; |
- // Reset for the real request and response headers. |
- request_headers_->headers_.clear(); |
- request_headers_bytes_sent_ = 0; |
- header_buf_len_ = 0; |
- header_buf_body_offset_ = -1; |
- establishing_tunnel_ = false; |
- return OK; |
- |
- // We aren't able to CONNECT to the remote host through the proxy. We |
- // need to be very suspicious about the response because an active network |
- // attacker can force us into this state by masquerading as the proxy. |
- // The only safe thing to do here is to fail the connection because our |
- // client is expecting an SSL protected response. |
- // See http://crbug.com/7338. |
- case 407: // Proxy Authentication Required |
- // We need this status code to allow proxy authentication. Our |
- // authentication code is smart enough to avoid being tricked by an |
- // active network attacker. |
- break; |
- default: |
- // For all other status codes, we conservatively fail the CONNECT |
- // request. |
- // We lose something by doing this. We have seen proxy 403, 404, and |
- // 501 response bodies that contain a useful error message. For |
- // example, Squid uses a 404 response to report the DNS error: "The |
- // domain name does not exist." |
- LogBlockedTunnelResponse(headers->response_code()); |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
- } |
- |
- // Check for an intermediate 100 Continue response. An origin server is |
- // allowed to send this response even if we didn't ask for it, so we just |
- // need to skip over it. |
- // We treat any other 1xx in this same way (although in practice getting |
- // a 1xx that isn't a 100 is rare). |
- if (headers->response_code() / 100 == 1) { |
- header_buf_len_ -= header_buf_body_offset_; |
- // If we've already received some bytes after the 1xx response, |
- // move them to the beginning of header_buf_. |
- if (header_buf_len_) { |
- memmove(header_buf_->headers(), |
- header_buf_->headers() + header_buf_body_offset_, |
- header_buf_len_); |
- } |
- header_buf_body_offset_ = -1; |
- next_state_ = STATE_READ_HEADERS; |
- return OK; |
- } |
- |
- response_.headers = headers; |
- response_.vary_data.Init(*request_, *response_.headers); |
- |
- // Figure how to determine EOF: |
- |
- // For certain responses, we know the content length is always 0. From |
- // RFC 2616 Section 4.3 Message Body: |
- // |
- // For response messages, whether or not a message-body is included with |
- // a message is dependent on both the request method and the response |
- // status code (section 6.1.1). All responses to the HEAD request method |
- // MUST NOT include a message-body, even though the presence of entity- |
- // header fields might lead one to believe they do. All 1xx |
- // (informational), 204 (no content), and 304 (not modified) responses |
- // MUST NOT include a message-body. All other responses do include a |
- // message-body, although it MAY be of zero length. |
- switch (response_.headers->response_code()) { |
- // Note that 1xx was already handled earlier. |
- case 204: // No Content |
- case 205: // Reset Content |
- case 304: // Not Modified |
- response_body_length_ = 0; |
- break; |
- } |
- if (request_->method == "HEAD") |
- response_body_length_ = 0; |
- |
- if (response_body_length_ == -1) { |
- // Ignore spurious chunked responses from HTTP/1.0 servers and proxies. |
- // Otherwise "Transfer-Encoding: chunked" trumps "Content-Length: N" |
- if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1) && |
- response_.headers->HasHeaderValue("Transfer-Encoding", "chunked")) { |
- chunked_decoder_.reset(new HttpChunkedDecoder()); |
- } else { |
- response_body_length_ = response_.headers->GetContentLength(); |
- // If response_body_length_ is still -1, then we have to wait for the |
- // server to close the connection. |
- } |
- } |
- |
- int rv = HandleAuthChallenge(); |
- if (rv != OK) |
- return rv; |
- |
- if (using_ssl_ && !establishing_tunnel_) { |
- SSLClientSocket* ssl_socket = |
- reinterpret_cast<SSLClientSocket*>(connection_.socket()); |
- ssl_socket->GetSSLInfo(&response_.ssl_info); |
- } |
- |
- return OK; |
-} |
- |
int HttpNetworkTransaction::HandleCertificateError(int error) { |
DCHECK(using_ssl_); |
@@ -1462,17 +1208,18 @@ |
} |
if (error != OK) { |
+ HttpResponseInfo* response = http_stream_->GetResponseInfo(); |
SSLClientSocket* ssl_socket = |
reinterpret_cast<SSLClientSocket*>(connection_.socket()); |
- ssl_socket->GetSSLInfo(&response_.ssl_info); |
+ ssl_socket->GetSSLInfo(&response->ssl_info); |
// Add the bad certificate to the set of allowed certificates in the |
// SSL info object. This data structure will be consulted after calling |
// RestartIgnoringLastError(). And the user will be asked interactively |
// before RestartIgnoringLastError() is ever called. |
SSLConfig::CertAndStatus bad_cert; |
- bad_cert.cert = response_.ssl_info.cert; |
- bad_cert.cert_status = response_.ssl_info.cert_status; |
+ bad_cert.cert = response->ssl_info.cert; |
+ bad_cert.cert_status = response->ssl_info.cert_status; |
ssl_config_.allowed_bad_certs.push_back(bad_cert); |
} |
return error; |
@@ -1489,10 +1236,11 @@ |
// test. |
DCHECK(reused_socket_ || !ssl_config_.send_client_cert); |
- response_.cert_request_info = new SSLCertRequestInfo; |
+ HttpResponseInfo* response = http_stream_->GetResponseInfo(); |
+ response->cert_request_info = new SSLCertRequestInfo; |
SSLClientSocket* ssl_socket = |
reinterpret_cast<SSLClientSocket*>(connection_.socket()); |
- ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info); |
+ ssl_socket->GetSSLCertRequestInfo(response->cert_request_info); |
// Close the connection while the user is selecting a certificate to send |
// to the server. |
@@ -1505,7 +1253,7 @@ |
Lookup(GetHostAndPort(request_->url)); |
if (client_cert) { |
const std::vector<scoped_refptr<X509Certificate> >& client_certs = |
- response_.cert_request_info->client_certs; |
+ response->cert_request_info->client_certs; |
for (size_t i = 0; i < client_certs.size(); ++i) { |
if (client_cert->fingerprint().Equals(client_certs[i]->fingerprint())) { |
ssl_config_.client_cert = client_cert; |
@@ -1570,33 +1318,25 @@ |
void HttpNetworkTransaction::ResetStateForRestart() { |
pending_auth_target_ = HttpAuth::AUTH_NONE; |
- header_buf_->Reset(); |
- header_buf_capacity_ = 0; |
- header_buf_len_ = 0; |
- header_buf_body_offset_ = -1; |
- header_buf_http_offset_ = -1; |
- response_body_length_ = -1; |
- response_body_read_ = 0; |
read_buf_ = NULL; |
read_buf_len_ = 0; |
- request_headers_->headers_.clear(); |
- request_headers_bytes_sent_ = 0; |
- chunked_decoder_.reset(); |
- // Reset all the members of response_. |
- response_ = HttpResponseInfo(); |
+ http_stream_.reset(new HttpBasicStream(&connection_)); |
+ headers_valid_ = false; |
+ request_headers_.clear(); |
} |
+HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const { |
+ CHECK(http_stream_.get()); |
+ return http_stream_->GetResponseInfo()->headers; |
+} |
+ |
bool HttpNetworkTransaction::ShouldResendRequest(int error) 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. |
if (establishing_tunnel_ || |
- // We used a socket that was never idle. |
- connection_.reuse_type() == ClientSocketHandle::UNUSED || |
- // We used an unused, idle socket and got a error that wasn't a TCP RST. |
- (connection_.reuse_type() == ClientSocketHandle::UNUSED_IDLE && |
- (error != OK && error != ERR_CONNECTION_RESET)) || |
- header_buf_len_) { // We have received some response headers. |
+ !connection_.ShouldResendFailedRequest(error) || |
+ GetResponseHeaders()) { // We have received some response headers. |
return false; |
} |
return true; |
@@ -1605,13 +1345,10 @@ |
void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { |
connection_.socket()->Disconnect(); |
connection_.Reset(); |
- // There are two reasons we need to clear request_headers_. 1) It contains |
- // the real request headers, but we may need to resend the CONNECT request |
- // first to recreate the SSL tunnel. 2) An empty request_headers_ causes |
- // BuildRequestHeaders to be called, which rewinds request_body_stream_ to |
- // the beginning of request_->upload_data. |
- request_headers_->headers_.clear(); |
- request_headers_bytes_sent_ = 0; |
+ // We need to clear request_headers_ because it contains the real request |
+ // headers, but we may need to resend the CONNECT request first to recreate |
+ // the SSL tunnel. |
+ request_headers_.clear(); |
next_state_ = STATE_INIT_CONNECTION; // Resend the request. |
} |
@@ -1653,7 +1390,6 @@ |
if (connection_.socket()) |
connection_.socket()->Disconnect(); |
connection_.Reset(); |
- DCHECK(!request_headers_bytes_sent_); |
next_state_ = STATE_RESOLVE_PROXY_COMPLETE; |
} else { |
rv = error; |
@@ -1813,15 +1549,14 @@ |
std::string msg; |
std::string header_val; |
void* iter = NULL; |
- while (response_.headers->EnumerateHeader(&iter, "proxy-authenticate", |
- &header_val)) { |
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); |
+ while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) { |
msg.append("\n Has header Proxy-Authenticate: "); |
msg.append(header_val); |
} |
iter = NULL; |
- while (response_.headers->EnumerateHeader(&iter, "www-authenticate", |
- &header_val)) { |
+ while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) { |
msg.append("\n Has header WWW-Authenticate: "); |
msg.append(header_val); |
} |
@@ -1830,8 +1565,7 @@ |
// authentication with a "Proxy-Support: Session-Based-Authentication" |
// response header. |
iter = NULL; |
- while (response_.headers->EnumerateHeader(&iter, "proxy-support", |
- &header_val)) { |
+ while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) { |
msg.append("\n Has header Proxy-Support: "); |
msg.append(header_val); |
} |
@@ -1840,9 +1574,10 @@ |
} |
int HttpNetworkTransaction::HandleAuthChallenge() { |
- DCHECK(response_.headers); |
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); |
+ DCHECK(headers); |
- int status = response_.headers->response_code(); |
+ int status = headers->response_code(); |
if (status != 401 && status != 407) |
return OK; |
HttpAuth::Target target = status == 407 ? |
@@ -1874,9 +1609,7 @@ |
if (target != HttpAuth::AUTH_SERVER || |
!(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) { |
// Find the best authentication challenge that we support. |
- HttpAuth::ChooseBestChallenge(response_.headers.get(), |
- target, |
- auth_origin, |
+ HttpAuth::ChooseBestChallenge(headers, target, auth_origin, |
&auth_handler_[target]); |
} |
@@ -1932,7 +1665,7 @@ |
auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme()); |
// TODO(eroman): decode realm according to RFC 2047. |
auth_info->realm = ASCIIToWide(auth_handler_[target]->realm()); |
- response_.auth_challenge = auth_info; |
+ http_stream_->GetResponseInfo()->auth_challenge = auth_info; |
} |
} // namespace net |