xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/balsa/balsa_headers.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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