Skip to content

Commit 0864365

Browse files
epistorBillyONeal
authored andcommitted
Fix Brotli compress_helper early termination issue (#963)
1 parent f3824aa commit 0864365

File tree

4 files changed

+316
-280
lines changed

4 files changed

+316
-280
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,16 @@ namespace details
423423
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
424424
}
425425

426+
template <typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5>
427+
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5) {
428+
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4), std::forward<_Arg5>(arg5)));
429+
}
430+
431+
template <typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4, typename _Arg5, typename _Arg6>
432+
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4, _Arg5&& arg5, _Arg6&& arg6) {
433+
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4), std::forward<_Arg5>(arg5), std::forward<_Arg6>(arg6)));
434+
}
435+
426436
/// <summary>
427437
/// Cross platform utility function for performing case insensitive string equality comparison.
428438
/// </summary>

Release/include/cpprest/http_compression.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ _ASYNCRTIMP std::unique_ptr<compress_provider> make_deflate_compressor(int compr
206206
/// A caller-owned pointer to a Brotli compression provider, or to nullptr if the library was built without built-in
207207
/// compression support.
208208
/// </returns>
209-
_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(uint32_t window, uint32_t quality, uint32_t mode);
209+
_ASYNCRTIMP std::unique_ptr<compress_provider> make_brotli_compressor(
210+
uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint);
210211
} // namespace builtin
211212

212213
/// <summary>

Release/src/http/common/http_compression.cpp

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ class zlib_compressor_base : public compress_provider
8686

8787
if (m_state != Z_OK && m_state != Z_BUF_ERROR && m_state != Z_STREAM_ERROR)
8888
{
89-
throw std::runtime_error("Prior unrecoverable compression stream error " +
90-
std::to_string(m_state));
89+
throw std::runtime_error("Prior unrecoverable compression stream error " + std::to_string(m_state));
9190
}
9291

9392
if (input_size > std::numeric_limits<uInt>::max() || output_size > std::numeric_limits<uInt>::max())
@@ -294,8 +293,17 @@ class brotli_compressor : public compress_provider
294293

295294
brotli_compressor(uint32_t window = BROTLI_DEFAULT_WINDOW,
296295
uint32_t quality = BROTLI_DEFAULT_QUALITY,
297-
uint32_t mode = BROTLI_DEFAULT_MODE)
298-
: m_algorithm(BROTLI), m_window(window), m_quality(quality), m_mode(mode)
296+
uint32_t mode = BROTLI_DEFAULT_MODE,
297+
uint32_t block = 0,
298+
uint32_t nomodel = 0,
299+
uint32_t hint = 0)
300+
: m_algorithm(BROTLI)
301+
, m_window(window)
302+
, m_quality(quality)
303+
, m_mode(mode)
304+
, m_block(block)
305+
, m_nomodel(nomodel)
306+
, m_hint(hint)
299307
{
300308
(void)reset();
301309
}
@@ -323,42 +331,36 @@ class brotli_compressor : public compress_provider
323331
}
324332

325333
const uint8_t* next_in = input;
326-
size_t avail_in;
334+
size_t avail_in = 0;
327335
uint8_t* next_out = output;
328336
size_t avail_out = output_size;
329337
size_t total_out;
330338

331339
if (BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE)
332340
{
333-
avail_in = 0;
341+
// Drain any compressed bytes remaining from a prior call
334342
do
335343
{
336-
m_state = BrotliEncoderCompressStream(m_stream,
337-
(hint == operation_hint::is_last) ? BROTLI_OPERATION_FINISH
338-
: BROTLI_OPERATION_FLUSH,
339-
&avail_in,
340-
&next_in,
341-
&avail_out,
342-
&next_out,
343-
&total_out);
344+
m_state = BrotliEncoderCompressStream(
345+
m_stream, BROTLI_OPERATION_FLUSH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
344346
} while (m_state == BROTLI_TRUE && avail_out && BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE);
345347
}
346348

347-
if (m_state == BROTLI_TRUE && avail_out)
349+
if (m_state == BROTLI_TRUE && avail_out && input_size)
348350
{
351+
// Compress the caller-supplied buffer
349352
avail_in = input_size;
350353
do
351354
{
352-
m_state = BrotliEncoderCompressStream(m_stream,
353-
(hint == operation_hint::is_last) ? BROTLI_OPERATION_FINISH
354-
: BROTLI_OPERATION_FLUSH,
355-
&avail_in,
356-
&next_in,
357-
&avail_out,
358-
&next_out,
359-
&total_out);
355+
m_state = BrotliEncoderCompressStream(
356+
m_stream, BROTLI_OPERATION_FLUSH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
360357
} while (m_state == BROTLI_TRUE && avail_out && BrotliEncoderHasMoreOutput(m_stream) == BROTLI_TRUE);
361358
}
359+
else
360+
{
361+
// We're not compressing any new data; ensure calculation sanity
362+
input_size = 0;
363+
}
362364

363365
if (m_state != BROTLI_TRUE)
364366
{
@@ -367,7 +369,18 @@ class brotli_compressor : public compress_provider
367369

368370
if (hint == operation_hint::is_last)
369371
{
370-
m_done = (BrotliEncoderIsFinished(m_stream) == BROTLI_TRUE);
372+
if (avail_out)
373+
{
374+
// Make one more pass to finalize the compressed stream
375+
_ASSERTE(!avail_in);
376+
m_state = BrotliEncoderCompressStream(
377+
m_stream, BROTLI_OPERATION_FINISH, &avail_in, &next_in, &avail_out, &next_out, &total_out);
378+
if (m_state != BROTLI_TRUE)
379+
{
380+
throw std::runtime_error("Unrecoverable error finalizing compression stream");
381+
}
382+
m_done = (BrotliEncoderIsFinished(m_stream) == BROTLI_TRUE);
383+
}
371384
}
372385

373386
input_bytes_processed = input_size - avail_in;
@@ -415,7 +428,19 @@ class brotli_compressor : public compress_provider
415428
}
416429
if (m_state == BROTLI_TRUE && m_mode != BROTLI_DEFAULT_MODE)
417430
{
418-
m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_MODE, m_window);
431+
m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_MODE, m_mode);
432+
}
433+
if (m_state == BROTLI_TRUE && m_block != 0)
434+
{
435+
m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_LGBLOCK, m_block);
436+
}
437+
if (m_state == BROTLI_TRUE && m_nomodel != 0)
438+
{
439+
m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING, m_nomodel);
440+
}
441+
if (m_state == BROTLI_TRUE && m_hint != 0)
442+
{
443+
m_state = BrotliEncoderSetParameter(m_stream, BROTLI_PARAM_SIZE_HINT, m_hint);
419444
}
420445

421446
if (m_state != BROTLI_TRUE)
@@ -439,6 +464,9 @@ class brotli_compressor : public compress_provider
439464
uint32_t m_window;
440465
uint32_t m_quality;
441466
uint32_t m_mode;
467+
uint32_t m_block;
468+
uint32_t m_nomodel;
469+
uint32_t m_hint;
442470
const utility::string_t& m_algorithm;
443471
};
444472

@@ -599,7 +627,8 @@ class generic_decompress_factory : public decompress_factory
599627
static const std::vector<std::shared_ptr<compress_factory>> g_compress_factories
600628
#if defined(CPPREST_HTTP_COMPRESSION)
601629
= {std::make_shared<generic_compress_factory>(
602-
algorithm::GZIP, []() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<gzip_compressor>(); }),
630+
algorithm::GZIP,
631+
[]() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<gzip_compressor>(); }),
603632
std::make_shared<generic_compress_factory>(
604633
algorithm::DEFLATE,
605634
[]() -> std::unique_ptr<compress_provider> { return utility::details::make_unique<deflate_compressor>(); }),
@@ -619,15 +648,17 @@ static const std::vector<std::shared_ptr<decompress_factory>> g_decompress_facto
619648
algorithm::GZIP,
620649
500,
621650
[]() -> std::unique_ptr<decompress_provider> { return utility::details::make_unique<gzip_decompressor>(); }),
622-
std::make_shared<generic_decompress_factory>(
623-
algorithm::DEFLATE,
624-
500,
625-
[]() -> std::unique_ptr<decompress_provider> { return utility::details::make_unique<deflate_decompressor>(); }),
651+
std::make_shared<generic_decompress_factory>(algorithm::DEFLATE,
652+
500,
653+
[]() -> std::unique_ptr<decompress_provider> {
654+
return utility::details::make_unique<deflate_decompressor>();
655+
}),
626656
#if defined(CPPREST_BROTLI_COMPRESSION)
627-
std::make_shared<generic_decompress_factory>(
628-
algorithm::BROTLI,
629-
500,
630-
[]() -> std::unique_ptr<decompress_provider> { return utility::details::make_unique<brotli_decompressor>(); })
657+
std::make_shared<generic_decompress_factory>(algorithm::BROTLI,
658+
500,
659+
[]() -> std::unique_ptr<decompress_provider> {
660+
return utility::details::make_unique<brotli_decompressor>();
661+
})
631662
#endif // CPPREST_BROTLI_COMPRESSION
632663
};
633664
#else // CPPREST_HTTP_COMPRESSION
@@ -713,7 +744,6 @@ std::shared_ptr<decompress_factory> get_decompress_factory(const utility::string
713744
return std::shared_ptr<decompress_factory>();
714745
}
715746

716-
717747
std::unique_ptr<compress_provider> make_gzip_compressor(int compressionLevel, int method, int strategy, int memLevel)
718748
{
719749
#if defined(CPPREST_HTTP_COMPRESSION)
@@ -740,14 +770,18 @@ std::unique_ptr<compress_provider> make_deflate_compressor(int compressionLevel,
740770
#endif // CPPREST_HTTP_COMPRESSION
741771
}
742772

743-
std::unique_ptr<compress_provider> make_brotli_compressor(uint32_t window, uint32_t quality, uint32_t mode)
773+
std::unique_ptr<compress_provider> make_brotli_compressor(
774+
uint32_t window, uint32_t quality, uint32_t mode, uint32_t block, uint32_t nomodel, uint32_t hint)
744775
{
745776
#if defined(CPPREST_HTTP_COMPRESSION) && defined(CPPREST_BROTLI_COMPRESSION)
746-
return utility::details::make_unique<brotli_compressor>(window, quality, mode);
777+
return utility::details::make_unique<brotli_compressor>(window, quality, mode, block, nomodel, hint);
747778
#else // CPPREST_BROTLI_COMPRESSION
748779
(void)window;
749780
(void)quality;
750781
(void)mode;
782+
(void)block;
783+
(void)nomodel;
784+
(void)hint;
751785
return std::unique_ptr<compress_provider>();
752786
#endif // CPPREST_BROTLI_COMPRESSION
753787
}

0 commit comments

Comments
 (0)