1 // Copyright 2017 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 "quiche/http2/hpack/decoder/hpack_decoder.h"
6
7 #include "quiche/http2/decoder/decode_status.h"
8 #include "quiche/common/platform/api/quiche_flag_utils.h"
9 #include "quiche/common/platform/api/quiche_logging.h"
10
11 namespace http2 {
12
HpackDecoder(HpackDecoderListener * listener,size_t max_string_size)13 HpackDecoder::HpackDecoder(HpackDecoderListener* listener,
14 size_t max_string_size)
15 : decoder_state_(listener),
16 entry_buffer_(&decoder_state_, max_string_size),
17 block_decoder_(&entry_buffer_),
18 error_(HpackDecodingError::kOk) {}
19
20 HpackDecoder::~HpackDecoder() = default;
21
set_max_string_size_bytes(size_t max_string_size_bytes)22 void HpackDecoder::set_max_string_size_bytes(size_t max_string_size_bytes) {
23 entry_buffer_.set_max_string_size_bytes(max_string_size_bytes);
24 }
25
ApplyHeaderTableSizeSetting(uint32_t max_header_table_size)26 void HpackDecoder::ApplyHeaderTableSizeSetting(uint32_t max_header_table_size) {
27 decoder_state_.ApplyHeaderTableSizeSetting(max_header_table_size);
28 }
29
StartDecodingBlock()30 bool HpackDecoder::StartDecodingBlock() {
31 QUICHE_DVLOG(3) << "HpackDecoder::StartDecodingBlock, error_detected="
32 << (DetectError() ? "true" : "false");
33 if (DetectError()) {
34 return false;
35 }
36 // TODO(jamessynge): Eliminate Reset(), which shouldn't be necessary
37 // if there are no errors, and shouldn't be necessary with errors if
38 // we never resume decoding after an error has been detected.
39 block_decoder_.Reset();
40 decoder_state_.OnHeaderBlockStart();
41 return true;
42 }
43
DecodeFragment(DecodeBuffer * db)44 bool HpackDecoder::DecodeFragment(DecodeBuffer* db) {
45 QUICHE_DVLOG(3) << "HpackDecoder::DecodeFragment, error_detected="
46 << (DetectError() ? "true" : "false")
47 << ", size=" << db->Remaining();
48 if (DetectError()) {
49 QUICHE_CODE_COUNT_N(decompress_failure_3, 3, 23);
50 return false;
51 }
52 // Decode contents of db as an HPACK block fragment, forwards the decoded
53 // entries to entry_buffer_, which in turn forwards them to decode_state_,
54 // which finally forwards them to the HpackDecoderListener.
55 DecodeStatus status = block_decoder_.Decode(db);
56 if (status == DecodeStatus::kDecodeError) {
57 ReportError(block_decoder_.error());
58 QUICHE_CODE_COUNT_N(decompress_failure_3, 4, 23);
59 return false;
60 } else if (DetectError()) {
61 QUICHE_CODE_COUNT_N(decompress_failure_3, 5, 23);
62 return false;
63 }
64 // Should be positioned between entries iff decoding is complete.
65 QUICHE_DCHECK_EQ(block_decoder_.before_entry(),
66 status == DecodeStatus::kDecodeDone)
67 << status;
68 if (!block_decoder_.before_entry()) {
69 entry_buffer_.BufferStringsIfUnbuffered();
70 }
71 return true;
72 }
73
EndDecodingBlock()74 bool HpackDecoder::EndDecodingBlock() {
75 QUICHE_DVLOG(3) << "HpackDecoder::EndDecodingBlock, error_detected="
76 << (DetectError() ? "true" : "false");
77 if (DetectError()) {
78 QUICHE_CODE_COUNT_N(decompress_failure_3, 6, 23);
79 return false;
80 }
81 if (!block_decoder_.before_entry()) {
82 // The HPACK block ended in the middle of an entry.
83 ReportError(HpackDecodingError::kTruncatedBlock);
84 QUICHE_CODE_COUNT_N(decompress_failure_3, 7, 23);
85 return false;
86 }
87 decoder_state_.OnHeaderBlockEnd();
88 if (DetectError()) {
89 // HpackDecoderState will have reported the error.
90 QUICHE_CODE_COUNT_N(decompress_failure_3, 8, 23);
91 return false;
92 }
93 return true;
94 }
95
DetectError()96 bool HpackDecoder::DetectError() {
97 if (error_ != HpackDecodingError::kOk) {
98 return true;
99 }
100
101 if (decoder_state_.error() != HpackDecodingError::kOk) {
102 QUICHE_DVLOG(2) << "Error detected in decoder_state_";
103 QUICHE_CODE_COUNT_N(decompress_failure_3, 10, 23);
104 error_ = decoder_state_.error();
105 }
106
107 return error_ != HpackDecodingError::kOk;
108 }
109
ReportError(HpackDecodingError error)110 void HpackDecoder::ReportError(HpackDecodingError error) {
111 QUICHE_DVLOG(3) << "HpackDecoder::ReportError is new="
112 << (error_ == HpackDecodingError::kOk ? "true" : "false")
113 << ", error: " << HpackDecodingErrorToString(error);
114 if (error_ == HpackDecodingError::kOk) {
115 error_ = error;
116 decoder_state_.listener()->OnHeaderErrorDetected(
117 HpackDecodingErrorToString(error));
118 }
119 }
120
121 } // namespace http2
122