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