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_token_utils.h"
6#include "net/tools/balsa/string_piece_utils.h"
7
8namespace net {
9
10inline void BalsaHeadersTokenUtils::TokenizeHeaderLine(
11    const BalsaHeaders& headers,
12    const BalsaHeaders::HeaderLineDescription& header_line,
13    BalsaHeaders::HeaderTokenList* tokens) {
14  CHECK(tokens);
15
16  // Find where this line is stored
17  const char* stream_begin = headers.GetPtr(header_line.buffer_base_idx);
18
19  // Determine the boundaries of the value
20  const char* value_begin = stream_begin + header_line.value_begin_idx;
21  const char* line_end = stream_begin + header_line.last_char_idx;
22
23  // Tokenize
24  ParseTokenList(value_begin, line_end, tokens);
25}
26
27void BalsaHeadersTokenUtils::RemoveLastTokenFromHeaderValue(
28    const base::StringPiece& key, BalsaHeaders* headers) {
29  BalsaHeaders::HeaderLines::iterator it =
30      headers->GetHeaderLinesIterator(key, headers->header_lines_.begin());
31  if (it == headers->header_lines_.end()) {
32    DLOG(WARNING) << "Attempting to remove last token from a non-existent "
33                  << "header \"" << key << "\"";
34    return;
35  }
36
37  // Find the last line with that key.
38  BalsaHeaders::HeaderLines::iterator header_line;
39  do {
40    header_line = it;
41    it = headers->GetHeaderLinesIterator(key, it + 1);
42  }
43  while (it != headers->header_lines_.end());
44
45  // Tokenize just that line.
46  BalsaHeaders::HeaderTokenList tokens;
47  TokenizeHeaderLine(*headers, *header_line, &tokens);
48
49  if (tokens.empty()) {
50    DLOG(WARNING) << "Attempting to remove a token from an empty header value "
51                  << "for header \"" << key << "\"";
52    header_line->skip = true;  // remove the whole line
53  } else if (tokens.size() == 1) {
54    header_line->skip = true;  // remove the whole line
55  } else {
56    // Shrink the line size and leave the extra data in the buffer.
57    const base::StringPiece& new_last_token = tokens[tokens.size() - 2];
58    const char* last_char_address =
59        new_last_token.data() + new_last_token.size() - 1;
60    const char* stream_begin = headers->GetPtr(header_line->buffer_base_idx);
61
62    header_line->last_char_idx = last_char_address - stream_begin + 1;
63  }
64}
65
66bool BalsaHeadersTokenUtils::CheckHeaderForLastToken(
67    const BalsaHeaders& headers,
68    const base::StringPiece& key,
69    const base::StringPiece& token) {
70  BalsaHeaders::const_header_lines_key_iterator it =
71      headers.GetIteratorForKey(key);
72  if (it == headers.header_lines_key_end())
73    return false;
74
75  // Find the last line
76  BalsaHeaders::const_header_lines_key_iterator header_line = it;
77  do {
78    header_line = it;
79    ++it;
80  }
81  while (it != headers.header_lines_key_end());
82
83  // Tokenize just that line
84  BalsaHeaders::HeaderTokenList tokens;
85  ParseTokenList(header_line->second.begin(), header_line->second.end(),
86                 &tokens);
87
88  return !tokens.empty() &&
89      StringPieceUtils::StartsWithIgnoreCase(tokens.back(), token);
90}
91
92void BalsaHeadersTokenUtils::TokenizeHeaderValue(
93    const BalsaHeaders& headers,
94    const base::StringPiece& key,
95    BalsaHeaders::HeaderTokenList* tokens) {
96  CHECK(tokens);
97  tokens->clear();
98
99  // We may have more then 1 line with the same header key. Tokenize them all
100  // and stick all the tokens into the same list.
101  for (BalsaHeaders::const_header_lines_key_iterator header_line =
102           headers.GetIteratorForKey(key);
103       header_line != headers.header_lines_key_end(); ++header_line) {
104    ParseTokenList(header_line->second.begin(), header_line->second.end(),
105                   tokens);
106  }
107}
108
109void BalsaHeadersTokenUtils::ParseTokenList(
110    const char* start,
111    const char* end,
112    BalsaHeaders::HeaderTokenList* tokens) {
113  if (start == end) {
114    return;
115  }
116  while (true) {
117    // search for first nonwhitespace, non separator char.
118    while (*start == ',' || *start <= ' ') {
119      ++start;
120      if (start == end) {
121        return;
122      }
123    }
124    // found. marked.
125    const char* nws = start;
126
127    // search for next whitspace or separator char.
128    while (*start != ',' && *start > ' ') {
129      ++start;
130      if (start == end) {
131        if (nws != start) {
132          tokens->push_back(base::StringPiece(nws, start - nws));
133        }
134        return;
135      }
136    }
137    tokens->push_back(base::StringPiece(nws, start - nws));
138  }
139}
140
141}  // namespace net
142
143