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