• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/spdy/core/hpack/hpack_decoder_adapter.h"
6 
7 #include <cstddef>
8 #include <string>
9 
10 #include "absl/strings/string_view.h"
11 #include "quiche/http2/decoder/decode_buffer.h"
12 #include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
13 #include "quiche/common/platform/api/quiche_logging.h"
14 #include "quiche/spdy/core/http2_header_block.h"
15 #include "quiche/spdy/core/spdy_headers_handler_interface.h"
16 
17 namespace spdy {
18 namespace {
19 const size_t kMaxDecodeBufferSizeBytes = 32 * 1024;  // 32 KB
20 }  // namespace
21 
HpackDecoderAdapter()22 HpackDecoderAdapter::HpackDecoderAdapter()
23     : hpack_decoder_(&listener_adapter_, kMaxDecodeBufferSizeBytes),
24       max_decode_buffer_size_bytes_(kMaxDecodeBufferSizeBytes),
25       max_header_block_bytes_(0),
26       header_block_started_(false),
27       error_(http2::HpackDecodingError::kOk) {}
28 
29 HpackDecoderAdapter::~HpackDecoderAdapter() = default;
30 
ApplyHeaderTableSizeSetting(size_t size_setting)31 void HpackDecoderAdapter::ApplyHeaderTableSizeSetting(size_t size_setting) {
32   QUICHE_DVLOG(2) << "HpackDecoderAdapter::ApplyHeaderTableSizeSetting";
33   hpack_decoder_.ApplyHeaderTableSizeSetting(size_setting);
34 }
35 
GetCurrentHeaderTableSizeSetting() const36 size_t HpackDecoderAdapter::GetCurrentHeaderTableSizeSetting() const {
37   return hpack_decoder_.GetCurrentHeaderTableSizeSetting();
38 }
39 
HandleControlFrameHeadersStart(SpdyHeadersHandlerInterface * handler)40 void HpackDecoderAdapter::HandleControlFrameHeadersStart(
41     SpdyHeadersHandlerInterface* handler) {
42   QUICHE_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersStart";
43   QUICHE_DCHECK(!header_block_started_);
44   listener_adapter_.set_handler(handler);
45 }
46 
HandleControlFrameHeadersData(const char * headers_data,size_t headers_data_length)47 bool HpackDecoderAdapter::HandleControlFrameHeadersData(
48     const char* headers_data, size_t headers_data_length) {
49   QUICHE_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersData: len="
50                   << headers_data_length;
51   if (!header_block_started_) {
52     // Initialize the decoding process here rather than in
53     // HandleControlFrameHeadersStart because that method is not always called.
54     header_block_started_ = true;
55     if (!hpack_decoder_.StartDecodingBlock()) {
56       header_block_started_ = false;
57       error_ = hpack_decoder_.error();
58       detailed_error_ = hpack_decoder_.detailed_error();
59       return false;
60     }
61   }
62 
63   // Sometimes we get a call with headers_data==nullptr and
64   // headers_data_length==0, in which case we need to avoid creating
65   // a DecodeBuffer, which would otherwise complain.
66   if (headers_data_length > 0) {
67     QUICHE_DCHECK_NE(headers_data, nullptr);
68     if (headers_data_length > max_decode_buffer_size_bytes_) {
69       QUICHE_DVLOG(1) << "max_decode_buffer_size_bytes_ < headers_data_length: "
70                       << max_decode_buffer_size_bytes_ << " < "
71                       << headers_data_length;
72       error_ = http2::HpackDecodingError::kFragmentTooLong;
73       detailed_error_ = "";
74       return false;
75     }
76     listener_adapter_.AddToTotalHpackBytes(headers_data_length);
77     if (max_header_block_bytes_ != 0 &&
78         listener_adapter_.total_hpack_bytes() > max_header_block_bytes_) {
79       error_ = http2::HpackDecodingError::kCompressedHeaderSizeExceedsLimit;
80       detailed_error_ = "";
81       return false;
82     }
83     http2::DecodeBuffer db(headers_data, headers_data_length);
84     bool ok = hpack_decoder_.DecodeFragment(&db);
85     QUICHE_DCHECK(!ok || db.Empty()) << "Remaining=" << db.Remaining();
86     if (!ok) {
87       error_ = hpack_decoder_.error();
88       detailed_error_ = hpack_decoder_.detailed_error();
89     }
90     return ok;
91   }
92   return true;
93 }
94 
HandleControlFrameHeadersComplete()95 bool HpackDecoderAdapter::HandleControlFrameHeadersComplete() {
96   QUICHE_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersComplete";
97   if (!hpack_decoder_.EndDecodingBlock()) {
98     QUICHE_DVLOG(3) << "EndDecodingBlock returned false";
99     error_ = hpack_decoder_.error();
100     detailed_error_ = hpack_decoder_.detailed_error();
101     return false;
102   }
103   header_block_started_ = false;
104   return true;
105 }
106 
decoded_block() const107 const Http2HeaderBlock& HpackDecoderAdapter::decoded_block() const {
108   return listener_adapter_.decoded_block();
109 }
110 
set_max_decode_buffer_size_bytes(size_t max_decode_buffer_size_bytes)111 void HpackDecoderAdapter::set_max_decode_buffer_size_bytes(
112     size_t max_decode_buffer_size_bytes) {
113   QUICHE_DVLOG(2) << "HpackDecoderAdapter::set_max_decode_buffer_size_bytes";
114   max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
115   hpack_decoder_.set_max_string_size_bytes(max_decode_buffer_size_bytes);
116 }
117 
set_max_header_block_bytes(size_t max_header_block_bytes)118 void HpackDecoderAdapter::set_max_header_block_bytes(
119     size_t max_header_block_bytes) {
120   max_header_block_bytes_ = max_header_block_bytes;
121 }
122 
ListenerAdapter()123 HpackDecoderAdapter::ListenerAdapter::ListenerAdapter() : handler_(nullptr) {}
124 HpackDecoderAdapter::ListenerAdapter::~ListenerAdapter() = default;
125 
set_handler(SpdyHeadersHandlerInterface * handler)126 void HpackDecoderAdapter::ListenerAdapter::set_handler(
127     SpdyHeadersHandlerInterface* handler) {
128   handler_ = handler;
129 }
130 
OnHeaderListStart()131 void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() {
132   QUICHE_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart";
133   total_hpack_bytes_ = 0;
134   total_uncompressed_bytes_ = 0;
135   decoded_block_.clear();
136   if (handler_ != nullptr) {
137     handler_->OnHeaderBlockStart();
138   }
139 }
140 
OnHeader(const std::string & name,const std::string & value)141 void HpackDecoderAdapter::ListenerAdapter::OnHeader(const std::string& name,
142                                                     const std::string& value) {
143   QUICHE_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeader:\n name: "
144                   << name << "\n value: " << value;
145   total_uncompressed_bytes_ += name.size() + value.size();
146   if (handler_ == nullptr) {
147     QUICHE_DVLOG(3) << "Adding to decoded_block";
148     decoded_block_.AppendValueOrAddHeader(name, value);
149   } else {
150     QUICHE_DVLOG(3) << "Passing to handler";
151     handler_->OnHeader(name, value);
152   }
153 }
154 
OnHeaderListEnd()155 void HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd() {
156   QUICHE_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd";
157   // We don't clear the Http2HeaderBlock here to allow access to it until the
158   // next HPACK block is decoded.
159   if (handler_ != nullptr) {
160     handler_->OnHeaderBlockEnd(total_uncompressed_bytes_, total_hpack_bytes_);
161     handler_ = nullptr;
162   }
163 }
164 
OnHeaderErrorDetected(absl::string_view error_message)165 void HpackDecoderAdapter::ListenerAdapter::OnHeaderErrorDetected(
166     absl::string_view error_message) {
167   QUICHE_VLOG(1) << error_message;
168 }
169 
170 }  // namespace spdy
171