xref: /aosp_15_r20/external/cronet/net/http/http_vary_data.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 "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"
15 
16 namespace net {
17 
18 HttpVaryData::HttpVaryData() = default;
19 
Init(const HttpRequestInfo & request_info,const HttpResponseHeaders & response_headers)20 bool HttpVaryData::Init(const HttpRequestInfo& request_info,
21                         const HttpResponseHeaders& response_headers) {
22   base::MD5Context ctx;
23   base::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 can just notice it based on
32   // |cached_response_headers| in MatchesRequest(), and don't have to worry
33   // about the specific headers.  We still want an HttpVaryData around, to let
34   // us handle this case. See section 4.1 of RFC 7234.
35   //
36   size_t iter = 0;
37   std::string name = "vary", request_header;
38   while (response_headers.EnumerateHeader(&iter, name, &request_header)) {
39     if (request_header == "*") {
40       // What's in request_digest_ will never be looked at, but make it
41       // deterministic so we don't serialize out uninitialized memory content.
42       memset(&request_digest_, 0, sizeof(request_digest_));
43       return is_valid_ = true;
44     }
45     AddField(request_info, request_header, &ctx);
46     processed_header = true;
47   }
48 
49   if (!processed_header)
50     return false;
51 
52   base::MD5Final(&request_digest_, &ctx);
53   return is_valid_ = true;
54 }
55 
InitFromPickle(base::PickleIterator * iter)56 bool HttpVaryData::InitFromPickle(base::PickleIterator* iter) {
57   is_valid_ = false;
58   const char* data;
59   if (iter->ReadBytes(&data, sizeof(request_digest_))) {
60     memcpy(&request_digest_, data, sizeof(request_digest_));
61     return is_valid_ = true;
62   }
63   return false;
64 }
65 
Persist(base::Pickle * pickle) const66 void HttpVaryData::Persist(base::Pickle* pickle) const {
67   DCHECK(is_valid());
68   pickle->WriteBytes(&request_digest_, sizeof(request_digest_));
69 }
70 
MatchesRequest(const HttpRequestInfo & request_info,const HttpResponseHeaders & cached_response_headers) const71 bool HttpVaryData::MatchesRequest(
72     const HttpRequestInfo& request_info,
73     const HttpResponseHeaders& cached_response_headers) const {
74   // Vary: * never matches.
75   if (cached_response_headers.HasHeaderValue("vary", "*"))
76     return false;
77 
78   HttpVaryData new_vary_data;
79   if (!new_vary_data.Init(request_info, cached_response_headers)) {
80     // This case can happen if |this| was loaded from a cache that was populated
81     // by a build before crbug.com/469675 was fixed.
82     return false;
83   }
84   return memcmp(&new_vary_data.request_digest_, &request_digest_,
85                 sizeof(request_digest_)) == 0;
86 }
87 
88 // static
GetRequestValue(const HttpRequestInfo & request_info,const std::string & request_header)89 std::string HttpVaryData::GetRequestValue(
90     const HttpRequestInfo& request_info,
91     const std::string& request_header) {
92   // Unfortunately, we do not have access to all of the request headers at this
93   // point.  Most notably, we do not have access to an Authorization header if
94   // one will be added to the request.
95 
96   std::string result;
97   if (request_info.extra_headers.GetHeader(request_header, &result))
98     return result;
99 
100   return std::string();
101 }
102 
103 // static
AddField(const HttpRequestInfo & request_info,const std::string & request_header,base::MD5Context * ctx)104 void HttpVaryData::AddField(const HttpRequestInfo& request_info,
105                             const std::string& request_header,
106                             base::MD5Context* ctx) {
107   std::string request_value = GetRequestValue(request_info, request_header);
108 
109   // Append a character that cannot appear in the request header line so that we
110   // protect against case where the concatenation of two request headers could
111   // look the same for a variety of values for the individual request headers.
112   // For example, "foo: 12\nbar: 3" looks like "foo: 1\nbar: 23" otherwise.
113   request_value.append(1, '\n');
114 
115   base::MD5Update(ctx, request_value);
116 }
117 
118 }  // namespace net
119