1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/memory/ref_counted.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "net/base/address_list.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/net_util.h"
16 #include "net/base/request_priority.h"
17 #include "net/dns/host_cache.h"
18 #include "net/dns/mock_host_resolver.h"
19 #include "net/http/http_auth_handler_mock.h"
20 #include "net/http/http_network_session.h"
21 #include "net/http/http_network_transaction.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_server_properties_impl.h"
24 #include "net/proxy/proxy_config_service.h"
25 #include "net/proxy/proxy_service.h"
26 #include "net/socket/client_socket_handle.h"
27 #include "net/socket/client_socket_pool_histograms.h"
28 #include "net/socket/client_socket_pool_manager.h"
29 #include "net/socket/socket_test_util.h"
30 #include "net/ssl/ssl_config_service_defaults.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
40 class SimpleProxyConfigService : public ProxyConfigService {
42 virtual void AddObserver(Observer* observer) OVERRIDE {
46 virtual void RemoveObserver(Observer* observer) OVERRIDE {
47 if (observer_ == observer) {
52 virtual ConfigAvailability GetLatestProxyConfig(
53 ProxyConfig* config) OVERRIDE {
58 void IncrementConfigId() {
59 config_.set_id(config_.id() + 1);
60 observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID);
68 class HttpPipelinedNetworkTransactionTest : public testing::Test {
70 HttpPipelinedNetworkTransactionTest()
72 pool_(1, 1, &histograms_, &factory_) {
75 void Initialize(bool force_http_pipelining) {
76 // Normally, this code could just go in SetUp(). For a few of these tests,
77 // we change the default number of sockets per group. That needs to be done
78 // before we construct the HttpNetworkSession.
79 proxy_config_service_ = new SimpleProxyConfigService();
80 proxy_service_.reset(new ProxyService(proxy_config_service_, NULL, NULL));
81 ssl_config_ = new SSLConfigServiceDefaults;
82 auth_handler_factory_.reset(new HttpAuthHandlerMock::Factory());
84 HttpNetworkSession::Params session_params;
85 session_params.client_socket_factory = &factory_;
86 session_params.proxy_service = proxy_service_.get();
87 session_params.host_resolver = &mock_resolver_;
88 session_params.ssl_config_service = ssl_config_.get();
89 session_params.http_auth_handler_factory = auth_handler_factory_.get();
90 session_params.http_server_properties =
91 http_server_properties_.GetWeakPtr();
92 session_params.force_http_pipelining = force_http_pipelining;
93 session_params.http_pipelining_enabled = true;
94 session_ = new HttpNetworkSession(session_params);
97 void AddExpectedConnection(MockRead* reads, size_t reads_count,
98 MockWrite* writes, size_t writes_count) {
99 DeterministicSocketData* data = new DeterministicSocketData(
100 reads, reads_count, writes, writes_count);
101 data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
102 if (reads_count || writes_count) {
103 data->StopAfter(reads_count + writes_count);
105 factory_.AddSocketDataProvider(data);
106 data_vector_.push_back(data);
109 enum RequestInfoOptions {
111 REQUEST_MAIN_RESOURCE,
114 HttpRequestInfo* GetRequestInfo(
115 const char* filename, RequestInfoOptions options = REQUEST_DEFAULT) {
116 std::string url = base::StringPrintf("http://localhost/%s", filename);
117 HttpRequestInfo* request_info = new HttpRequestInfo;
118 request_info->url = GURL(url);
119 request_info->method = "GET";
120 if (options == REQUEST_MAIN_RESOURCE) {
121 request_info->load_flags = LOAD_MAIN_FRAME;
123 request_info_vector_.push_back(request_info);
127 void ExpectResponse(const std::string& expected,
128 HttpNetworkTransaction& transaction,
130 scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size()));
131 if (io_mode == ASYNC) {
132 EXPECT_EQ(ERR_IO_PENDING, transaction.Read(buffer.get(), expected.size(),
133 callback_.callback()));
134 data_vector_[0]->RunFor(1);
135 EXPECT_EQ(static_cast<int>(expected.length()), callback_.WaitForResult());
137 EXPECT_EQ(static_cast<int>(expected.size()),
138 transaction.Read(buffer.get(), expected.size(),
139 callback_.callback()));
141 std::string actual(buffer->data(), expected.size());
142 EXPECT_THAT(actual, StrEq(expected));
143 EXPECT_EQ(OK, transaction.Read(buffer.get(), expected.size(),
144 callback_.callback()));
147 void CompleteTwoRequests(int data_index, int stop_at_step) {
148 scoped_ptr<HttpNetworkTransaction> one_transaction(
149 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
150 TestCompletionCallback one_callback;
151 EXPECT_EQ(ERR_IO_PENDING,
152 one_transaction->Start(GetRequestInfo("one.html"),
153 one_callback.callback(), BoundNetLog()));
154 EXPECT_EQ(OK, one_callback.WaitForResult());
156 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
157 TestCompletionCallback two_callback;
158 EXPECT_EQ(ERR_IO_PENDING,
159 two_transaction.Start(GetRequestInfo("two.html"),
160 two_callback.callback(), BoundNetLog()));
162 TestCompletionCallback one_read_callback;
163 scoped_refptr<IOBuffer> buffer(new IOBuffer(8));
164 EXPECT_EQ(ERR_IO_PENDING,
165 one_transaction->Read(buffer.get(), 8,
166 one_read_callback.callback()));
168 data_vector_[data_index]->SetStop(stop_at_step);
169 data_vector_[data_index]->Run();
170 EXPECT_EQ(8, one_read_callback.WaitForResult());
171 data_vector_[data_index]->SetStop(10);
172 std::string actual(buffer->data(), 8);
173 EXPECT_THAT(actual, StrEq("one.html"));
174 EXPECT_EQ(OK, one_transaction->Read(buffer.get(), 8,
175 one_read_callback.callback()));
177 EXPECT_EQ(OK, two_callback.WaitForResult());
178 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
181 void CompleteFourRequests(RequestInfoOptions options) {
182 scoped_ptr<HttpNetworkTransaction> one_transaction(
183 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
184 TestCompletionCallback one_callback;
185 EXPECT_EQ(ERR_IO_PENDING,
186 one_transaction->Start(GetRequestInfo("one.html", options),
187 one_callback.callback(), BoundNetLog()));
188 EXPECT_EQ(OK, one_callback.WaitForResult());
190 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
191 TestCompletionCallback two_callback;
192 EXPECT_EQ(ERR_IO_PENDING,
193 two_transaction.Start(GetRequestInfo("two.html", options),
194 two_callback.callback(), BoundNetLog()));
196 HttpNetworkTransaction three_transaction(DEFAULT_PRIORITY, session_.get());
197 TestCompletionCallback three_callback;
198 EXPECT_EQ(ERR_IO_PENDING,
199 three_transaction.Start(GetRequestInfo("three.html", options),
200 three_callback.callback(),
203 HttpNetworkTransaction four_transaction(DEFAULT_PRIORITY, session_.get());
204 TestCompletionCallback four_callback;
205 EXPECT_EQ(ERR_IO_PENDING,
206 four_transaction.Start(GetRequestInfo("four.html", options),
207 four_callback.callback(), BoundNetLog()));
209 ExpectResponse("one.html", *one_transaction.get(), SYNCHRONOUS);
210 EXPECT_EQ(OK, two_callback.WaitForResult());
211 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
212 EXPECT_EQ(OK, three_callback.WaitForResult());
213 ExpectResponse("three.html", three_transaction, SYNCHRONOUS);
215 one_transaction.reset();
216 EXPECT_EQ(OK, four_callback.WaitForResult());
217 ExpectResponse("four.html", four_transaction, SYNCHRONOUS);
220 DeterministicMockClientSocketFactory factory_;
221 ClientSocketPoolHistograms histograms_;
222 MockTransportClientSocketPool pool_;
223 ScopedVector<DeterministicSocketData> data_vector_;
224 TestCompletionCallback callback_;
225 ScopedVector<HttpRequestInfo> request_info_vector_;
227 SimpleProxyConfigService* proxy_config_service_;
228 scoped_ptr<ProxyService> proxy_service_;
229 MockHostResolver mock_resolver_;
230 scoped_refptr<SSLConfigService> ssl_config_;
231 scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory_;
232 HttpServerPropertiesImpl http_server_properties_;
233 scoped_refptr<HttpNetworkSession> session_;
236 TEST_F(HttpPipelinedNetworkTransactionTest, OneRequest) {
239 MockWrite writes[] = {
240 MockWrite(SYNCHRONOUS, 0, "GET /test.html HTTP/1.1\r\n"
241 "Host: localhost\r\n"
242 "Connection: keep-alive\r\n\r\n"),
245 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
246 MockRead(SYNCHRONOUS, 2, "Content-Length: 9\r\n\r\n"),
247 MockRead(SYNCHRONOUS, 3, "test.html"),
249 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
251 HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
252 EXPECT_EQ(ERR_IO_PENDING,
253 transaction.Start(GetRequestInfo("test.html"), callback_.callback(),
255 EXPECT_EQ(OK, callback_.WaitForResult());
256 ExpectResponse("test.html", transaction, SYNCHRONOUS);
259 TEST_F(HttpPipelinedNetworkTransactionTest, ReusePipeline) {
262 MockWrite writes[] = {
263 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
264 "Host: localhost\r\n"
265 "Connection: keep-alive\r\n\r\n"),
266 MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
267 "Host: localhost\r\n"
268 "Connection: keep-alive\r\n\r\n"),
271 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
272 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
273 MockRead(ASYNC, 4, "one.html"),
274 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
275 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
276 MockRead(SYNCHRONOUS, 7, "two.html"),
278 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
280 CompleteTwoRequests(0, 5);
283 TEST_F(HttpPipelinedNetworkTransactionTest, ReusesOnSpaceAvailable) {
284 int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
285 HttpNetworkSession::NORMAL_SOCKET_POOL);
286 ClientSocketPoolManager::set_max_sockets_per_group(
287 HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
290 MockWrite writes[] = {
291 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
292 "Host: localhost\r\n"
293 "Connection: keep-alive\r\n\r\n"),
294 MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
295 "Host: localhost\r\n"
296 "Connection: keep-alive\r\n\r\n"),
297 MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n"
298 "Host: localhost\r\n"
299 "Connection: keep-alive\r\n\r\n"),
300 MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
301 "Host: localhost\r\n"
302 "Connection: keep-alive\r\n\r\n"),
305 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
306 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
307 MockRead(SYNCHRONOUS, 3, "one.html"),
308 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
309 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
310 MockRead(SYNCHRONOUS, 8, "two.html"),
311 MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
312 MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
313 MockRead(SYNCHRONOUS, 11, "three.html"),
314 MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
315 MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
316 MockRead(SYNCHRONOUS, 15, "four.html"),
318 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
320 CompleteFourRequests(REQUEST_DEFAULT);
322 ClientSocketPoolManager::set_max_sockets_per_group(
323 HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
326 TEST_F(HttpPipelinedNetworkTransactionTest, WontPipelineMainResource) {
327 int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
328 HttpNetworkSession::NORMAL_SOCKET_POOL);
329 ClientSocketPoolManager::set_max_sockets_per_group(
330 HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
333 MockWrite writes[] = {
334 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
335 "Host: localhost\r\n"
336 "Connection: keep-alive\r\n\r\n"),
337 MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
338 "Host: localhost\r\n"
339 "Connection: keep-alive\r\n\r\n"),
340 MockWrite(SYNCHRONOUS, 8, "GET /three.html HTTP/1.1\r\n"
341 "Host: localhost\r\n"
342 "Connection: keep-alive\r\n\r\n"),
343 MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
344 "Host: localhost\r\n"
345 "Connection: keep-alive\r\n\r\n"),
348 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
349 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
350 MockRead(SYNCHRONOUS, 3, "one.html"),
351 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
352 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
353 MockRead(SYNCHRONOUS, 7, "two.html"),
354 MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
355 MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
356 MockRead(SYNCHRONOUS, 11, "three.html"),
357 MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
358 MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
359 MockRead(SYNCHRONOUS, 15, "four.html"),
361 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
363 CompleteFourRequests(REQUEST_MAIN_RESOURCE);
365 ClientSocketPoolManager::set_max_sockets_per_group(
366 HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
369 TEST_F(HttpPipelinedNetworkTransactionTest, UnknownSizeEvictsToNewPipeline) {
372 MockWrite writes[] = {
373 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
374 "Host: localhost\r\n"
375 "Connection: keep-alive\r\n\r\n"),
378 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
379 MockRead(ASYNC, 2, "one.html"),
380 MockRead(SYNCHRONOUS, OK, 3),
382 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
384 MockWrite writes2[] = {
385 MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
386 "Host: localhost\r\n"
387 "Connection: keep-alive\r\n\r\n"),
389 MockRead reads2[] = {
390 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
391 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
392 MockRead(SYNCHRONOUS, 3, "two.html"),
394 AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
396 CompleteTwoRequests(0, 3);
399 TEST_F(HttpPipelinedNetworkTransactionTest, ConnectionCloseEvictToNewPipeline) {
402 MockWrite writes[] = {
403 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
404 "Host: localhost\r\n"
405 "Connection: keep-alive\r\n\r\n"),
406 MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
407 "Host: localhost\r\n"
408 "Connection: keep-alive\r\n\r\n"),
411 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
412 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
413 MockRead(ASYNC, 4, "one.html"),
414 MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5),
416 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
418 MockWrite writes2[] = {
419 MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
420 "Host: localhost\r\n"
421 "Connection: keep-alive\r\n\r\n"),
423 MockRead reads2[] = {
424 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
425 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
426 MockRead(SYNCHRONOUS, 3, "two.html"),
428 AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
430 CompleteTwoRequests(0, 5);
433 TEST_F(HttpPipelinedNetworkTransactionTest, ErrorEvictsToNewPipeline) {
436 MockWrite writes[] = {
437 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
438 "Host: localhost\r\n"
439 "Connection: keep-alive\r\n\r\n"),
440 MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
441 "Host: localhost\r\n"
442 "Connection: keep-alive\r\n\r\n"),
445 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"),
446 MockRead(SYNCHRONOUS, ERR_FAILED, 2),
448 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
450 MockWrite writes2[] = {
451 MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
452 "Host: localhost\r\n"
453 "Connection: keep-alive\r\n\r\n"),
455 MockRead reads2[] = {
456 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
457 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
458 MockRead(SYNCHRONOUS, 3, "two.html"),
460 AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
462 HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
463 TestCompletionCallback one_callback;
464 EXPECT_EQ(ERR_IO_PENDING,
465 one_transaction.Start(GetRequestInfo("one.html"),
466 one_callback.callback(), BoundNetLog()));
467 EXPECT_EQ(OK, one_callback.WaitForResult());
469 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
470 TestCompletionCallback two_callback;
471 EXPECT_EQ(ERR_IO_PENDING,
472 two_transaction.Start(GetRequestInfo("two.html"),
473 two_callback.callback(), BoundNetLog()));
475 scoped_refptr<IOBuffer> buffer(new IOBuffer(1));
476 EXPECT_EQ(ERR_FAILED,
477 one_transaction.Read(buffer.get(), 1, callback_.callback()));
478 EXPECT_EQ(OK, two_callback.WaitForResult());
479 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
482 TEST_F(HttpPipelinedNetworkTransactionTest, SendErrorEvictsToNewPipeline) {
485 MockWrite writes[] = {
486 MockWrite(ASYNC, ERR_FAILED, 0),
488 AddExpectedConnection(NULL, 0, writes, arraysize(writes));
490 MockWrite writes2[] = {
491 MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
492 "Host: localhost\r\n"
493 "Connection: keep-alive\r\n\r\n"),
495 MockRead reads2[] = {
496 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
497 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
498 MockRead(SYNCHRONOUS, 3, "two.html"),
500 AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
502 HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
503 TestCompletionCallback one_callback;
504 EXPECT_EQ(ERR_IO_PENDING,
505 one_transaction.Start(GetRequestInfo("one.html"),
506 one_callback.callback(), BoundNetLog()));
508 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
509 TestCompletionCallback two_callback;
510 EXPECT_EQ(ERR_IO_PENDING,
511 two_transaction.Start(GetRequestInfo("two.html"),
512 two_callback.callback(), BoundNetLog()));
514 data_vector_[0]->RunFor(1);
515 EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
517 EXPECT_EQ(OK, two_callback.WaitForResult());
518 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
521 TEST_F(HttpPipelinedNetworkTransactionTest, RedirectDrained) {
524 MockWrite writes[] = {
525 MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n"
526 "Host: localhost\r\n"
527 "Connection: keep-alive\r\n\r\n"),
528 MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n"
529 "Host: localhost\r\n"
530 "Connection: keep-alive\r\n\r\n"),
533 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 OK\r\n"),
534 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
535 MockRead(ASYNC, 4, "redirect"),
536 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
537 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
538 MockRead(SYNCHRONOUS, 7, "two.html"),
540 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
542 scoped_ptr<HttpNetworkTransaction> one_transaction(
543 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
544 TestCompletionCallback one_callback;
545 EXPECT_EQ(ERR_IO_PENDING,
546 one_transaction->Start(GetRequestInfo("redirect.html"),
547 one_callback.callback(), BoundNetLog()));
548 EXPECT_EQ(OK, one_callback.WaitForResult());
550 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
551 TestCompletionCallback two_callback;
552 EXPECT_EQ(ERR_IO_PENDING,
553 two_transaction.Start(GetRequestInfo("two.html"),
554 two_callback.callback(), BoundNetLog()));
556 one_transaction.reset();
557 data_vector_[0]->RunFor(2);
558 data_vector_[0]->SetStop(10);
560 EXPECT_EQ(OK, two_callback.WaitForResult());
561 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
564 TEST_F(HttpPipelinedNetworkTransactionTest, BasicHttpAuthentication) {
567 MockWrite writes[] = {
568 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
569 "Host: localhost\r\n"
570 "Connection: keep-alive\r\n\r\n"),
571 MockWrite(SYNCHRONOUS, 5, "GET /one.html HTTP/1.1\r\n"
572 "Host: localhost\r\n"
573 "Connection: keep-alive\r\n"
574 "Authorization: auth_token\r\n\r\n"),
577 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Authentication Required\r\n"),
578 MockRead(SYNCHRONOUS, 2,
579 "WWW-Authenticate: Basic realm=\"Secure Area\"\r\n"),
580 MockRead(SYNCHRONOUS, 3, "Content-Length: 20\r\n\r\n"),
581 MockRead(SYNCHRONOUS, 4, "needs authentication"),
582 MockRead(SYNCHRONOUS, 6, "HTTP/1.1 200 OK\r\n"),
583 MockRead(SYNCHRONOUS, 7, "Content-Length: 8\r\n\r\n"),
584 MockRead(SYNCHRONOUS, 8, "one.html"),
586 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
588 HttpAuthHandlerMock* mock_auth = new HttpAuthHandlerMock;
589 std::string challenge_text = "Basic";
590 HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(),
591 challenge_text.end());
592 GURL origin("localhost");
593 EXPECT_TRUE(mock_auth->InitFromChallenge(&challenge,
594 HttpAuth::AUTH_SERVER,
597 auth_handler_factory_->AddMockHandler(mock_auth, HttpAuth::AUTH_SERVER);
599 HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
600 EXPECT_EQ(ERR_IO_PENDING,
601 transaction.Start(GetRequestInfo("one.html"),
602 callback_.callback(),
604 EXPECT_EQ(OK, callback_.WaitForResult());
606 AuthCredentials credentials(ASCIIToUTF16("user"), ASCIIToUTF16("pass"));
607 EXPECT_EQ(OK, transaction.RestartWithAuth(credentials, callback_.callback()));
609 ExpectResponse("one.html", transaction, SYNCHRONOUS);
612 TEST_F(HttpPipelinedNetworkTransactionTest, OldVersionDisablesPipelining) {
615 MockWrite writes[] = {
616 MockWrite(SYNCHRONOUS, 0, "GET /pipelined.html HTTP/1.1\r\n"
617 "Host: localhost\r\n"
618 "Connection: keep-alive\r\n\r\n"),
621 MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"),
622 MockRead(SYNCHRONOUS, 2, "Content-Length: 14\r\n\r\n"),
623 MockRead(SYNCHRONOUS, 3, "pipelined.html"),
625 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
627 MockWrite writes2[] = {
628 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
629 "Host: localhost\r\n"
630 "Connection: keep-alive\r\n\r\n"),
632 MockRead reads2[] = {
633 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
634 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
635 MockRead(ASYNC, 3, "one.html"),
636 MockRead(SYNCHRONOUS, OK, 4),
638 AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2));
640 MockWrite writes3[] = {
641 MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n"
642 "Host: localhost\r\n"
643 "Connection: keep-alive\r\n\r\n"),
645 MockRead reads3[] = {
646 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
647 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
648 MockRead(SYNCHRONOUS, 3, "two.html"),
649 MockRead(SYNCHRONOUS, OK, 4),
651 AddExpectedConnection(reads3, arraysize(reads3), writes3, arraysize(writes3));
653 HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
654 TestCompletionCallback one_callback;
655 EXPECT_EQ(ERR_IO_PENDING,
656 one_transaction.Start(GetRequestInfo("pipelined.html"),
657 one_callback.callback(), BoundNetLog()));
658 EXPECT_EQ(OK, one_callback.WaitForResult());
659 ExpectResponse("pipelined.html", one_transaction, SYNCHRONOUS);
661 CompleteTwoRequests(1, 4);
664 TEST_F(HttpPipelinedNetworkTransactionTest, PipelinesImmediatelyIfKnownGood) {
665 // The first request gets us an HTTP/1.1. The next 3 test pipelining. When the
666 // 3rd request completes, we know pipelining is safe. After the first 4
667 // complete, the 5th and 6th should then be immediately sent pipelined on a
668 // new HttpPipelinedConnection.
669 int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group(
670 HttpNetworkSession::NORMAL_SOCKET_POOL);
671 ClientSocketPoolManager::set_max_sockets_per_group(
672 HttpNetworkSession::NORMAL_SOCKET_POOL, 1);
675 MockWrite writes[] = {
676 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
677 "Host: localhost\r\n"
678 "Connection: keep-alive\r\n\r\n"),
679 MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n"
680 "Host: localhost\r\n"
681 "Connection: keep-alive\r\n\r\n"),
682 MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n"
683 "Host: localhost\r\n"
684 "Connection: keep-alive\r\n\r\n"),
685 MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n"
686 "Host: localhost\r\n"
687 "Connection: keep-alive\r\n\r\n"),
688 MockWrite(SYNCHRONOUS, 16, "GET /second-pipeline-one.html HTTP/1.1\r\n"
689 "Host: localhost\r\n"
690 "Connection: keep-alive\r\n\r\n"),
691 MockWrite(SYNCHRONOUS, 17, "GET /second-pipeline-two.html HTTP/1.1\r\n"
692 "Host: localhost\r\n"
693 "Connection: keep-alive\r\n\r\n"),
696 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
697 MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"),
698 MockRead(SYNCHRONOUS, 3, "one.html"),
699 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
700 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
701 MockRead(SYNCHRONOUS, 8, "two.html"),
702 MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"),
703 MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"),
704 MockRead(SYNCHRONOUS, 11, "three.html"),
705 MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"),
706 MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"),
707 MockRead(SYNCHRONOUS, 15, "four.html"),
708 MockRead(ASYNC, 18, "HTTP/1.1 200 OK\r\n"),
709 MockRead(ASYNC, 19, "Content-Length: 24\r\n\r\n"),
710 MockRead(SYNCHRONOUS, 20, "second-pipeline-one.html"),
711 MockRead(SYNCHRONOUS, 21, "HTTP/1.1 200 OK\r\n"),
712 MockRead(SYNCHRONOUS, 22, "Content-Length: 24\r\n\r\n"),
713 MockRead(SYNCHRONOUS, 23, "second-pipeline-two.html"),
715 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
717 CompleteFourRequests(REQUEST_DEFAULT);
719 HttpNetworkTransaction second_one_transaction(
720 DEFAULT_PRIORITY, session_.get());
721 TestCompletionCallback second_one_callback;
722 EXPECT_EQ(ERR_IO_PENDING,
723 second_one_transaction.Start(
724 GetRequestInfo("second-pipeline-one.html"),
725 second_one_callback.callback(), BoundNetLog()));
726 base::MessageLoop::current()->RunUntilIdle();
728 HttpNetworkTransaction second_two_transaction(
729 DEFAULT_PRIORITY, session_.get());
730 TestCompletionCallback second_two_callback;
731 EXPECT_EQ(ERR_IO_PENDING,
732 second_two_transaction.Start(
733 GetRequestInfo("second-pipeline-two.html"),
734 second_two_callback.callback(), BoundNetLog()));
736 data_vector_[0]->RunFor(3);
737 EXPECT_EQ(OK, second_one_callback.WaitForResult());
738 data_vector_[0]->StopAfter(100);
739 ExpectResponse("second-pipeline-one.html", second_one_transaction,
741 EXPECT_EQ(OK, second_two_callback.WaitForResult());
742 ExpectResponse("second-pipeline-two.html", second_two_transaction,
745 ClientSocketPoolManager::set_max_sockets_per_group(
746 HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets);
749 class DataRunnerObserver : public base::MessageLoop::TaskObserver {
751 DataRunnerObserver(DeterministicSocketData* data, int run_before_task)
753 run_before_task_(run_before_task),
756 virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
758 if (current_task_ == run_before_task_) {
760 base::MessageLoop::current()->RemoveTaskObserver(this);
764 virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {}
767 DeterministicSocketData* data_;
768 int run_before_task_;
772 TEST_F(HttpPipelinedNetworkTransactionTest, OpenPipelinesWhileBinding) {
773 // There was a racy crash in the pipelining code. This test recreates that
774 // race. The steps are:
775 // 1. The first request starts a pipeline and requests headers.
776 // 2. HttpStreamFactoryImpl::Job tries to bind a pending request to a new
777 // pipeline and queues a task to do so.
778 // 3. Before that task runs, the first request receives its headers and
779 // determines this host is probably capable of pipelining.
780 // 4. All of the hosts' pipelines are notified they have capacity in a loop.
781 // 5. On the first iteration, the first pipeline is opened up to accept new
782 // requests and steals the request from step #2.
783 // 6. The pipeline from #2 is deleted because it has no streams.
784 // 7. On the second iteration, the host tries to notify the pipeline from step
785 // #2 that it has capacity. This is a use-after-free.
788 MockWrite writes[] = {
789 MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n"
790 "Host: localhost\r\n"
791 "Connection: keep-alive\r\n\r\n"),
792 MockWrite(ASYNC, 3, "GET /two.html HTTP/1.1\r\n"
793 "Host: localhost\r\n"
794 "Connection: keep-alive\r\n\r\n"),
797 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"),
798 MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"),
799 MockRead(SYNCHRONOUS, 4, "one.html"),
800 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"),
801 MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"),
802 MockRead(SYNCHRONOUS, 7, "two.html"),
804 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
806 AddExpectedConnection(NULL, 0, NULL, 0);
808 HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get());
809 TestCompletionCallback one_callback;
810 EXPECT_EQ(ERR_IO_PENDING,
811 one_transaction.Start(GetRequestInfo("one.html"),
812 one_callback.callback(), BoundNetLog()));
814 data_vector_[0]->SetStop(2);
815 data_vector_[0]->Run();
817 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
818 TestCompletionCallback two_callback;
819 EXPECT_EQ(ERR_IO_PENDING,
820 two_transaction.Start(GetRequestInfo("two.html"),
821 two_callback.callback(), BoundNetLog()));
822 // Posted tasks should be:
823 // 1. MockHostResolverBase::ResolveNow
824 // 2. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 1
825 // 3. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 2
827 // We need to make sure that the response that triggers OnPipelineFeedback(OK)
828 // is called in between when task #3 is scheduled and when it runs. The
829 // DataRunnerObserver does that.
830 DataRunnerObserver observer(data_vector_[0], 3);
831 base::MessageLoop::current()->AddTaskObserver(&observer);
832 data_vector_[0]->SetStop(4);
833 base::MessageLoop::current()->RunUntilIdle();
834 data_vector_[0]->SetStop(10);
836 EXPECT_EQ(OK, one_callback.WaitForResult());
837 ExpectResponse("one.html", one_transaction, SYNCHRONOUS);
838 EXPECT_EQ(OK, two_callback.WaitForResult());
839 ExpectResponse("two.html", two_transaction, SYNCHRONOUS);
842 TEST_F(HttpPipelinedNetworkTransactionTest, ProxyChangesWhileConnecting) {
845 DeterministicSocketData data(NULL, 0, NULL, 0);
846 data.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED));
847 factory_.AddSocketDataProvider(&data);
849 DeterministicSocketData data2(NULL, 0, NULL, 0);
850 data2.set_connect_data(MockConnect(ASYNC, ERR_FAILED));
851 factory_.AddSocketDataProvider(&data2);
853 HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get());
854 EXPECT_EQ(ERR_IO_PENDING,
855 transaction.Start(GetRequestInfo("test.html"), callback_.callback(),
858 proxy_config_service_->IncrementConfigId();
860 EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
863 TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineSharesConnection) {
866 MockWrite writes[] = {
867 MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n"
868 "Host: localhost\r\n"
869 "Connection: keep-alive\r\n\r\n"
870 "GET /two.html HTTP/1.1\r\n"
871 "Host: localhost\r\n"
872 "Connection: keep-alive\r\n\r\n"),
875 MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"),
876 MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"),
877 MockRead(ASYNC, 3, "one.html"),
878 MockRead(ASYNC, 4, "HTTP/1.1 200 OK\r\n"),
879 MockRead(ASYNC, 5, "Content-Length: 8\r\n\r\n"),
880 MockRead(ASYNC, 6, "two.html"),
882 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
884 scoped_ptr<HttpNetworkTransaction> one_transaction(
885 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
886 TestCompletionCallback one_callback;
887 EXPECT_EQ(ERR_IO_PENDING,
888 one_transaction->Start(GetRequestInfo("one.html"),
889 one_callback.callback(), BoundNetLog()));
891 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
892 TestCompletionCallback two_callback;
893 EXPECT_EQ(ERR_IO_PENDING,
894 two_transaction.Start(GetRequestInfo("two.html"),
895 two_callback.callback(), BoundNetLog()));
897 data_vector_[0]->RunFor(3); // Send + 2 lines of headers.
898 EXPECT_EQ(OK, one_callback.WaitForResult());
899 ExpectResponse("one.html", *one_transaction.get(), ASYNC);
900 one_transaction.reset();
902 data_vector_[0]->RunFor(2); // 2 lines of headers.
903 EXPECT_EQ(OK, two_callback.WaitForResult());
904 ExpectResponse("two.html", two_transaction, ASYNC);
907 TEST_F(HttpPipelinedNetworkTransactionTest,
908 ForcedPipelineConnectionErrorFailsBoth) {
911 DeterministicSocketData data(NULL, 0, NULL, 0);
912 data.set_connect_data(MockConnect(ASYNC, ERR_FAILED));
913 factory_.AddSocketDataProvider(&data);
915 scoped_ptr<HttpNetworkTransaction> one_transaction(
916 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
917 TestCompletionCallback one_callback;
918 EXPECT_EQ(ERR_IO_PENDING,
919 one_transaction->Start(GetRequestInfo("one.html"),
920 one_callback.callback(), BoundNetLog()));
922 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
923 TestCompletionCallback two_callback;
924 EXPECT_EQ(ERR_IO_PENDING,
925 two_transaction.Start(GetRequestInfo("two.html"),
926 two_callback.callback(), BoundNetLog()));
929 EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
930 EXPECT_EQ(ERR_FAILED, two_callback.WaitForResult());
933 TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineEvictionIsFatal) {
936 MockWrite writes[] = {
937 MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n"
938 "Host: localhost\r\n"
939 "Connection: keep-alive\r\n\r\n"
940 "GET /two.html HTTP/1.1\r\n"
941 "Host: localhost\r\n"
942 "Connection: keep-alive\r\n\r\n"),
945 MockRead(ASYNC, ERR_FAILED, 1),
947 AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes));
949 scoped_ptr<HttpNetworkTransaction> one_transaction(
950 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
951 TestCompletionCallback one_callback;
952 EXPECT_EQ(ERR_IO_PENDING,
953 one_transaction->Start(GetRequestInfo("one.html"),
954 one_callback.callback(), BoundNetLog()));
956 HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get());
957 TestCompletionCallback two_callback;
958 EXPECT_EQ(ERR_IO_PENDING,
959 two_transaction.Start(GetRequestInfo("two.html"),
960 two_callback.callback(), BoundNetLog()));
962 data_vector_[0]->RunFor(2);
963 EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
964 one_transaction.reset();
965 EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult());
968 TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineOrder) {
971 MockWrite writes[] = {
973 "GET /one.html HTTP/1.1\r\n"
974 "Host: localhost\r\n"
975 "Connection: keep-alive\r\n\r\n"
976 "GET /two.html HTTP/1.1\r\n"
977 "Host: localhost\r\n"
978 "Connection: keep-alive\r\n\r\n"
979 "GET /three.html HTTP/1.1\r\n"
980 "Host: localhost\r\n"
981 "Connection: keep-alive\r\n\r\n"
982 "GET /four.html HTTP/1.1\r\n"
983 "Host: localhost\r\n"
984 "Connection: keep-alive\r\n\r\n"
988 MockRead(ASYNC, ERR_FAILED, 1),
990 DeterministicSocketData data(
991 reads, arraysize(reads), writes, arraysize(writes));
992 data.set_connect_data(MockConnect(ASYNC, OK));
993 factory_.AddSocketDataProvider(&data);
995 scoped_ptr<HttpNetworkTransaction> one_transaction(
996 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
997 TestCompletionCallback one_callback;
998 EXPECT_EQ(ERR_IO_PENDING,
999 one_transaction->Start(GetRequestInfo("one.html"),
1000 one_callback.callback(), BoundNetLog()));
1002 scoped_ptr<HttpNetworkTransaction> two_transaction(
1003 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
1004 TestCompletionCallback two_callback;
1005 EXPECT_EQ(ERR_IO_PENDING,
1006 two_transaction->Start(GetRequestInfo("two.html"),
1007 two_callback.callback(), BoundNetLog()));
1009 scoped_ptr<HttpNetworkTransaction> three_transaction(
1010 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
1011 TestCompletionCallback three_callback;
1012 EXPECT_EQ(ERR_IO_PENDING,
1013 three_transaction->Start(GetRequestInfo("three.html"),
1014 three_callback.callback(), BoundNetLog()));
1016 scoped_ptr<HttpNetworkTransaction> four_transaction(
1017 new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
1018 TestCompletionCallback four_callback;
1019 EXPECT_EQ(ERR_IO_PENDING,
1020 four_transaction->Start(GetRequestInfo("four.html"),
1021 four_callback.callback(), BoundNetLog()));
1024 EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult());
1025 one_transaction.reset();
1026 EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult());
1027 two_transaction.reset();
1028 EXPECT_EQ(ERR_PIPELINE_EVICTION, three_callback.WaitForResult());
1029 three_transaction.reset();
1030 EXPECT_EQ(ERR_PIPELINE_EVICTION, four_callback.WaitForResult());
1033 } // anonymous namespace