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