| // 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" |
| #include "net/spdy/spdy_bug_tracker.h" |
| #include "net/spdy/spdy_flags.h" |
| |
| namespace net { |
| namespace { |
| |
| // 0 is invalid according to both the SPDY 3.1 and HTTP/2 specifications. |
| const SpdyStreamId kInvalidStreamId = 0; |
| |
| } // anonymous namespace |
| |
| namespace { |
| const size_t kLengthFieldSize = sizeof(uint32_t); |
| } // anonymous namespace |
| |
| const size_t SpdyHeadersBlockParser::kMaximumFieldLength = 16 * 1024; |
| |
| SpdyHeadersBlockParser::SpdyHeadersBlockParser( |
| SpdyMajorVersion spdy_version, |
| SpdyHeadersHandlerInterface* handler) |
| : state_(READING_HEADER_BLOCK_LEN), |
| max_headers_in_block_(MaxNumberOfHeaders()), |
| 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) { |
| SPDY_BUG << "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_) { |
| SPDY_BUG << "Unexpected stream id: " << stream_id << " (expected " |
| << stream_id_ << ")"; |
| error_ = UNEXPECTED_STREAM_ID; |
| return false; |
| } |
| if (stream_id_ == kInvalidStreamId) { |
| SPDY_BUG << "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; |
| if (FLAGS_chromium_http2_flag_log_compressed_size) { |
| // We reach here in two cases: 1) Spdy3 or 2) HTTP/2 without hpack |
| // encoding. For the first case, we just log the uncompressed size |
| // since we are going to deprecate Spdy3 soon. For the second case, |
| // the compressed size is the same as the uncompressed size. |
| handler_->OnHeaderBlockEnd(total_bytes_received_, |
| total_bytes_received_); |
| } else { |
| 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_->OnHeaderBlockStart(); |
| } |
| } |
| |
| 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(kLengthFieldSize, buffer)) { |
| error_ = NEED_MORE_DATA; |
| return; |
| } |
| // Convert from network to host order and return the parsed out integer. |
| *parsed_length = |
| base::NetToHost32(*reinterpret_cast<const uint32_t*>(buffer)); |
| } |
| |
| size_t SpdyHeadersBlockParser::MaxNumberOfHeaders() { |
| // Account for the length of the header block field. |
| size_t max_bytes_for_headers = kMaximumFieldLength - kLengthFieldSize; |
| |
| // 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 * kLengthFieldSize); |
| } |
| |
| } // namespace net |