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.
5 #include "chrome/browser/net/http_pipelining_compatibility_client.h"
11 #include "base/basictypes.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/histogram_samples.h"
15 #include "base/metrics/statistics_recorder.h"
16 #include "base/run_loop.h"
17 #include "base/stl_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/test_completion_callback.h"
23 #include "net/test/spawned_test_server/spawned_test_server.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using base::HistogramBase
;
30 using base::HistogramSamples
;
32 namespace chrome_browser_net
{
36 static const char* const kHistogramNames
[] = {
37 "NetConnectivity.Pipeline.CanarySuccess",
38 "NetConnectivity.Pipeline.Depth",
39 "NetConnectivity.Pipeline.AllHTTP11",
40 "NetConnectivity.Pipeline.Success",
41 "NetConnectivity.Pipeline.0.NetworkError",
42 "NetConnectivity.Pipeline.0.ResponseCode",
43 "NetConnectivity.Pipeline.0.Status",
44 "NetConnectivity.Pipeline.1.NetworkError",
45 "NetConnectivity.Pipeline.1.ResponseCode",
46 "NetConnectivity.Pipeline.1.Status",
47 "NetConnectivity.Pipeline.2.NetworkError",
48 "NetConnectivity.Pipeline.2.ResponseCode",
49 "NetConnectivity.Pipeline.2.Status",
62 class MockFactory
: public internal::PipelineTestRequest::Factory
{
64 MOCK_METHOD6(NewRequest
, internal::PipelineTestRequest
*(
65 int, const std::string
&, const RequestInfo
&,
66 internal::PipelineTestRequest::Delegate
*, net::URLRequestContext
*,
67 internal::PipelineTestRequest::Type
));
70 class MockRequest
: public internal::PipelineTestRequest
{
72 MOCK_METHOD0(Start
, void());
75 using content::BrowserThread
;
78 using testing::Invoke
;
79 using testing::Return
;
82 class HttpPipeliningCompatibilityClientTest
: public testing::Test
{
84 HttpPipeliningCompatibilityClientTest()
85 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
86 test_server_(net::SpawnedTestServer::TYPE_HTTP
,
87 net::SpawnedTestServer::kLocalhost
,
88 base::FilePath(FILE_PATH_LITERAL(
89 "chrome/test/data/http_pipelining"))) {
93 virtual void SetUp() OVERRIDE
{
94 // Start up a histogram recorder.
95 // TODO(rtenneti): Leaks StatisticsRecorder and will update suppressions.
96 base::StatisticsRecorder::Initialize();
97 ASSERT_TRUE(test_server_
.Start());
98 context_
= new net::TestURLRequestContextGetter(
99 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
));
102 for (size_t i
= 0; i
< arraysize(kHistogramNames
); ++i
) {
103 const char* name
= kHistogramNames
[i
];
104 scoped_ptr
<HistogramSamples
> samples
= GetHistogram(name
);
105 if (samples
.get() && samples
->TotalCount() > 0) {
106 original_samples_
[name
] = samples
.release();
111 virtual void TearDown() OVERRIDE
{
112 BrowserThread::ReleaseSoon(BrowserThread::IO
, FROM_HERE
, context_
);
113 base::RunLoop().RunUntilIdle();
114 STLDeleteValues(&original_samples_
);
118 std::vector
<RequestInfo
> requests
,
119 HttpPipeliningCompatibilityClient::Options options
) {
120 HttpPipeliningCompatibilityClient
client(NULL
);
121 net::TestCompletionCallback callback
;
122 client
.Start(test_server_
.GetURL(std::string()).spec(),
126 context_
->GetURLRequestContext());
127 callback
.WaitForResult();
130 void ExpectHistogramCount(int expected_count
,
132 HistogramField field
) {
137 name
= "NetConnectivity.Pipeline.CanarySuccess";
141 name
= "NetConnectivity.Pipeline.Depth";
145 name
= "NetConnectivity.Pipeline.AllHTTP11";
149 name
= "NetConnectivity.Pipeline.Success";
153 FAIL() << "Unexpected field: " << field
;
156 scoped_ptr
<HistogramSamples
> samples
= GetHistogram(name
);
160 if (ContainsKey(original_samples_
, name
)) {
161 samples
->Subtract((*original_samples_
[name
]));
164 EXPECT_EQ(expected_count
, samples
->TotalCount()) << name
;
165 if (expected_count
> 0) {
166 EXPECT_EQ(expected_count
, samples
->GetCount(expected_value
)) << name
;
170 void ExpectRequestHistogramCount(int expected_count
,
173 HistogramField field
) {
174 const char* field_str
= "";
177 field_str
= "Status";
180 case FIELD_NETWORK_ERROR
:
181 field_str
= "NetworkError";
184 case FIELD_RESPONSE_CODE
:
185 field_str
= "ResponseCode";
189 FAIL() << "Unexpected field: " << field
;
192 std::string name
= base::StringPrintf("NetConnectivity.Pipeline.%d.%s",
193 request_id
, field_str
);
194 scoped_ptr
<HistogramSamples
> samples
= GetHistogram(name
.c_str());
198 if (ContainsKey(original_samples_
, name
)) {
199 samples
->Subtract(*(original_samples_
[name
]));
202 EXPECT_EQ(expected_count
, samples
->TotalCount()) << name
;
203 if (expected_count
> 0) {
204 EXPECT_EQ(expected_count
, samples
->GetCount(expected_value
)) << name
;
208 content::TestBrowserThreadBundle thread_bundle_
;
209 net::SpawnedTestServer test_server_
;
210 net::TestURLRequestContextGetter
* context_
;
213 scoped_ptr
<HistogramSamples
> GetHistogram(const char* name
) {
214 scoped_ptr
<HistogramSamples
> samples
;
215 HistogramBase
* cached_histogram
= NULL
;
216 HistogramBase
* current_histogram
=
217 base::StatisticsRecorder::FindHistogram(name
);
218 if (ContainsKey(histograms_
, name
)) {
219 cached_histogram
= histograms_
[name
];
222 // This is to work around the CACHE_HISTOGRAM_* macros caching the last used
223 // histogram by name. So, even though we throw out the StatisticsRecorder
224 // between tests, the CACHE_HISTOGRAM_* might still write into the old
225 // Histogram if it has the same name as the last run. We keep a cache of the
226 // last used Histogram and then update the cache if it's different than the
227 // current Histogram.
228 if (cached_histogram
&& current_histogram
) {
229 samples
= cached_histogram
->SnapshotSamples();
230 if (cached_histogram
!= current_histogram
) {
231 samples
->Add(*(current_histogram
->SnapshotSamples()));
232 histograms_
[name
] = current_histogram
;
234 } else if (current_histogram
) {
235 samples
= current_histogram
->SnapshotSamples();
236 histograms_
[name
] = current_histogram
;
237 } else if (cached_histogram
) {
238 samples
= cached_histogram
->SnapshotSamples();
240 return samples
.Pass();
243 static std::map
<std::string
, HistogramBase
*> histograms_
;
244 std::map
<std::string
, HistogramSamples
*> original_samples_
;
248 std::map
<std::string
, HistogramBase
*>
249 HttpPipeliningCompatibilityClientTest::histograms_
;
251 TEST_F(HttpPipeliningCompatibilityClientTest
, Success
) {
253 info
.filename
= "files/alphabet.txt";
254 info
.expected_response
= "abcdefghijklmnopqrstuvwxyz";
255 std::vector
<RequestInfo
> requests
;
256 requests
.push_back(info
);
258 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
260 ExpectHistogramCount(1, true, FIELD_SUCCESS
);
261 ExpectHistogramCount(0, 0, FIELD_DEPTH
);
262 ExpectHistogramCount(0, 0, FIELD_HTTP_1_1
);
263 ExpectRequestHistogramCount(
264 1, internal::PipelineTestRequest::STATUS_SUCCESS
, 0, FIELD_STATUS
);
265 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
266 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
269 TEST_F(HttpPipeliningCompatibilityClientTest
, TooSmall
) {
271 info
.filename
= "files/alphabet.txt";
272 info
.expected_response
= "abcdefghijklmnopqrstuvwxyz26";
273 std::vector
<RequestInfo
> requests
;
274 requests
.push_back(info
);
276 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
278 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
279 ExpectRequestHistogramCount(
280 1, internal::PipelineTestRequest::STATUS_TOO_SMALL
, 0, FIELD_STATUS
);
281 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
282 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
285 TEST_F(HttpPipeliningCompatibilityClientTest
, TooLarge
) {
287 info
.filename
= "files/alphabet.txt";
288 info
.expected_response
= "abc";
289 std::vector
<RequestInfo
> requests
;
290 requests
.push_back(info
);
292 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
294 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
295 ExpectRequestHistogramCount(
296 1, internal::PipelineTestRequest::STATUS_TOO_LARGE
, 0, FIELD_STATUS
);
297 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
298 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
301 TEST_F(HttpPipeliningCompatibilityClientTest
, Mismatch
) {
303 info
.filename
= "files/alphabet.txt";
304 info
.expected_response
= "zyxwvutsrqponmlkjihgfedcba";
305 std::vector
<RequestInfo
> requests
;
306 requests
.push_back(info
);
308 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
310 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
311 ExpectRequestHistogramCount(
312 1, internal::PipelineTestRequest::STATUS_CONTENT_MISMATCH
,
314 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
315 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
318 TEST_F(HttpPipeliningCompatibilityClientTest
, Redirect
) {
320 info
.filename
= "server-redirect?http://foo.bar/asdf";
321 info
.expected_response
= "shouldn't matter";
322 std::vector
<RequestInfo
> requests
;
323 requests
.push_back(info
);
325 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
327 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
328 ExpectRequestHistogramCount(
329 1, internal::PipelineTestRequest::STATUS_REDIRECTED
, 0, FIELD_STATUS
);
330 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
331 ExpectRequestHistogramCount(0, 0, 0, FIELD_RESPONSE_CODE
);
334 TEST_F(HttpPipeliningCompatibilityClientTest
, AuthRequired
) {
336 info
.filename
= "auth-basic";
337 info
.expected_response
= "shouldn't matter";
338 std::vector
<RequestInfo
> requests
;
339 requests
.push_back(info
);
341 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
343 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
344 ExpectRequestHistogramCount(
345 1, internal::PipelineTestRequest::STATUS_BAD_RESPONSE_CODE
,
347 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
348 ExpectRequestHistogramCount(1, 401, 0, FIELD_RESPONSE_CODE
);
351 TEST_F(HttpPipeliningCompatibilityClientTest
, NoContent
) {
353 info
.filename
= "nocontent";
354 info
.expected_response
= "shouldn't matter";
355 std::vector
<RequestInfo
> requests
;
356 requests
.push_back(info
);
358 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
360 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
361 ExpectRequestHistogramCount(
362 1, internal::PipelineTestRequest::STATUS_BAD_RESPONSE_CODE
,
364 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
365 ExpectRequestHistogramCount(1, 204, 0, FIELD_RESPONSE_CODE
);
368 TEST_F(HttpPipeliningCompatibilityClientTest
, CloseSocket
) {
370 info
.filename
= "close-socket";
371 info
.expected_response
= "shouldn't matter";
372 std::vector
<RequestInfo
> requests
;
373 requests
.push_back(info
);
375 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
377 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
378 ExpectRequestHistogramCount(
379 1, internal::PipelineTestRequest::STATUS_NETWORK_ERROR
, 0, FIELD_STATUS
);
380 ExpectRequestHistogramCount(
381 1, -net::ERR_EMPTY_RESPONSE
, 0, FIELD_NETWORK_ERROR
);
382 ExpectRequestHistogramCount(0, 0, 0, FIELD_RESPONSE_CODE
);
385 TEST_F(HttpPipeliningCompatibilityClientTest
, OldHttpVersion
) {
387 info
.filename
= "http-1.0";
388 info
.expected_response
= "abcdefghijklmnopqrstuvwxyz";
389 std::vector
<RequestInfo
> requests
;
390 requests
.push_back(info
);
392 RunTest(requests
, HttpPipeliningCompatibilityClient::PIPE_TEST_DEFAULTS
);
394 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
395 ExpectRequestHistogramCount(
396 1, internal::PipelineTestRequest::STATUS_BAD_HTTP_VERSION
,
398 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
399 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
402 #if defined(OS_CHROMEOS)
403 // http://crbug.com/147903: test fails on ChromeOS
404 #define MAYBE_MultipleRequests DISABLED_MultipleRequests
406 #define MAYBE_MultipleRequests MultipleRequests
408 TEST_F(HttpPipeliningCompatibilityClientTest
, MAYBE_MultipleRequests
) {
409 std::vector
<RequestInfo
> requests
;
412 info1
.filename
= "files/alphabet.txt";
413 info1
.expected_response
= "abcdefghijklmnopqrstuvwxyz";
414 requests
.push_back(info1
);
417 info2
.filename
= "close-socket";
418 info2
.expected_response
= "shouldn't matter";
419 requests
.push_back(info2
);
422 info3
.filename
= "auth-basic";
423 info3
.expected_response
= "shouldn't matter";
424 requests
.push_back(info3
);
427 HttpPipeliningCompatibilityClient::PIPE_TEST_COLLECT_SERVER_STATS
);
429 ExpectHistogramCount(1, false, FIELD_SUCCESS
);
431 ExpectRequestHistogramCount(
432 1, internal::PipelineTestRequest::STATUS_SUCCESS
, 0, FIELD_STATUS
);
433 ExpectRequestHistogramCount(0, 0, 0, FIELD_NETWORK_ERROR
);
434 ExpectRequestHistogramCount(1, 200, 0, FIELD_RESPONSE_CODE
);
436 ExpectRequestHistogramCount(
437 1, internal::PipelineTestRequest::STATUS_NETWORK_ERROR
, 1, FIELD_STATUS
);
438 ExpectRequestHistogramCount(
439 1, -net::ERR_PIPELINE_EVICTION
, 1, FIELD_NETWORK_ERROR
);
440 ExpectRequestHistogramCount(0, 0, 1, FIELD_RESPONSE_CODE
);
442 ExpectRequestHistogramCount(
443 1, internal::PipelineTestRequest::STATUS_NETWORK_ERROR
, 2, FIELD_STATUS
);
444 ExpectRequestHistogramCount(
445 1, -net::ERR_PIPELINE_EVICTION
, 2, FIELD_NETWORK_ERROR
);
446 ExpectRequestHistogramCount(0, 0, 2, FIELD_RESPONSE_CODE
);
448 ExpectRequestHistogramCount(
449 1, internal::PipelineTestRequest::STATUS_NETWORK_ERROR
, 3, FIELD_STATUS
);
450 ExpectRequestHistogramCount(
451 1, -net::ERR_PIPELINE_EVICTION
, 3, FIELD_NETWORK_ERROR
);
452 ExpectRequestHistogramCount(0, 0, 3, FIELD_RESPONSE_CODE
);
455 TEST_F(HttpPipeliningCompatibilityClientTest
, StatsOk
) {
456 EXPECT_EQ(internal::PipelineTestRequest::STATUS_SUCCESS
,
457 internal::ProcessStatsResponse(
458 "max_pipeline_depth:3,were_all_requests_http_1_1:0"));
459 ExpectHistogramCount(1, 3, FIELD_DEPTH
);
460 ExpectHistogramCount(1, 0, FIELD_HTTP_1_1
);
463 TEST_F(HttpPipeliningCompatibilityClientTest
, StatsIndifferentToOrder
) {
464 EXPECT_EQ(internal::PipelineTestRequest::STATUS_SUCCESS
,
465 internal::ProcessStatsResponse(
466 "were_all_requests_http_1_1:1,max_pipeline_depth:2"));
467 ExpectHistogramCount(1, 2, FIELD_DEPTH
);
468 ExpectHistogramCount(1, 1, FIELD_HTTP_1_1
);
471 #if defined(OS_CHROMEOS)
472 // http://crbug.com/147903: test fails on ChromeOS
473 #define MAYBE_StatsBadField DISABLED_StatsBadField
475 #define MAYBE_StatsBadField StatsBadField
477 TEST_F(HttpPipeliningCompatibilityClientTest
, MAYBE_StatsBadField
) {
478 EXPECT_EQ(internal::PipelineTestRequest::STATUS_CORRUPT_STATS
,
479 internal::ProcessStatsResponse(
480 "foo:3,were_all_requests_http_1_1:1"));
481 ExpectHistogramCount(0, 0, FIELD_DEPTH
);
482 ExpectHistogramCount(0, 0, FIELD_HTTP_1_1
);
485 TEST_F(HttpPipeliningCompatibilityClientTest
, StatsTooShort
) {
486 EXPECT_EQ(internal::PipelineTestRequest::STATUS_CORRUPT_STATS
,
487 internal::ProcessStatsResponse("were_all_requests_http_1_1:1"));
488 ExpectHistogramCount(0, 0, FIELD_DEPTH
);
489 ExpectHistogramCount(0, 0, FIELD_HTTP_1_1
);
492 TEST_F(HttpPipeliningCompatibilityClientTest
, WaitForCanary
) {
493 MockFactory
* factory
= new MockFactory
;
494 HttpPipeliningCompatibilityClient
client(factory
);
496 MockRequest
* request
= new MockRequest
;
497 base::Closure request_cb
= base::Bind(
498 &internal::PipelineTestRequest::Delegate::OnRequestFinished
,
499 base::Unretained(&client
), 0,
500 internal::PipelineTestRequest::STATUS_SUCCESS
);
502 MockRequest
* canary
= new MockRequest
;
503 base::Closure canary_cb
= base::Bind(
504 &internal::PipelineTestRequest::Delegate::OnCanaryFinished
,
505 base::Unretained(&client
), internal::PipelineTestRequest::STATUS_SUCCESS
);
507 EXPECT_CALL(*factory
, NewRequest(
508 0, _
, Field(&RequestInfo::filename
, StrEq("request.txt")), _
, _
,
509 internal::PipelineTestRequest::TYPE_PIPELINED
))
511 .WillOnce(Return(request
));
512 EXPECT_CALL(*factory
, NewRequest(
513 999, _
, Field(&RequestInfo::filename
, StrEq("index.html")), _
, _
,
514 internal::PipelineTestRequest::TYPE_CANARY
))
516 .WillOnce(Return(canary
));
518 EXPECT_CALL(*canary
, Start())
520 .WillOnce(Invoke(&canary_cb
, &base::Closure::Run
));
521 EXPECT_CALL(*request
, Start())
523 .WillOnce(Invoke(&request_cb
, &base::Closure::Run
));
525 std::vector
<RequestInfo
> requests
;
528 info1
.filename
= "request.txt";
529 requests
.push_back(info1
);
531 net::TestCompletionCallback callback
;
532 client
.Start("http://base/", requests
,
533 HttpPipeliningCompatibilityClient::PIPE_TEST_RUN_CANARY_REQUEST
,
534 callback
.callback(), context_
->GetURLRequestContext());
535 callback
.WaitForResult();
537 ExpectHistogramCount(1, true, FIELD_CANARY
);
538 ExpectHistogramCount(1, true, FIELD_SUCCESS
);
539 ExpectRequestHistogramCount(
540 1, internal::PipelineTestRequest::STATUS_SUCCESS
, 0, FIELD_STATUS
);
543 #if defined(OS_CHROMEOS)
544 // http://crbug.com/147903: test fails on ChromeOS
545 #define MAYBE_CanaryFailure DISABLED_CanaryFailure
547 #define MAYBE_CanaryFailure CanaryFailure
549 TEST_F(HttpPipeliningCompatibilityClientTest
, MAYBE_CanaryFailure
) {
550 MockFactory
* factory
= new MockFactory
;
551 HttpPipeliningCompatibilityClient
client(factory
);
553 MockRequest
* request
= new MockRequest
;
555 MockRequest
* canary
= new MockRequest
;
556 base::Closure canary_cb
= base::Bind(
557 &internal::PipelineTestRequest::Delegate::OnCanaryFinished
,
558 base::Unretained(&client
),
559 internal::PipelineTestRequest::STATUS_REDIRECTED
);
561 EXPECT_CALL(*factory
, NewRequest(
562 0, _
, Field(&RequestInfo::filename
, StrEq("request.txt")), _
, _
,
563 internal::PipelineTestRequest::TYPE_PIPELINED
))
565 .WillOnce(Return(request
));
566 EXPECT_CALL(*factory
, NewRequest(
567 999, _
, Field(&RequestInfo::filename
, StrEq("index.html")), _
, _
,
568 internal::PipelineTestRequest::TYPE_CANARY
))
570 .WillOnce(Return(canary
));
572 EXPECT_CALL(*canary
, Start())
574 .WillOnce(Invoke(&canary_cb
, &base::Closure::Run
));
575 EXPECT_CALL(*request
, Start())
578 std::vector
<RequestInfo
> requests
;
581 info1
.filename
= "request.txt";
582 requests
.push_back(info1
);
584 net::TestCompletionCallback callback
;
585 client
.Start("http://base/", requests
,
586 HttpPipeliningCompatibilityClient::PIPE_TEST_RUN_CANARY_REQUEST
,
587 callback
.callback(), context_
->GetURLRequestContext());
588 callback
.WaitForResult();
590 ExpectHistogramCount(1, false, FIELD_CANARY
);
591 ExpectHistogramCount(0, false, FIELD_SUCCESS
);
592 ExpectHistogramCount(0, true, FIELD_SUCCESS
);
595 } // anonymous namespace
597 } // namespace chrome_browser_net