| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/tools/balsa/balsa_headers_token_utils.h" |
| #include "net/tools/balsa/string_piece_utils.h" |
| |
| namespace net { |
| |
| inline void BalsaHeadersTokenUtils::TokenizeHeaderLine( |
| const BalsaHeaders& headers, |
| const BalsaHeaders::HeaderLineDescription& header_line, |
| BalsaHeaders::HeaderTokenList* tokens) { |
| CHECK(tokens); |
| |
| // Find where this line is stored |
| const char* stream_begin = headers.GetPtr(header_line.buffer_base_idx); |
| |
| // Determine the boundaries of the value |
| const char* value_begin = stream_begin + header_line.value_begin_idx; |
| const char* line_end = stream_begin + header_line.last_char_idx; |
| |
| // Tokenize |
| ParseTokenList(value_begin, line_end, tokens); |
| } |
| |
| void BalsaHeadersTokenUtils::RemoveLastTokenFromHeaderValue( |
| const base::StringPiece& key, BalsaHeaders* headers) { |
| BalsaHeaders::HeaderLines::iterator it = |
| headers->GetHeaderLinesIterator(key, headers->header_lines_.begin()); |
| if (it == headers->header_lines_.end()) { |
| DLOG(WARNING) << "Attempting to remove last token from a non-existent " |
| << "header \"" << key << "\""; |
| return; |
| } |
| |
| // Find the last line with that key. |
| BalsaHeaders::HeaderLines::iterator header_line; |
| do { |
| header_line = it; |
| it = headers->GetHeaderLinesIterator(key, it + 1); |
| } |
| while (it != headers->header_lines_.end()); |
| |
| // Tokenize just that line. |
| BalsaHeaders::HeaderTokenList tokens; |
| TokenizeHeaderLine(*headers, *header_line, &tokens); |
| |
| if (tokens.empty()) { |
| DLOG(WARNING) << "Attempting to remove a token from an empty header value " |
| << "for header \"" << key << "\""; |
| header_line->skip = true; // remove the whole line |
| } else if (tokens.size() == 1) { |
| header_line->skip = true; // remove the whole line |
| } else { |
| // Shrink the line size and leave the extra data in the buffer. |
| const base::StringPiece& new_last_token = tokens[tokens.size() - 2]; |
| const char* last_char_address = |
| new_last_token.data() + new_last_token.size() - 1; |
| const char* stream_begin = headers->GetPtr(header_line->buffer_base_idx); |
| |
| header_line->last_char_idx = last_char_address - stream_begin + 1; |
| } |
| } |
| |
| bool BalsaHeadersTokenUtils::CheckHeaderForLastToken( |
| const BalsaHeaders& headers, |
| const base::StringPiece& key, |
| const base::StringPiece& token) { |
| BalsaHeaders::const_header_lines_key_iterator it = |
| headers.GetIteratorForKey(key); |
| if (it == headers.header_lines_key_end()) |
| return false; |
| |
| // Find the last line |
| BalsaHeaders::const_header_lines_key_iterator header_line = it; |
| do { |
| header_line = it; |
| ++it; |
| } |
| while (it != headers.header_lines_key_end()); |
| |
| // Tokenize just that line |
| BalsaHeaders::HeaderTokenList tokens; |
| ParseTokenList(header_line->second.begin(), header_line->second.end(), |
| &tokens); |
| |
| return !tokens.empty() && |
| StringPieceUtils::StartsWithIgnoreCase(tokens.back(), token); |
| } |
| |
| void BalsaHeadersTokenUtils::TokenizeHeaderValue( |
| const BalsaHeaders& headers, |
| const base::StringPiece& key, |
| BalsaHeaders::HeaderTokenList* tokens) { |
| CHECK(tokens); |
| tokens->clear(); |
| |
| // We may have more then 1 line with the same header key. Tokenize them all |
| // and stick all the tokens into the same list. |
| for (BalsaHeaders::const_header_lines_key_iterator header_line = |
| headers.GetIteratorForKey(key); |
| header_line != headers.header_lines_key_end(); ++header_line) { |
| ParseTokenList(header_line->second.begin(), header_line->second.end(), |
| tokens); |
| } |
| } |
| |
| void BalsaHeadersTokenUtils::ParseTokenList( |
| const char* start, |
| const char* end, |
| BalsaHeaders::HeaderTokenList* tokens) { |
| if (start == end) { |
| return; |
| } |
| while (true) { |
| // search for first nonwhitespace, non separator char. |
| while (*start == ',' || *start <= ' ') { |
| ++start; |
| if (start == end) { |
| return; |
| } |
| } |
| // found. marked. |
| const char* nws = start; |
| |
| // search for next whitspace or separator char. |
| while (*start != ',' && *start > ' ') { |
| ++start; |
| if (start == end) { |
| if (nws != start) { |
| tokens->push_back(base::StringPiece(nws, start - nws)); |
| } |
| return; |
| } |
| } |
| tokens->push_back(base::StringPiece(nws, start - nws)); |
| } |
| } |
| |
| } // namespace net |
| |