diff options
Diffstat (limited to 'net/spdy/spdy_headers_block_parser.cc')
-rw-r--r-- | net/spdy/spdy_headers_block_parser.cc | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/net/spdy/spdy_headers_block_parser.cc b/net/spdy/spdy_headers_block_parser.cc new file mode 100644 index 0000000..30bc00c --- /dev/null +++ b/net/spdy/spdy_headers_block_parser.cc @@ -0,0 +1,181 @@ +// Copyright 2014 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/spdy/spdy_headers_block_parser.h" + +#include <memory> + +#include "base/sys_byteorder.h" + +namespace net { + +SpdyHeadersBlockParserReader::SpdyHeadersBlockParserReader( + base::StringPiece prefix, + base::StringPiece suffix) + : prefix_(prefix), + suffix_(suffix), + in_suffix_(false), + offset_(0) {} + +size_t SpdyHeadersBlockParserReader::Available() { + if (in_suffix_) { + return suffix_.length() - offset_; + } else { + return prefix_.length() + suffix_.length() - offset_; + } +} + +bool SpdyHeadersBlockParserReader::ReadN(size_t count, char* out) { + if (Available() < count) + return false; + + if (!in_suffix_ && count > (prefix_.length() - offset_)) { + count -= prefix_.length() - offset_; + out = std::copy(prefix_.begin() + offset_, prefix_.end(), out); + in_suffix_ = true; + offset_ = 0; + // Fallthrough to suffix read. + } else if (!in_suffix_) { + // Read is satisfied by the prefix. + DCHECK_GE(prefix_.length() - offset_, count); + std::copy(prefix_.begin() + offset_, + prefix_.begin() + offset_ + count, + out); + offset_ += count; + return true; + } + // Read is satisfied by the suffix. + DCHECK(in_suffix_); + DCHECK_GE(suffix_.length() - offset_, count); + std::copy(suffix_.begin() + offset_, + suffix_.begin() + offset_ + count, + out); + offset_ += count; + return true; +} + +std::vector<char> SpdyHeadersBlockParserReader::Remainder() { + std::vector<char> remainder(Available(), '\0'); + if (remainder.size()) { + ReadN(remainder.size(), &remainder[0]); + } + DCHECK_EQ(0u, Available()); + return remainder; +} + +SpdyHeadersBlockParser::SpdyHeadersBlockParser(KeyValueHandler* handler) : + state_(READING_HEADER_BLOCK_LEN), + remaining_key_value_pairs_for_frame_(0), + next_field_len_(0), + handler_(handler) { +} + +SpdyHeadersBlockParser::~SpdyHeadersBlockParser() {} + +bool SpdyHeadersBlockParser::ParseUInt32(Reader* reader, + uint32_t* parsed_value) { + // Are there enough bytes available? + if (reader->Available() < sizeof(uint32_t)) { + return false; + } + + // Read the required bytes, convert from network to host + // order and return the parsed out integer. + char buf[sizeof(uint32_t)]; + reader->ReadN(sizeof(uint32_t), buf); + *parsed_value = base::NetToHost32(*reinterpret_cast<const uint32_t *>(buf)); + return true; +} + +void SpdyHeadersBlockParser::Reset() { + // Clear any saved state about the last headers block. + headers_block_prefix_.clear(); + state_ = READING_HEADER_BLOCK_LEN; +} + +void SpdyHeadersBlockParser::HandleControlFrameHeadersData( + const char* headers_data, size_t len) { + // Only do something if we received anything new. + if (len == 0) { + return; + } + + // Reader avoids copying data. + base::StringPiece prefix; + if (headers_block_prefix_.size()) { + prefix.set(&headers_block_prefix_[0], headers_block_prefix_.size()); + } + Reader reader(prefix, base::StringPiece(headers_data, len)); + + // If we didn't parse out yet the number of key-value pairs in this + // headers block, try to do it now (succeeds if we received enough bytes). + if (state_ == READING_HEADER_BLOCK_LEN) { + if (ParseUInt32(&reader, &remaining_key_value_pairs_for_frame_)) { + state_ = READING_KEY_LEN; + } else { + headers_block_prefix_ = reader.Remainder(); + return; + } + } + + // Parse out and handle the key-value pairs. + while (remaining_key_value_pairs_for_frame_ > 0) { + // Parse the key-value length, in case we don't already have it. + if ((state_ == READING_KEY_LEN) || (state_ == READING_VALUE_LEN)) { + if (ParseUInt32(&reader, &next_field_len_)) { + state_ == READING_KEY_LEN ? state_ = READING_KEY : + state_ = READING_VALUE; + } else { + // Did not receive enough bytes. + break; + } + } + + // Parse the next field if we received enough bytes. + if (reader.Available() >= next_field_len_) { + // Copy the field from the cord. + char* key_value_buf(new char[next_field_len_]); + reader.ReadN(next_field_len_, key_value_buf); + + // Is this field a key? + if (state_ == READING_KEY) { + current_key.reset(key_value_buf); + key_len_ = next_field_len_; + state_ = READING_VALUE_LEN; + + } else if (state_ == READING_VALUE) { + // We already had the key, now we got its value. + current_value.reset(key_value_buf); + + // Call the handler for the key-value pair that we received. + handler_->OnKeyValuePair( + base::StringPiece(current_key.get(), key_len_), + base::StringPiece(current_value.get(), next_field_len_)); + + // Free allocated key and value strings. + current_key.reset(); + current_value.reset(); + + // Finished handling a key-value pair. + remaining_key_value_pairs_for_frame_--; + + // Finished handling a header, prepare for the next one. + state_ = READING_KEY_LEN; + } + } else { + // Did not receive enough bytes. + break; + } + } + + // Unread suffix becomes the prefix upon next invocation. + headers_block_prefix_ = reader.Remainder(); + + // Did we finish handling the current block? + if (remaining_key_value_pairs_for_frame_ == 0) { + Reset(); + } +} + +} // namespace net |