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