Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / spdy / spdy_headers_block_parser.cc
blob154f6477356a444da5e397d2422e0abe30cff219
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/spdy/spdy_headers_block_parser.h"
7 #include "base/sys_byteorder.h"
9 namespace net {
10 namespace {
12 // 0 is invalid according to both the SPDY 3.1 and HTTP/2 specifications.
13 const SpdyStreamId kInvalidStreamId = 0;
15 } // anonymous namespace
17 const size_t SpdyHeadersBlockParser::kMaximumFieldLength = 16 * 1024;
19 SpdyHeadersBlockParser::SpdyHeadersBlockParser(
20 SpdyMajorVersion spdy_version,
21 SpdyHeadersHandlerInterface* handler)
22 : state_(READING_HEADER_BLOCK_LEN),
23 length_field_size_(LengthFieldSizeForVersion(spdy_version)),
24 max_headers_in_block_(MaxNumberOfHeadersForVersion(spdy_version)),
25 total_bytes_received_(0),
26 remaining_key_value_pairs_for_frame_(0),
27 handler_(handler),
28 stream_id_(kInvalidStreamId),
29 error_(NO_PARSER_ERROR),
30 spdy_version_(spdy_version) {
31 // The handler that we set must not be NULL.
32 DCHECK(handler_ != NULL);
35 SpdyHeadersBlockParser::~SpdyHeadersBlockParser() {}
37 bool SpdyHeadersBlockParser::HandleControlFrameHeadersData(
38 SpdyStreamId stream_id,
39 const char* headers_data,
40 size_t headers_data_length) {
41 if (error_ == NEED_MORE_DATA) {
42 error_ = NO_PARSER_ERROR;
44 if (error_ != NO_PARSER_ERROR) {
45 LOG(DFATAL) << "Unexpected error: " << error_;
46 return false;
49 // If this is the first call with the current header block,
50 // save its stream id.
51 if (state_ == READING_HEADER_BLOCK_LEN && stream_id_ == kInvalidStreamId) {
52 stream_id_ = stream_id;
54 if (stream_id != stream_id_) {
55 LOG(DFATAL) << "Unexpected stream id: " << stream_id << " (expected "
56 << stream_id_ << ")";
57 error_ = UNEXPECTED_STREAM_ID;
58 return false;
60 if (stream_id_ == kInvalidStreamId) {
61 LOG(DFATAL) << "Expected nonzero stream id, saw: " << stream_id_;
62 error_ = UNEXPECTED_STREAM_ID;
63 return false;
65 total_bytes_received_ += headers_data_length;
67 SpdyPinnableBufferPiece prefix, key, value;
68 // Simultaneously tie lifetimes to the stack, and clear member variables.
69 prefix.Swap(&headers_block_prefix_);
70 key.Swap(&key_);
72 // Apply the parsing state machine to the remaining prefix
73 // from last invocation, plus newly-available headers data.
74 Reader reader(prefix.buffer(), prefix.length(),
75 headers_data, headers_data_length);
76 while (error_ == NO_PARSER_ERROR) {
77 ParserState next_state(FINISHED_HEADER);
79 switch (state_) {
80 case READING_HEADER_BLOCK_LEN:
81 next_state = READING_KEY_LEN;
82 ParseBlockLength(&reader);
83 break;
84 case READING_KEY_LEN:
85 next_state = READING_KEY;
86 ParseFieldLength(&reader);
87 break;
88 case READING_KEY:
89 next_state = READING_VALUE_LEN;
90 if (!reader.ReadN(next_field_length_, &key)) {
91 error_ = NEED_MORE_DATA;
93 break;
94 case READING_VALUE_LEN:
95 next_state = READING_VALUE;
96 ParseFieldLength(&reader);
97 break;
98 case READING_VALUE:
99 next_state = FINISHED_HEADER;
100 if (!reader.ReadN(next_field_length_, &value)) {
101 error_ = NEED_MORE_DATA;
102 } else {
103 handler_->OnHeader(key, value);
105 break;
106 case FINISHED_HEADER:
107 // Prepare for next header or block.
108 if (--remaining_key_value_pairs_for_frame_ > 0) {
109 next_state = READING_KEY_LEN;
110 } else {
111 next_state = READING_HEADER_BLOCK_LEN;
112 handler_->OnHeaderBlockEnd(total_bytes_received_);
113 stream_id_ = kInvalidStreamId;
114 // Expect to have consumed all buffer.
115 if (reader.Available() != 0) {
116 error_ = TOO_MUCH_DATA;
119 break;
122 if (error_ == NO_PARSER_ERROR) {
123 state_ = next_state;
125 if (next_state == READING_HEADER_BLOCK_LEN) {
126 // We completed reading a full header block. Return to caller.
127 total_bytes_received_ = 0;
128 break;
130 } else if (error_ == NEED_MORE_DATA) {
131 // We can't continue parsing until more data is available. Make copies of
132 // the key and buffer remainder, in preperation for the next invocation.
133 if (state_ > READING_KEY) {
134 key_.Swap(&key);
135 key_.Pin();
137 reader.ReadN(reader.Available(), &headers_block_prefix_);
138 headers_block_prefix_.Pin();
141 return error_ == NO_PARSER_ERROR;
144 void SpdyHeadersBlockParser::ParseBlockLength(Reader* reader) {
145 ParseLength(reader, &remaining_key_value_pairs_for_frame_);
146 if (error_ == NO_PARSER_ERROR &&
147 remaining_key_value_pairs_for_frame_ > max_headers_in_block_) {
148 error_ = HEADER_BLOCK_TOO_LARGE;
150 if (error_ == NO_PARSER_ERROR) {
151 handler_->OnHeaderBlock(remaining_key_value_pairs_for_frame_);
155 void SpdyHeadersBlockParser::ParseFieldLength(Reader* reader) {
156 ParseLength(reader, &next_field_length_);
157 if (error_ == NO_PARSER_ERROR && next_field_length_ > kMaximumFieldLength) {
158 error_ = HEADER_FIELD_TOO_LARGE;
162 void SpdyHeadersBlockParser::ParseLength(Reader* reader,
163 uint32_t* parsed_length) {
164 char buffer[] = {0, 0, 0, 0};
165 if (!reader->ReadN(length_field_size_, buffer)) {
166 error_ = NEED_MORE_DATA;
167 return;
169 // Convert from network to host order and return the parsed out integer.
170 if (length_field_size_ == sizeof(uint32_t)) {
171 *parsed_length = ntohl(*reinterpret_cast<const uint32_t *>(buffer));
172 } else {
173 *parsed_length = ntohs(*reinterpret_cast<const uint16_t *>(buffer));
177 size_t SpdyHeadersBlockParser::LengthFieldSizeForVersion(
178 SpdyMajorVersion spdy_version) {
179 if (spdy_version < SPDY3) {
180 return sizeof(uint16_t);
182 return sizeof(uint32_t);
185 size_t SpdyHeadersBlockParser::MaxNumberOfHeadersForVersion(
186 SpdyMajorVersion spdy_version) {
187 // Account for the length of the header block field.
188 size_t max_bytes_for_headers =
189 kMaximumFieldLength - LengthFieldSizeForVersion(spdy_version);
191 // A minimal size header is twice the length field size (and has a
192 // zero-lengthed key and a zero-lengthed value).
193 return max_bytes_for_headers / (2 * LengthFieldSizeForVersion(spdy_version));
196 } // namespace net