]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: stream-int: Update the stream expiration date in stream_int_notify()
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 3 Jan 2019 15:24:54 +0000 (16:24 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 3 Jan 2019 17:45:00 +0000 (18:45 +0100)
Since a long time, the expiration date of a stream is only updated in
process_stream(). It is calculated, among others, using the channels expiration
dates for reads and writes (.rex and .wex values). But these values are updated
by the stream-interface. So when this happens at the connection layer, the
update is only done if the stream's task is woken up. Otherwise, the stream
expiration date is not immediatly updated. This leads to unexpected
behaviours. Time to time, users reported that the wrong timeout was hitted or
the wrong termination state was reported. This is partly because of this
bug.

Recently, we observed some blocked sessions for a while when big objects are
served from the cache applet. It seems only concern the clients not reading the
response. Because delivered objects are big, not all data can be sent. And
because delivered objects are big, data are fast forwarded (from the input to
the output with no stream wakeup). So in such situation, the stream expiration
date is never updated and no timeout is hitted. The session remains blocked
while the client remains connected.

This bug exists at least since HAProxy 1.5. But recent changes on the connection
layer make it more visible. It must be backported from 1.9 to 1.6. And with more
pain it should be backported to 1.5.

src/stream_interface.c

index 05306cf5a704894175b3a97e09f634333f48b86c..cbe6f6ea3414522d020c9fda8ba7a1c0764d698f 100644 (file)
@@ -436,6 +436,7 @@ static void stream_int_notify(struct stream_interface *si)
        struct channel *ic = si_ic(si);
        struct channel *oc = si_oc(si);
        struct stream_interface *sio = si_opposite(si);
+       struct task *task = si_task(si);
 
        /* process consumer side */
        if (channel_is_empty(oc)) {
@@ -540,7 +541,14 @@ static void stream_int_notify(struct stream_interface *si)
                !(oc->flags & (CF_AUTO_CLOSE|CF_SHUTW_NOW|CF_SHUTW))) &&
               (sio->state != SI_ST_EST ||
                (channel_is_empty(oc) && !oc->to_forward)))))) {
-               task_wakeup(si_task(si), TASK_WOKEN_IO);
+               task_wakeup(task, TASK_WOKEN_IO);
+       }
+       else {
+               /* Update expiration date for the task and requeue it */
+               task->expire = tick_first((tick_is_expired(task->expire, now_ms) ? 0 : task->expire),
+                                         tick_first(tick_first(ic->rex, ic->wex),
+                                                    tick_first(oc->rex, oc->wex)));
+               task_queue(task);
        }
        if (ic->flags & CF_READ_ACTIVITY)
                ic->flags &= ~CF_READ_DONTWAIT;