Home | History | Annotate | Download | only in flip_server
      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 
     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 
     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 
     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 
    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 
    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 
    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'.
    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 
    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 
    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 
    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 
    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 
    314 void BalsaHeaders::AppendToHeader(const base::StringPiece& key,
    315                                   const base::StringPiece& value) {
    316   AppendOrPrependToHeader(key, value, true);
    317 }
    318 
    319 void BalsaHeaders::PrependToHeader(const base::StringPiece& key,
    320                                    const base::StringPiece& value) {
    321   AppendOrPrependToHeader(key, value, false);
    322 }
    323 
    324 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 
    331 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 
    346 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 
    358 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 
    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
    390 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 
    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 
    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 
    456 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 
    464 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 
    473 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
    489 bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) {
    490   return g_multivalued_headers.find(header) != g_multivalued_headers.end();
    491 }
    492 
    493 void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) {
    494   HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin());
    495   RemoveAllOfHeaderStartingAt(key, it);
    496 }
    497 
    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 
    516 size_t BalsaHeaders::GetMemoryUsedLowerBound() const {
    517   return (sizeof(*this) +
    518           balsa_buffer_.GetTotalBufferBlockSize() +
    519           header_lines_.capacity() * sizeof(HeaderLineDescription));
    520 }
    521 
    522 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 
    540 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 
    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 
    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 
    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.
    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 
    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 
    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 
    706 void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) {
    707   SetFirstlineFromStringPieces(request_method(), uri, request_version());
    708 }
    709 
    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 
    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 
    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 
    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