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