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