Index: net/http/bidirectional_stream_unittest.cc |
diff --git a/net/http/bidirectional_stream_unittest.cc b/net/http/bidirectional_stream_unittest.cc |
index a08b7fbb92548b091599a3c80c51804bc6b223f2..22d711b705361e8c9a404343955ad6396c67220c 100644 |
--- a/net/http/bidirectional_stream_unittest.cc |
+++ b/net/http/bidirectional_stream_unittest.cc |
@@ -14,6 +14,8 @@ |
#include "base/strings/string_piece.h" |
#include "base/time/time.h" |
#include "base/timer/mock_timer.h" |
+#include "net/base/load_timing_info.h" |
+#include "net/base/load_timing_info_test_util.h" |
#include "net/base/net_errors.h" |
#include "net/http/bidirectional_stream_request_info.h" |
#include "net/http/http_network_session.h" |
@@ -46,6 +48,38 @@ const size_t kBodyDataSize = arraysize(kBodyData); |
// Size of the buffer to be allocated for each read. |
const size_t kReadBufferSize = 4096; |
+// Expects that fields of |load_timing_info| are valid time stamps. |
+void ExpectLoadTimingValid(const LoadTimingInfo& load_timing_info) { |
+ EXPECT_FALSE(load_timing_info.request_start.is_null()); |
+ EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
+ EXPECT_FALSE(load_timing_info.receive_headers_end.is_null()); |
+ EXPECT_FALSE(load_timing_info.send_start.is_null()); |
+ EXPECT_FALSE(load_timing_info.send_end.is_null()); |
+ EXPECT_TRUE(load_timing_info.request_start < |
+ load_timing_info.receive_headers_end); |
+ EXPECT_TRUE(load_timing_info.send_start <= load_timing_info.send_end); |
+} |
+ |
+// Tests the load timing of a stream that's connected and is not the first |
+// request sent on a connection. |
+void TestLoadTimingReused(const LoadTimingInfo& load_timing_info) { |
+ EXPECT_TRUE(load_timing_info.socket_reused); |
+ |
+ ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
+ ExpectLoadTimingValid(load_timing_info); |
+} |
+ |
+// Tests the load timing of a stream that's connected and using a fresh |
+// connection. |
+void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info) { |
+ EXPECT_FALSE(load_timing_info.socket_reused); |
+ |
+ ExpectConnectTimingHasTimes( |
+ load_timing_info.connect_timing, |
+ CONNECT_TIMING_HAS_SSL_TIMES | CONNECT_TIMING_HAS_DNS_TIMES); |
+ ExpectLoadTimingValid(load_timing_info); |
+} |
+ |
// Delegate that reads data but does not send any data. |
class TestDelegateBase : public BidirectionalStream::Delegate { |
public: |
@@ -140,9 +174,11 @@ class TestDelegateBase : public BidirectionalStream::Delegate { |
stream_.reset(new BidirectionalStream(std::move(request_info), session, |
true, this, std::move(timer_))); |
if (run_until_completion_) |
- loop_->Run(); |
+ WaitUntilCompletion(); |
} |
+ void WaitUntilCompletion() { loop_->Run(); } |
+ |
void SendData(const scoped_refptr<IOBuffer>& data, |
int length, |
bool end_of_stream) { |
@@ -185,6 +221,7 @@ class TestDelegateBase : public BidirectionalStream::Delegate { |
next_proto_ = stream_->GetProtocol(); |
received_bytes_ = stream_->GetTotalReceivedBytes(); |
sent_bytes_ = stream_->GetTotalSentBytes(); |
+ stream_->GetLoadTimingInfo(&load_timing_info_); |
stream_.reset(); |
} |
@@ -206,6 +243,14 @@ class TestDelegateBase : public BidirectionalStream::Delegate { |
return sent_bytes_; |
} |
+ void GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { |
+ if (stream_) { |
+ stream_->GetLoadTimingInfo(load_timing_info); |
+ return; |
+ } |
+ *load_timing_info = load_timing_info_; |
+ } |
+ |
// Const getters for internal states. |
const std::string& data_received() const { return data_received_; } |
int error() const { return error_; } |
@@ -240,6 +285,7 @@ class TestDelegateBase : public BidirectionalStream::Delegate { |
NextProto next_proto_; |
int64_t received_bytes_; |
int64_t sent_bytes_; |
+ LoadTimingInfo load_timing_info_; |
int error_; |
int on_data_read_count_; |
int on_data_sent_count_; |
@@ -406,6 +452,108 @@ TEST_F(BidirectionalStreamTest, CreateInsecureStream) { |
EXPECT_THAT(delegate.error(), IsError(ERR_DISALLOWED_URL_SCHEME)); |
} |
+TEST_F(BidirectionalStreamTest, SimplePostRequest) { |
+ SpdySerializedFrame req(spdy_util_.ConstructSpdyPost( |
+ kDefaultUrl, 1, kBodyDataSize, LOW, nullptr, 0)); |
+ SpdySerializedFrame data_frame(spdy_util_.ConstructSpdyDataFrame( |
+ 1, kBodyData, kBodyDataSize, /*fin=*/true)); |
+ MockWrite writes[] = { |
+ CreateMockWrite(req, 0), CreateMockWrite(data_frame, 3), |
+ }; |
+ SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); |
+ SpdySerializedFrame response_body_frame( |
+ spdy_util_.ConstructSpdyDataFrame(1, /*fin=*/true)); |
+ MockRead reads[] = { |
+ CreateMockRead(resp, 1), |
+ MockRead(ASYNC, ERR_IO_PENDING, 2), // Force a pause. |
+ CreateMockRead(response_body_frame, 4), MockRead(ASYNC, 0, 5), |
+ }; |
+ InitSession(reads, arraysize(reads), writes, arraysize(writes)); |
+ |
+ std::unique_ptr<BidirectionalStreamRequestInfo> request_info( |
+ new BidirectionalStreamRequestInfo); |
+ request_info->method = "POST"; |
+ request_info->url = default_url_; |
+ request_info->extra_headers.SetHeader(net::HttpRequestHeaders::kContentLength, |
+ base::SizeTToString(kBodyDataSize)); |
+ scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); |
+ std::unique_ptr<TestDelegateBase> delegate( |
+ new TestDelegateBase(read_buffer.get(), kReadBufferSize)); |
+ delegate->Start(std::move(request_info), http_session_.get()); |
+ sequenced_data_->RunUntilPaused(); |
+ |
+ scoped_refptr<StringIOBuffer> write_buffer( |
+ new StringIOBuffer(std::string(kBodyData, kBodyDataSize))); |
+ delegate->SendData(write_buffer.get(), write_buffer->size(), true); |
+ sequenced_data_->Resume(); |
+ base::RunLoop().RunUntilIdle(); |
+ LoadTimingInfo load_timing_info; |
+ delegate->GetLoadTimingInfo(&load_timing_info); |
+ TestLoadTimingNotReused(load_timing_info); |
+ |
+ EXPECT_EQ(1, delegate->on_data_read_count()); |
+ EXPECT_EQ(1, delegate->on_data_sent_count()); |
+ EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); |
+ EXPECT_EQ(CountWriteBytes(writes, arraysize(writes)), |
+ delegate->GetTotalSentBytes()); |
+ EXPECT_EQ(CountReadBytes(reads, arraysize(reads)), |
+ delegate->GetTotalReceivedBytes()); |
+} |
+ |
+TEST_F(BidirectionalStreamTest, LoadTimingTwoRequests) { |
+ SpdySerializedFrame req( |
+ spdy_util_.ConstructSpdyGet(nullptr, 0, /*stream_id=*/1, LOW, true)); |
+ SpdySerializedFrame req2( |
+ spdy_util_.ConstructSpdyGet(nullptr, 0, /*stream_id=*/3, LOW, true)); |
+ MockWrite writes[] = { |
+ CreateMockWrite(req, 0), CreateMockWrite(req2, 2), |
+ }; |
+ SpdySerializedFrame resp( |
+ spdy_util_.ConstructSpdyGetReply(nullptr, 0, /*stream_id=*/1)); |
+ SpdySerializedFrame resp2( |
+ spdy_util_.ConstructSpdyGetReply(nullptr, 0, /*stream_id=*/3)); |
+ SpdySerializedFrame resp_body( |
+ spdy_util_.ConstructSpdyDataFrame(/*stream_id=*/1, /*fin=*/true)); |
+ SpdySerializedFrame resp_body2( |
+ spdy_util_.ConstructSpdyDataFrame(/*stream_id=*/3, /*fin=*/true)); |
+ MockRead reads[] = {CreateMockRead(resp, 1), CreateMockRead(resp_body, 3), |
+ CreateMockRead(resp2, 4), CreateMockRead(resp_body2, 5), |
+ MockRead(ASYNC, 0, 6)}; |
+ InitSession(reads, arraysize(reads), writes, arraysize(writes)); |
+ |
+ std::unique_ptr<BidirectionalStreamRequestInfo> request_info( |
+ new BidirectionalStreamRequestInfo); |
+ request_info->method = "GET"; |
+ request_info->url = default_url_; |
+ request_info->end_stream_on_headers = true; |
+ std::unique_ptr<BidirectionalStreamRequestInfo> request_info2( |
+ new BidirectionalStreamRequestInfo); |
+ request_info2->method = "GET"; |
+ request_info2->url = default_url_; |
+ request_info2->end_stream_on_headers = true; |
+ |
+ scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); |
+ scoped_refptr<IOBuffer> read_buffer2(new IOBuffer(kReadBufferSize)); |
+ std::unique_ptr<TestDelegateBase> delegate( |
+ new TestDelegateBase(read_buffer.get(), kReadBufferSize)); |
+ std::unique_ptr<TestDelegateBase> delegate2( |
+ new TestDelegateBase(read_buffer2.get(), kReadBufferSize)); |
+ delegate->Start(std::move(request_info), http_session_.get()); |
+ delegate2->Start(std::move(request_info2), http_session_.get()); |
+ delegate->SetRunUntilCompletion(true); |
+ delegate2->SetRunUntilCompletion(true); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ delegate->WaitUntilCompletion(); |
+ delegate2->WaitUntilCompletion(); |
+ LoadTimingInfo load_timing_info; |
+ delegate->GetLoadTimingInfo(&load_timing_info); |
+ TestLoadTimingNotReused(load_timing_info); |
+ LoadTimingInfo load_timing_info2; |
+ delegate2->GetLoadTimingInfo(&load_timing_info2); |
+ TestLoadTimingReused(load_timing_info2); |
+} |
+ |
// Creates a BidirectionalStream with an insecure scheme. Destroy the stream |
// without waiting for the OnFailed task to be executed. |
TEST_F(BidirectionalStreamTest, |