balsa_headers.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
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 79void BalsaHeaders::Clear() { 80 balsa_buffer_.Clear(); 81 transfer_encoding_is_chunked_ = false; 82 content_length_ = 0; 83 content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; 84 parsed_response_code_ = 0; 85 firstline_buffer_base_idx_ = 0; 86 whitespace_1_idx_ = 0; 87 non_whitespace_1_idx_ = 0; 88 whitespace_2_idx_ = 0; 89 non_whitespace_2_idx_ = 0; 90 whitespace_3_idx_ = 0; 91 non_whitespace_3_idx_ = 0; 92 whitespace_4_idx_ = 0; 93 end_of_firstline_idx_ = 0; 94 header_lines_.clear(); 95} 96 97void BalsaHeaders::Swap(BalsaHeaders* other) { 98 // Protect against swapping with self. 99 if (this == other) return; 100 101 balsa_buffer_.Swap(&other->balsa_buffer_); 102 103 bool tmp_bool = transfer_encoding_is_chunked_; 104 transfer_encoding_is_chunked_ = other->transfer_encoding_is_chunked_; 105 other->transfer_encoding_is_chunked_ = tmp_bool; 106 107 size_t tmp_size_t = content_length_; 108 content_length_ = other->content_length_; 109 other->content_length_ = tmp_size_t; 110 111 BalsaHeadersEnums::ContentLengthStatus tmp_status = 112 content_length_status_; 113 content_length_status_ = other->content_length_status_; 114 other->content_length_status_ = tmp_status; 115 116 tmp_size_t = parsed_response_code_; 117 parsed_response_code_ = other->parsed_response_code_; 118 other->parsed_response_code_ = tmp_size_t; 119 120 BalsaBuffer::Blocks::size_type tmp_blk_idx = firstline_buffer_base_idx_; 121 firstline_buffer_base_idx_ = other->firstline_buffer_base_idx_; 122 other->firstline_buffer_base_idx_ = tmp_blk_idx; 123 124 tmp_size_t = whitespace_1_idx_; 125 whitespace_1_idx_ = other->whitespace_1_idx_; 126 other->whitespace_1_idx_ = tmp_size_t; 127 128 tmp_size_t = non_whitespace_1_idx_; 129 non_whitespace_1_idx_ = other->non_whitespace_1_idx_; 130 other->non_whitespace_1_idx_ = tmp_size_t; 131 132 tmp_size_t = whitespace_2_idx_; 133 whitespace_2_idx_ = other->whitespace_2_idx_; 134 other->whitespace_2_idx_ = tmp_size_t; 135 136 tmp_size_t = non_whitespace_2_idx_; 137 non_whitespace_2_idx_ = other->non_whitespace_2_idx_; 138 other->non_whitespace_2_idx_ = tmp_size_t; 139 140 tmp_size_t = whitespace_3_idx_; 141 whitespace_3_idx_ = other->whitespace_3_idx_; 142 other->whitespace_3_idx_ = tmp_size_t; 143 144 tmp_size_t = non_whitespace_3_idx_; 145 non_whitespace_3_idx_ = other->non_whitespace_3_idx_; 146 other->non_whitespace_3_idx_ = tmp_size_t; 147 148 tmp_size_t = whitespace_4_idx_; 149 whitespace_4_idx_ = other->whitespace_4_idx_; 150 other->whitespace_4_idx_ = tmp_size_t; 151 152 tmp_size_t = end_of_firstline_idx_; 153 end_of_firstline_idx_ = other->end_of_firstline_idx_; 154 other->end_of_firstline_idx_ = tmp_size_t; 155 156 swap(header_lines_, other->header_lines_); 157} 158 159void BalsaHeaders::CopyFrom(const BalsaHeaders& other) { 160 // Protect against copying with self. 161 if (this == &other) return; 162 163 balsa_buffer_.CopyFrom(other.balsa_buffer_); 164 transfer_encoding_is_chunked_ = other.transfer_encoding_is_chunked_; 165 content_length_ = other.content_length_; 166 content_length_status_ = other.content_length_status_; 167 parsed_response_code_ = other.parsed_response_code_; 168 firstline_buffer_base_idx_ = other.firstline_buffer_base_idx_; 169 whitespace_1_idx_ = other.whitespace_1_idx_; 170 non_whitespace_1_idx_ = other.non_whitespace_1_idx_; 171 whitespace_2_idx_ = other.whitespace_2_idx_; 172 non_whitespace_2_idx_ = other.non_whitespace_2_idx_; 173 whitespace_3_idx_ = other.whitespace_3_idx_; 174 non_whitespace_3_idx_ = other.non_whitespace_3_idx_; 175 whitespace_4_idx_ = other.whitespace_4_idx_; 176 end_of_firstline_idx_ = other.end_of_firstline_idx_; 177 header_lines_ = other.header_lines_; 178} 179 180void BalsaHeaders::AddAndMakeDescription(const base::StringPiece& key, 181 const base::StringPiece& value, 182 HeaderLineDescription* d) { 183 CHECK(d != NULL); 184 // + 2 to size for ": " 185 size_t line_size = key.size() + 2 + value.size(); 186 BalsaBuffer::Blocks::size_type block_buffer_idx = 0; 187 char* storage = balsa_buffer_.Reserve(line_size, &block_buffer_idx); 188 size_t base_idx = storage - GetPtr(block_buffer_idx); 189 190 char* cur_loc = storage; 191 memcpy(cur_loc, key.data(), key.size()); 192 cur_loc += key.size(); 193 *cur_loc = ':'; 194 ++cur_loc; 195 *cur_loc = ' '; 196 ++cur_loc; 197 memcpy(cur_loc, value.data(), value.size()); 198 *d = HeaderLineDescription(base_idx, 199 base_idx + key.size(), 200 base_idx + key.size() + 2, 201 base_idx + key.size() + 2 + value.size(), 202 block_buffer_idx); 203} 204 205void BalsaHeaders::AppendOrPrependAndMakeDescription( 206 const base::StringPiece& key, 207 const base::StringPiece& value, 208 bool append, 209 HeaderLineDescription* d) { 210 // Figure out how much space we need to reserve for the new header size. 211 size_t old_value_size = d->last_char_idx - d->value_begin_idx; 212 if (old_value_size == 0) { 213 AddAndMakeDescription(key, value, d); 214 return; 215 } 216 base::StringPiece old_value(GetPtr(d->buffer_base_idx) + d->value_begin_idx, 217 old_value_size); 218 219 BalsaBuffer::Blocks::size_type block_buffer_idx = 0; 220 // + 3 because we potentially need to add ": ", and "," to the line. 221 size_t new_size = key.size() + 3 + old_value_size + value.size(); 222 char* storage = balsa_buffer_.Reserve(new_size, &block_buffer_idx); 223 size_t base_idx = storage - GetPtr(block_buffer_idx); 224 225 base::StringPiece first_value = old_value; 226 base::StringPiece second_value = value; 227 if (!append) { // !append == prepend 228 first_value = value; 229 second_value = old_value; 230 } 231 char* cur_loc = storage; 232 memcpy(cur_loc, key.data(), key.size()); 233 cur_loc += key.size(); 234 *cur_loc = ':'; 235 ++cur_loc; 236 *cur_loc = ' '; 237 ++cur_loc; 238 memcpy(cur_loc, first_value.data(), first_value.size()); 239 cur_loc += first_value.size(); 240 *cur_loc = ','; 241 ++cur_loc; 242 memcpy(cur_loc, second_value.data(), second_value.size()); 243 244 *d = HeaderLineDescription(base_idx, 245 base_idx + key.size(), 246 base_idx + key.size() + 2, 247 base_idx + new_size, 248 block_buffer_idx); 249} 250 251// Removes all keys value pairs with key 'key' starting at 'start'. 252void BalsaHeaders::RemoveAllOfHeaderStartingAt(const base::StringPiece& key, 253 HeaderLines::iterator start) { 254 while (start != header_lines_.end()) { 255 start->skip = true; 256 ++start; 257 start = GetHeaderLinesIterator(key, start); 258 } 259} 260 261void BalsaHeaders::HackHeader(const base::StringPiece& key, 262 const base::StringPiece& value) { 263 // See TODO in balsa_headers.h 264 const HeaderLines::iterator end = header_lines_.end(); 265 const HeaderLines::iterator begin = header_lines_.begin(); 266 HeaderLines::iterator i = GetHeaderLinesIteratorNoSkip(key, begin); 267 if (i != end) { 268 // First, remove all of the header lines including this one. We want to 269 // remove before replacing, in case our replacement ends up being appended 270 // at the end (and thus would be removed by this call) 271 RemoveAllOfHeaderStartingAt(key, i); 272 // Now add the replacement, at this location. 273 AddAndMakeDescription(key, value, &(*i)); 274 return; 275 } 276 AppendHeader(key, value); 277} 278 279void BalsaHeaders::HackAppendToHeader(const base::StringPiece& key, 280 const base::StringPiece& append_value) { 281 // See TODO in balsa_headers.h 282 const HeaderLines::iterator end = header_lines_.end(); 283 const HeaderLines::iterator begin = header_lines_.begin(); 284 285 HeaderLines::iterator i = GetHeaderLinesIterator(key, begin); 286 if (i == end) { 287 HackHeader(key, append_value); 288 return; 289 } 290 291 AppendOrPrependAndMakeDescription(key, append_value, true, &(*i)); 292} 293 294void BalsaHeaders::ReplaceOrAppendHeader(const base::StringPiece& key, 295 const base::StringPiece& value) { 296 const HeaderLines::iterator end = header_lines_.end(); 297 const HeaderLines::iterator begin = header_lines_.begin(); 298 HeaderLines::iterator i = GetHeaderLinesIterator(key, begin); 299 if (i != end) { 300 // First, remove all of the header lines including this one. We want to 301 // remove before replacing, in case our replacement ends up being appended 302 // at the end (and thus would be removed by this call) 303 RemoveAllOfHeaderStartingAt(key, i); 304 // Now, take the first instance and replace it. This will remove the 305 // 'skipped' tag if the replacement is done in-place. 306 AddAndMakeDescription(key, value, &(*i)); 307 return; 308 } 309 AppendHeader(key, value); 310} 311 312void BalsaHeaders::AppendHeader(const base::StringPiece& key, 313 const base::StringPiece& value) { 314 HeaderLineDescription hld; 315 AddAndMakeDescription(key, value, &hld); 316 header_lines_.push_back(hld); 317} 318 319void BalsaHeaders::AppendToHeader(const base::StringPiece& key, 320 const base::StringPiece& value) { 321 AppendOrPrependToHeader(key, value, true); 322} 323 324void BalsaHeaders::PrependToHeader(const base::StringPiece& key, 325 const base::StringPiece& value) { 326 AppendOrPrependToHeader(key, value, false); 327} 328 329base::StringPiece BalsaHeaders::GetValueFromHeaderLineDescription( 330 const HeaderLineDescription& line) const { 331 DCHECK_GE(line.last_char_idx, line.value_begin_idx); 332 return base::StringPiece(GetPtr(line.buffer_base_idx) + line.value_begin_idx, 333 line.last_char_idx - line.value_begin_idx); 334} 335 336const base::StringPiece BalsaHeaders::GetHeader( 337 const base::StringPiece& key) const { 338 DCHECK(!IsMultivaluedHeader(key)) 339 << "Header '" << key << "' may consist of multiple lines. Do not " 340 << "use BalsaHeaders::GetHeader() or you may be missing some of its " 341 << "values."; 342 const HeaderLines::const_iterator end = header_lines_.end(); 343 const HeaderLines::const_iterator begin = header_lines_.begin(); 344 HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin); 345 if (i == end) { 346 return base::StringPiece(NULL, 0); 347 } 348 return GetValueFromHeaderLineDescription(*i); 349} 350 351BalsaHeaders::const_header_lines_iterator BalsaHeaders::GetHeaderPosition( 352 const base::StringPiece& key) const { 353 const HeaderLines::const_iterator end = header_lines_.end(); 354 const HeaderLines::const_iterator begin = header_lines_.begin(); 355 HeaderLines::const_iterator i = GetConstHeaderLinesIterator(key, begin); 356 if (i == end) { 357 return header_lines_end(); 358 } 359 360 return const_header_lines_iterator(this, (i - begin)); 361} 362 363BalsaHeaders::const_header_lines_key_iterator BalsaHeaders::GetIteratorForKey( 364 const base::StringPiece& key) const { 365 HeaderLines::const_iterator i = 366 GetConstHeaderLinesIterator(key, header_lines_.begin()); 367 if (i == header_lines_.end()) { 368 return header_lines_key_end(); 369 } 370 371 const HeaderLines::const_iterator begin = header_lines_.begin(); 372 return const_header_lines_key_iterator(this, (i - begin), key); 373} 374 375void BalsaHeaders::AppendOrPrependToHeader(const base::StringPiece& key, 376 const base::StringPiece& value, 377 bool append) { 378 HeaderLines::iterator i = GetHeaderLinesIterator(key, header_lines_.begin()); 379 if (i == header_lines_.end()) { 380 // The header did not exist already. Instead of appending to an existing 381 // header simply append the key/value pair to the headers. 382 AppendHeader(key, value); 383 return; 384 } 385 HeaderLineDescription hld = *i; 386 387 AppendOrPrependAndMakeDescription(key, value, append, &hld); 388 389 // Invalidate the old header line and add the new one. 390 i->skip = true; 391 header_lines_.push_back(hld); 392} 393 394BalsaHeaders::HeaderLines::const_iterator 395BalsaHeaders::GetConstHeaderLinesIterator( 396 const base::StringPiece& key, 397 BalsaHeaders::HeaderLines::const_iterator start) const { 398 const HeaderLines::const_iterator end = header_lines_.end(); 399 for (HeaderLines::const_iterator i = start; i != end; ++i) { 400 const HeaderLineDescription& line = *i; 401 if (line.skip) { 402 continue; 403 } 404 const size_t key_len = line.key_end_idx - line.first_char_idx; 405 406 if (key_len != key.size()) { 407 continue; 408 } 409 if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, 410 key.data(), key_len) == 0) { 411 DCHECK_GE(line.last_char_idx, line.value_begin_idx); 412 return i; 413 } 414 } 415 return end; 416} 417 418BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIteratorNoSkip( 419 const base::StringPiece& key, 420 BalsaHeaders::HeaderLines::iterator start) { 421 const HeaderLines::iterator end = header_lines_.end(); 422 for (HeaderLines::iterator i = start; i != end; ++i) { 423 const HeaderLineDescription& line = *i; 424 const size_t key_len = line.key_end_idx - line.first_char_idx; 425 426 if (key_len != key.size()) { 427 continue; 428 } 429 if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, 430 key.data(), key_len) == 0) { 431 DCHECK_GE(line.last_char_idx, line.value_begin_idx); 432 return i; 433 } 434 } 435 return end; 436} 437 438BalsaHeaders::HeaderLines::iterator BalsaHeaders::GetHeaderLinesIterator( 439 const base::StringPiece& key, 440 BalsaHeaders::HeaderLines::iterator start) { 441 const HeaderLines::iterator end = header_lines_.end(); 442 for (HeaderLines::iterator i = start; i != end; ++i) { 443 const HeaderLineDescription& line = *i; 444 if (line.skip) { 445 continue; 446 } 447 const size_t key_len = line.key_end_idx - line.first_char_idx; 448 449 if (key_len != key.size()) { 450 continue; 451 } 452 if (strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, 453 key.data(), key_len) == 0) { 454 DCHECK_GE(line.last_char_idx, line.value_begin_idx); 455 return i; 456 } 457 } 458 return end; 459} 460 461void BalsaHeaders::GetAllOfHeader( 462 const base::StringPiece& key, std::vector<base::StringPiece>* out) const { 463 for (const_header_lines_key_iterator it = GetIteratorForKey(key); 464 it != header_lines_end(); ++it) { 465 out->push_back(it->second); 466 } 467} 468 469bool BalsaHeaders::HasNonEmptyHeader(const base::StringPiece& key) const { 470 for (const_header_lines_key_iterator it = GetIteratorForKey(key); 471 it != header_lines_key_end(); ++it) { 472 if (!it->second.empty()) 473 return true; 474 } 475 return false; 476} 477 478void BalsaHeaders::GetAllOfHeaderAsString(const base::StringPiece& key, 479 std::string* out) const { 480 const_header_lines_iterator it = header_lines_begin(); 481 const_header_lines_iterator end = header_lines_end(); 482 483 for (; it != end; ++it) { 484 if (key == it->first) { 485 if (!out->empty()) { 486 out->append(","); 487 } 488 out->append(std::string(it->second.data(), it->second.size())); 489 } 490 } 491} 492 493// static 494bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) { 495 return g_multivalued_headers.find(header) != g_multivalued_headers.end(); 496} 497 498void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) { 499 HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin()); 500 RemoveAllOfHeaderStartingAt(key, it); 501} 502 503void BalsaHeaders::RemoveAllHeadersWithPrefix(const base::StringPiece& key) { 504 for (HeaderLines::size_type i = 0; i < header_lines_.size(); ++i) { 505 if (header_lines_[i].skip) { 506 continue; 507 } 508 HeaderLineDescription& line = header_lines_[i]; 509 const size_t key_len = line.key_end_idx - line.first_char_idx; 510 if (key_len < key.size()) { 511 // If the key given to us is longer than this header, don't consider it. 512 continue; 513 } 514 if (!strncasecmp(GetPtr(line.buffer_base_idx) + line.first_char_idx, 515 key.data(), key.size())) { 516 line.skip = true; 517 } 518 } 519} 520 521size_t BalsaHeaders::GetMemoryUsedLowerBound() const { 522 return (sizeof(*this) + 523 balsa_buffer_.GetTotalBufferBlockSize() + 524 header_lines_.capacity() * sizeof(HeaderLineDescription)); 525} 526 527size_t BalsaHeaders::GetSizeForWriteBuffer() const { 528 // First add the space required for the first line + CRLF 529 size_t write_buf_size = whitespace_4_idx_ - non_whitespace_1_idx_ + 2; 530 // Then add the space needed for each header line to write out + CRLF. 531 const HeaderLines::size_type end = header_lines_.size(); 532 for (HeaderLines::size_type i = 0; i < end; ++i) { 533 const HeaderLineDescription& line = header_lines_[i]; 534 if (!line.skip) { 535 // Add the key size and ": ". 536 write_buf_size += line.key_end_idx - line.first_char_idx + 2; 537 // Add the value size and the CRLF 538 write_buf_size += line.last_char_idx - line.value_begin_idx + 2; 539 } 540 } 541 // Finally tag on the terminal CRLF. 542 return write_buf_size + 2; 543} 544 545void BalsaHeaders::DumpToString(std::string* str) const { 546 const base::StringPiece firstline = first_line(); 547 const int buffer_length = 548 OriginalHeaderStreamEnd() - OriginalHeaderStreamBegin(); 549 // First check whether the header object is empty. 550 if (firstline.empty() && buffer_length == 0) { 551 str->append("\n<empty header>\n"); 552 return; 553 } 554 555 // Then check whether the header is in a partially parsed state. If so, just 556 // dump the raw data. 557 if (balsa_buffer_.can_write_to_contiguous_buffer()) { 558 StringAppendF(str, "\n<incomplete header len: %d>\n%.*s\n", 559 buffer_length, buffer_length, OriginalHeaderStreamBegin()); 560 return; 561 } 562 563 // If the header is complete, then just dump them with the logical key value 564 // pair. 565 str->reserve(str->size() + GetSizeForWriteBuffer()); 566 StringAppendF(str, "\n %.*s\n", 567 static_cast<int>(firstline.size()), 568 firstline.data()); 569 BalsaHeaders::const_header_lines_iterator i = header_lines_begin(); 570 for (; i != header_lines_end(); ++i) { 571 StringAppendF(str, " %.*s: %.*s\n", 572 static_cast<int>(i->first.size()), i->first.data(), 573 static_cast<int>(i->second.size()), i->second.data()); 574 } 575} 576 577void BalsaHeaders::SetFirstLine(const base::StringPiece& line) { 578 base::StringPiece new_line = balsa_buffer_.Write(line, 579 &firstline_buffer_base_idx_); 580 whitespace_1_idx_ = new_line.data() - GetPtr(firstline_buffer_base_idx_); 581 non_whitespace_1_idx_ = whitespace_1_idx_; 582 whitespace_4_idx_ = whitespace_1_idx_ + line.size(); 583 whitespace_2_idx_ = whitespace_4_idx_; 584 non_whitespace_2_idx_ = whitespace_4_idx_; 585 whitespace_3_idx_ = whitespace_4_idx_; 586 non_whitespace_3_idx_ = whitespace_4_idx_; 587 end_of_firstline_idx_ = whitespace_4_idx_; 588} 589 590void BalsaHeaders::SetContentLength(size_t length) { 591 // If the content-length is already the one we want, don't do anything. 592 if (content_length_status_ == BalsaHeadersEnums::VALID_CONTENT_LENGTH && 593 content_length_ == length) { 594 return; 595 } 596 const base::StringPiece content_length(kContentLength, 597 sizeof(kContentLength) - 1); 598 // If header state indicates that there is either a content length or 599 // transfer encoding header, remove them before adding the new content 600 // length. There is always the possibility that client can manually add 601 // either header directly and cause content_length_status_ or 602 // transfer_encoding_is_chunked_ to be inconsistent with the actual header. 603 // In the interest of efficiency, however, we will assume that clients will 604 // use the header object correctly and thus we will not scan the all headers 605 // each time this function is called. 606 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH) { 607 RemoveAllOfHeader(content_length); 608 } else if (transfer_encoding_is_chunked_) { 609 const base::StringPiece transfer_encoding(kTransferEncoding, 610 sizeof(kTransferEncoding) - 1); 611 RemoveAllOfHeader(transfer_encoding); 612 transfer_encoding_is_chunked_ = false; 613 } 614 content_length_status_ = BalsaHeadersEnums::VALID_CONTENT_LENGTH; 615 content_length_ = length; 616 // FastUInt64ToBuffer is supposed to use a maximum of kFastToBufferSize bytes. 617 char buffer[kFastToBufferSize]; 618 int len_converted = snprintf(buffer, sizeof(buffer), "%d", length); 619 CHECK_GT(len_converted, 0); 620 const base::StringPiece length_str(buffer, len_converted); 621 AppendHeader(content_length, length_str); 622} 623 624void BalsaHeaders::SetChunkEncoding(bool chunk_encode) { 625 if (transfer_encoding_is_chunked_ == chunk_encode) { 626 return; 627 } 628 if (content_length_status_ != BalsaHeadersEnums::NO_CONTENT_LENGTH && 629 chunk_encode) { 630 // Want to change to chunk encoding, but have content length. Arguably we 631 // can leave this step out, since transfer-encoding overrides 632 // content-length. 633 const base::StringPiece content_length(kContentLength, 634 sizeof(kContentLength) - 1); 635 RemoveAllOfHeader(content_length); 636 content_length_status_ = BalsaHeadersEnums::NO_CONTENT_LENGTH; 637 content_length_ = 0; 638 } 639 const base::StringPiece transfer_encoding(kTransferEncoding, 640 sizeof(kTransferEncoding) - 1); 641 if (chunk_encode) { 642 const char kChunked[] = "chunked"; 643 const base::StringPiece chunked(kChunked, sizeof(kChunked) - 1); 644 AppendHeader(transfer_encoding, chunked); 645 } else { 646 RemoveAllOfHeader(transfer_encoding); 647 } 648 transfer_encoding_is_chunked_ = chunk_encode; 649} 650 651// See the comment about this function in the header file for a 652// warning about its usage. 653void BalsaHeaders::SetFirstlineFromStringPieces( 654 const base::StringPiece& firstline_a, 655 const base::StringPiece& firstline_b, 656 const base::StringPiece& firstline_c) { 657 size_t line_size = (firstline_a.size() + 658 firstline_b.size() + 659 firstline_c.size() + 660 2); 661 char* storage = balsa_buffer_.Reserve(line_size, &firstline_buffer_base_idx_); 662 char* cur_loc = storage; 663 664 memcpy(cur_loc, firstline_a.data(), firstline_a.size()); 665 cur_loc += firstline_a.size(); 666 667 *cur_loc = ' '; 668 ++cur_loc; 669 670 memcpy(cur_loc, firstline_b.data(), firstline_b.size()); 671 cur_loc += firstline_b.size(); 672 673 *cur_loc = ' '; 674 ++cur_loc; 675 676 memcpy(cur_loc, firstline_c.data(), firstline_c.size()); 677 678 whitespace_1_idx_ = storage - GetPtr(firstline_buffer_base_idx_); 679 non_whitespace_1_idx_ = whitespace_1_idx_; 680 whitespace_2_idx_ = non_whitespace_1_idx_ + firstline_a.size(); 681 non_whitespace_2_idx_ = whitespace_2_idx_ + 1; 682 whitespace_3_idx_ = non_whitespace_2_idx_ + firstline_b.size(); 683 non_whitespace_3_idx_ = whitespace_3_idx_ + 1; 684 whitespace_4_idx_ = non_whitespace_3_idx_ + firstline_c.size(); 685 end_of_firstline_idx_ = whitespace_4_idx_; 686} 687 688void BalsaHeaders::SetRequestMethod(const base::StringPiece& method) { 689 // This is the first of the three parts of the firstline. 690 if (method.size() <= (whitespace_2_idx_ - non_whitespace_1_idx_)) { 691 non_whitespace_1_idx_ = whitespace_2_idx_ - method.size(); 692 char* stream_begin = GetPtr(firstline_buffer_base_idx_); 693 memcpy(stream_begin + non_whitespace_1_idx_, 694 method.data(), 695 method.size()); 696 } else { 697 // The new method is too large to fit in the space available for the old 698 // one, so we have to reformat the firstline. 699 SetFirstlineFromStringPieces(method, request_uri(), request_version()); 700 } 701} 702 703void BalsaHeaders::SetResponseVersion(const base::StringPiece& version) { 704 // Note: There is no difference between request_method() and 705 // response_Version(). Thus, a function to set one is equivalent to a 706 // function to set the other. We maintain two functions for this as it is 707 // much more descriptive, and makes code more understandable. 708 SetRequestMethod(version); 709} 710 711void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) { 712 SetFirstlineFromStringPieces(request_method(), uri, request_version()); 713} 714 715void BalsaHeaders::SetResponseCode(const base::StringPiece& code) { 716 // Note: There is no difference between request_uri() and response_code(). 717 // Thus, a function to set one is equivalent to a function to set the other. 718 // We maintain two functions for this as it is much more descriptive, and 719 // makes code more understandable. 720 SetRequestUri(code); 721} 722 723void BalsaHeaders::SetParsedResponseCodeAndUpdateFirstline( 724 size_t parsed_response_code) { 725 char buffer[kFastToBufferSize]; 726 int len_converted = snprintf(buffer, sizeof(buffer), 727 "%d", parsed_response_code); 728 CHECK_GT(len_converted, 0); 729 SetResponseCode(base::StringPiece(buffer, len_converted)); 730} 731 732void BalsaHeaders::SetRequestVersion(const base::StringPiece& version) { 733 // This is the last of the three parts of the firstline. 734 // Since whitespace_3_idx and non_whitespace_3_idx may point to the same 735 // place, we ensure below that any available space includes space for a 736 // litteral space (' ') character between the second component and the third 737 // component. If the space between whitespace_3_idx_ and 738 // end_of_firstline_idx_ is >= to version.size() + 1 (for the space), then we 739 // can update the firstline in-place. 740 char* stream_begin = GetPtr(firstline_buffer_base_idx_); 741 if (version.size() + 1 <= end_of_firstline_idx_ - whitespace_3_idx_) { 742 *(stream_begin + whitespace_3_idx_) = kSpaceChar; 743 non_whitespace_3_idx_ = whitespace_3_idx_ + 1; 744 whitespace_4_idx_ = non_whitespace_3_idx_ + version.size(); 745 memcpy(stream_begin + non_whitespace_3_idx_, 746 version.data(), 747 version.size()); 748 } else { 749 // The new version is to large to fit in the space available for the old 750 // one, so we have to reformat the firstline. 751 SetFirstlineFromStringPieces(request_method(), request_uri(), version); 752 } 753} 754 755void BalsaHeaders::SetResponseReasonPhrase(const base::StringPiece& reason) { 756 // Note: There is no difference between request_version() and 757 // response_reason_phrase(). Thus, a function to set one is equivalent to a 758 // function to set the other. We maintain two functions for this as it is 759 // much more descriptive, and makes code more understandable. 760 SetRequestVersion(reason); 761} 762 763} // namespace net 764