aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjade <lix@jade.fyi>2024-09-18 23:36:25 +0000
committerGerrit Code Review <gerrit@localhost>2024-09-18 23:36:25 +0000
commit79246a37337c5df2224dbc2461c722e1e678f6de (patch)
treea75bf243c2714564a1f0596e9771e820aa672a63 /src
parent0943b214c9d664453560c6490ee769d20714201b (diff)
parent789b19a0cfe583586c85657e88d5933d2dbe5715 (diff)
Merge "util: fix brotli decompression of empty input" into main
Diffstat (limited to 'src')
-rw-r--r--src/libstore/filetransfer.cc2
-rw-r--r--src/libutil/compression.cc30
-rw-r--r--src/libutil/serialise.hh5
3 files changed, 31 insertions, 6 deletions
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index f3e8a5312..10c810e49 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -337,7 +337,7 @@ struct curlFileTransfer : public FileTransfer
// wrapping user `callback`s instead is not possible because the
// Callback api expects std::functions, and copying Callbacks is
// not possible due the promises they hold.
- if (code == CURLE_OK && !dataCallback) {
+ if (code == CURLE_OK && !dataCallback && result.data.length() > 0) {
result.data = decompress(encoding, result.data);
}
diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc
index 5152a2146..51c820d55 100644
--- a/src/libutil/compression.cc
+++ b/src/libutil/compression.cc
@@ -144,6 +144,7 @@ struct BrotliDecompressionSource : Source
std::unique_ptr<char[]> buf;
size_t avail_in = 0;
const uint8_t * next_in;
+ std::exception_ptr inputEofException = nullptr;
Source * inner;
std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState *)> state;
@@ -167,23 +168,42 @@ struct BrotliDecompressionSource : Source
while (len && !BrotliDecoderIsFinished(state.get())) {
checkInterrupt();
- while (avail_in == 0) {
+ while (avail_in == 0 && inputEofException == nullptr) {
try {
avail_in = inner->read(buf.get(), BUF_SIZE);
} catch (EndOfFile &) {
+ // No more data, but brotli may still have output remaining
+ // from the last call.
+ inputEofException = std::current_exception();
break;
}
next_in = charptr_cast<const uint8_t *>(buf.get());
}
- if (!BrotliDecoderDecompressStream(
- state.get(), &avail_in, &next_in, &len, &out, nullptr
- ))
- {
+ BrotliDecoderResult res = BrotliDecoderDecompressStream(
+ state.get(), &avail_in, &next_in, &len, &out, nullptr
+ );
+
+ switch (res) {
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ // We're done here!
+ goto finish;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ // Grab more input. Don't try if we already have exhausted our input stream.
+ if (inputEofException != nullptr) {
+ std::rethrow_exception(inputEofException);
+ } else {
+ continue;
+ }
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ // Need more output space: we can only get another buffer by someone calling us again, so get out.
+ goto finish;
+ case BROTLI_DECODER_RESULT_ERROR:
throw CompressionError("error while decompressing brotli file");
}
}
+finish:
if (begin != out) {
return out - begin;
} else {
diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh
index 612658b2d..3a9685e0e 100644
--- a/src/libutil/serialise.hh
+++ b/src/libutil/serialise.hh
@@ -77,6 +77,11 @@ struct Source
* Store up to ‘len’ in the buffer pointed to by ‘data’, and
* return the number of bytes stored. It blocks until at least
* one byte is available.
+ *
+ * Should not return 0 (generally you want to throw EndOfFile), but nothing
+ * stops that.
+ *
+ * \throws EndOfFile if there is no more data.
*/
virtual size_t read(char * data, size_t len) = 0;