diff options
author | mmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-21 18:23:25 +0000 |
---|---|---|
committer | mmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-21 18:23:25 +0000 |
commit | 58e32bb4ef2ea561fddca0271d0d9e0e3fe24ee1 (patch) | |
tree | 264d6e557346f132600aad2e2b683cccc37a3aa6 /net | |
parent | b1d2c7467b805b0e2d5f7fe6d8fd39e2cb06d4c8 (diff) | |
download | chromium_src-58e32bb4ef2ea561fddca0271d0d9e0e3fe24ee1.zip chromium_src-58e32bb4ef2ea561fddca0271d0d9e0e3fe24ee1.tar.gz chromium_src-58e32bb4ef2ea561fddca0271d0d9e0e3fe24ee1.tar.bz2 |
LoadTiming in net, part 3.
Add send and receive times to HttpNetworkTransaction.
Add LoadTimingInfo accessors to Http*Transaction,
URLRequestJob, and URLRequest classes.
Proxy resolution times still need to be added.
BUG=77446
Review URL: https://chromiumcodereview.appspot.com/11929017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177942 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_cache_transaction.cc | 9 | ||||
-rw-r--r-- | net/http/http_cache_transaction.h | 2 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 20 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 11 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 134 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 134 | ||||
-rw-r--r-- | net/http/http_transaction.h | 6 | ||||
-rw-r--r-- | net/http/http_transaction_unittest.cc | 5 | ||||
-rw-r--r-- | net/http/http_transaction_unittest.h | 3 | ||||
-rw-r--r-- | net/url_request/url_request.cc | 20 | ||||
-rw-r--r-- | net/url_request/url_request.h | 17 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.cc | 6 | ||||
-rw-r--r-- | net/url_request/url_request_http_job.h | 2 | ||||
-rw-r--r-- | net/url_request/url_request_job.cc | 4 | ||||
-rw-r--r-- | net/url_request/url_request_job.h | 3 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.cc | 41 | ||||
-rw-r--r-- | net/url_request/url_request_test_util.h | 22 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 176 |
18 files changed, 610 insertions, 5 deletions
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 62f1ee6..6ee76a7 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -464,6 +464,15 @@ UploadProgress HttpCache::Transaction::GetUploadProgress() const { return final_upload_progress_; } +bool HttpCache::Transaction::GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const { + if (network_trans_) + return network_trans_->GetLoadTimingInfo(load_timing_info); + // Don't modify |load_timing_info| when reading from the cache instead of the + // network. + return false; +} + //----------------------------------------------------------------------------- void HttpCache::Transaction::DoCallback(int rv) { diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h index 86e42f6..bd5a46f 100644 --- a/net/http/http_cache_transaction.h +++ b/net/http/http_cache_transaction.h @@ -122,6 +122,8 @@ class HttpCache::Transaction : public HttpTransaction { virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; virtual LoadState GetLoadState() const OVERRIDE; virtual UploadProgress GetUploadProgress(void) const OVERRIDE; + virtual bool GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const OVERRIDE; private: static const size_t kNumValidationHeaders = 2; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 05ed0b0..caa8f11 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -26,6 +26,7 @@ #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/base/ssl_cert_request_info.h" @@ -481,6 +482,17 @@ void HttpNetworkTransaction::OnHttpsProxyTunnelResponse( OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE); } +bool HttpNetworkTransaction::GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const { + if (!stream_ || !stream_->GetLoadTimingInfo(load_timing_info)) + return false; + + load_timing_info->send_start = send_start_time_; + load_timing_info->send_end = send_end_time_; + load_timing_info->receive_headers_end = receive_headers_end_; + return true; +} + bool HttpNetworkTransaction::is_https_request() const { return request_->url.SchemeIs("https"); } @@ -788,12 +800,14 @@ int HttpNetworkTransaction::DoBuildRequestComplete(int result) { } int HttpNetworkTransaction::DoSendRequest() { + send_start_time_ = base::TimeTicks::Now(); next_state_ = STATE_SEND_REQUEST_COMPLETE; return stream_->SendRequest(request_headers_, &response_, io_callback_); } int HttpNetworkTransaction::DoSendRequestComplete(int result) { + send_end_time_ = base::TimeTicks::Now(); if (result < 0) return HandleIOError(result); next_state_ = STATE_READ_HEADERS; @@ -816,6 +830,8 @@ int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { } int HttpNetworkTransaction::DoReadHeadersComplete(int result) { + receive_headers_end_ = base::TimeTicks::Now(); + // We can get a certificate error or ERR_SSL_CLIENT_AUTH_CERT_NEEDED here // due to SSL renegotiation. if (IsCertificateError(result)) { @@ -1306,6 +1322,10 @@ void HttpNetworkTransaction::ResetStateForRestart() { } void HttpNetworkTransaction::ResetStateForAuthRestart() { + send_start_time_ = base::TimeTicks(); + send_end_time_ = base::TimeTicks(); + receive_headers_end_ = base::TimeTicks(); + pending_auth_target_ = HttpAuth::AUTH_NONE; read_buf_ = NULL; read_buf_len_ = 0; diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 92dabe5..47f7d91 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -82,6 +82,9 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction const ProxyInfo& used_proxy_info, HttpStreamBase* stream) OVERRIDE; + virtual bool GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const OVERRIDE; + private: FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionSpdy2Test, ResetStateForRestart); @@ -289,6 +292,14 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction // The time the Start method was called. base::Time start_time_; + // When the transaction started / finished sending the request, including + // the body, if present. + base::TimeTicks send_start_time_; + base::TimeTicks send_end_time_; + + // When the transaction finished reading the request headers. + base::TimeTicks receive_headers_end_; + // The next state in the state machine. State next_state_; diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index 62d5b9d..89fa251 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -24,6 +24,8 @@ #include "net/base/cert_test_util.h" #include "net/base/completion_callback.h" #include "net/base/host_cache.h" +#include "net/base/load_timing_info.h" +#include "net/base/load_timing_info_test_util.h" #include "net/base/mock_cert_verifier.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_log.h" @@ -131,6 +133,44 @@ bool GetHeaders(DictionaryValue* params, std::string* headers) { return true; } +// |connect_time_flags| is ignored if expect_reused is true. +void TestLoadTiming(const net::LoadTimingInfo& load_timing_info, + bool expect_reused, int connect_timing_flags) { + EXPECT_EQ(expect_reused, load_timing_info.socket_reused); + EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + + if (expect_reused) { + net::ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); + EXPECT_FALSE(load_timing_info.send_start.is_null()); + } else { + net::ExpectConnectTimingHasTimes(load_timing_info.connect_timing, + connect_timing_flags); + EXPECT_LE(load_timing_info.connect_timing.connect_end, + load_timing_info.send_start); + } + + EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); + EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); + + // Not set by these tests. + EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); + EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); + + // Set by URLRequest, at a higher level. + EXPECT_TRUE(load_timing_info.request_start_time.is_null()); + EXPECT_TRUE(load_timing_info.request_start.is_null()); +} + +void TestLoadTimingReused(const net::LoadTimingInfo& load_timing_info) { + TestLoadTiming(load_timing_info, true, + net::CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); +} + +void TestLoadTimingNotReused(const net::LoadTimingInfo& load_timing_info, + int connect_timing_flags) { + TestLoadTiming(load_timing_info, false, connect_timing_flags); +} + } // namespace namespace net { @@ -149,6 +189,7 @@ class HttpNetworkTransactionSpdy2Test : public PlatformTest { int rv; std::string status_line; std::string response_data; + LoadTimingInfo load_timing_info; }; virtual void SetUp() { @@ -183,6 +224,8 @@ class HttpNetworkTransactionSpdy2Test : public PlatformTest { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingBoundNetLog log; + session_deps.net_log = log.bound().net_log(); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); @@ -192,12 +235,17 @@ class HttpNetworkTransactionSpdy2Test : public PlatformTest { TestCompletionCallback callback; - CapturingBoundNetLog log; EXPECT_TRUE(log.bound().IsLoggingAllEvents()); int rv = trans->Start(&request, callback.callback(), log.bound()); EXPECT_EQ(ERR_IO_PENDING, rv); out.rv = callback.WaitForResult(); + + // Even in the failure cases that use this function, connections are always + // successfully established before the error. + EXPECT_TRUE(trans->GetLoadTimingInfo(&out.load_timing_info)); + TestLoadTimingNotReused(out.load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + if (out.rv != OK) return out; @@ -1009,6 +1057,8 @@ void HttpNetworkTransactionSpdy2Test::KeepAliveConnectionResendRequestTest( request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog net_log; + session_deps.net_log = &net_log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // Written data for successfully sending both requests. @@ -1052,6 +1102,7 @@ void HttpNetworkTransactionSpdy2Test::KeepAliveConnectionResendRequestTest( "hello", "world" }; + uint32 first_socket_log_id = NetLog::Source::kInvalidId; for (int i = 0; i < 2; ++i) { TestCompletionCallback callback; @@ -1063,6 +1114,16 @@ void HttpNetworkTransactionSpdy2Test::KeepAliveConnectionResendRequestTest( 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); + if (i == 0) { + first_socket_log_id = load_timing_info.socket_log_id; + } else { + // The second request should be using a new socket. + EXPECT_NE(first_socket_log_id, load_timing_info.socket_log_id); + } + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -1236,6 +1297,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, KeepAliveAfterUnreadBody) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog net_log; + session_deps.net_log = &net_log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // Note that because all these reads happen in the same @@ -1270,6 +1333,7 @@ TEST_F(HttpNetworkTransactionSpdy2Test, KeepAliveAfterUnreadBody) { const int kNumUnreadBodies = arraysize(data1_reads) - 2; std::string response_lines[kNumUnreadBodies]; + uint32 first_socket_log_id = NetLog::Source::kInvalidId; for (size_t i = 0; i < arraysize(data1_reads) - 2; ++i) { TestCompletionCallback callback; @@ -1281,6 +1345,16 @@ TEST_F(HttpNetworkTransactionSpdy2Test, KeepAliveAfterUnreadBody) { rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + if (i == 0) { + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + first_socket_log_id = load_timing_info.socket_log_id; + } else { + TestLoadTimingReused(load_timing_info); + EXPECT_EQ(first_socket_log_id, load_timing_info.socket_log_id); + } + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -1331,6 +1405,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuth) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog log; + session_deps.net_log = &log; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); @@ -1385,6 +1461,10 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuth) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info1; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1)); + TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); @@ -1398,6 +1478,15 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuth) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info2; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2)); + TestLoadTimingNotReused(load_timing_info2, CONNECT_TIMING_HAS_DNS_TIMES); + // The load timing after restart should have a new socket ID, and times after + // those of the first load timing. + EXPECT_LE(load_timing_info1.receive_headers_end, + load_timing_info2.connect_timing.connect_start); + EXPECT_NE(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -1454,6 +1543,8 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuthKeepAlive) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog log; + session_deps.net_log = &log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); MockWrite data_writes1[] = { @@ -1505,6 +1596,10 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuthKeepAlive) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info1; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1)); + TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); @@ -1518,6 +1613,15 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuthKeepAlive) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info2; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2)); + TestLoadTimingReused(load_timing_info2); + // The load timing after restart should have the same socket ID, and times + // those of the first load timing. + EXPECT_LE(load_timing_info1.receive_headers_end, + load_timing_info2.send_start); + EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -2183,6 +2287,11 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxyGet) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -2242,6 +2351,11 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxySpdyGet) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2422,6 +2536,10 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxySpdyConnectHttps) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_SSL_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2500,6 +2618,10 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxySpdyConnectSpdy) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_SSL_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2625,6 +2747,11 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxyAuthRetry) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_FALSE(response->headers == NULL); @@ -2641,6 +2768,11 @@ TEST_F(HttpNetworkTransactionSpdy2Test, HttpsProxyAuthRetry) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + load_timing_info = LoadTimingInfo(); + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + // Retrying with HTTP AUTH is considered to be reusing a socket. + TestLoadTimingReused(load_timing_info); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index 3cc1b2c..5a8155b 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -24,6 +24,8 @@ #include "net/base/cert_test_util.h" #include "net/base/completion_callback.h" #include "net/base/host_cache.h" +#include "net/base/load_timing_info.h" +#include "net/base/load_timing_info_test_util.h" #include "net/base/mock_cert_verifier.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_log.h" @@ -141,6 +143,44 @@ bool GetHeaders(DictionaryValue* params, std::string* headers) { return true; } +// |connect_time_flags| is ignored if expect_reused is true. +void TestLoadTiming(const net::LoadTimingInfo& load_timing_info, + bool expect_reused, int connect_timing_flags) { + EXPECT_EQ(expect_reused, load_timing_info.socket_reused); + EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + + if (expect_reused) { + net::ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); + EXPECT_FALSE(load_timing_info.send_start.is_null()); + } else { + net::ExpectConnectTimingHasTimes(load_timing_info.connect_timing, + connect_timing_flags); + EXPECT_LE(load_timing_info.connect_timing.connect_end, + load_timing_info.send_start); + } + + EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); + EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); + + // Not set by these tests. + EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); + EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); + + // Set by URLRequest, at a higher level. + EXPECT_TRUE(load_timing_info.request_start_time.is_null()); + EXPECT_TRUE(load_timing_info.request_start.is_null()); +} + +void TestLoadTimingReused(const net::LoadTimingInfo& load_timing_info) { + TestLoadTiming(load_timing_info, true, + net::CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); +} + +void TestLoadTimingNotReused(const net::LoadTimingInfo& load_timing_info, + int connect_timing_flags) { + TestLoadTiming(load_timing_info, false, connect_timing_flags); +} + } // namespace class HttpNetworkTransactionSpdy3Test : public PlatformTest { @@ -149,6 +189,7 @@ class HttpNetworkTransactionSpdy3Test : public PlatformTest { int rv; std::string status_line; std::string response_data; + LoadTimingInfo load_timing_info; }; virtual void SetUp() { @@ -183,6 +224,8 @@ class HttpNetworkTransactionSpdy3Test : public PlatformTest { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingBoundNetLog log; + session_deps.net_log = log.bound().net_log(); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); @@ -192,12 +235,17 @@ class HttpNetworkTransactionSpdy3Test : public PlatformTest { TestCompletionCallback callback; - CapturingBoundNetLog log; EXPECT_TRUE(log.bound().IsLoggingAllEvents()); int rv = trans->Start(&request, callback.callback(), log.bound()); EXPECT_EQ(ERR_IO_PENDING, rv); out.rv = callback.WaitForResult(); + + // Even in the failure cases that use this function, connections are always + // successfully established before the error. + EXPECT_TRUE(trans->GetLoadTimingInfo(&out.load_timing_info)); + TestLoadTimingNotReused(out.load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + if (out.rv != OK) return out; @@ -1009,6 +1057,8 @@ void HttpNetworkTransactionSpdy3Test::KeepAliveConnectionResendRequestTest( request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog net_log; + session_deps.net_log = &net_log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // Written data for successfully sending both requests. @@ -1052,6 +1102,7 @@ void HttpNetworkTransactionSpdy3Test::KeepAliveConnectionResendRequestTest( "hello", "world" }; + uint32 first_socket_log_id = NetLog::Source::kInvalidId; for (int i = 0; i < 2; ++i) { TestCompletionCallback callback; @@ -1063,6 +1114,16 @@ void HttpNetworkTransactionSpdy3Test::KeepAliveConnectionResendRequestTest( 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); + if (i == 0) { + first_socket_log_id = load_timing_info.socket_log_id; + } else { + // The second request should be using a new socket. + EXPECT_NE(first_socket_log_id, load_timing_info.socket_log_id); + } + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -1236,6 +1297,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, KeepAliveAfterUnreadBody) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog net_log; + session_deps.net_log = &net_log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // Note that because all these reads happen in the same @@ -1270,6 +1333,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, KeepAliveAfterUnreadBody) { const int kNumUnreadBodies = arraysize(data1_reads) - 2; std::string response_lines[kNumUnreadBodies]; + uint32 first_socket_log_id = NetLog::Source::kInvalidId; for (size_t i = 0; i < arraysize(data1_reads) - 2; ++i) { TestCompletionCallback callback; @@ -1281,6 +1345,16 @@ TEST_F(HttpNetworkTransactionSpdy3Test, KeepAliveAfterUnreadBody) { rv = callback.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + if (i == 0) { + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + first_socket_log_id = load_timing_info.socket_log_id; + } else { + TestLoadTimingReused(load_timing_info); + EXPECT_EQ(first_socket_log_id, load_timing_info.socket_log_id); + } + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -1331,6 +1405,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuth) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog log; + session_deps.net_log = &log; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); @@ -1385,6 +1461,10 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuth) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info1; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1)); + TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); @@ -1398,6 +1478,15 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuth) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info2; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2)); + TestLoadTimingNotReused(load_timing_info2, CONNECT_TIMING_HAS_DNS_TIMES); + // The load timing after restart should have a new socket ID, and times after + // those of the first load timing. + EXPECT_LE(load_timing_info1.receive_headers_end, + load_timing_info2.connect_timing.connect_start); + EXPECT_NE(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -1454,6 +1543,8 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuthKeepAlive) { request.load_flags = 0; SpdySessionDependencies session_deps; + CapturingNetLog log; + session_deps.net_log = &log; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); MockWrite data_writes1[] = { @@ -1505,6 +1596,10 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuthKeepAlive) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info1; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1)); + TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); @@ -1518,6 +1613,15 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuthKeepAlive) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info2; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info2)); + TestLoadTimingReused(load_timing_info2); + // The load timing after restart should have the same socket ID, and times + // those of the first load timing. + EXPECT_LE(load_timing_info1.receive_headers_end, + load_timing_info2.send_start); + EXPECT_EQ(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -2183,6 +2287,11 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxyGet) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -2242,6 +2351,11 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyGet) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2422,6 +2536,10 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyConnectHttps) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_SSL_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2500,6 +2618,10 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyConnectSpdy) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_SSL_TIMES); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_TRUE(response->headers != NULL); @@ -2625,6 +2747,11 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxyAuthRetry) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, + CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_FALSE(response->headers == NULL); @@ -2641,6 +2768,11 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxyAuthRetry) { rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); + load_timing_info = LoadTimingInfo(); + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + // Retrying with HTTP AUTH is considered to be reusing a socket. + TestLoadTimingReused(load_timing_info); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h index bae9fdc..7b4ab99 100644 --- a/net/http/http_transaction.h +++ b/net/http/http_transaction.h @@ -17,6 +17,7 @@ class BoundNetLog; struct HttpRequestInfo; class HttpResponseInfo; class IOBuffer; +struct LoadTimingInfo; class X509Certificate; // Represents a single HTTP transaction (i.e., a single request/response pair). @@ -110,6 +111,11 @@ class NET_EXPORT_PRIVATE HttpTransaction { // Returns the upload progress in bytes. If there is no upload data, // zero will be returned. This does not include the request headers. virtual UploadProgress GetUploadProgress() const = 0; + + // Populates all of load timing, except for request start times. + // |load_timing_info| must have all null times when called. Returns false and + // does not modify |load_timing_info| if not currently connected. + virtual bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const = 0; }; } // namespace net diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc index b956245..9eca8c6 100644 --- a/net/http/http_transaction_unittest.cc +++ b/net/http/http_transaction_unittest.cc @@ -320,6 +320,11 @@ net::UploadProgress MockNetworkTransaction::GetUploadProgress() const { return net::UploadProgress(); } +bool MockNetworkTransaction::GetLoadTimingInfo( + net::LoadTimingInfo* load_timing_info) const { + return false; +} + void MockNetworkTransaction::CallbackLater( const net::CompletionCallback& callback, int result) { MessageLoop::current()->PostTask( diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h index e29c35b..f1bd78c 100644 --- a/net/http/http_transaction_unittest.h +++ b/net/http/http_transaction_unittest.h @@ -186,6 +186,9 @@ class MockNetworkTransaction : public net::HttpTransaction { virtual net::UploadProgress GetUploadProgress() const OVERRIDE; + virtual bool GetLoadTimingInfo( + net::LoadTimingInfo* load_timing_info) const OVERRIDE; + private: void CallbackLater(const net::CompletionCallback& callback, int result); void RunCallback(const net::CompletionCallback& callback, int result); diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index 735a34d..f642e2b 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -18,6 +18,7 @@ #include "net/base/auth.h" #include "net/base/host_port_pair.h" #include "net/base/load_flags.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/base/network_change_notifier.h" @@ -370,6 +371,16 @@ HttpResponseHeaders* URLRequest::response_headers() const { return response_info_.headers.get(); } +void URLRequest::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { + // Downstream functions all expect |load_timing_info| to have null times. + *load_timing_info = LoadTimingInfo(); + + if (job_) + job_->GetLoadTimingInfo(load_timing_info); + load_timing_info->request_start_time = start_time_; + load_timing_info->request_start = start_time_ticks_; +} + bool URLRequest::GetResponseCookies(ResponseCookies* cookies) { DCHECK(job_); return job_->GetResponseCookies(cookies); @@ -453,7 +464,9 @@ void URLRequest::Start() { DCHECK_EQ(network_delegate_, context_->network_delegate()); g_url_requests_started = true; - response_info_.request_time = Time::Now(); + response_info_.request_time = base::Time::Now(); + start_time_ = response_info_.request_time; + start_time_ticks_ = base::TimeTicks::Now(); // Only notify the delegate for the initial request. if (network_delegate_) { @@ -719,7 +732,10 @@ void URLRequest::PrepareToRestart() { OrphanJob(); response_info_ = HttpResponseInfo(); - response_info_.request_time = Time::Now(); + response_info_.request_time = base::Time::Now(); + start_time_ = response_info_.request_time; + start_time_ticks_ = base::TimeTicks::Now(); + status_ = URLRequestStatus(); is_pending_ = false; } diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 8be1c94..fa71043 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -89,6 +89,7 @@ namespace net { class CookieOptions; class HostPortPair; class IOBuffer; +struct LoadTimingInfo; class SSLCertRequestInfo; class SSLInfo; class UploadDataStream; @@ -495,6 +496,18 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), return response_info_.ssl_info; } + // Gets timing information related to the request. Events that have not yet + // occurred are left uninitialized. After a second request occurs, due to + // a redirect or authentication, values will be reset. + // + // LoadTimingInfo only contains ConnectTiming information and socket IDs for + // non-cached HTTP responses. To get this information, this must be called + // while there's still a connection. The delegate callbacks where this will + // be the case are: OnBeforeSendHeaders, OnBeforeRedirect, and + // OnResponseBodyStarted. In some error cases there may be no connect times. + // Note that this does not include OnReadCompleted or OnCompleted. + void GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const; + // Returns the cookie values included in the response, if the request is one // that can have cookies. Returns true if the request is a cookie-bearing // type, false otherwise. This method may only be called once the @@ -835,6 +848,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), base::TimeTicks creation_time_; + // Time the last request was started, as a Time and the equivalent TimeTicks. + base::Time start_time_; + base::TimeTicks start_time_ticks_; + scoped_ptr<const base::debug::StackTrace> stack_trace_; DISALLOW_COPY_AND_ASSIGN(URLRequest); diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index fe5f325..c950475 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -949,6 +949,12 @@ void URLRequestHttpJob::GetResponseInfo(HttpResponseInfo* info) { } } +void URLRequestHttpJob::GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const { + if (transaction_) + transaction_->GetLoadTimingInfo(load_timing_info); +} + bool URLRequestHttpJob::GetResponseCookies(std::vector<std::string>* cookies) { DCHECK(transaction_.get()); diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h index 9ef3884..a303dff 100644 --- a/net/url_request/url_request_http_job.h +++ b/net/url_request/url_request_http_job.h @@ -80,6 +80,8 @@ class URLRequestHttpJob : public URLRequestJob { virtual bool GetMimeType(std::string* mime_type) const OVERRIDE; virtual bool GetCharset(std::string* charset) OVERRIDE; virtual void GetResponseInfo(HttpResponseInfo* info) OVERRIDE; + virtual void GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const OVERRIDE; virtual bool GetResponseCookies(std::vector<std::string>* cookies) OVERRIDE; virtual int GetResponseCode() const OVERRIDE; virtual Filter* SetupFilter() const OVERRIDE; diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index c929665..de9f19d 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc @@ -117,6 +117,10 @@ bool URLRequestJob::GetCharset(std::string* charset) { void URLRequestJob::GetResponseInfo(HttpResponseInfo* info) { } +void URLRequestJob::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { + // Only certain request types return more than just request start times. +} + bool URLRequestJob::GetResponseCookies(std::vector<std::string>* cookies) { return false; } diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h index 3178285..4d985f8 100644 --- a/net/url_request/url_request_job.h +++ b/net/url_request/url_request_job.h @@ -28,6 +28,7 @@ class CookieOptions; class HttpRequestHeaders; class HttpResponseInfo; class IOBuffer; +struct LoadTimingInfo; class NetworkDelegate; class SSLCertRequestInfo; class SSLInfo; @@ -110,6 +111,8 @@ class NET_EXPORT URLRequestJob : public base::RefCounted<URLRequestJob>, // Called to get response info. virtual void GetResponseInfo(HttpResponseInfo* info); + virtual void GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const; + // Returns the cookie values included in the response, if applicable. // Returns true if applicable. // NOTE: This removes the cookies from the job, so it will only return diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index c9038ed..19e1c0f 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc @@ -293,7 +293,10 @@ TestNetworkDelegate::TestNetworkDelegate() cookie_options_bit_mask_(0), blocked_get_cookies_count_(0), blocked_set_cookie_count_(0), - set_cookie_count_(0) { + set_cookie_count_(0), + has_load_timing_info_(false), + has_load_timing_info_before_redirect_(false), + has_load_timing_info_before_auth_(false) { } TestNetworkDelegate::~TestNetworkDelegate() { @@ -304,6 +307,24 @@ TestNetworkDelegate::~TestNetworkDelegate() { } } +bool TestNetworkDelegate::GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const { + *load_timing_info = load_timing_info_; + return has_load_timing_info_; +} + +bool TestNetworkDelegate::GetLoadTimingInfoBeforeRedirect( + LoadTimingInfo* load_timing_info_before_redirect) const { + *load_timing_info_before_redirect = load_timing_info_before_redirect_; + return has_load_timing_info_before_redirect_; +} + +bool TestNetworkDelegate::GetLoadTimingInfoBeforeAuth( + LoadTimingInfo* load_timing_info_before_auth) const { + *load_timing_info_before_auth = load_timing_info_before_auth_; + return has_load_timing_info_before_auth_; +} + void TestNetworkDelegate::InitRequestStatesIfNew(int request_id) { if (next_states_.find(request_id) == next_states_.end()) { next_states_[request_id] = kStageBeforeURLRequest; @@ -384,6 +405,12 @@ int TestNetworkDelegate::OnHeadersReceived( void TestNetworkDelegate::OnBeforeRedirect(URLRequest* request, const GURL& new_location) { + load_timing_info_before_redirect_ = LoadTimingInfo(); + request->GetLoadTimingInfo(&load_timing_info_before_redirect_); + has_load_timing_info_before_redirect_ = true; + EXPECT_FALSE(load_timing_info_before_redirect_.request_start_time.is_null()); + EXPECT_FALSE(load_timing_info_before_redirect_.request_start.is_null()); + int req_id = request->identifier(); InitRequestStatesIfNew(req_id); event_order_[req_id] += "OnBeforeRedirect\n"; @@ -401,6 +428,12 @@ void TestNetworkDelegate::OnBeforeRedirect(URLRequest* request, } void TestNetworkDelegate::OnResponseStarted(URLRequest* request) { + load_timing_info_ = LoadTimingInfo(); + request->GetLoadTimingInfo(&load_timing_info_); + has_load_timing_info_ = true; + EXPECT_FALSE(load_timing_info_.request_start_time.is_null()); + EXPECT_FALSE(load_timing_info_.request_start.is_null()); + int req_id = request->identifier(); InitRequestStatesIfNew(req_id); event_order_[req_id] += "OnResponseStarted\n"; @@ -457,6 +490,12 @@ NetworkDelegate::AuthRequiredResponse TestNetworkDelegate::OnAuthRequired( const AuthChallengeInfo& auth_info, const AuthCallback& callback, AuthCredentials* credentials) { + load_timing_info_before_auth_ = LoadTimingInfo(); + request->GetLoadTimingInfo(&load_timing_info_before_auth_); + has_load_timing_info_before_auth_ = true; + EXPECT_FALSE(load_timing_info_before_auth_.request_start_time.is_null()); + EXPECT_FALSE(load_timing_info_before_auth_.request_start.is_null()); + int req_id = request->identifier(); InitRequestStatesIfNew(req_id); event_order_[req_id] += "OnAuthRequired\n"; diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h index 488d5f2..361f51d 100644 --- a/net/url_request/url_request_test_util.h +++ b/net/url_request/url_request_test_util.h @@ -23,6 +23,7 @@ #include "googleurl/src/url_util.h" #include "net/base/cert_verifier.h" #include "net/base/io_buffer.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/network_delegate.h" #include "net/base/ssl_config_service_defaults.h" @@ -195,6 +196,18 @@ class TestNetworkDelegate : public NetworkDelegate { TestNetworkDelegate(); virtual ~TestNetworkDelegate(); + // Writes the LoadTimingInfo during the most recent call to OnResponseStarted + // to |load_timing_info|. Returns the same value as URLequest::GetLoadTiming. + bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const; + + // Same as GetLoadTimingInfo, except for calls to OnBeforeRedirect. + bool GetLoadTimingInfoBeforeRedirect( + LoadTimingInfo* load_timing_info_before_redirect) const; + + // Same as GetLoadTimingInfo, except for calls to AuthRequiredResponse. + bool GetLoadTimingInfoBeforeAuth( + LoadTimingInfo* load_timing_info_before_auth) const; + void set_cookie_options(int o) {cookie_options_bit_mask_ = o; } int last_error() const { return last_error_; } @@ -271,6 +284,15 @@ class TestNetworkDelegate : public NetworkDelegate { // A log that records for each request id (key) the order in which On... // functions were called. std::map<int, std::string> event_order_; + + LoadTimingInfo load_timing_info_; + bool has_load_timing_info_; + + LoadTimingInfo load_timing_info_before_redirect_; + bool has_load_timing_info_before_redirect_; + + LoadTimingInfo load_timing_info_before_auth_; + bool has_load_timing_info_before_auth_; }; // Overrides the host used by the LocalHttpTestServer in diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 6e22588..fca70b7 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -27,9 +27,12 @@ #include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" +#include "net/base/capturing_net_log.h" #include "net/base/cert_test_util.h" #include "net/base/ev_root_ca_metadata.h" #include "net/base/load_flags.h" +#include "net/base/load_timing_info.h" +#include "net/base/load_timing_info_test_util.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" @@ -82,6 +85,52 @@ const string16 kChrome(ASCIIToUTF16("chrome")); const string16 kSecret(ASCIIToUTF16("secret")); const string16 kUser(ASCIIToUTF16("user")); +// Tests load timing information in the case a fresh connection was used. +// These tests use the TestServer, which doesn't support keep-alive sockets, +// so there are no tests here that reuse sockets. +void TestLoadTimingNotReused(const net::LoadTimingInfo& load_timing_info, + int connect_timing_flags) { + EXPECT_FALSE(load_timing_info.socket_reused); + EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + + EXPECT_FALSE(load_timing_info.request_start_time.is_null()); + EXPECT_FALSE(load_timing_info.request_start.is_null()); + + EXPECT_LE(load_timing_info.request_start, + load_timing_info.connect_timing.connect_start); + ExpectConnectTimingHasTimes(load_timing_info.connect_timing, + connect_timing_flags); + EXPECT_LE(load_timing_info.connect_timing.connect_end, + load_timing_info.send_start); + EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); + EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_end); + + // Not set by these tests. + EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); + EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); +} + +// Tests load timing in the case that there is no underlying connection. This +// can be used to test in the case of cached responses, errors, or non-HTTP +// requests. +void TestLoadTimingNoHttpConnection( + const net::LoadTimingInfo& load_timing_info) { + EXPECT_FALSE(load_timing_info.socket_reused); + EXPECT_EQ(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + + // Only the request times should be non-null. + EXPECT_FALSE(load_timing_info.request_start_time.is_null()); + EXPECT_FALSE(load_timing_info.request_start.is_null()); + + ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); + + EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); + EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); + EXPECT_TRUE(load_timing_info.send_start.is_null()); + EXPECT_TRUE(load_timing_info.send_end.is_null()); + EXPECT_TRUE(load_timing_info.receive_headers_end.is_null()); +} + base::StringPiece TestNetResourceProvider(int key) { return "header"; } @@ -464,6 +513,7 @@ class URLRequestTest : public PlatformTest { public: URLRequestTest() : default_context_(true) { default_context_.set_network_delegate(&default_network_delegate_); + default_context_.set_net_log(&net_log_); default_context_.Init(); } virtual ~URLRequestTest() {} @@ -478,6 +528,7 @@ class URLRequestTest : public PlatformTest { } protected: + CapturingNetLog net_log_; TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. scoped_ptr<URLRequestJobFactoryImpl> job_factory_; TestURLRequestContext default_context_; @@ -2811,6 +2862,32 @@ TEST_F(URLRequestTestHTTP, GetTest) { } } +TEST_F(URLRequestTestHTTP, GetTestLoadTiming) { + ASSERT_TRUE(test_server_.Start()); + + TestDelegate d; + { + URLRequest r(test_server_.GetURL(""), &d, &default_context_); + + r.Start(); + EXPECT_TRUE(r.is_pending()); + + MessageLoop::current()->Run(); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + + EXPECT_EQ(1, d.response_started_count()); + EXPECT_FALSE(d.received_data_before_response()); + EXPECT_NE(0, d.bytes_received()); + EXPECT_EQ(test_server_.host_port_pair().host(), + r.GetSocketAddress().host()); + EXPECT_EQ(test_server_.host_port_pair().port(), + r.GetSocketAddress().port()); + } +} + TEST_F(URLRequestTestHTTP, GetZippedTest) { ASSERT_TRUE(test_server_.Start()); @@ -2890,6 +2967,44 @@ TEST_F(URLRequestTestHTTP, DISABLED_HTTPSToHTTPRedirectNoRefererTest) { EXPECT_EQ(std::string(), req.referrer()); } +TEST_F(URLRequestTestHTTP, RedirectLoadTiming) { + ASSERT_TRUE(test_server_.Start()); + + GURL destination_url = test_server_.GetURL(""); + GURL original_url = test_server_.GetURL( + "server-redirect?" + destination_url.spec()); + TestDelegate d; + URLRequest req(original_url, &d, &default_context_); + req.Start(); + MessageLoop::current()->Run(); + + EXPECT_EQ(1, d.response_started_count()); + EXPECT_EQ(1, d.received_redirect_count()); + EXPECT_EQ(destination_url, req.url()); + EXPECT_EQ(original_url, req.original_url()); + ASSERT_EQ(2U, req.url_chain().size()); + EXPECT_EQ(original_url, req.url_chain()[0]); + EXPECT_EQ(destination_url, req.url_chain()[1]); + + LoadTimingInfo load_timing_info_before_redirect; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfoBeforeRedirect( + &load_timing_info_before_redirect)); + TestLoadTimingNotReused(load_timing_info_before_redirect, + CONNECT_TIMING_HAS_DNS_TIMES); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + + // Check that a new socket was used on redirect, since the server does not + // supposed keep-alive sockets, and that the times before the redirect are + // before the ones recorded for the second request. + EXPECT_NE(load_timing_info_before_redirect.socket_log_id, + load_timing_info.socket_log_id); + EXPECT_LE(load_timing_info_before_redirect.receive_headers_end, + load_timing_info.connect_timing.connect_start); +} + TEST_F(URLRequestTestHTTP, MultipleRedirectTest) { ASSERT_TRUE(test_server_.Start()); @@ -3599,6 +3714,63 @@ TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) { } } +// Tests that load timing works as expected with auth and the cache. +TEST_F(URLRequestTestHTTP, BasicAuthLoadTiming) { + ASSERT_TRUE(test_server_.Start()); + + // populate the cache + { + TestDelegate d; + d.set_credentials(AuthCredentials(kUser, kSecret)); + + URLRequest r(test_server_.GetURL("auth-basic"), &d, &default_context_); + r.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); + + LoadTimingInfo load_timing_info_before_auth; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfoBeforeAuth( + &load_timing_info_before_auth)); + TestLoadTimingNotReused(load_timing_info_before_auth, + CONNECT_TIMING_HAS_DNS_TIMES); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfo(&load_timing_info)); + // The test server does not support keep alive sockets, so the second + // request with auth should use a new socket. + TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); + EXPECT_NE(load_timing_info_before_auth.socket_log_id, + load_timing_info.socket_log_id); + EXPECT_LE(load_timing_info_before_auth.receive_headers_end, + load_timing_info.connect_timing.connect_start); + } + + // repeat request with end-to-end validation. since auth-basic results in a + // cachable page, we expect this test to result in a 304. in which case, the + // response should be fetched from the cache. + { + TestDelegate d; + d.set_credentials(AuthCredentials(kUser, kSecret)); + + URLRequest r(test_server_.GetURL("auth-basic"), &d, &default_context_); + r.set_load_flags(LOAD_VALIDATE_CACHE); + r.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); + + // Should be the same cached document. + EXPECT_TRUE(r.was_cached()); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNoHttpConnection(load_timing_info); + } +} + // In this test, we do a POST which the server will 302 redirect. // The subsequent transaction should use GET, and should not send the // Content-Type header. @@ -4959,6 +5131,10 @@ TEST_F(URLRequestTestFTP, DISABLED_FTPGetTest) { EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size)); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(default_network_delegate_.GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNoHttpConnection(load_timing_info); } } |