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