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