summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-21 18:23:25 +0000
committermmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-21 18:23:25 +0000
commit58e32bb4ef2ea561fddca0271d0d9e0e3fe24ee1 (patch)
tree264d6e557346f132600aad2e2b683cccc37a3aa6 /net
parentb1d2c7467b805b0e2d5f7fe6d8fd39e2cb06d4c8 (diff)
downloadchromium_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.cc9
-rw-r--r--net/http/http_cache_transaction.h2
-rw-r--r--net/http/http_network_transaction.cc20
-rw-r--r--net/http/http_network_transaction.h11
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc134
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc134
-rw-r--r--net/http/http_transaction.h6
-rw-r--r--net/http/http_transaction_unittest.cc5
-rw-r--r--net/http/http_transaction_unittest.h3
-rw-r--r--net/url_request/url_request.cc20
-rw-r--r--net/url_request/url_request.h17
-rw-r--r--net/url_request/url_request_http_job.cc6
-rw-r--r--net/url_request/url_request_http_job.h2
-rw-r--r--net/url_request/url_request_job.cc4
-rw-r--r--net/url_request/url_request_job.h3
-rw-r--r--net/url_request/url_request_test_util.cc41
-rw-r--r--net/url_request/url_request_test_util.h22
-rw-r--r--net/url_request/url_request_unittest.cc176
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);
}
}