[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
/*
|
|
|
|
|
* Functions managing stream_interface structures
|
|
|
|
|
*
|
2009-09-05 14:57:35 -04:00
|
|
|
* Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
|
[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include <common/compat.h>
|
|
|
|
|
#include <common/config.h>
|
|
|
|
|
#include <common/debug.h>
|
|
|
|
|
#include <common/standard.h>
|
|
|
|
|
#include <common/ticks.h>
|
|
|
|
|
#include <common/time.h>
|
|
|
|
|
|
|
|
|
|
#include <proto/buffers.h>
|
|
|
|
|
#include <proto/client.h>
|
|
|
|
|
#include <proto/fd.h>
|
2009-09-20 14:14:49 -04:00
|
|
|
#include <proto/stream_interface.h>
|
[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
#include <proto/stream_sock.h>
|
|
|
|
|
#include <proto/task.h>
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function only has to be called once after a wakeup event in case of
|
|
|
|
|
* suspected timeout. It controls the stream interface timeouts and sets
|
|
|
|
|
* si->flags accordingly. It does NOT close anything, as this timeout may
|
|
|
|
|
* be used for any purpose. It returns 1 if the timeout fired, otherwise
|
|
|
|
|
* zero.
|
|
|
|
|
*/
|
|
|
|
|
int stream_int_check_timeouts(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
if (tick_is_expired(si->exp, now_ms)) {
|
|
|
|
|
si->flags |= SI_FL_EXP;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 12:14:12 -05:00
|
|
|
/* to be called only when in SI_ST_DIS with SI_FL_ERR */
|
[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
void stream_int_report_error(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
if (!si->err_type)
|
|
|
|
|
si->err_type = SI_ET_DATA_ERR;
|
|
|
|
|
|
|
|
|
|
si->ob->flags |= BF_WRITE_ERROR;
|
|
|
|
|
si->ib->flags |= BF_READ_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 13:22:53 -05:00
|
|
|
/*
|
2009-03-08 15:33:29 -04:00
|
|
|
* Erase any content from input and output buffers, and return a message into
|
|
|
|
|
* the output buffer. The message is provided as a "chunk". If it is null,
|
|
|
|
|
* then an empty message is used.
|
2008-11-30 13:22:53 -05:00
|
|
|
*/
|
|
|
|
|
void stream_int_return(struct stream_interface *si, const struct chunk *msg)
|
|
|
|
|
{
|
2009-03-08 15:33:29 -04:00
|
|
|
buffer_erase(si->ib);
|
2009-09-15 15:23:54 -04:00
|
|
|
buffer_cut_tail(si->ob);
|
2008-11-30 13:22:53 -05:00
|
|
|
if (msg && msg->len)
|
|
|
|
|
buffer_write(si->ob, msg->str, msg->len);
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-30 13:48:07 -05:00
|
|
|
/*
|
|
|
|
|
* Returns a message to the client ; the connection is shut down for read,
|
|
|
|
|
* and the request is cleared so that no server connection can be initiated.
|
|
|
|
|
* The buffer is marked for read shutdown on the other side to protect the
|
|
|
|
|
* message, and the buffer write is enabled. The message is contained in a
|
|
|
|
|
* "chunk". If it is null, then an empty message is used. The reply buffer
|
|
|
|
|
* doesn't need to be empty before this. The goal of this function is to
|
|
|
|
|
* return error messages to a client.
|
|
|
|
|
*/
|
|
|
|
|
void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg)
|
|
|
|
|
{
|
|
|
|
|
buffer_abort(si->ib);
|
2009-03-08 15:33:29 -04:00
|
|
|
buffer_erase(si->ob);
|
2008-11-30 13:48:07 -05:00
|
|
|
buffer_shutr_now(si->ob);
|
|
|
|
|
if (msg && msg->len)
|
|
|
|
|
buffer_write(si->ob, msg->str, msg->len);
|
|
|
|
|
|
|
|
|
|
si->ob->wex = tick_add_ifset(now_ms, si->ob->wto);
|
2009-09-19 15:04:57 -04:00
|
|
|
buffer_auto_close(si->ob);
|
2008-11-30 13:48:07 -05:00
|
|
|
}
|
|
|
|
|
|
2009-09-05 14:57:35 -04:00
|
|
|
/* default update function for scheduled tasks, not used for embedded tasks */
|
|
|
|
|
void stream_int_update(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* default update function for embedded tasks, to be used at the end of the i/o handler */
|
|
|
|
|
void stream_int_update_embedded(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
if (si->state != SI_ST_EST)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((si->ob->flags & (BF_OUT_EMPTY|BF_SHUTW|BF_HIJACK|BF_SHUTW_NOW)) == (BF_OUT_EMPTY|BF_SHUTW_NOW))
|
|
|
|
|
si->shutw(si);
|
|
|
|
|
|
|
|
|
|
if ((si->ob->flags & (BF_FULL|BF_SHUTW|BF_SHUTW_NOW|BF_HIJACK)) == 0)
|
|
|
|
|
si->flags |= SI_FL_WAIT_DATA;
|
|
|
|
|
|
|
|
|
|
if ((si->ib->flags & (BF_FULL|BF_SHUTR)) == BF_FULL)
|
|
|
|
|
si->flags |= SI_FL_WAIT_ROOM;
|
|
|
|
|
|
[MEDIUM] new option "independant-streams" to stop updating read timeout on writes
By default, when data is sent over a socket, both the write timeout and the
read timeout for that socket are refreshed, because we consider that there is
activity on that socket, and we have no other means of guessing if we should
receive data or not.
While this default behaviour is desirable for almost all applications, there
exists a situation where it is desirable to disable it, and only refresh the
read timeout if there are incoming data. This happens on sessions with large
timeouts and low amounts of exchanged data such as telnet session. If the
server suddenly disappears, the output data accumulates in the system's
socket buffers, both timeouts are correctly refreshed, and there is no way
to know the server does not receive them, so we don't timeout. However, when
the underlying protocol always echoes sent data, it would be enough by itself
to detect the issue using the read timeout. Note that this problem does not
happen with more verbose protocols because data won't accumulate long in the
socket buffers.
When this option is set on the frontend, it will disable read timeout updates
on data sent to the client. There probably is little use of this case. When
the option is set on the backend, it will disable read timeout updates on
data sent to the server. Doing so will typically break large HTTP posts from
slow lines, so use it with caution.
2009-10-03 16:01:18 -04:00
|
|
|
if (si->ob->flags & BF_WRITE_ACTIVITY) {
|
2009-09-05 14:57:35 -04:00
|
|
|
if (tick_isset(si->ob->wex))
|
|
|
|
|
si->ob->wex = tick_add_ifset(now_ms, si->ob->wto);
|
|
|
|
|
}
|
|
|
|
|
|
[MEDIUM] new option "independant-streams" to stop updating read timeout on writes
By default, when data is sent over a socket, both the write timeout and the
read timeout for that socket are refreshed, because we consider that there is
activity on that socket, and we have no other means of guessing if we should
receive data or not.
While this default behaviour is desirable for almost all applications, there
exists a situation where it is desirable to disable it, and only refresh the
read timeout if there are incoming data. This happens on sessions with large
timeouts and low amounts of exchanged data such as telnet session. If the
server suddenly disappears, the output data accumulates in the system's
socket buffers, both timeouts are correctly refreshed, and there is no way
to know the server does not receive them, so we don't timeout. However, when
the underlying protocol always echoes sent data, it would be enough by itself
to detect the issue using the read timeout. Note that this problem does not
happen with more verbose protocols because data won't accumulate long in the
socket buffers.
When this option is set on the frontend, it will disable read timeout updates
on data sent to the client. There probably is little use of this case. When
the option is set on the backend, it will disable read timeout updates on
data sent to the server. Doing so will typically break large HTTP posts from
slow lines, so use it with caution.
2009-10-03 16:01:18 -04:00
|
|
|
if (si->ib->flags & BF_READ_ACTIVITY ||
|
|
|
|
|
(si->ob->flags & BF_WRITE_ACTIVITY && !(si->flags & SI_FL_INDEP_STR))) {
|
|
|
|
|
if (tick_isset(si->ib->rex))
|
|
|
|
|
si->ib->rex = tick_add_ifset(now_ms, si->ib->rto);
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-05 14:57:35 -04:00
|
|
|
if (si->ob->flags & BF_WRITE_PARTIAL)
|
|
|
|
|
si->ob->prod->chk_rcv(si->ob->prod);
|
|
|
|
|
|
|
|
|
|
if (si->ib->flags & BF_READ_PARTIAL)
|
|
|
|
|
si->ib->cons->chk_snd(si->ib->cons);
|
|
|
|
|
|
|
|
|
|
/* Note that we're trying to wake up in two conditions here :
|
|
|
|
|
* - special event, which needs the holder task attention
|
|
|
|
|
* - status indicating that the applet can go on working. This
|
|
|
|
|
* is rather hard because we might be blocking on output and
|
|
|
|
|
* don't want to wake up on input and vice-versa. The idea is
|
|
|
|
|
* the to only rely the changes the chk_* might have performed.
|
|
|
|
|
*/
|
|
|
|
|
if (/* check stream interface changes */
|
|
|
|
|
(si->flags & SI_FL_ERR) || si->state != SI_ST_EST || si->ib->cons->state != SI_ST_EST ||
|
|
|
|
|
/* check response buffer changes */
|
|
|
|
|
(si->ib->flags & (BF_READ_NULL|BF_READ_ERROR|BF_READ_DONTWAIT)) ||
|
|
|
|
|
((si->ib->flags & BF_READ_ACTIVITY) && !si->ib->to_forward) ||
|
|
|
|
|
(!(si->ib->flags & BF_FULL) && (si->ib->flags & BF_WRITE_ACTIVITY) && si->ib->to_forward) ||
|
|
|
|
|
/* check request buffer changes */
|
|
|
|
|
(si->ob->flags & (BF_WRITE_ERROR)) ||
|
|
|
|
|
((si->ob->flags & BF_WRITE_ACTIVITY) && (si->ob->flags & BF_OUT_EMPTY) && !si->ob->to_forward) ||
|
|
|
|
|
(si->ob->flags & BF_READ_ACTIVITY)) {
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* default shutr function for scheduled tasks */
|
|
|
|
|
void stream_int_shutr(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
si->ib->flags &= ~BF_SHUTR_NOW;
|
|
|
|
|
if (si->ib->flags & BF_SHUTR)
|
|
|
|
|
return;
|
|
|
|
|
si->ib->flags |= BF_SHUTR;
|
|
|
|
|
si->ib->rex = TICK_ETERNITY;
|
|
|
|
|
si->flags &= ~SI_FL_WAIT_ROOM;
|
|
|
|
|
|
|
|
|
|
if (si->state != SI_ST_EST && si->state != SI_ST_CON)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (si->ob->flags & BF_SHUTW) {
|
|
|
|
|
si->state = SI_ST_DIS;
|
|
|
|
|
si->exp = TICK_ETERNITY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* note that if the task exist, it must unregister itself once it runs */
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* default shutw function for scheduled tasks */
|
|
|
|
|
void stream_int_shutw(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
si->ob->flags &= ~BF_SHUTW_NOW;
|
|
|
|
|
if (si->ob->flags & BF_SHUTW)
|
|
|
|
|
return;
|
|
|
|
|
si->ob->flags |= BF_SHUTW;
|
|
|
|
|
si->ob->wex = TICK_ETERNITY;
|
|
|
|
|
si->flags &= ~SI_FL_WAIT_DATA;
|
|
|
|
|
|
|
|
|
|
switch (si->state) {
|
|
|
|
|
case SI_ST_EST:
|
|
|
|
|
if (!(si->ib->flags & BF_SHUTR))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* fall through */
|
|
|
|
|
case SI_ST_CON:
|
|
|
|
|
case SI_ST_CER:
|
|
|
|
|
si->state = SI_ST_DIS;
|
|
|
|
|
/* fall through */
|
|
|
|
|
default:
|
|
|
|
|
si->flags &= ~SI_FL_WAIT_ROOM;
|
|
|
|
|
si->ib->flags |= BF_SHUTR;
|
|
|
|
|
si->ib->rex = TICK_ETERNITY;
|
|
|
|
|
si->exp = TICK_ETERNITY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* note that if the task exist, it must unregister itself once it runs */
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* default chk_rcv function for scheduled tasks */
|
|
|
|
|
void stream_int_chk_rcv(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct buffer *ib = si->ib;
|
|
|
|
|
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
if (unlikely(si->state != SI_ST_EST || (ib->flags & BF_SHUTR)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ib->flags & (BF_FULL|BF_HIJACK)) {
|
|
|
|
|
/* stop reading */
|
|
|
|
|
if ((ib->flags & (BF_FULL|BF_HIJACK)) == BF_FULL)
|
|
|
|
|
si->flags |= SI_FL_WAIT_ROOM;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* (re)start reading */
|
|
|
|
|
si->flags &= ~SI_FL_WAIT_ROOM;
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* default chk_snd function for scheduled tasks */
|
|
|
|
|
void stream_int_chk_snd(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct buffer *ob = si->ob;
|
|
|
|
|
|
|
|
|
|
DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
|
|
|
|
|
__FUNCTION__,
|
|
|
|
|
si, si->state, si->ib->flags, si->ob->flags);
|
|
|
|
|
|
|
|
|
|
if (unlikely(si->state != SI_ST_EST || (si->ob->flags & BF_SHUTW)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!(si->flags & SI_FL_WAIT_DATA) || /* not waiting for data */
|
|
|
|
|
(ob->flags & BF_OUT_EMPTY)) /* called with nothing to send ! */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Otherwise there are remaining data to be sent in the buffer,
|
|
|
|
|
* so we tell the handler.
|
|
|
|
|
*/
|
|
|
|
|
si->flags &= ~SI_FL_WAIT_DATA;
|
|
|
|
|
if (!tick_isset(ob->wex))
|
|
|
|
|
ob->wex = tick_add_ifset(now_ms, ob->wto);
|
|
|
|
|
|
|
|
|
|
if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_IO);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Register a function to handle a stream_interface as part of the stream
|
|
|
|
|
* interface's owner task, which is returned. The SI will wake it up everytime
|
|
|
|
|
* it is solicited. The task's processing function must call the specified
|
|
|
|
|
* function before returning. It must be deleted by the task handler using
|
|
|
|
|
* stream_int_unregister_handler(), possibly from withing the function itself.
|
|
|
|
|
*/
|
|
|
|
|
struct task *stream_int_register_handler(struct stream_interface *si,
|
|
|
|
|
void (*fct)(struct stream_interface *))
|
|
|
|
|
{
|
|
|
|
|
DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", fct, si, si->owner);
|
|
|
|
|
|
|
|
|
|
si->update = stream_int_update_embedded;
|
|
|
|
|
si->shutr = stream_int_shutr;
|
|
|
|
|
si->shutw = stream_int_shutw;
|
|
|
|
|
si->chk_rcv = stream_int_chk_rcv;
|
|
|
|
|
si->chk_snd = stream_int_chk_snd;
|
|
|
|
|
si->connect = NULL;
|
|
|
|
|
si->iohandler = fct;
|
|
|
|
|
si->flags |= SI_FL_WAIT_DATA;
|
|
|
|
|
return si->owner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Register a function to handle a stream_interface as a standalone task. The
|
|
|
|
|
* new task itself is returned and is assigned as si->owner. The stream_interface
|
|
|
|
|
* pointer will be pointed to by the task's context. The handler can be detached
|
|
|
|
|
* by using stream_int_unregister_handler().
|
|
|
|
|
*/
|
|
|
|
|
struct task *stream_int_register_handler_task(struct stream_interface *si,
|
|
|
|
|
struct task *(*fct)(struct task *))
|
|
|
|
|
{
|
|
|
|
|
struct task *t;
|
|
|
|
|
|
|
|
|
|
DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", fct, si, si->owner);
|
|
|
|
|
|
|
|
|
|
si->update = stream_int_update;
|
|
|
|
|
si->shutr = stream_int_shutr;
|
|
|
|
|
si->shutw = stream_int_shutw;
|
|
|
|
|
si->chk_rcv = stream_int_chk_rcv;
|
|
|
|
|
si->chk_snd = stream_int_chk_snd;
|
|
|
|
|
si->connect = NULL;
|
|
|
|
|
si->iohandler = NULL; /* not used when running as an external task */
|
|
|
|
|
si->flags |= SI_FL_WAIT_DATA;
|
|
|
|
|
|
|
|
|
|
t = task_new();
|
|
|
|
|
si->owner = t;
|
|
|
|
|
if (!t)
|
|
|
|
|
return t;
|
|
|
|
|
t->process = fct;
|
|
|
|
|
t->context = si;
|
|
|
|
|
task_wakeup(si->owner, TASK_WOKEN_INIT);
|
|
|
|
|
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unregister a stream interface handler. This must be called by the handler task
|
|
|
|
|
* itself when it detects that it is in the SI_ST_DIS state. This function can
|
|
|
|
|
* both detach standalone handlers and embedded handlers.
|
|
|
|
|
*/
|
|
|
|
|
void stream_int_unregister_handler(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
if (!si->iohandler && si->owner) {
|
|
|
|
|
/* external handler : kill the task */
|
|
|
|
|
task_delete(si->owner);
|
|
|
|
|
task_free(si->owner);
|
|
|
|
|
}
|
|
|
|
|
si->iohandler = NULL;
|
|
|
|
|
si->owner = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
[MAJOR] add a connection error state to the stream_interface
Tracking connection status changes was hard, and some code was
redundant. A new SI_ST_CER state was added to the stream interface
to indicate a past connection error, and an SI_FL_ERR flag was
added to report past I/O error. The stream_sock code does not set
the connection to SI_ST_CLO anymore in case of I/O error, it's
the upper layer which does it. This makes it possible to know
exactly when the file descriptors are allocated.
The new SI_ST_CER state permitted to split tcp_connection_status()
in two parts, one processing SI_ST_CON and the other one SI_ST_CER.
Synchronous connection errors now make use of this last state, hence
eliminating duplicate code.
Some ib<->ob copy paste errors were found and fixed, and all entities
setting SI_ST_CLO also shut the buffers down.
Some of these stream_interface specific functions and structures
have migrated to a new stream_interface.c file.
Some types of errors are still not detected by the buffers. For
instance, let's assume the following scenario in one single pass
of process_session: a connection sits in SI_ST_TAR state during
a retry. At TAR expiration, a new connection attempt is made, the
connection is obtained and srv->cur_sess is increased. Then the
buffer timeout is fires and everything is cleared, the new state
becomes SI_ST_CLO. The cleaning code checks that previous state
was either SI_ST_CON or SI_ST_EST to release the connection. But
that's wrong because last state is still SI_ST_TAR. So the
server's connection count does not get decreased.
This means that prev_state must not be used, and must be replaced
by some transition detection instead of level detection.
The following debugging line was useful to track state changes :
fprintf(stderr, "%s:%d: cs=%d ss=%d(%d) rqf=0x%08x rpf=0x%08x\n", __FUNCTION__, __LINE__,
s->si[0].state, s->si[1].state, s->si[1].err_type, s->req->flags, s-> rep->flags);
2008-11-03 00:26:53 -05:00
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|