• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 namespace {
24 
25 const char kContentLength[] = "Content-Length";
26 const char kTransferEncoding[] = "Transfer-Encoding";
27 const char kSpaceChar = ' ';
28 
29 __gnu_cxx::hash_set<base::StringPiece,
30                     net::StringPieceCaseHash,
31                     net::StringPieceCaseEqual> g_multivalued_headers;
32 
InitMultivaluedHeaders()33 void 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 
61 REGISTER_MODULE_INITIALIZER(multivalued_headers, InitMultivaluedHeaders());
62 
63 const int kFastToBufferSize = 32;  // I think 22 is adequate, but anyway..
64 
65 }  // namespace
66 
67 namespace net {
68 
69 const size_t BalsaBuffer::kDefaultBlocksize;
70 
iterator_base()71 BalsaHeaders::iterator_base::iterator_base() : headers_(NULL), idx_(0) { }
72 
iterator_base(const iterator_base & it)73 BalsaHeaders::iterator_base::iterator_base(const iterator_base& it)
74     : headers_(it.headers_),
75       idx_(it.idx_) {
76 }
77 
operator <<(std::ostream & os) const78 std::ostream& BalsaHeaders::iterator_base::operator<<(std::ostream& os) const {
79   os << "[" << this->headers_ << ", " << this->idx_ << "]";
80   return os;
81 }
82 
iterator_base(const BalsaHeaders * headers,HeaderLines::size_type index)83 BalsaHeaders::iterator_base::iterator_base(const BalsaHeaders* headers,
84                                            HeaderLines::size_type index)
85     : headers_(headers),
86       idx_(index) {
87 }
88 
~BalsaBuffer()89 BalsaBuffer::~BalsaBuffer() {
90   CleanupBlocksStartingFrom(0);
91 }
92 
93 // Returns the total amount of memory used by the buffer blocks.
GetTotalBufferBlockSize() const94 size_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 
WriteToContiguousBuffer(const base::StringPiece & sp)104 void 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 
Write(const base::StringPiece & sp,Blocks::size_type * block_buffer_idx)133 base::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 
Reserve(size_t size,Blocks::size_type * block_buffer_idx)143 char* 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 
Clear()173 void 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 
Swap(BalsaBuffer * b)186 void 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 
CopyFrom(const BalsaBuffer & b)193 void 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 
BalsaBuffer()203 BalsaBuffer::BalsaBuffer()
204     : blocksize_(kDefaultBlocksize), can_write_to_contiguous_buffer_(true) {
205   blocks_.push_back(AllocBlock());
206 }
207 
BalsaBuffer(size_t blocksize)208 BalsaBuffer::BalsaBuffer(size_t blocksize) :
209     blocksize_(blocksize), can_write_to_contiguous_buffer_(true) {
210   blocks_.push_back(AllocBlock());
211 }
212 
AllocBlock()213 BalsaBuffer::BufferBlock BalsaBuffer::AllocBlock() {
214   return AllocCustomBlock(blocksize_);
215 }
216 
AllocCustomBlock(size_t blocksize)217 BalsaBuffer::BufferBlock BalsaBuffer::AllocCustomBlock(size_t blocksize) {
218   return BufferBlock(new char[blocksize], blocksize, blocksize);
219 }
220 
CopyBlock(const BufferBlock & b)221 BalsaBuffer::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 
CleanupBlocksStartingFrom(Blocks::size_type start_idx)232 void 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 
const_header_lines_key_iterator(const const_header_lines_key_iterator & other)239 BalsaHeaders::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 
const_header_lines_key_iterator(const BalsaHeaders * headers,HeaderLines::size_type index,const base::StringPiece & key)245 BalsaHeaders::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 
const_header_lines_key_iterator(const BalsaHeaders * headers,HeaderLines::size_type index)253 BalsaHeaders::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 
BalsaHeaders()259 BalsaHeaders::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 
~BalsaHeaders()276 BalsaHeaders::~BalsaHeaders() {}
277 
Clear()278 void 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 
Swap(BalsaHeaders * other)296 void 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 
CopyFrom(const BalsaHeaders & other)358 void 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 
AddAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,HeaderLineDescription * d)379 void 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 
AppendOrPrependAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,bool append,HeaderLineDescription * d)404 void 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'.
RemoveAllOfHeaderStartingAt(const base::StringPiece & key,HeaderLines::iterator start)451 void 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 
HackHeader(const base::StringPiece & key,const base::StringPiece & value)460 void 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 
HackAppendToHeader(const base::StringPiece & key,const base::StringPiece & append_value)478 void 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 
ReplaceOrAppendHeader(const base::StringPiece & key,const base::StringPiece & value)493 void 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 
AppendHeader(const base::StringPiece & key,const base::StringPiece & value)511 void 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 
AppendToHeader(const base::StringPiece & key,const base::StringPiece & value)518 void BalsaHeaders::AppendToHeader(const base::StringPiece& key,
519                                   const base::StringPiece& value) {
520   AppendOrPrependToHeader(key, value, true);
521 }
522 
PrependToHeader(const base::StringPiece & key,const base::StringPiece & value)523 void BalsaHeaders::PrependToHeader(const base::StringPiece& key,
524                                    const base::StringPiece& value) {
525   AppendOrPrependToHeader(key, value, false);
526 }
527 
GetValueFromHeaderLineDescription(const HeaderLineDescription & line) const528 base::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 
GetHeader(const base::StringPiece & key) const535 const 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 
GetHeaderPosition(const base::StringPiece & key) const550 BalsaHeaders::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 
GetIteratorForKey(const base::StringPiece & key) const562 BalsaHeaders::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 
AppendOrPrependToHeader(const base::StringPiece & key,const base::StringPiece & value,bool append)574 void 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 
593 BalsaHeaders::HeaderLines::const_iterator
GetConstHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::const_iterator start) const594 BalsaHeaders::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 
GetHeaderLinesIteratorNoSkip(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)617 BalsaHeaders::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 
GetHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)637 BalsaHeaders::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 
GetAllOfHeader(const base::StringPiece & key,std::vector<base::StringPiece> * out) const660 void 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 
HasNonEmptyHeader(const base::StringPiece & key) const668 bool 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 
GetAllOfHeaderAsString(const base::StringPiece & key,std::string * out) const677 void 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
IsMultivaluedHeader(const base::StringPiece & header)693 bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) {
694   return g_multivalued_headers.find(header) != g_multivalued_headers.end();
695 }
696 
RemoveAllOfHeader(const base::StringPiece & key)697 void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) {
698   HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin());
699   RemoveAllOfHeaderStartingAt(key, it);
700 }
701 
RemoveAllHeadersWithPrefix(const base::StringPiece & key)702 void 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 
GetMemoryUsedLowerBound() const720 size_t BalsaHeaders::GetMemoryUsedLowerBound() const {
721   return (sizeof(*this) +
722           balsa_buffer_.GetTotalBufferBlockSize() +
723           header_lines_.capacity() * sizeof(HeaderLineDescription));
724 }
725 
GetSizeForWriteBuffer() const726 size_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 
DumpToString(std::string * str) const744 void 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 
SetFirstLine(const base::StringPiece & line)777 void 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 
SetContentLength(size_t length)790 void 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 
SetChunkEncoding(bool chunk_encode)824 void 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.
SetFirstlineFromStringPieces(const base::StringPiece & firstline_a,const base::StringPiece & firstline_b,const base::StringPiece & firstline_c)853 void 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 
SetRequestMethod(const base::StringPiece & method)888 void 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 
SetResponseVersion(const base::StringPiece & version)903 void 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 
SetRequestUri(const base::StringPiece & uri)911 void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) {
912   SetFirstlineFromStringPieces(request_method(), uri, request_version());
913 }
914 
SetResponseCode(const base::StringPiece & code)915 void 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 
SetParsedResponseCodeAndUpdateFirstline(size_t parsed_response_code)923 void 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 
SetRequestVersion(const base::StringPiece & version)932 void 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 
SetResponseReasonPhrase(const base::StringPiece & reason)955 void 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