• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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 
HttpVaryData()18 HttpVaryData::HttpVaryData() : is_valid_(false) {
19 }
20 
Init(const HttpRequestInfo & request_info,const HttpResponseHeaders & response_headers)21 bool HttpVaryData::Init(const HttpRequestInfo& request_info,
22                         const HttpResponseHeaders& response_headers) {
23   MD5Context ctx;
24   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   MD5Final(&request_digest_, &ctx);
64   return is_valid_ = true;
65 }
66 
InitFromPickle(const Pickle & pickle,void ** iter)67 bool HttpVaryData::InitFromPickle(const Pickle& pickle, void** 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 
Persist(Pickle * pickle) const77 void HttpVaryData::Persist(Pickle* pickle) const {
78   DCHECK(is_valid());
79   pickle->WriteBytes(&request_digest_, sizeof(request_digest_));
80 }
81 
MatchesRequest(const HttpRequestInfo & request_info,const HttpResponseHeaders & cached_response_headers) const82 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
GetRequestValue(const HttpRequestInfo & request_info,const std::string & request_header)97 std::string HttpVaryData::GetRequestValue(
98     const HttpRequestInfo& request_info,
99     const std::string& request_header) {
100   // Some special cases:
101   if (!base::strcasecmp(request_header.c_str(), HttpRequestHeaders::kReferer))
102     return request_info.referrer.spec();
103 
104   // Unfortunately, we do not have access to all of the request headers at this
105   // point.  Most notably, we do not have access to an Authorization header if
106   // one will be added to the request.
107 
108   std::string result;
109   if (request_info.extra_headers.GetHeader(request_header, &result))
110     return result;
111 
112   return "";
113 }
114 
115 // static
AddField(const HttpRequestInfo & request_info,const std::string & request_header,MD5Context * ctx)116 void HttpVaryData::AddField(const HttpRequestInfo& request_info,
117                             const std::string& request_header,
118                             MD5Context* ctx) {
119   std::string request_value = GetRequestValue(request_info, request_header);
120 
121   // Append a character that cannot appear in the request header line so that we
122   // protect against case where the concatenation of two request headers could
123   // look the same for a variety of values for the individual request headers.
124   // For example, "foo: 12\nbar: 3" looks like "foo: 1\nbar: 23" otherwise.
125   request_value.append(1, '\n');
126 
127   MD5Update(ctx, request_value.data(), request_value.size());
128 }
129 
130 }  // namespace net
131