Nothing
# zu_decoder_reset(): public ABI that had no caller outside its own
# definition.
#
# It has more state to get right than the encoder's -- total_in/total_out and
# so every limit budget, the wrapper state machine, the gzip header parser,
# and miniz's own stream. The shape that matters is a keep-alive connection
# decoding a second response body through the handle that decoded the first.
test_that("a reset decoder decodes a second message identically to a fresh one", {
for (codec in c("identity", "deflate-raw", "zlib", "gzip")) {
for (kind in c("ascii", "utf8", "structured", "lcg")) {
a <- new_payload(kind, 2000)
b <- new_payload(kind, 3500)
got <- zu_test_decoder_reset(komp_compress(a, codec),
komp_compress(b, codec), codec)
expect_identical(got, c(a, b), info = paste(codec, kind))
}
}
})
test_that("the second message need not resemble the first", {
# Everything that could carry over is different between the two: length,
# content, compressibility, and for gzip the whole header/trailer cycle.
for (codec in c("deflate-raw", "zlib", "gzip")) {
a <- new_payload("ascii", 30000) # highly compressible
b <- new_payload("lcg", 17) # incompressible, and tiny
got <- zu_test_decoder_reset(komp_compress(a, codec),
komp_compress(b, codec), codec)
expect_identical(got, c(a, b), info = codec)
}
})
test_that("an empty message on either side of the reset still works", {
for (codec in c("identity", "deflate-raw", "zlib", "gzip")) {
x <- new_payload("ascii", 1000)
expect_identical(
zu_test_decoder_reset(komp_compress(raw(0), codec),
komp_compress(x, codec), codec),
x, info = codec)
expect_identical(
zu_test_decoder_reset(komp_compress(x, codec),
komp_compress(raw(0), codec), codec),
x, info = codec)
}
})
test_that("the limit budget restarts rather than carrying over", {
# This is the one that would bite zuhttp. total_out is per-stream, so a
# cap that the first message nearly spends must be fully available to the
# second. Carrying it over would make the second response on a connection
# fail a limit the first had already used.
for (codec in c("deflate-raw", "zlib", "gzip")) {
a <- new_payload("ascii", 4000)
b <- new_payload("ascii", 4000)
# A cap that comfortably fits either message alone, but not both.
cap <- 5000
got <- zu_test_decoder_reset(komp_compress(a, codec),
komp_compress(b, codec), codec,
max_output = cap)
expect_identical(got, c(a, b), info = codec)
}
})
test_that("the reset limit is still enforced on the second message", {
# The mirror of the test above: restarting the budget must not mean
# abandoning it. A second message over the cap still fails.
for (codec in c("deflate-raw", "zlib", "gzip")) {
a <- new_payload("ascii", 100)
b <- new_payload("ascii", 9000)
expect_error(
zu_test_decoder_reset(komp_compress(a, codec),
komp_compress(b, codec), codec,
max_output = 1000),
class = "zukomp_output_limit", info = codec
)
}
})
test_that("resetting onto a different codec is refused", {
# Reset re-parameterises one codec's stream; it does not switch codecs.
# The vtable is fixed at zu_decoder_new() time, so swapping means a new
# handle -- and a reset that appeared to succeed would decode the next
# message with the wrong state machine.
expect_error(zu_test_decoder_reset_codec("gzip", "zlib"),
class = "zukomp_invalid_argument")
expect_error(zu_test_decoder_reset_codec("zlib", "deflate-raw"),
class = "zukomp_invalid_argument")
expect_error(zu_test_decoder_reset_codec("identity", "gzip"),
class = "zukomp_invalid_argument")
# Naming the same codec is what a reset is, so that one succeeds.
expect_true(zu_test_decoder_reset_codec("gzip", "gzip"))
})
test_that("a multi-member gzip stream survives a reset", {
# The member state machine is the part most likely to be left mid-cycle:
# ST_MEMBER_END decides whether another member follows, and a reset that
# left that decision pending would mis-parse the next message's header.
skip_if_not(komp_codec_available("gzip"))
a <- new_payload("ascii", 800)
b <- new_payload("utf8", 1200)
multi <- c(komp_compress(a, "gzip"), komp_compress(b, "gzip"))
single <- komp_compress(a, "gzip")
expect_identical(zu_test_decoder_reset(multi, single, "gzip"),
c(a, b, a))
expect_identical(zu_test_decoder_reset(single, multi, "gzip"),
c(a, a, b))
})
test_that("a reset after a failed message still decodes the next one", {
# A decoder that hit an error must be usable again after a reset,
# otherwise a single malformed response poisons the connection.
#
# The assertion has to be that the *second* message comes back intact.
# Asserting only that the call errors passes whether reset-after-error
# works or is never reached at all -- which is exactly what happened when
# the harness loop was guarded on `st == ZU_OK`.
skip_if_not(komp_codec_available("gzip"))
codes <- zu_status_codes()
ok <- c(codes[["ZU_OK"]], codes[["ZU_STREAM_END"]])
x <- new_payload("ascii", 2000)
good <- komp_compress(x, "gzip")
bad <- good
bad[[length(bad) - 4L]] <- as.raw(bitwXor(as.integer(bad[[length(bad) - 4L]]), 0xFFL))
got <- zu_test_decoder_reset(bad, good, "gzip", allow_first_error = TRUE)
# The first message really did fail ...
expect_false(attr(got, "first_status") %in% ok)
# ... and the second one still decoded, in full, through the same handle.
expect_identical(utils::tail(as.raw(got), length(x)), x)
# Without opting in, a failed first message is still an error.
expect_error(zu_test_decoder_reset(bad, good, "gzip"), class = "zukomp_error")
})
test_that("a reset clears a truncated message's half-parsed state", {
# Truncation leaves the wrapper mid-field rather than at a clean error, so
# it is the harsher case for reset: the gzip header parser, the trailer
# cursor and miniz's stream are all part-way through.
skip_if_not(komp_codec_available("gzip"))
x <- new_payload("ascii", 3000)
good <- komp_compress(x, "gzip")
for (cut in c(3L, 8L, 12L, 40L)) {
got <- zu_test_decoder_reset(good[seq_len(cut)], good, "gzip",
allow_first_error = TRUE)
expect_identical(utils::tail(as.raw(got), length(x)), x, info = cut)
}
})
test_that("the harness's own sink exhaustion is not an output-limit error", {
# The harness decodes into a fixed 1 MiB buffer. Reporting that as
# ZU_ERR_OUTPUT_LIMIT -- the status the max_output tests assert on -- would
# let a harness overflow masquerade as the security limit firing, and the
# limit test would pass for the wrong reason.
skip_if_not(komp_codec_available("gzip"))
big <- new_payload("zeros", 700000L)
z <- komp_compress(big, "gzip")
expect_error(zu_test_decoder_reset(z, z, "gzip"),
class = "zukomp_internal_error")
# And the real limit still reports itself distinctly.
small <- komp_compress(new_payload("ascii", 9000L), "gzip")
expect_error(zu_test_decoder_reset(small, small, "gzip", max_output = 1000),
class = "zukomp_output_limit")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.