/* default chk_snd function for scheduled tasks */ static void stream_int_chk_snd(struct stream_interface *si) { struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, si_ic(si)->flags, oc->flags); if (unlikely(si->state != SI_ST_EST || (oc->flags & CF_SHUTW))) return; if (!(si->flags & SI_FL_WAIT_DATA) || /* not waiting for data */ channel_is_empty(oc)) /* 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(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); }
/* * This function performs a shutdown-read on a detached stream interface in a * connected or init state (it does nothing for other states). It either shuts * the read side or marks itself as closed. The buffer flags are updated to * reflect the new state. If the stream interface has SI_FL_NOHALF, we also * forward the close to the write side. The owner task is woken up if it exists. */ static void stream_int_shutr(struct stream_interface *si) { struct channel *ic = si_ic(si); ic->flags &= ~CF_SHUTR_NOW; if (ic->flags & CF_SHUTR) return; ic->flags |= CF_SHUTR; ic->rex = TICK_ETERNITY; si->flags &= ~SI_FL_WAIT_ROOM; if (si->state != SI_ST_EST && si->state != SI_ST_CON) return; if (si_oc(si)->flags & CF_SHUTW) { si->state = SI_ST_DIS; si->exp = TICK_ETERNITY; } else if (si->flags & SI_FL_NOHALF) { /* we want to immediately forward this close to the write side */ return stream_int_shutw(si); } /* note that if the task exists, it must unregister itself once it runs */ if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); }
/* Register an applet to handle a stream_interface as a new appctx. The SI will * wake it up everytime it is solicited. The appctx must be deleted by the task * handler using si_release_endpoint(), possibly from within the function itself. * It also pre-initializes the applet's context and returns it (or NULL in case * it could not be allocated). */ struct appctx *stream_int_register_handler(struct stream_interface *si, struct applet *app) { struct appctx *appctx; DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", app, si, si_task(si)); appctx = si_alloc_appctx(si, app); if (!appctx) return NULL; si_applet_cant_get(si); appctx_wakeup(appctx); return si_appctx(si); }
/* Register an applet to handle a stream_interface as part of the * stream interface's owner task. The SI will wake it up everytime it * is solicited. The task's processing function must call the applet's * function before returning. It must be deleted by the task handler * using stream_int_unregister_handler(), possibly from within the * function itself. It also pre-initializes the applet's context and * returns it (or NULL in case it could not be allocated). */ struct appctx *stream_int_register_handler(struct stream_interface *si, struct si_applet *app) { struct appctx *appctx; DPRINTF(stderr, "registering handler %p for si %p (was %p)\n", app, si, si_task(si)); appctx = si_alloc_appctx(si); if (!appctx) return NULL; appctx_set_applet(appctx, app); si->flags |= SI_FL_WAIT_DATA; return si_appctx(si); }
/* * This function performs a shutdown-write on a stream interface attached to an * applet in a connected or init state (it does nothing for other states). It * either shuts the write side or marks itself as closed. The buffer flags are * updated to reflect the new state. It does also close everything if the SI * was marked as being in error state. The owner task is woken up if it exists. */ static void stream_int_shutw(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); oc->flags &= ~CF_SHUTW_NOW; if (oc->flags & CF_SHUTW) return; oc->flags |= CF_SHUTW; oc->wex = TICK_ETERNITY; si->flags &= ~SI_FL_WAIT_DATA; switch (si->state) { case SI_ST_EST: /* we have to shut before closing, otherwise some short messages * may never leave the system, especially when there are remaining * unread data in the socket input buffer, or when nolinger is set. * However, if SI_FL_NOLINGER is explicitly set, we know there is * no risk so we close both sides immediately. */ if (!(si->flags & (SI_FL_ERR | SI_FL_NOLINGER)) && !(ic->flags & (CF_SHUTR|CF_DONT_READ))) return; /* fall through */ case SI_ST_CON: case SI_ST_CER: case SI_ST_QUE: case SI_ST_TAR: /* Note that none of these states may happen with applets */ si->state = SI_ST_DIS; si_applet_release(si); default: si->flags &= ~(SI_FL_WAIT_ROOM | SI_FL_NOLINGER); ic->flags &= ~CF_SHUTR_NOW; ic->flags |= CF_SHUTR; ic->rex = TICK_ETERNITY; si->exp = TICK_ETERNITY; } /* note that if the task exists, it must unregister itself once it runs */ if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); }
/* default chk_rcv function for scheduled tasks */ static void stream_int_chk_rcv(struct stream_interface *si) { struct channel *ic = si_ic(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, ic->flags, si_oc(si)->flags); if (unlikely(si->state != SI_ST_EST || (ic->flags & (CF_SHUTR|CF_DONT_READ)))) return; if (!channel_may_recv(ic) || ic->pipe) { /* stop reading */ si->flags |= SI_FL_WAIT_ROOM; } else { /* (re)start reading */ si->flags &= ~SI_FL_WAIT_ROOM; if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); } }
/* Callback to be used by applet handlers upon completion. It updates the stream * (which may or may not take this opportunity to try to forward data), then * may disable the applet's based on the channels and stream interface's final * states. */ void si_applet_wake_cb(struct stream_interface *si) { struct channel *ic = si_ic(si); /* If the applet wants to write and the channel is closed, it's a * broken pipe and it must be reported. */ if ((si->flags & SI_FL_WANT_PUT) && (ic->flags & CF_SHUTR)) si->flags |= SI_FL_ERR; /* update the stream-int, channels, and possibly wake the stream up */ stream_int_notify(si); /* Get away from the active list if we can't work anymore. * We also do that if the main task has already scheduled, because it * saves a useless wakeup/pause/wakeup cycle causing one useless call * per session on average. */ if (task_in_rq(si_task(si)) || (((si->flags & (SI_FL_WANT_PUT|SI_FL_WAIT_ROOM)) != SI_FL_WANT_PUT) && ((si->flags & (SI_FL_WANT_GET|SI_FL_WAIT_DATA)) != SI_FL_WANT_GET))) appctx_pause(si_appctx(si)); }
/* This function is used for inter-stream-interface calls. It is called by the * producer to inform the consumer side that it may be interested in checking * for data in the buffer. Note that it intentionally does not update timeouts, * so that we can still check them later at wake-up. */ static void stream_int_chk_snd_conn(struct stream_interface *si) { struct channel *oc = si_oc(si); struct connection *conn = __objt_conn(si->end); if (unlikely(si->state > SI_ST_EST || (oc->flags & CF_SHUTW))) return; if (unlikely(channel_is_empty(oc))) /* called with nothing to send ! */ return; if (!oc->pipe && /* spliced data wants to be forwarded ASAP */ !(si->flags & SI_FL_WAIT_DATA)) /* not waiting for data */ return; if (conn->flags & (CO_FL_DATA_WR_ENA|CO_FL_CURR_WR_ENA)) { /* already subscribed to write notifications, will be called * anyway, so let's avoid calling it especially if the reader * is not ready. */ return; } /* Before calling the data-level operations, we have to prepare * the polling flags to ensure we properly detect changes. */ conn_refresh_polling_flags(conn); __conn_data_want_send(conn); if (!(conn->flags & (CO_FL_HANDSHAKE|CO_FL_WAIT_L4_CONN|CO_FL_WAIT_L6_CONN))) { si_conn_send(conn); if (conn->flags & CO_FL_ERROR) { /* Write error on the file descriptor */ __conn_data_stop_both(conn); si->flags |= SI_FL_ERR; goto out_wakeup; } } /* OK, so now we know that some data might have been sent, and that we may * have to poll first. We have to do that too if the buffer is not empty. */ if (channel_is_empty(oc)) { /* the connection is established but we can't write. Either the * buffer is empty, or we just refrain from sending because the * ->o limit was reached. Maybe we just wrote the last * chunk and need to close. */ __conn_data_stop_send(conn); if (((oc->flags & (CF_SHUTW|CF_AUTO_CLOSE|CF_SHUTW_NOW)) == (CF_AUTO_CLOSE|CF_SHUTW_NOW)) && (si->state == SI_ST_EST)) { si_shutw(si); goto out_wakeup; } if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0) si->flags |= SI_FL_WAIT_DATA; oc->wex = TICK_ETERNITY; } else { /* Otherwise there are remaining data to be sent in the buffer, * which means we have to poll before doing so. */ __conn_data_want_send(conn); si->flags &= ~SI_FL_WAIT_DATA; if (!tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); } if (likely(oc->flags & CF_WRITE_ACTIVITY)) { struct channel *ic = si_ic(si); /* update timeout if we have written something */ if ((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(oc)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (tick_isset(ic->rex) && !(si->flags & SI_FL_INDEP_STR)) { /* Note: to prevent the client from expiring read timeouts * during writes, we refresh it. We only do this if the * interface is not configured for "independent streams", * because for some applications it's better not to do this, * for instance when continuously exchanging small amounts * of data which can full the socket buffers long before a * write timeout is detected. */ ic->rex = tick_add_ifset(now_ms, ic->rto); } } /* in case of special condition (error, shutdown, end of write...), we * have to notify the task. */ if (likely((oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR|CF_SHUTW)) || ((oc->flags & CF_WAKE_WRITE) && ((channel_is_empty(oc) && !oc->to_forward) || si->state != SI_ST_EST)))) { out_wakeup: if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); } /* commit possible polling changes */ conn_cond_update_polling(conn); }
/* This function is the equivalent to stream_int_update() except that it's * designed to be called from outside the stream handlers, typically the lower * layers (applets, connections) after I/O completion. After updating the stream * interface and timeouts, it will try to forward what can be forwarded, then to * wake the associated task up if an important event requires special handling. * It should not be called from within the stream itself, stream_int_update() * is designed for this. */ void stream_int_notify(struct stream_interface *si) { struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); /* process consumer side */ if (channel_is_empty(oc)) { if (((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) && (si->state == SI_ST_EST)) si_shutw(si); oc->wex = TICK_ETERNITY; } /* indicate that we may be waiting for data from the output channel */ if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; /* update OC timeouts and wake the other side up if it's waiting for room */ if (oc->flags & CF_WRITE_ACTIVITY) { if ((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(oc)) if (tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!(si->flags & SI_FL_INDEP_STR)) if (tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); if (likely((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && channel_may_recv(oc) && (si_opposite(si)->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si_opposite(si)); } /* Notify the other side when we've injected data into the IC that * needs to be forwarded. We can do fast-forwarding as soon as there * are output data, but we avoid doing this if some of the data are * not yet scheduled for being forwarded, because it is very likely * that it will be done again immediately afterwards once the following * data are parsed (eg: HTTP chunking). We only SI_FL_WAIT_ROOM once * we've emptied *some* of the output buffer, and not just when there * is available room, because applets are often forced to stop before * the buffer is full. We must not stop based on input data alone because * an HTTP parser might need more data to complete the parsing. */ if (!channel_is_empty(ic) && (si_opposite(si)->flags & SI_FL_WAIT_DATA) && (ic->buf->i == 0 || ic->pipe)) { int new_len, last_len; last_len = ic->buf->o; if (ic->pipe) last_len += ic->pipe->data; si_chk_snd(si_opposite(si)); new_len = ic->buf->o; if (ic->pipe) new_len += ic->pipe->data; /* check if the consumer has freed some space either in the * buffer or in the pipe. */ if (channel_may_recv(ic) && new_len < last_len) si->flags &= ~SI_FL_WAIT_ROOM; } if (si->flags & SI_FL_WAIT_ROOM) { ic->rex = TICK_ETERNITY; } else if ((ic->flags & (CF_SHUTR|CF_READ_PARTIAL|CF_DONT_READ)) == CF_READ_PARTIAL && channel_may_recv(ic)) { /* we must re-enable reading if si_chk_snd() has freed some space */ if (!(ic->flags & CF_READ_NOEXP) && tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } /* wake the task up only when needed */ if (/* changes on the production side */ (ic->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((ic->flags & CF_READ_PARTIAL) && (!ic->to_forward || si_opposite(si)->state != SI_ST_EST)) || /* changes on the consumption side */ (oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((oc->flags & CF_WRITE_ACTIVITY) && ((oc->flags & CF_SHUTW) || ((oc->flags & CF_WAKE_WRITE) && (si_opposite(si)->state != SI_ST_EST || (channel_is_empty(oc) && !oc->to_forward)))))) { task_wakeup(si_task(si), TASK_WOKEN_IO); } if (ic->flags & CF_READ_ACTIVITY) ic->flags &= ~CF_READ_DONTWAIT; stream_release_buffers(si_strm(si)); }
/* Callback to be used by connection I/O handlers upon completion. It differs from * the update function in that it is designed to be called by lower layers after I/O * events have been completed. It will also try to wake the associated task up if * an important event requires special handling. It relies on the connection handler * to commit any polling updates. The function always returns 0. */ static int si_conn_wake_cb(struct connection *conn) { struct stream_interface *si = conn->owner; struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, ic->flags, oc->flags); if (conn->flags & CO_FL_ERROR) si->flags |= SI_FL_ERR; /* check for recent connection establishment */ if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED)))) { si->exp = TICK_ETERNITY; oc->flags |= CF_WRITE_NULL; } /* process consumer side */ if (channel_is_empty(oc)) { if (((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW) && (si->state == SI_ST_EST)) stream_int_shutw_conn(si); __conn_data_stop_send(conn); oc->wex = TICK_ETERNITY; } if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; if (oc->flags & CF_WRITE_ACTIVITY) { /* update timeouts if we have written something */ if ((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL)) == CF_WRITE_PARTIAL && !channel_is_empty(oc)) if (tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); if (!(si->flags & SI_FL_INDEP_STR)) if (tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); if (likely((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && channel_may_recv(oc) && (si_opposite(si)->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si_opposite(si)); } /* process producer side. * We might have some data the consumer is waiting for. * We can do fast-forwarding, but we avoid doing this for partial * buffers, because it is very likely that it will be done again * immediately afterwards once the following data is parsed (eg: * HTTP chunking). */ if (((ic->flags & CF_READ_PARTIAL) && !channel_is_empty(ic)) && (ic->pipe /* always try to send spliced data */ || (si_ib(si)->i == 0 && (si_opposite(si)->flags & SI_FL_WAIT_DATA)))) { int last_len = ic->pipe ? ic->pipe->data : 0; si_chk_snd(si_opposite(si)); /* check if the consumer has freed some space either in the * buffer or in the pipe. */ if (channel_may_recv(ic) && (!last_len || !ic->pipe || ic->pipe->data < last_len)) si->flags &= ~SI_FL_WAIT_ROOM; } if (si->flags & SI_FL_WAIT_ROOM) { __conn_data_stop_recv(conn); ic->rex = TICK_ETERNITY; } else if ((ic->flags & (CF_SHUTR|CF_READ_PARTIAL|CF_DONT_READ)) == CF_READ_PARTIAL && channel_may_recv(ic)) { /* we must re-enable reading if si_chk_snd() has freed some space */ __conn_data_want_recv(conn); if (!(ic->flags & CF_READ_NOEXP) && tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } /* wake the task up only when needed */ if (/* changes on the production side */ (ic->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((ic->flags & CF_READ_PARTIAL) && (!ic->to_forward || si_opposite(si)->state != SI_ST_EST)) || /* changes on the consumption side */ (oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((oc->flags & CF_WRITE_ACTIVITY) && ((oc->flags & CF_SHUTW) || ((oc->flags & CF_WAKE_WRITE) && (si_opposite(si)->state != SI_ST_EST || (channel_is_empty(oc) && !oc->to_forward)))))) { task_wakeup(si_task(si), TASK_WOKEN_IO); } if (ic->flags & CF_READ_ACTIVITY) ic->flags &= ~CF_READ_DONTWAIT; session_release_buffers(si_sess(si)); return 0; }
/* default update function for embedded tasks, to be used at the end of the i/o handler */ static void stream_int_update_embedded(struct stream_interface *si) { int old_flags = si->flags; struct channel *ic = si_ic(si); struct channel *oc = si_oc(si); DPRINTF(stderr, "%s: si=%p, si->state=%d ic->flags=%08x oc->flags=%08x\n", __FUNCTION__, si, si->state, ic->flags, oc->flags); if (si->state != SI_ST_EST) return; if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW && channel_is_empty(oc)) si_shutw(si); if ((oc->flags & (CF_SHUTW|CF_SHUTW_NOW)) == 0 && channel_may_recv(oc)) si->flags |= SI_FL_WAIT_DATA; /* we're almost sure that we need some space if the buffer is not * empty, even if it's not full, because the applets can't fill it. */ if ((ic->flags & (CF_SHUTR|CF_DONT_READ)) == 0 && !channel_is_empty(ic)) si->flags |= SI_FL_WAIT_ROOM; if (oc->flags & CF_WRITE_ACTIVITY) { if (tick_isset(oc->wex)) oc->wex = tick_add_ifset(now_ms, oc->wto); } if (ic->flags & CF_READ_ACTIVITY || (oc->flags & CF_WRITE_ACTIVITY && !(si->flags & SI_FL_INDEP_STR))) { if (tick_isset(ic->rex)) ic->rex = tick_add_ifset(now_ms, ic->rto); } /* save flags to detect changes */ old_flags = si->flags; if (likely((oc->flags & (CF_SHUTW|CF_WRITE_PARTIAL|CF_DONT_READ)) == CF_WRITE_PARTIAL && channel_may_recv(oc) && (si_opposite(si)->flags & SI_FL_WAIT_ROOM))) si_chk_rcv(si_opposite(si)); if (((ic->flags & CF_READ_PARTIAL) && !channel_is_empty(ic)) && (ic->pipe /* always try to send spliced data */ || (ic->buf->i == 0 && (si_opposite(si)->flags & SI_FL_WAIT_DATA)))) { si_chk_snd(si_opposite(si)); /* check if the consumer has freed some space */ if (channel_may_recv(ic) && !ic->pipe) si->flags &= ~SI_FL_WAIT_ROOM; } /* 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 * to only rely on the changes the chk_* might have performed. */ if (/* check stream interface changes */ ((old_flags & ~si->flags) & (SI_FL_WAIT_ROOM|SI_FL_WAIT_DATA)) || /* changes on the production side */ (ic->flags & (CF_READ_NULL|CF_READ_ERROR)) || si->state != SI_ST_EST || (si->flags & SI_FL_ERR) || ((ic->flags & CF_READ_PARTIAL) && (!ic->to_forward || si_opposite(si)->state != SI_ST_EST)) || /* changes on the consumption side */ (oc->flags & (CF_WRITE_NULL|CF_WRITE_ERROR)) || ((oc->flags & CF_WRITE_ACTIVITY) && ((oc->flags & CF_SHUTW) || ((oc->flags & CF_WAKE_WRITE) && (si_opposite(si)->state != SI_ST_EST || (channel_is_empty(oc) && !oc->to_forward)))))) { if (!(si->flags & SI_FL_DONT_WAKE)) task_wakeup(si_task(si), TASK_WOKEN_IO); } if (ic->flags & CF_READ_ACTIVITY) ic->flags &= ~CF_READ_DONTWAIT; }