Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2006-2008 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/http/http_vary_data.h"
      6 
      7 #include <stdlib.h>
      8 
      9 #include "base/pickle.h"
     10 #include "base/string_util.h"
     11 #include "net/http/http_request_info.h"
     12 #include "net/http/http_response_headers.h"
     13 #include "net/http/http_util.h"
     14 
     15 namespace net {
     16 
     17 HttpVaryData::HttpVaryData() : is_valid_(false) {
     18 }
     19 
     20 bool HttpVaryData::Init(const HttpRequestInfo& request_info,
     21                         const HttpResponseHeaders& response_headers) {
     22   MD5Context ctx;
     23   MD5Init(&ctx);
     24 
     25   is_valid_ = false;
     26   bool processed_header = false;
     27 
     28   // Feed the MD5 context in the order of the Vary header enumeration.  If the
     29   // Vary header repeats a header name, then that's OK.
     30   //
     31   // If the Vary header contains '*' then we should not construct any vary data
     32   // since it is all usurped by a '*'.  See section 13.6 of RFC 2616.
     33   //
     34   void* iter = NULL;
     35   std::string name = "vary", request_header;
     36   while (response_headers.EnumerateHeader(&iter, name, &request_header)) {
     37     if (request_header == "*")
     38       return false;
     39     AddField(request_info, request_header, &ctx);
     40     processed_header = true;
     41   }
     42 
     43   // Add an implicit 'Vary: cookie' header to any redirect to avoid redirect
     44   // loops which may result from redirects that are incorrectly marked as
     45   // cachable by the server.  Unfortunately, other browsers do not cache
     46   // redirects that result from requests containing a cookie header.  We are
     47   // treading on untested waters here, so we want to be extra careful to make
     48   // sure we do not end up with a redirect loop served from cache.
     49   //
     50   // If there is an explicit 'Vary: cookie' header, then we will just end up
     51   // digesting the cookie header twice.  Not a problem.
     52   //
     53   std::string location;
     54   if (response_headers.IsRedirect(&location)) {
     55     AddField(request_info, "cookie", &ctx);
     56     processed_header = true;
     57   }
     58 
     59   if (!processed_header)
     60     return false;
     61 
     62   MD5Final(&request_digest_, &ctx);
     63   return is_valid_ = true;
     64 }
     65 
     66 bool HttpVaryData::InitFromPickle(const Pickle& pickle, void** iter) {
     67   is_valid_ = false;
     68   const char* data;
     69   if (pickle.ReadBytes(iter, &data, sizeof(request_digest_))) {
     70     memcpy(&request_digest_, data, sizeof(request_digest_));
     71     return is_valid_ = true;
     72   }
     73   return false;
     74 }
     75 
     76 void HttpVaryData::Persist(Pickle* pickle) const {
     77   DCHECK(is_valid());
     78   pickle->WriteBytes(&request_digest_, sizeof(request_digest_));
     79 }
     80 
     81 bool HttpVaryData::MatchesRequest(
     82     const HttpRequestInfo& request_info,
     83     const HttpResponseHeaders& cached_response_headers) const {
     84   HttpVaryData new_vary_data;
     85   if (!new_vary_data.Init(request_info, cached_response_headers)) {
     86     // This shouldn't happen provided the same response headers passed here
     87     // were also used when initializing |this|.
     88     NOTREACHED();
     89     return false;
     90   }
     91   return memcmp(&new_vary_data.request_digest_, &request_digest_,
     92                 sizeof(request_digest_)) == 0;
     93 }
     94 
     95 // static
     96 std::string HttpVaryData::GetRequestValue(
     97     const HttpRequestInfo& request_info,
     98     const std::string& request_header) {
     99   // Some special cases:
    100   if (LowerCaseEqualsASCII(request_header, "referer"))
    101     return request_info.referrer.spec();
    102   if (LowerCaseEqualsASCII(request_header, "user-agent"))
    103     return request_info.user_agent;
    104 
    105   std::string result;
    106 
    107   // Check extra headers:
    108   HttpUtil::HeadersIterator it(request_info.extra_headers.begin(),
    109                                request_info.extra_headers.end(),
    110                                "\r\n");
    111   while (it.GetNext()) {
    112     size_t name_len = it.name_end() - it.name_begin();
    113     if (request_header.size() == name_len &&
    114         std::equal(it.name_begin(), it.name_end(), request_header.begin(),
    115                    CaseInsensitiveCompare<char>())) {
    116       if (!result.empty())
    117         result.append(1, ',');
    118       result.append(it.values());
    119     }
    120   }
    121 
    122   // Unfortunately, we do not have access to all of the request headers at this
    123   // point.  Most notably, we do not have access to an Authorization header if
    124   // one will be added to the request.
    125 
    126   return result;
    127 }
    128 
    129 // static
    130 void HttpVaryData::AddField(const HttpRequestInfo& request_info,
    131                             const std::string& request_header,
    132                             MD5Context* ctx) {
    133   std::string request_value = GetRequestValue(request_info, request_header);
    134 
    135   // Append a character that cannot appear in the request header line so that we
    136   // protect against case where the concatenation of two request headers could
    137   // look the same for a variety of values for the individual request headers.
    138   // For example, "foo: 12\nbar: 3" looks like "foo: 1\nbar: 23" otherwise.
    139   request_value.append(1, '\n');
    140 
    141   MD5Update(ctx, request_value.data(), request_value.size());
    142 }
    143 
    144 }  // namespace net
    145