summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/http/http_network_transaction.cc55
-rw-r--r--net/http/http_network_transaction_unittest.cc209
-rw-r--r--net/http/http_response_body_drainer.cc119
-rw-r--r--net/http/http_response_body_drainer.h66
-rw-r--r--net/http/http_response_body_drainer_unittest.cc216
-rw-r--r--net/http/http_util.cc10
-rw-r--r--net/http/http_util.h7
-rw-r--r--net/net.gyp3
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc5
10 files changed, 590 insertions, 103 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 7f2a6c2..abf18d1 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -379,6 +379,9 @@ NET_ERROR(MISCONFIGURED_AUTH_ENVIRONMENT, -343)
// An undocumented SSPI or GSSAPI status code was returned.
NET_ERROR(UNDOCUMENTED_SECURITY_LIBRARY_STATUS, -344)
+// The HTTP response was too big to drain.
+NET_ERROR(RESPONSE_BODY_TOO_BIG_TO_DRAIN, -345)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index e04ed1d..e6084bc 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -166,6 +166,40 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
}
+HttpNetworkTransaction::~HttpNetworkTransaction() {
+ if (stream_.get()) {
+ HttpResponseHeaders* headers = GetResponseHeaders();
+ // TODO(mbelshe): The stream_ should be able to compute whether or not the
+ // stream should be kept alive. No reason to compute here
+ // and pass it in.
+ bool try_to_keep_alive =
+ next_state_ == STATE_NONE &&
+ stream_->CanFindEndOfResponse() &&
+ (!headers || headers->IsKeepAlive());
+ if (!try_to_keep_alive) {
+ stream_->Close(true /* not reusable */);
+ } else {
+ if (stream_->IsResponseBodyComplete()) {
+ // If the response body is complete, we can just reuse the socket.
+ stream_->Close(false /* reusable */);
+ } else {
+ // Otherwise, we try to drain the response body.
+ // TODO(willchan): Consider moving this response body draining to the
+ // stream implementation. For SPDY, there's clearly no point. For
+ // HTTP, it can vary depending on whether or not we're pipelining. It's
+ // stream dependent, so the different subtypes should be implementing
+ // their solutions.
+ HttpUtil::DrainStreamBodyAndClose(stream_.release());
+ }
+ }
+ }
+
+ if (stream_request_.get()) {
+ stream_request_->Cancel();
+ stream_request_ = NULL;
+ }
+}
+
int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
CompletionCallback* callback,
const BoundNetLog& net_log) {
@@ -440,31 +474,12 @@ void HttpNetworkTransaction::OnNeedsClientAuth(
OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
}
-HttpNetworkTransaction::~HttpNetworkTransaction() {
- if (stream_.get()) {
- HttpResponseHeaders* headers = GetResponseHeaders();
- // TODO(mbelshe): The stream_ should be able to compute whether or not the
- // stream should be kept alive. No reason to compute here
- // and pass it in.
- bool keep_alive = next_state_ == STATE_NONE &&
- stream_->IsResponseBodyComplete() &&
- stream_->CanFindEndOfResponse() &&
- (!headers || headers->IsKeepAlive());
- stream_->Close(!keep_alive);
- }
-
- if (stream_request_.get()) {
- stream_request_->Cancel();
- stream_request_ = NULL;
- }
-}
-
bool HttpNetworkTransaction::is_https_request() const {
return request_->url.SchemeIs("https");
}
void HttpNetworkTransaction::DoCallback(int rv) {
- DCHECK(rv != ERR_IO_PENDING);
+ DCHECK_NE(rv, ERR_IO_PENDING);
DCHECK(user_callback_);
// Since Run may result in Read being called, clear user_callback_ up front.
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index d2a35bc..a8d9ea5 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -154,14 +154,20 @@ HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
class HttpNetworkTransactionTest : public PlatformTest {
public:
virtual void SetUp() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunAllPending();
spdy::SpdyFramer::set_enable_compression_default(false);
}
virtual void TearDown() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunAllPending();
spdy::SpdyFramer::set_enable_compression_default(true);
// Empty the current queue.
MessageLoop::current()->RunAllPending();
PlatformTest::TearDown();
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunAllPending();
}
protected:
@@ -560,7 +566,7 @@ TEST_F(HttpNetworkTransactionTest, ReuseConnection) {
StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
session_deps.socket_factory.AddSocketDataProvider(&data);
- const char* kExpectedResponseData[] = {
+ const char* const kExpectedResponseData[] = {
"hello", "world"
};
@@ -850,8 +856,9 @@ TEST_F(HttpNetworkTransactionTest, NonKeepAliveConnectionEOF) {
EXPECT_EQ(ERR_EMPTY_RESPONSE, out.rv);
}
-// Test that we correctly reuse a keep-alive connection after receiving a 304.
-TEST_F(HttpNetworkTransactionTest, KeepAliveAfter304) {
+// Test that we correctly reuse a keep-alive connection after not explicitly
+// reading the body.
+TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) {
SessionDependencies session_deps;
scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
@@ -860,8 +867,23 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfter304) {
request.url = GURL("http://www.foo.com/");
request.load_flags = 0;
+ // Note that because all these reads happen in the same
+ // StaticSocketDataProvider, it shows that the same socket is being reused for
+ // all transactions.
MockRead data1_reads[] = {
+ MockRead("HTTP/1.1 204 No Content\r\n\r\n"),
+ MockRead("HTTP/1.1 205 Reset Content\r\n\r\n"),
MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
+ MockRead("HTTP/1.1 302 Found\r\n"
+ "Content-Length: 0\r\n\r\n"),
+ MockRead("HTTP/1.1 302 Found\r\n"
+ "Content-Length: 5\r\n\r\n"
+ "hello"),
+ MockRead("HTTP/1.1 301 Moved Permanently\r\n"
+ "Content-Length: 0\r\n\r\n"),
+ MockRead("HTTP/1.1 301 Moved Permanently\r\n"
+ "Content-Length: 5\r\n\r\n"
+ "hello"),
MockRead("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"),
MockRead("hello"),
};
@@ -874,7 +896,10 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfter304) {
StaticSocketDataProvider data2(data2_reads, arraysize(data2_reads), NULL, 0);
session_deps.socket_factory.AddSocketDataProvider(&data2);
- for (int i = 0; i < 2; ++i) {
+ const int kNumUnreadBodies = arraysize(data1_reads) - 2;
+ std::string response_lines[kNumUnreadBodies];
+
+ for (size_t i = 0; i < arraysize(data1_reads) - 2; ++i) {
TestCompletionCallback callback;
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
@@ -886,22 +911,44 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfter304) {
EXPECT_EQ(OK, rv);
const HttpResponseInfo* response = trans->GetResponseInfo();
- EXPECT_TRUE(response != NULL);
+ ASSERT_TRUE(response != NULL);
- EXPECT_TRUE(response->headers != NULL);
- if (i == 0) {
- EXPECT_EQ("HTTP/1.1 304 Not Modified",
- response->headers->GetStatusLine());
- // We intentionally don't read the response in this case, to reflect how
- // HttpCache::Transaction uses HttpNetworkTransaction.
- } else {
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
- std::string response_data;
- rv = ReadTransaction(trans.get(), &response_data);
- EXPECT_EQ(OK, rv);
- EXPECT_EQ("hello", response_data);
- }
+ ASSERT_TRUE(response->headers != NULL);
+ response_lines[i] = response->headers->GetStatusLine();
+
+ // We intentionally don't read the response bodies.
}
+
+ const char* const kStatusLines[] = {
+ "HTTP/1.1 204 No Content",
+ "HTTP/1.1 205 Reset Content",
+ "HTTP/1.1 304 Not Modified",
+ "HTTP/1.1 302 Found",
+ "HTTP/1.1 302 Found",
+ "HTTP/1.1 301 Moved Permanently",
+ "HTTP/1.1 301 Moved Permanently",
+ };
+
+ COMPILE_ASSERT(kNumUnreadBodies == arraysize(kStatusLines),
+ forgot_to_update_kStatusLines);
+
+ for (int i = 0; i < kNumUnreadBodies; ++i)
+ EXPECT_EQ(kStatusLines[i], response_lines[i]);
+
+ TestCompletionCallback callback;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello", response_data);
}
// Test the request-challenge-retry sequence for basic auth.
@@ -1036,8 +1083,7 @@ TEST_F(HttpNetworkTransactionTest, DoNotSendAuth) {
// connection.
TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -1067,8 +1113,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
// Lastly, the server responds with the actual content.
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
- MockRead("Content-Length: 100\r\n\r\n"),
- MockRead(false, OK),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("Hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -1077,6 +1123,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1104,15 +1151,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) {
response = trans->GetResponseInfo();
EXPECT_FALSE(response == NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
- EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_EQ(5, response->headers->GetContentLength());
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
// connection and with no response body to drain.
TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -1140,8 +1186,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) {
// Lastly, the server responds with the actual content.
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
- MockRead("Content-Length: 100\r\n\r\n"),
- MockRead(false, OK),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -1150,6 +1196,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1177,15 +1224,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) {
response = trans->GetResponseInfo();
EXPECT_FALSE(response == NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
- EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_EQ(5, response->headers->GetContentLength());
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
// connection and with a large response body to drain.
TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -1221,8 +1267,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) {
// Lastly, the server responds with the actual content.
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
- MockRead("Content-Length: 100\r\n\r\n"),
- MockRead(false, OK),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -1231,6 +1277,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1258,15 +1305,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) {
response = trans->GetResponseInfo();
EXPECT_FALSE(response == NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
- EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_EQ(5, response->headers->GetContentLength());
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
// connection, but the server gets impatient and closes the connection.
TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -1309,8 +1355,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) {
MockRead data_reads2[] = {
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
- MockRead("Content-Length: 100\r\n\r\n"),
- MockRead(false, OK),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -1322,6 +1368,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1349,7 +1396,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) {
response = trans->GetResponseInfo();
ASSERT_FALSE(response == NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
- EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_EQ(5, response->headers->GetContentLength());
}
// Test the request-challenge-retry sequence for basic auth, over a connection
@@ -1361,8 +1408,6 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
@@ -1399,8 +1444,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) {
MockRead("HTTP/1.1 200 OK\r\n"),
MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
- MockRead("Content-Length: 100\r\n\r\n"),
- MockRead(false, OK),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead(false, "hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -1411,6 +1456,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1450,11 +1497,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) {
EXPECT_TRUE(response->headers->IsKeepAlive());
EXPECT_EQ(200, response->headers->response_code());
- EXPECT_EQ(100, response->headers->GetContentLength());
+ EXPECT_EQ(5, response->headers->GetContentLength());
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
// The password prompt info should not be set.
EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+ trans.reset();
+ session->FlushSocketPools();
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
@@ -1676,8 +1726,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://www.google.com/");
@@ -1704,6 +1752,8 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1730,8 +1780,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://www.google.com/");
@@ -1765,6 +1813,8 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1789,8 +1839,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://www.google.com/");
@@ -1834,6 +1882,8 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1880,8 +1930,6 @@ void HttpNetworkTransactionTest::ConnectStatusHelperWithExpectedStatus(
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
@@ -1907,6 +1955,8 @@ void HttpNetworkTransactionTest::ConnectStatusHelperWithExpectedStatus(
TestCompletionCallback callback;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -2227,8 +2277,7 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) {
HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom1,
MockGetHostName);
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -2309,6 +2358,8 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -2354,8 +2405,7 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) {
HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom2,
MockGetHostName);
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
HttpRequestInfo request;
request.method = "GET";
@@ -2487,6 +2537,8 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -5134,8 +5186,7 @@ TEST_F(HttpNetworkTransactionTest, ConnectionClosedAfterStartOfHeaders) {
// restart does the right thing.
TEST_F(HttpNetworkTransactionTest, DrainResetOK) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
HttpRequestInfo request;
request.method = "GET";
@@ -5184,6 +5235,8 @@ TEST_F(HttpNetworkTransactionTest, DrainResetOK) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -5488,8 +5541,6 @@ TEST_F(HttpNetworkTransactionTest, UnreadableUploadFileAfterAuthRestart) {
// Tests that changes to Auth realms are treated like auth rejections.
TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) {
SessionDependencies session_deps;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
HttpRequestInfo request;
request.method = "GET";
@@ -5552,8 +5603,9 @@ TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) {
MockRead data_reads4[] = {
MockRead("HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
- "Content-Length: 100\r\n"
- "\r\n"),
+ "Content-Length: 5\r\n"
+ "\r\n"
+ "hello"),
};
StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
@@ -5571,6 +5623,9 @@ TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
// Issue the first request with Authorize headers. There should be a
// password prompt for first_realm waiting to be filled in after the
// transaction completes.
@@ -6195,9 +6250,9 @@ TEST_F(HttpNetworkTransactionTest,
// specifies both the configuration for the test as well as the expectations
// for the results.
TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
- const char* kServer = "http://www.example.com";
- const char* kSecureServer = "https://www.example.com";
- const char* kProxy = "myproxy:70";
+ static const char kServer[] = "http://www.example.com";
+ static const char kSecureServer[] = "https://www.example.com";
+ static const char kProxy[] = "myproxy:70";
const int kAuthErr = ERR_INVALID_AUTH_CREDENTIALS;
enum AuthTiming {
@@ -6482,7 +6537,6 @@ TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
};
SessionDependencies session_deps;
- scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
HttpAuthHandlerMock::Factory* auth_factory(
new HttpAuthHandlerMock::Factory());
session_deps.http_auth_handler_factory.reset(auth_factory);
@@ -6529,8 +6583,8 @@ TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
request.url = GURL(test_config.server_url);
request.load_flags = 0;
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(CreateSession(&session_deps)));
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+ HttpNetworkTransaction trans(CreateSession(&session_deps));
for (int round = 0; round < test_config.num_auth_rounds; ++round) {
const TestRound& read_write_round = test_config.rounds[round];
@@ -6565,16 +6619,16 @@ TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
TestCompletionCallback callback;
int rv;
if (round == 0) {
- rv = trans->Start(&request, &callback, BoundNetLog());
+ rv = trans.Start(&request, &callback, BoundNetLog());
} else {
- rv = trans->RestartWithAuth(kFoo, kBar, &callback);
+ rv = trans.RestartWithAuth(kFoo, kBar, &callback);
}
if (rv == ERR_IO_PENDING)
rv = callback.WaitForResult();
// Compare results with expected data.
EXPECT_EQ(read_write_round.expected_rv, rv);
- const HttpResponseInfo* response = trans->GetResponseInfo();
+ const HttpResponseInfo* response = trans.GetResponseInfo();
if (read_write_round.expected_rv == OK) {
EXPECT_FALSE(response == NULL);
} else {
@@ -6589,9 +6643,6 @@ TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
}
}
}
-
- // Flush the idle socket before the HttpNetworkTransaction goes out of scope.
- session->FlushSocketPools();
}
TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) {
@@ -7120,8 +7171,6 @@ TEST_F(HttpNetworkTransactionTest, ProxyGet) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://www.google.com/");
@@ -7145,6 +7194,8 @@ TEST_F(HttpNetworkTransactionTest, ProxyGet) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -7168,8 +7219,6 @@ TEST_F(HttpNetworkTransactionTest, ProxyTunnelGet) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
@@ -7202,6 +7251,8 @@ TEST_F(HttpNetworkTransactionTest, ProxyTunnelGet) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -7233,8 +7284,6 @@ TEST_F(HttpNetworkTransactionTest, ProxyTunnelGetHangup) {
session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
- scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
-
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
@@ -7264,6 +7313,8 @@ TEST_F(HttpNetworkTransactionTest, ProxyTunnelGetHangup) {
TestCompletionCallback callback1;
+ scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
diff --git a/net/http/http_response_body_drainer.cc b/net/http/http_response_body_drainer.cc
new file mode 100644
index 0000000..599c534
--- /dev/null
+++ b/net/http/http_response_body_drainer.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_response_body_drainer.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_stream.h"
+
+namespace net {
+
+HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
+ : stream_(stream),
+ next_state_(STATE_NONE),
+ total_read_(0),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &HttpResponseBodyDrainer::OnIOComplete)),
+ user_callback_(NULL) {}
+
+HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
+
+void HttpResponseBodyDrainer::Start() {
+ read_buf_ = new IOBuffer(kDrainBodyBufferSize);
+ next_state_ = STATE_DRAIN_RESPONSE_BODY;
+ int rv = DoLoop(OK);
+
+ if (rv == ERR_IO_PENDING) {
+ timer_.Start(base::TimeDelta::FromSeconds(kTimeoutInSeconds),
+ this,
+ &HttpResponseBodyDrainer::OnTimerFired);
+ return;
+ }
+
+ Finish(rv);
+}
+
+int HttpResponseBodyDrainer::DoLoop(int result) {
+ DCHECK_NE(next_state_, STATE_NONE);
+
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_DRAIN_RESPONSE_BODY:
+ DCHECK_EQ(OK, rv);
+ rv = DoDrainResponseBody();
+ break;
+ case STATE_DRAIN_RESPONSE_BODY_COMPLETE:
+ rv = DoDrainResponseBodyComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_UNEXPECTED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+ return rv;
+}
+
+int HttpResponseBodyDrainer::DoDrainResponseBody() {
+ next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
+
+ return stream_->ReadResponseBody(
+ read_buf_, kDrainBodyBufferSize - total_read_,
+ &io_callback_);
+}
+
+int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ if (result < 0)
+ return result;
+
+ if (result == 0)
+ return ERR_CONNECTION_CLOSED;
+
+ total_read_ += result;
+ if (stream_->IsResponseBodyComplete())
+ return OK;
+
+ DCHECK_LE(total_read_, kDrainBodyBufferSize);
+ if (total_read_ >= kDrainBodyBufferSize)
+ return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
+
+ next_state_ = STATE_DRAIN_RESPONSE_BODY;
+ return OK;
+}
+
+void HttpResponseBodyDrainer::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING) {
+ timer_.Stop();
+ Finish(rv);
+ }
+}
+
+void HttpResponseBodyDrainer::OnTimerFired() {
+ Finish(ERR_TIMED_OUT);
+}
+
+void HttpResponseBodyDrainer::Finish(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+
+ if (result < 0) {
+ stream_->Close(true /* no keep-alive */);
+ } else {
+ DCHECK_EQ(OK, result);
+ stream_->Close(false /* keep-alive */);
+ }
+
+ delete this;
+}
+
+} // namespace net
diff --git a/net/http/http_response_body_drainer.h b/net/http/http_response_body_drainer.h
new file mode 100644
index 0000000..dbcdd99
--- /dev/null
+++ b/net/http/http_response_body_drainer.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP_HTTP_RESPONSE_BODY_DRAINER_H_
+#define NET_HTTP_HTTP_RESPONSE_BODY_DRAINER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/timer.h"
+#include "net/base/completion_callback.h"
+
+namespace net {
+
+class HttpStream;
+class IOBuffer;
+
+class HttpResponseBodyDrainer {
+ public:
+ // The size in bytes of the buffer we use to drain the response body that
+ // we want to throw away. The response body is typically a small page just a
+ // few hundred bytes long. We set a limit to prevent it from taking too long,
+ // since we may as well just create a new socket then.
+ enum { kDrainBodyBufferSize = 16384 };
+ enum { kTimeoutInSeconds = 5 };
+
+ explicit HttpResponseBodyDrainer(HttpStream* stream);
+
+ // Starts reading the body until completion, or we hit the buffer limit, or we
+ // timeout. After Start(), |this| will eventually delete itself.
+ void Start();
+
+ private:
+ enum State {
+ STATE_DRAIN_RESPONSE_BODY,
+ STATE_DRAIN_RESPONSE_BODY_COMPLETE,
+ STATE_NONE,
+ };
+
+ ~HttpResponseBodyDrainer();
+
+ int DoLoop(int result);
+
+ int DoDrainResponseBody();
+ int DoDrainResponseBodyComplete(int result);
+
+ void OnIOComplete(int result);
+ void OnTimerFired();
+ void Finish(int result);
+
+ const scoped_ptr<HttpStream> stream_;
+ State next_state_;
+ scoped_refptr<IOBuffer> read_buf_;
+ int total_read_;
+ CompletionCallbackImpl<HttpResponseBodyDrainer> io_callback_;
+ CompletionCallback* user_callback_;
+ base::OneShotTimer<HttpResponseBodyDrainer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpResponseBodyDrainer);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_RESPONSE_BODY_DRAINER_H_
diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc
new file mode 100644
index 0000000..b8e0934
--- /dev/null
+++ b/net/http/http_response_body_drainer_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_response_body_drainer.h"
+
+#include <cstring>
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const int kMagicChunkSize = 1024;
+COMPILE_ASSERT(
+ (HttpResponseBodyDrainer::kDrainBodyBufferSize % kMagicChunkSize) == 0,
+ chunk_size_needs_to_divide_evenly_into_buffer_size);
+
+class CloseResultWaiter {
+ public:
+ CloseResultWaiter()
+ : result_(false),
+ have_result_(false),
+ waiting_for_result_(false) {}
+
+ int WaitForResult() {
+ DCHECK(!waiting_for_result_);
+ while (!have_result_) {
+ waiting_for_result_ = true;
+ MessageLoop::current()->Run();
+ waiting_for_result_ = false;
+ }
+ return result_;
+ }
+
+ void set_result(bool result) {
+ result_ = result;
+ have_result_ = true;
+ if (waiting_for_result_)
+ MessageLoop::current()->Quit();
+ }
+
+ private:
+ int result_;
+ bool have_result_;
+ bool waiting_for_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter);
+};
+
+class MockHttpStream : public HttpStream {
+ public:
+ MockHttpStream(CloseResultWaiter* result_waiter)
+ : result_waiter_(result_waiter),
+ user_callback_(NULL),
+ closed_(false),
+ stall_reads_forever_(false),
+ num_chunks_(0),
+ is_complete_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
+ virtual ~MockHttpStream() {}
+
+ // HttpStream implementation:
+ virtual int InitializeStream(const HttpRequestInfo* request_info,
+ const BoundNetLog& net_log,
+ CompletionCallback* callback) {
+ return ERR_UNEXPECTED;
+ }
+ virtual int SendRequest(const std::string& request_headers,
+ UploadDataStream* request_body,
+ HttpResponseInfo* response,
+ CompletionCallback* callback) {
+ return ERR_UNEXPECTED;
+ }
+ virtual uint64 GetUploadProgress() const { return 0; }
+ virtual int ReadResponseHeaders(CompletionCallback* callback) {
+ return ERR_UNEXPECTED;
+ }
+ virtual const HttpResponseInfo* GetResponseInfo() const { return NULL; }
+
+ virtual bool CanFindEndOfResponse() const { return true; }
+ virtual bool IsMoreDataBuffered() const { return false; }
+ virtual bool IsConnectionReused() const { return false; }
+ virtual void SetConnectionReused() {}
+ virtual void GetSSLInfo(SSLInfo* ssl_info) {}
+ virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) {}
+
+ // Mocked API
+ virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback);
+ virtual void Close(bool not_reusable) {
+ DCHECK(!closed_);
+ closed_ = true;
+ result_waiter_->set_result(not_reusable);
+ }
+ virtual bool IsResponseBodyComplete() const { return is_complete_; }
+
+ // Methods to tweak/observer mock behavior:
+ void StallReadsForever() { stall_reads_forever_ = true; }
+
+ void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; }
+
+ private:
+ void CompleteRead();
+
+ bool closed() const { return closed_; }
+
+ CloseResultWaiter* const result_waiter_;
+ scoped_refptr<IOBuffer> user_buf_;
+ CompletionCallback* user_callback_;
+ bool closed_;
+ bool stall_reads_forever_;
+ int num_chunks_;
+ bool is_complete_;
+ ScopedRunnableMethodFactory<MockHttpStream> method_factory_;
+};
+
+int MockHttpStream::ReadResponseBody(
+ IOBuffer* buf, int buf_len, CompletionCallback* callback) {
+ DCHECK(callback);
+ DCHECK(!user_callback_);
+ DCHECK(buf);
+
+ if (stall_reads_forever_)
+ return ERR_IO_PENDING;
+
+ if (num_chunks_ == 0)
+ return ERR_UNEXPECTED;
+
+ if (buf_len > kMagicChunkSize && num_chunks_ > 1) {
+ user_buf_ = buf;
+ user_callback_ = callback;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(&MockHttpStream::CompleteRead));
+ return ERR_IO_PENDING;
+ }
+
+ num_chunks_--;
+ if (!num_chunks_)
+ is_complete_ = true;
+
+ return buf_len;
+}
+
+void MockHttpStream::CompleteRead() {
+ CompletionCallback* callback = user_callback_;
+ std::memset(user_buf_->data(), 1, kMagicChunkSize);
+ user_buf_ = NULL;
+ user_callback_ = NULL;
+ num_chunks_--;
+ if (!num_chunks_)
+ is_complete_ = true;
+ callback->Run(kMagicChunkSize);
+}
+
+class HttpResponseBodyDrainerTest : public testing::Test {
+ protected:
+ HttpResponseBodyDrainerTest()
+ : mock_stream_(new MockHttpStream(&result_waiter_)),
+ drainer_(new HttpResponseBodyDrainer(mock_stream_)) {}
+ ~HttpResponseBodyDrainerTest() {}
+
+ CloseResultWaiter result_waiter_;
+ MockHttpStream* const mock_stream_; // Owned by |drainer_|.
+ HttpResponseBodyDrainer* const drainer_; // Deletes itself.
+};
+
+TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) {
+ mock_stream_->set_num_chunks(1);
+ drainer_->Start();
+ EXPECT_FALSE(result_waiter_.WaitForResult());
+}
+
+TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) {
+ mock_stream_->set_num_chunks(3);
+ drainer_->Start();
+ EXPECT_FALSE(result_waiter_.WaitForResult());
+}
+
+TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
+ mock_stream_->set_num_chunks(
+ HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize);
+ drainer_->Start();
+ EXPECT_FALSE(result_waiter_.WaitForResult());
+}
+
+TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) {
+ mock_stream_->set_num_chunks(2);
+ mock_stream_->StallReadsForever();
+ drainer_->Start();
+ EXPECT_TRUE(result_waiter_.WaitForResult());
+}
+
+TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) {
+ TestCompletionCallback callback;
+ int too_many_chunks =
+ HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize;
+ too_many_chunks += 1; // Now it's too large.
+
+ mock_stream_->set_num_chunks(too_many_chunks);
+ drainer_->Start();
+ EXPECT_TRUE(result_waiter_.WaitForResult());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 83c17ab..4b52cc3 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -9,11 +9,15 @@
#include <algorithm>
+#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
#include "base/string_util.h"
+#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "net/http/http_response_body_drainer.h"
+#include "net/http/http_stream.h"
using std::string;
@@ -695,4 +699,10 @@ bool HttpUtil::ValuesIterator::GetNext() {
return false;
}
+void HttpUtil::DrainStreamBodyAndClose(HttpStream* stream) {
+ HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(stream);
+ drainer->Start();
+ // |drainer| will delete itself.
+}
+
} // namespace net
diff --git a/net/http/http_util.h b/net/http/http_util.h
index 33da33d..3179782 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -10,6 +10,7 @@
#include "base/string_tokenizer.h"
#include "googleurl/src/gurl.h"
+#include "net/base/completion_callback.h"
#include "net/http/http_byte_range.h"
// This is a macro to support extending this string literal at compile time.
@@ -18,6 +19,8 @@
namespace net {
+class HttpStream;
+
class HttpUtil {
public:
// Returns the absolute path of the URL, to be used for the http request.
@@ -247,6 +250,10 @@ class HttpUtil {
std::string::const_iterator value_begin_;
std::string::const_iterator value_end_;
};
+
+ // Attempts to read all of the response body of |stream|. Closes |stream| and
+ // deletes it when complete.
+ static void DrainStreamBodyAndClose(HttpStream* stream);
};
} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index 10a4d8d..080b5b0 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -429,6 +429,8 @@
'http/http_request_headers.cc',
'http/http_request_headers.h',
'http/http_request_info.h',
+ 'http/http_response_body_drainer.cc',
+ 'http/http_response_body_drainer.h',
'http/http_response_headers.cc',
'http/http_response_headers.h',
'http/http_response_info.cc',
@@ -806,6 +808,7 @@
'http/http_network_transaction_unittest.cc',
'http/http_proxy_client_socket_pool_unittest.cc',
'http/http_request_headers_unittest.cc',
+ 'http/http_response_body_drainer_unittest.cc',
'http/http_response_headers_unittest.cc',
'http/http_transaction_unittest.cc',
'http/http_transaction_unittest.h',
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 6d5d644..927d2aa 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -2296,18 +2296,15 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
"301 Moved Permanently", "http://www.foo.com/index.php",
"http://www.foo.com/index.php"));
scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
- scoped_ptr<spdy::SpdyFrame> res(
- ConstructSpdyRstStream(2, spdy::CANCEL));
MockWrite writes[] = {
CreateMockWrite(*req, 1),
- CreateMockWrite(*res, 6),
};
MockRead reads[] = {
CreateMockRead(*resp, 2),
CreateMockRead(*rep, 3),
CreateMockRead(*body, 4),
MockRead(true, ERR_IO_PENDING, 5), // Force a pause
- MockRead(true, 0, 0, 7) // EOF
+ MockRead(true, 0, 0, 6) // EOF
};
// Setup writes/reads to www.foo.com