blob: 2a302c187621bfe25bc947c51c4a5a0c97779e73 [file] [log] [blame]
// 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 "base/sys_byteorder.h"
namespace net {
const size_t SpdyHeadersBlockParser::kMaximumFieldLength = 16 * 1024;
SpdyHeadersBlockParser::SpdyHeadersBlockParser(
SpdyMajorVersion spdy_version,
SpdyHeadersHandlerInterface* handler) :
state_(READING_HEADER_BLOCK_LEN),
length_field_size_(LengthFieldSizeForVersion(spdy_version)),
max_headers_in_block_(MaxNumberOfHeadersForVersion(spdy_version)),
total_bytes_received_(0),
remaining_key_value_pairs_for_frame_(0),
handler_(handler),
error_(OK) {
// The handler that we set must not be NULL.
DCHECK(handler_ != NULL);
}
SpdyHeadersBlockParser::~SpdyHeadersBlockParser() {}
bool SpdyHeadersBlockParser::HandleControlFrameHeadersData(
SpdyStreamId stream_id,
const char* headers_data,
size_t headers_data_length) {
if (error_ == NEED_MORE_DATA) {
error_ = OK;
}
CHECK_EQ(error_, OK);
// If this is the first call with the current header block,
// save its stream id.
if (state_ == READING_HEADER_BLOCK_LEN) {
stream_id_ = stream_id;
}
CHECK_EQ(stream_id_, stream_id);
total_bytes_received_ += headers_data_length;
SpdyPinnableBufferPiece prefix, key, value;
// Simultaneously tie lifetimes to the stack, and clear member variables.
prefix.Swap(&headers_block_prefix_);
key.Swap(&key_);
// Apply the parsing state machine to the remaining prefix
// from last invocation, plus newly-available headers data.
Reader reader(prefix.buffer(), prefix.length(),
headers_data, headers_data_length);
while (error_ == OK) {
ParserState next_state(FINISHED_HEADER);
switch (state_) {
case READING_HEADER_BLOCK_LEN:
next_state = READING_KEY_LEN;
ParseBlockLength(&reader);
break;
case READING_KEY_LEN:
next_state = READING_KEY;
ParseFieldLength(&reader);
break;
case READING_KEY:
next_state = READING_VALUE_LEN;
if (!reader.ReadN(next_field_length_, &key)) {
error_ = NEED_MORE_DATA;
}
break;
case READING_VALUE_LEN:
next_state = READING_VALUE;
ParseFieldLength(&reader);
break;
case READING_VALUE:
next_state = FINISHED_HEADER;
if (!reader.ReadN(next_field_length_, &value)) {
error_ = NEED_MORE_DATA;
} else {
handler_->OnHeader(stream_id, key, value);
}
break;
case FINISHED_HEADER:
// Prepare for next header or block.
if (--remaining_key_value_pairs_for_frame_ > 0) {
next_state = READING_KEY_LEN;
} else {
next_state = READING_HEADER_BLOCK_LEN;
handler_->OnHeaderBlockEnd(stream_id, total_bytes_received_);
// Expect to have consumed all buffer.
if (reader.Available() != 0) {
error_ = TOO_MUCH_DATA;
}
}
break;
default:
CHECK(false) << "Not reached.";
}
if (error_ == OK) {
state_ = next_state;
if (next_state == READING_HEADER_BLOCK_LEN) {
// We completed reading a full header block. Return to caller.
total_bytes_received_ = 0;
break;
}
} else if (error_ == NEED_MORE_DATA) {
// We can't continue parsing until more data is available. Make copies of
// the key and buffer remainder, in preperation for the next invocation.
if (state_ > READING_KEY) {
key_.Swap(&key);
key_.Pin();
}
reader.ReadN(reader.Available(), &headers_block_prefix_);
headers_block_prefix_.Pin();
}
}
return error_ == OK;
}
void SpdyHeadersBlockParser::ParseBlockLength(Reader* reader) {
ParseLength(reader, &remaining_key_value_pairs_for_frame_);
if (error_ == OK &&
remaining_key_value_pairs_for_frame_ > max_headers_in_block_) {
error_ = HEADER_BLOCK_TOO_LARGE;
}
if (error_ == OK) {
handler_->OnHeaderBlock(stream_id_, remaining_key_value_pairs_for_frame_);
}
}
void SpdyHeadersBlockParser::ParseFieldLength(Reader* reader) {
ParseLength(reader, &next_field_length_);
if (error_ == OK &&
next_field_length_ > kMaximumFieldLength) {
error_ = HEADER_FIELD_TOO_LARGE;
}
}
void SpdyHeadersBlockParser::ParseLength(Reader* reader,
uint32_t* parsed_length) {
char buffer[] = {0, 0, 0, 0};
if (!reader->ReadN(length_field_size_, buffer)) {
error_ = NEED_MORE_DATA;
return;
}
// Convert from network to host order and return the parsed out integer.
if (length_field_size_ == sizeof(uint32_t)) {
*parsed_length = ntohl(*reinterpret_cast<const uint32_t *>(buffer));
} else {
*parsed_length = ntohs(*reinterpret_cast<const uint16_t *>(buffer));
}
}
void SpdyHeadersBlockParser::Reset() {
{
SpdyPinnableBufferPiece empty;
headers_block_prefix_.Swap(&empty);
}
{
SpdyPinnableBufferPiece empty;
key_.Swap(&empty);
}
error_ = OK;
state_ = READING_HEADER_BLOCK_LEN;
stream_id_ = 0;
total_bytes_received_ = 0;
}
size_t SpdyHeadersBlockParser::LengthFieldSizeForVersion(
SpdyMajorVersion spdy_version) {
if (spdy_version < SPDY3) {
return sizeof(uint16_t);
}
return sizeof(uint32_t);
}
size_t SpdyHeadersBlockParser::MaxNumberOfHeadersForVersion(
SpdyMajorVersion spdy_version) {
// Account for the length of the header block field.
size_t max_bytes_for_headers =
kMaximumFieldLength - LengthFieldSizeForVersion(spdy_version);
// A minimal size header is twice the length field size (and has a
// zero-lengthed key and a zero-lengthed value).
return max_bytes_for_headers / (2 * LengthFieldSizeForVersion(spdy_version));
}
} // namespace net