1 // Copyright 2022 The Chromium Authors. All rights reserved.
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 "quiche/balsa/balsa_headers.h"
6
7 #include <sys/types.h>
8
9 #include <cstdint>
10 #include <functional>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "absl/container/flat_hash_set.h"
16 #include "absl/strings/ascii.h"
17 #include "absl/strings/match.h"
18 #include "absl/strings/str_cat.h"
19 #include "absl/strings/str_format.h"
20 #include "absl/strings/str_join.h"
21 #include "absl/strings/string_view.h"
22 #include "quiche/balsa/balsa_enums.h"
23 #include "quiche/balsa/header_properties.h"
24 #include "quiche/common/platform/api/quiche_header_policy.h"
25 #include "quiche/common/platform/api/quiche_logging.h"
26
27 namespace {
28
29 constexpr absl::string_view kContentLength("Content-Length");
30 constexpr absl::string_view kCookie("Cookie");
31 constexpr absl::string_view kHost("Host");
32 constexpr absl::string_view kTransferEncoding("Transfer-Encoding");
33
34 // The following list defines list of headers that Envoy considers multivalue.
35 // Headers on this list are coalesced by EFG in order to provide forward
36 // compatibility with Envoy behavior. See b/143490671 for details.
37 // Date, Last-Modified and Location are excluded because they're found on Chrome
38 // HttpUtil::IsNonCoalescingHeader() list.
39 #define ALL_ENVOY_HEADERS(HEADER_FUNC) \
40 HEADER_FUNC("Accept") \
41 HEADER_FUNC("Accept-Encoding") \
42 HEADER_FUNC("Access-Control-Request-Headers") \
43 HEADER_FUNC("Access-Control-Request-Method") \
44 HEADER_FUNC("Access-Control-Allow-Origin") \
45 HEADER_FUNC("Access-Control-Allow-Headers") \
46 HEADER_FUNC("Access-Control-Allow-Methods") \
47 HEADER_FUNC("Access-Control-Allow-Credentials") \
48 HEADER_FUNC("Access-Control-Expose-Headers") \
49 HEADER_FUNC("Access-Control-Max-Age") \
50 HEADER_FUNC("Authorization") \
51 HEADER_FUNC("Cache-Control") \
52 HEADER_FUNC("X-Client-Trace-Id") \
53 HEADER_FUNC("Connection") \
54 HEADER_FUNC("Content-Encoding") \
55 HEADER_FUNC("Content-Length") \
56 HEADER_FUNC("Content-Type") \
57 /* HEADER_FUNC("Date") */ \
58 HEADER_FUNC("Envoy-Attempt-Count") \
59 HEADER_FUNC("Envoy-Degraded") \
60 HEADER_FUNC("Envoy-Decorator-Operation") \
61 HEADER_FUNC("Envoy-Downstream-Service-Cluster") \
62 HEADER_FUNC("Envoy-Downstream-Service-Node") \
63 HEADER_FUNC("Envoy-Expected-Request-Timeout-Ms") \
64 HEADER_FUNC("Envoy-External-Address") \
65 HEADER_FUNC("Envoy-Force-Trace") \
66 HEADER_FUNC("Envoy-Hedge-On-Per-Try-Timeout") \
67 HEADER_FUNC("Envoy-Immediate-Health-Check-Fail") \
68 HEADER_FUNC("Envoy-Internal-Request") \
69 HEADER_FUNC("Envoy-Ip-Tags") \
70 HEADER_FUNC("Envoy-Max-Retries") \
71 HEADER_FUNC("Envoy-Original-Path") \
72 HEADER_FUNC("Envoy-Original-Url") \
73 HEADER_FUNC("Envoy-Overloaded") \
74 HEADER_FUNC("Envoy-Rate-Limited") \
75 HEADER_FUNC("Envoy-Retry-On") \
76 HEADER_FUNC("Envoy-Retry-Grpc-On") \
77 HEADER_FUNC("Envoy-Retriable-StatusCodes") \
78 HEADER_FUNC("Envoy-Retriable-HeaderNames") \
79 HEADER_FUNC("Envoy-Upstream-AltStatName") \
80 HEADER_FUNC("Envoy-Upstream-Canary") \
81 HEADER_FUNC("Envoy-Upstream-HealthCheckedCluster") \
82 HEADER_FUNC("Envoy-Upstream-RequestPerTryTimeoutMs") \
83 HEADER_FUNC("Envoy-Upstream-RequestTimeoutAltResponse") \
84 HEADER_FUNC("Envoy-Upstream-RequestTimeoutMs") \
85 HEADER_FUNC("Envoy-Upstream-ServiceTime") \
86 HEADER_FUNC("Etag") \
87 HEADER_FUNC("Expect") \
88 HEADER_FUNC("X-Forwarded-Client-Cert") \
89 HEADER_FUNC("X-Forwarded-For") \
90 HEADER_FUNC("X-Forwarded-Proto") \
91 HEADER_FUNC("Grpc-Accept-Encoding") \
92 HEADER_FUNC("Grpc-Message") \
93 HEADER_FUNC("Grpc-Status") \
94 HEADER_FUNC("Grpc-Timeout") \
95 HEADER_FUNC("Host") \
96 HEADER_FUNC("Keep-Alive") \
97 /* HEADER_FUNC("Last-Modified") */ \
98 /* HEADER_FUNC("Location") */ \
99 HEADER_FUNC("Method") \
100 HEADER_FUNC("No-Chunks") \
101 HEADER_FUNC("Origin") \
102 HEADER_FUNC("X-Ot-Span-Context") \
103 HEADER_FUNC("Path") \
104 HEADER_FUNC("Protocol") \
105 HEADER_FUNC("Proxy-Connection") \
106 HEADER_FUNC("Referer") \
107 HEADER_FUNC("X-Request-Id") \
108 HEADER_FUNC("Scheme") \
109 HEADER_FUNC("Server") \
110 HEADER_FUNC("Status") \
111 HEADER_FUNC("TE") \
112 HEADER_FUNC("Transfer-Encoding") \
113 HEADER_FUNC("Upgrade") \
114 HEADER_FUNC("User-Agent") \
115 HEADER_FUNC("Vary") \
116 HEADER_FUNC("Via")
117
118 // HEADER_FUNC to insert "name" into the MultivaluedHeadersSet of Envoy headers.
119 #define MULTIVALUE_ENVOY_HEADER(name) {name},
120
FindIgnoreCase(absl::string_view haystack,absl::string_view needle)121 absl::string_view::difference_type FindIgnoreCase(absl::string_view haystack,
122 absl::string_view needle) {
123 absl::string_view::difference_type pos = 0;
124 while (haystack.size() >= needle.size()) {
125 if (absl::StartsWithIgnoreCase(haystack, needle)) {
126 return pos;
127 }
128 ++pos;
129 haystack.remove_prefix(1);
130 }
131
132 return absl::string_view::npos;
133 }
134
RemoveLeadingWhitespace(absl::string_view * text)135 absl::string_view::difference_type RemoveLeadingWhitespace(
136 absl::string_view* text) {
137 size_t count = 0;
138 const char* ptr = text->data();
139 while (count < text->size() && absl::ascii_isspace(*ptr)) {
140 count++;
141 ptr++;
142 }
143 text->remove_prefix(count);
144 return count;
145 }
146
RemoveTrailingWhitespace(absl::string_view * text)147 absl::string_view::difference_type RemoveTrailingWhitespace(
148 absl::string_view* text) {
149 size_t count = 0;
150 const char* ptr = text->data() + text->size() - 1;
151 while (count < text->size() && absl::ascii_isspace(*ptr)) {
152 ++count;
153 --ptr;
154 }
155 text->remove_suffix(count);
156 return count;
157 }
158
RemoveWhitespaceContext(absl::string_view * text)159 absl::string_view::difference_type RemoveWhitespaceContext(
160 absl::string_view* text) {
161 return RemoveLeadingWhitespace(text) + RemoveTrailingWhitespace(text);
162 }
163
164 } // namespace
165
166 namespace quiche {
167
168 const size_t BalsaBuffer::kDefaultBlocksize;
169
170 const BalsaHeaders::MultivaluedHeadersSet&
multivalued_envoy_headers()171 BalsaHeaders::multivalued_envoy_headers() {
172 static const MultivaluedHeadersSet* multivalued_envoy_headers =
173 new MultivaluedHeadersSet({ALL_ENVOY_HEADERS(MULTIVALUE_ENVOY_HEADER)});
174 return *multivalued_envoy_headers;
175 }
176
ParseTokenList(absl::string_view header_value,HeaderTokenList * tokens)177 void BalsaHeaders::ParseTokenList(absl::string_view header_value,
178 HeaderTokenList* tokens) {
179 if (header_value.empty()) {
180 return;
181 }
182 const char* start = header_value.data();
183 const char* end = header_value.data() + header_value.size();
184 while (true) {
185 // Cast `*start` to unsigned char to make values above 127 rank as expected
186 // on platforms with signed char, where such values are represented as
187 // negative numbers before the cast.
188
189 // search for first nonwhitespace, non separator char.
190 while (*start == ',' || static_cast<unsigned char>(*start) <= ' ') {
191 ++start;
192 if (start == end) {
193 return;
194 }
195 }
196 // found. marked.
197 const char* nws = start;
198
199 // search for next whitspace or separator char.
200 while (*start != ',' && static_cast<unsigned char>(*start) > ' ') {
201 ++start;
202 if (start == end) {
203 if (nws != start) {
204 tokens->push_back(absl::string_view(nws, start - nws));
205 }
206 return;
207 }
208 }
209 tokens->push_back(absl::string_view(nws, start - nws));
210 }
211 }
212
213 // This can be called after a std::move() operation, so things might be
214 // in an unspecified state after the move.
Clear()215 void BalsaHeaders::Clear() {
216 balsa_buffer_.Clear();
217 transfer_encoding_is_chunked_ = false;
218 content_length_ = 0;
219 content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH;
220 parsed_response_code_ = 0;
221 firstline_buffer_base_idx_ = 0;
222 whitespace_1_idx_ = 0;
223 non_whitespace_1_idx_ = 0;
224 whitespace_2_idx_ = 0;
225 non_whitespace_2_idx_ = 0;
226 whitespace_3_idx_ = 0;
227 non_whitespace_3_idx_ = 0;
228 whitespace_4_idx_ = 0;
229 header_lines_.clear();
230 header_lines_.shrink_to_fit();
231 }
232
CopyFrom(const BalsaHeaders & other)233 void BalsaHeaders::CopyFrom(const BalsaHeaders& other) {
234 // Protect against copying with self.
235 if (this == &other) {
236 return;
237 }
238
239 balsa_buffer_.CopyFrom(other.balsa_buffer_);
240 transfer_encoding_is_chunked_ = other.transfer_encoding_is_chunked_;
241 content_length_ = other.content_length_;
242 content_length_status_ = other.content_length_status_;
243 parsed_response_code_ = other.parsed_response_code_;
244 firstline_buffer_base_idx_ = other.firstline_buffer_base_idx_;
245 whitespace_1_idx_ = other.whitespace_1_idx_;
246 non_whitespace_1_idx_ = other.non_whitespace_1_idx_;
247 whitespace_2_idx_ = other.whitespace_2_idx_;
248 non_whitespace_2_idx_ = other.non_whitespace_2_idx_;
249 whitespace_3_idx_ = other.whitespace_3_idx_;
250 non_whitespace_3_idx_ = other.non_whitespace_3_idx_;
251 whitespace_4_idx_ = other.whitespace_4_idx_;
252 header_lines_ = other.header_lines_;
253 }
254
AddAndMakeDescription(absl::string_view key,absl::string_view value,HeaderLineDescription * d)255 void BalsaHeaders::AddAndMakeDescription(absl::string_view key,
256 absl::string_view value,
257 HeaderLineDescription* d) {
258 QUICHE_CHECK(d != nullptr);
259
260 if (enforce_header_policy_) {
261 QuicheHandleHeaderPolicy(key);
262 }
263
264 // + 2 to size for ": "
265 size_t line_size = key.size() + 2 + value.size();
266 BalsaBuffer::Blocks::size_type block_buffer_idx = 0;
267 char* storage = balsa_buffer_.Reserve(line_size, &block_buffer_idx);
268 size_t base_idx = storage - GetPtr(block_buffer_idx);
269
270 char* cur_loc = storage;
271 memcpy(cur_loc, key.data(), key.size());
272 cur_loc += key.size();
273 *cur_loc = ':';
274 ++cur_loc;
275 *cur_loc = ' ';
276 ++cur_loc;
277 memcpy(cur_loc, value.data(), value.size());
278 *d = HeaderLineDescription(
279 base_idx, base_idx + key.size(), base_idx + key.size() + 2,
280 base_idx + key.size() + 2 + value.size(), block_buffer_idx);
281 }
282
AppendAndMakeDescription(absl::string_view key,absl::string_view value,HeaderLineDescription * d)283 void BalsaHeaders::AppendAndMakeDescription(absl::string_view key,
284 absl::string_view value,
285 HeaderLineDescription* d) {
286 // Figure out how much space we need to reserve for the new header size.
287 size_t old_value_size = d->last_char_idx - d->value_begin_idx;
288 if (old_value_size == 0) {
289 AddAndMakeDescription(key, value, d);
290 return;
291 }
292 absl::string_view old_value(GetPtr(d->buffer_base_idx) + d->value_begin_idx,
293 old_value_size);
294
295 BalsaBuffer::Blocks::size_type block_buffer_idx = 0;
296 // + 3 because we potentially need to add ": ", and "," to the line.
297 size_t new_size = key.size() + 3 + old_value_size + value.size();
298 char* storage = balsa_buffer_.Reserve(new_size, &block_buffer_idx);
299 size_t base_idx = storage - GetPtr(block_buffer_idx);
300
301 absl::string_view first_value = old_value;
302 absl::string_view second_value = value;
303 char* cur_loc = storage;
304 memcpy(cur_loc, key.data(), key.size());
305 cur_loc += key.size();
306 *cur_loc = ':';
307 ++cur_loc;
308 *cur_loc = ' ';
309 ++cur_loc;
310 memcpy(cur_loc, first_value.data(), first_value.size());
311 cur_loc += first_value.size();
312 *cur_loc = ',';
313 ++cur_loc;
314 memcpy(cur_loc, second_value.data(), second_value.size());
315
316 *d = HeaderLineDescription(base_idx, base_idx + key.size(),
317 base_idx + key.size() + 2, base_idx + new_size,
318 block_buffer_idx);
319 }
320
321 // Reset internal flags for chunked transfer encoding or content length if a
322 // header we're removing is one of those headers.
MaybeClearSpecialHeaderValues(absl::string_view key)323 void BalsaHeaders::MaybeClearSpecialHeaderValues(absl::string_view key) {
324 if (absl::EqualsIgnoreCase(key, kContentLength)) {
325 if (transfer_encoding_is_chunked_) {
326 return;
327 }
328
329 content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH;
330 content_length_ = 0;
331 return;
332 }
333
334 if (absl::EqualsIgnoreCase(key, kTransferEncoding)) {
335 transfer_encoding_is_chunked_ = false;
336 }
337 }
338
339 // Removes all keys value pairs with key 'key' starting at 'start'.
RemoveAllOfHeaderStartingAt(absl::string_view key,HeaderLines::iterator start)340 void BalsaHeaders::RemoveAllOfHeaderStartingAt(absl::string_view key,
341 HeaderLines::iterator start) {
342 MaybeClearSpecialHeaderValues(key);
343 while (start != header_lines_.end()) {
344 start->skip = true;
345 ++start;
346 start = GetHeaderLinesIterator(key, start);
347 }
348 }
349
ReplaceOrAppendHeader(absl::string_view key,absl::string_view value)350 void BalsaHeaders::ReplaceOrAppendHeader(absl::string_view key,
351 absl::string_view value) {
352 const HeaderLines::iterator end = header_lines_.end();
353 const HeaderLines::iterator begin = header_lines_.begin();
354 HeaderLines::iterator i = GetHeaderLinesIterator(key, begin);
355 if (i != end) {
356 // First, remove all of the header lines including this one. We want to
357 // remove before replacing, in case our replacement ends up being appended
358 // at the end (and thus would be removed by this call)
359 RemoveAllOfHeaderStartingAt(key, i);
360 // Now, take the first instance and replace it. This will remove the
361 // 'skipped' tag if the replacement is done in-place.
362 AddAndMakeDescription(key, value, &(*i));
363 return;
364 }
365 AppendHeader(key, value);
366 }
367
AppendHeader(absl::string_view key,absl::string_view value)368 void BalsaHeaders::AppendHeader(absl::string_view key,
369 absl::string_view value) {
370 HeaderLineDescription hld;
371 AddAndMakeDescription(key, value, &hld);
372 header_lines_.push_back(hld);
373 }
374
AppendToHeader(absl::string_view key,absl::string_view value)375 void BalsaHeaders::AppendToHeader(absl::string_view key,
376 absl::string_view value) {
377 HeaderLines::iterator i = GetHeaderLinesIterator(key, header_lines_.begin());
378 if (i == header_lines_.end()) {
379 // The header did not exist already. Instead of appending to an existing
380 // header simply append the key/value pair to the headers.
381 AppendHeader(key, value);
382 return;
383 }
384 HeaderLineDescription hld = *i;
385
386 AppendAndMakeDescription(key, value, &hld);
387
388 // Invalidate the old header line and add the new one.
389 i->skip = true;
390 header_lines_.push_back(hld);
391 }
392
AppendToHeaderWithCommaAndSpace(absl::string_view key,absl::string_view value)393 void BalsaHeaders::AppendToHeaderWithCommaAndSpace(absl::string_view key,
394 absl::string_view value) {
395 HeaderLines::iterator i = GetHeaderLinesIteratorForLastMultivaluedHeader(key);
396 if (i == header_lines_.end()) {
397 // The header did not exist already. Instead of appending to an existing
398 // header simply append the key/value pair to the headers. No extra
399 // space will be added before the value.
400 AppendHeader(key, value);
401 return;
402 }
403
404 std::string space_and_value = absl::StrCat(" ", value);
405
406 HeaderLineDescription hld = *i;
407 AppendAndMakeDescription(key, space_and_value, &hld);
408
409 // Invalidate the old header line and add the new one.
410 i->skip = true;
411 header_lines_.push_back(hld);
412 }
413
GetValueFromHeaderLineDescription(const HeaderLineDescription & line) const414 absl::string_view BalsaHeaders::GetValueFromHeaderLineDescription(
415 const HeaderLineDescription& line) const {
416 QUICHE_DCHECK_GE(line.last_char_idx, line.value_begin_idx);
417 return absl::string_view(GetPtr(line.buffer_base_idx) + line.value_begin_idx,
418 line.last_char_idx - line.value_begin_idx);
419 }
420
GetHeader(absl::string_view key) const421 absl::string_view BalsaHeaders::GetHeader(absl::string_view key) const {
422 QUICHE_DCHECK(!header_properties::IsMultivaluedHeader(key))
423 << "Header '" << key << "' may consist of multiple lines. Do not "
424 << "use BalsaHeaders::GetHeader() or you may be missing some of its "
425 << "values.";
426 const HeaderLines::const_iterator end = header_lines_.end();
427 HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key);
428 if (i == end) {
429 return absl::string_view();
430 }
431 return GetValueFromHeaderLineDescription(*i);
432 }
433
GetHeaderPosition(absl::string_view key) const434 BalsaHeaders::const_header_lines_iterator BalsaHeaders::GetHeaderPosition(
435 absl::string_view key) const {
436 const HeaderLines::const_iterator end = header_lines_.end();
437 HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key);
438 if (i == end) {
439 // TODO(tgreer) Convert from HeaderLines::const_iterator to
440 // const_header_lines_iterator without calling lines().end(), which is
441 // nontrivial. Look for other needless calls to lines().end(), or make
442 // lines().end() trivial.
443 return lines().end();
444 }
445
446 return const_header_lines_iterator(this, (i - header_lines_.begin()));
447 }
448
GetIteratorForKey(absl::string_view key) const449 BalsaHeaders::const_header_lines_key_iterator BalsaHeaders::GetIteratorForKey(
450 absl::string_view key) const {
451 HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key);
452 if (i == header_lines_.end()) {
453 return header_lines_key_end();
454 }
455
456 return const_header_lines_key_iterator(this, (i - header_lines_.begin()),
457 key);
458 }
459
460 BalsaHeaders::HeaderLines::const_iterator
GetConstHeaderLinesIterator(absl::string_view key) const461 BalsaHeaders::GetConstHeaderLinesIterator(absl::string_view key) const {
462 const HeaderLines::const_iterator end = header_lines_.end();
463 for (HeaderLines::const_iterator i = header_lines_.begin(); i != end; ++i) {
464 const HeaderLineDescription& line = *i;
465 if (line.skip) {
466 continue;
467 }
468 const absl::string_view current_key(
469 GetPtr(line.buffer_base_idx) + line.first_char_idx,
470 line.key_end_idx - line.first_char_idx);
471 if (absl::EqualsIgnoreCase(current_key, key)) {
472 QUICHE_DCHECK_GE(line.last_char_idx, line.value_begin_idx);
473 return i;
474 }
475 }
476 return end;
477 }
478
GetHeaderLinesIterator(absl::string_view key,BalsaHeaders::HeaderLines::iterator start)479 BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIterator(
480 absl::string_view key, BalsaHeaders::HeaderLines::iterator start) {
481 const HeaderLines::iterator end = header_lines_.end();
482 for (HeaderLines::iterator i = start; i != end; ++i) {
483 const HeaderLineDescription& line = *i;
484 if (line.skip) {
485 continue;
486 }
487 const absl::string_view current_key(
488 GetPtr(line.buffer_base_idx) + line.first_char_idx,
489 line.key_end_idx - line.first_char_idx);
490 if (absl::EqualsIgnoreCase(current_key, key)) {
491 QUICHE_DCHECK_GE(line.last_char_idx, line.value_begin_idx);
492 return i;
493 }
494 }
495 return end;
496 }
497
498 BalsaHeaders::HeaderLines::iterator
GetHeaderLinesIteratorForLastMultivaluedHeader(absl::string_view key)499 BalsaHeaders::GetHeaderLinesIteratorForLastMultivaluedHeader(
500 absl::string_view key) {
501 const HeaderLines::iterator end = header_lines_.end();
502 HeaderLines::iterator last_found_match;
503 bool found_a_match = false;
504 for (HeaderLines::iterator i = header_lines_.begin(); i != end; ++i) {
505 const HeaderLineDescription& line = *i;
506 if (line.skip) {
507 continue;
508 }
509 const absl::string_view current_key(
510 GetPtr(line.buffer_base_idx) + line.first_char_idx,
511 line.key_end_idx - line.first_char_idx);
512 if (absl::EqualsIgnoreCase(current_key, key)) {
513 QUICHE_DCHECK_GE(line.last_char_idx, line.value_begin_idx);
514 last_found_match = i;
515 found_a_match = true;
516 }
517 }
518 return (found_a_match ? last_found_match : end);
519 }
520
GetAllOfHeader(absl::string_view key,std::vector<absl::string_view> * out) const521 void BalsaHeaders::GetAllOfHeader(absl::string_view key,
522 std::vector<absl::string_view>* out) const {
523 for (const_header_lines_key_iterator it = GetIteratorForKey(key);
524 it != lines().end(); ++it) {
525 out->push_back(it->second);
526 }
527 }
528
GetAllOfHeaderIncludeRemoved(absl::string_view key,std::vector<absl::string_view> * out) const529 void BalsaHeaders::GetAllOfHeaderIncludeRemoved(
530 absl::string_view key, std::vector<absl::string_view>* out) const {
531 const HeaderLines::const_iterator begin = header_lines_.begin();
532 const HeaderLines::const_iterator end = header_lines_.end();
533 for (bool add_removed : {false, true}) {
534 for (HeaderLines::const_iterator i = begin; i != end; ++i) {
535 const HeaderLineDescription& line = *i;
536 if ((!add_removed && line.skip) || (add_removed && !line.skip)) {
537 continue;
538 }
539 const absl::string_view current_key(
540 GetPtr(line.buffer_base_idx) + line.first_char_idx,
541 line.key_end_idx - line.first_char_idx);
542 if (absl::EqualsIgnoreCase(current_key, key)) {
543 QUICHE_DCHECK_GE(line.last_char_idx, line.value_begin_idx);
544 out->push_back(GetValueFromHeaderLineDescription(line));
545 }
546 }
547 }
548 }
549
550 namespace {
551
552 // Helper function for HeaderHasValue that checks that the specified region
553 // within line is preceded by whitespace and a comma or beginning of line,
554 // and followed by whitespace and a comma or end of line.
SurroundedOnlyBySpacesAndCommas(absl::string_view::difference_type idx,absl::string_view::difference_type end_idx,absl::string_view line)555 bool SurroundedOnlyBySpacesAndCommas(absl::string_view::difference_type idx,
556 absl::string_view::difference_type end_idx,
557 absl::string_view line) {
558 for (idx = idx - 1; idx >= 0; --idx) {
559 if (line[idx] == ',') {
560 break;
561 }
562 if (line[idx] != ' ') {
563 return false;
564 }
565 }
566
567 for (; end_idx < static_cast<int64_t>(line.size()); ++end_idx) {
568 if (line[end_idx] == ',') {
569 break;
570 }
571 if (line[end_idx] != ' ') {
572 return false;
573 }
574 }
575 return true;
576 }
577
578 } // namespace
579
HeaderHasValueHelper(absl::string_view key,absl::string_view value,bool case_sensitive) const580 bool BalsaHeaders::HeaderHasValueHelper(absl::string_view key,
581 absl::string_view value,
582 bool case_sensitive) const {
583 for (const_header_lines_key_iterator it = GetIteratorForKey(key);
584 it != lines().end(); ++it) {
585 absl::string_view line = it->second;
586 absl::string_view::size_type idx =
587 case_sensitive ? line.find(value, 0) : FindIgnoreCase(line, value);
588 while (idx != absl::string_view::npos) {
589 absl::string_view::difference_type end_idx = idx + value.size();
590 if (SurroundedOnlyBySpacesAndCommas(idx, end_idx, line)) {
591 return true;
592 }
593 idx = line.find(value, idx + 1);
594 }
595 }
596 return false;
597 }
598
HasNonEmptyHeader(absl::string_view key) const599 bool BalsaHeaders::HasNonEmptyHeader(absl::string_view key) const {
600 for (const_header_lines_key_iterator it = GetIteratorForKey(key);
601 it != header_lines_key_end(); ++it) {
602 if (!it->second.empty()) {
603 return true;
604 }
605 }
606 return false;
607 }
608
GetAllOfHeaderAsString(absl::string_view key) const609 std::string BalsaHeaders::GetAllOfHeaderAsString(absl::string_view key) const {
610 // Use custom formatter to ignore header key and join only header values.
611 // absl::AlphaNumFormatter is the default formatter for absl::StrJoin().
612 auto formatter = [](std::string* out,
613 std::pair<absl::string_view, absl::string_view> header) {
614 return absl::AlphaNumFormatter()(out, header.second);
615 };
616 return absl::StrJoin(GetIteratorForKey(key), header_lines_key_end(), ",",
617 formatter);
618 }
619
RemoveAllOfHeaderInList(const HeaderTokenList & keys)620 void BalsaHeaders::RemoveAllOfHeaderInList(const HeaderTokenList& keys) {
621 if (keys.empty()) {
622 return;
623 }
624
625 // This extra copy sacrifices some performance to prevent the possible
626 // mistakes that the caller does not lower case the headers in keys.
627 // Better performance can be achieved by asking caller to lower case
628 // the keys and RemoveAllOfheaderInlist just does lookup.
629 absl::flat_hash_set<std::string> lowercase_keys;
630 for (const auto& key : keys) {
631 MaybeClearSpecialHeaderValues(key);
632 lowercase_keys.insert(absl::AsciiStrToLower(key));
633 }
634
635 for (HeaderLineDescription& line : header_lines_) {
636 if (line.skip) {
637 continue;
638 }
639 // Remove the header if it matches any of the keys to remove.
640 const size_t key_len = line.key_end_idx - line.first_char_idx;
641 absl::string_view key(GetPtr(line.buffer_base_idx) + line.first_char_idx,
642 key_len);
643
644 std::string lowercase_key = absl::AsciiStrToLower(key);
645 if (lowercase_keys.count(lowercase_key) != 0) {
646 line.skip = true;
647 }
648 }
649 }
650
RemoveAllOfHeader(absl::string_view key)651 void BalsaHeaders::RemoveAllOfHeader(absl::string_view key) {
652 HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin());
653 RemoveAllOfHeaderStartingAt(key, it);
654 }
655
RemoveAllHeadersWithPrefix(absl::string_view prefix)656 void BalsaHeaders::RemoveAllHeadersWithPrefix(absl::string_view prefix) {
657 for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) {
658 if (header_lines_[i].skip) {
659 continue;
660 }
661
662 HeaderLineDescription& line = header_lines_[i];
663 const size_t key_len = line.key_end_idx - line.first_char_idx;
664 if (key_len < prefix.size()) {
665 continue;
666 }
667
668 const absl::string_view current_key_prefix(
669 GetPtr(line.buffer_base_idx) + line.first_char_idx, prefix.size());
670 if (absl::EqualsIgnoreCase(current_key_prefix, prefix)) {
671 const absl::string_view current_key(
672 GetPtr(line.buffer_base_idx) + line.first_char_idx, key_len);
673 MaybeClearSpecialHeaderValues(current_key);
674 line.skip = true;
675 }
676 }
677 }
678
HasHeadersWithPrefix(absl::string_view prefix) const679 bool BalsaHeaders::HasHeadersWithPrefix(absl::string_view prefix) const {
680 for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) {
681 if (header_lines_[i].skip) {
682 continue;
683 }
684
685 const HeaderLineDescription& line = header_lines_[i];
686 if (line.key_end_idx - line.first_char_idx < prefix.size()) {
687 continue;
688 }
689
690 const absl::string_view current_key_prefix(
691 GetPtr(line.buffer_base_idx) + line.first_char_idx, prefix.size());
692 if (absl::EqualsIgnoreCase(current_key_prefix, prefix)) {
693 return true;
694 }
695 }
696 return false;
697 }
698
GetAllOfHeaderWithPrefix(absl::string_view prefix,std::vector<std::pair<absl::string_view,absl::string_view>> * out) const699 void BalsaHeaders::GetAllOfHeaderWithPrefix(
700 absl::string_view prefix,
701 std::vector<std::pair<absl::string_view, absl::string_view>>* out) const {
702 for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) {
703 if (header_lines_[i].skip) {
704 continue;
705 }
706 const HeaderLineDescription& line = header_lines_[i];
707 absl::string_view key(GetPtr(line.buffer_base_idx) + line.first_char_idx,
708 line.key_end_idx - line.first_char_idx);
709 if (absl::StartsWithIgnoreCase(key, prefix)) {
710 out->push_back(std::make_pair(
711 key,
712 absl::string_view(GetPtr(line.buffer_base_idx) + line.value_begin_idx,
713 line.last_char_idx - line.value_begin_idx)));
714 }
715 }
716 }
717
GetAllHeadersWithLimit(std::vector<std::pair<absl::string_view,absl::string_view>> * out,int limit) const718 void BalsaHeaders::GetAllHeadersWithLimit(
719 std::vector<std::pair<absl::string_view, absl::string_view>>* out,
720 int limit) const {
721 for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) {
722 if (limit >= 0 && out->size() >= static_cast<size_t>(limit)) {
723 return;
724 }
725 if (header_lines_[i].skip) {
726 continue;
727 }
728 const HeaderLineDescription& line = header_lines_[i];
729 absl::string_view key(GetPtr(line.buffer_base_idx) + line.first_char_idx,
730 line.key_end_idx - line.first_char_idx);
731 out->push_back(std::make_pair(
732 key,
733 absl::string_view(GetPtr(line.buffer_base_idx) + line.value_begin_idx,
734 line.last_char_idx - line.value_begin_idx)));
735 }
736 }
737
RemoveValue(absl::string_view key,absl::string_view search_value)738 size_t BalsaHeaders::RemoveValue(absl::string_view key,
739 absl::string_view search_value) {
740 // Remove whitespace around search value.
741 absl::string_view needle = search_value;
742 RemoveWhitespaceContext(&needle);
743 QUICHE_BUG_IF(bug_22783_2, needle != search_value)
744 << "Search value should not be surrounded by spaces.";
745
746 // We have nothing to do for empty needle strings.
747 if (needle.empty()) {
748 return 0;
749 }
750
751 // The return value: number of removed values.
752 size_t removals = 0;
753
754 // Iterate over all header lines matching key with skip=false.
755 for (HeaderLines::iterator it =
756 GetHeaderLinesIterator(key, header_lines_.begin());
757 it != header_lines_.end(); it = GetHeaderLinesIterator(key, ++it)) {
758 HeaderLineDescription* line = &(*it);
759
760 // If needle given to us is longer than this header, don't consider it.
761 if (line->ValuesLength() < needle.size()) {
762 continue;
763 }
764
765 // If the values are equivalent, just remove the whole line.
766 char* buf = GetPtr(line->buffer_base_idx); // The head of our buffer.
767 char* value_begin = buf + line->value_begin_idx;
768 // StringPiece containing values that have yet to be processed. The head of
769 // this stringpiece will continually move forward, and its tail
770 // (head+length) will always remain the same.
771 absl::string_view values(value_begin, line->ValuesLength());
772 RemoveWhitespaceContext(&values);
773 if (values.size() == needle.size()) {
774 if (values == needle) {
775 line->skip = true;
776 removals++;
777 }
778 continue;
779 }
780
781 // Find all occurrences of the needle to be removed.
782 char* insertion = value_begin;
783 while (values.size() >= needle.size()) {
784 // Strip leading whitespace.
785 ssize_t cur_leading_whitespace = RemoveLeadingWhitespace(&values);
786
787 // See if we've got a match (at least as a prefix).
788 bool found = absl::StartsWith(values, needle);
789
790 // Find the entirety of this value (including trailing comma if existent).
791 const size_t next_comma =
792 values.find(',', /* pos = */ (found ? needle.size() : 0));
793 const bool comma_found = next_comma != absl::string_view::npos;
794 const size_t cur_size = (comma_found ? next_comma + 1 : values.size());
795
796 // Make sure that our prefix match is a full match.
797 if (found && cur_size != needle.size()) {
798 absl::string_view cur(values.data(), cur_size);
799 if (comma_found) {
800 cur.remove_suffix(1);
801 }
802 RemoveTrailingWhitespace(&cur);
803 found = (cur.size() == needle.size());
804 }
805
806 // Move as necessary (avoid move just for the sake of leading whitespace).
807 if (found) {
808 removals++;
809 // Remove trailing comma if we happen to have found the last value.
810 if (!comma_found) {
811 // We modify insertion since it'll be used to update last_char_idx.
812 insertion--;
813 }
814 } else {
815 if (insertion + cur_leading_whitespace != values.data()) {
816 // Has the side-effect of also copying any trailing whitespace.
817 memmove(insertion, values.data(), cur_size);
818 insertion += cur_size;
819 } else {
820 insertion += cur_leading_whitespace + cur_size;
821 }
822 }
823
824 // No longer consider the current value. (Increment.)
825 values.remove_prefix(cur_size);
826 }
827 // Move remaining data.
828 if (!values.empty()) {
829 if (insertion != values.data()) {
830 memmove(insertion, values.data(), values.size());
831 }
832 insertion += values.size();
833 }
834 // Set new line size.
835 if (insertion <= value_begin) {
836 // All values removed.
837 line->skip = true;
838 } else {
839 line->last_char_idx = insertion - buf;
840 }
841 }
842
843 return removals;
844 }
845
GetSizeForWriteBuffer() const846 size_t BalsaHeaders::GetSizeForWriteBuffer() const {
847 // First add the space required for the first line + line separator.
848 size_t write_buf_size = whitespace_4_idx_ - non_whitespace_1_idx_ + 2;
849 // Then add the space needed for each header line to write out + line
850 // separator.
851 const HeaderLines::size_type end = header_lines_.size();
852 for (HeaderLines::size_type i = 0; i < end; ++i) {
853 const HeaderLineDescription& line = header_lines_[i];
854 if (!line.skip) {
855 // Add the key size and ": ".
856 write_buf_size += line.key_end_idx - line.first_char_idx + 2;
857 // Add the value size and the line separator.
858 write_buf_size += line.last_char_idx - line.value_begin_idx + 2;
859 }
860 }
861 // Finally tack on the terminal line separator.
862 return write_buf_size + 2;
863 }
864
DumpToString(std::string * str) const865 void BalsaHeaders::DumpToString(std::string* str) const {
866 DumpToPrefixedString(" ", str);
867 }
868
DebugString() const869 std::string BalsaHeaders::DebugString() const {
870 std::string s;
871 DumpToString(&s);
872 return s;
873 }
874
ForEachHeader(quiche::UnretainedCallback<bool (const absl::string_view key,const absl::string_view value)> fn) const875 bool BalsaHeaders::ForEachHeader(
876 quiche::UnretainedCallback<bool(const absl::string_view key,
877 const absl::string_view value)>
878 fn) const {
879 int s = header_lines_.size();
880 for (int i = 0; i < s; ++i) {
881 const HeaderLineDescription& desc = header_lines_[i];
882 if (!desc.skip && desc.KeyLength() > 0) {
883 const char* stream_begin = GetPtr(desc.buffer_base_idx);
884 if (!fn(absl::string_view(stream_begin + desc.first_char_idx,
885 desc.KeyLength()),
886 absl::string_view(stream_begin + desc.value_begin_idx,
887 desc.ValuesLength()))) {
888 return false;
889 }
890 }
891 }
892 return true;
893 }
894
DumpToPrefixedString(const char * spaces,std::string * str) const895 void BalsaHeaders::DumpToPrefixedString(const char* spaces,
896 std::string* str) const {
897 const absl::string_view firstline = first_line();
898 const int buffer_length = GetReadableBytesFromHeaderStream();
899 // First check whether the header object is empty.
900 if (firstline.empty() && buffer_length == 0) {
901 absl::StrAppend(str, "\n", spaces, "<empty header>\n");
902 return;
903 }
904
905 // Then check whether the header is in a partially parsed state. If so, just
906 // dump the raw data.
907 if (!FramerIsDoneWriting()) {
908 absl::StrAppendFormat(str, "\n%s<incomplete header len: %d>\n%s%.*s\n",
909 spaces, buffer_length, spaces, buffer_length,
910 OriginalHeaderStreamBegin());
911 return;
912 }
913
914 // If the header is complete, then just dump them with the logical key value
915 // pair.
916 str->reserve(str->size() + GetSizeForWriteBuffer());
917 absl::StrAppend(str, "\n", spaces, firstline, "\n");
918 for (const auto& line : lines()) {
919 absl::StrAppend(str, spaces, line.first, ": ", line.second, "\n");
920 }
921 }
922
SetContentLength(size_t length)923 void BalsaHeaders::SetContentLength(size_t length) {
924 // If the content-length is already the one we want, don't do anything.
925 if (content_length_status_ == BalsaHeadersEnums::VALID_CONTENT_LENGTH &&
926 content_length_ == length) {
927 return;
928 }
929 // If header state indicates that there is either a content length or
930 // transfer encoding header, remove them before adding the new content
931 // length. There is always the possibility that client can manually add
932 // either header directly and cause content_length_status_ or
933 // transfer_encoding_is_chunked_ to be inconsistent with the actual header.
934 // In the interest of efficiency, however, we will assume that clients will
935 // use the header object correctly and thus we will not scan the all headers
936 // each time this function is called.
937 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) {
938 RemoveAllOfHeader(kContentLength);
939 } else if (transfer_encoding_is_chunked_) {
940 RemoveAllOfHeader(kTransferEncoding);
941 }
942 content_length_status_ = BalsaHeadersEnums::VALID_CONTENT_LENGTH;
943 content_length_ = length;
944
945 AppendHeader(kContentLength, absl::StrCat(length));
946 }
947
SetTransferEncodingToChunkedAndClearContentLength()948 void BalsaHeaders::SetTransferEncodingToChunkedAndClearContentLength() {
949 if (transfer_encoding_is_chunked_) {
950 return;
951 }
952 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) {
953 // Per https://httpwg.org/specs/rfc7230.html#header.content-length, we can't
954 // send both transfer-encoding and content-length.
955 ClearContentLength();
956 }
957 ReplaceOrAppendHeader(kTransferEncoding, "chunked");
958 transfer_encoding_is_chunked_ = true;
959 }
960
SetNoTransferEncoding()961 void BalsaHeaders::SetNoTransferEncoding() {
962 if (transfer_encoding_is_chunked_) {
963 // clears transfer_encoding_is_chunked_
964 RemoveAllOfHeader(kTransferEncoding);
965 }
966 }
967
ClearContentLength()968 void BalsaHeaders::ClearContentLength() { RemoveAllOfHeader(kContentLength); }
969
IsEmpty() const970 bool BalsaHeaders::IsEmpty() const {
971 return balsa_buffer_.GetTotalBytesUsed() == 0;
972 }
973
Authority() const974 absl::string_view BalsaHeaders::Authority() const { return GetHeader(kHost); }
975
ReplaceOrAppendAuthority(absl::string_view value)976 void BalsaHeaders::ReplaceOrAppendAuthority(absl::string_view value) {
977 ReplaceOrAppendHeader(kHost, value);
978 }
979
RemoveAuthority()980 void BalsaHeaders::RemoveAuthority() { RemoveAllOfHeader(kHost); }
981
ApplyToCookie(quiche::UnretainedCallback<void (absl::string_view cookie)> f) const982 void BalsaHeaders::ApplyToCookie(
983 quiche::UnretainedCallback<void(absl::string_view cookie)> f) const {
984 f(GetHeader(kCookie));
985 }
986
SetResponseFirstline(absl::string_view version,size_t parsed_response_code,absl::string_view reason_phrase)987 void BalsaHeaders::SetResponseFirstline(absl::string_view version,
988 size_t parsed_response_code,
989 absl::string_view reason_phrase) {
990 SetFirstlineFromStringPieces(version, absl::StrCat(parsed_response_code),
991 reason_phrase);
992 parsed_response_code_ = parsed_response_code;
993 }
994
SetFirstlineFromStringPieces(absl::string_view firstline_a,absl::string_view firstline_b,absl::string_view firstline_c)995 void BalsaHeaders::SetFirstlineFromStringPieces(absl::string_view firstline_a,
996 absl::string_view firstline_b,
997 absl::string_view firstline_c) {
998 size_t line_size =
999 (firstline_a.size() + firstline_b.size() + firstline_c.size() + 2);
1000 char* storage = balsa_buffer_.Reserve(line_size, &firstline_buffer_base_idx_);
1001 char* cur_loc = storage;
1002
1003 memcpy(cur_loc, firstline_a.data(), firstline_a.size());
1004 cur_loc += firstline_a.size();
1005
1006 *cur_loc = ' ';
1007 ++cur_loc;
1008
1009 memcpy(cur_loc, firstline_b.data(), firstline_b.size());
1010 cur_loc += firstline_b.size();
1011
1012 *cur_loc = ' ';
1013 ++cur_loc;
1014
1015 memcpy(cur_loc, firstline_c.data(), firstline_c.size());
1016
1017 whitespace_1_idx_ = storage - BeginningOfFirstLine();
1018 non_whitespace_1_idx_ = whitespace_1_idx_;
1019 whitespace_2_idx_ = non_whitespace_1_idx_ + firstline_a.size();
1020 non_whitespace_2_idx_ = whitespace_2_idx_ + 1;
1021 whitespace_3_idx_ = non_whitespace_2_idx_ + firstline_b.size();
1022 non_whitespace_3_idx_ = whitespace_3_idx_ + 1;
1023 whitespace_4_idx_ = non_whitespace_3_idx_ + firstline_c.size();
1024 }
1025
SetRequestMethod(absl::string_view method)1026 void BalsaHeaders::SetRequestMethod(absl::string_view method) {
1027 // This is the first of the three parts of the firstline.
1028 if (method.size() <= (whitespace_2_idx_ - non_whitespace_1_idx_)) {
1029 non_whitespace_1_idx_ = whitespace_2_idx_ - method.size();
1030 if (!method.empty()) {
1031 char* stream_begin = BeginningOfFirstLine();
1032 memcpy(stream_begin + non_whitespace_1_idx_, method.data(),
1033 method.size());
1034 }
1035 } else {
1036 // The new method is too large to fit in the space available for the old
1037 // one, so we have to reformat the firstline.
1038 SetRequestFirstlineFromStringPieces(method, request_uri(),
1039 request_version());
1040 }
1041 }
1042
SetResponseVersion(absl::string_view version)1043 void BalsaHeaders::SetResponseVersion(absl::string_view version) {
1044 // Note: There is no difference between request_method() and
1045 // response_Version(). Thus, a function to set one is equivalent to a
1046 // function to set the other. We maintain two functions for this as it is
1047 // much more descriptive, and makes code more understandable.
1048 SetRequestMethod(version);
1049 }
1050
SetRequestUri(absl::string_view uri)1051 void BalsaHeaders::SetRequestUri(absl::string_view uri) {
1052 SetRequestFirstlineFromStringPieces(request_method(), uri, request_version());
1053 }
1054
SetResponseCode(absl::string_view code)1055 void BalsaHeaders::SetResponseCode(absl::string_view code) {
1056 // Note: There is no difference between request_uri() and response_code().
1057 // Thus, a function to set one is equivalent to a function to set the other.
1058 // We maintain two functions for this as it is much more descriptive, and
1059 // makes code more understandable.
1060 SetRequestUri(code);
1061 }
1062
SetParsedResponseCodeAndUpdateFirstline(size_t parsed_response_code)1063 void BalsaHeaders::SetParsedResponseCodeAndUpdateFirstline(
1064 size_t parsed_response_code) {
1065 parsed_response_code_ = parsed_response_code;
1066 SetResponseCode(absl::StrCat(parsed_response_code));
1067 }
1068
SetRequestVersion(absl::string_view version)1069 void BalsaHeaders::SetRequestVersion(absl::string_view version) {
1070 // This is the last of the three parts of the firstline.
1071 // Since whitespace_3_idx and non_whitespace_3_idx may point to the same
1072 // place, we ensure below that any available space includes space for a
1073 // literal space (' ') character between the second component and the third
1074 // component.
1075 bool fits_in_space_allowed =
1076 version.size() + 1 <= whitespace_4_idx_ - whitespace_3_idx_;
1077
1078 if (!fits_in_space_allowed) {
1079 // If the new version is too large, then reformat the firstline.
1080 SetRequestFirstlineFromStringPieces(request_method(), request_uri(),
1081 version);
1082 return;
1083 }
1084
1085 char* stream_begin = BeginningOfFirstLine();
1086 *(stream_begin + whitespace_3_idx_) = ' ';
1087 non_whitespace_3_idx_ = whitespace_3_idx_ + 1;
1088 whitespace_4_idx_ = non_whitespace_3_idx_ + version.size();
1089 memcpy(stream_begin + non_whitespace_3_idx_, version.data(), version.size());
1090 }
1091
SetResponseReasonPhrase(absl::string_view reason)1092 void BalsaHeaders::SetResponseReasonPhrase(absl::string_view reason) {
1093 // Note: There is no difference between request_version() and
1094 // response_reason_phrase(). Thus, a function to set one is equivalent to a
1095 // function to set the other. We maintain two functions for this as it is
1096 // much more descriptive, and makes code more understandable.
1097 SetRequestVersion(reason);
1098 }
1099
RemoveLastTokenFromHeaderValue(absl::string_view key)1100 void BalsaHeaders::RemoveLastTokenFromHeaderValue(absl::string_view key) {
1101 BalsaHeaders::HeaderLines::iterator it =
1102 GetHeaderLinesIterator(key, header_lines_.begin());
1103 if (it == header_lines_.end()) {
1104 QUICHE_DLOG(WARNING)
1105 << "Attempting to remove last token from a non-existent "
1106 << "header \"" << key << "\"";
1107 return;
1108 }
1109
1110 // Find the last line with that key.
1111 BalsaHeaders::HeaderLines::iterator header_line;
1112 do {
1113 header_line = it;
1114 it = GetHeaderLinesIterator(key, it + 1);
1115 } while (it != header_lines_.end());
1116
1117 // Tokenize just that line.
1118 BalsaHeaders::HeaderTokenList tokens;
1119 // Find where this line is stored.
1120 absl::string_view value(
1121 GetPtr(header_line->buffer_base_idx) + header_line->value_begin_idx,
1122 header_line->last_char_idx - header_line->value_begin_idx);
1123 // Tokenize.
1124 ParseTokenList(value, &tokens);
1125
1126 if (tokens.empty()) {
1127 QUICHE_DLOG(WARNING)
1128 << "Attempting to remove a token from an empty header value "
1129 << "for header \"" << key << "\"";
1130 header_line->skip = true; // remove the whole line
1131 } else if (tokens.size() == 1) {
1132 header_line->skip = true; // remove the whole line
1133 } else {
1134 // Shrink the line size and leave the extra data in the buffer.
1135 absl::string_view new_last_token = tokens[tokens.size() - 2];
1136 const char* last_char_address =
1137 new_last_token.data() + new_last_token.size() - 1;
1138 const char* const stream_begin = GetPtr(header_line->buffer_base_idx);
1139
1140 header_line->last_char_idx = last_char_address - stream_begin + 1;
1141 }
1142 }
1143
ResponseCanHaveBody(int response_code)1144 bool BalsaHeaders::ResponseCanHaveBody(int response_code) {
1145 // For responses, can't have a body if the request was a HEAD, or if it is
1146 // one of these response-codes. rfc2616 section 4.3
1147 if (response_code >= 100 && response_code < 200) {
1148 // 1xx responses can't have bodies.
1149 return false;
1150 }
1151
1152 // No content and Not modified responses have no body.
1153 return (response_code != 204) && (response_code != 304);
1154 }
1155
1156 } // namespace quiche
1157