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
HttpVaryData()17 HttpVaryData::HttpVaryData() : is_valid_(false) {
18 }
19
Init(const HttpRequestInfo & request_info,const HttpResponseHeaders & response_headers)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
InitFromPickle(const Pickle & pickle,void ** iter)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
Persist(Pickle * pickle) const76 void HttpVaryData::Persist(Pickle* pickle) const {
77 DCHECK(is_valid());
78 pickle->WriteBytes(&request_digest_, sizeof(request_digest_));
79 }
80
MatchesRequest(const HttpRequestInfo & request_info,const HttpResponseHeaders & cached_response_headers) const81 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
GetRequestValue(const HttpRequestInfo & request_info,const std::string & request_header)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
AddField(const HttpRequestInfo & request_info,const std::string & request_header,MD5Context * ctx)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