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.
5 #include "net/http/http_vary_data.h"
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"
18 HttpVaryData::HttpVaryData() : is_valid_(false) {
21 bool HttpVaryData::Init(const HttpRequestInfo
& request_info
,
22 const HttpResponseHeaders
& response_headers
) {
27 bool processed_header
= false;
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.
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.
36 std::string name
= "vary", request_header
;
37 while (response_headers
.EnumerateHeader(&iter
, name
, &request_header
)) {
38 if (request_header
== "*")
40 AddField(request_info
, request_header
, &ctx
);
41 processed_header
= true;
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.
51 // If there is an explicit 'Vary: cookie' header, then we will just end up
52 // digesting the cookie header twice. Not a problem.
55 if (response_headers
.IsRedirect(&location
)) {
56 AddField(request_info
, "cookie", &ctx
);
57 processed_header
= true;
60 if (!processed_header
)
63 base::MD5Final(&request_digest_
, &ctx
);
64 return is_valid_
= true;
67 bool HttpVaryData::InitFromPickle(PickleIterator
* iter
) {
70 if (iter
->ReadBytes(&data
, sizeof(request_digest_
))) {
71 memcpy(&request_digest_
, data
, sizeof(request_digest_
));
72 return is_valid_
= true;
77 void HttpVaryData::Persist(Pickle
* pickle
) const {
79 pickle
->WriteBytes(&request_digest_
, sizeof(request_digest_
));
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|.
92 return memcmp(&new_vary_data
.request_digest_
, &request_digest_
,
93 sizeof(request_digest_
)) == 0;
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.
105 if (request_info
.extra_headers
.GetHeader(request_header
, &result
))
108 return std::string();
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
);
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');
123 base::MD5Update(ctx
, request_value
);