Home | History | Annotate | Download | only in spdy
      1 // Copyright 2014 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 #include "net/spdy/hpack_huffman_aggregator.h"
      5 
      6 #include "base/metrics/bucket_ranges.h"
      7 #include "base/metrics/field_trial.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/metrics/sample_vector.h"
     10 #include "base/stl_util.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_util.h"
     13 #include "net/base/load_flags.h"
     14 #include "net/http/http_request_headers.h"
     15 #include "net/http/http_request_info.h"
     16 #include "net/http/http_response_headers.h"
     17 #include "net/spdy/hpack_encoder.h"
     18 #include "net/spdy/spdy_http_utils.h"
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
     25 
     26 const size_t kTotalCountsPublishThreshold = 50000;
     27 
     28 // Each encoder uses the default dynamic table size of 4096 total bytes.
     29 const size_t kMaxEncoders = 20;
     30 
     31 }  // namespace
     32 
     33 HpackHuffmanAggregator::HpackHuffmanAggregator()
     34   : counts_(256, 0),
     35     total_counts_(0),
     36     max_encoders_(kMaxEncoders) {
     37 }
     38 
     39 HpackHuffmanAggregator::~HpackHuffmanAggregator() {
     40   STLDeleteContainerPairSecondPointers(encoders_.begin(), encoders_.end());
     41   encoders_.clear();
     42 }
     43 
     44 void HpackHuffmanAggregator::AggregateTransactionCharacterCounts(
     45     const HttpRequestInfo& request,
     46     const HttpRequestHeaders& request_headers,
     47     const ProxyServer& proxy,
     48     const HttpResponseHeaders& response_headers) {
     49   if (IsCrossOrigin(request)) {
     50     return;
     51   }
     52   HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
     53                                        request.url.EffectiveIntPort());
     54   HpackEncoder* encoder = ObtainEncoder(
     55       SpdySessionKey(endpoint, proxy, request.privacy_mode));
     56 
     57   // Convert and encode the request and response header sets.
     58   {
     59     SpdyHeaderBlock headers;
     60     CreateSpdyHeadersFromHttpRequest(
     61         request, request_headers, &headers, SPDY4, false);
     62 
     63     std::string tmp_out;
     64     encoder->EncodeHeaderSet(headers, &tmp_out);
     65   }
     66   {
     67     SpdyHeaderBlock headers;
     68     CreateSpdyHeadersFromHttpResponse(response_headers, &headers);
     69 
     70     std::string tmp_out;
     71     encoder->EncodeHeaderSet(headers, &tmp_out);
     72   }
     73   if (total_counts_ >= kTotalCountsPublishThreshold) {
     74     PublishCounts();
     75   }
     76 }
     77 
     78 // static
     79 bool HpackHuffmanAggregator::UseAggregator() {
     80   const std::string group_name =
     81       base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
     82   if (group_name == "Enabled") {
     83     return true;
     84   }
     85   return false;
     86 }
     87 
     88 // static
     89 void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
     90     const HttpResponseHeaders& headers,
     91     SpdyHeaderBlock* headers_out) {
     92   // Lower-case header names, and coalesce multiple values delimited by \0.
     93   // Also add the fixed status header.
     94   std::string name, value;
     95   void* it = NULL;
     96   while (headers.EnumerateHeaderLines(&it, &name, &value)) {
     97     StringToLowerASCII(&name);
     98     if (headers_out->find(name) == headers_out->end()) {
     99       (*headers_out)[name] = value;
    100     } else {
    101       (*headers_out)[name] += std::string(1, '\0') + value;
    102     }
    103   }
    104   (*headers_out)[":status"] = base::IntToString(headers.response_code());
    105 }
    106 
    107 // static
    108 bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo& request) {
    109   // Require that the request is top-level, or that it shares
    110   // an origin with its referer.
    111   HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
    112                                        request.url.EffectiveIntPort());
    113   if ((request.load_flags & LOAD_MAIN_FRAME) == 0) {
    114     std::string referer_str;
    115     if (!request.extra_headers.GetHeader(HttpRequestHeaders::kReferer,
    116                                          &referer_str)) {
    117       // Require a referer.
    118       return true;
    119     }
    120     GURL referer(referer_str);
    121     HostPortPair referer_endpoint = HostPortPair(referer.HostNoBrackets(),
    122                                                  referer.EffectiveIntPort());
    123     if (!endpoint.Equals(referer_endpoint)) {
    124       // Cross-origin request.
    125       return true;
    126     }
    127   }
    128   return false;
    129 }
    130 
    131 HpackEncoder* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey& key) {
    132   for (OriginEncoders::iterator it = encoders_.begin();
    133        it != encoders_.end(); ++it) {
    134     if (key.Equals(it->first)) {
    135       // Move to head of list and return.
    136       OriginEncoder origin_encoder = *it;
    137       encoders_.erase(it);
    138       encoders_.push_front(origin_encoder);
    139       return origin_encoder.second;
    140     }
    141   }
    142   // Not found. Create a new encoder, evicting one if needed.
    143   encoders_.push_front(std::make_pair(
    144       key, new HpackEncoder(ObtainHpackHuffmanTable())));
    145   if (encoders_.size() > max_encoders_) {
    146     delete encoders_.back().second;
    147     encoders_.pop_back();
    148   }
    149   encoders_.front().second->SetCharCountsStorage(&counts_, &total_counts_);
    150   return encoders_.front().second;
    151 }
    152 
    153 void HpackHuffmanAggregator::PublishCounts() {
    154   // base::Histogram requires that values be 1-indexed.
    155   const size_t kRangeMin = 1;
    156   const size_t kRangeMax = counts_.size() + 1;
    157   const size_t kBucketCount = kRangeMax + 1;
    158 
    159   base::BucketRanges ranges(kBucketCount + 1);
    160   for (size_t i = 0; i != ranges.size(); ++i) {
    161     ranges.set_range(i, i);
    162   }
    163   ranges.ResetChecksum();
    164 
    165   // Copy |counts_| into a SampleVector.
    166   base::SampleVector samples(&ranges);
    167   for (size_t i = 0; i != counts_.size(); ++i) {
    168     samples.Accumulate(i + 1, counts_[i]);
    169   }
    170 
    171   STATIC_HISTOGRAM_POINTER_BLOCK(
    172       kHistogramName,
    173       AddSamples(samples),
    174       base::LinearHistogram::FactoryGet(
    175           kHistogramName, kRangeMin, kRangeMax, kBucketCount,
    176           base::HistogramBase::kUmaTargetedHistogramFlag));
    177 
    178   // Clear counts.
    179   counts_.assign(counts_.size(), 0);
    180   total_counts_ = 0;
    181 }
    182 
    183 }  // namespace net
    184