OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "net/spdy/hpack_decoder.h" | |
6 | |
7 #include "base/basictypes.h" | |
8 #include "base/logging.h" | |
9 #include "net/spdy/hpack_constants.h" | |
10 #include "net/spdy/hpack_output_stream.h" | |
11 | |
12 namespace net { | |
13 | |
14 using base::StringPiece; | |
15 using std::string; | |
16 | |
17 namespace { | |
18 | |
19 const char kCookieKey[] = "cookie"; | |
20 | |
21 } // namespace | |
22 | |
23 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table) | |
24 : max_string_literal_size_(kDefaultMaxStringLiteralSize), | |
25 regular_header_seen_(false), | |
26 huffman_table_(table) {} | |
27 | |
28 HpackDecoder::~HpackDecoder() {} | |
29 | |
30 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id, | |
31 const char* headers_data, | |
32 size_t headers_data_length) { | |
33 decoded_block_.clear(); | |
34 | |
35 size_t new_size = headers_block_buffer_.size() + headers_data_length; | |
36 if (new_size > kMaxDecodeBufferSize) { | |
37 return false; | |
38 } | |
39 headers_block_buffer_.insert(headers_block_buffer_.end(), | |
40 headers_data, | |
41 headers_data + headers_data_length); | |
42 return true; | |
43 } | |
44 | |
45 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) { | |
46 HpackInputStream input_stream(max_string_literal_size_, | |
47 headers_block_buffer_); | |
48 regular_header_seen_ = false; | |
49 while (input_stream.HasMoreData()) { | |
50 if (!DecodeNextOpcode(&input_stream)) { | |
51 headers_block_buffer_.clear(); | |
52 return false; | |
53 } | |
54 } | |
55 headers_block_buffer_.clear(); | |
56 | |
57 // Emit the Cookie header, if any crumbles were encountered. | |
58 if (!cookie_value_.empty()) { | |
59 decoded_block_[kCookieKey] = cookie_value_; | |
60 cookie_value_.clear(); | |
61 } | |
62 return true; | |
63 } | |
64 | |
65 bool HpackDecoder::HandleHeaderRepresentation(StringPiece name, | |
66 StringPiece value) { | |
67 typedef std::pair<std::map<string, string>::iterator, bool> InsertResult; | |
68 | |
69 // Fail if pseudo-header follows regular header. | |
70 if (name.size() > 0) { | |
71 if (name[0] == kPseudoHeaderPrefix) { | |
72 if (regular_header_seen_) return false; | |
73 } else { | |
74 regular_header_seen_ = true; | |
75 } | |
76 } | |
77 | |
78 if (name == kCookieKey) { | |
79 if (cookie_value_.empty()) { | |
80 cookie_value_.assign(value.data(), value.size()); | |
81 } else { | |
82 cookie_value_ += "; "; | |
83 cookie_value_.insert(cookie_value_.end(), value.begin(), value.end()); | |
84 } | |
85 } else { | |
86 InsertResult result = decoded_block_.insert( | |
87 std::make_pair(name.as_string(), value.as_string())); | |
88 if (!result.second) { | |
89 result.first->second.push_back('\0'); | |
90 result.first->second.insert(result.first->second.end(), | |
91 value.begin(), | |
92 value.end()); | |
93 } | |
94 } | |
95 return true; | |
96 } | |
97 | |
98 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) { | |
99 // Implements 7.1: Indexed Header Field Representation. | |
100 if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) { | |
101 return DecodeNextIndexedHeader(input_stream); | |
102 } | |
103 // Implements 7.2.1: Literal Header Field with Incremental Indexing. | |
104 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) { | |
105 return DecodeNextLiteralHeader(input_stream, true); | |
106 } | |
107 // Implements 7.2.2: Literal Header Field without Indexing. | |
108 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) { | |
109 return DecodeNextLiteralHeader(input_stream, false); | |
110 } | |
111 // Implements 7.2.3: Literal Header Field never Indexed. | |
112 // TODO(jgraettinger): Preserve the never-indexed bit. | |
113 if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) { | |
114 return DecodeNextLiteralHeader(input_stream, false); | |
115 } | |
116 // Implements 7.3: Header Table Size Update. | |
117 if (input_stream->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode)) { | |
118 return DecodeNextHeaderTableSizeUpdate(input_stream); | |
119 } | |
120 // Unrecognized opcode. | |
121 return false; | |
122 } | |
123 | |
124 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate( | |
125 HpackInputStream* input_stream) { | |
126 uint32 size = 0; | |
127 if (!input_stream->DecodeNextUint32(&size)) { | |
128 return false; | |
129 } | |
130 if (size > header_table_.settings_size_bound()) { | |
131 return false; | |
132 } | |
133 header_table_.SetMaxSize(size); | |
134 return true; | |
135 } | |
136 | |
137 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) { | |
138 uint32 index = 0; | |
139 if (!input_stream->DecodeNextUint32(&index)) | |
140 return false; | |
141 | |
142 const HpackEntry* entry = header_table_.GetByIndex(index); | |
143 if (entry == NULL) | |
144 return false; | |
145 | |
146 return HandleHeaderRepresentation(entry->name(), entry->value()); | |
147 } | |
148 | |
149 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream, | |
150 bool should_index) { | |
151 StringPiece name; | |
152 if (!DecodeNextName(input_stream, &name)) | |
153 return false; | |
154 | |
155 StringPiece value; | |
156 if (!DecodeNextStringLiteral(input_stream, false, &value)) | |
157 return false; | |
158 | |
159 if (!HandleHeaderRepresentation(name, value)) return false; | |
160 | |
161 if (!should_index) | |
162 return true; | |
163 | |
164 ignore_result(header_table_.TryAddEntry(name, value)); | |
165 return true; | |
166 } | |
167 | |
168 bool HpackDecoder::DecodeNextName( | |
169 HpackInputStream* input_stream, StringPiece* next_name) { | |
170 uint32 index_or_zero = 0; | |
171 if (!input_stream->DecodeNextUint32(&index_or_zero)) | |
172 return false; | |
173 | |
174 if (index_or_zero == 0) | |
175 return DecodeNextStringLiteral(input_stream, true, next_name); | |
176 | |
177 const HpackEntry* entry = header_table_.GetByIndex(index_or_zero); | |
178 if (entry == NULL) { | |
179 return false; | |
180 } else if (entry->IsStatic()) { | |
181 *next_name = entry->name(); | |
182 } else { | |
183 // |entry| could be evicted as part of this insertion. Preemptively copy. | |
184 key_buffer_.assign(entry->name()); | |
185 *next_name = key_buffer_; | |
186 } | |
187 return true; | |
188 } | |
189 | |
190 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, | |
191 bool is_key, | |
192 StringPiece* output) { | |
193 if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { | |
194 string* buffer = is_key ? &key_buffer_ : &value_buffer_; | |
195 bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer); | |
196 *output = StringPiece(*buffer); | |
197 return result; | |
198 } else if (input_stream->MatchPrefixAndConsume( | |
199 kStringLiteralIdentityEncoded)) { | |
200 return input_stream->DecodeNextIdentityString(output); | |
201 } else { | |
202 return false; | |
203 } | |
204 } | |
205 | |
206 } // namespace net | |
OLD | NEW |