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 16namespace net { 17 18HttpVaryData::HttpVaryData() : is_valid_(false) { 19} 20 21bool 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 67bool 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 77void HttpVaryData::Persist(Pickle* pickle) const { 78 DCHECK(is_valid()); 79 pickle->WriteBytes(&request_digest_, sizeof(request_digest_)); 80} 81 82bool 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 97std::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 116void 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