/* Updates the activity status of an applet outside of the applet handler based
 * on the channel's flags and the stream interface's flags. It needs to be
 * called once after the channels' flags have settled down and the stream has
 * been updated. It is not designed to be called from within the applet handler
 * itself.
 */
void stream_int_update_applet(struct stream_interface *si)
{
	if (((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_wakeup(si_appctx(si));
	else
		appctx_pause(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->owner);

	appctx = si_alloc_appctx(si);
	if (!appctx)
		return NULL;

	appctx_set_applet(appctx, app);
	si->flags |= SI_FL_WAIT_DATA;
	return si_appctx(si);
}
/* 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);
}
/* chk_rcv function for applets */
static void stream_int_chk_rcv_applet(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;
	/* here we only wake the applet up if it was waiting for some room */
	if (!(si->flags & SI_FL_WAIT_ROOM))
		return;

	if (channel_may_recv(ic) && !ic->pipe) {
		/* (re)start reading */
		appctx_wakeup(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_applet(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;

	/* on shutw we always wake the applet up */
	appctx_wakeup(si_appctx(si));

	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_applet_release(si);
		si->state = SI_ST_DIS;
	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;
	}
}
/* 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));
}
/* chk_snd function for applets */
static void stream_int_chk_snd_applet(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;

	/* we only wake the applet up if it was waiting for some data */

	if (!(si->flags & SI_FL_WAIT_DATA))
		return;

	if (!tick_isset(oc->wex))
		oc->wex = tick_add_ifset(now_ms, oc->wto);

	if (!channel_is_empty(oc)) {
		/* (re)start sending */
		appctx_wakeup(si_appctx(si));
	}
}