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