1// Copyright (c) 2011 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 "net/url_request/url_request_http_job.h"
6
7#include "base/base_switches.h"
8#include "base/command_line.h"
9#include "base/compiler_specific.h"
10#include "base/file_util.h"
11#include "base/file_version_info.h"
12#include "base/message_loop.h"
13#include "base/metrics/field_trial.h"
14#include "base/metrics/histogram.h"
15#include "base/rand_util.h"
16#include "base/string_util.h"
17#include "base/time.h"
18#include "net/base/cert_status_flags.h"
19#include "net/base/cookie_policy.h"
20#include "net/base/cookie_store.h"
21#include "net/base/filter.h"
22#include "net/base/host_port_pair.h"
23#include "net/base/load_flags.h"
24#include "net/base/mime_util.h"
25#include "net/base/net_errors.h"
26#include "net/base/net_util.h"
27#include "net/base/sdch_manager.h"
28#include "net/base/ssl_cert_request_info.h"
29#include "net/base/transport_security_state.h"
30#include "net/http/http_request_headers.h"
31#include "net/http/http_response_headers.h"
32#include "net/http/http_response_info.h"
33#include "net/http/http_transaction.h"
34#include "net/http/http_transaction_factory.h"
35#include "net/http/http_util.h"
36#include "net/url_request/https_prober.h"
37#include "net/url_request/url_request.h"
38#include "net/url_request/url_request_context.h"
39#include "net/url_request/url_request_error_job.h"
40#include "net/url_request/url_request_redirect_job.h"
41#include "net/url_request/url_request_throttler_header_adapter.h"
42#include "net/url_request/url_request_throttler_manager.h"
43
44static const char kAvailDictionaryHeader[] = "Avail-Dictionary";
45
46// When histogramming results related to SDCH and/or an SDCH latency test, the
47// number of packets for which we need to record arrival times so as to
48// calculate interpacket latencies.  We currently are only looking at the
49// first few packets, as we're monitoring the impact of the initial TCP
50// congestion window on stalling of transmissions.
51static const size_t kSdchPacketHistogramCount = 5;
52
53namespace net {
54
55namespace {
56
57class HTTPSProberDelegateImpl : public HTTPSProberDelegate {
58 public:
59  HTTPSProberDelegateImpl(const std::string& host, int max_age,
60                          bool include_subdomains,
61                          TransportSecurityState* sts)
62      : host_(host),
63        max_age_(max_age),
64        include_subdomains_(include_subdomains),
65        sts_(sts) { }
66
67  virtual void ProbeComplete(bool result) {
68    if (result) {
69      base::Time current_time(base::Time::Now());
70      base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age_);
71
72      TransportSecurityState::DomainState domain_state;
73      domain_state.expiry = current_time + max_age_delta;
74      domain_state.mode =
75          TransportSecurityState::DomainState::MODE_OPPORTUNISTIC;
76      domain_state.include_subdomains = include_subdomains_;
77
78      sts_->EnableHost(host_, domain_state);
79    }
80
81    delete this;
82  }
83
84 private:
85  const std::string host_;
86  const int max_age_;
87  const bool include_subdomains_;
88  scoped_refptr<TransportSecurityState> sts_;
89};
90
91}  // namespace
92
93class URLRequestHttpJob::HttpFilterContext : public FilterContext {
94 public:
95  explicit HttpFilterContext(URLRequestHttpJob* job);
96  virtual ~HttpFilterContext();
97
98  // FilterContext implementation.
99  virtual bool GetMimeType(std::string* mime_type) const;
100  virtual bool GetURL(GURL* gurl) const;
101  virtual base::Time GetRequestTime() const;
102  virtual bool IsCachedContent() const;
103  virtual bool IsDownload() const;
104  virtual bool IsSdchResponse() const;
105  virtual int64 GetByteReadCount() const;
106  virtual int GetResponseCode() const;
107  virtual void RecordPacketStats(StatisticSelector statistic) const;
108
109 private:
110  URLRequestHttpJob* job_;
111
112  DISALLOW_COPY_AND_ASSIGN(HttpFilterContext);
113};
114
115URLRequestHttpJob::HttpFilterContext::HttpFilterContext(URLRequestHttpJob* job)
116    : job_(job) {
117  DCHECK(job_);
118}
119
120URLRequestHttpJob::HttpFilterContext::~HttpFilterContext() {
121}
122
123bool URLRequestHttpJob::HttpFilterContext::GetMimeType(
124    std::string* mime_type) const {
125  return job_->GetMimeType(mime_type);
126}
127
128bool URLRequestHttpJob::HttpFilterContext::GetURL(GURL* gurl) const {
129  if (!job_->request())
130    return false;
131  *gurl = job_->request()->url();
132  return true;
133}
134
135base::Time URLRequestHttpJob::HttpFilterContext::GetRequestTime() const {
136  return job_->request() ? job_->request()->request_time() : base::Time();
137}
138
139bool URLRequestHttpJob::HttpFilterContext::IsCachedContent() const {
140  return job_->is_cached_content_;
141}
142
143bool URLRequestHttpJob::HttpFilterContext::IsDownload() const {
144  return (job_->request_info_.load_flags & LOAD_IS_DOWNLOAD) != 0;
145}
146
147bool URLRequestHttpJob::HttpFilterContext::IsSdchResponse() const {
148  return job_->sdch_dictionary_advertised_;
149}
150
151int64 URLRequestHttpJob::HttpFilterContext::GetByteReadCount() const {
152  return job_->filter_input_byte_count();
153}
154
155int URLRequestHttpJob::HttpFilterContext::GetResponseCode() const {
156  return job_->GetResponseCode();
157}
158
159void URLRequestHttpJob::HttpFilterContext::RecordPacketStats(
160    StatisticSelector statistic) const {
161  job_->RecordPacketStats(statistic);
162}
163
164// TODO(darin): make sure the port blocking code is not lost
165// static
166URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request,
167                                          const std::string& scheme) {
168  DCHECK(scheme == "http" || scheme == "https");
169
170  int port = request->url().IntPort();
171  if (!IsPortAllowedByDefault(port) && !IsPortAllowedByOverride(port))
172    return new URLRequestErrorJob(request, ERR_UNSAFE_PORT);
173
174  if (!request->context() ||
175      !request->context()->http_transaction_factory()) {
176    NOTREACHED() << "requires a valid context";
177    return new URLRequestErrorJob(request, ERR_INVALID_ARGUMENT);
178  }
179
180  TransportSecurityState::DomainState domain_state;
181  if (scheme == "http" &&
182      request->context()->transport_security_state() &&
183      request->context()->transport_security_state()->IsEnabledForHost(
184          &domain_state,
185          request->url().host(),
186          request->context()->IsSNIAvailable())) {
187    if (domain_state.mode ==
188         TransportSecurityState::DomainState::MODE_STRICT) {
189      DCHECK_EQ(request->url().scheme(), "http");
190      url_canon::Replacements<char> replacements;
191      static const char kNewScheme[] = "https";
192      replacements.SetScheme(kNewScheme,
193                             url_parse::Component(0, strlen(kNewScheme)));
194      GURL new_location = request->url().ReplaceComponents(replacements);
195      return new URLRequestRedirectJob(request, new_location);
196    } else {
197      // TODO(agl): implement opportunistic HTTPS upgrade.
198    }
199  }
200
201  return new URLRequestHttpJob(request);
202}
203
204
205URLRequestHttpJob::URLRequestHttpJob(URLRequest* request)
206    : URLRequestJob(request),
207      response_info_(NULL),
208      response_cookies_save_index_(0),
209      proxy_auth_state_(AUTH_STATE_DONT_NEED_AUTH),
210      server_auth_state_(AUTH_STATE_DONT_NEED_AUTH),
211      ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_(
212          this, &URLRequestHttpJob::OnStartCompleted)),
213      ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_(
214          this, &URLRequestHttpJob::OnReadCompleted)),
215      read_in_progress_(false),
216      transaction_(NULL),
217      throttling_entry_(URLRequestThrottlerManager::GetInstance()->
218          RegisterRequestUrl(request->url())),
219      sdch_dictionary_advertised_(false),
220      sdch_test_activated_(false),
221      sdch_test_control_(false),
222      is_cached_content_(false),
223      request_creation_time_(),
224      packet_timing_enabled_(false),
225      bytes_observed_in_packets_(0),
226      packet_times_(),
227      request_time_snapshot_(),
228      final_packet_time_(),
229      observed_packet_count_(0),
230      ALLOW_THIS_IN_INITIALIZER_LIST(
231          filter_context_(new HttpFilterContext(this))),
232      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
233  ResetTimer();
234}
235
236void URLRequestHttpJob::NotifyHeadersComplete() {
237  DCHECK(!response_info_);
238
239  response_info_ = transaction_->GetResponseInfo();
240
241  // Save boolean, as we'll need this info at destruction time, and filters may
242  // also need this info.
243  is_cached_content_ = response_info_->was_cached;
244
245  if (!is_cached_content_) {
246    URLRequestThrottlerHeaderAdapter response_adapter(
247        response_info_->headers);
248    throttling_entry_->UpdateWithResponse(request_info_.url.host(),
249                                          &response_adapter);
250  }
251
252  ProcessStrictTransportSecurityHeader();
253
254  if (SdchManager::Global() &&
255      SdchManager::Global()->IsInSupportedDomain(request_->url())) {
256    static const std::string name = "Get-Dictionary";
257    std::string url_text;
258    void* iter = NULL;
259    // TODO(jar): We need to not fetch dictionaries the first time they are
260    // seen, but rather wait until we can justify their usefulness.
261    // For now, we will only fetch the first dictionary, which will at least
262    // require multiple suggestions before we get additional ones for this site.
263    // Eventually we should wait until a dictionary is requested several times
264    // before we even download it (so that we don't waste memory or bandwidth).
265    if (response_info_->headers->EnumerateHeader(&iter, name, &url_text)) {
266      // request_->url() won't be valid in the destructor, so we use an
267      // alternate copy.
268      DCHECK_EQ(request_->url(), request_info_.url);
269      // Resolve suggested URL relative to request url.
270      sdch_dictionary_url_ = request_info_.url.Resolve(url_text);
271    }
272  }
273
274  // The HTTP transaction may be restarted several times for the purposes
275  // of sending authorization information. Each time it restarts, we get
276  // notified of the headers completion so that we can update the cookie store.
277  if (transaction_->IsReadyToRestartForAuth()) {
278    DCHECK(!response_info_->auth_challenge.get());
279    RestartTransactionWithAuth(string16(), string16());
280    return;
281  }
282
283  URLRequestJob::NotifyHeadersComplete();
284}
285
286void URLRequestHttpJob::NotifyDone(const URLRequestStatus& status) {
287  RecordCompressionHistograms();
288  URLRequestJob::NotifyDone(status);
289}
290
291void URLRequestHttpJob::DestroyTransaction() {
292  DCHECK(transaction_.get());
293
294  transaction_.reset();
295  response_info_ = NULL;
296  context_ = NULL;
297}
298
299void URLRequestHttpJob::StartTransaction() {
300  // NOTE: This method assumes that request_info_ is already setup properly.
301
302  // If we already have a transaction, then we should restart the transaction
303  // with auth provided by username_ and password_.
304
305  int rv;
306
307  if (transaction_.get()) {
308    rv = transaction_->RestartWithAuth(username_, password_, &start_callback_);
309    username_.clear();
310    password_.clear();
311  } else {
312    DCHECK(request_->context());
313    DCHECK(request_->context()->http_transaction_factory());
314
315    rv = request_->context()->http_transaction_factory()->CreateTransaction(
316        &transaction_);
317    if (rv == OK) {
318      if (!URLRequestThrottlerManager::GetInstance()->enforce_throttling() ||
319          !throttling_entry_->IsDuringExponentialBackoff()) {
320        rv = transaction_->Start(
321            &request_info_, &start_callback_, request_->net_log());
322      } else {
323        // Special error code for the exponential back-off module.
324        rv = ERR_TEMPORARILY_THROTTLED;
325      }
326      // Make sure the context is alive for the duration of the
327      // transaction.
328      context_ = request_->context();
329    }
330  }
331
332  if (rv == ERR_IO_PENDING)
333    return;
334
335  // The transaction started synchronously, but we need to notify the
336  // URLRequest delegate via the message loop.
337  MessageLoop::current()->PostTask(
338      FROM_HERE,
339      method_factory_.NewRunnableMethod(
340          &URLRequestHttpJob::OnStartCompleted, rv));
341}
342
343void URLRequestHttpJob::AddExtraHeaders() {
344  // TODO(jar): Consider optimizing away SDCH advertising bytes when the URL is
345  // probably an img or such (and SDCH encoding is not likely).
346  bool advertise_sdch = SdchManager::Global() &&
347      SdchManager::Global()->IsInSupportedDomain(request_->url());
348  std::string avail_dictionaries;
349  if (advertise_sdch) {
350    SdchManager::Global()->GetAvailDictionaryList(request_->url(),
351                                                  &avail_dictionaries);
352
353    // The AllowLatencyExperiment() is only true if we've successfully done a
354    // full SDCH compression recently in this browser session for this host.
355    // Note that for this path, there might be no applicable dictionaries, and
356    // hence we can't participate in the experiment.
357    if (!avail_dictionaries.empty() &&
358        SdchManager::Global()->AllowLatencyExperiment(request_->url())) {
359      // We are participating in the test (or control), and hence we'll
360      // eventually record statistics via either SDCH_EXPERIMENT_DECODE or
361      // SDCH_EXPERIMENT_HOLDBACK, and we'll need some packet timing data.
362      packet_timing_enabled_ = true;
363      if (base::RandDouble() < .01) {
364        sdch_test_control_ = true;  // 1% probability.
365        advertise_sdch = false;
366      } else {
367        sdch_test_activated_ = true;
368      }
369    }
370  }
371
372  // Supply Accept-Encoding headers first so that it is more likely that they
373  // will be in the first transmitted packet.  This can sometimes make it easier
374  // to filter and analyze the streams to assure that a proxy has not damaged
375  // these headers.  Some proxies deliberately corrupt Accept-Encoding headers.
376  if (!advertise_sdch) {
377    // Tell the server what compression formats we support (other than SDCH).
378    request_info_.extra_headers.SetHeader(
379        HttpRequestHeaders::kAcceptEncoding, "gzip,deflate");
380  } else {
381    // Include SDCH in acceptable list.
382    request_info_.extra_headers.SetHeader(
383        HttpRequestHeaders::kAcceptEncoding, "gzip,deflate,sdch");
384    if (!avail_dictionaries.empty()) {
385      request_info_.extra_headers.SetHeader(
386          kAvailDictionaryHeader,
387          avail_dictionaries);
388      sdch_dictionary_advertised_ = true;
389      // Since we're tagging this transaction as advertising a dictionary, we'll
390      // definately employ an SDCH filter (or tentative sdch filter) when we get
391      // a response.  When done, we'll record histograms via SDCH_DECODE or
392      // SDCH_PASSTHROUGH.  Hence we need to record packet arrival times.
393      packet_timing_enabled_ = true;
394    }
395  }
396
397  URLRequestContext* context = request_->context();
398  if (context) {
399    // Only add default Accept-Language and Accept-Charset if the request
400    // didn't have them specified.
401    if (!context->accept_language().empty()) {
402      request_info_.extra_headers.SetHeaderIfMissing(
403          HttpRequestHeaders::kAcceptLanguage,
404          context->accept_language());
405    }
406    if (!context->accept_charset().empty()) {
407      request_info_.extra_headers.SetHeaderIfMissing(
408          HttpRequestHeaders::kAcceptCharset,
409          context->accept_charset());
410    }
411  }
412}
413
414void URLRequestHttpJob::AddCookieHeaderAndStart() {
415  // No matter what, we want to report our status as IO pending since we will
416  // be notifying our consumer asynchronously via OnStartCompleted.
417  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
418
419  int policy = OK;
420
421  if (request_info_.load_flags & LOAD_DO_NOT_SEND_COOKIES) {
422    policy = ERR_FAILED;
423  } else if (request_->context()->cookie_policy()) {
424    policy = request_->context()->cookie_policy()->CanGetCookies(
425        request_->url(),
426        request_->first_party_for_cookies());
427  }
428
429  OnCanGetCookiesCompleted(policy);
430}
431
432void URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete() {
433  DCHECK(transaction_.get());
434
435  const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
436  DCHECK(response_info);
437
438  response_cookies_.clear();
439  response_cookies_save_index_ = 0;
440
441  FetchResponseCookies(response_info, &response_cookies_);
442
443  // Now, loop over the response cookies, and attempt to persist each.
444  SaveNextCookie();
445}
446
447void URLRequestHttpJob::SaveNextCookie() {
448  if (response_cookies_save_index_ == response_cookies_.size()) {
449    response_cookies_.clear();
450    response_cookies_save_index_ = 0;
451    SetStatus(URLRequestStatus());  // Clear the IO_PENDING status
452    NotifyHeadersComplete();
453    return;
454  }
455
456  // No matter what, we want to report our status as IO pending since we will
457  // be notifying our consumer asynchronously via OnStartCompleted.
458  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
459
460  int policy = OK;
461
462  if (request_info_.load_flags & LOAD_DO_NOT_SAVE_COOKIES) {
463    policy = ERR_FAILED;
464  } else if (request_->context()->cookie_policy()) {
465    policy = request_->context()->cookie_policy()->CanSetCookie(
466        request_->url(),
467        request_->first_party_for_cookies(),
468        response_cookies_[response_cookies_save_index_]);
469  }
470
471  OnCanSetCookieCompleted(policy);
472}
473
474void URLRequestHttpJob::FetchResponseCookies(
475    const HttpResponseInfo* response_info,
476    std::vector<std::string>* cookies) {
477  std::string name = "Set-Cookie";
478  std::string value;
479
480  void* iter = NULL;
481  while (response_info->headers->EnumerateHeader(&iter, name, &value)) {
482    if (!value.empty())
483      cookies->push_back(value);
484  }
485}
486
487void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() {
488  DCHECK(response_info_);
489
490  URLRequestContext* ctx = request_->context();
491  if (!ctx || !ctx->transport_security_state())
492    return;
493
494  const bool https = response_info_->ssl_info.is_valid();
495  const bool valid_https =
496      https && !IsCertStatusError(response_info_->ssl_info.cert_status);
497
498  std::string name = "Strict-Transport-Security";
499  std::string value;
500
501  int max_age;
502  bool include_subdomains;
503
504  void* iter = NULL;
505  while (response_info_->headers->EnumerateHeader(&iter, name, &value)) {
506    const bool ok = TransportSecurityState::ParseHeader(
507        value, &max_age, &include_subdomains);
508    if (!ok)
509      continue;
510    // We will only accept strict mode if we saw the header from an HTTPS
511    // connection with no certificate problems.
512    if (!valid_https)
513      continue;
514    base::Time current_time(base::Time::Now());
515    base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
516
517    TransportSecurityState::DomainState domain_state;
518    domain_state.expiry = current_time + max_age_delta;
519    domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
520    domain_state.include_subdomains = include_subdomains;
521
522    ctx->transport_security_state()->EnableHost(request_info_.url.host(),
523                                                domain_state);
524  }
525
526  // TODO(agl): change this over when we have fixed things at the server end.
527  // The string should be "Opportunistic-Transport-Security";
528  name = "X-Bodge-Transport-Security";
529
530  while (response_info_->headers->EnumerateHeader(&iter, name, &value)) {
531    const bool ok = TransportSecurityState::ParseHeader(
532        value, &max_age, &include_subdomains);
533    if (!ok)
534      continue;
535    // If we saw an opportunistic request over HTTPS, then clearly we can make
536    // HTTPS connections to the host so we should remember this.
537    if (https) {
538      base::Time current_time(base::Time::Now());
539      base::TimeDelta max_age_delta = base::TimeDelta::FromSeconds(max_age);
540
541      TransportSecurityState::DomainState domain_state;
542      domain_state.expiry = current_time + max_age_delta;
543      domain_state.mode =
544          TransportSecurityState::DomainState::MODE_SPDY_ONLY;
545      domain_state.include_subdomains = include_subdomains;
546
547      ctx->transport_security_state()->EnableHost(request_info_.url.host(),
548                                                  domain_state);
549      continue;
550    }
551
552    if (!request())
553      break;
554
555    // At this point, we have a request for opportunistic encryption over HTTP.
556    // In this case we need to probe to check that we can make HTTPS
557    // connections to that host.
558    HTTPSProber* const prober = HTTPSProber::GetInstance();
559    if (prober->HaveProbed(request_info_.url.host()) ||
560        prober->InFlight(request_info_.url.host())) {
561      continue;
562    }
563
564    HTTPSProberDelegateImpl* delegate =
565        new HTTPSProberDelegateImpl(request_info_.url.host(), max_age,
566                                    include_subdomains,
567                                    ctx->transport_security_state());
568    if (!prober->ProbeHost(request_info_.url.host(), request()->context(),
569                           delegate)) {
570      delete delegate;
571    }
572  }
573}
574
575void URLRequestHttpJob::OnCanGetCookiesCompleted(int policy) {
576  // If the request was destroyed, then there is no more work to do.
577  if (request_ && request_->delegate()) {
578    if (request_->context()->cookie_store()) {
579      if (policy == ERR_ACCESS_DENIED) {
580        request_->delegate()->OnGetCookies(request_, true);
581      } else if (policy == OK) {
582        request_->delegate()->OnGetCookies(request_, false);
583        CookieOptions options;
584        options.set_include_httponly();
585        std::string cookies =
586            request_->context()->cookie_store()->GetCookiesWithOptions(
587                request_->url(), options);
588        if (!cookies.empty()) {
589          request_info_.extra_headers.SetHeader(
590              HttpRequestHeaders::kCookie, cookies);
591        }
592      }
593    }
594    // We may have been canceled within OnGetCookies.
595    if (GetStatus().is_success()) {
596      StartTransaction();
597    } else {
598      NotifyCanceled();
599    }
600  }
601}
602
603void URLRequestHttpJob::OnCanSetCookieCompleted(int policy) {
604  // If the request was destroyed, then there is no more work to do.
605  if (request_ && request_->delegate()) {
606    if (request_->context()->cookie_store()) {
607      if (policy == ERR_ACCESS_DENIED) {
608        CookieOptions options;
609        options.set_include_httponly();
610        request_->delegate()->OnSetCookie(
611            request_,
612            response_cookies_[response_cookies_save_index_],
613            options,
614            true);
615      } else if (policy == OK || policy == OK_FOR_SESSION_ONLY) {
616        // OK to save the current response cookie now.
617        CookieOptions options;
618        options.set_include_httponly();
619        if (policy == OK_FOR_SESSION_ONLY)
620          options.set_force_session();
621        request_->context()->cookie_store()->SetCookieWithOptions(
622            request_->url(), response_cookies_[response_cookies_save_index_],
623            options);
624        request_->delegate()->OnSetCookie(
625            request_,
626            response_cookies_[response_cookies_save_index_],
627            options,
628            false);
629      }
630    }
631    response_cookies_save_index_++;
632    // We may have been canceled within OnSetCookie.
633    if (GetStatus().is_success()) {
634      SaveNextCookie();
635    } else {
636      NotifyCanceled();
637    }
638  }
639}
640
641void URLRequestHttpJob::OnStartCompleted(int result) {
642  RecordTimer();
643
644  // If the request was destroyed, then there is no more work to do.
645  if (!request_ || !request_->delegate())
646    return;
647
648  // If the transaction was destroyed, then the job was cancelled, and
649  // we can just ignore this notification.
650  if (!transaction_.get())
651    return;
652
653  // Clear the IO_PENDING status
654  SetStatus(URLRequestStatus());
655
656  // Take care of any mandates for public key pinning.
657  // TODO(agl): we might have an issue here where a request for foo.example.com
658  // merges into a SPDY connection to www.example.com, and gets a different
659  // certificate.
660  const SSLInfo& ssl_info = transaction_->GetResponseInfo()->ssl_info;
661  if (result == OK &&
662      ssl_info.is_valid() &&
663      context_->transport_security_state()) {
664    TransportSecurityState::DomainState domain_state;
665    if (context_->transport_security_state()->IsEnabledForHost(
666            &domain_state,
667            request_->url().host(),
668            context_->IsSNIAvailable()) &&
669        ssl_info.is_issued_by_known_root &&
670        !domain_state.IsChainOfPublicKeysPermitted(ssl_info.public_key_hashes)){
671      result = ERR_CERT_INVALID;
672    }
673  }
674
675  if (result == OK) {
676    SaveCookiesAndNotifyHeadersComplete();
677  } else if (ShouldTreatAsCertificateError(result)) {
678    // We encountered an SSL certificate error.  Ask our delegate to decide
679    // what we should do.
680    // TODO(wtc): also pass ssl_info.cert_status, or just pass the whole
681    // ssl_info.
682    request_->delegate()->OnSSLCertificateError(
683        request_, result, transaction_->GetResponseInfo()->ssl_info.cert);
684  } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
685    request_->delegate()->OnCertificateRequested(
686        request_, transaction_->GetResponseInfo()->cert_request_info);
687  } else {
688    NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, result));
689  }
690}
691
692void URLRequestHttpJob::OnReadCompleted(int result) {
693  read_in_progress_ = false;
694
695  if (result == 0) {
696    NotifyDone(URLRequestStatus());
697  } else if (result < 0) {
698    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
699  } else {
700    // Clear the IO_PENDING status
701    SetStatus(URLRequestStatus());
702  }
703
704  NotifyReadComplete(result);
705}
706
707bool URLRequestHttpJob::ShouldTreatAsCertificateError(int result) {
708  if (!IsCertificateError(result))
709    return false;
710
711  // Revocation check failures are always certificate errors, even if the host
712  // is using Strict-Transport-Security.
713  if (result == ERR_CERT_UNABLE_TO_CHECK_REVOCATION)
714    return true;
715
716  // Check whether our context is using Strict-Transport-Security.
717  if (!context_->transport_security_state())
718    return true;
719
720  TransportSecurityState::DomainState domain_state;
721  // TODO(agl): don't ignore opportunistic mode.
722  const bool r = context_->transport_security_state()->IsEnabledForHost(
723      &domain_state, request_info_.url.host(), context_->IsSNIAvailable());
724
725  return !r || domain_state.mode ==
726               TransportSecurityState::DomainState::MODE_OPPORTUNISTIC;
727}
728
729void URLRequestHttpJob::RestartTransactionWithAuth(
730    const string16& username,
731    const string16& password) {
732  username_ = username;
733  password_ = password;
734
735  // These will be reset in OnStartCompleted.
736  response_info_ = NULL;
737  response_cookies_.clear();
738
739  ResetTimer();
740
741  // Update the cookies, since the cookie store may have been updated from the
742  // headers in the 401/407. Since cookies were already appended to
743  // extra_headers, we need to strip them out before adding them again.
744  request_info_.extra_headers.RemoveHeader(
745      HttpRequestHeaders::kCookie);
746
747  AddCookieHeaderAndStart();
748}
749
750void URLRequestHttpJob::SetUpload(UploadData* upload) {
751  DCHECK(!transaction_.get()) << "cannot change once started";
752  request_info_.upload_data = upload;
753}
754
755void URLRequestHttpJob::SetExtraRequestHeaders(
756    const HttpRequestHeaders& headers) {
757  DCHECK(!transaction_.get()) << "cannot change once started";
758  request_info_.extra_headers.CopyFrom(headers);
759}
760
761void URLRequestHttpJob::Start() {
762  DCHECK(!transaction_.get());
763
764  // Ensure that we do not send username and password fields in the referrer.
765  GURL referrer(request_->GetSanitizedReferrer());
766
767  request_info_.url = request_->url();
768  request_info_.referrer = referrer;
769  request_info_.method = request_->method();
770  request_info_.load_flags = request_->load_flags();
771  request_info_.priority = request_->priority();
772  request_info_.request_id = request_->identifier();
773
774  if (request_->context()) {
775    request_info_.extra_headers.SetHeaderIfMissing(
776        HttpRequestHeaders::kUserAgent,
777        request_->context()->GetUserAgent(request_->url()));
778  }
779
780  AddExtraHeaders();
781
782#ifdef ANDROID
783  // Attribute network traffic to the UID of the caller
784  request_info_.valid_uid = false;
785  request_info_.calling_uid = 0;
786
787  if (request_->context()) {
788    uid_t uid;
789    if(request_->context()->getUID(&uid)) {
790      request_info_.valid_uid = true;
791      request_info_.calling_uid = uid;
792    }
793  }
794#endif
795
796  AddCookieHeaderAndStart();
797}
798
799void URLRequestHttpJob::Kill() {
800  if (!transaction_.get())
801    return;
802
803  DestroyTransaction();
804  URLRequestJob::Kill();
805}
806
807LoadState URLRequestHttpJob::GetLoadState() const {
808  return transaction_.get() ?
809      transaction_->GetLoadState() : LOAD_STATE_IDLE;
810}
811
812uint64 URLRequestHttpJob::GetUploadProgress() const {
813  return transaction_.get() ? transaction_->GetUploadProgress() : 0;
814}
815
816bool URLRequestHttpJob::GetMimeType(std::string* mime_type) const {
817  DCHECK(transaction_.get());
818
819  if (!response_info_)
820    return false;
821
822  return response_info_->headers->GetMimeType(mime_type);
823}
824
825bool URLRequestHttpJob::GetCharset(std::string* charset) {
826  DCHECK(transaction_.get());
827
828  if (!response_info_)
829    return false;
830
831  return response_info_->headers->GetCharset(charset);
832}
833
834void URLRequestHttpJob::GetResponseInfo(HttpResponseInfo* info) {
835  DCHECK(request_);
836  DCHECK(transaction_.get());
837
838  if (response_info_)
839    *info = *response_info_;
840}
841
842bool URLRequestHttpJob::GetResponseCookies(
843    std::vector<std::string>* cookies) {
844  DCHECK(transaction_.get());
845
846  if (!response_info_)
847    return false;
848
849  // TODO(darin): Why are we extracting response cookies again?  Perhaps we
850  // should just leverage response_cookies_.
851
852  cookies->clear();
853  FetchResponseCookies(response_info_, cookies);
854  return true;
855}
856
857int URLRequestHttpJob::GetResponseCode() const {
858  DCHECK(transaction_.get());
859
860  if (!response_info_)
861    return -1;
862
863  return response_info_->headers->response_code();
864}
865
866Filter* URLRequestHttpJob::SetupFilter() const {
867  DCHECK(transaction_.get());
868  if (!response_info_)
869    return NULL;
870
871  std::vector<Filter::FilterType> encoding_types;
872  std::string encoding_type;
873  void* iter = NULL;
874  while (response_info_->headers->EnumerateHeader(&iter, "Content-Encoding",
875                                                  &encoding_type)) {
876    encoding_types.push_back(Filter::ConvertEncodingToType(encoding_type));
877  }
878
879  // Even if encoding types are empty, there is a chance that we need to add
880  // some decoding, as some proxies strip encoding completely. In such cases,
881  // we may need to add (for example) SDCH filtering (when the context suggests
882  // it is appropriate).
883  Filter::FixupEncodingTypes(*filter_context_, &encoding_types);
884
885  return !encoding_types.empty()
886      ? Filter::Factory(encoding_types, *filter_context_) : NULL;
887}
888
889bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) {
890  // We only allow redirects to certain "safe" protocols.  This does not
891  // restrict redirects to externally handled protocols.  Our consumer would
892  // need to take care of those.
893
894  if (!URLRequest::IsHandledURL(location))
895    return true;
896
897  static const char* kSafeSchemes[] = {
898    "http",
899    "https",
900    "ftp"
901  };
902
903  for (size_t i = 0; i < arraysize(kSafeSchemes); ++i) {
904    if (location.SchemeIs(kSafeSchemes[i]))
905      return true;
906  }
907
908  return false;
909}
910
911bool URLRequestHttpJob::NeedsAuth() {
912  int code = GetResponseCode();
913  if (code == -1)
914    return false;
915
916  // Check if we need either Proxy or WWW Authentication.  This could happen
917  // because we either provided no auth info, or provided incorrect info.
918  switch (code) {
919    case 407:
920      if (proxy_auth_state_ == AUTH_STATE_CANCELED)
921        return false;
922      proxy_auth_state_ = AUTH_STATE_NEED_AUTH;
923      return true;
924    case 401:
925      if (server_auth_state_ == AUTH_STATE_CANCELED)
926        return false;
927      server_auth_state_ = AUTH_STATE_NEED_AUTH;
928      return true;
929  }
930  return false;
931}
932
933void URLRequestHttpJob::GetAuthChallengeInfo(
934    scoped_refptr<AuthChallengeInfo>* result) {
935  DCHECK(transaction_.get());
936  DCHECK(response_info_);
937
938  // sanity checks:
939  DCHECK(proxy_auth_state_ == AUTH_STATE_NEED_AUTH ||
940         server_auth_state_ == AUTH_STATE_NEED_AUTH);
941  DCHECK(response_info_->headers->response_code() == 401 ||
942         response_info_->headers->response_code() == 407);
943
944  *result = response_info_->auth_challenge;
945}
946
947void URLRequestHttpJob::SetAuth(const string16& username,
948                                const string16& password) {
949  DCHECK(transaction_.get());
950
951  // Proxy gets set first, then WWW.
952  if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
953    proxy_auth_state_ = AUTH_STATE_HAVE_AUTH;
954  } else {
955    DCHECK_EQ(server_auth_state_, AUTH_STATE_NEED_AUTH);
956    server_auth_state_ = AUTH_STATE_HAVE_AUTH;
957  }
958
959  RestartTransactionWithAuth(username, password);
960}
961
962void URLRequestHttpJob::CancelAuth() {
963  // Proxy gets set first, then WWW.
964  if (proxy_auth_state_ == AUTH_STATE_NEED_AUTH) {
965    proxy_auth_state_ = AUTH_STATE_CANCELED;
966  } else {
967    DCHECK_EQ(server_auth_state_, AUTH_STATE_NEED_AUTH);
968    server_auth_state_ = AUTH_STATE_CANCELED;
969  }
970
971  // These will be reset in OnStartCompleted.
972  response_info_ = NULL;
973  response_cookies_.clear();
974
975  ResetTimer();
976
977  // OK, let the consumer read the error page...
978  //
979  // Because we set the AUTH_STATE_CANCELED flag, NeedsAuth will return false,
980  // which will cause the consumer to receive OnResponseStarted instead of
981  // OnAuthRequired.
982  //
983  // We have to do this via InvokeLater to avoid "recursing" the consumer.
984  //
985  MessageLoop::current()->PostTask(
986      FROM_HERE,
987      method_factory_.NewRunnableMethod(
988          &URLRequestHttpJob::OnStartCompleted, OK));
989}
990
991void URLRequestHttpJob::ContinueWithCertificate(
992    X509Certificate* client_cert) {
993  DCHECK(transaction_.get());
994
995  DCHECK(!response_info_) << "should not have a response yet";
996
997  ResetTimer();
998
999  // No matter what, we want to report our status as IO pending since we will
1000  // be notifying our consumer asynchronously via OnStartCompleted.
1001  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
1002
1003  int rv = transaction_->RestartWithCertificate(client_cert, &start_callback_);
1004  if (rv == ERR_IO_PENDING)
1005    return;
1006
1007  // The transaction started synchronously, but we need to notify the
1008  // URLRequest delegate via the message loop.
1009  MessageLoop::current()->PostTask(
1010      FROM_HERE,
1011      method_factory_.NewRunnableMethod(
1012          &URLRequestHttpJob::OnStartCompleted, rv));
1013}
1014
1015void URLRequestHttpJob::ContinueDespiteLastError() {
1016  // If the transaction was destroyed, then the job was cancelled.
1017  if (!transaction_.get())
1018    return;
1019
1020  DCHECK(!response_info_) << "should not have a response yet";
1021
1022  ResetTimer();
1023
1024  // No matter what, we want to report our status as IO pending since we will
1025  // be notifying our consumer asynchronously via OnStartCompleted.
1026  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
1027
1028  int rv = transaction_->RestartIgnoringLastError(&start_callback_);
1029  if (rv == ERR_IO_PENDING)
1030    return;
1031
1032  // The transaction started synchronously, but we need to notify the
1033  // URLRequest delegate via the message loop.
1034  MessageLoop::current()->PostTask(
1035      FROM_HERE,
1036      method_factory_.NewRunnableMethod(
1037          &URLRequestHttpJob::OnStartCompleted, rv));
1038}
1039
1040bool URLRequestHttpJob::ReadRawData(IOBuffer* buf, int buf_size,
1041                                    int *bytes_read) {
1042  DCHECK_NE(buf_size, 0);
1043  DCHECK(bytes_read);
1044  DCHECK(!read_in_progress_);
1045
1046  int rv = transaction_->Read(buf, buf_size, &read_callback_);
1047  if (rv >= 0) {
1048    *bytes_read = rv;
1049    return true;
1050  }
1051
1052  if (rv == ERR_IO_PENDING) {
1053    read_in_progress_ = true;
1054    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
1055  } else {
1056    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
1057  }
1058
1059  return false;
1060}
1061
1062void URLRequestHttpJob::StopCaching() {
1063  if (transaction_.get())
1064    transaction_->StopCaching();
1065}
1066
1067HostPortPair URLRequestHttpJob::GetSocketAddress() const {
1068  return response_info_ ? response_info_->socket_address : HostPortPair();
1069}
1070
1071URLRequestHttpJob::~URLRequestHttpJob() {
1072  DCHECK(!sdch_test_control_ || !sdch_test_activated_);
1073  if (!is_cached_content_) {
1074    if (sdch_test_control_)
1075      RecordPacketStats(FilterContext::SDCH_EXPERIMENT_HOLDBACK);
1076    if (sdch_test_activated_)
1077      RecordPacketStats(FilterContext::SDCH_EXPERIMENT_DECODE);
1078  }
1079  // Make sure SDCH filters are told to emit histogram data while
1080  // filter_context_ is still alive.
1081  DestroyFilters();
1082
1083  if (sdch_dictionary_url_.is_valid()) {
1084    // Prior to reaching the destructor, request_ has been set to a NULL
1085    // pointer, so request_->url() is no longer valid in the destructor, and we
1086    // use an alternate copy |request_info_.url|.
1087    SdchManager* manager = SdchManager::Global();
1088    // To be extra safe, since this is a "different time" from when we decided
1089    // to get the dictionary, we'll validate that an SdchManager is available.
1090    // At shutdown time, care is taken to be sure that we don't delete this
1091    // globally useful instance "too soon," so this check is just defensive
1092    // coding to assure that IF the system is shutting down, we don't have any
1093    // problem if the manager was deleted ahead of time.
1094    if (manager)  // Defensive programming.
1095      manager->FetchDictionary(request_info_.url, sdch_dictionary_url_);
1096  }
1097}
1098
1099void URLRequestHttpJob::RecordTimer() {
1100  if (request_creation_time_.is_null()) {
1101    NOTREACHED()
1102        << "The same transaction shouldn't start twice without new timing.";
1103    return;
1104  }
1105
1106  base::TimeDelta to_start = base::Time::Now() - request_creation_time_;
1107  request_creation_time_ = base::Time();
1108
1109  static const bool use_prefetch_histogram =
1110      base::FieldTrialList::Find("Prefetch") &&
1111      !base::FieldTrialList::Find("Prefetch")->group_name().empty();
1112
1113  UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpTimeToFirstByte", to_start);
1114  if (use_prefetch_histogram) {
1115    UMA_HISTOGRAM_MEDIUM_TIMES(
1116        base::FieldTrial::MakeName("Net.HttpTimeToFirstByte",
1117                                   "Prefetch"),
1118        to_start);
1119  }
1120
1121  const bool is_prerender = !!(request_info_.load_flags & LOAD_PRERENDER);
1122  if (is_prerender) {
1123    UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpTimeToFirstByte_Prerender",
1124                               to_start);
1125    if (use_prefetch_histogram) {
1126      UMA_HISTOGRAM_MEDIUM_TIMES(
1127          base::FieldTrial::MakeName("Net.HttpTimeToFirstByte_Prerender",
1128                                     "Prefetch"),
1129          to_start);
1130    }
1131  } else {
1132    UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpTimeToFirstByte_NonPrerender",
1133                               to_start);
1134    if (use_prefetch_histogram) {
1135      UMA_HISTOGRAM_MEDIUM_TIMES(
1136          base::FieldTrial::MakeName("Net.HttpTimeToFirstByte_NonPrerender",
1137                                     "Prefetch"),
1138          to_start);
1139    }
1140  }
1141}
1142
1143void URLRequestHttpJob::ResetTimer() {
1144  if (!request_creation_time_.is_null()) {
1145    NOTREACHED()
1146        << "The timer was reset before it was recorded.";
1147    return;
1148  }
1149  request_creation_time_ = base::Time::Now();
1150}
1151
1152void URLRequestHttpJob::UpdatePacketReadTimes() {
1153  if (!packet_timing_enabled_)
1154    return;
1155
1156  if (filter_input_byte_count() <= bytes_observed_in_packets_) {
1157    DCHECK_EQ(filter_input_byte_count(), bytes_observed_in_packets_);
1158    return;  // No new bytes have arrived.
1159  }
1160
1161  if (!bytes_observed_in_packets_)
1162    request_time_snapshot_ = request_ ? request_->request_time() : base::Time();
1163
1164  final_packet_time_ = base::Time::Now();
1165  const size_t kTypicalPacketSize = 1430;
1166  while (filter_input_byte_count() > bytes_observed_in_packets_) {
1167    ++observed_packet_count_;
1168    if (packet_times_.size() < kSdchPacketHistogramCount) {
1169      packet_times_.push_back(final_packet_time_);
1170      DCHECK_EQ(static_cast<size_t>(observed_packet_count_),
1171                packet_times_.size());
1172    }
1173    bytes_observed_in_packets_ += kTypicalPacketSize;
1174  }
1175  // Since packets may not be full, we'll remember the number of bytes we've
1176  // accounted for in packets thus far.
1177  bytes_observed_in_packets_ = filter_input_byte_count();
1178}
1179
1180void URLRequestHttpJob::RecordPacketStats(
1181    FilterContext::StatisticSelector statistic) const {
1182  if (!packet_timing_enabled_ || (final_packet_time_ == base::Time()))
1183    return;
1184
1185  base::TimeDelta duration = final_packet_time_ - request_time_snapshot_;
1186  switch (statistic) {
1187    case FilterContext::SDCH_DECODE: {
1188      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_Latency_F_a", duration,
1189                                  base::TimeDelta::FromMilliseconds(20),
1190                                  base::TimeDelta::FromMinutes(10), 100);
1191      UMA_HISTOGRAM_COUNTS_100("Sdch3.Network_Decode_Packets_b",
1192                               static_cast<int>(observed_packet_count_));
1193      UMA_HISTOGRAM_CUSTOM_COUNTS("Sdch3.Network_Decode_Bytes_Processed_b",
1194          static_cast<int>(bytes_observed_in_packets_), 500, 100000, 100);
1195      if (packet_times_.empty())
1196        return;
1197      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_1st_To_Last_a",
1198                                  final_packet_time_ - packet_times_[0],
1199                                  base::TimeDelta::FromMilliseconds(20),
1200                                  base::TimeDelta::FromMinutes(10), 100);
1201
1202      DCHECK_GT(kSdchPacketHistogramCount, 4u);
1203      if (packet_times_.size() <= 4)
1204        return;
1205      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_1st_To_2nd_c",
1206                                  packet_times_[1] - packet_times_[0],
1207                                  base::TimeDelta::FromMilliseconds(1),
1208                                  base::TimeDelta::FromSeconds(10), 100);
1209      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_2nd_To_3rd_c",
1210                                  packet_times_[2] - packet_times_[1],
1211                                  base::TimeDelta::FromMilliseconds(1),
1212                                  base::TimeDelta::FromSeconds(10), 100);
1213      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_3rd_To_4th_c",
1214                                  packet_times_[3] - packet_times_[2],
1215                                  base::TimeDelta::FromMilliseconds(1),
1216                                  base::TimeDelta::FromSeconds(10), 100);
1217      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Decode_4th_To_5th_c",
1218                                  packet_times_[4] - packet_times_[3],
1219                                  base::TimeDelta::FromMilliseconds(1),
1220                                  base::TimeDelta::FromSeconds(10), 100);
1221      return;
1222    }
1223    case FilterContext::SDCH_PASSTHROUGH: {
1224      // Despite advertising a dictionary, we handled non-sdch compressed
1225      // content.
1226      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_Latency_F_a",
1227                                  duration,
1228                                  base::TimeDelta::FromMilliseconds(20),
1229                                  base::TimeDelta::FromMinutes(10), 100);
1230      UMA_HISTOGRAM_COUNTS_100("Sdch3.Network_Pass-through_Packets_b",
1231                               observed_packet_count_);
1232      if (packet_times_.empty())
1233        return;
1234      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_1st_To_Last_a",
1235                                  final_packet_time_ - packet_times_[0],
1236                                  base::TimeDelta::FromMilliseconds(20),
1237                                  base::TimeDelta::FromMinutes(10), 100);
1238      DCHECK_GT(kSdchPacketHistogramCount, 4u);
1239      if (packet_times_.size() <= 4)
1240        return;
1241      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_1st_To_2nd_c",
1242                                  packet_times_[1] - packet_times_[0],
1243                                  base::TimeDelta::FromMilliseconds(1),
1244                                  base::TimeDelta::FromSeconds(10), 100);
1245      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_2nd_To_3rd_c",
1246                                  packet_times_[2] - packet_times_[1],
1247                                  base::TimeDelta::FromMilliseconds(1),
1248                                  base::TimeDelta::FromSeconds(10), 100);
1249      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_3rd_To_4th_c",
1250                                  packet_times_[3] - packet_times_[2],
1251                                  base::TimeDelta::FromMilliseconds(1),
1252                                  base::TimeDelta::FromSeconds(10), 100);
1253      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Network_Pass-through_4th_To_5th_c",
1254                                  packet_times_[4] - packet_times_[3],
1255                                  base::TimeDelta::FromMilliseconds(1),
1256                                  base::TimeDelta::FromSeconds(10), 100);
1257      return;
1258    }
1259
1260    case FilterContext::SDCH_EXPERIMENT_DECODE: {
1261      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Decode",
1262                                  duration,
1263                                  base::TimeDelta::FromMilliseconds(20),
1264                                  base::TimeDelta::FromMinutes(10), 100);
1265      // We already provided interpacket histograms above in the SDCH_DECODE
1266      // case, so we don't need them here.
1267      return;
1268    }
1269    case FilterContext::SDCH_EXPERIMENT_HOLDBACK: {
1270      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback",
1271                                  duration,
1272                                  base::TimeDelta::FromMilliseconds(20),
1273                                  base::TimeDelta::FromMinutes(10), 100);
1274      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback_1st_To_Last_a",
1275                                  final_packet_time_ - packet_times_[0],
1276                                  base::TimeDelta::FromMilliseconds(20),
1277                                  base::TimeDelta::FromMinutes(10), 100);
1278
1279      DCHECK_GT(kSdchPacketHistogramCount, 4u);
1280      if (packet_times_.size() <= 4)
1281        return;
1282      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback_1st_To_2nd_c",
1283                                  packet_times_[1] - packet_times_[0],
1284                                  base::TimeDelta::FromMilliseconds(1),
1285                                  base::TimeDelta::FromSeconds(10), 100);
1286      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback_2nd_To_3rd_c",
1287                                  packet_times_[2] - packet_times_[1],
1288                                  base::TimeDelta::FromMilliseconds(1),
1289                                  base::TimeDelta::FromSeconds(10), 100);
1290      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback_3rd_To_4th_c",
1291                                  packet_times_[3] - packet_times_[2],
1292                                  base::TimeDelta::FromMilliseconds(1),
1293                                  base::TimeDelta::FromSeconds(10), 100);
1294      UMA_HISTOGRAM_CLIPPED_TIMES("Sdch3.Experiment_Holdback_4th_To_5th_c",
1295                                  packet_times_[4] - packet_times_[3],
1296                                  base::TimeDelta::FromMilliseconds(1),
1297                                  base::TimeDelta::FromSeconds(10), 100);
1298      return;
1299    }
1300    default:
1301      NOTREACHED();
1302      return;
1303  }
1304}
1305
1306// The common type of histogram we use for all compression-tracking histograms.
1307#define COMPRESSION_HISTOGRAM(name, sample) \
1308    do { \
1309      UMA_HISTOGRAM_CUSTOM_COUNTS("Net.Compress." name, sample, \
1310                                  500, 1000000, 100); \
1311    } while(0)
1312
1313void URLRequestHttpJob::RecordCompressionHistograms() {
1314  DCHECK(request_);
1315  if (!request_)
1316    return;
1317
1318  if (is_cached_content_ ||                // Don't record cached content
1319      !GetStatus().is_success() ||         // Don't record failed content
1320      !IsCompressibleContent() ||          // Only record compressible content
1321      !prefilter_bytes_read())       // Zero-byte responses aren't useful.
1322    return;
1323
1324  // Miniature requests aren't really compressible.  Don't count them.
1325  const int kMinSize = 16;
1326  if (prefilter_bytes_read() < kMinSize)
1327    return;
1328
1329  // Only record for http or https urls.
1330  bool is_http = request_->url().SchemeIs("http");
1331  bool is_https = request_->url().SchemeIs("https");
1332  if (!is_http && !is_https)
1333    return;
1334
1335  int compressed_B = prefilter_bytes_read();
1336  int decompressed_B = postfilter_bytes_read();
1337  bool was_filtered = HasFilter();
1338
1339  // We want to record how often downloaded resources are compressed.
1340  // But, we recognize that different protocols may have different
1341  // properties.  So, for each request, we'll put it into one of 3
1342  // groups:
1343  //      a) SSL resources
1344  //         Proxies cannot tamper with compression headers with SSL.
1345  //      b) Non-SSL, loaded-via-proxy resources
1346  //         In this case, we know a proxy might have interfered.
1347  //      c) Non-SSL, loaded-without-proxy resources
1348  //         In this case, we know there was no explicit proxy.  However,
1349  //         it is possible that a transparent proxy was still interfering.
1350  //
1351  // For each group, we record the same 3 histograms.
1352
1353  if (is_https) {
1354    if (was_filtered) {
1355      COMPRESSION_HISTOGRAM("SSL.BytesBeforeCompression", compressed_B);
1356      COMPRESSION_HISTOGRAM("SSL.BytesAfterCompression", decompressed_B);
1357    } else {
1358      COMPRESSION_HISTOGRAM("SSL.ShouldHaveBeenCompressed", decompressed_B);
1359    }
1360    return;
1361  }
1362
1363  if (request_->was_fetched_via_proxy()) {
1364    if (was_filtered) {
1365      COMPRESSION_HISTOGRAM("Proxy.BytesBeforeCompression", compressed_B);
1366      COMPRESSION_HISTOGRAM("Proxy.BytesAfterCompression", decompressed_B);
1367    } else {
1368      COMPRESSION_HISTOGRAM("Proxy.ShouldHaveBeenCompressed", decompressed_B);
1369    }
1370    return;
1371  }
1372
1373  if (was_filtered) {
1374    COMPRESSION_HISTOGRAM("NoProxy.BytesBeforeCompression", compressed_B);
1375    COMPRESSION_HISTOGRAM("NoProxy.BytesAfterCompression", decompressed_B);
1376  } else {
1377    COMPRESSION_HISTOGRAM("NoProxy.ShouldHaveBeenCompressed", decompressed_B);
1378  }
1379}
1380
1381bool URLRequestHttpJob::IsCompressibleContent() const {
1382  std::string mime_type;
1383  return GetMimeType(&mime_type) &&
1384      (IsSupportedJavascriptMimeType(mime_type.c_str()) ||
1385       IsSupportedNonImageMimeType(mime_type.c_str()));
1386}
1387
1388}  // namespace net
1389