| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/http/http_vary_data.h" |
| |
| #include <stdlib.h> |
| |
| #include "base/pickle.h" |
| #include "base/string_util.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_request_info.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_util.h" |
| |
| namespace net { |
| |
| HttpVaryData::HttpVaryData() : is_valid_(false) { |
| } |
| |
| bool HttpVaryData::Init(const HttpRequestInfo& request_info, |
| const HttpResponseHeaders& response_headers) { |
| base::MD5Context ctx; |
| base::MD5Init(&ctx); |
| |
| is_valid_ = false; |
| bool processed_header = false; |
| |
| // Feed the MD5 context in the order of the Vary header enumeration. If the |
| // Vary header repeats a header name, then that's OK. |
| // |
| // If the Vary header contains '*' then we should not construct any vary data |
| // since it is all usurped by a '*'. See section 13.6 of RFC 2616. |
| // |
| void* iter = NULL; |
| std::string name = "vary", request_header; |
| while (response_headers.EnumerateHeader(&iter, name, &request_header)) { |
| if (request_header == "*") |
| return false; |
| AddField(request_info, request_header, &ctx); |
| processed_header = true; |
| } |
| |
| // Add an implicit 'Vary: cookie' header to any redirect to avoid redirect |
| // loops which may result from redirects that are incorrectly marked as |
| // cachable by the server. Unfortunately, other browsers do not cache |
| // redirects that result from requests containing a cookie header. We are |
| // treading on untested waters here, so we want to be extra careful to make |
| // sure we do not end up with a redirect loop served from cache. |
| // |
| // If there is an explicit 'Vary: cookie' header, then we will just end up |
| // digesting the cookie header twice. Not a problem. |
| // |
| std::string location; |
| if (response_headers.IsRedirect(&location)) { |
| AddField(request_info, "cookie", &ctx); |
| processed_header = true; |
| } |
| |
| if (!processed_header) |
| return false; |
| |
| base::MD5Final(&request_digest_, &ctx); |
| return is_valid_ = true; |
| } |
| |
| bool HttpVaryData::InitFromPickle(const Pickle& pickle, PickleIterator* iter) { |
| is_valid_ = false; |
| const char* data; |
| if (pickle.ReadBytes(iter, &data, sizeof(request_digest_))) { |
| memcpy(&request_digest_, data, sizeof(request_digest_)); |
| return is_valid_ = true; |
| } |
| return false; |
| } |
| |
| void HttpVaryData::Persist(Pickle* pickle) const { |
| DCHECK(is_valid()); |
| pickle->WriteBytes(&request_digest_, sizeof(request_digest_)); |
| } |
| |
| bool HttpVaryData::MatchesRequest( |
| const HttpRequestInfo& request_info, |
| const HttpResponseHeaders& cached_response_headers) const { |
| HttpVaryData new_vary_data; |
| if (!new_vary_data.Init(request_info, cached_response_headers)) { |
| // This shouldn't happen provided the same response headers passed here |
| // were also used when initializing |this|. |
| NOTREACHED(); |
| return false; |
| } |
| return memcmp(&new_vary_data.request_digest_, &request_digest_, |
| sizeof(request_digest_)) == 0; |
| } |
| |
| // static |
| std::string HttpVaryData::GetRequestValue( |
| const HttpRequestInfo& request_info, |
| const std::string& request_header) { |
| // Unfortunately, we do not have access to all of the request headers at this |
| // point. Most notably, we do not have access to an Authorization header if |
| // one will be added to the request. |
| |
| std::string result; |
| if (request_info.extra_headers.GetHeader(request_header, &result)) |
| return result; |
| |
| return ""; |
| } |
| |
| // static |
| void HttpVaryData::AddField(const HttpRequestInfo& request_info, |
| const std::string& request_header, |
| base::MD5Context* ctx) { |
| std::string request_value = GetRequestValue(request_info, request_header); |
| |
| // Append a character that cannot appear in the request header line so that we |
| // protect against case where the concatenation of two request headers could |
| // look the same for a variety of values for the individual request headers. |
| // For example, "foo: 12\nbar: 3" looks like "foo: 1\nbar: 23" otherwise. |
| request_value.append(1, '\n'); |
| |
| base::MD5Update(ctx, request_value); |
| } |
| |
| } // namespace net |