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