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
InitMultivaluedHeaders()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
Clear()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
Swap(BalsaHeaders * other)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
CopyFrom(const BalsaHeaders & other)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
AddAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,HeaderLineDescription * d)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
AppendOrPrependAndMakeDescription(const base::StringPiece & key,const base::StringPiece & value,bool append,HeaderLineDescription * d)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'.
RemoveAllOfHeaderStartingAt(const base::StringPiece & key,HeaderLines::iterator 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
HackHeader(const base::StringPiece & key,const base::StringPiece & value)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
HackAppendToHeader(const base::StringPiece & key,const base::StringPiece & append_value)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
ReplaceOrAppendHeader(const base::StringPiece & key,const base::StringPiece & value)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
AppendHeader(const base::StringPiece & key,const base::StringPiece & value)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
AppendToHeader(const base::StringPiece & key,const base::StringPiece & value)314 void BalsaHeaders::AppendToHeader(const base::StringPiece& key,
315 const base::StringPiece& value) {
316 AppendOrPrependToHeader(key, value, true);
317 }
318
PrependToHeader(const base::StringPiece & key,const base::StringPiece & value)319 void BalsaHeaders::PrependToHeader(const base::StringPiece& key,
320 const base::StringPiece& value) {
321 AppendOrPrependToHeader(key, value, false);
322 }
323
GetValueFromHeaderLineDescription(const HeaderLineDescription & line) const324 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
GetHeader(const base::StringPiece & key) const331 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
GetHeaderPosition(const base::StringPiece & key) const346 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
GetIteratorForKey(const base::StringPiece & key) const358 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
AppendOrPrependToHeader(const base::StringPiece & key,const base::StringPiece & value,bool append)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
GetConstHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::const_iterator start) const390 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
GetHeaderLinesIteratorNoSkip(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)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
GetHeaderLinesIterator(const base::StringPiece & key,BalsaHeaders::HeaderLines::iterator start)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
GetAllOfHeader(const base::StringPiece & key,std::vector<base::StringPiece> * out) const456 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
HasNonEmptyHeader(const base::StringPiece & key) const464 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
GetAllOfHeaderAsString(const base::StringPiece & key,std::string * out) const473 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
IsMultivaluedHeader(const base::StringPiece & header)489 bool BalsaHeaders::IsMultivaluedHeader(const base::StringPiece& header) {
490 return g_multivalued_headers.find(header) != g_multivalued_headers.end();
491 }
492
RemoveAllOfHeader(const base::StringPiece & key)493 void BalsaHeaders::RemoveAllOfHeader(const base::StringPiece& key) {
494 HeaderLines::iterator it = GetHeaderLinesIterator(key, header_lines_.begin());
495 RemoveAllOfHeaderStartingAt(key, it);
496 }
497
RemoveAllHeadersWithPrefix(const base::StringPiece & key)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
GetMemoryUsedLowerBound() const516 size_t BalsaHeaders::GetMemoryUsedLowerBound() const {
517 return (sizeof(*this) +
518 balsa_buffer_.GetTotalBufferBlockSize() +
519 header_lines_.capacity() * sizeof(HeaderLineDescription));
520 }
521
GetSizeForWriteBuffer() const522 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
DumpToString(std::string * str) const540 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
SetFirstLine(const base::StringPiece & line)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
SetContentLength(size_t length)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
SetChunkEncoding(bool chunk_encode)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.
SetFirstlineFromStringPieces(const base::StringPiece & firstline_a,const base::StringPiece & firstline_b,const base::StringPiece & firstline_c)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
SetRequestMethod(const base::StringPiece & method)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
SetResponseVersion(const base::StringPiece & version)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
SetRequestUri(const base::StringPiece & uri)706 void BalsaHeaders::SetRequestUri(const base::StringPiece& uri) {
707 SetFirstlineFromStringPieces(request_method(), uri, request_version());
708 }
709
SetResponseCode(const base::StringPiece & code)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
SetParsedResponseCodeAndUpdateFirstline(size_t parsed_response_code)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
SetRequestVersion(const base::StringPiece & version)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
SetResponseReasonPhrase(const base::StringPiece & reason)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