mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
235 lines
7.5 KiB
C++
235 lines
7.5 KiB
C++
// Copyright 2016 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/http2/hpack/decoder/hpack_decoder_string_buffer.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/trace_event/memory_usage_estimator.h"
|
|
#include "net/http2/tools/http2_bug_tracker.h"
|
|
|
|
namespace net {
|
|
|
|
std::ostream& operator<<(std::ostream& out,
|
|
const HpackDecoderStringBuffer::State v) {
|
|
switch (v) {
|
|
case HpackDecoderStringBuffer::State::RESET:
|
|
return out << "RESET";
|
|
case HpackDecoderStringBuffer::State::COLLECTING:
|
|
return out << "COLLECTING";
|
|
case HpackDecoderStringBuffer::State::COMPLETE:
|
|
return out << "COMPLETE";
|
|
}
|
|
// Since the value doesn't come over the wire, only a programming bug should
|
|
// result in reaching this point.
|
|
int unknown = static_cast<int>(v);
|
|
HTTP2_BUG << "Invalid HpackDecoderStringBuffer::State: " << unknown;
|
|
return out << "HpackDecoderStringBuffer::State(" << unknown << ")";
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out,
|
|
const HpackDecoderStringBuffer::Backing v) {
|
|
switch (v) {
|
|
case HpackDecoderStringBuffer::Backing::RESET:
|
|
return out << "RESET";
|
|
case HpackDecoderStringBuffer::Backing::UNBUFFERED:
|
|
return out << "UNBUFFERED";
|
|
case HpackDecoderStringBuffer::Backing::BUFFERED:
|
|
return out << "BUFFERED";
|
|
case HpackDecoderStringBuffer::Backing::STATIC:
|
|
return out << "STATIC";
|
|
}
|
|
// Since the value doesn't come over the wire, only a programming bug should
|
|
// result in reaching this point.
|
|
auto v2 = static_cast<int>(v);
|
|
HTTP2_BUG << "Invalid HpackDecoderStringBuffer::Backing: " << v2;
|
|
return out << "HpackDecoderStringBuffer::Backing(" << v2 << ")";
|
|
}
|
|
|
|
HpackDecoderStringBuffer::HpackDecoderStringBuffer()
|
|
: remaining_len_(0),
|
|
is_huffman_encoded_(false),
|
|
state_(State::RESET),
|
|
backing_(Backing::RESET) {}
|
|
HpackDecoderStringBuffer::~HpackDecoderStringBuffer() {}
|
|
|
|
void HpackDecoderStringBuffer::Reset() {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::Reset";
|
|
state_ = State::RESET;
|
|
}
|
|
|
|
void HpackDecoderStringBuffer::Set(Http2StringPiece value, bool is_static) {
|
|
DVLOG(2) << "HpackDecoderStringBuffer::Set";
|
|
DCHECK_EQ(state_, State::RESET);
|
|
value_ = value;
|
|
state_ = State::COMPLETE;
|
|
backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
|
|
// TODO(jamessynge): Determine which of these two fields must be set.
|
|
remaining_len_ = 0;
|
|
is_huffman_encoded_ = false;
|
|
}
|
|
|
|
void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
|
|
DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
|
|
DCHECK_EQ(state_, State::RESET);
|
|
|
|
remaining_len_ = len;
|
|
is_huffman_encoded_ = huffman_encoded;
|
|
state_ = State::COLLECTING;
|
|
|
|
if (huffman_encoded) {
|
|
// We don't set, clear or use value_ for buffered strings until OnEnd.
|
|
decoder_.Reset();
|
|
buffer_.clear();
|
|
backing_ = Backing::BUFFERED;
|
|
|
|
// Reserve space in buffer_ for the uncompressed string, assuming the
|
|
// maximum expansion. The shortest Huffman codes in the RFC are 5 bits long,
|
|
// which then expand to 8 bits during decoding (i.e. each code is for one
|
|
// plain text octet, aka byte), so the maximum size is 60% longer than the
|
|
// encoded size.
|
|
len = len * 8 / 5;
|
|
if (buffer_.capacity() < len) {
|
|
buffer_.reserve(len);
|
|
}
|
|
} else {
|
|
// Assume for now that we won't need to use buffer_, so don't reserve space
|
|
// in it.
|
|
backing_ = Backing::RESET;
|
|
// OnData is not called for empty (zero length) strings, so make sure that
|
|
// value_ is cleared.
|
|
value_ = Http2StringPiece();
|
|
}
|
|
}
|
|
|
|
bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
|
|
DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
|
|
<< ", backing=" << backing_;
|
|
DCHECK_EQ(state_, State::COLLECTING);
|
|
DCHECK_LE(len, remaining_len_);
|
|
remaining_len_ -= len;
|
|
|
|
if (is_huffman_encoded_) {
|
|
DCHECK_EQ(backing_, Backing::BUFFERED);
|
|
return decoder_.Decode(Http2StringPiece(data, len), &buffer_);
|
|
}
|
|
|
|
if (backing_ == Backing::RESET) {
|
|
// This is the first call to OnData. If data contains the entire string,
|
|
// don't copy the string. If we later find that the HPACK entry is split
|
|
// across input buffers, then we'll copy the string into buffer_.
|
|
if (remaining_len_ == 0) {
|
|
value_ = Http2StringPiece(data, len);
|
|
backing_ = Backing::UNBUFFERED;
|
|
return true;
|
|
}
|
|
|
|
// We need to buffer the string because it is split across input buffers.
|
|
// Reserve space in buffer_ for the entire string.
|
|
backing_ = Backing::BUFFERED;
|
|
buffer_.reserve(remaining_len_ + len);
|
|
buffer_.assign(data, len);
|
|
return true;
|
|
}
|
|
|
|
// This is not the first call to OnData for this string, so it should be
|
|
// buffered.
|
|
DCHECK_EQ(backing_, Backing::BUFFERED);
|
|
|
|
// Append to the current contents of the buffer.
|
|
buffer_.append(data, len);
|
|
return true;
|
|
}
|
|
|
|
bool HpackDecoderStringBuffer::OnEnd() {
|
|
DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
|
|
DCHECK_EQ(state_, State::COLLECTING);
|
|
DCHECK_EQ(0u, remaining_len_);
|
|
|
|
if (is_huffman_encoded_) {
|
|
DCHECK_EQ(backing_, Backing::BUFFERED);
|
|
// Did the Huffman encoding of the string end properly?
|
|
if (!decoder_.InputProperlyTerminated()) {
|
|
return false; // No, it didn't.
|
|
}
|
|
value_ = buffer_;
|
|
} else if (backing_ == Backing::BUFFERED) {
|
|
value_ = buffer_;
|
|
}
|
|
state_ = State::COMPLETE;
|
|
return true;
|
|
}
|
|
|
|
void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
|
|
<< state_ << ", backing=" << backing_;
|
|
if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
|
|
DVLOG(2) << "HpackDecoderStringBuffer buffering string of length "
|
|
<< value_.size();
|
|
value_.CopyToString(&buffer_);
|
|
if (state_ == State::COMPLETE) {
|
|
value_ = buffer_;
|
|
}
|
|
backing_ = Backing::BUFFERED;
|
|
}
|
|
}
|
|
|
|
bool HpackDecoderStringBuffer::IsBuffered() const {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::IsBuffered";
|
|
return state_ != State::RESET && backing_ == Backing::BUFFERED;
|
|
}
|
|
|
|
size_t HpackDecoderStringBuffer::BufferedLength() const {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
|
|
return IsBuffered() ? buffer_.size() : 0;
|
|
}
|
|
|
|
Http2StringPiece HpackDecoderStringBuffer::str() const {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::str";
|
|
DCHECK_EQ(state_, State::COMPLETE);
|
|
return value_;
|
|
}
|
|
|
|
Http2String HpackDecoderStringBuffer::ReleaseString() {
|
|
DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString";
|
|
DCHECK_EQ(state_, State::COMPLETE);
|
|
DCHECK_EQ(backing_, Backing::BUFFERED);
|
|
if (state_ == State::COMPLETE) {
|
|
state_ = State::RESET;
|
|
if (backing_ == Backing::BUFFERED) {
|
|
return std::move(buffer_);
|
|
} else {
|
|
return value_.as_string();
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
|
|
out << "{state=" << state_;
|
|
if (state_ != State::RESET) {
|
|
out << ", backing=" << backing_;
|
|
out << ", remaining_len=" << remaining_len_;
|
|
out << ", is_huffman_encoded=" << is_huffman_encoded_;
|
|
if (backing_ == Backing::BUFFERED) {
|
|
out << ", buffer: " << buffer_;
|
|
} else {
|
|
out << ", value: " << value_;
|
|
}
|
|
}
|
|
out << "}";
|
|
}
|
|
|
|
size_t HpackDecoderStringBuffer::EstimateMemoryUsage() const {
|
|
return base::trace_event::EstimateMemoryUsage(buffer_);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
|
|
v.OutputDebugStringTo(out);
|
|
return out;
|
|
}
|
|
|
|
} // namespace net
|