When a filter asks to wait during the analysis of a channel, with
filter.WAIT, all the data it holds are kept in the buffer, waiting to be
processed when it is called back. But a close could be propagated to the
other side in the meantime: when a shutdown or an abort was received on
the channel, the close was forwarded to the peer side, for instance a
backend EOF was immediately forwarded to the client. The stream could
then terminate before the filter resumed, and the held data were lost,
typically a complete response held by lua filters waiting for their
wake-up, delivered with no framing and delimited by the backend close.
So now, when a filter asks to wait, the close is no longer propagated:
like the connection on the request side, the channel keeps the
CF_AUTO_CLOSE flag cleared while a filter waits on it. The flag is
restored by the analyzers on each pass, so the close is simply deferred
until the filter resumes, and nothing changes for the streams without
filters waiting.
The "lua-eof" reg-test, introduced to record this race and marked broken
until it was fixed, now passes and is un-broken. It exercises both the
legacy filter chains and the filter instance mode, with a keep-alive
connection reuse.
This patch should be backported to all supported version after a perido of
observation.
varnishtest "Waiting Lua filters drain a complete response followed by backend EOF"
-#REGTEST_TYPE=broken
+#REGTEST_TYPE=devel
feature cmd "$HAPROXY_PROGRAM -cc 'feature(LUA)'"
feature ignore_unknown_macro
-# A backend closing after a complete chunked response can make the stream
-# close before waiting Lua callbacks resume. This also occurs with legacy
-# filters before the instance refactoring. Keep the reproducer separate
-# from the passing resumption test, which holds the backend open with a barrier.
+# A backend closing after a complete chunked response while lua filters
+# wait must not lose the held response: the close is now deferred until
+# the filters resume, and the complete response is delivered to the
+# client. This also exercises keep-alive reuse and both the legacy
+# "filter" chains and the filter instance mode.
server s1 {
rxreq
txresp -body "first response"
wait:
if (!(chn->flags & CF_ISRESP))
channel_dont_connect(chn);
+ channel_dont_close(chn);
DBG_TRACE_DEVEL("wairing for more data", STRM_EV_FLT_ANA, s);
return 0;
}