• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
20 
21 #include <grpc/slice.h>
22 #include <grpc/slice_buffer.h>
23 #include <grpc/support/port_platform.h>
24 
25 #include <algorithm>
26 #include <cstdint>
27 
28 #include "absl/log/check.h"
29 #include "absl/log/log.h"
30 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
31 #include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
32 #include "src/core/ext/transport/chttp2/transport/hpack_encoder_table.h"
33 #include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
34 #include "src/core/ext/transport/chttp2/transport/varint.h"
35 #include "src/core/lib/debug/trace.h"
36 #include "src/core/lib/surface/validate_metadata.h"
37 #include "src/core/lib/transport/timeout_encoding.h"
38 #include "src/core/util/crash.h"
39 
40 namespace grpc_core {
41 
42 namespace {
43 
44 constexpr size_t kHeadersFrameHeaderSize = 9;
45 
46 }  // namespace
47 
48 // fills p (which is expected to be kHeadersFrameHeaderSize bytes long)
49 // with a headers frame header
FillHeader(uint8_t * p,uint8_t type,uint32_t id,size_t len,uint8_t flags)50 static void FillHeader(uint8_t* p, uint8_t type, uint32_t id, size_t len,
51                        uint8_t flags) {
52   // len is the current frame size (i.e. for the frame we're finishing).
53   // We finish a frame if:
54   // 1) We called ensure_space(), (i.e. add_tiny_header_data()) and adding
55   //    'need_bytes' to the frame would cause us to exceed max_frame_size.
56   // 2) We called add_header_data, and adding the slice would cause us to exceed
57   //    max_frame_size.
58   // 3) We're done encoding the header.
59 
60   // Thus, len is always <= max_frame_size.
61   // max_frame_size is derived from GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
62   // which has a max allowable value of 16777215 (see chttp_transport.cc).
63   // Thus, the following assert can be a debug assert.
64   DCHECK_LE(len, 16777216u);
65   *p++ = static_cast<uint8_t>(len >> 16);
66   *p++ = static_cast<uint8_t>(len >> 8);
67   *p++ = static_cast<uint8_t>(len);
68   *p++ = type;
69   *p++ = flags;
70   *p++ = static_cast<uint8_t>(id >> 24);
71   *p++ = static_cast<uint8_t>(id >> 16);
72   *p++ = static_cast<uint8_t>(id >> 8);
73   *p++ = static_cast<uint8_t>(id);
74 }
75 
Frame(const EncodeHeaderOptions & options,SliceBuffer & raw,grpc_slice_buffer * output)76 void HPackCompressor::Frame(const EncodeHeaderOptions& options,
77                             SliceBuffer& raw, grpc_slice_buffer* output) {
78   uint8_t frame_type = GRPC_CHTTP2_FRAME_HEADER;
79   uint8_t flags = 0;
80   // per the HTTP/2 spec:
81   //   A HEADERS frame carries the END_STREAM flag that signals the end of a
82   //   stream. However, a HEADERS frame with the END_STREAM flag set can be
83   //   followed by CONTINUATION frames on the same stream. Logically, the
84   //   CONTINUATION frames are part of the HEADERS frame.
85   // Thus, we add the END_STREAM flag to the HEADER frame (the first frame).
86   if (options.is_end_of_stream) {
87     flags |= GRPC_CHTTP2_DATA_FLAG_END_STREAM;
88   }
89   options.call_tracer->RecordOutgoingBytes({0, 0, raw.Length()});
90   while (frame_type == GRPC_CHTTP2_FRAME_HEADER || raw.Length() > 0) {
91     // per the HTTP/2 spec:
92     //   A HEADERS frame without the END_HEADERS flag set MUST be followed by
93     //   a CONTINUATION frame for the same stream.
94     // Thus, we add the END_HEADER flag to the last frame.
95     size_t len = raw.Length();
96     if (len <= options.max_frame_size) {
97       flags |= GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
98     } else {
99       len = options.max_frame_size;
100     }
101     FillHeader(grpc_slice_buffer_tiny_add(output, kHeadersFrameHeaderSize),
102                frame_type, options.stream_id, len, flags);
103     options.call_tracer->RecordOutgoingBytes({kHeadersFrameHeaderSize, 0, 0});
104     grpc_slice_buffer_move_first(raw.c_slice_buffer(), len, output);
105 
106     frame_type = GRPC_CHTTP2_FRAME_CONTINUATION;
107     flags = 0;
108   }
109 }
110 
SetMaxUsableSize(uint32_t max_table_size)111 void HPackCompressor::SetMaxUsableSize(uint32_t max_table_size) {
112   max_usable_size_ = max_table_size;
113   SetMaxTableSize(std::min(table_.max_size(), max_table_size));
114 }
115 
SetMaxTableSize(uint32_t max_table_size)116 void HPackCompressor::SetMaxTableSize(uint32_t max_table_size) {
117   if (table_.SetMaxSize(std::min(max_usable_size_, max_table_size))) {
118     advertise_table_size_change_ = true;
119     GRPC_TRACE_LOG(http, INFO)
120         << "set max table size from encoder to " << max_table_size;
121   }
122 }
123 
124 namespace {
125 struct WireValue {
WireValuegrpc_core::__anon4dc18ba40211::WireValue126   WireValue(uint8_t huffman_prefix, bool insert_null_before_wire_value,
127             Slice slice)
128       : data(std::move(slice)),
129         huffman_prefix(huffman_prefix),
130         insert_null_before_wire_value(insert_null_before_wire_value),
131         length(data.length() + (insert_null_before_wire_value ? 1 : 0)),
132         hpack_length(length) {}
WireValuegrpc_core::__anon4dc18ba40211::WireValue133   WireValue(uint8_t huffman_prefix, bool insert_null_before_wire_value,
134             Slice slice, size_t hpack_length)
135       : data(std::move(slice)),
136         huffman_prefix(huffman_prefix),
137         insert_null_before_wire_value(insert_null_before_wire_value),
138         length(data.length() + (insert_null_before_wire_value ? 1 : 0)),
139         hpack_length(hpack_length + (insert_null_before_wire_value ? 1 : 0)) {}
140   Slice data;
141   const uint8_t huffman_prefix;
142   const bool insert_null_before_wire_value;
143   const size_t length;
144   const size_t hpack_length;
145 };
146 
147 // Construct a wire value from a slice.
148 // true_binary_enabled => use the true binary system
149 // is_bin_hdr => the header is -bin suffixed
GetWireValue(Slice value,bool true_binary_enabled,bool is_bin_hdr)150 WireValue GetWireValue(Slice value, bool true_binary_enabled, bool is_bin_hdr) {
151   if (is_bin_hdr) {
152     if (true_binary_enabled) {
153       return WireValue(0x00, true, std::move(value));
154     } else {
155       uint32_t hpack_length;
156       Slice output(grpc_chttp2_base64_encode_and_huffman_compress(
157           value.c_slice(), &hpack_length));
158       return WireValue(0x80, false, std::move(output), hpack_length);
159     }
160   } else {
161     // TODO(ctiller): opportunistically compress non-binary headers
162     return WireValue(0x00, false, std::move(value));
163   }
164 }
165 
166 struct DefinitelyInterned {
IsBinarygrpc_core::__anon4dc18ba40211::DefinitelyInterned167   static bool IsBinary(grpc_slice key) {
168     return grpc_is_refcounted_slice_binary_header(key);
169   }
170 };
171 struct UnsureIfInterned {
IsBinarygrpc_core::__anon4dc18ba40211::UnsureIfInterned172   static bool IsBinary(grpc_slice key) {
173     return grpc_is_binary_header_internal(key);
174   }
175 };
176 
177 class BinaryStringValue {
178  public:
BinaryStringValue(Slice value,bool use_true_binary_metadata)179   explicit BinaryStringValue(Slice value, bool use_true_binary_metadata)
180       : wire_value_(
181             GetWireValue(std::move(value), use_true_binary_metadata, true)),
182         len_val_(wire_value_.length) {}
183 
prefix_length() const184   size_t prefix_length() const {
185     return len_val_.length() +
186            (wire_value_.insert_null_before_wire_value ? 1 : 0);
187   }
188 
WritePrefix(uint8_t * prefix_data)189   void WritePrefix(uint8_t* prefix_data) {
190     len_val_.Write(wire_value_.huffman_prefix, prefix_data);
191     if (wire_value_.insert_null_before_wire_value) {
192       prefix_data[len_val_.length()] = 0;
193     }
194   }
195 
data()196   Slice data() { return std::move(wire_value_.data); }
197 
hpack_length()198   uint32_t hpack_length() { return wire_value_.hpack_length; }
199 
200  private:
201   WireValue wire_value_;
202   VarintWriter<1> len_val_;
203 };
204 
205 class NonBinaryStringValue {
206  public:
NonBinaryStringValue(Slice value)207   explicit NonBinaryStringValue(Slice value)
208       : value_(std::move(value)), len_val_(value_.length()) {}
209 
prefix_length() const210   size_t prefix_length() const { return len_val_.length(); }
211 
WritePrefix(uint8_t * prefix_data)212   void WritePrefix(uint8_t* prefix_data) { len_val_.Write(0x00, prefix_data); }
213 
data()214   Slice data() { return std::move(value_); }
215 
216  private:
217   Slice value_;
218   VarintWriter<1> len_val_;
219 };
220 
221 class StringKey {
222  public:
StringKey(Slice key)223   explicit StringKey(Slice key)
224       : key_(std::move(key)), len_key_(key_.length()) {}
225 
prefix_length() const226   size_t prefix_length() const { return 1 + len_key_.length(); }
227 
WritePrefix(uint8_t type,uint8_t * data)228   void WritePrefix(uint8_t type, uint8_t* data) {
229     data[0] = type;
230     len_key_.Write(0x00, data + 1);
231   }
232 
key()233   Slice key() { return std::move(key_); }
234 
235  private:
236   Slice key_;
237   VarintWriter<1> len_key_;
238 };
239 }  // namespace
240 
241 namespace hpack_encoder_detail {
EmitIndexed(uint32_t elem_index)242 void Encoder::EmitIndexed(uint32_t elem_index) {
243   VarintWriter<1> w(elem_index);
244   w.Write(0x80, output_.AddTiny(w.length()));
245 }
246 
EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice key_slice,Slice value_slice)247 uint32_t Encoder::EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice key_slice,
248                                                          Slice value_slice) {
249   auto key_len = key_slice.length();
250   auto value_len = value_slice.length();
251   StringKey key(std::move(key_slice));
252   key.WritePrefix(0x40, output_.AddTiny(key.prefix_length()));
253   output_.Append(key.key());
254   NonBinaryStringValue emit(std::move(value_slice));
255   emit.WritePrefix(output_.AddTiny(emit.prefix_length()));
256   // Allocate an index in the hpack table for this newly emitted entry.
257   // (we do so here because we know the length of the key and value)
258   uint32_t index = compressor_->table_.AllocateIndex(
259       key_len + value_len + hpack_constants::kEntryOverhead);
260   output_.Append(emit.data());
261   return index;
262 }
263 
EmitLitHdrWithBinaryStringKeyNotIdx(Slice key_slice,Slice value_slice)264 void Encoder::EmitLitHdrWithBinaryStringKeyNotIdx(Slice key_slice,
265                                                   Slice value_slice) {
266   StringKey key(std::move(key_slice));
267   key.WritePrefix(0x00, output_.AddTiny(key.prefix_length()));
268   output_.Append(key.key());
269   BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
270   emit.WritePrefix(output_.AddTiny(emit.prefix_length()));
271   output_.Append(emit.data());
272 }
273 
EmitLitHdrWithBinaryStringKeyIncIdx(Slice key_slice,Slice value_slice)274 uint32_t Encoder::EmitLitHdrWithBinaryStringKeyIncIdx(Slice key_slice,
275                                                       Slice value_slice) {
276   auto key_len = key_slice.length();
277   StringKey key(std::move(key_slice));
278   key.WritePrefix(0x40, output_.AddTiny(key.prefix_length()));
279   output_.Append(key.key());
280   BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
281   emit.WritePrefix(output_.AddTiny(emit.prefix_length()));
282   // Allocate an index in the hpack table for this newly emitted entry.
283   // (we do so here because we know the length of the key and value)
284   uint32_t index = compressor_->table_.AllocateIndex(
285       key_len + emit.hpack_length() + hpack_constants::kEntryOverhead);
286   output_.Append(emit.data());
287   return index;
288 }
289 
EmitLitHdrWithBinaryStringKeyNotIdx(uint32_t key_index,Slice value_slice)290 void Encoder::EmitLitHdrWithBinaryStringKeyNotIdx(uint32_t key_index,
291                                                   Slice value_slice) {
292   BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
293   VarintWriter<4> key(key_index);
294   uint8_t* data = output_.AddTiny(key.length() + emit.prefix_length());
295   key.Write(0x00, data);
296   emit.WritePrefix(data + key.length());
297   output_.Append(emit.data());
298 }
299 
EmitLitHdrWithNonBinaryStringKeyNotIdx(Slice key_slice,Slice value_slice)300 void Encoder::EmitLitHdrWithNonBinaryStringKeyNotIdx(Slice key_slice,
301                                                      Slice value_slice) {
302   StringKey key(std::move(key_slice));
303   key.WritePrefix(0x00, output_.AddTiny(key.prefix_length()));
304   output_.Append(key.key());
305   NonBinaryStringValue emit(std::move(value_slice));
306   emit.WritePrefix(output_.AddTiny(emit.prefix_length()));
307   output_.Append(emit.data());
308 }
309 
AdvertiseTableSizeChange()310 void Encoder::AdvertiseTableSizeChange() {
311   VarintWriter<3> w(compressor_->table_.max_size());
312   w.Write(0x20, output_.AddTiny(w.length()));
313 }
314 
EmitTo(absl::string_view key,const Slice & value,Encoder * encoder)315 void SliceIndex::EmitTo(absl::string_view key, const Slice& value,
316                         Encoder* encoder) {
317   auto& table = encoder->hpack_table();
318   using It = std::vector<ValueIndex>::iterator;
319   It prev = values_.end();
320   size_t transport_length =
321       key.length() + value.length() + hpack_constants::kEntryOverhead;
322   if (transport_length > HPackEncoderTable::MaxEntrySize()) {
323     encoder->EmitLitHdrWithNonBinaryStringKeyNotIdx(
324         Slice::FromStaticString(key), value.Ref());
325     return;
326   }
327   // Linear scan through previous values to see if we find the value.
328   for (It it = values_.begin(); it != values_.end(); ++it) {
329     if (value == it->value) {
330       // Got a hit... is it still in the decode table?
331       if (table.ConvertibleToDynamicIndex(it->index)) {
332         // Yes, emit the index and proceed to cleanup.
333         encoder->EmitIndexed(table.DynamicIndex(it->index));
334       } else {
335         // Not current, emit a new literal and update the index.
336         it->index = encoder->EmitLitHdrWithNonBinaryStringKeyIncIdx(
337             Slice::FromStaticString(key), value.Ref());
338       }
339       // Bubble this entry up if we can - ensures that the most used values end
340       // up towards the start of the array.
341       if (prev != values_.end()) std::swap(*prev, *it);
342       // If there are entries at the end of the array, and those entries are no
343       // longer in the table, remove them.
344       while (!values_.empty() &&
345              !table.ConvertibleToDynamicIndex(values_.back().index)) {
346         values_.pop_back();
347       }
348       // All done, early out.
349       return;
350     }
351     prev = it;
352   }
353   // No hit, emit a new literal and add it to the index.
354   uint32_t index = encoder->EmitLitHdrWithNonBinaryStringKeyIncIdx(
355       Slice::FromStaticString(key), value.Ref());
356   values_.emplace_back(value.Ref(), index);
357 }
358 
Encode(const Slice & key,const Slice & value)359 void Encoder::Encode(const Slice& key, const Slice& value) {
360   if (absl::EndsWith(key.as_string_view(), "-bin")) {
361     EmitLitHdrWithBinaryStringKeyNotIdx(key.Ref(), value.Ref());
362   } else {
363     EmitLitHdrWithNonBinaryStringKeyNotIdx(key.Ref(), value.Ref());
364   }
365 }
366 
EncodeWith(HttpSchemeMetadata,HttpSchemeMetadata::ValueType value,Encoder * encoder)367 void Compressor<HttpSchemeMetadata, HttpSchemeCompressor>::EncodeWith(
368     HttpSchemeMetadata, HttpSchemeMetadata::ValueType value, Encoder* encoder) {
369   switch (value) {
370     case HttpSchemeMetadata::ValueType::kHttp:
371       encoder->EmitIndexed(6);  // :scheme: http
372       break;
373     case HttpSchemeMetadata::ValueType::kHttps:
374       encoder->EmitIndexed(7);  // :scheme: https
375       break;
376     case HttpSchemeMetadata::ValueType::kInvalid:
377       LOG(ERROR) << "Not encoding bad http scheme";
378       encoder->NoteEncodingError();
379       break;
380   }
381 }
382 
EncodeWith(HttpStatusMetadata,uint32_t status,Encoder * encoder)383 void Compressor<HttpStatusMetadata, HttpStatusCompressor>::EncodeWith(
384     HttpStatusMetadata, uint32_t status, Encoder* encoder) {
385   if (status == 200) {
386     encoder->EmitIndexed(8);  // :status: 200
387     return;
388   }
389   uint8_t index = 0;
390   switch (status) {
391     case 204:
392       index = 9;  // :status: 204
393       break;
394     case 206:
395       index = 10;  // :status: 206
396       break;
397     case 304:
398       index = 11;  // :status: 304
399       break;
400     case 400:
401       index = 12;  // :status: 400
402       break;
403     case 404:
404       index = 13;  // :status: 404
405       break;
406     case 500:
407       index = 14;  // :status: 500
408       break;
409   }
410   if (GPR_LIKELY(index != 0)) {
411     encoder->EmitIndexed(index);
412   } else {
413     encoder->EmitLitHdrWithNonBinaryStringKeyNotIdx(
414         Slice::FromStaticString(":status"), Slice::FromInt64(status));
415   }
416 }
417 
EncodeWith(HttpMethodMetadata,HttpMethodMetadata::ValueType method,Encoder * encoder)418 void Compressor<HttpMethodMetadata, HttpMethodCompressor>::EncodeWith(
419     HttpMethodMetadata, HttpMethodMetadata::ValueType method,
420     Encoder* encoder) {
421   switch (method) {
422     case HttpMethodMetadata::ValueType::kPost:
423       encoder->EmitIndexed(3);  // :method: POST
424       break;
425     case HttpMethodMetadata::ValueType::kGet:
426       encoder->EmitIndexed(2);  // :method: GET
427       break;
428     case HttpMethodMetadata::ValueType::kPut:
429       // Right now, we only emit PUT as a method for testing purposes, so it's
430       // fine to not index it.
431       encoder->EmitLitHdrWithNonBinaryStringKeyNotIdx(
432           Slice::FromStaticString(":method"), Slice::FromStaticString("PUT"));
433       break;
434     case HttpMethodMetadata::ValueType::kInvalid:
435       LOG(ERROR) << "Not encoding bad http method";
436       encoder->NoteEncodingError();
437       break;
438   }
439 }
440 
EncodeAlwaysIndexed(uint32_t * index,absl::string_view key,Slice value,size_t)441 void Encoder::EncodeAlwaysIndexed(uint32_t* index, absl::string_view key,
442                                   Slice value, size_t) {
443   if (compressor_->table_.ConvertibleToDynamicIndex(*index)) {
444     EmitIndexed(compressor_->table_.DynamicIndex(*index));
445   } else {
446     *index = EmitLitHdrWithNonBinaryStringKeyIncIdx(
447         Slice::FromStaticString(key), std::move(value));
448   }
449 }
450 
EncodeIndexedKeyWithBinaryValue(uint32_t * index,absl::string_view key,Slice value)451 void Encoder::EncodeIndexedKeyWithBinaryValue(uint32_t* index,
452                                               absl::string_view key,
453                                               Slice value) {
454   if (compressor_->table_.ConvertibleToDynamicIndex(*index)) {
455     EmitLitHdrWithBinaryStringKeyNotIdx(
456         compressor_->table_.DynamicIndex(*index), std::move(value));
457   } else {
458     *index = EmitLitHdrWithBinaryStringKeyIncIdx(Slice::FromStaticString(key),
459                                                  std::move(value));
460   }
461 }
462 
EncodeRepeatingSliceValue(const absl::string_view & key,const Slice & slice,uint32_t * index,size_t max_compression_size)463 void Encoder::EncodeRepeatingSliceValue(const absl::string_view& key,
464                                         const Slice& slice, uint32_t* index,
465                                         size_t max_compression_size) {
466   if (hpack_constants::SizeForEntry(key.size(), slice.size()) >
467       max_compression_size) {
468     EmitLitHdrWithBinaryStringKeyNotIdx(Slice::FromStaticString(key),
469                                         slice.Ref());
470   } else {
471     EncodeIndexedKeyWithBinaryValue(index, key, slice.Ref());
472   }
473 }
474 
EncodeWith(absl::string_view key,Timestamp deadline,Encoder * encoder)475 void TimeoutCompressorImpl::EncodeWith(absl::string_view key,
476                                        Timestamp deadline, Encoder* encoder) {
477   const Timeout timeout = Timeout::FromDuration(deadline - Timestamp::Now());
478   auto& table = encoder->hpack_table();
479   for (size_t i = 0; i < kNumPreviousValues; i++) {
480     const auto& previous = previous_timeouts_[i];
481     if (!table.ConvertibleToDynamicIndex(previous.index)) continue;
482     const double ratio = timeout.RatioVersus(previous.timeout);
483     // If the timeout we're sending is shorter than a previous timeout, but
484     // within 3% of it, we'll consider sending it.
485     if (ratio > -3 && ratio <= 0) {
486       encoder->EmitIndexed(table.DynamicIndex(previous.index));
487       return;
488     }
489   }
490   Slice encoded = timeout.Encode();
491   uint32_t index = encoder->EmitLitHdrWithNonBinaryStringKeyIncIdx(
492       Slice::FromStaticString(key), std::move(encoded));
493   uint32_t i = next_previous_value_;
494   ++next_previous_value_;
495   previous_timeouts_[i % kNumPreviousValues] = PreviousTimeout{timeout, index};
496 }
497 
Encoder(HPackCompressor * compressor,bool use_true_binary_metadata,SliceBuffer & output)498 Encoder::Encoder(HPackCompressor* compressor, bool use_true_binary_metadata,
499                  SliceBuffer& output)
500     : use_true_binary_metadata_(use_true_binary_metadata),
501       compressor_(compressor),
502       output_(output) {
503   if (std::exchange(compressor_->advertise_table_size_change_, false)) {
504     AdvertiseTableSizeChange();
505   }
506 }
507 
508 }  // namespace hpack_encoder_detail
509 }  // namespace grpc_core
510