proxygen/lib/http/codec/compress/HPACKDecodeBuffer.cpp (123 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <proxygen/lib/http/codec/compress/HPACKDecodeBuffer.h>
#include <limits>
#include <memory>
#include <proxygen/lib/http/codec/compress/Huffman.h>
using folly::IOBuf;
using proxygen::HPACK::DecodeError;
using std::unique_ptr;
namespace proxygen {
void HPACKDecodeBuffer::EOB_LOG(std::string msg, DecodeError code) const {
if (endOfBufferIsError_ || code != DecodeError::BUFFER_UNDERFLOW) {
LOG(ERROR) << msg;
} else {
VLOG(4) << msg;
}
}
bool HPACKDecodeBuffer::empty() {
return remainingBytes_ == 0;
}
uint8_t HPACKDecodeBuffer::next() {
CHECK_GT(remainingBytes_, 0);
// in case we are the end of an IOBuf, peek() will move to the next one
uint8_t byte = peek();
cursor_.skip(1);
remainingBytes_--;
return byte;
}
uint8_t HPACKDecodeBuffer::peek() {
CHECK_GT(remainingBytes_, 0);
if (cursor_.length() == 0) {
cursor_.peek();
}
return *cursor_.data();
}
DecodeError HPACKDecodeBuffer::decodeLiteral(folly::fbstring& literal) {
return decodeLiteral(7, literal);
}
DecodeError HPACKDecodeBuffer::decodeLiteral(uint8_t nbit,
folly::fbstring& literal) {
literal.clear();
if (remainingBytes_ == 0) {
EOB_LOG("remainingBytes_ == 0");
return DecodeError::BUFFER_UNDERFLOW;
}
auto byte = peek();
uint8_t huffmanCheck = uint8_t(1 << nbit);
bool huffman = byte & huffmanCheck;
// extract the size
uint64_t size;
DecodeError result = decodeInteger(nbit, size);
if (result != DecodeError::NONE) {
EOB_LOG("Could not decode literal size", result);
return result;
}
if (size > remainingBytes_) {
EOB_LOG(folly::to<std::string>(
"size(", size, ") > remainingBytes_(", remainingBytes_, ")"));
return DecodeError::BUFFER_UNDERFLOW;
}
if (size > maxLiteralSize_) {
LOG(ERROR) << "Literal too large, size=" << size;
return DecodeError::LITERAL_TOO_LARGE;
}
const uint8_t* data;
unique_ptr<IOBuf> tmpbuf;
// handle the case where the buffer spans multiple buffers
if (cursor_.length() >= size) {
data = cursor_.data();
cursor_.skip(size);
} else {
// temporary buffer to pull the chunks together
tmpbuf = IOBuf::create(size);
// pull() will move the cursor
cursor_.pull(tmpbuf->writableData(), size);
data = tmpbuf->data();
}
if (huffman) {
static auto& huffmanTree = huffman::huffTree();
huffmanTree.decode(data, size, literal);
} else {
literal.append((const char*)data, size);
}
remainingBytes_ -= size;
return DecodeError::NONE;
}
DecodeError HPACKDecodeBuffer::decodeInteger(uint64_t& integer) {
return decodeInteger(8, integer);
}
DecodeError HPACKDecodeBuffer::decodeInteger(uint8_t nbit, uint64_t& integer) {
if (remainingBytes_ == 0) {
EOB_LOG("remainingBytes_ == 0");
return DecodeError::BUFFER_UNDERFLOW;
}
uint8_t byte = next();
uint8_t mask = HPACK::NBIT_MASKS[nbit];
// remove the first (8 - nbit) bits
byte = byte & mask;
integer = byte;
if (byte != mask) {
// the value fit in one byte
return DecodeError::NONE;
}
uint64_t f = 1;
uint32_t fexp = 0;
do {
if (remainingBytes_ == 0) {
EOB_LOG("remainingBytes_ == 0");
return DecodeError::BUFFER_UNDERFLOW;
}
byte = next();
if (fexp > 64) {
// overflow in factorizer, f > 2^64
LOG(ERROR) << "overflow fexp=" << fexp;
return DecodeError::INTEGER_OVERFLOW;
}
uint64_t add = (byte & 127) * f;
if (std::numeric_limits<uint64_t>::max() - integer <= add) {
// overflow detected - we disallow uint64_t max.
LOG(ERROR) << "overflow integer=" << integer << " add=" << add;
return DecodeError::INTEGER_OVERFLOW;
}
integer += add;
f = f << 7;
fexp += 7;
} while (byte & 128);
return DecodeError::NONE;
}
namespace HPACK {
std::ostream& operator<<(std::ostream& os, DecodeError err) {
return os << static_cast<uint32_t>(err);
}
} // namespace HPACK
} // namespace proxygen