proxygen
proxygen::GzipHeaderCodec Class Reference

#include <GzipHeaderCodec.h>

Inheritance diagram for proxygen::GzipHeaderCodec:
proxygen::HeaderCodec

Public Member Functions

 GzipHeaderCodec (int compressionLevel, const SPDYVersionSettings &versionSettings)
 
 GzipHeaderCodec (int compressionLevel, SPDYVersion version=SPDYVersion::SPDY3_1)
 
 ~GzipHeaderCodec () override
 
std::unique_ptr< folly::IOBufencode (std::vector< compress::Header > &headers) noexcept
 
folly::Expected< HeaderDecodeResult, GzipDecodeErrordecode (folly::io::Cursor &cursor, uint32_t length) noexcept
 
const HTTPHeaderSizegetDecodedSize ()
 
- Public Member Functions inherited from proxygen::HeaderCodec
 HeaderCodec ()
 
virtual ~HeaderCodec ()
 
const HTTPHeaderSizegetEncodedSize ()
 
void setEncodeHeadroom (uint32_t headroom)
 
virtual void setMaxUncompressed (uint64_t maxUncompressed)
 
uint64_t getMaxUncompressed () const
 
void setStats (Stats *stats)
 

Private Member Functions

folly::IOBufgetHeaderBuf ()
 
folly::Expected< size_t, GzipDecodeErrorparseNameValues (const folly::IOBuf &uncompressed, uint32_t uncompressedLength) noexcept
 

Private Attributes

const SPDYVersionSettingsversionSettings_
 
z_stream deflater_
 
z_stream inflater_
 
compress::HeaderPieceList outHeaders_
 
HTTPHeaderSize decodedSize_
 

Additional Inherited Members

- Public Types inherited from proxygen::HeaderCodec
enum  Type : uint8_t { Type::GZIP = 0, Type::HPACK = 1, Type::QPACK = 2 }
 
- Static Public Attributes inherited from proxygen::HeaderCodec
static const uint32_t kMaxUncompressed = 128 * 1024
 
- Protected Attributes inherited from proxygen::HeaderCodec
HTTPHeaderSize encodedSize_
 
uint32_t encodeHeadroom_ {0}
 
uint64_t maxUncompressed_ {kMaxUncompressed}
 
Statsstats_ {nullptr}
 

Detailed Description

Definition at line 34 of file GzipHeaderCodec.h.

Constructor & Destructor Documentation

proxygen::GzipHeaderCodec::GzipHeaderCodec ( int  compressionLevel,
const SPDYVersionSettings versionSettings 
)

Definition at line 156 of file GzipHeaderCodec.cpp.

References context, deflater_, and inflater_.

158  : versionSettings_(versionSettings) {
159  // Create compression and decompression contexts by cloning thread-local
160  // copies of the initial SPDY compression state
161  auto context = getZlibContext(versionSettings, compressionLevel);
162  deflateCopy(&deflater_, const_cast<z_stream*>(&(context->deflater)));
163  inflateCopy(&inflater_, const_cast<z_stream*>(&(context->inflater)));
164 }
context
Definition: CMakeCache.txt:563
const SPDYVersionSettings & versionSettings_
proxygen::GzipHeaderCodec::GzipHeaderCodec ( int  compressionLevel,
SPDYVersion  version = SPDYVersion::SPDY3_1 
)
explicit

Definition at line 166 of file GzipHeaderCodec.cpp.

168  : GzipHeaderCodec(
169  compressionLevel,
ProtocolVersion version
static const SPDYVersionSettings & getVersionSettings(SPDYVersion version)
Definition: SPDYCodec.cpp:138
GzipHeaderCodec(int compressionLevel, const SPDYVersionSettings &versionSettings)
proxygen::GzipHeaderCodec::~GzipHeaderCodec ( )
override

Definition at line 172 of file GzipHeaderCodec.cpp.

References deflater_, and inflater_.

172  {
173  deflateEnd(&deflater_);
174  inflateEnd(&inflater_);
175 }

Member Function Documentation

folly::Expected< HeaderDecodeResult, GzipDecodeError > proxygen::GzipHeaderCodec::decode ( folly::io::Cursor cursor,
uint32_t  length 
)
noexcept

Definition at line 291 of file GzipHeaderCodec.cpp.

References proxygen::BAD_ENCODING, wangle::HTTPHeaderSize::compressed, decodedSize_, proxygen::SPDYVersionSettings::dict, proxygen::SPDYVersionSettings::dictSize, proxygen::ERROR, getHeaderBuf(), proxygen::HeaderCodec::GZIP, proxygen::HEADERS_TOO_LARGE, if(), proxygen::INFLATE_DICTIONARY, inflater_, folly::makeUnexpected(), proxygen::HeaderCodec::maxUncompressed_, min, cpp.ast::next(), outHeaders_, parseNameValues(), proxygen::HeaderCodec::Stats::recordDecode(), proxygen::HeaderCodec::stats_, uint32_t, uint8_t, wangle::HTTPHeaderSize::uncompressed, UNLIKELY, and versionSettings_.

Referenced by proxygen::SPDYCodec::decodeHeaders().

291  {
292  outHeaders_.clear();
293 
294  // empty header block
295  if (length == 0) {
296  return HeaderDecodeResult{outHeaders_, 0};
297  }
298 
299  // Get the thread local buffer space to use
300  auto& uncompressed = getHeaderBuf();
301  uint32_t consumed = 0;
302  // Decompress the headers
303  while (length > 0) {
304  auto next = cursor.peek();
305  uint32_t chunkLen = std::min((uint32_t)next.second, length);
306  inflater_.avail_in = chunkLen;
307  inflater_.next_in = (uint8_t *)next.first;
308  do {
309  if (uncompressed.tailroom() == 0) {
310  // This code should not execute, since we throw an error if the
311  // decompressed size of the headers is too large and we initialize
312  // the buffer to that size.
313  LOG(ERROR) << "Doubling capacity of SPDY headers buffer";
314  uncompressed.reserve(0, uncompressed.capacity());
315  }
316 
317  inflater_.next_out = uncompressed.writableTail();
318  inflater_.avail_out = uncompressed.tailroom();
319  int r = inflate(&inflater_, Z_NO_FLUSH);
320  if (r == Z_NEED_DICT) {
321  // we cannot initialize the inflater dictionary before calling inflate()
322  // as it checks the adler-32 checksum of the supplied dictionary
323  r = inflateSetDictionary(&inflater_, versionSettings_.dict,
325  if (r != Z_OK) {
326  LOG(ERROR) << "inflate set dictionary failed with error=" << r;
328  }
329  inflater_.avail_out = 0;
330  continue;
331  }
332  if (r != 0) {
333  // probably bad encoding
334  LOG(ERROR) << "inflate failed with error=" << r;
336  }
337  uncompressed.append(uncompressed.tailroom() - inflater_.avail_out);
338  if (uncompressed.length() > maxUncompressed_) {
339  LOG(ERROR) << "Decompressed headers too large";
341  }
342  } while (inflater_.avail_in > 0 && inflater_.avail_out == 0);
343  length -= chunkLen;
344  consumed += chunkLen;
345  cursor.skip(chunkLen);
346  }
347 
348  decodedSize_.compressed = consumed;
349  decodedSize_.uncompressed = uncompressed.computeChainDataLength();
350  if (stats_) {
352  }
353 
354  size_t expandedHeaderLineBytes = 0;
355  auto result = parseNameValues(uncompressed, decodedSize_.uncompressed);
356  if (result.hasError()) {
357  return folly::makeUnexpected(result.error());
358  }
359  expandedHeaderLineBytes = *result;
360 
361  if (UNLIKELY(expandedHeaderLineBytes > kMaxExpandedHeaderLineBytes)) {
362  LOG(ERROR) << "expanded headers too large";
364  }
365 
366  return HeaderDecodeResult{outHeaders_, consumed};
367 }
const SPDYVersionSettings & versionSettings_
uint64_t maxUncompressed_
Definition: HeaderCodec.h:92
std::pair< const uint8_t *, size_t > peek()
Definition: Cursor.h:451
LogLevel min
Definition: LogLevel.cpp:30
constexpr Unexpected< typename std::decay< Error >::type > makeUnexpected(Error &&)
Definition: Expected.h:785
if(FOLLY_USE_SYMBOLIZER) add_library(folly_exception_tracer_base ExceptionTracer.cpp StackTrace.cpp) apply_folly_compile_options_to_target(folly_exception_tracer_base) target_link_libraries(folly_exception_tracer_base PUBLIC folly) add_library(folly_exception_tracer ExceptionStackTraceLib.cpp ExceptionTracerLib.cpp) apply_folly_compile_options_to_target(folly_exception_tracer) target_link_libraries(folly_exception_tracer PUBLIC folly_exception_tracer_base) add_library(folly_exception_counter ExceptionCounterLib.cpp) apply_folly_compile_options_to_target(folly_exception_counter) target_link_libraries(folly_exception_counter PUBLIC folly_exception_tracer) install(FILES ExceptionAbi.h ExceptionCounterLib.h ExceptionTracer.h ExceptionTracerLib.h StackTrace.h DESTINATION $
Definition: CMakeLists.txt:1
virtual void recordDecode(Type type, HTTPHeaderSize &size)=0
void skip(size_t len)
Definition: Cursor.h:371
compress::HeaderPieceList outHeaders_
folly::Expected< size_t, GzipDecodeError > parseNameValues(const folly::IOBuf &uncompressed, uint32_t uncompressedLength) noexcept
#define UNLIKELY(x)
Definition: Likely.h:48
def next(obj)
Definition: ast.py:58
unique_ptr< IOBuf > proxygen::GzipHeaderCodec::encode ( std::vector< compress::Header > &  headers)
noexcept

Definition at line 181 of file GzipHeaderCodec.cpp.

References folly::IOBuf::advance(), folly::IOBuf::append(), proxygen::SPDYVersionSettings::appendSizeFun, wangle::HTTPHeaderSize::compressed, deflater_, proxygen::empty_string, proxygen::HeaderCodec::encodedSize_, proxygen::HeaderCodec::encodeHeadroom_, getHeaderBuf(), proxygen::HeaderCodec::GZIP, proxygen::HTTP_HEADER_OTHER, folly::IOBuf::length(), proxygen::SPDYVersionSettings::nameValueSize, proxygen::HeaderCodec::Stats::recordEncode(), proxygen::HeaderCodec::stats_, folly::toLowerAscii(), uint8_t, wangle::HTTPHeaderSize::uncompressed, versionSettings_, and folly::IOBuf::writableData().

Referenced by proxygen::SPDYCodec::encodeHeaders().

181  {
182  // Build a sequence of the header names and values, sorted by name.
183  // The purpose of the sort is to make it easier to combine the
184  // values of multiple headers with the same name. The SPDY spec
185  // prohibits any header name from appearing more than once in the
186  // Name/Value list, so we must combine values when serializing.
187  std::sort(headers.begin(), headers.end());
188 
189  auto& uncompressed = getHeaderBuf();
190  // Compute the amount of space needed to hold the uncompressed
191  // representation of the headers. This is an upper bound on the
192  // amount of space we'll actually need, because if we end up
193  // combining any headers with the same name, the combined
194  // representation will be smaller than the original.
195  size_t maxUncompressedSize = versionSettings_.nameValueSize;
196  for (const Header& header : headers) {
197  maxUncompressedSize += versionSettings_.nameValueSize;
198  maxUncompressedSize += header.name->length();
199  maxUncompressedSize += versionSettings_.nameValueSize;
200  maxUncompressedSize += header.value->length();
201  }
202 
203  // TODO: give on 'onError()' callback if the space in uncompressed buf
204  // cannot fit the headers and then skip the "reserve" code below. We
205  // have already reserved the maximum legal amount of space for
206  // uncompressed headers.
207 
208  VLOG(5) << "reserving " << maxUncompressedSize
209  << " bytes for uncompressed headers";
210  uncompressed.reserve(0, maxUncompressedSize);
211 
212  // Serialize the uncompressed representation of the headers.
213  uint8_t* dst = uncompressed.writableData();
214  dst += versionSettings_.nameValueSize; // Leave space for count of headers.
216  const string* lastName = &empty_string;
217  uint8_t* lastValueLenPtr = nullptr;
218  size_t lastValueLen = 0;
219  unsigned numHeaders = 0;
220  for (const Header& header : headers) {
221  if ((header.code != lastCode) || (*header.name != *lastName)) {
222  // Simple case: this header name is different from the previous
223  // one, so we don't need to combine values.
224  numHeaders++;
225  versionSettings_.appendSizeFun(dst, header.name->length());
226 
227  // lowercasing the header name inline
228  char* nameBegin = (char *)dst;
229  appendString(dst, *header.name);
230  folly::toLowerAscii((char *)nameBegin, header.name->size());
231 
232  lastValueLenPtr = dst;
233  lastValueLen = header.value->length();
234  versionSettings_.appendSizeFun(dst, header.value->length());
235  appendString(dst, *header.value);
236  lastCode = header.code;
237  lastName = header.name;
238  } else if (header.value->length() > 0) {
239  // More complicated case: we do need to combine values.
240  if (lastValueLen > 0) {
241  // Only nul terminate if previous value was non-empty
242  *dst++ = 0; // SPDY uses a null byte as a separator
243  lastValueLen++;
244  }
245  appendString(dst, *header.value);
246  // Go back and rewrite the length field in front of the value
247  lastValueLen += header.value->length();
248  uint8_t* tmp = lastValueLenPtr;
249  versionSettings_.appendSizeFun(tmp, lastValueLen);
250  }
251  }
252 
253  // Compute the uncompressed length; if we combined any header values,
254  // we will have used less space than originally estimated.
255  size_t uncompressedLen = dst - uncompressed.writableData();
256 
257  // Go back and write the count of unique header names at the start.
258  dst = uncompressed.writableData();
259  versionSettings_.appendSizeFun(dst, numHeaders);
260 
261  // Allocate a contiguous space big enough to hold the compressed headers,
262  // plus any headroom requested by the caller.
263  size_t maxDeflatedSize = deflateBound(&deflater_, uncompressedLen);
264  unique_ptr<IOBuf> out(IOBuf::create(maxDeflatedSize + encodeHeadroom_));
265  out->advance(encodeHeadroom_);
266 
267  // Compress
268  deflater_.next_in = uncompressed.writableData();
269  deflater_.avail_in = uncompressedLen;
270  deflater_.next_out = out->writableData();
271  deflater_.avail_out = maxDeflatedSize;
272  int r = deflate(&deflater_, Z_SYNC_FLUSH);
273  CHECK_EQ(r, Z_OK);
274  CHECK_EQ(deflater_.avail_in, 0);
275  out->append(maxDeflatedSize - deflater_.avail_out);
276 
277  VLOG(4) << "header size orig=" << uncompressedLen
278  << ", max deflated=" << maxDeflatedSize
279  << ", actual deflated=" << out->length();
280 
281  encodedSize_.compressed = out->length();
282  encodedSize_.uncompressed = uncompressedLen;
283  if (stats_) {
285  }
286 
287  return out;
288 }
const SPDYVersionSettings & versionSettings_
HTTPHeaderSize encodedSize_
Definition: HeaderCodec.h:90
uint32_t encodeHeadroom_
Definition: HeaderCodec.h:91
void(* appendSizeFun)(uint8_t *&, size_t)
const std::string empty_string
Definition: HTTPHeaders.cpp:23
void toLowerAscii(char *str, size_t length)
Definition: String.cpp:601
virtual void recordEncode(Type type, HTTPHeaderSize &size)=0
const HTTPHeaderSize& proxygen::GzipHeaderCodec::getDecodedSize ( )
inline

same as above, but for decode

Definition at line 52 of file GzipHeaderCodec.h.

References folly::pushmi::__adl::noexcept(), and uint32_t.

Referenced by proxygen::SPDYCodec::onControlFrame().

52  {
53  return decodedSize_;
54  }
folly::IOBuf & proxygen::GzipHeaderCodec::getHeaderBuf ( )
private

Definition at line 177 of file GzipHeaderCodec.cpp.

References proxygen::HeaderCodec::maxUncompressed_.

Referenced by decode(), and encode().

177  {
178  return getStaticHeaderBufSpace(maxUncompressed_);
179 }
uint64_t maxUncompressed_
Definition: HeaderCodec.h:92
folly::Expected< size_t, GzipDecodeError > proxygen::GzipHeaderCodec::parseNameValues ( const folly::IOBuf uncompressed,
uint32_t  uncompressedLength 
)
privatenoexcept

Parse the decompressed name/value header block.

Definition at line 370 of file GzipHeaderCodec.cpp.

References proxygen::BAD_ENCODING, c, data, folly::Range< Iter >::data(), proxygen::EMPTY_HEADER_NAME, proxygen::EMPTY_HEADER_VALUE, proxygen::ERROR, folly::exceptionStr(), folly::gen::first, i, proxygen::INVALID_HEADER_VALUE, folly::makeUnexpected(), proxygen::SPDYVersionSettings::nameValueSize, cpp.ast::next(), outHeaders_, proxygen::SPDYVersionSettings::parseSizeFun, folly::io::detail::CursorBase< Derived, BufType >::peek(), proxygen::IOBufPrinter::printHexFolly(), folly::io::detail::CursorBase< Derived, BufType >::pull(), folly::Range< Iter >::reset(), folly::Range< Iter >::size(), folly::io::detail::CursorBase< Derived, BufType >::skip(), stop(), proxygen::compress::HeaderPiece::str, uint32_t, and versionSettings_.

Referenced by decode().

371  {
372 
373  size_t expandedHeaderLineBytes = 0;
374  Cursor headerCursor(&uncompressed);
375  uint32_t numNV = 0;
376  const HeaderPiece* headerName = nullptr;
377 
378  try {
379  numNV = versionSettings_.parseSizeFun(&headerCursor);
380  } catch (const std::out_of_range& ex) {
382  }
383 
384  for (uint32_t i = 0; i < numNV * 2; i++) {
385  uint32_t len = 0;
386  try {
387  len = versionSettings_.parseSizeFun(&headerCursor);
388  uncompressedLength -= versionSettings_.nameValueSize;
389  } catch (const std::out_of_range& ex) {
391  }
392 
393  if (len == 0 && !headerName) {
394  LOG(ERROR) << "empty header name";
396  }
397  auto next = headerCursor.peek();
398  try {
399  if (len > uncompressedLength) {
400  throw std::out_of_range(
401  folly::to<string>("bad length=", len, " uncompressedLength=",
402  uncompressedLength));
403  } else if (next.second >= len) {
404  // string is contiguous, just put a pointer into the headers structure
405  outHeaders_.emplace_back((char *)next.first, len, false, false);
406  headerCursor.skip(len);
407  } else {
408  // string is not contiguous, allocate a buffer and pull into it
409  unique_ptr<char[]> data (new char[len]);
410  headerCursor.pull(data.get(), len);
411  outHeaders_.emplace_back(data.release(), len, true, false);
412  }
413  uncompressedLength -= len;
414  } catch (const std::out_of_range& ex) {
415  LOG(ERROR) << "bad encoding for nv=" << i << ": "
416  << folly::exceptionStr(ex);
417  VLOG(3) << IOBufPrinter::printHexFolly(&uncompressed, true);
419  }
420  if (i % 2 == 0) {
421  headerName = &outHeaders_.back();
422  for (const char c: headerName->str) {
423  if (c < 0x20 || c > 0x7e || ('A' <= c && c <= 'Z')) {
424  LOG(ERROR) << "invalid header value";
426  }
427  }
428  } else {
429  HeaderPiece& headerValue = outHeaders_.back();
430  bool first = true;
431  const char* valueStart = headerValue.str.data();
432  const char* pos = valueStart;
433  const char* stop = valueStart + headerValue.str.size();
434  while(pos < stop) {
435  if (*pos == '\0') {
436  if (pos - valueStart == 0) {
437  LOG(ERROR) << "empty header value for header=" << headerName;
439  }
440  if (first) {
441  headerValue.str.reset(valueStart, pos - valueStart);
442  first = false;
443  } else {
444  outHeaders_.emplace_back(headerName->str.data(),
445  headerName->str.size(),
446  false, true);
447  outHeaders_.emplace_back(valueStart, pos - valueStart, false, true);
448  expandedHeaderLineBytes += ((pos - valueStart) +
449  headerName->str.size());
450  }
451  valueStart = pos + 1;
452  }
453  pos++;
454  }
455  if (!first) {
456  // value contained at least one \0, add the last value
457  if (pos - valueStart == 0) {
458  LOG(ERROR) << "empty header value for header=" << headerName;
460  }
461  outHeaders_.emplace_back(headerName->str.data(),
462  headerName->str.size(),
463  false, true);
464  outHeaders_.emplace_back(valueStart, pos - valueStart, false, true);
465  expandedHeaderLineBytes += (pos - valueStart) + headerName->str.size();
466  }
467  headerName = nullptr;
468  }
469  }
470  return expandedHeaderLineBytes;
471 }
static std::string printHexFolly(const folly::IOBuf *buf, bool coalesce=false)
Definition: Logging.h:127
fbstring exceptionStr(const std::exception &e)
const SPDYVersionSettings & versionSettings_
constexpr size_type size() const
Definition: Range.h:431
static void stop()
constexpr Unexpected< typename std::decay< Error >::type > makeUnexpected(Error &&)
Definition: Expected.h:785
constexpr Iter data() const
Definition: Range.h:446
compress::HeaderPieceList outHeaders_
char c
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
uint32_t(* parseSizeFun)(folly::io::Cursor *)
void reset(Iter start, size_type size)
Definition: Range.h:421
constexpr detail::First first
Definition: Base-inl.h:2553
def next(obj)
Definition: ast.py:58

Member Data Documentation

HTTPHeaderSize proxygen::GzipHeaderCodec::decodedSize_
private

Definition at line 70 of file GzipHeaderCodec.h.

Referenced by decode().

z_stream proxygen::GzipHeaderCodec::deflater_
private

Definition at line 67 of file GzipHeaderCodec.h.

Referenced by encode(), GzipHeaderCodec(), and ~GzipHeaderCodec().

z_stream proxygen::GzipHeaderCodec::inflater_
private

Definition at line 68 of file GzipHeaderCodec.h.

Referenced by decode(), GzipHeaderCodec(), and ~GzipHeaderCodec().

compress::HeaderPieceList proxygen::GzipHeaderCodec::outHeaders_
private

Definition at line 69 of file GzipHeaderCodec.h.

Referenced by decode(), and parseNameValues().

const SPDYVersionSettings& proxygen::GzipHeaderCodec::versionSettings_
private

Definition at line 66 of file GzipHeaderCodec.h.

Referenced by decode(), encode(), and parseNameValues().


The documentation for this class was generated from the following files: