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