• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HpackDecoder(const HpackHuffmanTable & table)23 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
24     : max_string_literal_size_(kDefaultMaxStringLiteralSize),
25       regular_header_seen_(false),
26       huffman_table_(table) {}
27 
~HpackDecoder()28 HpackDecoder::~HpackDecoder() {}
29 
HandleControlFrameHeadersData(SpdyStreamId id,const char * headers_data,size_t headers_data_length)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 
HandleControlFrameHeadersComplete(SpdyStreamId id)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 
HandleHeaderRepresentation(StringPiece name,StringPiece value)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 
DecodeNextOpcode(HpackInputStream * input_stream)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 
DecodeNextHeaderTableSizeUpdate(HpackInputStream * input_stream)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 
DecodeNextIndexedHeader(HpackInputStream * input_stream)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 
DecodeNextLiteralHeader(HpackInputStream * input_stream,bool should_index)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 
DecodeNextName(HpackInputStream * input_stream,StringPiece * next_name)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 
DecodeNextStringLiteral(HpackInputStream * input_stream,bool is_key,StringPiece * output)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
207