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