summaryrefslogtreecommitdiff
path: root/chromium/net/http/http_pipelined_connection_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/http/http_pipelined_connection_impl_unittest.cc')
-rw-r--r--chromium/net/http/http_pipelined_connection_impl_unittest.cc1606
1 files changed, 1606 insertions, 0 deletions
diff --git a/chromium/net/http/http_pipelined_connection_impl_unittest.cc b/chromium/net/http/http_pipelined_connection_impl_unittest.cc
new file mode 100644
index 00000000000..1bf7597e33d
--- /dev/null
+++ b/chromium/net/http/http_pipelined_connection_impl_unittest.cc
@@ -0,0 +1,1606 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_pipelined_connection_impl.h"
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/io_buffer.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/base/request_priority.h"
+#include "net/http/http_pipelined_stream.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::StrEq;
+
+namespace net {
+
+class DummySocketParams : public base::RefCounted<DummySocketParams> {
+ private:
+ friend class base::RefCounted<DummySocketParams>;
+ ~DummySocketParams() {}
+};
+
+REGISTER_SOCKET_PARAMS_FOR_POOL(MockTransportClientSocketPool,
+ DummySocketParams);
+
+namespace {
+
+// Tests the load timing of a stream that's connected and is not the first
+// request sent on a connection.
+void TestLoadTimingReused(const HttpStream& stream) {
+ LoadTimingInfo load_timing_info;
+ EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
+
+ EXPECT_TRUE(load_timing_info.socket_reused);
+ EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
+
+ ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
+ ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
+}
+
+// Tests the load timing of a stream that's connected and using a fresh
+// connection.
+void TestLoadTimingNotReused(const HttpStream& stream) {
+ LoadTimingInfo load_timing_info;
+ EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info));
+
+ EXPECT_FALSE(load_timing_info.socket_reused);
+ EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id);
+
+ ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
+ CONNECT_TIMING_HAS_DNS_TIMES);
+ ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info);
+}
+
+class MockPipelineDelegate : public HttpPipelinedConnection::Delegate {
+ public:
+ MOCK_METHOD1(OnPipelineHasCapacity, void(HttpPipelinedConnection* pipeline));
+ MOCK_METHOD2(OnPipelineFeedback, void(
+ HttpPipelinedConnection* pipeline,
+ HttpPipelinedConnection::Feedback feedback));
+};
+
+class SuddenCloseObserver : public base::MessageLoop::TaskObserver {
+ public:
+ SuddenCloseObserver(HttpStream* stream, int close_before_task)
+ : stream_(stream),
+ close_before_task_(close_before_task),
+ current_task_(0) { }
+
+ virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
+ ++current_task_;
+ if (current_task_ == close_before_task_) {
+ stream_->Close(false);
+ base::MessageLoop::current()->RemoveTaskObserver(this);
+ }
+ }
+
+ virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {}
+
+ private:
+ HttpStream* stream_;
+ int close_before_task_;
+ int current_task_;
+};
+
+class HttpPipelinedConnectionImplTest : public testing::Test {
+ public:
+ HttpPipelinedConnectionImplTest()
+ : histograms_("a"),
+ pool_(1, 1, &histograms_, &factory_),
+ origin_("host", 123) {
+ }
+
+ void TearDown() {
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ void Initialize(MockRead* reads, size_t reads_count,
+ MockWrite* writes, size_t writes_count) {
+ data_.reset(new DeterministicSocketData(reads, reads_count,
+ writes, writes_count));
+ data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ if (reads_count || writes_count) {
+ data_->StopAfter(reads_count + writes_count);
+ }
+ factory_.AddSocketDataProvider(data_.get());
+ scoped_refptr<DummySocketParams> params;
+ ClientSocketHandle* connection = new ClientSocketHandle;
+ // Only give the connection a real NetLog to make sure that LoadTiming uses
+ // the connection's ID, rather than the pipeline's. Since pipelines are
+ // destroyed when they've responded to all requests, but the connection
+ // lives on, this is an important behavior.
+ connection->Init("a", params, MEDIUM, CompletionCallback(), &pool_,
+ net_log_.bound());
+ pipeline_.reset(new HttpPipelinedConnectionImpl(
+ connection, &delegate_, origin_, ssl_config_, proxy_info_,
+ BoundNetLog(), false, kProtoUnknown));
+ }
+
+ HttpRequestInfo* GetRequestInfo(const std::string& filename) {
+ HttpRequestInfo* request_info = new HttpRequestInfo;
+ request_info->url = GURL("http://localhost/" + filename);
+ request_info->method = "GET";
+ request_info_vector_.push_back(request_info);
+ return request_info;
+ }
+
+ HttpStream* NewTestStream(const std::string& filename) {
+ HttpStream* stream = pipeline_->CreateNewStream();
+ HttpRequestInfo* request_info = GetRequestInfo(filename);
+ int rv = stream->InitializeStream(
+ request_info, DEFAULT_PRIORITY, BoundNetLog(), CompletionCallback());
+ DCHECK_EQ(OK, rv);
+ return stream;
+ }
+
+ void ExpectResponse(const std::string& expected,
+ scoped_ptr<HttpStream>& stream, bool async) {
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
+
+ if (async) {
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream->ReadResponseBody(buffer.get(), expected.size(),
+ callback_.callback()));
+ data_->RunFor(1);
+ EXPECT_EQ(static_cast<int>(expected.size()), callback_.WaitForResult());
+ } else {
+ EXPECT_EQ(static_cast<int>(expected.size()),
+ stream->ReadResponseBody(buffer.get(), expected.size(),
+ callback_.callback()));
+ }
+ std::string actual(buffer->data(), expected.size());
+ EXPECT_THAT(actual, StrEq(expected));
+ }
+
+ void TestSyncRequest(scoped_ptr<HttpStream>& stream,
+ const std::string& filename) {
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse(filename, stream, false);
+
+ stream->Close(false);
+ }
+
+ CapturingBoundNetLog net_log_;
+ DeterministicMockClientSocketFactory factory_;
+ ClientSocketPoolHistograms histograms_;
+ MockTransportClientSocketPool pool_;
+ scoped_ptr<DeterministicSocketData> data_;
+
+ HostPortPair origin_;
+ SSLConfig ssl_config_;
+ ProxyInfo proxy_info_;
+ NiceMock<MockPipelineDelegate> delegate_;
+ TestCompletionCallback callback_;
+ scoped_ptr<HttpPipelinedConnectionImpl> pipeline_;
+ ScopedVector<HttpRequestInfo> request_info_vector_;
+};
+
+TEST_F(HttpPipelinedConnectionImplTest, PipelineNotUsed) {
+ Initialize(NULL, 0, NULL, 0);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, StreamNotUsed) {
+ Initialize(NULL, 0, NULL, 0);
+
+ scoped_ptr<HttpStream> stream(pipeline_->CreateNewStream());
+
+ stream->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, StreamBoundButNotUsed) {
+ Initialize(NULL, 0, NULL, 0);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+
+ TestLoadTimingNotReused(*stream);
+ stream->Close(false);
+ TestLoadTimingNotReused(*stream);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, SyncSingleRequest) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 3, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ TestLoadTimingNotReused(*stream);
+ TestSyncRequest(stream, "ok.html");
+ TestLoadTimingNotReused(*stream);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AsyncSingleRequest) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(ASYNC, 3, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
+ callback_.callback()));
+ data_->RunFor(1);
+ EXPECT_LE(OK, callback_.WaitForResult());
+ TestLoadTimingNotReused(*stream);
+
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
+ data_->RunFor(2);
+ EXPECT_LE(OK, callback_.WaitForResult());
+ TestLoadTimingNotReused(*stream);
+
+ ExpectResponse("ok.html", stream, true);
+ TestLoadTimingNotReused(*stream);
+
+ stream->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, LockStepAsyncRequests) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(ASYNC, 4, "ok.html"),
+ MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(ASYNC, 7, "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1,
+ callback_.callback()));
+ TestLoadTimingNotReused(*stream1);
+
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
+ callback_.callback()));
+ TestLoadTimingReused(*stream2);
+
+ data_->RunFor(1);
+ EXPECT_LE(OK, callback_.WaitForResult());
+ data_->RunFor(1);
+ EXPECT_LE(OK, callback_.WaitForResult());
+
+ EXPECT_EQ(ERR_IO_PENDING, stream1->ReadResponseHeaders(callback_.callback()));
+ EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback()));
+
+ data_->RunFor(2);
+ EXPECT_LE(OK, callback_.WaitForResult());
+
+ ExpectResponse("ok.html", stream1, true);
+
+ TestLoadTimingNotReused(*stream1);
+ LoadTimingInfo load_timing_info1;
+ EXPECT_TRUE(stream1->GetLoadTimingInfo(&load_timing_info1));
+ stream1->Close(false);
+
+ data_->RunFor(2);
+ EXPECT_LE(OK, callback_.WaitForResult());
+
+ ExpectResponse("ko.html", stream2, true);
+
+ TestLoadTimingReused(*stream2);
+ LoadTimingInfo load_timing_info2;
+ EXPECT_TRUE(stream2->GetLoadTimingInfo(&load_timing_info2));
+ EXPECT_EQ(load_timing_info1.socket_log_id,
+ load_timing_info2.socket_log_id);
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, TwoResponsesInOnePacket) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 7\r\n\r\n"
+ "ok.html"
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 7\r\n\r\n"
+ "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", stream1, false);
+ stream1->Close(false);
+
+ EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ko.html", stream2, false);
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, SendOrderSwapped) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 4, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 3, "ko.html"),
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ TestSyncRequest(stream2, "ko.html");
+ TestSyncRequest(stream1, "ok.html");
+ TestLoadTimingNotReused(*stream1);
+ TestLoadTimingReused(*stream2);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, ReadOrderSwapped) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+ EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback()));
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", stream1, false);
+
+ stream1->Close(false);
+
+ EXPECT_LE(OK, callback_.WaitForResult());
+ ExpectResponse("ko.html", stream2, false);
+
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, SendWhileReading) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 3, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+ ExpectResponse("ok.html", stream1, false);
+ stream1->Close(false);
+
+ EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ko.html", stream2, false);
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AsyncSendWhileAsyncReadBlocked) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, 3, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(ASYNC, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ TestCompletionCallback callback1;
+ std::string expected = "ok.html";
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream1->ReadResponseBody(buffer.get(), expected.size(),
+ callback1.callback()));
+
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
+ callback2.callback()));
+
+ data_->RunFor(1);
+ EXPECT_LE(OK, callback2.WaitForResult());
+ EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback2.callback()));
+
+ data_->RunFor(1);
+ EXPECT_EQ(static_cast<int>(expected.size()), callback1.WaitForResult());
+ std::string actual(buffer->data(), expected.size());
+ EXPECT_THAT(actual, StrEq(expected));
+ stream1->Close(false);
+
+ data_->StopAfter(8);
+ EXPECT_LE(OK, callback2.WaitForResult());
+ ExpectResponse("ko.html", stream2, false);
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, UnusedStreamAllowsLaterUse) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 3, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> unused_stream(NewTestStream("unused.html"));
+ unused_stream->Close(false);
+
+ scoped_ptr<HttpStream> later_stream(NewTestStream("ok.html"));
+ TestSyncRequest(later_stream, "ok.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, UnsentStreamAllowsLaterUse) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 4, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
+ MockRead(ASYNC, 3, "ok.html"),
+ MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 7, "ko.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ scoped_ptr<HttpStream> unsent_stream(NewTestStream("unsent.html"));
+ HttpRequestHeaders unsent_headers;
+ HttpResponseInfo unsent_response;
+ EXPECT_EQ(ERR_IO_PENDING, unsent_stream->SendRequest(unsent_headers,
+ &unsent_response,
+ callback_.callback()));
+ unsent_stream->Close(false);
+
+ data_->RunFor(1);
+ EXPECT_LE(OK, callback_.WaitForResult());
+
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback()));
+ data_->RunFor(2);
+ EXPECT_LE(OK, callback_.WaitForResult());
+
+ ExpectResponse("ok.html", stream, true);
+
+ stream->Close(false);
+
+ data_->StopAfter(8);
+ scoped_ptr<HttpStream> later_stream(NewTestStream("ko.html"));
+ TestSyncRequest(later_stream, "ko.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FailedSend) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, ERR_FAILED),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> failed_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+ scoped_ptr<HttpStream> closed_stream(NewTestStream("closed.html"));
+ scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ TestCompletionCallback failed_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ failed_stream->SendRequest(headers, &response,
+ failed_callback.callback()));
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->SendRequest(headers, &response,
+ evicted_callback.callback()));
+ EXPECT_EQ(ERR_IO_PENDING, closed_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ closed_stream->Close(false);
+
+ data_->RunFor(1);
+ EXPECT_EQ(ERR_FAILED, failed_callback.WaitForResult());
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ rejected_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ failed_stream->Close(true);
+ evicted_stream->Close(true);
+ rejected_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, ConnectionSuddenlyClosedAfterResponse) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /read_evicted.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "GET /read_rejected.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(ASYNC, OK, 6), // Connection closed message. Not read before the
+ // ERR_SOCKET_NOT_CONNECTED.
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> closed_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> read_evicted_stream(
+ NewTestStream("read_evicted.html"));
+ scoped_ptr<HttpStream> read_rejected_stream(
+ NewTestStream("read_rejected.html"));
+ scoped_ptr<HttpStream> send_closed_stream(
+ NewTestStream("send_closed.html"));
+ scoped_ptr<HttpStream> send_evicted_stream(
+ NewTestStream("send_evicted.html"));
+ scoped_ptr<HttpStream> send_rejected_stream(
+ NewTestStream("send_rejected.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, closed_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, read_evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK, read_rejected_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ TestCompletionCallback send_closed_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ send_closed_stream->SendRequest(headers, &response,
+ send_closed_callback.callback()));
+ TestCompletionCallback send_evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ send_evicted_stream->SendRequest(headers, &response,
+ send_evicted_callback.callback()));
+
+ TestCompletionCallback read_evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ read_evicted_stream->ReadResponseHeaders(
+ read_evicted_callback.callback()));
+
+ EXPECT_EQ(OK, closed_stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", closed_stream, false);
+ closed_stream->Close(true);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, read_evicted_callback.WaitForResult());
+ read_evicted_stream->Close(true);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ read_rejected_stream->ReadResponseHeaders(callback_.callback()));
+ read_rejected_stream->Close(true);
+
+ data_->RunFor(1);
+ EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, send_closed_callback.WaitForResult());
+ send_closed_stream->Close(true);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, send_evicted_callback.WaitForResult());
+ send_evicted_stream->Close(true);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ send_rejected_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ send_rejected_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSending) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ TestCompletionCallback aborted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ aborted_stream->SendRequest(headers, &response,
+ aborted_callback.callback()));
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->SendRequest(headers, &response,
+ evicted_callback.callback()));
+
+ aborted_stream->Close(true);
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(true);
+ EXPECT_FALSE(aborted_callback.have_result());
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendingSecondRequest) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, 1, "GET /aborts.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ TestCompletionCallback ok_callback;
+ EXPECT_EQ(ERR_IO_PENDING, ok_stream->SendRequest(headers, &response,
+ ok_callback.callback()));
+ TestCompletionCallback aborted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ aborted_stream->SendRequest(headers, &response,
+ aborted_callback.callback()));
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->SendRequest(headers, &response,
+ evicted_callback.callback()));
+
+ data_->RunFor(1);
+ EXPECT_LE(OK, ok_callback.WaitForResult());
+ base::MessageLoop::current()->RunUntilIdle();
+ aborted_stream->Close(true);
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(true);
+ EXPECT_FALSE(aborted_callback.have_result());
+ ok_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AbortWhileReadingHeaders) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, ERR_FAILED, 2),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+ scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK,
+ aborted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK,
+ evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ aborted_stream->ReadResponseHeaders(callback_.callback()));
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ aborted_stream->Close(true);
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(true);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ rejected_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ rejected_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, PendingResponseAbandoned) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /abandoned.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 4, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 5, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> abandoned_stream(NewTestStream("abandoned.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK, abandoned_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
+ TestCompletionCallback abandoned_callback;
+ EXPECT_EQ(ERR_IO_PENDING, abandoned_stream->ReadResponseHeaders(
+ abandoned_callback.callback()));
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ abandoned_stream->Close(false);
+
+ ExpectResponse("ok.html", ok_stream, false);
+ ok_stream->Close(false);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(true);
+ EXPECT_FALSE(evicted_stream->IsConnectionReusable());
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, DisconnectedAfterOneRequestRecovery) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /rejected.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5),
+ MockWrite(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 7),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 6),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> rejected_read_stream(NewTestStream("rejected.html"));
+ scoped_ptr<HttpStream> evicted_send_stream(NewTestStream("evicted.html"));
+ scoped_ptr<HttpStream> rejected_send_stream(NewTestStream("rejected.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, rejected_read_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", ok_stream, false);
+ ok_stream->Close(false);
+
+ TestCompletionCallback read_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_send_stream->SendRequest(headers, &response,
+ read_callback.callback()));
+ data_->RunFor(1);
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, read_callback.WaitForResult());
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ rejected_read_stream->ReadResponseHeaders(callback_.callback()));
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ rejected_send_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ rejected_read_stream->Close(true);
+ rejected_send_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, DisconnectedPendingReadRecovery) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", ok_stream, false);
+
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ ok_stream->Close(false);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeNextReadLoop) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", ok_stream, false);
+
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ ok_stream->Close(false);
+ evicted_stream->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeReadCallback) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", ok_stream, false);
+
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ ok_stream->Close(false);
+
+ // The posted tasks should be:
+ // 1. DoReadHeadersLoop, which will post:
+ // 2. InvokeUserCallback
+ SuddenCloseObserver observer(evicted_stream.get(), 2);
+ base::MessageLoop::current()->AddTaskObserver(&observer);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(evicted_callback.have_result());
+}
+
+class StreamDeleter {
+ public:
+ StreamDeleter(HttpStream* stream)
+ : stream_(stream),
+ callback_(base::Bind(&StreamDeleter::OnIOComplete,
+ base::Unretained(this))) {
+ }
+
+ ~StreamDeleter() {
+ EXPECT_FALSE(stream_);
+ }
+
+ const CompletionCallback& callback() { return callback_; }
+
+ private:
+ void OnIOComplete(int result) {
+ stream_->Close(true);
+ stream_.reset();
+ }
+
+ scoped_ptr<HttpStream> stream_;
+ CompletionCallback callback_;
+};
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringSendCallback) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ HttpStream* stream(NewTestStream("ok.html"));
+
+ StreamDeleter deleter(stream);
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response,
+ deleter.callback()));
+ data_->RunFor(1);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringReadCallback) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ HttpStream* stream(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, stream->SendRequest(headers,
+ &response, callback_.callback()));
+
+ StreamDeleter deleter(stream);
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(deleter.callback()));
+ data_->RunFor(1);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest,
+ CloseCalledDuringReadCallbackWithPendingRead) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /failed.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ HttpStream* failed_stream(NewTestStream("failed.html"));
+ HttpStream* evicted_stream(NewTestStream("evicted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, failed_stream->SendRequest(headers, &response,
+ callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ StreamDeleter failed_deleter(failed_stream);
+ EXPECT_EQ(ERR_IO_PENDING,
+ failed_stream->ReadResponseHeaders(failed_deleter.callback()));
+ StreamDeleter evicted_deleter(evicted_stream);
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_deleter.callback()));
+ data_->RunFor(1);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseOtherDuringReadCallback) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /deleter.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /deleted.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> deleter_stream(NewTestStream("deleter.html"));
+ HttpStream* deleted_stream(NewTestStream("deleted.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, deleter_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, deleted_stream->SendRequest(headers,
+ &response, callback_.callback()));
+
+ StreamDeleter deleter(deleted_stream);
+ EXPECT_EQ(ERR_IO_PENDING,
+ deleter_stream->ReadResponseHeaders(deleter.callback()));
+ EXPECT_EQ(ERR_IO_PENDING,
+ deleted_stream->ReadResponseHeaders(callback_.callback()));
+ data_->RunFor(1);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeSendCallbackRuns) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, 1, "GET /dummy.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
+ scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
+
+ scoped_ptr<TestCompletionCallback> close_callback(
+ new TestCompletionCallback);
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(ERR_IO_PENDING,
+ close_stream->SendRequest(headers,
+ &response, close_callback->callback()));
+
+ data_->RunFor(1);
+ EXPECT_FALSE(close_callback->have_result());
+
+ close_stream->Close(false);
+ close_stream.reset();
+ close_callback.reset();
+
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeReadCallbackRuns) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 3, "GET /dummy.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
+ scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, close_stream->SendRequest(headers,
+ &response, callback_.callback()));
+
+ scoped_ptr<TestCompletionCallback> close_callback(
+ new TestCompletionCallback);
+ EXPECT_EQ(ERR_IO_PENDING,
+ close_stream->ReadResponseHeaders(close_callback->callback()));
+
+ data_->RunFor(1);
+ EXPECT_FALSE(close_callback->have_result());
+
+ close_stream->Close(false);
+ close_stream.reset();
+ close_callback.reset();
+
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendQueued) {
+ MockWrite writes[] = {
+ MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ko.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ TestCompletionCallback callback1;
+ EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1,
+ callback1.callback()));
+
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ TestCompletionCallback callback2;
+ EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2,
+ callback2.callback()));
+
+ stream2.reset();
+ stream1->Close(true);
+
+ EXPECT_FALSE(callback2.have_result());
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, NoGapBetweenCloseAndEviction) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "GET /dummy.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> close_stream(NewTestStream("close.html"));
+ scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, close_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ TestCompletionCallback close_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ close_stream->ReadResponseHeaders(close_callback.callback()));
+
+ EXPECT_EQ(OK, dummy_stream->SendRequest(headers, &response,
+ callback_.callback()));
+
+ TestCompletionCallback dummy_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ dummy_stream->ReadResponseHeaders(dummy_callback.callback()));
+
+ close_stream->Close(true);
+ close_stream.reset();
+
+ EXPECT_TRUE(dummy_callback.have_result());
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, dummy_callback.WaitForResult());
+ dummy_stream->Close(true);
+ dummy_stream.reset();
+ pipeline_.reset();
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, RecoverFromDrainOnRedirect) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2,
+ "HTTP/1.1 302 OK\r\n"
+ "Content-Length: 8\r\n\r\n"
+ "redirect"),
+ MockRead(SYNCHRONOUS, 3,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 7\r\n\r\n"
+ "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ stream1.release()->Drain(NULL);
+
+ EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback()));
+ ExpectResponse("ok.html", stream2, false);
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, EvictAfterDrainOfUnknownSize) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2,
+ "HTTP/1.1 302 OK\r\n\r\n"
+ "redirect"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ stream1.release()->Drain(NULL);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ stream2->ReadResponseHeaders(callback_.callback()));
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, EvictAfterFailedDrain) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2,
+ "HTTP/1.1 302 OK\r\n"
+ "Content-Length: 8\r\n\r\n"),
+ MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 3),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ stream1.release()->Drain(NULL);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ stream2->ReadResponseHeaders(callback_.callback()));
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, EvictIfDrainingChunkedEncoding) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 2,
+ "HTTP/1.1 302 OK\r\n"
+ "Transfer-Encoding: chunked\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 3,
+ "jibberish"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html"));
+ scoped_ptr<HttpStream> stream2(NewTestStream("ok.html"));
+
+ HttpRequestHeaders headers1;
+ HttpResponseInfo response1;
+ EXPECT_EQ(OK, stream1->SendRequest(headers1,
+ &response1, callback_.callback()));
+ HttpRequestHeaders headers2;
+ HttpResponseInfo response2;
+ EXPECT_EQ(OK, stream2->SendRequest(headers2,
+ &response2, callback_.callback()));
+
+
+ EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback()));
+ stream1.release()->Drain(NULL);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ stream2->ReadResponseHeaders(callback_.callback()));
+ stream2->Close(false);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, EvictionDueToMissingContentLength) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "GET /rejected.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ MockRead(SYNCHRONOUS, OK, 5),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html"));
+ scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html"));
+ scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html"));
+
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, ok_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, evicted_stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(OK, rejected_stream->SendRequest(headers,
+ &response, callback_.callback()));
+
+ TestCompletionCallback ok_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ ok_stream->ReadResponseHeaders(ok_callback.callback()));
+
+ TestCompletionCallback evicted_callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ evicted_stream->ReadResponseHeaders(evicted_callback.callback()));
+
+ data_->RunFor(1);
+ EXPECT_LE(OK, ok_callback.WaitForResult());
+ data_->StopAfter(10);
+
+ ExpectResponse("ok.html", ok_stream, false);
+ ok_stream->Close(false);
+
+ EXPECT_EQ(ERR_PIPELINE_EVICTION,
+ rejected_stream->ReadResponseHeaders(callback_.callback()));
+ rejected_stream->Close(true);
+ EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult());
+ evicted_stream->Close(true);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnSocketError) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, ERR_FAILED, 1),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_,
+ OnPipelineFeedback(
+ pipeline_.get(),
+ HttpPipelinedConnection::PIPELINE_SOCKET_ERROR))
+ .Times(1);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(ERR_FAILED, stream->ReadResponseHeaders(callback_.callback()));
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoInternetConnection) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED, 1),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_, OnPipelineFeedback(_, _))
+ .Times(0);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, stream->SendRequest(headers,
+ &response, callback_.callback()));
+ EXPECT_EQ(ERR_INTERNET_DISCONNECTED,
+ stream->ReadResponseHeaders(callback_.callback()));
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnHttp10) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Connection: keep-alive\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_,
+ OnPipelineFeedback(pipeline_.get(),
+ HttpPipelinedConnection::OLD_HTTP_VERSION))
+ .Times(1);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ TestSyncRequest(stream, "ok.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnMustClose) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Connection: close\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_,
+ OnPipelineFeedback(
+ pipeline_.get(),
+ HttpPipelinedConnection::MUST_CLOSE_CONNECTION))
+ .Times(1);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ TestSyncRequest(stream, "ok.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoContentLength) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 2, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_,
+ OnPipelineFeedback(
+ pipeline_.get(),
+ HttpPipelinedConnection::MUST_CLOSE_CONNECTION))
+ .Times(1);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ TestSyncRequest(stream, "ok.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnAuthenticationRequired) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Unauthorized\r\n"),
+ MockRead(SYNCHRONOUS, 2, "WWW-Authenticate: NTLM\r\n"),
+ MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"),
+ MockRead(SYNCHRONOUS, 4, "ok.html"),
+ };
+ Initialize(reads, arraysize(reads), writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_,
+ OnPipelineFeedback(
+ pipeline_.get(),
+ HttpPipelinedConnection::AUTHENTICATION_REQUIRED))
+ .Times(1);
+
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+ TestSyncRequest(stream, "ok.html");
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacity) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
+ HttpRequestHeaders headers;
+ HttpResponseInfo response;
+ EXPECT_EQ(OK, stream->SendRequest(headers,
+ &response, callback_.callback()));
+
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ stream->Close(false);
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
+ stream.reset(NULL);
+}
+
+TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacityWithoutSend) {
+ MockWrite writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"),
+ };
+ Initialize(NULL, 0, writes, arraysize(writes));
+
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0);
+ scoped_ptr<HttpStream> stream(NewTestStream("ok.html"));
+
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
+ base::MessageLoop::current()->RunUntilIdle();
+
+ stream->Close(false);
+ EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1);
+ stream.reset(NULL);
+}
+
+} // anonymous namespace
+
+} // namespace net