| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/net/http_pipelining_compatibility_client.h" | |
| 6 | |
| 7 #include "base/metrics/field_trial.h" | |
| 8 #include "base/metrics/histogram.h" | |
| 9 #include "base/strings/string_number_conversions.h" | |
| 10 #include "base/strings/string_split.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "chrome/browser/io_thread.h" | |
| 13 #include "chrome/common/chrome_version_info.h" | |
| 14 #include "content/public/browser/browser_thread.h" | |
| 15 #include "net/base/load_flags.h" | |
| 16 #include "net/base/network_change_notifier.h" | |
| 17 #include "net/base/request_priority.h" | |
| 18 #include "net/disk_cache/blockfile/histogram_macros.h" | |
| 19 #include "net/http/http_network_layer.h" | |
| 20 #include "net/http/http_network_session.h" | |
| 21 #include "net/http/http_response_headers.h" | |
| 22 #include "net/http/http_version.h" | |
| 23 #include "net/proxy/proxy_config.h" | |
| 24 #include "net/proxy/proxy_service.h" | |
| 25 #include "net/url_request/url_request_context.h" | |
| 26 #include "net/url_request/url_request_context_getter.h" | |
| 27 | |
| 28 namespace chrome_browser_net { | |
| 29 | |
| 30 static const int kCanaryRequestId = 999; | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 // There is one Request per RequestInfo passed in to Start() above. | |
| 35 class Request : public internal::PipelineTestRequest, | |
| 36 public net::URLRequest::Delegate { | |
| 37 public: | |
| 38 Request(int request_id, | |
| 39 const std::string& base_url, | |
| 40 const RequestInfo& info, | |
| 41 internal::PipelineTestRequest::Delegate* delegate, | |
| 42 net::URLRequestContext* url_request_context); | |
| 43 | |
| 44 virtual ~Request() {} | |
| 45 | |
| 46 virtual void Start() OVERRIDE; | |
| 47 | |
| 48 protected: | |
| 49 // Called when this request has determined its result. Returns the result to | |
| 50 // the |client_|. | |
| 51 virtual void Finished(internal::PipelineTestRequest::Status result); | |
| 52 | |
| 53 const std::string& response() const { return response_; } | |
| 54 | |
| 55 internal::PipelineTestRequest::Delegate* delegate() { return delegate_; } | |
| 56 | |
| 57 private: | |
| 58 // Called when a response can be read. Reads bytes into |response_| until it | |
| 59 // consumes the entire response or it encounters an error. | |
| 60 void DoRead(); | |
| 61 | |
| 62 // Called when all bytes have been received. Compares the |response_| to | |
| 63 // |info_|'s expected response. | |
| 64 virtual void DoReadFinished(); | |
| 65 | |
| 66 // net::URLRequest::Delegate interface | |
| 67 virtual void OnReceivedRedirect(net::URLRequest* request, | |
| 68 const GURL& new_url, | |
| 69 bool* defer_redirect) OVERRIDE; | |
| 70 virtual void OnSSLCertificateError(net::URLRequest* request, | |
| 71 const net::SSLInfo& ssl_info, | |
| 72 bool fatal) OVERRIDE; | |
| 73 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; | |
| 74 virtual void OnReadCompleted(net::URLRequest* request, | |
| 75 int bytes_read) OVERRIDE; | |
| 76 | |
| 77 internal::PipelineTestRequest::Delegate* delegate_; | |
| 78 const int request_id_; | |
| 79 scoped_ptr<net::URLRequest> url_request_; | |
| 80 const RequestInfo info_; | |
| 81 scoped_refptr<net::IOBuffer> read_buffer_; | |
| 82 std::string response_; | |
| 83 int response_code_; | |
| 84 }; | |
| 85 | |
| 86 Request::Request(int request_id, | |
| 87 const std::string& base_url, | |
| 88 const RequestInfo& info, | |
| 89 internal::PipelineTestRequest::Delegate* delegate, | |
| 90 net::URLRequestContext* url_request_context) | |
| 91 : delegate_(delegate), | |
| 92 request_id_(request_id), | |
| 93 url_request_(url_request_context->CreateRequest(GURL(base_url + | |
| 94 info.filename), | |
| 95 net::DEFAULT_PRIORITY, | |
| 96 this, | |
| 97 NULL)), | |
| 98 info_(info), | |
| 99 response_code_(0) { | |
| 100 url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE | | |
| 101 net::LOAD_DISABLE_CACHE | | |
| 102 net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 103 net::LOAD_DO_NOT_SEND_COOKIES | | |
| 104 net::LOAD_DO_NOT_PROMPT_FOR_LOGIN | | |
| 105 net::LOAD_DO_NOT_SEND_AUTH_DATA); | |
| 106 } | |
| 107 | |
| 108 void Request::Start() { | |
| 109 url_request_->Start(); | |
| 110 } | |
| 111 | |
| 112 void Request::OnReceivedRedirect( | |
| 113 net::URLRequest* request, | |
| 114 const GURL& new_url, | |
| 115 bool* defer_redirect) { | |
| 116 *defer_redirect = true; | |
| 117 request->Cancel(); | |
| 118 Finished(STATUS_REDIRECTED); | |
| 119 } | |
| 120 | |
| 121 void Request::OnSSLCertificateError( | |
| 122 net::URLRequest* request, | |
| 123 const net::SSLInfo& ssl_info, | |
| 124 bool fatal) { | |
| 125 Finished(STATUS_CERT_ERROR); | |
| 126 } | |
| 127 | |
| 128 void Request::OnResponseStarted(net::URLRequest* request) { | |
| 129 response_code_ = request->GetResponseCode(); | |
| 130 if (response_code_ != 200) { | |
| 131 Finished(STATUS_BAD_RESPONSE_CODE); | |
| 132 return; | |
| 133 } | |
| 134 const net::HttpVersion required_version(1, 1); | |
| 135 if (request->response_info().headers->GetParsedHttpVersion() < | |
| 136 required_version) { | |
| 137 Finished(STATUS_BAD_HTTP_VERSION); | |
| 138 return; | |
| 139 } | |
| 140 read_buffer_ = new net::IOBuffer(info_.expected_response.length()); | |
| 141 DoRead(); | |
| 142 } | |
| 143 | |
| 144 void Request::OnReadCompleted(net::URLRequest* request, int bytes_read) { | |
| 145 if (bytes_read == 0) { | |
| 146 DoReadFinished(); | |
| 147 } else if (bytes_read < 0) { | |
| 148 Finished(STATUS_NETWORK_ERROR); | |
| 149 } else { | |
| 150 response_.append(read_buffer_->data(), bytes_read); | |
| 151 if (response_.length() <= info_.expected_response.length()) { | |
| 152 DoRead(); | |
| 153 } else if (response_.find(info_.expected_response) == 0) { | |
| 154 Finished(STATUS_TOO_LARGE); | |
| 155 } else { | |
| 156 Finished(STATUS_CONTENT_MISMATCH); | |
| 157 } | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 void Request::DoRead() { | |
| 162 int bytes_read = 0; | |
| 163 if (url_request_->Read(read_buffer_.get(), info_.expected_response.length(), | |
| 164 &bytes_read)) { | |
| 165 OnReadCompleted(url_request_.get(), bytes_read); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 void Request::DoReadFinished() { | |
| 170 if (response_.length() != info_.expected_response.length()) { | |
| 171 if (info_.expected_response.find(response_) == 0) { | |
| 172 Finished(STATUS_TOO_SMALL); | |
| 173 } else { | |
| 174 Finished(STATUS_CONTENT_MISMATCH); | |
| 175 } | |
| 176 } else if (response_ == info_.expected_response) { | |
| 177 Finished(STATUS_SUCCESS); | |
| 178 } else { | |
| 179 Finished(STATUS_CONTENT_MISMATCH); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 void Request::Finished(internal::PipelineTestRequest::Status result) { | |
| 184 const net::URLRequestStatus status = url_request_->status(); | |
| 185 url_request_.reset(); | |
| 186 if (response_code_ > 0) { | |
| 187 delegate()->ReportResponseCode(request_id_, response_code_); | |
| 188 } | |
| 189 if (status.status() == net::URLRequestStatus::FAILED) { | |
| 190 // Network errors trump all other status codes, because network errors can | |
| 191 // be detected by the network stack even with real content. If we determine | |
| 192 // that all pipelining errors can be detected by the network stack, then we | |
| 193 // don't need to worry about broken proxies. | |
| 194 delegate()->ReportNetworkError(request_id_, status.error()); | |
| 195 delegate()->OnRequestFinished(request_id_, STATUS_NETWORK_ERROR); | |
| 196 } else { | |
| 197 delegate()->OnRequestFinished(request_id_, result); | |
| 198 } | |
| 199 // WARNING: We may be deleted at this point. | |
| 200 } | |
| 201 | |
| 202 // A special non-pipelined request sent before pipelining begins to test basic | |
| 203 // HTTP connectivity. | |
| 204 class CanaryRequest : public Request { | |
| 205 public: | |
| 206 CanaryRequest(int request_id, | |
| 207 const std::string& base_url, | |
| 208 const RequestInfo& info, | |
| 209 internal::PipelineTestRequest::Delegate* delegate, | |
| 210 net::URLRequestContext* url_request_context) | |
| 211 : Request(request_id, base_url, info, delegate, url_request_context) { | |
| 212 } | |
| 213 | |
| 214 virtual ~CanaryRequest() {} | |
| 215 | |
| 216 private: | |
| 217 virtual void Finished( | |
| 218 internal::PipelineTestRequest::Status result) OVERRIDE { | |
| 219 delegate()->OnCanaryFinished(result); | |
| 220 } | |
| 221 }; | |
| 222 | |
| 223 // A special request that parses a /stats.txt response from the test server. | |
| 224 class StatsRequest : public Request { | |
| 225 public: | |
| 226 // Note that |info.expected_response| is only used to determine the correct | |
| 227 // length of the response. The exact string content isn't used. | |
| 228 StatsRequest(int request_id, | |
| 229 const std::string& base_url, | |
| 230 const RequestInfo& info, | |
| 231 internal::PipelineTestRequest::Delegate* delegate, | |
| 232 net::URLRequestContext* url_request_context) | |
| 233 : Request(request_id, base_url, info, delegate, url_request_context) { | |
| 234 } | |
| 235 | |
| 236 virtual ~StatsRequest() {} | |
| 237 | |
| 238 private: | |
| 239 virtual void DoReadFinished() OVERRIDE { | |
| 240 internal::PipelineTestRequest::Status status = | |
| 241 internal::ProcessStatsResponse(response()); | |
| 242 Finished(status); | |
| 243 } | |
| 244 }; | |
| 245 | |
| 246 class RequestFactory : public internal::PipelineTestRequest::Factory { | |
| 247 public: | |
| 248 virtual internal::PipelineTestRequest* NewRequest( | |
| 249 int request_id, | |
| 250 const std::string& base_url, | |
| 251 const RequestInfo& info, | |
| 252 internal::PipelineTestRequest::Delegate* delegate, | |
| 253 net::URLRequestContext* url_request_context, | |
| 254 internal::PipelineTestRequest::Type request_type) OVERRIDE { | |
| 255 switch (request_type) { | |
| 256 case internal::PipelineTestRequest::TYPE_PIPELINED: | |
| 257 return new Request(request_id, base_url, info, delegate, | |
| 258 url_request_context); | |
| 259 | |
| 260 case internal::PipelineTestRequest::TYPE_CANARY: | |
| 261 return new CanaryRequest(request_id, base_url, info, delegate, | |
| 262 url_request_context); | |
| 263 | |
| 264 case internal::PipelineTestRequest::TYPE_STATS: | |
| 265 return new StatsRequest(request_id, base_url, info, delegate, | |
| 266 url_request_context); | |
| 267 | |
| 268 default: | |
| 269 NOTREACHED(); | |
| 270 return NULL; | |
| 271 } | |
| 272 } | |
| 273 }; | |
| 274 | |
| 275 } // anonymous namespace | |
| 276 | |
| 277 HttpPipeliningCompatibilityClient::HttpPipeliningCompatibilityClient( | |
| 278 internal::PipelineTestRequest::Factory* factory) | |
| 279 : factory_(factory), | |
| 280 num_finished_(0), | |
| 281 num_succeeded_(0) { | |
| 282 if (!factory_.get()) { | |
| 283 factory_.reset(new RequestFactory); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 HttpPipeliningCompatibilityClient::~HttpPipeliningCompatibilityClient() { | |
| 288 } | |
| 289 | |
| 290 void HttpPipeliningCompatibilityClient::Start( | |
| 291 const std::string& base_url, | |
| 292 std::vector<RequestInfo>& requests, | |
| 293 Options options, | |
| 294 const net::CompletionCallback& callback, | |
| 295 net::URLRequestContext* url_request_context) { | |
| 296 net::HttpNetworkSession* old_session = | |
| 297 url_request_context->http_transaction_factory()->GetSession(); | |
| 298 net::HttpNetworkSession::Params params = old_session->params(); | |
| 299 params.force_http_pipelining = true; | |
| 300 scoped_refptr<net::HttpNetworkSession> session = | |
| 301 new net::HttpNetworkSession(params); | |
| 302 http_transaction_factory_.reset( | |
| 303 net::HttpNetworkLayer::CreateFactory(session.get())); | |
| 304 | |
| 305 url_request_context_.reset(new net::URLRequestContext); | |
| 306 url_request_context_->CopyFrom(url_request_context); | |
| 307 url_request_context_->set_http_transaction_factory( | |
| 308 http_transaction_factory_.get()); | |
| 309 | |
| 310 finished_callback_ = callback; | |
| 311 for (size_t i = 0; i < requests.size(); ++i) { | |
| 312 requests_.push_back(factory_->NewRequest( | |
| 313 i, base_url, requests[i], this, url_request_context_.get(), | |
| 314 internal::PipelineTestRequest::TYPE_PIPELINED)); | |
| 315 } | |
| 316 if (options == PIPE_TEST_COLLECT_SERVER_STATS || | |
| 317 options == PIPE_TEST_CANARY_AND_STATS) { | |
| 318 RequestInfo info; | |
| 319 info.filename = "stats.txt"; | |
| 320 // This is just to determine the expected length of the response. | |
| 321 // StatsRequest doesn't expect this exact value, but it does expect this | |
| 322 // exact length. | |
| 323 info.expected_response = | |
| 324 "were_all_requests_http_1_1:1,max_pipeline_depth:5"; | |
| 325 requests_.push_back(factory_->NewRequest( | |
| 326 requests.size(), base_url, info, this, url_request_context_.get(), | |
| 327 internal::PipelineTestRequest::TYPE_STATS)); | |
| 328 } | |
| 329 if (options == PIPE_TEST_RUN_CANARY_REQUEST || | |
| 330 options == PIPE_TEST_CANARY_AND_STATS) { | |
| 331 RequestInfo info; | |
| 332 info.filename = "index.html"; | |
| 333 info.expected_response = | |
| 334 "\nThis is a test server operated by Google. It's used by Google " | |
| 335 "Chrome to test\nproxies for compatibility with HTTP pipelining. More " | |
| 336 "information can be found\nhere:\n\nhttp://dev.chromium.org/developers/" | |
| 337 "design-documents/network-stack/http-pipelining\n\nSource code can be " | |
| 338 "found here:\n\nhttp://code.google.com/p/http-pipelining-test/\n"; | |
| 339 canary_request_.reset(factory_->NewRequest( | |
| 340 kCanaryRequestId, base_url, info, this, url_request_context, | |
| 341 internal::PipelineTestRequest::TYPE_CANARY)); | |
| 342 canary_request_->Start(); | |
| 343 } else { | |
| 344 StartTestRequests(); | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 void HttpPipeliningCompatibilityClient::StartTestRequests() { | |
| 349 for (size_t i = 0; i < requests_.size(); ++i) { | |
| 350 requests_[i]->Start(); | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 void HttpPipeliningCompatibilityClient::OnCanaryFinished( | |
| 355 internal::PipelineTestRequest::Status status) { | |
| 356 canary_request_.reset(); | |
| 357 bool success = (status == internal::PipelineTestRequest::STATUS_SUCCESS); | |
| 358 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.CanarySuccess", success); | |
| 359 if (success) { | |
| 360 StartTestRequests(); | |
| 361 } else { | |
| 362 finished_callback_.Run(0); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 void HttpPipeliningCompatibilityClient::OnRequestFinished( | |
| 367 int request_id, internal::PipelineTestRequest::Status status) { | |
| 368 // The CACHE_HISTOGRAM_* macros are used, because they allow dynamic metric | |
| 369 // names. | |
| 370 // TODO(gavinp): Clean up this dependency by moving the needed functionality | |
| 371 // into base/. | |
| 372 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "Status"), | |
| 373 status, | |
| 374 internal::PipelineTestRequest::STATUS_MAX); | |
| 375 | |
| 376 ++num_finished_; | |
| 377 if (status == internal::PipelineTestRequest::STATUS_SUCCESS) { | |
| 378 ++num_succeeded_; | |
| 379 } | |
| 380 if (num_finished_ == requests_.size()) { | |
| 381 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.Success", | |
| 382 num_succeeded_ == requests_.size()); | |
| 383 finished_callback_.Run(0); | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 void HttpPipeliningCompatibilityClient::ReportNetworkError(int request_id, | |
| 388 int error_code) { | |
| 389 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "NetworkError"), | |
| 390 -error_code, 900); | |
| 391 } | |
| 392 | |
| 393 void HttpPipeliningCompatibilityClient::ReportResponseCode(int request_id, | |
| 394 int response_code) { | |
| 395 CACHE_HISTOGRAM_ENUMERATION(GetMetricName(request_id, "ResponseCode"), | |
| 396 response_code, 600); | |
| 397 } | |
| 398 | |
| 399 std::string HttpPipeliningCompatibilityClient::GetMetricName( | |
| 400 int request_id, const char* description) { | |
| 401 return base::StringPrintf("NetConnectivity.Pipeline.%d.%s", | |
| 402 request_id, description); | |
| 403 } | |
| 404 | |
| 405 namespace internal { | |
| 406 | |
| 407 internal::PipelineTestRequest::Status ProcessStatsResponse( | |
| 408 const std::string& response) { | |
| 409 bool were_all_requests_http_1_1 = false; | |
| 410 int max_pipeline_depth = 0; | |
| 411 | |
| 412 std::vector<std::pair<std::string, std::string> > kv_pairs; | |
| 413 base::SplitStringIntoKeyValuePairs(response, ':', ',', &kv_pairs); | |
| 414 | |
| 415 if (kv_pairs.size() != 2) { | |
| 416 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS; | |
| 417 } | |
| 418 | |
| 419 for (size_t i = 0; i < kv_pairs.size(); ++i) { | |
| 420 const std::string& key = kv_pairs[i].first; | |
| 421 int value; | |
| 422 if (!base::StringToInt(kv_pairs[i].second, &value)) { | |
| 423 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS; | |
| 424 } | |
| 425 | |
| 426 if (key == "were_all_requests_http_1_1") { | |
| 427 were_all_requests_http_1_1 = (value == 1); | |
| 428 } else if (key == "max_pipeline_depth") { | |
| 429 max_pipeline_depth = value; | |
| 430 } else { | |
| 431 return internal::PipelineTestRequest::STATUS_CORRUPT_STATS; | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 UMA_HISTOGRAM_BOOLEAN("NetConnectivity.Pipeline.AllHTTP11", | |
| 436 were_all_requests_http_1_1); | |
| 437 UMA_HISTOGRAM_ENUMERATION("NetConnectivity.Pipeline.Depth", | |
| 438 max_pipeline_depth, 6); | |
| 439 | |
| 440 return internal::PipelineTestRequest::STATUS_SUCCESS; | |
| 441 } | |
| 442 | |
| 443 } // namespace internal | |
| 444 | |
| 445 namespace { | |
| 446 | |
| 447 void DeleteClient(IOThread* io_thread, int /* rv */) { | |
| 448 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 449 io_thread->globals()->http_pipelining_compatibility_client.reset(); | |
| 450 } | |
| 451 | |
| 452 void CollectPipeliningCapabilityStatsOnIOThread( | |
| 453 const std::string& pipeline_test_server, | |
| 454 IOThread* io_thread) { | |
| 455 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 456 | |
| 457 net::URLRequestContext* url_request_context = | |
| 458 io_thread->globals()->system_request_context.get(); | |
| 459 if (!url_request_context->proxy_service()->config().proxy_rules().empty()) { | |
| 460 // Pipelining with explicitly configured proxies is disabled for now. | |
| 461 return; | |
| 462 } | |
| 463 | |
| 464 const base::FieldTrial::Probability kDivisor = 100; | |
| 465 base::FieldTrial::Probability probability_to_run_test = 0; | |
| 466 | |
| 467 const char* kTrialName = "HttpPipeliningCompatibility"; | |
| 468 base::FieldTrial* trial = base::FieldTrialList::Find(kTrialName); | |
| 469 if (trial) { | |
| 470 return; | |
| 471 } | |
| 472 // After May 4, 2012, the trial will disable itself. | |
| 473 trial = base::FieldTrialList::FactoryGetFieldTrial( | |
| 474 kTrialName, kDivisor, "disable_test", 2012, 5, 4, | |
| 475 base::FieldTrial::SESSION_RANDOMIZED, NULL); | |
| 476 | |
| 477 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); | |
| 478 if (channel == chrome::VersionInfo::CHANNEL_CANARY) { | |
| 479 probability_to_run_test = 100; | |
| 480 } else if (channel == chrome::VersionInfo::CHANNEL_DEV) { | |
| 481 probability_to_run_test = 100; | |
| 482 } | |
| 483 | |
| 484 int collect_stats_group = trial->AppendGroup("enable_test", | |
| 485 probability_to_run_test); | |
| 486 if (trial->group() != collect_stats_group) { | |
| 487 return; | |
| 488 } | |
| 489 | |
| 490 std::vector<RequestInfo> requests; | |
| 491 | |
| 492 RequestInfo info0; | |
| 493 info0.filename = "alphabet.txt"; | |
| 494 info0.expected_response = "abcdefghijklmnopqrstuvwxyz"; | |
| 495 requests.push_back(info0); | |
| 496 | |
| 497 RequestInfo info1; | |
| 498 info1.filename = "cached.txt"; | |
| 499 info1.expected_response = "azbycxdwevfugthsirjqkplomn"; | |
| 500 requests.push_back(info1); | |
| 501 | |
| 502 RequestInfo info2; | |
| 503 info2.filename = "reverse.txt"; | |
| 504 info2.expected_response = "zyxwvutsrqponmlkjihgfedcba"; | |
| 505 requests.push_back(info2); | |
| 506 | |
| 507 RequestInfo info3; | |
| 508 info3.filename = "chunked.txt"; | |
| 509 info3.expected_response = "chunkedencodingisfun"; | |
| 510 requests.push_back(info3); | |
| 511 | |
| 512 RequestInfo info4; | |
| 513 info4.filename = "cached.txt"; | |
| 514 info4.expected_response = "azbycxdwevfugthsirjqkplomn"; | |
| 515 requests.push_back(info4); | |
| 516 | |
| 517 HttpPipeliningCompatibilityClient* client = | |
| 518 new HttpPipeliningCompatibilityClient(NULL); | |
| 519 client->Start(pipeline_test_server, requests, | |
| 520 HttpPipeliningCompatibilityClient::PIPE_TEST_CANARY_AND_STATS, | |
| 521 base::Bind(&DeleteClient, io_thread), | |
| 522 url_request_context); | |
| 523 io_thread->globals()->http_pipelining_compatibility_client.reset(client); | |
| 524 } | |
| 525 | |
| 526 } // anonymous namespace | |
| 527 | |
| 528 void CollectPipeliningCapabilityStatsOnUIThread( | |
| 529 const std::string& pipeline_test_server, IOThread* io_thread) { | |
| 530 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 531 if (pipeline_test_server.empty()) | |
| 532 return; | |
| 533 | |
| 534 content::BrowserThread::PostTask( | |
| 535 content::BrowserThread::IO, | |
| 536 FROM_HERE, | |
| 537 base::Bind(&CollectPipeliningCapabilityStatsOnIOThread, | |
| 538 pipeline_test_server, | |
| 539 io_thread)); | |
| 540 } | |
| 541 | |
| 542 } // namespace chrome_browser_net | |
| OLD | NEW |