• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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 "net/tools/flip_server/balsa_headers.h"
6 
7 #include <emmintrin.h>
8 
9 #include <algorithm>
10 #include <ext/hash_set>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/logging.h"
16 #include "base/port.h"
17 #include "base/string_piece.h"
18 #include "base/string_util.h"
19 #include "net/tools/flip_server/balsa_enums.h"
20 #include "net/tools/flip_server/buffer_interface.h"
21 #include "net/tools/flip_server/simple_buffer.h"
22 #include "third_party/tcmalloc/chromium/src/base/googleinit.h"
23 // #include "util/gtl/iterator_adaptors-inl.h"
24 // #include "util/gtl/map-util.h"
25 
26 namespace {
27 
28 const char kContentLength[] = "Content-Length";
29 const char kTransferEncoding[] = "Transfer-Encoding";
30 const char kSpaceChar = ' ';
31 
32 __gnu_cxx::hash_set<base::StringPiece,
33                     net::StringPieceCaseHash,
34                     net::StringPieceCaseEqual> g_multivalued_headers;
35 
InitMultivaluedHeaders()36 void InitMultivaluedHeaders() {
37   g_multivalued_headers.insert("accept");
38   g_multivalued_headers.insert("accept-charset");
39   g_multivalued_headers.insert("accept-encoding");
40   g_multivalued_headers.insert("accept-language");
41   g_multivalued_headers.insert("accept-ranges");
42   g_multivalued_headers.insert("allow");
43   g_multivalued_headers.insert("cache-control");
44   g_multivalued_headers.insert("connection");
45   g_multivalued_headers.insert("content-encoding");
46   g_multivalued_headers.insert("content-language");
47   g_multivalued_headers.insert("expect");
48   g_multivalued_headers.insert("if-match");
49   g_multivalued_headers.insert("if-none-match");
50   g_multivalued_headers.insert("pragma");
51   g_multivalued_headers.insert("proxy-authenticate");
52   g_multivalued_headers.insert("te");
53   g_multivalued_headers.insert("trailer");
54   g_multivalued_headers.insert("transfer-encoding");
55   g_multivalued_headers.insert("upgrade");
56   g_multivalued_headers.insert("vary");
57   g_multivalued_headers.insert("via");
58   g_multivalued_headers.insert("warning");
59   g_multivalued_headers.insert("www-authenticate");
60   // Not mentioned in RFC 2616, but it can have multiple values.
61   g_multivalued_headers.insert("set-cookie");
62 }
63 
64 REGISTER_MODULE_INITIALIZER(multivalued_headers, InitMultivaluedHeaders());
65 
66 const int kFastToBufferSize = 32;  // I think 22 is adequate, but anyway..
67 
68 }  // namespace
69 
70 namespace net {
71 
72 const size_t BalsaBuffer::kDefaultBlocksize;
73 
Clear()74 void BalsaHeaders::Clear() {
75   balsa_buffer_.Clear();
76   transfer_encoding_is_chunked_ = false;
77   content_length_ = 0;
78   content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH;
79   parsed_response_code_ = 0;
80   firstline_buffer_base_idx_ = 0;
81   whitespace_1_idx_ = 0;
82   non_whitespace_1_idx_ = 0;
83   whitespace_2_idx_ = 0;
84   non_whitespace_2_idx_ = 0;
85   whitespace_3_idx_ = 0;
86   non_whitespace_3_idx_ = 0;
87   whitespace_4_idx_ = 0;
88   end_of_firstline_idx_ = 0;
89   header_lines_.clear();
90 }
91 
Swap(BalsaHeaders * other)92 void BalsaHeaders::Swap(BalsaHeaders* other) {
93   // Protect against swapping with self.
94   if (this == other) return;
95 
96   balsa_buffer_.Swap(&other->balsa_buffer_);
97 
98   bool tmp_bool = transfer_encoding_is_chunked_;
99   transfer_encoding_is_chunked_ = other->transfer_encoding_is_chunked_;
100   other->transfer_encoding_is_chunked_ = tmp_bool;
101 
102   size_t tmp_size_t = content_length_;
103   content_length_ = other->content_length_;
104   other->content_length_ = tmp_size_t;
105 
106   BalsaHeadersEnums::ContentLengthStatus tmp_status =
107       content_length_status_;
108   content_length_status_ = other->content_length_status_;
109   other->content_length_status_ = tmp_status;
110 
111   tmp_size_t = parsed_response_code_;
112   parsed_response_code_ = other->parsed_response_code_;
113   other->parsed_response_code_ = tmp_size_t;
114 
115   BalsaBuffer::Blocks::size_type tmp_blk_idx = firstline_buffer_base_idx_;
116   firstline_buffer_base_idx_ = other->firstline_buffer_base_idx_;
117   other->firstline_buffer_base_idx_ = tmp_blk_idx;
118 
119   tmp_size_t = whitespace_1_idx_;
120   whitespace_1_idx_ = other->whitespace_1_idx_;
121   other->whitespace_1_idx_ = tmp_size_t;
122 
123   tmp_size_t = non_whitespace_1_idx_;
124   non_whitespace_1_idx_ = other->non_whitespace_1_idx_;
125   other->non_whitespace_1_idx_ = tmp_size_t;
126 
127   tmp_size_t = whitespace_2_idx_;
128   whitespace_2_idx_ = other->whitespace_2_idx_;
129   other->whitespace_2_idx_ = tmp_size_t;
130 
131   tmp_size_t = non_whitespace_2_idx_;
132   non_whitespace_2_idx_ = other->non_whitespace_2_idx_;
133   other->non_whitespace_2_idx_ = tmp_size_t;
134 
135   tmp_size_t = whitespace_3_idx_;
136   whitespace_3_idx_ = other->whitespace_3_idx_;
137   other->whitespace_3_idx_ = tmp_size_t;
138 
139   tmp_size_t = non_whitespace_3_idx_;
140   non_whitespace_3_idx_ = other->non_whitespace_3_idx_;
141   other->non_whitespace_3_idx_ = tmp_size_t;
142 
143   tmp_size_t = whitespace_4_idx_;
144   whitespace_4_idx_ = other->whitespace_4_idx_;
145   other->whitespace_4_idx_ = tmp_size_t;
146 
147   tmp_size_t = end_of_firstline_idx_;
148   end_of_firstline_idx_ = other->end_of_firstline_idx_;
149   other->end_of_firstline_idx_ = tmp_size_t;
150 
151   swap(header_lines_, other->header_lines_);
152 }
153 
CopyFrom(const BalsaHeaders & other)154 void BalsaHeaders::CopyFrom(const BalsaHeaders& other) {
155   // Protect against copying with self.
156   if (this == &other) return;
157 
158   balsa_buffer_.CopyFrom(other.balsa_buffer_);
159   transfer_encoding_is_chunked_ = other.transfer_encoding_is_chunked_;
160   content_length_ = other.content_length_;
161   content_length_status_ = other.content_length_status_;
162   parsed_response_code_ = other.parsed_response_code_;
163   firstline_buffer_base_idx_ = other.firstline_buffer_base_idx_;
164   whitespace_1_idx_ = other.whitespace_1_idx_;
165   non_whitespace_1_idx_ = other.non_whitespace_1_idx_;
166   whitespace_2_idx_ = other.whitespace_2_idx_;
167   non_whitespace_2_idx_ = other.non_whitespace_2_idx_;
168   whitespace_3_idx_ = other.whitespace_3_idx_;
169   non_whitespace_3_idx_ = other.non_whitespace_3_idx_;
170   whitespace_4_idx_ = other.whitespace_4_idx_;
171   end_of_firstline_idx_ = other.end_of_firstline_idx_;
172   header_lines_ = other.header_lines_;
173 }
174 
AddAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,HeaderLineDescription * d)175 void BalsaHeaders::AddAndMakeDescription(const base::StringPiece& key,
176                                          const base::StringPiece& value,
177                                          HeaderLineDescription* d) {
178   CHECK(d != NULL);
179   // + 2 to size for ": "
180   size_t line_size = key.size() + 2 + value.size();
181   BalsaBuffer::Blocks::size_type block_buffer_idx = 0;
182   char* storage = balsa_buffer_.Reserve(line_size, &block_buffer_idx);
183   size_t base_idx = storage - GetPtr(block_buffer_idx);
184 
185   char* cur_loc = storage;
186   memcpy(cur_loc, key.data(), key.size());
187   cur_loc += key.size();
188   *cur_loc = ':';
189   ++cur_loc;
190   *cur_loc = ' ';
191   ++cur_loc;
192   memcpy(cur_loc, value.data(), value.size());
193   *d = HeaderLineDescription(base_idx,
194                              base_idx + key.size(),
195                              base_idx + key.size() + 2,
196                              base_idx + key.size() + 2 + value.size(),
197                              block_buffer_idx);
198 }
199 
AppendOrPrependAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,bool append,HeaderLineDescription * d)200 void BalsaHeaders::AppendOrPrependAndMakeDescription(
201     const base::StringPiece& key,
202     const base::StringPiece& value,
203     bool append,
204     HeaderLineDescription* d) {
205   // Figure out how much space we need to reserve for the new header size.
206   size_t old_value_size = d->last_char_idx - d->value_begin_idx;
207   if (old_value_size == 0) {
208     AddAndMakeDescription(key, value, d);
209     return;
210   }
211   base::StringPiece old_value(GetPtr(d->buffer_base_idx) + d->value_begin_idx,
212                         old_value_size);
213 
214   BalsaBuffer::Blocks::size_type block_buffer_idx = 0;
215   // + 3 because we potentially need to add ": ", and "," to the line.
216   size_t new_size = key.size() + 3 + old_value_size + value.size();
217   char* storage = balsa_buffer_.Reserve(new_size, &block_buffer_idx);
218   size_t base_idx = storage - GetPtr(block_buffer_idx);
219 
220   base::StringPiece first_value = old_value;
221   base::StringPiece second_value = value;
222   if (!append) {  // !append == prepend
223     first_value = value;
224     second_value = old_value;
225   }
226   char* cur_loc = storage;
227   memcpy(cur_loc, key.data(), key.size());
228   cur_loc += key.size();
229   *cur_loc = ':';
230   ++cur_loc;
231   *cur_loc = ' ';
232   ++cur_loc;
233   memcpy(cur_loc, first_value.data(), first_value.size());
234   cur_loc += first_value.size();
235   *cur_loc = ',';
236   ++cur_loc;
237   memcpy(cur_loc, second_value.data(), second_value.size());
238 
239   *d = HeaderLineDescription(base_idx,
240                              base_idx + key.size(),
241                              base_idx + key.size() + 2,
242                              base_idx + new_size,
243                              block_buffer_idx);
244 }
245 
246 // Removes all keys value pairs with key 'key' starting at 'start'.
RemoveAllOfHeaderStartingAt(const base::StringPiece & key,HeaderLines::iterator start)247 void BalsaHeaders::RemoveAllOfHeaderStartingAt(const base::StringPiece& key,
248                                                HeaderLines::iterator start) {
249   while (start != header_lines_.end()) {
250     start->skip = true;
251     ++start;
252     start = GetHeaderLinesIterator(key, start);
253   }
254 }
255 
HackHeader(const base::StringPiece & key,const base::StringPiece & value)256 void BalsaHeaders::HackHeader(const base::StringPiece& key,
257                               const base::StringPiece& value) {
258   // See TODO in balsa_headers.h
259   const HeaderLines::iterator end = header_lines_.end();
260   const HeaderLines::iterator begin = header_lines_.begin();
261   HeaderLines::iterator i = GetHeaderLinesIteratorNoSkip(key, begin);
262   if (i != end) {
263     // First, remove all of the header lines including this one.  We want to
264     // remove before replacing, in case our replacement ends up being appended
265     // at the end (and thus would be removed by this call)
266     RemoveAllOfHeaderStartingAt(key, i);
267     // Now add the replacement, at this location.
268     AddAndMakeDescription(key, value, &(*i));
269     return;
270   }
271   AppendHeader(key, value);
272 }
273 
HackAppendToHeader(const base::StringPiece & key,const base::StringPiece & append_value)274 void BalsaHeaders::HackAppendToHeader(const base::StringPiece& key,
275                                       const base::StringPiece& append_value) {
276   // See TODO in balsa_headers.h
277   const HeaderLines::iterator end = header_lines_.end();
278   const HeaderLines::iterator begin = header_lines_.begin();
279 
280   HeaderLines::iterator i = GetHeaderLinesIterator(key, begin);
281   if (i == end) {
282     HackHeader(key, append_value);
283     return;
284   }
285 
286   AppendOrPrependAndMakeDescription(key, append_value, true, &(*i));
287 }
288 
ReplaceOrAppendHeader(const base::StringPiece & key,const base::StringPiece & value)289 void BalsaHeaders::ReplaceOrAppendHeader(const base::StringPiece& key,
290                                          const base::StringPiece& value) {
291   const HeaderLines::iterator end = header_lines_.end();
292   const HeaderLines::iterator begin = header_lines_.begin();
293   HeaderLines::iterator i = GetHeaderLinesIterator(key, begin);
294   if (i != end) {
295     // First, remove all of the header lines including this one.  We want to
296     // remove before replacing, in case our replacement ends up being appended
297     // at the end (and thus would be removed by this call)
298     RemoveAllOfHeaderStartingAt(key, i);
299     // Now, take the first instance and replace it.  This will remove the
300     // 'skipped' tag if the replacement is done in-place.
301     AddAndMakeDescription(key, value, &(*i));
302     return;
303   }
304   AppendHeader(key, value);
305 }
306 
AppendHeader(const base::StringPiece & key,const base::StringPiece & value)307 void BalsaHeaders::AppendHeader(const base::StringPiece& key,
308                                 const base::StringPiece& value) {
309   HeaderLineDescription hld;
310   AddAndMakeDescription(key, value, &hld);
311   header_lines_.push_back(hld);
312 }
313 
AppendToHeader(const base::StringPiece & key,const base::StringPiece & value)314 void BalsaHeaders::AppendToHeader(const base::StringPiece& key,
315                                   const base::StringPiece& value) {
316   AppendOrPrependToHeader(key, value, true);
317 }
318 
PrependToHeader(const base::StringPiece & key,const base::StringPiece & value)319 void BalsaHeaders::PrependToHeader(const base::StringPiece& key,
320                                    const base::StringPiece& value) {
321   AppendOrPrependToHeader(key, value, false);
322 }
323 
GetValueFromHeaderLineDescription(const HeaderLineDescription & line) const324 base::StringPiece BalsaHeaders::GetValueFromHeaderLineDescription(
325     const HeaderLineDescription& line) const {
326   DCHECK_GE(line.last_char_idx, line.value_begin_idx);
327   return base::StringPiece(GetPtr(line.buffer_base_idx) + line.value_begin_idx,
328                      line.last_char_idx - line.value_begin_idx);
329 }
330 
GetHeader(const base::StringPiece & key) const331 const base::StringPiece BalsaHeaders::GetHeader(
332     const base::StringPiece& key) const {
333   DCHECK(!IsMultivaluedHeader(key))
334       << "Header '" << key << "' may consist of multiple lines. Do not "
335       << "use BalsaHeaders::GetHeader() or you may be missing some of its "
336       << "values.";
337   const HeaderLines::const_iterator end = header_lines_.end();
338   const HeaderLines::const_iterator begin = header_lines_.begin();
339   HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin);
340   if (i == end) {
341     return base::StringPiece(NULL, 0);
342   }
343   return GetValueFromHeaderLineDescription(*i);
344 }
345 
GetHeaderPosition(const base::StringPiece & key) const346 BalsaHeaders::const_header_lines_iterator BalsaHeaders::GetHeaderPosition(
347     const base::StringPiece& key) const {
348   const HeaderLines::const_iterator end = header_lines_.end();
349   const HeaderLines::const_iterator begin = header_lines_.begin();
350   HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin);
351   if (i == end) {
352     return header_lines_end();
353   }
354 
355   return const_header_lines_iterator(this, (i - begin));
356 }
357 
GetIteratorForKey(const base::StringPiece & key) const358 BalsaHeaders::const_header_lines_key_iterator BalsaHeaders::GetIteratorForKey(
359     const base::StringPiece& key) const {
360   HeaderLines::const_iterator i =
361       GetConstHeaderLinesIterator(key, header_lines_.begin());
362   if (i == header_lines_.end()) {
363     return header_lines_key_end();
364   }
365 
366   const HeaderLines::const_iterator begin = header_lines_.begin();
367   return const_header_lines_key_iterator(this, (i - begin), key);
368 }
369 
AppendOrPrependToHeader(const base::StringPiece & key,const base::StringPiece & value,bool append)370 void BalsaHeaders::AppendOrPrependToHeader(const base::StringPiece& key,
371                                            const base::StringPiece& value,
372                                            bool append) {
373   HeaderLines::iterator i = GetHeaderLinesIterator(key, header_lines_.begin());
374   if (i == header_lines_.end()) {
375     // The header did not exist already.  Instead of appending to an existing
376     // header simply append the key/value pair to the headers.
377     AppendHeader(key, value);
378     return;
379   }
380   HeaderLineDescription hld = *i;
381 
382   AppendOrPrependAndMakeDescription(key, value, append, &hld);
383 
384   // Invalidate the old header line and add the new one.
385   i->skip = true;
386   header_lines_.push_back(hld);
387 }
388 
389 BalsaHeaders::HeaderLines::const_iterator
GetConstHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::const_iterator start) const390 BalsaHeaders::GetConstHeaderLinesIterator(
391     const base::StringPiece& key,
392     BalsaHeaders::HeaderLines::const_iterator start) const {
393   const HeaderLines::const_iterator end = header_lines_.end();
394   for (HeaderLines::const_iterator i = start; i != end; ++i) {
395     const HeaderLineDescription& line = *i;
396     if (line.skip) {
397       continue;
398     }
399     const size_t key_len = line.key_end_idx - line.first_char_idx;
400 
401     if (key_len != key.size()) {
402       continue;
403     }
404     if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx,
405                     key.data(), key_len) == 0) {
406       DCHECK_GE(line.last_char_idx, line.value_begin_idx);
407       return i;
408     }
409   }
410   return end;
411 }
412 
GetHeaderLinesIteratorNoSkip(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)413 BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIteratorNoSkip(
414     const base::StringPiece& key,
415     BalsaHeaders::HeaderLines::iterator start) {
416   const HeaderLines::iterator end = header_lines_.end();
417   for (HeaderLines::iterator i = start; i != end; ++i) {
418     const HeaderLineDescription& line = *i;
419     const size_t key_len = line.key_end_idx - line.first_char_idx;
420 
421     if (key_len != key.size()) {
422       continue;
423     }
424     if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx,
425                     key.data(), key_len) == 0) {
426       DCHECK_GE(line.last_char_idx, line.value_begin_idx);
427       return i;
428     }
429   }
430   return end;
431 }
432 
GetHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)433 BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIterator(
434     const base::StringPiece& key,
435     BalsaHeaders::HeaderLines::iterator start) {
436   const HeaderLines::iterator end = header_lines_.end();
437   for (HeaderLines::iterator i = start; i != end; ++i) {
438     const HeaderLineDescription& line = *i;
439     if (line.skip) {
440       continue;
441     }
442     const size_t key_len = line.key_end_idx - line.first_char_idx;
443 
444     if (key_len != key.size()) {
445       continue;
446     }
447     if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx,
448                     key.data(), key_len) == 0) {
449       DCHECK_GE(line.last_char_idx, line.value_begin_idx);
450       return i;
451     }
452   }
453   return end;
454 }
455 
GetAllOfHeader(const base::StringPiece & key,std::vector<base::StringPiece> * out) const456 void BalsaHeaders::GetAllOfHeader(
457     const base::StringPiece& key, std::vector<base::StringPiece>* out) const {
458   for (const_header_lines_key_iterator it = GetIteratorForKey(key);
459        it != header_lines_end(); ++it) {
460     out->push_back(it->second);
461   }
462 }
463 
HasNonEmptyHeader(const base::StringPiece & key) const464 bool BalsaHeaders::HasNonEmptyHeader(const base::StringPiece& key) const {
465   for (const_header_lines_key_iterator it = GetIteratorForKey(key);
466        it != header_lines_key_end(); ++it) {
467     if (!it->second.empty())
468       return true;
469   }
470   return false;
471 }
472 
GetAllOfHeaderAsString(const base::StringPiece & key,std::string * out) const473 void BalsaHeaders::GetAllOfHeaderAsString(const base::StringPiece& key,
474                                           std::string* out) const {
475   const_header_lines_iterator it = header_lines_begin();
476   const_header_lines_iterator end = header_lines_end();
477 
478   for (; it != end; ++it) {
479     if (key == it->first) {
480       if (!out->empty()) {
481         out->append(",");
482       }
483       out->append(std::string(it->second.data(), it->second.size()));
484     }
485   }
486 }
487 
488 // static
IsMultivaluedHeader(const base::StringPiece & header)489 bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) {
490   return g_multivalued_headers.find(header) != g_multivalued_headers.end();
491 }
492 
RemoveAllOfHeader(const base::StringPiece & key)493 void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) {
494   HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin());
495   RemoveAllOfHeaderStartingAt(key, it);
496 }
497 
RemoveAllHeadersWithPrefix(const base::StringPiece & key)498 void BalsaHeaders::RemoveAllHeadersWithPrefix(const base::StringPiece& key) {
499   for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) {
500     if (header_lines_[i].skip) {
501       continue;
502     }
503     HeaderLineDescription& line = header_lines_[i];
504     const size_t key_len = line.key_end_idx - line.first_char_idx;
505     if (key_len < key.size()) {
506       // If the key given to us is longer than this header, don't consider it.
507       continue;
508     }
509     if (!strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx,
510                      key.data(), key.size())) {
511       line.skip = true;
512     }
513   }
514 }
515 
GetMemoryUsedLowerBound() const516 size_t BalsaHeaders::GetMemoryUsedLowerBound() const {
517   return (sizeof(*this) +
518           balsa_buffer_.GetTotalBufferBlockSize() +
519           header_lines_.capacity() * sizeof(HeaderLineDescription));
520 }
521 
GetSizeForWriteBuffer() const522 size_t BalsaHeaders::GetSizeForWriteBuffer() const {
523   // First add the space required for the first line + CRLF
524   size_t write_buf_size = whitespace_4_idx_ - non_whitespace_1_idx_ + 2;
525   // Then add the space needed for each header line to write out + CRLF.
526   const HeaderLines::size_type end = header_lines_.size();
527   for (HeaderLines::size_type i = 0; i < end; ++i) {
528     const HeaderLineDescription& line = header_lines_[i];
529     if (!line.skip) {
530       // Add the key size and ": ".
531       write_buf_size += line.key_end_idx - line.first_char_idx + 2;
532       // Add the value size and the CRLF
533       write_buf_size += line.last_char_idx - line.value_begin_idx + 2;
534     }
535   }
536   // Finally tag on the terminal CRLF.
537   return write_buf_size + 2;
538 }
539 
DumpToString(std::string * str) const540 void BalsaHeaders::DumpToString(std::string* str) const {
541   const base::StringPiece firstline = first_line();
542   const int buffer_length =
543       OriginalHeaderStreamEnd() - OriginalHeaderStreamBegin();
544   // First check whether the header object is empty.
545   if (firstline.empty() && buffer_length == 0) {
546     str->append("\n<empty header>\n");
547     return;
548   }
549 
550   // Then check whether the header is in a partially parsed state. If so, just
551   // dump the raw data.
552   if (balsa_buffer_.can_write_to_contiguous_buffer()) {
553     StringAppendF(str, "\n<incomplete header len: %d>\n%.*s\n",
554                   buffer_length, buffer_length, OriginalHeaderStreamBegin());
555     return;
556   }
557 
558   // If the header is complete, then just dump them with the logical key value
559   // pair.
560   str->reserve(str->size() + GetSizeForWriteBuffer());
561   StringAppendF(str, "\n %.*s\n",
562                 static_cast<int>(firstline.size()),
563                 firstline.data());
564   BalsaHeaders::const_header_lines_iterator i = header_lines_begin();
565   for (; i != header_lines_end(); ++i) {
566     StringAppendF(str, " %.*s: %.*s\n",
567                   static_cast<int>(i->first.size()), i->first.data(),
568                   static_cast<int>(i->second.size()), i->second.data());
569   }
570 }
571 
SetFirstLine(const base::StringPiece & line)572 void BalsaHeaders::SetFirstLine(const base::StringPiece& line) {
573   base::StringPiece new_line = balsa_buffer_.Write(line,
574                                                    &firstline_buffer_base_idx_);
575   whitespace_1_idx_ = new_line.data() - GetPtr(firstline_buffer_base_idx_);
576   non_whitespace_1_idx_ = whitespace_1_idx_;
577   whitespace_4_idx_ = whitespace_1_idx_ + line.size();
578   whitespace_2_idx_ = whitespace_4_idx_;
579   non_whitespace_2_idx_ = whitespace_4_idx_;
580   whitespace_3_idx_ = whitespace_4_idx_;
581   non_whitespace_3_idx_ = whitespace_4_idx_;
582   end_of_firstline_idx_ = whitespace_4_idx_;
583 }
584 
SetContentLength(size_t length)585 void BalsaHeaders::SetContentLength(size_t length) {
586   // If the content-length is already the one we want, don't do anything.
587   if (content_length_status_ == BalsaHeadersEnums::VALID_CONTENT_LENGTH &&
588       content_length_ == length) {
589     return;
590   }
591   const base::StringPiece content_length(kContentLength,
592                                          sizeof(kContentLength) - 1);
593   // If header state indicates that there is either a content length or
594   // transfer encoding header, remove them before adding the new content
595   // length. There is always the possibility that client can manually add
596   // either header directly and cause content_length_status_ or
597   // transfer_encoding_is_chunked_ to be inconsistent with the actual header.
598   // In the interest of efficiency, however, we will assume that clients will
599   // use the header object correctly and thus we will not scan the all headers
600   // each time this function is called.
601   if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) {
602     RemoveAllOfHeader(content_length);
603   } else if (transfer_encoding_is_chunked_) {
604     const base::StringPiece transfer_encoding(kTransferEncoding,
605                                         sizeof(kTransferEncoding) - 1);
606     RemoveAllOfHeader(transfer_encoding);
607     transfer_encoding_is_chunked_ = false;
608   }
609   content_length_status_ = BalsaHeadersEnums::VALID_CONTENT_LENGTH;
610   content_length_ = length;
611   // FastUInt64ToBuffer is supposed to use a maximum of kFastToBufferSize bytes.
612   char buffer[kFastToBufferSize];
613   int len_converted = snprintf(buffer, sizeof(buffer), "%ld", length);
614   CHECK_GT(len_converted, 0);
615   const base::StringPiece length_str(buffer, len_converted);
616   AppendHeader(content_length, length_str);
617 }
618 
SetChunkEncoding(bool chunk_encode)619 void BalsaHeaders::SetChunkEncoding(bool chunk_encode) {
620   if (transfer_encoding_is_chunked_ == chunk_encode) {
621     return;
622   }
623   if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH &&
624       chunk_encode) {
625     // Want to change to chunk encoding, but have content length. Arguably we
626     // can leave this step out, since transfer-encoding overrides
627     // content-length.
628     const base::StringPiece content_length(kContentLength,
629                                      sizeof(kContentLength) - 1);
630     RemoveAllOfHeader(content_length);
631     content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH;
632     content_length_ = 0;
633   }
634   const base::StringPiece transfer_encoding(kTransferEncoding,
635                                       sizeof(kTransferEncoding) - 1);
636   if (chunk_encode) {
637     const char kChunked[] = "chunked";
638     const base::StringPiece chunked(kChunked, sizeof(kChunked) - 1);
639     AppendHeader(transfer_encoding, chunked);
640   } else {
641     RemoveAllOfHeader(transfer_encoding);
642   }
643   transfer_encoding_is_chunked_ = chunk_encode;
644 }
645 
646 // See the comment about this function in the header file for a
647 // warning about its usage.
SetFirstlineFromStringPieces(const base::StringPiece & firstline_a,const base::StringPiece & firstline_b,const base::StringPiece & firstline_c)648 void BalsaHeaders::SetFirstlineFromStringPieces(
649     const base::StringPiece& firstline_a,
650     const base::StringPiece& firstline_b,
651     const base::StringPiece& firstline_c) {
652   size_t line_size = (firstline_a.size() +
653                       firstline_b.size() +
654                       firstline_c.size() +
655                       2);
656   char* storage = balsa_buffer_.Reserve(line_size, &firstline_buffer_base_idx_);
657   char* cur_loc = storage;
658 
659   memcpy(cur_loc, firstline_a.data(), firstline_a.size());
660   cur_loc += firstline_a.size();
661 
662   *cur_loc = ' ';
663   ++cur_loc;
664 
665   memcpy(cur_loc, firstline_b.data(), firstline_b.size());
666   cur_loc += firstline_b.size();
667 
668   *cur_loc = ' ';
669   ++cur_loc;
670 
671   memcpy(cur_loc, firstline_c.data(), firstline_c.size());
672 
673   whitespace_1_idx_ = storage - GetPtr(firstline_buffer_base_idx_);
674   non_whitespace_1_idx_ = whitespace_1_idx_;
675   whitespace_2_idx_ = non_whitespace_1_idx_ + firstline_a.size();
676   non_whitespace_2_idx_ = whitespace_2_idx_ + 1;
677   whitespace_3_idx_ = non_whitespace_2_idx_ + firstline_b.size();
678   non_whitespace_3_idx_ = whitespace_3_idx_ + 1;
679   whitespace_4_idx_ = non_whitespace_3_idx_ + firstline_c.size();
680   end_of_firstline_idx_ = whitespace_4_idx_;
681 }
682 
SetRequestMethod(const base::StringPiece & method)683 void BalsaHeaders::SetRequestMethod(const base::StringPiece& method) {
684   // This is the first of the three parts of the firstline.
685   if (method.size() <= (whitespace_2_idx_ - non_whitespace_1_idx_)) {
686     non_whitespace_1_idx_ = whitespace_2_idx_ - method.size();
687     char* stream_begin = GetPtr(firstline_buffer_base_idx_);
688     memcpy(stream_begin + non_whitespace_1_idx_,
689            method.data(),
690            method.size());
691   } else {
692     // The new method is too large to fit in the space available for the old
693     // one, so we have to reformat the firstline.
694     SetFirstlineFromStringPieces(method, request_uri(), request_version());
695   }
696 }
697 
SetResponseVersion(const base::StringPiece & version)698 void BalsaHeaders::SetResponseVersion(const base::StringPiece& version) {
699   // Note: There is no difference between request_method() and
700   // response_Version(). Thus, a function to set one is equivalent to a
701   // function to set the other. We maintain two functions for this as it is
702   // much more descriptive, and makes code more understandable.
703   SetRequestMethod(version);
704 }
705 
SetRequestUri(const base::StringPiece & uri)706 void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) {
707   SetFirstlineFromStringPieces(request_method(), uri, request_version());
708 }
709 
SetResponseCode(const base::StringPiece & code)710 void BalsaHeaders::SetResponseCode(const base::StringPiece& code) {
711   // Note: There is no difference between request_uri() and response_code().
712   // Thus, a function to set one is equivalent to a function to set the other.
713   // We maintain two functions for this as it is much more descriptive, and
714   // makes code more understandable.
715   SetRequestUri(code);
716 }
717 
SetParsedResponseCodeAndUpdateFirstline(size_t parsed_response_code)718 void BalsaHeaders::SetParsedResponseCodeAndUpdateFirstline(
719     size_t parsed_response_code) {
720   char buffer[kFastToBufferSize];
721   int len_converted = snprintf(buffer, sizeof(buffer),
722                                "%ld", parsed_response_code);
723   CHECK_GT(len_converted, 0);
724   SetResponseCode(base::StringPiece(buffer, len_converted));
725 }
726 
SetRequestVersion(const base::StringPiece & version)727 void BalsaHeaders::SetRequestVersion(const base::StringPiece& version) {
728   // This is the last of the three parts of the firstline.
729   // Since whitespace_3_idx and non_whitespace_3_idx may point to the same
730   // place, we ensure below that any available space includes space for a
731   // litteral space (' ') character between the second component and the third
732   // component. If the space between whitespace_3_idx_ and
733   // end_of_firstline_idx_ is >= to version.size() + 1 (for the space), then we
734   // can update the firstline in-place.
735   char* stream_begin = GetPtr(firstline_buffer_base_idx_);
736   if (version.size() + 1 <= end_of_firstline_idx_ - whitespace_3_idx_) {
737     *(stream_begin + whitespace_3_idx_) = kSpaceChar;
738     non_whitespace_3_idx_ = whitespace_3_idx_ + 1;
739     whitespace_4_idx_ = non_whitespace_3_idx_ + version.size();
740     memcpy(stream_begin + non_whitespace_3_idx_,
741            version.data(),
742            version.size());
743   } else {
744     // The new version is to large to fit in the space available for the old
745     // one, so we have to reformat the firstline.
746     SetFirstlineFromStringPieces(request_method(), request_uri(), version);
747   }
748 }
749 
SetResponseReasonPhrase(const base::StringPiece & reason)750 void BalsaHeaders::SetResponseReasonPhrase(const base::StringPiece& reason) {
751   // Note: There is no difference between request_version() and
752   // response_reason_phrase(). Thus, a function to set one is equivalent to a
753   // function to set the other. We maintain two functions for this as it is
754   // much more descriptive, and makes code more understandable.
755   SetRequestVersion(reason);
756 }
757 
758 }  // namespace net
759 
760