| 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());
|
|
|