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 <string> 10 #include <utility> 11 #include <vector> 12 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" 22 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 30 31 namespace { 32 33 const char kContentLength[] = "Content-Length"; 34 const char kTransferEncoding[] = "Transfer-Encoding"; 35 const char kSpaceChar = ' '; 36 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 45 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"); 72 } 73 74 REGISTER_MODULE_INITIALIZER(multivalued_headers, InitMultivaluedHeaders()); 75 76 const int kFastToBufferSize = 32; // I think 22 is adequate, but anyway.. 77 78 } // namespace 79 80 namespace net { 81 82 BalsaHeaders::iterator_base::iterator_base() : headers_(NULL), idx_(0) { } 83 84 BalsaHeaders::iterator_base::iterator_base(const iterator_base& it) 85 : headers_(it.headers_), 86 idx_(it.idx_) { 87 } 88 89 std::ostream& BalsaHeaders::iterator_base::operator<<(std::ostream& os) const { 90 os << "[" << this->headers_ << ", " << this->idx_ << "]"; 91 return os; 92 } 93 94 BalsaHeaders::iterator_base::iterator_base(const BalsaHeaders* headers, 95 HeaderLines::size_type index) 96 : headers_(headers), 97 idx_(index) { 98 } 99 100 BalsaBuffer::~BalsaBuffer() { 101 CleanupBlocksStartingFrom(0); 102 } 103 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; 111 } 112 return buffer_size; 113 } 114 115 void BalsaBuffer::WriteToContiguousBuffer(const base::StringPiece& sp) { 116 if (sp.empty()) { 117 return; 118 } 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); 132 } 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()); 140 } 141 blocks_[0].bytes_free -= sp.size(); 142 } 143 144 base::StringPiece BalsaBuffer::Write(const base::StringPiece& sp, 145 Blocks::size_type* block_buffer_idx) { 146 if (sp.empty()) { 147 return sp; 148 } 149 char* storage = Reserve(sp.size(), block_buffer_idx); 150 memcpy(storage, sp.data(), sp.size()); 151 return base::StringPiece(storage, sp.size()); 152 } 153 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; 165 } 166 } 167 if (block == NULL) { 168 if (blocksize_ < size) { 169 blocks_.push_back(AllocCustomBlock(size)); 170 } else { 171 blocks_.push_back(AllocBlock()); 172 } 173 block = &blocks_.back(); 174 } 175 176 char* storage = block->start_of_unused_bytes(); 177 block->bytes_free -= size; 178 if (block_buffer_idx) { 179 *block_buffer_idx = block_idx; 180 } 181 return storage; 182 } 183 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()); 192 } 193 DCHECK_GE(blocks_.size(), 1u); 194 can_write_to_contiguous_buffer_ = true; 195 } 196 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_); 202 } 203 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]); 209 } 210 blocksize_ = b.blocksize_; 211 can_write_to_contiguous_buffer_ = b.can_write_to_contiguous_buffer_; 212 } 213 214 BalsaBuffer::BalsaBuffer() 215 : blocksize_(kDefaultBlocksize), can_write_to_contiguous_buffer_(true) { 216 blocks_.push_back(AllocBlock()); 217 } 218 219 BalsaBuffer::BalsaBuffer(size_t blocksize) : 220 blocksize_(blocksize), can_write_to_contiguous_buffer_(true) { 221 blocks_.push_back(AllocBlock()); 222 } 223 224 BalsaBuffer::BufferBlock BalsaBuffer::AllocBlock() { 225 return AllocCustomBlock(blocksize_); 226 } 227 228 BalsaBuffer::BufferBlock BalsaBuffer::AllocCustomBlock(size_t blocksize) { 229 return BufferBlock(new char[blocksize], blocksize, blocksize); 230 } 231 232 BalsaBuffer::BufferBlock BalsaBuffer::CopyBlock(const BufferBlock& b) { 233 BufferBlock block = b; 234 if (b.buffer == NULL) { 235 return block; 236 } 237 238 block.buffer = new char[b.buffer_size]; 239 memcpy(block.buffer, b.buffer, b.bytes_used()); 240 return block; 241 } 242 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; 246 } 247 blocks_.resize(start_idx); 248 } 249 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_) { 254 } 255 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) { 262 } 263 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) { 268 } 269 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) { 285 } 286 287 BalsaHeaders::~BalsaHeaders() {} 288 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(); 305 } 306 307 void BalsaHeaders::Swap(BalsaHeaders* other) { 308 // Protect against swapping with self. 309 if (this == other) return; 310 311 balsa_buffer_.Swap(&other->balsa_buffer_); 312 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; 316 317 size_t tmp_size_t = content_length_; 318 content_length_ = other->content_length_; 319 other->content_length_ = tmp_size_t; 320 321 BalsaHeadersEnums::ContentLengthStatus tmp_status = 322 content_length_status_; 323 content_length_status_ = other->content_length_status_; 324 other->content_length_status_ = tmp_status; 325 326 tmp_size_t = parsed_response_code_; 327 parsed_response_code_ = other->parsed_response_code_; 328 other->parsed_response_code_ = tmp_size_t; 329 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; 333 334 tmp_size_t = whitespace_1_idx_; 335 whitespace_1_idx_ = other->whitespace_1_idx_; 336 other->whitespace_1_idx_ = tmp_size_t; 337 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; 341 342 tmp_size_t = whitespace_2_idx_; 343 whitespace_2_idx_ = other->whitespace_2_idx_; 344 other->whitespace_2_idx_ = tmp_size_t; 345 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; 349 350 tmp_size_t = whitespace_3_idx_; 351 whitespace_3_idx_ = other->whitespace_3_idx_; 352 other->whitespace_3_idx_ = tmp_size_t; 353 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; 357 358 tmp_size_t = whitespace_4_idx_; 359 whitespace_4_idx_ = other->whitespace_4_idx_; 360 other->whitespace_4_idx_ = tmp_size_t; 361 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; 365 366 swap(header_lines_, other->header_lines_); 367 } 368 369 void BalsaHeaders::CopyFrom(const BalsaHeaders& other) { 370 // Protect against copying with self. 371 if (this == &other) return; 372 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_; 388 } 389 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); 399 400 char* cur_loc = storage; 401 memcpy(cur_loc, key.data(), key.size()); 402 cur_loc += key.size(); 403 *cur_loc = ':'; 404 ++cur_loc; 405 *cur_loc = ' '; 406 ++cur_loc; 407 memcpy(cur_loc, 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); 413 } 414 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; 425 } 426 base::StringPiece old_value(GetPtr(d->buffer_base_idx) + d->value_begin_idx, 427 old_value_size); 428 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); 434 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; 440 } 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()); 453 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); 459 } 460 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); 468 } 469 } 470 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; 485 } 486 AppendHeader(key, value); 487 } 488 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(); 494 495 HeaderLines::iterator i = GetHeaderLinesIterator(key, begin); 496 if (i == end) { 497 HackHeader(key, append_value); 498 return; 499 } 500 501 AppendOrPrependAndMakeDescription(key, append_value, true, &(*i)); 502 } 503 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; 518 } 519 AppendHeader(key, value); 520 } 521 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); 527 } 528 529 void BalsaHeaders::AppendToHeader(const base::StringPiece& key, 530 const base::StringPiece& value) { 531 AppendOrPrependToHeader(key, value, true); 532 } 533 534 void BalsaHeaders::PrependToHeader(const base::StringPiece& key, 535 const base::StringPiece& value) { 536 AppendOrPrependToHeader(key, value, false); 537 } 538 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); 544 } 545 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(); 557 } 558 return GetValueFromHeaderLineDescription(*i); 559 } 560 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(); 568 } 569 570 return const_header_lines_iterator(this, (i - begin)); 571 } 572 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(); 579 } 580 581 const HeaderLines::const_iterator begin = header_lines_.begin(); 582 return const_header_lines_key_iterator(this, (i - begin), key); 583 } 584 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; 594 } 595 HeaderLineDescription hld = *i; 596 597 AppendOrPrependAndMakeDescription(key, value, append, &hld); 598 599 // Invalidate the old header line and add the new one. 600 i->skip = true; 601 header_lines_.push_back(hld); 602 } 603 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; 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 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; 635 636 if (key_len != key.size()) { 637 continue; 638 } 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; 643 } 644 } 645 return end; 646 } 647 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; 656 } 657 const size_t key_len = line.key_end_idx - line.first_char_idx; 658 659 if (key_len != key.size()) { 660 continue; 661 } 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; 666 } 667 } 668 return end; 669 } 670 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); 676 } 677 } 678 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; 684 } 685 return false; 686 } 687 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(); 692 693 for (; it != end; ++it) { 694 if (key == it->first) { 695 if (!out->empty()) { 696 out->append(","); 697 } 698 out->append(std::string(it->second.data(), it->second.size())); 699 } 700 } 701 } 702 703 // static 704 bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) { 705 return g_multivalued_headers.find(header) != g_multivalued_headers.end(); 706 } 707 708 void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) { 709 HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin()); 710 RemoveAllOfHeaderStartingAt(key, it); 711 } 712 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; 717 } 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; 723 } 724 if (!strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, 725 key.data(), key.size())) { 726 line.skip = true; 727 } 728 } 729 } 730 731 size_t BalsaHeaders::GetMemoryUsedLowerBound() const { 732 return (sizeof(*this) + 733 balsa_buffer_.GetTotalBufferBlockSize() + 734 header_lines_.capacity() * sizeof(HeaderLineDescription)); 735 } 736 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; 749 } 750 } 751 // Finally tag on the terminal CRLF. 752 return write_buf_size + 2; 753 } 754 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; 763 } 764 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; 772 } 773 774 // If the header is complete, then just dump them with the logical key value 775 // pair. 776 str->reserve(str->size() + GetSizeForWriteBuffer()); 777 base::StringAppendF(str, "\n %.*s\n", 778 static_cast<int>(firstline.size()), 779 firstline.data()); 780 BalsaHeaders::const_header_lines_iterator i = header_lines_begin(); 781 for (; i != header_lines_end(); ++i) { 782 base::StringAppendF(str, " %.*s: %.*s\n", 783 static_cast<int>(i->first.size()), i->first.data(), 784 static_cast<int>(i->second.size()), i->second.data()); 785 } 786 } 787 788 void BalsaHeaders::SetFirstLine(const base::StringPiece& line) { 789 base::StringPiece new_line = balsa_buffer_.Write(line, 790 &firstline_buffer_base_idx_); 791 whitespace_1_idx_ = new_line.data() - GetPtr(firstline_buffer_base_idx_); 792 non_whitespace_1_idx_ = whitespace_1_idx_; 793 whitespace_4_idx_ = whitespace_1_idx_ + line.size(); 794 whitespace_2_idx_ = whitespace_4_idx_; 795 non_whitespace_2_idx_ = whitespace_4_idx_; 796 whitespace_3_idx_ = whitespace_4_idx_; 797 non_whitespace_3_idx_ = whitespace_4_idx_; 798 end_of_firstline_idx_ = whitespace_4_idx_; 799 } 800 801 void BalsaHeaders::SetContentLength(size_t length) { 802 // If the content-length is already the one we want, don't do anything. 803 if (content_length_status_ == BalsaHeadersEnums::VALID_CONTENT_LENGTH && 804 content_length_ == length) { 805 return; 806 } 807 const base::StringPiece content_length(kContentLength, 808 sizeof(kContentLength) - 1); 809 // If header state indicates that there is either a content length or 810 // transfer encoding header, remove them before adding the new content 811 // length. There is always the possibility that client can manually add 812 // either header directly and cause content_length_status_ or 813 // transfer_encoding_is_chunked_ to be inconsistent with the actual header. 814 // In the interest of efficiency, however, we will assume that clients will 815 // use the header object correctly and thus we will not scan the all headers 816 // each time this function is called. 817 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) { 818 RemoveAllOfHeader(content_length); 819 } else if (transfer_encoding_is_chunked_) { 820 const base::StringPiece transfer_encoding(kTransferEncoding, 821 sizeof(kTransferEncoding) - 1); 822 RemoveAllOfHeader(transfer_encoding); 823 transfer_encoding_is_chunked_ = false; 824 } 825 content_length_status_ = BalsaHeadersEnums::VALID_CONTENT_LENGTH; 826 content_length_ = length; 827 // FastUInt64ToBuffer is supposed to use a maximum of kFastToBufferSize bytes. 828 char buffer[kFastToBufferSize]; 829 int len_converted = snprintf(buffer, sizeof(buffer), "%zd", length); 830 CHECK_GT(len_converted, 0); 831 const base::StringPiece length_str(buffer, len_converted); 832 AppendHeader(content_length, length_str); 833 } 834 835 void BalsaHeaders::SetChunkEncoding(bool chunk_encode) { 836 if (transfer_encoding_is_chunked_ == chunk_encode) { 837 return; 838 } 839 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH && 840 chunk_encode) { 841 // Want to change to chunk encoding, but have content length. Arguably we 842 // can leave this step out, since transfer-encoding overrides 843 // content-length. 844 const base::StringPiece content_length(kContentLength, 845 sizeof(kContentLength) - 1); 846 RemoveAllOfHeader(content_length); 847 content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; 848 content_length_ = 0; 849 } 850 const base::StringPiece transfer_encoding(kTransferEncoding, 851 sizeof(kTransferEncoding) - 1); 852 if (chunk_encode) { 853 const char kChunked[] = "chunked"; 854 const base::StringPiece chunked(kChunked, sizeof(kChunked) - 1); 855 AppendHeader(transfer_encoding, chunked); 856 } else { 857 RemoveAllOfHeader(transfer_encoding); 858 } 859 transfer_encoding_is_chunked_ = chunk_encode; 860 } 861 862 // See the comment about this function in the header file for a 863 // warning about its usage. 864 void BalsaHeaders::SetFirstlineFromStringPieces( 865 const base::StringPiece& firstline_a, 866 const base::StringPiece& firstline_b, 867 const base::StringPiece& firstline_c) { 868 size_t line_size = (firstline_a.size() + 869 firstline_b.size() + 870 firstline_c.size() + 871 2); 872 char* storage = balsa_buffer_.Reserve(line_size, &firstline_buffer_base_idx_); 873 char* cur_loc = storage; 874 875 memcpy(cur_loc, firstline_a.data(), firstline_a.size()); 876 cur_loc += firstline_a.size(); 877 878 *cur_loc = ' '; 879 ++cur_loc; 880 881 memcpy(cur_loc, firstline_b.data(), firstline_b.size()); 882 cur_loc += firstline_b.size(); 883 884 *cur_loc = ' '; 885 ++cur_loc; 886 887 memcpy(cur_loc, firstline_c.data(), firstline_c.size()); 888 889 whitespace_1_idx_ = storage - GetPtr(firstline_buffer_base_idx_); 890 non_whitespace_1_idx_ = whitespace_1_idx_; 891 whitespace_2_idx_ = non_whitespace_1_idx_ + firstline_a.size(); 892 non_whitespace_2_idx_ = whitespace_2_idx_ + 1; 893 whitespace_3_idx_ = non_whitespace_2_idx_ + firstline_b.size(); 894 non_whitespace_3_idx_ = whitespace_3_idx_ + 1; 895 whitespace_4_idx_ = non_whitespace_3_idx_ + firstline_c.size(); 896 end_of_firstline_idx_ = whitespace_4_idx_; 897 } 898 899 void BalsaHeaders::SetRequestMethod(const base::StringPiece& method) { 900 // This is the first of the three parts of the firstline. 901 if (method.size() <= (whitespace_2_idx_ - non_whitespace_1_idx_)) { 902 non_whitespace_1_idx_ = whitespace_2_idx_ - method.size(); 903 char* stream_begin = GetPtr(firstline_buffer_base_idx_); 904 memcpy(stream_begin + non_whitespace_1_idx_, 905 method.data(), 906 method.size()); 907 } else { 908 // The new method is too large to fit in the space available for the old 909 // one, so we have to reformat the firstline. 910 SetFirstlineFromStringPieces(method, request_uri(), request_version()); 911 } 912 } 913 914 void BalsaHeaders::SetResponseVersion(const base::StringPiece& version) { 915 // Note: There is no difference between request_method() and 916 // response_Version(). Thus, a function to set one is equivalent to a 917 // function to set the other. We maintain two functions for this as it is 918 // much more descriptive, and makes code more understandable. 919 SetRequestMethod(version); 920 } 921 922 void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) { 923 SetFirstlineFromStringPieces(request_method(), uri, request_version()); 924 } 925 926 void BalsaHeaders::SetResponseCode(const base::StringPiece& code) { 927 // Note: There is no difference between request_uri() and response_code(). 928 // Thus, a function to set one is equivalent to a function to set the other. 929 // We maintain two functions for this as it is much more descriptive, and 930 // makes code more understandable. 931 SetRequestUri(code); 932 } 933 934 void BalsaHeaders::SetParsedResponseCodeAndUpdateFirstline( 935 size_t parsed_response_code) { 936 char buffer[kFastToBufferSize]; 937 int len_converted = snprintf(buffer, sizeof(buffer), 938 "%zd", parsed_response_code); 939 CHECK_GT(len_converted, 0); 940 SetResponseCode(base::StringPiece(buffer, len_converted)); 941 } 942 943 void BalsaHeaders::SetRequestVersion(const base::StringPiece& version) { 944 // This is the last of the three parts of the firstline. 945 // Since whitespace_3_idx and non_whitespace_3_idx may point to the same 946 // place, we ensure below that any available space includes space for a 947 // litteral space (' ') character between the second component and the third 948 // component. If the space between whitespace_3_idx_ and 949 // end_of_firstline_idx_ is >= to version.size() + 1 (for the space), then we 950 // can update the firstline in-place. 951 char* stream_begin = GetPtr(firstline_buffer_base_idx_); 952 if (version.size() + 1 <= end_of_firstline_idx_ - whitespace_3_idx_) { 953 *(stream_begin + whitespace_3_idx_) = kSpaceChar; 954 non_whitespace_3_idx_ = whitespace_3_idx_ + 1; 955 whitespace_4_idx_ = non_whitespace_3_idx_ + version.size(); 956 memcpy(stream_begin + non_whitespace_3_idx_, 957 version.data(), 958 version.size()); 959 } else { 960 // The new version is to large to fit in the space available for the old 961 // one, so we have to reformat the firstline. 962 SetFirstlineFromStringPieces(request_method(), request_uri(), version); 963 } 964 } 965 966 void BalsaHeaders::SetResponseReasonPhrase(const base::StringPiece& reason) { 967 // Note: There is no difference between request_version() and 968 // response_reason_phrase(). Thus, a function to set one is equivalent to a 969 // function to set the other. We maintain two functions for this as it is 970 // much more descriptive, and makes code more understandable. 971 SetRequestVersion(reason); 972 } 973 974 } // namespace net 975