Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / net / spdy / hpack_decoder.cc
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 "base/strings/string_util.h"
10 #include "net/spdy/hpack_constants.h"
11 #include "net/spdy/hpack_output_stream.h"
12
13 namespace net {
14
15 using base::StringPiece;
16 using std::string;
17
18 namespace {
19
20 const uint8 kNoState = 0;
21 // Set on entries added to the reference set during this decoding.
22 const uint8 kReferencedThisEncoding = 1;
23
24 }  // namespace
25
26 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
27     : max_string_literal_size_(kDefaultMaxStringLiteralSize),
28       huffman_table_(table) {}
29
30 HpackDecoder::~HpackDecoder() {}
31
32 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id,
33                                                  const char* headers_data,
34                                                  size_t headers_data_length) {
35   decoded_block_.clear();
36
37   size_t new_size = headers_block_buffer_.size() + headers_data_length;
38   if (new_size > kMaxDecodeBufferSize) {
39     return false;
40   }
41   headers_block_buffer_.insert(headers_block_buffer_.end(),
42                                headers_data,
43                                headers_data + headers_data_length);
44   return true;
45 }
46
47 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) {
48   HpackInputStream input_stream(max_string_literal_size_,
49                                 headers_block_buffer_);
50   while (input_stream.HasMoreData()) {
51     if (!DecodeNextOpcode(&input_stream))
52       return false;
53   }
54   headers_block_buffer_.clear();
55
56   // Emit everything in the reference set that hasn't already been emitted.
57   // Also clear entry state for the next decoded headers block.
58   // TODO(jgraettinger): We may need to revisit the order in which headers
59   // are emitted (b/14051713).
60   for (HpackEntry::OrderedSet::const_iterator it =
61           header_table_.reference_set().begin();
62        it != header_table_.reference_set().end(); ++it) {
63     HpackEntry* entry = *it;
64
65     if (entry->state() == kNoState) {
66       HandleHeaderRepresentation(entry->name(), entry->value());
67     } else {
68       entry->set_state(kNoState);
69     }
70   }
71   // Emit the Cookie header, if any crumbles were encountered.
72   if (!cookie_name_.empty()) {
73     decoded_block_[cookie_name_] = cookie_value_;
74     cookie_name_.clear();
75     cookie_value_.clear();
76   }
77   return true;
78 }
79
80 void HpackDecoder::HandleHeaderRepresentation(StringPiece name,
81                                               StringPiece value) {
82   typedef std::pair<std::map<string, string>::iterator, bool> InsertResult;
83
84   // TODO(jgraettinger): HTTP/2 requires strict lowercasing of headers,
85   // and the permissiveness here isn't wanted. Back this out in upstream.
86   if (LowerCaseEqualsASCII(name.begin(), name.end(), "cookie")) {
87     if (cookie_name_.empty()) {
88       cookie_name_.assign(name.data(), name.size());
89       cookie_value_.assign(value.data(), value.size());
90     } else {
91       cookie_value_ += "; ";
92       cookie_value_.insert(cookie_value_.end(), value.begin(), value.end());
93     }
94   } else {
95     InsertResult result = decoded_block_.insert(
96         std::make_pair(name.as_string(), value.as_string()));
97     if (!result.second) {
98       result.first->second.push_back('\0');
99       result.first->second.insert(result.first->second.end(),
100                                   value.begin(),
101                                   value.end());
102     }
103   }
104 }
105
106 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) {
107   // Implements 4.4: Encoding context update. Context updates are a special-case
108   // of indexed header, and must be tested prior to |kIndexedOpcode| below.
109   if (input_stream->MatchPrefixAndConsume(kEncodingContextOpcode)) {
110     return DecodeNextContextUpdate(input_stream);
111   }
112   // Implements 4.2: Indexed Header Field Representation.
113   if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) {
114     return DecodeNextIndexedHeader(input_stream);
115   }
116   // Implements 4.3.1: Literal Header Field without Indexing.
117   if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
118     return DecodeNextLiteralHeader(input_stream, false);
119   }
120   // Implements 4.3.2: Literal Header Field with Incremental Indexing.
121   if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) {
122     return DecodeNextLiteralHeader(input_stream, true);
123   }
124   // Unrecognized opcode.
125   return false;
126 }
127
128 bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream* input_stream) {
129   if (input_stream->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet)) {
130     header_table_.ClearReferenceSet();
131     return true;
132   }
133   if (input_stream->MatchPrefixAndConsume(kEncodingContextNewMaximumSize)) {
134     uint32 size = 0;
135     if (!input_stream->DecodeNextUint32(&size)) {
136       return false;
137     }
138     if (size > header_table_.settings_size_bound()) {
139       return false;
140     }
141     header_table_.SetMaxSize(size);
142     return true;
143   }
144   // Unrecognized encoding context update.
145   return false;
146 }
147
148 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
149   uint32 index = 0;
150   if (!input_stream->DecodeNextUint32(&index))
151     return false;
152
153   // If index == 0, |kEncodingContextOpcode| would have matched.
154   CHECK_NE(index, 0u);
155
156   HpackEntry* entry = header_table_.GetByIndex(index);
157   if (entry == NULL)
158     return false;
159
160   if (entry->IsStatic()) {
161     HandleHeaderRepresentation(entry->name(), entry->value());
162
163     HpackEntry* new_entry = header_table_.TryAddEntry(
164         entry->name(), entry->value());
165     if (new_entry) {
166       header_table_.Toggle(new_entry);
167       new_entry->set_state(kReferencedThisEncoding);
168     }
169   } else {
170     entry->set_state(kNoState);
171     if (header_table_.Toggle(entry)) {
172       HandleHeaderRepresentation(entry->name(), entry->value());
173       entry->set_state(kReferencedThisEncoding);
174     }
175   }
176   return true;
177 }
178
179 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream,
180                                            bool should_index) {
181   StringPiece name;
182   if (!DecodeNextName(input_stream, &name))
183     return false;
184
185   StringPiece value;
186   if (!DecodeNextStringLiteral(input_stream, false, &value))
187     return false;
188
189   HandleHeaderRepresentation(name, value);
190
191   if (!should_index)
192     return true;
193
194   HpackEntry* new_entry = header_table_.TryAddEntry(name, value);
195   if (new_entry) {
196     header_table_.Toggle(new_entry);
197     new_entry->set_state(kReferencedThisEncoding);
198   }
199   return true;
200 }
201
202 bool HpackDecoder::DecodeNextName(
203     HpackInputStream* input_stream, StringPiece* next_name) {
204   uint32 index_or_zero = 0;
205   if (!input_stream->DecodeNextUint32(&index_or_zero))
206     return false;
207
208   if (index_or_zero == 0)
209     return DecodeNextStringLiteral(input_stream, true, next_name);
210
211   const HpackEntry* entry = header_table_.GetByIndex(index_or_zero);
212   if (entry == NULL) {
213     return false;
214   } else if (entry->IsStatic()) {
215     *next_name = entry->name();
216   } else {
217     // |entry| could be evicted as part of this insertion. Preemptively copy.
218     key_buffer_.assign(entry->name());
219     *next_name = key_buffer_;
220   }
221   return true;
222 }
223
224 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream,
225                                            bool is_key,
226                                            StringPiece* output) {
227   if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) {
228     string* buffer = is_key ? &key_buffer_ : &value_buffer_;
229     bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer);
230     *output = StringPiece(*buffer);
231     return result;
232   } else if (input_stream->MatchPrefixAndConsume(
233       kStringLiteralIdentityEncoded)) {
234     return input_stream->DecodeNextIdentityString(output);
235   } else {
236     return false;
237   }
238 }
239
240 }  // namespace net