// 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 #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" #include "net/base/io_buffer.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 { private: friend class base::RefCounted; }; REGISTER_SOCKET_PARAMS_FOR_POOL(MockTransportClientSocketPool, DummySocketParams); namespace { class MockPipelineDelegate : public HttpPipelinedConnection::Delegate { public: MOCK_METHOD1(OnPipelineHasCapacity, void(HttpPipelinedConnection* pipeline)); MOCK_METHOD2(OnPipelineFeedback, void( HttpPipelinedConnection* pipeline, HttpPipelinedConnection::Feedback feedback)); }; class SuddenCloseObserver : public MessageLoop::TaskObserver { public: SuddenCloseObserver(HttpStream* stream, int close_before_task) : stream_(stream), close_before_task_(close_before_task), current_task_(0) { } virtual void WillProcessTask(base::TimeTicks) OVERRIDE { ++current_task_; if (current_task_ == close_before_task_) { stream_->Close(false); MessageLoop::current()->RemoveTaskObserver(this); } } virtual void DidProcessTask(base::TimeTicks) 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() { MessageLoop::current()->RunAllPending(); } void Initialize(MockRead* reads, size_t reads_count, MockWrite* writes, size_t writes_count) { data_ = 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 params; ClientSocketHandle* connection = new ClientSocketHandle; connection->Init("a", params, MEDIUM, CompletionCallback(), &pool_, BoundNetLog()); pipeline_.reset(new HttpPipelinedConnectionImpl( connection, &delegate_, origin_, ssl_config_, proxy_info_, BoundNetLog(), false, SSLClientSocket::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, BoundNetLog(), CompletionCallback()); DCHECK_EQ(OK, rv); return stream; } void ExpectResponse(const std::string& expected, scoped_ptr& stream, bool async) { scoped_refptr 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(expected.size()), callback_.WaitForResult()); } else { EXPECT_EQ(static_cast(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& stream, const std::string& filename) { HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, stream->SendRequest( headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback())); ExpectResponse(filename, stream, false); stream->Close(false); } DeterministicMockClientSocketFactory factory_; ClientSocketPoolHistograms histograms_; MockTransportClientSocketPool pool_; scoped_refptr data_; HostPortPair origin_; SSLConfig ssl_config_; ProxyInfo proxy_info_; NiceMock delegate_; TestCompletionCallback callback_; scoped_ptr pipeline_; ScopedVector request_info_vector_; }; TEST_F(HttpPipelinedConnectionImplTest, PipelineNotUsed) { Initialize(NULL, 0, NULL, 0); } TEST_F(HttpPipelinedConnectionImplTest, StreamNotUsed) { Initialize(NULL, 0, NULL, 0); scoped_ptr stream(pipeline_->CreateNewStream()); stream->Close(false); } TEST_F(HttpPipelinedConnectionImplTest, StreamBoundButNotUsed) { Initialize(NULL, 0, NULL, 0); scoped_ptr stream(NewTestStream("ok.html")); stream->Close(false); } 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 stream(NewTestStream("ok.html")); TestSyncRequest(stream, "ok.html"); } 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 stream(NewTestStream("ok.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, NULL, &response, callback_.callback())); 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); } 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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, NULL, &response2, callback_.callback())); 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); stream1->Close(false); data_->RunFor(2); EXPECT_LE(OK, callback_.WaitForResult()); ExpectResponse("ko.html", stream2, true); 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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); TestSyncRequest(stream2, "ko.html"); TestSyncRequest(stream1, "ok.html"); } 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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); TestCompletionCallback callback1; std::string expected = "ok.html"; scoped_refptr 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, NULL, &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(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 unused_stream(NewTestStream("unused.html")); unused_stream->Close(false); scoped_ptr 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 stream(NewTestStream("ok.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, NULL, &response, callback_.callback())); scoped_ptr unsent_stream(NewTestStream("unsent.html")); HttpRequestHeaders unsent_headers; HttpResponseInfo unsent_response; EXPECT_EQ(ERR_IO_PENDING, unsent_stream->SendRequest(unsent_headers, NULL, &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 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 failed_stream(NewTestStream("ok.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); scoped_ptr closed_stream(NewTestStream("closed.html")); scoped_ptr rejected_stream(NewTestStream("rejected.html")); HttpRequestHeaders headers; HttpResponseInfo response; TestCompletionCallback failed_callback; EXPECT_EQ(ERR_IO_PENDING, failed_stream->SendRequest(headers, NULL, &response, failed_callback.callback())); TestCompletionCallback evicted_callback; EXPECT_EQ(ERR_IO_PENDING, evicted_stream->SendRequest(headers, NULL, &response, evicted_callback.callback())); EXPECT_EQ(ERR_IO_PENDING, closed_stream->SendRequest(headers, NULL, &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, NULL, &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"), }; Initialize(reads, arraysize(reads), writes, arraysize(writes)); scoped_ptr closed_stream(NewTestStream("ok.html")); scoped_ptr read_evicted_stream( NewTestStream("read_evicted.html")); scoped_ptr read_rejected_stream( NewTestStream("read_rejected.html")); scoped_ptr send_closed_stream( NewTestStream("send_closed.html")); scoped_ptr send_evicted_stream( NewTestStream("send_evicted.html")); scoped_ptr send_rejected_stream( NewTestStream("send_rejected.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, closed_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, read_evicted_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, read_rejected_stream->SendRequest(headers, NULL, &response, callback_.callback())); TestCompletionCallback send_closed_callback; EXPECT_EQ(ERR_IO_PENDING, send_closed_stream->SendRequest(headers, NULL, &response, send_closed_callback.callback())); TestCompletionCallback send_evicted_callback; EXPECT_EQ(ERR_IO_PENDING, send_evicted_stream->SendRequest(headers, NULL, &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, NULL, &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 aborted_stream(NewTestStream("aborts.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; TestCompletionCallback aborted_callback; EXPECT_EQ(ERR_IO_PENDING, aborted_stream->SendRequest(headers, NULL, &response, aborted_callback.callback())); TestCompletionCallback evicted_callback; EXPECT_EQ(ERR_IO_PENDING, evicted_stream->SendRequest(headers, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr aborted_stream(NewTestStream("aborts.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; TestCompletionCallback ok_callback; EXPECT_EQ(ERR_IO_PENDING, ok_stream->SendRequest(headers, NULL, &response, ok_callback.callback())); TestCompletionCallback aborted_callback; EXPECT_EQ(ERR_IO_PENDING, aborted_stream->SendRequest(headers, NULL, &response, aborted_callback.callback())); TestCompletionCallback evicted_callback; EXPECT_EQ(ERR_IO_PENDING, evicted_stream->SendRequest(headers, NULL, &response, evicted_callback.callback())); data_->RunFor(1); EXPECT_LE(OK, ok_callback.WaitForResult()); MessageLoop::current()->RunAllPending(); 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 aborted_stream(NewTestStream("aborts.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); scoped_ptr rejected_stream(NewTestStream("rejected.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, aborted_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest(headers, NULL, &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, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr abandoned_stream(NewTestStream("abandoned.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, abandoned_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest(headers, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr rejected_read_stream(NewTestStream("rejected.html")); scoped_ptr evicted_send_stream(NewTestStream("evicted.html")); scoped_ptr rejected_send_stream(NewTestStream("rejected.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, rejected_read_stream->SendRequest( headers, NULL, &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, NULL, &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, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest( headers, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest( headers, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest( headers, NULL, &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); MessageLoop::current()->AddTaskObserver(&observer); MessageLoop::current()->RunAllPending(); EXPECT_FALSE(evicted_callback.have_result()); } class StreamDeleter { public: StreamDeleter(HttpStream* stream) : stream_(stream), ALLOW_THIS_IN_INITIALIZER_LIST(callback_( base::Bind(&StreamDeleter::OnIOComplete, base::Unretained(this)))) { } const CompletionCallback& callback() { return callback_; } private: void OnIOComplete(int result) { delete stream_; } 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, NULL, &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, NULL, &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, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest(headers, NULL, &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 deleter_stream(NewTestStream("deleter.html")); HttpStream* deleted_stream(NewTestStream("deleted.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, deleter_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, deleted_stream->SendRequest(headers, NULL, &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 close_stream(NewTestStream("close.html")); scoped_ptr dummy_stream(NewTestStream("dummy.html")); scoped_ptr close_callback( new TestCompletionCallback); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(ERR_IO_PENDING, close_stream->SendRequest( headers, NULL, &response, close_callback->callback())); data_->RunFor(1); EXPECT_FALSE(close_callback->have_result()); close_stream->Close(false); close_stream.reset(); close_callback.reset(); MessageLoop::current()->RunAllPending(); } 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 close_stream(NewTestStream("close.html")); scoped_ptr dummy_stream(NewTestStream("dummy.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, close_stream->SendRequest(headers, NULL, &response, callback_.callback())); scoped_ptr 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(); MessageLoop::current()->RunAllPending(); } 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 stream1(NewTestStream("ok.html")); scoped_ptr stream2(NewTestStream("ko.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; TestCompletionCallback callback1; EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, NULL, &response1, callback1.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; TestCompletionCallback callback2; EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, NULL, &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 close_stream(NewTestStream("close.html")); scoped_ptr dummy_stream(NewTestStream("dummy.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, close_stream->SendRequest(headers, NULL, &response, callback_.callback())); TestCompletionCallback close_callback; EXPECT_EQ(ERR_IO_PENDING, close_stream->ReadResponseHeaders(close_callback.callback())); EXPECT_EQ(OK, dummy_stream->SendRequest(headers, NULL, &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 stream1(NewTestStream("redirect.html")); scoped_ptr stream2(NewTestStream("ok.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("redirect.html")); scoped_ptr stream2(NewTestStream("ok.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("redirect.html")); scoped_ptr stream2(NewTestStream("ok.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 stream1(NewTestStream("redirect.html")); scoped_ptr stream2(NewTestStream("ok.html")); HttpRequestHeaders headers1; HttpResponseInfo response1; EXPECT_EQ(OK, stream1->SendRequest(headers1, NULL, &response1, callback_.callback())); HttpRequestHeaders headers2; HttpResponseInfo response2; EXPECT_EQ(OK, stream2->SendRequest(headers2, NULL, &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 ok_stream(NewTestStream("ok.html")); scoped_ptr evicted_stream(NewTestStream("evicted.html")); scoped_ptr rejected_stream(NewTestStream("rejected.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, ok_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, evicted_stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_EQ(OK, rejected_stream->SendRequest(headers, NULL, &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 stream(NewTestStream("ok.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, stream->SendRequest(headers, NULL, &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 stream(NewTestStream("ok.html")); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, stream->SendRequest(headers, NULL, &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 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 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 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 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 stream(NewTestStream("ok.html")); EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); HttpRequestHeaders headers; HttpResponseInfo response; EXPECT_EQ(OK, stream->SendRequest(headers, NULL, &response, callback_.callback())); EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0); MessageLoop::current()->RunAllPending(); 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 stream(NewTestStream("ok.html")); EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); MessageLoop::current()->RunAllPending(); stream->Close(false); EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); stream.reset(NULL); } } // anonymous namespace } // namespace net