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