#include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_file_util.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/host_cache.h"
#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_digest.h"
#include "net/http/http_auth_handler_mock.h"
#include "net/http/http_auth_handler_ntlm.h"
#include "net/http/http_server_properties_impl.h"
#include "net/http/http_stream.h"
#include "net/http/http_stream_factory.h"
-#include "net/http/http_transaction_unittest.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_resolver.h"
#include "net/ssl/ssl_config_service_defaults.h"
#include "net/ssl/ssl_info.h"
#include "net/test/cert_test_util.h"
+#include "net/websockets/websocket_handshake_stream_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "url/gurl.h"
+using base::ASCIIToUTF16;
+
//-----------------------------------------------------------------------------
namespace {
return false;
std::string double_quote_headers;
base::JSONWriter::Write(header_list, &double_quote_headers);
- ReplaceChars(double_quote_headers, "\"", "'", headers);
+ base::ReplaceChars(double_quote_headers, "\"", "'", headers);
return true;
}
int rv;
std::string status_line;
std::string response_data;
+ int64 totalReceivedBytes;
LoadTimingInfo load_timing_info;
};
PlatformTest::TearDown();
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
base::MessageLoop::current()->RunUntilIdle();
- HttpStreamFactory::set_use_alternate_protocols(false);
- HttpStreamFactory::SetNextProtos(std::vector<NextProto>());
}
// This is the expected return from a current server advertising SPDY.
void KeepAliveConnectionResendRequestTest(const MockWrite* write_failure,
const MockRead* read_failure);
+ // Either |write_failure| specifies a write failure or |read_failure|
+ // specifies a read failure when using a reused socket. In either case, the
+ // failure should cause the network transaction to resend the request, and the
+ // other argument should be NULL.
+ void PreconnectErrorResendRequestTest(const MockWrite* write_failure,
+ const MockRead* read_failure,
+ bool use_spdy);
+
SimpleGetHelperResult SimpleGetHelperForData(StaticSocketDataProvider* data[],
size_t data_count) {
SimpleGetHelperResult out;
TestCompletionCallback callback;
- EXPECT_TRUE(log.bound().IsLoggingAllEvents());
+ EXPECT_TRUE(log.bound().IsLogging());
int rv = trans->Start(&request, callback.callback(), log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ("['Host: www.google.com','Connection: keep-alive']",
response_headers);
+ out.totalReceivedBytes = trans->GetTotalReceivedBytes();
return out;
}
return SimpleGetHelperForData(data, 1);
}
+ int64 ReadsSize(MockRead data_reads[], size_t reads_count) {
+ int64 size = 0;
+ for (size_t i = 0; i < reads_count; ++i)
+ size += data_reads[i].data_len;
+ return size;
+ }
+
void ConnectStatusHelperWithExpectedStatus(const MockRead& status,
int expected_status);
NextProto,
HttpNetworkTransactionTest,
testing::Values(kProtoDeprecatedSPDY2,
- kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
- kProtoHTTP2Draft04));
+ kProtoSPDY3, kProtoSPDY31, kProtoSPDY4));
namespace {
+class BeforeNetworkStartHandler {
+ public:
+ explicit BeforeNetworkStartHandler(bool defer)
+ : defer_on_before_network_start_(defer),
+ observed_before_network_start_(false) {}
+
+ void OnBeforeNetworkStart(bool* defer) {
+ *defer = defer_on_before_network_start_;
+ observed_before_network_start_ = true;
+ }
+
+ bool observed_before_network_start() const {
+ return observed_before_network_start_;
+ }
+
+ private:
+ const bool defer_on_before_network_start_;
+ bool observed_before_network_start_;
+
+ DISALLOW_COPY_AND_ASSIGN(BeforeNetworkStartHandler);
+};
+
+class BeforeProxyHeadersSentHandler {
+ public:
+ BeforeProxyHeadersSentHandler()
+ : observed_before_proxy_headers_sent_(false) {}
+
+ void OnBeforeProxyHeadersSent(const ProxyInfo& proxy_info,
+ HttpRequestHeaders* request_headers) {
+ observed_before_proxy_headers_sent_ = true;
+ observed_proxy_server_uri_ = proxy_info.proxy_server().ToURI();
+ }
+
+ bool observed_before_proxy_headers_sent() const {
+ return observed_before_proxy_headers_sent_;
+ }
+
+ std::string observed_proxy_server_uri() const {
+ return observed_proxy_server_uri_;
+ }
+
+ private:
+ bool observed_before_proxy_headers_sent_;
+ std::string observed_proxy_server_uri_;
+
+ DISALLOW_COPY_AND_ASSIGN(BeforeProxyHeadersSentHandler);
+};
+
// Fill |str| with a long header list that consumes >= |size| bytes.
void FillLargeHeadersString(std::string* str, int size) {
const char* row =
cert_verifier,
NULL,
NULL,
+ NULL,
std::string(),
NULL,
NULL,
NULL,
NULL,
NULL,
- NULL) {}
+ false,
+ NULL) {
+}
//-----------------------------------------------------------------------------
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
EXPECT_EQ("hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Response with no status line.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Allow up to 4 bytes of junk to precede status line.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Allow up to 4 bytes of junk to precede status line.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Beyond 4 bytes of slop and it should fail to find a status line.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("xxxxxHTTP/1.1 404 Not Found\nServer: blah", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Same as StatusLineJunk4Bytes, except the read chunks are smaller.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
EXPECT_EQ("DATA", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Close the connection before enough bytes to have a status line.
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
EXPECT_EQ("HTT", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, out.totalReceivedBytes);
}
// Simulate a 204 response, lacking a Content-Length header, sent over a
// persistent connection. The response should still terminate since a 204
// cannot have a response body.
TEST_P(HttpNetworkTransactionTest, StopsReading204) {
+ char junk[] = "junk";
MockRead data_reads[] = {
MockRead("HTTP/1.1 204 No Content\r\n\r\n"),
- MockRead("junk"), // Should not be read!!
+ MockRead(junk), // Should not be read!!
MockRead(SYNCHRONOUS, OK),
};
SimpleGetHelperResult out = SimpleGetHelper(data_reads,
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 204 No Content", out.status_line);
EXPECT_EQ("", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ int64 response_size = reads_size - strlen(junk);
+ EXPECT_EQ(response_size, out.totalReceivedBytes);
}
// A simple request using chunked encoding with some extra data after.
-// (Like might be seen in a pipelined response.)
TEST_P(HttpNetworkTransactionTest, ChunkedEncoding) {
+ std::string final_chunk = "0\r\n\r\n";
+ std::string extra_data = "HTTP/1.1 200 OK\r\n";
+ std::string last_read = final_chunk + extra_data;
MockRead data_reads[] = {
MockRead("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"),
MockRead("5\r\nHello\r\n"),
MockRead("1\r\n"),
MockRead(" \r\n"),
MockRead("5\r\nworld\r\n"),
- MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"),
+ MockRead(last_read.data()),
MockRead(SYNCHRONOUS, OK),
};
SimpleGetHelperResult out = SimpleGetHelper(data_reads,
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
EXPECT_EQ("Hello world", out.response_data);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ int64 response_size = reads_size - extra_data.size();
+ EXPECT_EQ(response_size, out.totalReceivedBytes);
}
// Next tests deal with http://crbug.com/56344.
std::string url;
EXPECT_TRUE(response->headers->IsRedirect(&url));
EXPECT_EQ("http://good.com/", url);
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
}
// Checks that two distinct Location headers result in an error.
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ BeforeProxyHeadersSentHandler proxy_headers_handler;
+ trans->SetBeforeProxyHeadersSentCallback(
+ base::Bind(&BeforeProxyHeadersSentHandler::OnBeforeProxyHeadersSent,
+ base::Unretained(&proxy_headers_handler)));
MockWrite data_writes1[] = {
MockWrite("HEAD / HTTP/1.1\r\n"
EXPECT_TRUE(response->headers.get() != NULL);
EXPECT_EQ(1234, response->headers->GetContentLength());
EXPECT_EQ("HTTP/1.1 404 Not Found", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
+ EXPECT_FALSE(proxy_headers_handler.observed_before_proxy_headers_sent());
std::string server_header;
void* iter = NULL;
EXPECT_TRUE(response->headers.get() != NULL);
EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->proxy_server.IsEmpty());
std::string response_data;
rv = ReadTransaction(trans.get(), &response_data);
};
if (write_failure) {
- ASSERT_TRUE(!read_failure);
+ ASSERT_FALSE(read_failure);
data1_writes[1] = *write_failure;
} else {
ASSERT_TRUE(read_failure);
}
}
+void HttpNetworkTransactionTest::PreconnectErrorResendRequestTest(
+ const MockWrite* write_failure,
+ const MockRead* read_failure,
+ bool use_spdy) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.foo.com/");
+ request.load_flags = 0;
+
+ CapturingNetLog net_log;
+ session_deps_.net_log = &net_log;
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ SSLSocketDataProvider ssl1(ASYNC, OK);
+ SSLSocketDataProvider ssl2(ASYNC, OK);
+ if (use_spdy) {
+ ssl1.SetNextProto(GetParam());
+ ssl2.SetNextProto(GetParam());
+ }
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl1);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2);
+
+ // SPDY versions of the request and response.
+ scoped_ptr<SpdyFrame> spdy_request(spdy_util_.ConstructSpdyGet(
+ request.url.spec().c_str(), false, 1, DEFAULT_PRIORITY));
+ scoped_ptr<SpdyFrame> spdy_response(
+ spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> spdy_data(
+ spdy_util_.ConstructSpdyBodyFrame(1, "hello", 5, true));
+
+ // HTTP/1.1 versions of the request and response.
+ const char kHttpRequest[] = "GET / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n\r\n";
+ const char kHttpResponse[] = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n";
+ const char kHttpData[] = "hello";
+
+ std::vector<MockRead> data1_reads;
+ std::vector<MockWrite> data1_writes;
+ if (write_failure) {
+ ASSERT_FALSE(read_failure);
+ data1_writes.push_back(*write_failure);
+ data1_reads.push_back(MockRead(ASYNC, OK));
+ } else {
+ ASSERT_TRUE(read_failure);
+ if (use_spdy) {
+ data1_writes.push_back(CreateMockWrite(*spdy_request));
+ } else {
+ data1_writes.push_back(MockWrite(kHttpRequest));
+ }
+ data1_reads.push_back(*read_failure);
+ }
+
+ StaticSocketDataProvider data1(&data1_reads[0], data1_reads.size(),
+ &data1_writes[0], data1_writes.size());
+ session_deps_.socket_factory->AddSocketDataProvider(&data1);
+
+ std::vector<MockRead> data2_reads;
+ std::vector<MockWrite> data2_writes;
+
+ if (use_spdy) {
+ data2_writes.push_back(CreateMockWrite(*spdy_request, 0, ASYNC));
+
+ data2_reads.push_back(CreateMockRead(*spdy_response, 1, ASYNC));
+ data2_reads.push_back(CreateMockRead(*spdy_data, 2, ASYNC));
+ data2_reads.push_back(MockRead(ASYNC, OK, 3));
+ } else {
+ data2_writes.push_back(
+ MockWrite(ASYNC, kHttpRequest, strlen(kHttpRequest), 0));
+
+ data2_reads.push_back(
+ MockRead(ASYNC, kHttpResponse, strlen(kHttpResponse), 1));
+ data2_reads.push_back(MockRead(ASYNC, kHttpData, strlen(kHttpData), 2));
+ data2_reads.push_back(MockRead(ASYNC, OK, 3));
+ }
+ OrderedSocketData data2(&data2_reads[0], data2_reads.size(),
+ &data2_writes[0], data2_writes.size());
+ session_deps_.socket_factory->AddSocketDataProvider(&data2);
+
+ // Preconnect a socket.
+ net::SSLConfig ssl_config;
+ session->ssl_config_service()->GetSSLConfig(&ssl_config);
+ session->GetNextProtos(&ssl_config.next_protos);
+ session->http_stream_factory()->PreconnectStreams(
+ 1, request, DEFAULT_PRIORITY, ssl_config, ssl_config);
+ // Wait for the preconnect to complete.
+ // TODO(davidben): Some way to wait for an idle socket count might be handy.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+
+ // Make the request.
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ LoadTimingInfo load_timing_info;
+ EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
+ TestLoadTimingNotReused(
+ load_timing_info,
+ CONNECT_TIMING_HAS_DNS_TIMES|CONNECT_TIMING_HAS_SSL_TIMES);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != 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(kHttpData, response_data);
+}
+
TEST_P(HttpNetworkTransactionTest,
KeepAliveConnectionNotConnectedOnWrite) {
MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
KeepAliveConnectionResendRequestTest(NULL, &read_failure);
}
+// Make sure that on a 408 response (Request Timeout), the request is retried,
+// if the socket was a reused keep alive socket.
+TEST_P(HttpNetworkTransactionTest, KeepAlive408) {
+ MockRead read_failure(SYNCHRONOUS,
+ "HTTP/1.1 408 Request Timeout\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 6\r\n\r\n"
+ "Pickle");
+ KeepAliveConnectionResendRequestTest(NULL, &read_failure);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PreconnectErrorNotConnectedOnWrite) {
+ MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+ PreconnectErrorResendRequestTest(&write_failure, NULL, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorReset) {
+ MockRead read_failure(ASYNC, ERR_CONNECTION_RESET);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorEOF) {
+ MockRead read_failure(SYNCHRONOUS, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest, PreconnectErrorAsyncEOF) {
+ MockRead read_failure(ASYNC, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+// Make sure that on a 408 response (Request Timeout), the request is retried,
+// if the socket was a preconnected (UNUSED_IDLE) socket.
+TEST_P(HttpNetworkTransactionTest, RetryOnIdle408) {
+ MockRead read_failure(SYNCHRONOUS,
+ "HTTP/1.1 408 Request Timeout\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 6\r\n\r\n"
+ "Pickle");
+ KeepAliveConnectionResendRequestTest(NULL, &read_failure);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, false);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ SpdyPreconnectErrorNotConnectedOnWrite) {
+ MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+ PreconnectErrorResendRequestTest(&write_failure, NULL, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorReset) {
+ MockRead read_failure(ASYNC, ERR_CONNECTION_RESET);
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorEOF) {
+ MockRead read_failure(SYNCHRONOUS, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
+TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorAsyncEOF) {
+ MockRead read_failure(ASYNC, OK); // EOF
+ PreconnectErrorResendRequestTest(NULL, &read_failure, true);
+}
+
TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) {
HttpRequestInfo request;
request.method = "GET";
EXPECT_EQ(ERR_EMPTY_RESPONSE, out.rv);
}
+// Test that network access can be deferred and resumed.
+TEST_P(HttpNetworkTransactionTest, ThrottleBeforeNetworkStart) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Defer on OnBeforeNetworkStart.
+ BeforeNetworkStartHandler net_start_handler(true); // defer
+ trans->SetBeforeNetworkStartCallback(
+ base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart,
+ base::Unretained(&net_start_handler)));
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Length: 5\r\n\r\n"),
+ MockRead("hello"),
+ MockRead(SYNCHRONOUS, 0),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Should have deferred for network start.
+ EXPECT_TRUE(net_start_handler.observed_before_network_start());
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState());
+ EXPECT_TRUE(trans->GetResponseInfo() == NULL);
+
+ trans->ResumeNetworkStart();
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(trans->GetResponseInfo() != NULL);
+
+ scoped_refptr<IOBufferWithSize> io_buf(new IOBufferWithSize(100));
+ rv = trans->Read(io_buf.get(), io_buf->size(), callback.callback());
+ if (rv == ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ EXPECT_EQ(5, rv);
+ trans.reset();
+}
+
+// Test that network use can be deferred and canceled.
+TEST_P(HttpNetworkTransactionTest, ThrottleAndCancelBeforeNetworkStart) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Defer on OnBeforeNetworkStart.
+ BeforeNetworkStartHandler net_start_handler(true); // defer
+ trans->SetBeforeNetworkStartCallback(
+ base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart,
+ base::Unretained(&net_start_handler)));
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Should have deferred for network start.
+ EXPECT_TRUE(net_start_handler.observed_before_network_start());
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState());
+ EXPECT_TRUE(trans->GetResponseInfo() == NULL);
+}
+
// Next 2 cases (KeepAliveEarlyClose and KeepAliveEarlyClose2) are regression
// tests. There was a bug causing HttpNetworkTransaction to hang in the
// destructor in such situations.
EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1));
TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES);
+ int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1));
+ EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes());
+
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
load_timing_info2.connect_timing.connect_start);
EXPECT_NE(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id);
+ int64 reads_size2 = ReadsSize(data_reads2, arraysize(data_reads2));
+ EXPECT_EQ(reads_size1 + reads_size2, trans->GetTotalReceivedBytes());
+
response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
rv = callback.WaitForResult();
EXPECT_EQ(0, rv);
+ int64 reads_size = ReadsSize(data_reads, arraysize(data_reads));
+ EXPECT_EQ(reads_size, trans->GetTotalReceivedBytes());
+
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
ASSERT_TRUE(response != NULL);
EXPECT_TRUE(response->auth_challenge.get() == NULL);
EXPECT_EQ(5, response->headers->GetContentLength());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1));
+ EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes());
}
// Test the request-challenge-retry sequence for basic auth, over a keep-alive
EXPECT_EQ(kUploadData, response_data);
}
+// Verifies that a session which races and wins against the owning transaction
+// (completing prior to host resolution), doesn't fail the transaction.
+// Regression test for crbug.com/334413.
+TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithSessionRace) {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ // Configure SPDY proxy server "proxy:70".
+ session_deps_.proxy_service.reset(
+ ProxyService::CreateFixed("https://proxy:70"));
+ CapturingBoundNetLog log;
+ session_deps_.net_log = log.bound().net_log();
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ // Fetch http://www.google.com/ through the SPDY proxy.
+ scoped_ptr<SpdyFrame> req(
+ spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false));
+ MockWrite spdy_writes[] = {CreateMockWrite(*req)};
+
+ scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<SpdyFrame> data(spdy_util_.ConstructSpdyBodyFrame(1, true));
+ MockRead spdy_reads[] = {
+ CreateMockRead(*resp), CreateMockRead(*data), MockRead(ASYNC, 0, 0),
+ };
+
+ DelayedSocketData spdy_data(
+ 1, // wait for one write to finish before reading.
+ spdy_reads,
+ arraysize(spdy_reads),
+ spdy_writes,
+ arraysize(spdy_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&spdy_data);
+
+ SSLSocketDataProvider ssl(ASYNC, OK);
+ ssl.SetNextProto(GetParam());
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ TestCompletionCallback callback1;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+
+ // Stall the hostname resolution begun by the transaction.
+ session_deps_.host_resolver->set_synchronous_mode(false);
+ session_deps_.host_resolver->set_ondemand_mode(true);
+
+ int rv = trans->Start(&request, callback1.callback(), log.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Race a session to the proxy, which completes first.
+ session_deps_.host_resolver->set_ondemand_mode(false);
+ SpdySessionKey key(
+ HostPortPair("proxy", 70), ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
+ base::WeakPtr<SpdySession> spdy_session =
+ CreateSecureSpdySession(session, key, log.bound());
+
+ // Unstall the resolution begun by the transaction.
+ session_deps_.host_resolver->set_ondemand_mode(true);
+ session_deps_.host_resolver->ResolveAllPending();
+
+ EXPECT_FALSE(callback1.have_result());
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ(kUploadData, response_data);
+}
+
// Test a SPDY get through an HTTPS Proxy.
TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) {
HttpRequestInfo request;
spdy_util_.ConstructSpdyWindowUpdate(1, wrapped_get_resp1->size()));
// CONNECT to news.google.com:443 via SPDY.
- const char* const kConnectHeaders2[] = {
- spdy_util_.GetMethodKey(), "CONNECT",
- spdy_util_.GetPathKey(), "news.google.com:443",
- spdy_util_.GetHostKey(), "news.google.com",
- spdy_util_.GetVersionKey(), "HTTP/1.1",
- };
+ SpdyHeaderBlock connect2_block;
+ connect2_block[spdy_util_.GetMethodKey()] = "CONNECT";
+ connect2_block[spdy_util_.GetPathKey()] = "news.google.com:443";
+ connect2_block[spdy_util_.GetHostKey()] = "news.google.com";
+ spdy_util_.MaybeAddVersionHeader(&connect2_block);
scoped_ptr<SpdyFrame> connect2(
- spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- /*compressed*/ false,
- 3,
- LOWEST,
- SYN_STREAM,
- CONTROL_FLAG_NONE,
- kConnectHeaders2,
- arraysize(kConnectHeaders2),
- 0));
+ spdy_util_.ConstructSpdySyn(3, connect2_block, LOWEST, false, false));
+
scoped_ptr<SpdyFrame> conn_resp2(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
// http://www.google.com/
scoped_ptr<SpdyHeaderBlock> headers(
spdy_util_.ConstructGetHeaderBlockForProxy("http://www.google.com/"));
- scoped_ptr<SpdyFrame> get1(spdy_util_.ConstructSpdyControlFrame(
- headers.Pass(), false, 1, LOWEST, SYN_STREAM, CONTROL_FLAG_FIN, 0));
+ scoped_ptr<SpdyFrame> get1(
+ spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true));
scoped_ptr<SpdyFrame> get_resp1(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<SpdyFrame> body1(
// http://news.google.com/
scoped_ptr<SpdyHeaderBlock> headers2(
spdy_util_.ConstructGetHeaderBlockForProxy("http://news.google.com/"));
- scoped_ptr<SpdyFrame> get2(spdy_util_.ConstructSpdyControlFrame(
- headers2.Pass(), false, 3, LOWEST, SYN_STREAM, CONTROL_FLAG_FIN, 0));
+ scoped_ptr<SpdyFrame> get2(
+ spdy_util_.ConstructSpdySyn(3, *headers2, LOWEST, false, true));
scoped_ptr<SpdyFrame> get_resp2(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
scoped_ptr<SpdyFrame> body2(
ConnectStatusHelper(MockRead("HTTP/1.1 307 Temporary Redirect\r\n"));
}
+TEST_P(HttpNetworkTransactionTest, ConnectStatus308) {
+ ConnectStatusHelper(MockRead("HTTP/1.1 308 Permanent Redirect\r\n"));
+}
+
TEST_P(HttpNetworkTransactionTest, ConnectStatus400) {
ConnectStatusHelper(MockRead("HTTP/1.1 400 Bad Request\r\n"));
}
// The proxy responds to the connect with a 407, using a persistent
// connection.
+ const char* const kAuthStatus = "407";
const char* const kAuthChallenge[] = {
- spdy_util_.GetStatusKey(), "407 Proxy Authentication Required",
- spdy_util_.GetVersionKey(), "HTTP/1.1",
"proxy-authenticate", "Basic realm=\"MyRealm1\"",
};
-
- scoped_ptr<SpdyFrame> conn_auth_resp(
- spdy_util_.ConstructSpdyControlFrame(NULL,
- 0,
- false,
- 1,
- LOWEST,
- SYN_REPLY,
- CONTROL_FLAG_NONE,
- kAuthChallenge,
- arraysize(kAuthChallenge),
- 0));
+ scoped_ptr<SpdyFrame> conn_auth_resp(spdy_util_.ConstructSpdySynReplyError(
+ kAuthStatus, kAuthChallenge, arraysize(kAuthChallenge) / 2, 1));
scoped_ptr<SpdyFrame> conn_resp(
spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3));
session->http_server_properties();
http_server_properties->SetAlternateProtocol(
HostPortPair("host.with.alternate", 80), 443,
- AlternateProtocolFromNextProto(next_proto));
+ AlternateProtocolFromNextProto(next_proto), 1);
return session;
}
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
new CaptureGroupNameTransportSocketPool(NULL, NULL);
CaptureGroupNameSSLSocketPool* ssl_conn_pool =
new CaptureGroupNameSSLSocketPool(NULL, NULL);
- MockClientSocketPoolManager* mock_pool_manager =
- new MockClientSocketPoolManager;
+ scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
+ new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
mock_pool_manager->SetSSLSocketPool(ssl_conn_pool);
- peer.SetClientSocketPoolManager(mock_pool_manager);
+ peer.SetClientSocketPoolManager(
+ mock_pool_manager.PassAs<ClientSocketPoolManager>());
EXPECT_EQ(ERR_IO_PENDING,
GroupNameTransactionHelper(tests[i].url, session));
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
CaptureGroupNameSSLSocketPool* ssl_conn_pool =
new CaptureGroupNameSSLSocketPool(NULL, NULL);
- MockClientSocketPoolManager* mock_pool_manager =
- new MockClientSocketPoolManager;
+ scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
+ new MockClientSocketPoolManager);
mock_pool_manager->SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool);
mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
- peer.SetClientSocketPoolManager(mock_pool_manager);
+ peer.SetClientSocketPoolManager(
+ mock_pool_manager.PassAs<ClientSocketPoolManager>());
EXPECT_EQ(ERR_IO_PENDING,
GroupNameTransactionHelper(tests[i].url, session));
},
};
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
session_deps_.proxy_service.reset(
CaptureGroupNameSSLSocketPool* ssl_conn_pool =
new CaptureGroupNameSSLSocketPool(NULL, NULL);
- MockClientSocketPoolManager* mock_pool_manager =
- new MockClientSocketPoolManager;
+ scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
+ new MockClientSocketPoolManager);
mock_pool_manager->SetSocketPoolForSOCKSProxy(proxy_host, socks_conn_pool);
mock_pool_manager->SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
- peer.SetClientSocketPoolManager(mock_pool_manager);
+ peer.SetClientSocketPoolManager(
+ mock_pool_manager.PassAs<ClientSocketPoolManager>());
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
TEST_P(HttpNetworkTransactionTest, UploadFileSmallerThanLength) {
base::FilePath temp_file_path;
- ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path));
+ ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path));
const uint64 kFakeSize = 100000; // file is actually blank
UploadFileElementReader::ScopedOverridingContentLengthForTests
overriding_content_length(kFakeSize);
TEST_P(HttpNetworkTransactionTest, UploadUnreadableFile) {
base::FilePath temp_file;
- ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file));
+ ASSERT_TRUE(base::CreateTemporaryFile(&temp_file));
std::string temp_file_content("Unreadable file.");
- ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_content.c_str(),
+ ASSERT_TRUE(base::WriteFile(temp_file, temp_file_content.c_str(),
temp_file_content.length()));
- ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file));
+ ASSERT_TRUE(base::MakeFileUnreadable(temp_file));
ScopedVector<UploadElementReader> element_readers;
element_readers.push_back(
request.upload_data_stream = &upload_data_stream;
request.load_flags = 0;
- // If we try to upload an unreadable file, the network stack should report
- // the file size as zero and upload zero bytes for that file.
+ // If we try to upload an unreadable file, the transaction should fail.
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
- MockRead data_reads[] = {
- MockRead("HTTP/1.0 200 OK\r\n\r\n"),
- MockRead(SYNCHRONOUS, OK),
- };
- MockWrite data_writes[] = {
- MockWrite("POST /upload HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n"
- "Content-Length: 0\r\n\r\n"),
- MockWrite(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
- arraysize(data_writes));
+ StaticSocketDataProvider data(NULL, 0, NULL, 0);
session_deps_.socket_factory->AddSocketDataProvider(&data);
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback.WaitForResult();
- EXPECT_EQ(OK, rv);
-
- const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response != NULL);
- EXPECT_TRUE(response->headers.get() != NULL);
- EXPECT_EQ("HTTP/1.0 200 OK", response->headers->GetStatusLine());
-
- base::DeleteFile(temp_file, false);
-}
-
-TEST_P(HttpNetworkTransactionTest, UnreadableUploadFileAfterAuthRestart) {
- base::FilePath temp_file;
- ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file));
- std::string temp_file_contents("Unreadable file.");
- std::string unreadable_contents(temp_file_contents.length(), '\0');
- ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_contents.c_str(),
- temp_file_contents.length()));
-
- ScopedVector<UploadElementReader> element_readers;
- element_readers.push_back(
- new UploadFileElementReader(base::MessageLoopProxy::current().get(),
- temp_file,
- 0,
- kuint64max,
- base::Time()));
- UploadDataStream upload_data_stream(element_readers.Pass(), 0);
-
- HttpRequestInfo request;
- request.method = "POST";
- request.url = GURL("http://www.google.com/upload");
- request.upload_data_stream = &upload_data_stream;
- request.load_flags = 0;
-
- scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
- scoped_ptr<HttpTransaction> trans(
- new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
-
- MockRead data_reads[] = {
- MockRead("HTTP/1.1 401 Unauthorized\r\n"),
- MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
- MockRead("Content-Length: 0\r\n\r\n"), // No response body.
-
- MockRead("HTTP/1.1 200 OK\r\n"),
- MockRead("Content-Length: 0\r\n\r\n"),
- MockRead(SYNCHRONOUS, OK),
- };
- MockWrite data_writes[] = {
- MockWrite("POST /upload HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n"
- "Content-Length: 16\r\n\r\n"),
- MockWrite(SYNCHRONOUS, temp_file_contents.c_str()),
-
- MockWrite("POST /upload HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n"
- "Content-Length: 0\r\n"
- "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
- MockWrite(SYNCHRONOUS, unreadable_contents.c_str(),
- temp_file_contents.length()),
- MockWrite(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
- arraysize(data_writes));
- session_deps_.socket_factory->AddSocketDataProvider(&data);
-
- TestCompletionCallback callback1;
-
- int rv = trans->Start(&request, callback1.callback(), BoundNetLog());
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- rv = callback1.WaitForResult();
- EXPECT_EQ(OK, rv);
+ EXPECT_EQ(ERR_ACCESS_DENIED, rv);
const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response != NULL);
- ASSERT_TRUE(response->headers.get() != NULL);
- EXPECT_EQ("HTTP/1.1 401 Unauthorized", response->headers->GetStatusLine());
- EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get()));
-
- // Now make the file unreadable and try again.
- ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file));
-
- TestCompletionCallback callback2;
-
- rv = trans->RestartWithAuth(
- AuthCredentials(kFoo, kBar), callback2.callback());
- EXPECT_EQ(ERR_IO_PENDING, rv);
-
- rv = callback2.WaitForResult();
- EXPECT_EQ(OK, rv);
-
- response = trans->GetResponseInfo();
- ASSERT_TRUE(response != NULL);
- EXPECT_TRUE(response->headers.get() != NULL);
- EXPECT_TRUE(response->auth_challenge.get() == NULL);
- EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_FALSE(response);
base::DeleteFile(temp_file, false);
}
}
TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.next_protos = SpdyNextProtos();
+ session_deps_.use_alternate_protocols = true;
std::string alternate_protocol_http_header =
GetAlternateProtocolHttpHeader();
EXPECT_EQ(ERR_IO_PENDING, rv);
HostPortPair http_host_port_pair("www.google.com", 80);
- const HttpServerProperties& http_server_properties =
+ HttpServerProperties& http_server_properties =
*session->http_server_properties();
EXPECT_FALSE(
http_server_properties.HasAlternateProtocol(http_host_port_pair));
EXPECT_EQ("hello world", response_data);
ASSERT_TRUE(http_server_properties.HasAlternateProtocol(http_host_port_pair));
- const PortAlternateProtocolPair alternate =
+ const AlternateProtocolInfo alternate =
http_server_properties.GetAlternateProtocol(http_host_port_pair);
- PortAlternateProtocolPair expected_alternate;
- expected_alternate.port = 443;
- expected_alternate.protocol = AlternateProtocolFromNextProto(GetParam());
+ AlternateProtocolInfo expected_alternate(
+ 443, AlternateProtocolFromNextProto(GetParam()), 1);
EXPECT_TRUE(expected_alternate.Equals(alternate));
}
TEST_P(HttpNetworkTransactionTest,
MarkBrokenAlternateProtocolAndFallback) {
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo request;
request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(request.url),
666 /* port is ignored by MockConnect anyway */,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
ASSERT_TRUE(http_server_properties->HasAlternateProtocol(
HostPortPair::FromURL(request.url)));
- const PortAlternateProtocolPair alternate =
+ const AlternateProtocolInfo alternate =
http_server_properties->GetAlternateProtocol(
HostPortPair::FromURL(request.url));
EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo restricted_port_request;
restricted_port_request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(restricted_port_request.url),
kUnrestrictedAlternatePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
// on a restricted port (port < 1024) if we set
// enable_user_alternate_protocol_ports.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
session_deps_.enable_user_alternate_protocol_ports = true;
HttpRequestInfo restricted_port_request;
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(restricted_port_request.url),
kUnrestrictedAlternatePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo restricted_port_request;
restricted_port_request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(restricted_port_request.url),
kRestrictedAlternatePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo unrestricted_port_request;
unrestricted_port_request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(unrestricted_port_request.url),
kRestrictedAlternatePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
// protocol to an unrestricted (port >= 1024) when the original traffic was
// on a restricted port (port < 1024). Ensure that we can redirect in all
// other cases.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo unrestricted_port_request;
unrestricted_port_request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(unrestricted_port_request.url),
kUnrestrictedAlternatePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
EXPECT_EQ(OK, callback.WaitForResult());
}
-TEST_P(HttpNetworkTransactionTest,
- AlternateProtocolUnsafeBlocked) {
+TEST_P(HttpNetworkTransactionTest, AlternateProtocolUnsafeBlocked) {
// Ensure that we're not allowed to redirect traffic via an alternate
// protocol to an unsafe port, and that we resume the second
// HttpStreamFactoryImpl::Job once the alternate protocol request fails.
- HttpStreamFactory::set_use_alternate_protocols(true);
+ session_deps_.use_alternate_protocols = true;
HttpRequestInfo request;
request.method = "GET";
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(request.url),
kUnsafePort,
- AlternateProtocolFromNextProto(GetParam()));
+ AlternateProtocolFromNextProto(GetParam()), 1);
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
EXPECT_EQ(OK, callback.WaitForResult());
// Disable alternate protocol before the asserts.
- HttpStreamFactory::set_use_alternate_protocols(false);
+ // HttpStreamFactory::set_use_alternate_protocols(false);
const HttpResponseInfo* response = trans->GetResponseInfo();
ASSERT_TRUE(response != NULL);
}
TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
}
TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
}
TEST_P(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
TEST_P(HttpNetworkTransactionTest,
UseAlternateProtocolForTunneledNpnSpdy) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
ProxyConfig proxy_config;
proxy_config.set_auto_detect(true);
TEST_P(HttpNetworkTransactionTest,
UseAlternateProtocolForNpnSpdyWithExistingSpdySession) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
// Set up an initial SpdySession in the pool to reuse.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> spdy_session =
CreateSecureSpdySession(session, key, BoundNetLog());
HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
std::string auth_challenge = "Mock realm=proxy";
GURL origin(test_config.proxy_url);
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_PROXY,
origin, BoundNetLog());
auth_handler->SetGenerateExpectation(
HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
std::string auth_challenge = "Mock realm=server";
GURL origin(test_config.server_url);
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
origin, BoundNetLog());
auth_handler->SetGenerateExpectation(
auth_handler->set_connection_based(true);
std::string auth_challenge = "Mock realm=server";
GURL origin("http://www.example.com");
- HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
- auth_challenge.end());
+ HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(),
+ auth_challenge.end());
auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
origin, BoundNetLog());
auth_factory->AddMockHandler(auth_handler, HttpAuth::AUTH_SERVER);
session_deps_.host_resolver.get(),
session_deps_.socket_factory.get(),
session_deps_.net_log);
- MockClientSocketPoolManager* mock_pool_manager =
- new MockClientSocketPoolManager;
+ scoped_ptr<MockClientSocketPoolManager> mock_pool_manager(
+ new MockClientSocketPoolManager);
mock_pool_manager->SetTransportSocketPool(transport_pool);
- session_peer.SetClientSocketPoolManager(mock_pool_manager);
+ session_peer.SetClientSocketPoolManager(
+ mock_pool_manager.PassAs<ClientSocketPoolManager>());
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
// This tests the case that a request is issued via http instead of spdy after
// npn is negotiated.
TEST_P(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- std::vector<NextProto> next_protos;
+ session_deps_.use_alternate_protocols = true;
+ NextProtoVector next_protos;
next_protos.push_back(kProtoHTTP11);
- HttpStreamFactory::SetNextProtos(next_protos);
+ session_deps_.next_protos = next_protos;
+
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("https://www.google.com/");
// Simulate the SSL handshake completing with an NPN negotiation
// followed by an immediate server closing of the socket.
// Fix crash: http://crbug.com/46369
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
HttpRequestInfo request;
request.method = "GET";
TEST_P(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) {
// This test ensures that the URL passed into the proxy is upgraded
// to https when doing an Alternate Protocol upgrade.
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
session_deps_.proxy_service.reset(
ProxyService::CreateFixedFromPacResult("PROXY myproxy:70"));
base::MessageLoop::current()->RunUntilIdle();
}
+// Test that if a transaction is cancelled after receiving the headers, the
+// stream is drained properly and added back to the socket pool. The main
+// purpose of this test is to make sure that an HttpStreamParser can be read
+// from after the HttpNetworkTransaction and the objects it owns have been
+// deleted.
+// See http://crbug.com/368418
+TEST_P(HttpNetworkTransactionTest, CancelAfterHeaders) {
+ MockRead data_reads[] = {
+ MockRead(ASYNC, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, "Content-Length: 2\r\n"),
+ MockRead(ASYNC, "Connection: Keep-Alive\r\n\r\n"),
+ MockRead(ASYNC, "1"),
+ // 2 async reads are necessary to trigger a ReadResponseBody call after the
+ // HttpNetworkTransaction has been deleted.
+ MockRead(ASYNC, "2"),
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING), // Should never read this.
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ {
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ HttpNetworkTransaction trans(DEFAULT_PRIORITY, session);
+ TestCompletionCallback callback;
+
+ int rv = trans.Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ callback.WaitForResult();
+
+ const HttpResponseInfo* response = trans.GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ // The transaction and HttpRequestInfo are deleted.
+ }
+
+ // Let the HttpResponseBodyDrainer drain the socket.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Socket should now be idle, waiting to be reused.
+ EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session));
+}
+
// Test a basic GET request through a proxy.
TEST_P(HttpNetworkTransactionTest, ProxyGet) {
session_deps_.proxy_service.reset(
scoped_ptr<HttpTransaction> trans(
new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
+ BeforeProxyHeadersSentHandler proxy_headers_handler;
+ trans->SetBeforeProxyHeadersSentCallback(
+ base::Bind(&BeforeProxyHeadersSentHandler::OnBeforeProxyHeadersSent,
+ base::Unretained(&proxy_headers_handler)));
int rv = trans->Start(&request, callback1.callback(), log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(200, response->headers->response_code());
EXPECT_EQ(100, response->headers->GetContentLength());
EXPECT_TRUE(response->was_fetched_via_proxy);
+ EXPECT_TRUE(
+ response->proxy_server.Equals(HostPortPair::FromString("myproxy:70")));
+ EXPECT_TRUE(proxy_headers_handler.observed_before_proxy_headers_sent());
+ EXPECT_EQ("myproxy:70", proxy_headers_handler.observed_proxy_server_uri());
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
LoadTimingInfo load_timing_info;
EXPECT_EQ(100, response->headers->GetContentLength());
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
EXPECT_TRUE(response->was_fetched_via_proxy);
+ EXPECT_TRUE(
+ response->proxy_server.Equals(HostPortPair::FromString("myproxy:70")));
LoadTimingInfo load_timing_info;
EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info));
// Set up an initial SpdySession in the pool to reuse.
HostPortPair host_port_pair("www.google.com", 443);
SpdySessionKey key(host_port_pair, ProxyServer::Direct(),
- kPrivacyModeDisabled);
+ PRIVACY_MODE_DISABLED);
base::WeakPtr<SpdySession> spdy_session =
CreateInsecureSpdySession(session, key, BoundNetLog());
request_info.load_flags = net::LOAD_NORMAL;
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "www.example.com:443";
+ cert_request->host_and_port = HostPortPair("www.example.com", 443);
// [ssl_]data1 contains the data for the first SSL handshake. When a
// CertificateRequest is received for the first time, the handshake will
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Restart the handshake. This will consume ssl_data2, which fails, and
// Ensure that the client certificate is removed from the cache on a
// handshake failure.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
// Ensure that a client certificate is removed from the SSL client auth
request_info.load_flags = net::LOAD_NORMAL;
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "www.example.com:443";
+ cert_request->host_and_port = HostPortPair("www.example.com", 443);
// When TLS False Start is used, SSLClientSocket::Connect() calls will
// return successfully after reading up to the peer's Certificate message.
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Restart the handshake. This will consume ssl_data2, which fails, and
// Ensure that the client certificate is removed from the cache on a
// handshake failure.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
// Ensure that a client certificate is removed from the SSL client auth
session_deps_.net_log = log.bound().net_log();
scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo());
- cert_request->host_and_port = "proxy:70";
+ cert_request->host_and_port = HostPortPair("proxy", 70);
// See ClientAuthCertCache_Direct_NoFalseStart for the explanation of
// [ssl_]data[1-3]. Rather than represending the endpoint
// Ensure the certificate was added to the client auth cache before
// allowing the connection to continue restarting.
scoped_refptr<X509Certificate> client_cert;
- ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70",
- &client_cert));
+ ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("proxy", 70), &client_cert));
ASSERT_EQ(NULL, client_cert.get());
// Ensure the certificate was NOT cached for the endpoint. This only
// applies to HTTPS requests, but is fine to check for HTTP requests.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
// Restart the handshake. This will consume ssl_data2, which fails, and
// then consume ssl_data3, which should also fail. The result code is
// Now that the new handshake has failed, ensure that the client
// certificate was removed from the client auth cache.
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("proxy:70",
- &client_cert));
- ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443",
- &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("proxy", 70), &client_cert));
+ ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup(
+ HostPortPair("www.example.com", 443), &client_cert));
}
}
#define MAYBE_UseIPConnectionPooling UseIPConnectionPooling
#endif
WRAPPED_TEST_P(HttpNetworkTransactionTest, MAYBE_UseIPConnectionPooling) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a MockCachingHostResolver.
session_deps_.host_resolver.reset(new MockCachingHostResolver());
#undef MAYBE_UseIPConnectionPooling
TEST_P(HttpNetworkTransactionTest, UseIPConnectionPoolingAfterResolution) {
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a MockCachingHostResolver.
session_deps_.host_resolver.reset(new MockCachingHostResolver());
// prefix doesn't work with parametrized tests).
#if defined(OS_WIN)
return;
-#endif
-
- HttpStreamFactory::set_use_alternate_protocols(true);
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+#else
+ session_deps_.use_alternate_protocols = true;
+ session_deps_.next_protos = SpdyNextProtos();
// Set up a special HttpNetworkSession with a OneTimeCachingHostResolver.
OneTimeCachingHostResolver host_resolver(HostPortPair("www.gmail.com", 443));
EXPECT_TRUE(response->was_npn_negotiated);
ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data));
EXPECT_EQ("hello!", response_data);
+#endif
}
#undef MAYBE_UseIPConnectionPoolingWithHostCacheExpiration
-TEST_P(HttpNetworkTransactionTest, ReadPipelineEvictionFallback) {
- MockRead data_reads1[] = {
- MockRead(SYNCHRONOUS, ERR_PIPELINE_EVICTION),
- };
- MockRead data_reads2[] = {
- MockRead("HTTP/1.0 200 OK\r\n\r\n"),
- MockRead("hello world"),
- MockRead(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), NULL, 0);
- StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), NULL, 0);
- StaticSocketDataProvider* data[] = { &data1, &data2 };
-
- SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data));
-
- EXPECT_EQ(OK, out.rv);
- EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
- EXPECT_EQ("hello world", out.response_data);
-}
-
-TEST_P(HttpNetworkTransactionTest, SendPipelineEvictionFallback) {
- MockWrite data_writes1[] = {
- MockWrite(SYNCHRONOUS, ERR_PIPELINE_EVICTION),
- };
- MockWrite data_writes2[] = {
- MockWrite("GET / HTTP/1.1\r\n"
- "Host: www.google.com\r\n"
- "Connection: keep-alive\r\n\r\n"),
- };
- MockRead data_reads2[] = {
- MockRead("HTTP/1.0 200 OK\r\n\r\n"),
- MockRead("hello world"),
- MockRead(SYNCHRONOUS, OK),
- };
- StaticSocketDataProvider data1(NULL, 0,
- data_writes1, arraysize(data_writes1));
- StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
- data_writes2, arraysize(data_writes2));
- StaticSocketDataProvider* data[] = { &data1, &data2 };
-
- SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data));
-
- EXPECT_EQ(OK, out.rv);
- EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
- EXPECT_EQ("hello world", out.response_data);
-}
-
TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttp) {
const std::string https_url = "https://www.google.com/";
const std::string http_url = "http://www.google.com:443/";
LOWEST));
scoped_ptr<SpdyFrame> req1(
spdy_util_.ConstructSpdyGet(https_url.c_str(), false, 1, LOWEST));
-
- // SPDY GET for HTTP URL (through the proxy, but not the tunnel)
scoped_ptr<SpdyFrame> wrapped_req1(
spdy_util_.ConstructWrappedSpdyFrame(req1, 1));
- const char* const headers[] = {
- spdy_util_.GetMethodKey(), "GET",
- spdy_util_.GetPathKey(), spdy_util_.is_spdy2() ? http_url.c_str() : "/",
- spdy_util_.GetHostKey(), "www.google.com:443",
- spdy_util_.GetSchemeKey(), "http",
- spdy_util_.GetVersionKey(), "HTTP/1.1"
- };
- scoped_ptr<SpdyFrame> req2(spdy_util_.ConstructSpdyControlFrame(
- NULL, 0, false, 3, MEDIUM, SYN_STREAM, CONTROL_FLAG_FIN,
- headers, arraysize(headers), 0));
+
+ // SPDY GET for HTTP URL (through the proxy, but not the tunnel).
+ SpdyHeaderBlock req2_block;
+ req2_block[spdy_util_.GetMethodKey()] = "GET";
+ req2_block[spdy_util_.GetPathKey()] =
+ spdy_util_.is_spdy2() ? http_url.c_str() : "/";
+ req2_block[spdy_util_.GetHostKey()] = "www.google.com:443";
+ req2_block[spdy_util_.GetSchemeKey()] = "http";
+ spdy_util_.MaybeAddVersionHeader(&req2_block);
+ scoped_ptr<SpdyFrame> req2(
+ spdy_util_.ConstructSpdySyn(3, req2_block, MEDIUM, false, true));
MockWrite writes1[] = {
CreateMockWrite(*connect, 0),
}
TEST_P(HttpNetworkTransactionTest, UseSpdySessionForHttpWhenForced) {
- HttpStreamFactory::set_force_spdy_always(true);
+ session_deps_.force_spdy_always = true;
const std::string https_url = "https://www.google.com/";
const std::string http_url = "http://www.google.com:443/";
// SPDY GET for HTTP URL (through SPDY proxy)
scoped_ptr<SpdyHeaderBlock> headers(
spdy_util_.ConstructGetHeaderBlockForProxy("http://www.google.com/"));
- scoped_ptr<SpdyFrame> req1(spdy_util_.ConstructSpdyControlFrame(
- headers.Pass(), false, 1, LOWEST, SYN_STREAM, CONTROL_FLAG_FIN, 0));
+ scoped_ptr<SpdyFrame> req1(
+ spdy_util_.ConstructSpdySyn(1, *headers, LOWEST, false, true));
MockWrite writes1[] = {
CreateMockWrite(*req1, 0),
}
TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) {
- HttpStreamFactory::SetNextProtos(SpdyNextProtos());
+ session_deps_.next_protos = SpdyNextProtos();
ClientSocketPoolManager::set_max_sockets_per_group(
HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
ClientSocketPoolManager::set_max_sockets_per_pool(
HostPortPair host_port_pair_a("www.a.com", 443);
SpdySessionKey spdy_session_key_a(
- host_port_pair_a, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_a, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_a));
HostPortPair host_port_pair_b("www.b.com", 443);
SpdySessionKey spdy_session_key_b(
- host_port_pair_b, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_b, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_b));
HttpRequestInfo request2;
HostPortPair host_port_pair_a1("www.a.com", 80);
SpdySessionKey spdy_session_key_a1(
- host_port_pair_a1, ProxyServer::Direct(), kPrivacyModeDisabled);
+ host_port_pair_a1, ProxyServer::Direct(), PRIVACY_MODE_DISABLED);
EXPECT_FALSE(
HasSpdySession(session->spdy_session_pool(), spdy_session_key_a1));
HttpRequestInfo request3;
return ERR_UNEXPECTED;
}
- virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
- ADD_FAILURE();
- return NULL;
- }
-
virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
const CompletionCallback& callback) OVERRIDE {
ADD_FAILURE();
return false;
}
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE {
+ ADD_FAILURE();
+ return 0;
+ }
+
virtual bool GetLoadTimingInfo(
LoadTimingInfo* load_timing_info) const OVERRIDE {
ADD_FAILURE();
FakeStreamRequest(RequestPriority priority,
HttpStreamRequest::Delegate* delegate)
: priority_(priority),
- delegate_(delegate) {}
+ delegate_(delegate),
+ websocket_stream_create_helper_(NULL) {}
+
+ FakeStreamRequest(RequestPriority priority,
+ HttpStreamRequest::Delegate* delegate,
+ WebSocketHandshakeStreamBase::CreateHelper* create_helper)
+ : priority_(priority),
+ delegate_(delegate),
+ websocket_stream_create_helper_(create_helper) {}
virtual ~FakeStreamRequest() {}
RequestPriority priority() const { return priority_; }
+ const WebSocketHandshakeStreamBase::CreateHelper*
+ websocket_stream_create_helper() const {
+ return websocket_stream_create_helper_;
+ }
+
// Create a new FakeStream and pass it to the request's
// delegate. Returns a weak pointer to the FakeStream.
base::WeakPtr<FakeStream> FinishStreamRequest() {
private:
RequestPriority priority_;
HttpStreamRequest::Delegate* const delegate_;
+ WebSocketHandshakeStreamBase::CreateHelper* websocket_stream_create_helper_;
DISALLOW_COPY_AND_ASSIGN(FakeStreamRequest);
};
const SSLConfig& server_ssl_config,
const SSLConfig& proxy_ssl_config,
HttpStreamRequest::Delegate* delegate,
- WebSocketHandshakeStreamBase::Factory* factory,
+ WebSocketHandshakeStreamBase::CreateHelper* create_helper,
const BoundNetLog& net_log) OVERRIDE {
- ADD_FAILURE();
- return NULL;
+ FakeStreamRequest* fake_request =
+ new FakeStreamRequest(priority, delegate, create_helper);
+ last_stream_request_ = fake_request->AsWeakPtr();
+ return fake_request;
}
virtual void PreconnectStreams(int num_streams,
ADD_FAILURE();
}
- virtual base::Value* PipelineInfoToValue() const OVERRIDE {
- ADD_FAILURE();
- return NULL;
- }
-
virtual const HostMappingRules* GetHostMappingRules() const OVERRIDE {
ADD_FAILURE();
return NULL;
DISALLOW_COPY_AND_ASSIGN(FakeStreamFactory);
};
+// TODO(yhirano): Split this class out into a net/websockets file, if it is
+// worth doing.
+class FakeWebSocketStreamCreateHelper :
+ public WebSocketHandshakeStreamBase::CreateHelper {
+ public:
+ virtual WebSocketHandshakeStreamBase* CreateBasicStream(
+ scoped_ptr<ClientSocketHandle> connection,
+ bool using_proxy) OVERRIDE {
+ NOTREACHED();
+ return NULL;
+ }
+
+ virtual WebSocketHandshakeStreamBase* CreateSpdyStream(
+ const base::WeakPtr<SpdySession>& session,
+ bool use_relative_url) OVERRIDE {
+ NOTREACHED();
+ return NULL;
+ };
+
+ virtual ~FakeWebSocketStreamCreateHelper() {}
+
+ virtual scoped_ptr<WebSocketStream> Upgrade() {
+ NOTREACHED();
+ return scoped_ptr<WebSocketStream>();
+ }
+};
+
} // namespace
// Make sure that HttpNetworkTransaction passes on its priority to its
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
HttpNetworkSessionPeer peer(session);
FakeStreamFactory* fake_factory = new FakeStreamFactory();
- peer.SetHttpStreamFactory(fake_factory);
+ peer.SetHttpStreamFactory(scoped_ptr<HttpStreamFactory>(fake_factory));
HttpNetworkTransaction trans(LOW, session);
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
HttpNetworkSessionPeer peer(session);
FakeStreamFactory* fake_factory = new FakeStreamFactory();
- peer.SetHttpStreamFactory(fake_factory);
+ peer.SetHttpStreamFactory(scoped_ptr<HttpStreamFactory>(fake_factory));
HttpNetworkTransaction trans(LOW, session);
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
HttpNetworkSessionPeer peer(session);
FakeStreamFactory* fake_factory = new FakeStreamFactory();
- peer.SetHttpStreamFactory(fake_factory);
+ peer.SetHttpStreamFactory(scoped_ptr<HttpStreamFactory>(fake_factory));
HttpNetworkTransaction trans(LOW, session);
EXPECT_EQ(LOWEST, fake_stream->priority());
}
+TEST_P(HttpNetworkTransactionTest, CreateWebSocketHandshakeStream) {
+ // The same logic needs to be tested for both ws: and wss: schemes, but this
+ // test is already parameterised on NextProto, so it uses a loop to verify
+ // that the different schemes work.
+ std::string test_cases[] = {"ws://www.google.com/", "wss://www.google.com/"};
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ HttpNetworkSessionPeer peer(session);
+ FakeStreamFactory* fake_factory = new FakeStreamFactory();
+ FakeWebSocketStreamCreateHelper websocket_stream_create_helper;
+ peer.SetHttpStreamFactoryForWebSocket(
+ scoped_ptr<HttpStreamFactory>(fake_factory));
+
+ HttpNetworkTransaction trans(LOW, session);
+ trans.SetWebSocketHandshakeStreamCreateHelper(
+ &websocket_stream_create_helper);
+
+ HttpRequestInfo request;
+ TestCompletionCallback callback;
+ request.method = "GET";
+ request.url = GURL(test_cases[i]);
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ trans.Start(&request, callback.callback(), BoundNetLog()));
+
+ base::WeakPtr<FakeStreamRequest> fake_request =
+ fake_factory->last_stream_request();
+ ASSERT_TRUE(fake_request != NULL);
+ EXPECT_EQ(&websocket_stream_create_helper,
+ fake_request->websocket_stream_create_helper());
+ }
+}
+
// Tests that when a used socket is returned to the SSL socket pool, it's closed
// if the transport socket pool is stalled on the global socket limit.
TEST_P(HttpNetworkTransactionTest, CloseSSLSocketOnIdleForHttpRequest) {
EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session));
}
+TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+// This test makes sure the retry logic doesn't trigger when reading an error
+// response from a server that rejected a POST with a CONNECTION_RESET.
+TEST_P(HttpNetworkTransactionTest,
+ PostReadsErrorResponseAfterResetOnReusedSocket) {
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 Peachy\r\n"
+ "Content-Length: 14\r\n\r\n"),
+ MockRead("first response"),
+ MockRead("HTTP/1.1 400 Not OK\r\n"
+ "Content-Length: 15\r\n\r\n"),
+ MockRead("second response"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+ HttpRequestInfo request1;
+ request1.method = "GET";
+ request1.url = GURL("http://www.foo.com/");
+ request1.load_flags = 0;
+
+ scoped_ptr<HttpTransaction> trans1(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ int rv = trans1->Start(&request1, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+ ASSERT_TRUE(response1 != NULL);
+
+ EXPECT_TRUE(response1->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 200 Peachy", response1->headers->GetStatusLine());
+
+ std::string response_data1;
+ rv = ReadTransaction(trans1.get(), &response_data1);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("first response", response_data1);
+ // Delete the transaction to release the socket back into the socket pool.
+ trans1.reset();
+
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request2;
+ request2.method = "POST";
+ request2.url = GURL("http://www.foo.com/");
+ request2.upload_data_stream = &upload_data_stream;
+ request2.load_flags = 0;
+
+ scoped_ptr<HttpTransaction> trans2(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ rv = trans2->Start(&request2, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response2 = trans2->GetResponseInfo();
+ ASSERT_TRUE(response2 != NULL);
+
+ EXPECT_TRUE(response2->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.1 400 Not OK", response2->headers->GetStatusLine());
+
+ std::string response_data2;
+ rv = ReadTransaction(trans2.get(), &response_data2);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("second response", response_data2);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PostReadsErrorResponseAfterResetPartialBodySent) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"
+ "fo"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+// This tests the more common case than the previous test, where headers and
+// body are not merged into a single request.
+TEST_P(HttpNetworkTransactionTest, ChunkedPostReadsErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Transfer-Encoding: chunked\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ // Make sure the headers are sent before adding a chunk. This ensures that
+ // they can't be merged with the body in a single send. Not currently
+ // necessary since a chunked body is never merged with headers, but this makes
+ // the test more future proof.
+ base::RunLoop().RunUntilIdle();
+
+ upload_data_stream.AppendChunk("last chunk", 10, true);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterResetAnd100) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 100 Continue\r\n\r\n"),
+ MockRead("HTTP/1.0 400 Not OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+
+ EXPECT_TRUE(response->headers.get() != NULL);
+ EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ rv = ReadTransaction(trans.get(), &response_data);
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("hello world", response_data);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresNonErrorResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 Just Dandy\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest,
+ PostIgnoresNonErrorResponseAfterResetAnd100) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 100 Continue\r\n\r\n"),
+ MockRead("HTTP/1.0 302 Redirect\r\n"),
+ MockRead("Location: http://somewhere-else.com/\r\n"),
+ MockRead("Content-Length: 0\r\n\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresHttp09ResponseAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP 0.9 rocks!"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
+TEST_P(HttpNetworkTransactionTest, PostIgnoresPartial400HeadersAfterReset) {
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(new UploadBytesElementReader("foo", 3));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+
+ HttpRequestInfo request;
+ request.method = "POST";
+ request.url = GURL("http://www.foo.com/");
+ request.upload_data_stream = &upload_data_stream;
+ request.load_flags = 0;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session));
+ // Send headers successfully, but get an error while sending the body.
+ MockWrite data_writes[] = {
+ MockWrite("POST / HTTP/1.1\r\n"
+ "Host: www.foo.com\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Length: 3\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 400 Not a Full Response\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+ arraysize(data_writes));
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_CONNECTION_RESET, rv);
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ EXPECT_TRUE(response == NULL);
+}
+
} // namespace net