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