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