• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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/spdy/header_coalescer.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "base/ranges/algorithm.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/trace_event/memory_usage_estimator.h"
16 #include "base/values.h"
17 #include "net/http/http_log_util.h"
18 #include "net/http/http_util.h"
19 #include "net/log/net_log_values.h"
20 
21 namespace net {
22 namespace {
23 
NetLogInvalidHeader(const NetLogWithSource & net_log,base::StringPiece header_name,base::StringPiece header_value,const char * error_message)24 void NetLogInvalidHeader(const NetLogWithSource& net_log,
25                          base::StringPiece header_name,
26                          base::StringPiece header_value,
27                          const char* error_message) {
28   net_log.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_INVALID_HEADER,
29                    [&](NetLogCaptureMode capture_mode) {
30                      base::Value::Dict dict;
31                      dict.Set("header_name", NetLogStringValue(header_name));
32                      dict.Set("header_value",
33                               NetLogStringValue(ElideHeaderValueForNetLog(
34                                   capture_mode, std::string(header_name),
35                                   std::string(header_value))));
36                      dict.Set("error", error_message);
37                      return dict;
38                    });
39 }
40 
ContainsUppercaseAscii(base::StringPiece str)41 bool ContainsUppercaseAscii(base::StringPiece str) {
42   return base::ranges::any_of(str, base::IsAsciiUpper<char>);
43 }
44 
45 }  // namespace
46 
HeaderCoalescer(uint32_t max_header_list_size,const NetLogWithSource & net_log)47 HeaderCoalescer::HeaderCoalescer(uint32_t max_header_list_size,
48                                  const NetLogWithSource& net_log)
49     : max_header_list_size_(max_header_list_size), net_log_(net_log) {}
50 
OnHeader(absl::string_view key,absl::string_view value)51 void HeaderCoalescer::OnHeader(absl::string_view key, absl::string_view value) {
52   if (error_seen_)
53     return;
54   if (!AddHeader(key, value)) {
55     error_seen_ = true;
56   }
57 }
58 
release_headers()59 spdy::Http2HeaderBlock HeaderCoalescer::release_headers() {
60   DCHECK(headers_valid_);
61   headers_valid_ = false;
62   return std::move(headers_);
63 }
64 
AddHeader(base::StringPiece key,base::StringPiece value)65 bool HeaderCoalescer::AddHeader(base::StringPiece key,
66                                 base::StringPiece value) {
67   if (key.empty()) {
68     NetLogInvalidHeader(net_log_, key, value, "Header name must not be empty.");
69     return false;
70   }
71 
72   base::StringPiece key_name = key;
73   if (key[0] == ':') {
74     if (regular_header_seen_) {
75       NetLogInvalidHeader(net_log_, key, value,
76                           "Pseudo header must not follow regular headers.");
77       return false;
78     }
79     key_name.remove_prefix(1);
80   } else if (!regular_header_seen_) {
81     regular_header_seen_ = true;
82   }
83 
84   if (!HttpUtil::IsValidHeaderName(key_name)) {
85     NetLogInvalidHeader(net_log_, key, value,
86                         "Invalid character in header name.");
87     return false;
88   }
89 
90   if (ContainsUppercaseAscii(key_name)) {
91     NetLogInvalidHeader(net_log_, key, value,
92                         "Upper case characters in header name.");
93     return false;
94   }
95 
96   // 32 byte overhead according to RFC 7540 Section 6.5.2.
97   header_list_size_ += key.size() + value.size() + 32;
98   if (header_list_size_ > max_header_list_size_) {
99     NetLogInvalidHeader(net_log_, key, value, "Header list too large.");
100     return false;
101   }
102 
103   // RFC 7540 Section 10.3: "Any request or response that contains a character
104   // not permitted in a header field value MUST be treated as malformed (Section
105   // 8.1.2.6). Valid characters are defined by the field-content ABNF rule in
106   // Section 3.2 of [RFC7230]." RFC 7230 Section 3.2 says:
107   // field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
108   // field-vchar    = VCHAR / obs-text
109   // RFC 5234 Appendix B.1 defines |VCHAR|:
110   // VCHAR          =  %x21-7E
111   // RFC 7230 Section 3.2.6 defines |obs-text|:
112   // obs-text       = %x80-FF
113   // Therefore allowed characters are '\t' (HTAB), x20 (SP), x21-7E, and x80-FF.
114   for (const unsigned char c : value) {
115     if (c < '\t' || ('\t' < c && c < 0x20) || c == 0x7f) {
116       std::string error_line;
117       base::StringAppendF(&error_line,
118                           "Invalid character 0x%02X in header value.", c);
119       NetLogInvalidHeader(net_log_, key, value, error_line.c_str());
120       return false;
121     }
122   }
123 
124   headers_.AppendValueOrAddHeader(key, value);
125   return true;
126 }
127 
128 
129 }  // namespace net
130