blob: 154f6477356a444da5e397d2422e0abe30cff219 [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 {
namespace {
// 0 is invalid according to both the SPDY 3.1 and HTTP/2 specifications.
const SpdyStreamId kInvalidStreamId = 0;
} // anonymous namespace
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),
stream_id_(kInvalidStreamId),
error_(NO_PARSER_ERROR),
spdy_version_(spdy_version) {
// 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_ = NO_PARSER_ERROR;
}
if (error_ != NO_PARSER_ERROR) {
LOG(DFATAL) << "Unexpected error: " << error_;
return false;
}
// If this is the first call with the current header block,
// save its stream id.
if (state_ == READING_HEADER_BLOCK_LEN && stream_id_ == kInvalidStreamId) {
stream_id_ = stream_id;
}
if (stream_id != stream_id_) {
LOG(DFATAL) << "Unexpected stream id: " << stream_id << " (expected "
<< stream_id_ << ")";
error_ = UNEXPECTED_STREAM_ID;
return false;
}
if (stream_id_ == kInvalidStreamId) {
LOG(DFATAL) << "Expected nonzero stream id, saw: " << stream_id_;
error_ = UNEXPECTED_STREAM_ID;
return false;
}
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_ == NO_PARSER_ERROR) {
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(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(total_bytes_received_);
stream_id_ = kInvalidStreamId;
// Expect to have consumed all buffer.
if (reader.Available() != 0) {
error_ = TOO_MUCH_DATA;
}
}
break;
}
if (error_ == NO_PARSER_ERROR) {
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_ == NO_PARSER_ERROR;
}
void SpdyHeadersBlockParser::ParseBlockLength(Reader* reader) {
ParseLength(reader, &remaining_key_value_pairs_for_frame_);
if (error_ == NO_PARSER_ERROR &&
remaining_key_value_pairs_for_frame_ > max_headers_in_block_) {
error_ = HEADER_BLOCK_TOO_LARGE;
}
if (error_ == NO_PARSER_ERROR) {
handler_->OnHeaderBlock(remaining_key_value_pairs_for_frame_);
}
}
void SpdyHeadersBlockParser::ParseFieldLength(Reader* reader) {
ParseLength(reader, &next_field_length_);
if (error_ == NO_PARSER_ERROR && 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));
}
}
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