Index: net/http/http_network_transaction.cc |
=================================================================== |
--- net/http/http_network_transaction.cc (revision 9963) |
+++ net/http/http_network_transaction.cc (working copy) |
@@ -127,10 +127,37 @@ |
auth_identity_[target].username, auth_identity_[target].password, |
AuthPath(target)); |
- next_state_ = STATE_INIT_CONNECTION; |
- connection_.set_socket(NULL); |
- connection_.Reset(); |
+ bool keep_alive = false; |
+ if (response_.headers->IsKeepAlive()) { |
+ // If there is a response body of known length, we need to drain it first. |
+ if (content_length_ > 0 || chunked_decoder_.get()) { |
+ next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; |
+ read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket |
+ read_buf_len_ = kDrainBodyBufferSize; |
+ return; |
+ } |
+ if (content_length_ == 0) // No response body to drain. |
+ keep_alive = true; |
+ // content_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. |
+ } |
+ // We don't need to drain the response body, so we act as if we had drained |
+ // the response body. |
+ DidDrainBodyForAuthRestart(keep_alive); |
+} |
+ |
+void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { |
+ if (keep_alive) { |
+ next_state_ = STATE_WRITE_HEADERS; |
+ reused_socket_ = true; |
+ } else { |
+ next_state_ = STATE_INIT_CONNECTION; |
+ connection_.set_socket(NULL); |
+ connection_.Reset(); |
+ } |
+ |
// Reset the other member variables. |
ResetStateForRestart(); |
} |
@@ -379,6 +406,17 @@ |
rv = DoReadBodyComplete(rv); |
TRACE_EVENT_END("http.read_body", request_, request_->url.spec()); |
break; |
+ case STATE_DRAIN_BODY_FOR_AUTH_RESTART: |
+ DCHECK(rv == OK); |
+ TRACE_EVENT_BEGIN("http.drain_body_for_auth_restart", |
+ request_, request_->url.spec()); |
+ rv = DoDrainBodyForAuthRestart(); |
+ break; |
+ case STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE: |
+ rv = DoDrainBodyForAuthRestartComplete(rv); |
+ TRACE_EVENT_END("http.drain_body_for_auth_restart", |
+ request_, request_->url.spec()); |
+ break; |
default: |
NOTREACHED() << "bad state"; |
rv = ERR_FAILED; |
@@ -778,7 +816,7 @@ |
} |
} |
- // Clean up the HttpConnection if we are done. |
+ // Clean up connection_ if we are done. |
if (done) { |
LogTransactionMetrics(); |
if (!keep_alive) |
@@ -794,6 +832,62 @@ |
return result; |
} |
+int HttpNetworkTransaction::DoDrainBodyForAuthRestart() { |
+ // This method differs from DoReadBody only in the next_state_. So we just |
+ // call DoReadBody and override the next_state_. Perhaps there is a more |
+ // elegant way for these two methods to share code. |
+ int rv = DoReadBody(); |
+ DCHECK(next_state_ == STATE_READ_BODY_COMPLETE); |
+ next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE; |
+ 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. |
+int HttpNetworkTransaction::DoDrainBodyForAuthRestartComplete(int result) { |
+ bool unfiltered_eof = (result == 0); |
+ |
+ // 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; |
+ } |
+ } |
+ |
+ bool done = false, keep_alive = false; |
+ if (result < 0) { |
+ // Error while reading the socket. |
+ done = true; |
+ } else { |
+ content_read_ += result; |
+ if (unfiltered_eof || |
+ (content_length_ != -1 && content_read_ >= content_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 || |
+ (content_length_ != -1 && content_read_ > content_length_)) |
+ keep_alive = false; |
+ } |
+ } |
+ |
+ if (done) { |
+ DidDrainBodyForAuthRestart(keep_alive); |
+ } else { |
+ // Keep draining. |
+ next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; |
+ } |
+ |
+ return OK; |
+} |
+ |
void HttpNetworkTransaction::LogTransactionMetrics() const { |
base::TimeDelta duration = base::Time::Now() - response_.request_time; |
if (60 < duration.InMinutes()) |
@@ -869,14 +963,6 @@ |
response_.headers = headers; |
response_.vary_data.Init(*request_, *response_.headers); |
- int rv = HandleAuthChallenge(); |
- if (rv == WILL_RESTART_TRANSACTION) { |
- DCHECK(next_state_ == STATE_INIT_CONNECTION); |
- return OK; |
- } |
- if (rv != OK) |
- return rv; |
- |
// Figure how to determine EOF: |
// For certain responses, we know the content length is always 0. |
@@ -901,6 +987,12 @@ |
} |
} |
+ int rv = HandleAuthChallenge(); |
+ if (rv == WILL_RESTART_TRANSACTION) |
+ return OK; |
+ if (rv != OK) |
+ return rv; |
+ |
if (using_ssl_ && !establishing_tunnel_) { |
SSLClientSocket* ssl_socket = |
reinterpret_cast<SSLClientSocket*>(connection_.socket()); |