• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 <string_view>
10 
11 #include "base/pickle.h"
12 #include "base/strings/string_util.h"
13 #include "net/http/http_request_headers.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_util.h"
17 
18 namespace net {
19 
20 HttpVaryData::HttpVaryData() = default;
21 
Init(const HttpRequestInfo & request_info,const HttpResponseHeaders & response_headers)22 bool HttpVaryData::Init(const HttpRequestInfo& request_info,
23                         const HttpResponseHeaders& response_headers) {
24   base::MD5Context ctx;
25   base::MD5Init(&ctx);
26 
27   is_valid_ = false;
28   bool processed_header = false;
29 
30   // Feed the MD5 context in the order of the Vary header enumeration.  If the
31   // Vary header repeats a header name, then that's OK.
32   //
33   // If the Vary header contains '*' then we can just notice it based on
34   // |cached_response_headers| in MatchesRequest(), and don't have to worry
35   // about the specific headers.  We still want an HttpVaryData around, to let
36   // us handle this case. See section 4.1 of RFC 7234.
37   //
38   size_t iter = 0;
39   constexpr std::string_view name = "vary";
40   std::optional<std::string_view> request_header;
41   while ((request_header = response_headers.EnumerateHeader(&iter, name))) {
42     if (*request_header == "*") {
43       // What's in request_digest_ will never be looked at, but make it
44       // deterministic so we don't serialize out uninitialized memory content.
45       memset(&request_digest_, 0, sizeof(request_digest_));
46       return is_valid_ = true;
47     }
48     AddField(request_info, *request_header, &ctx);
49     processed_header = true;
50   }
51 
52   if (!processed_header)
53     return false;
54 
55   base::MD5Final(&request_digest_, &ctx);
56   return is_valid_ = true;
57 }
58 
InitFromPickle(base::PickleIterator * iter)59 bool HttpVaryData::InitFromPickle(base::PickleIterator* iter) {
60   is_valid_ = false;
61   const char* data;
62   if (iter->ReadBytes(&data, sizeof(request_digest_))) {
63     memcpy(&request_digest_, data, sizeof(request_digest_));
64     return is_valid_ = true;
65   }
66   return false;
67 }
68 
Persist(base::Pickle * pickle) const69 void HttpVaryData::Persist(base::Pickle* pickle) const {
70   DCHECK(is_valid());
71   pickle->WriteBytes(&request_digest_, sizeof(request_digest_));
72 }
73 
MatchesRequest(const HttpRequestInfo & request_info,const HttpResponseHeaders & cached_response_headers) const74 bool HttpVaryData::MatchesRequest(
75     const HttpRequestInfo& request_info,
76     const HttpResponseHeaders& cached_response_headers) const {
77   // Vary: * never matches.
78   if (cached_response_headers.HasHeaderValue("vary", "*"))
79     return false;
80 
81   HttpVaryData new_vary_data;
82   if (!new_vary_data.Init(request_info, cached_response_headers)) {
83     // This case can happen if |this| was loaded from a cache that was populated
84     // by a build before crbug.com/469675 was fixed.
85     return false;
86   }
87   return memcmp(&new_vary_data.request_digest_, &request_digest_,
88                 sizeof(request_digest_)) == 0;
89 }
90 
91 // static
AddField(const HttpRequestInfo & request_info,std::string_view request_header,base::MD5Context * ctx)92 void HttpVaryData::AddField(const HttpRequestInfo& request_info,
93                             std::string_view request_header,
94                             base::MD5Context* ctx) {
95   std::string request_value =
96       request_info.extra_headers.GetHeader(request_header)
97           .value_or(std::string());
98 
99   // Append a character that cannot appear in the request header line so that we
100   // protect against case where the concatenation of two request headers could
101   // look the same for a variety of values for the individual request headers.
102   // For example, "foo: 12\nbar: 3" looks like "foo: 1\nbar: 23" otherwise.
103   request_value.append(1, '\n');
104 
105   base::MD5Update(ctx, request_value);
106 }
107 
108 }  // namespace net
109