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