mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-11-13 15:00:59 +01:00
Since the CS/SI refactoring, the .release callback function may be called twice. The first call when a shutdown for read or for write is performed. The second one when the applet is detached from its conn-stream. The second call must be guarded, just like the first one, to only be performed is the stream-interface is not the in disconnected (SI_ST_DIS) or closed (SI_ST_CLO) state. To simplify the fix, we now always rely on si_applet_release() function. It is 2.6-specific, no backport is needed.
161 lines
3.7 KiB
C
161 lines
3.7 KiB
C
/*
|
|
* Conn-stream management functions
|
|
*
|
|
* Copyright 2021 Christopher Faulet <cfaulet@haproxy.com>
|
|
*
|
|
* 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 <haproxy/api.h>
|
|
#include <haproxy/connection.h>
|
|
#include <haproxy/conn_stream.h>
|
|
#include <haproxy/pool.h>
|
|
#include <haproxy/stream_interface.h>
|
|
|
|
DECLARE_POOL(pool_head_connstream, "conn_stream", sizeof(struct conn_stream));
|
|
|
|
|
|
/* Tries to allocate a new conn_stream and initialize its main fields. On
|
|
* failure, nothing is allocated and NULL is returned.
|
|
*/
|
|
struct conn_stream *cs_new()
|
|
{
|
|
struct conn_stream *cs;
|
|
|
|
cs = pool_alloc(pool_head_connstream);
|
|
if (unlikely(!cs))
|
|
return NULL;
|
|
cs_init(cs);
|
|
return cs;
|
|
}
|
|
|
|
/* Releases a conn_stream previously allocated by cs_new(), as well as any
|
|
* buffer it would still hold.
|
|
*/
|
|
void cs_free(struct conn_stream *cs)
|
|
{
|
|
si_free(cs->si);
|
|
pool_free(pool_head_connstream, cs);
|
|
}
|
|
|
|
|
|
/* Attaches a conn_stream to an endpoint and sets the endpoint ctx */
|
|
void cs_attach_endp(struct conn_stream *cs, enum obj_type *endp, void *ctx)
|
|
{
|
|
struct connection *conn;
|
|
struct appctx *appctx;
|
|
|
|
cs->end = endp;
|
|
cs->ctx = ctx;
|
|
if ((conn = objt_conn(endp)) != NULL) {
|
|
if (!conn->ctx)
|
|
conn->ctx = cs;
|
|
if (cs_strm(cs)) {
|
|
cs->si->ops = &si_conn_ops;
|
|
cs->data_cb = &si_conn_cb;
|
|
}
|
|
else if (cs_check(cs))
|
|
cs->data_cb = &check_conn_cb;
|
|
}
|
|
else if ((appctx = objt_appctx(endp)) != NULL) {
|
|
appctx->owner = cs;
|
|
if (cs->si) {
|
|
cs->si->ops = &si_applet_ops;
|
|
cs->data_cb = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Attaches a conn_stream to a app layer and sets the relevant callbacks */
|
|
int cs_attach_app(struct conn_stream *cs, enum obj_type *app)
|
|
{
|
|
cs->app = app;
|
|
|
|
if (objt_stream(app)) {
|
|
if (!cs->si)
|
|
cs->si = si_new(cs);
|
|
if (unlikely(!cs->si))
|
|
return -1;
|
|
|
|
if (cs_conn(cs)) {
|
|
cs->si->ops = &si_conn_ops;
|
|
cs->data_cb = &si_conn_cb;
|
|
}
|
|
else if (cs_appctx(cs)) {
|
|
cs->si->ops = &si_applet_ops;
|
|
cs->data_cb = NULL;
|
|
}
|
|
else {
|
|
cs->si->ops = &si_embedded_ops;
|
|
cs->data_cb = NULL;
|
|
}
|
|
}
|
|
else if (objt_check(app))
|
|
cs->data_cb = &check_conn_cb;
|
|
return 0;
|
|
}
|
|
|
|
/* Detach the conn_stream from the endpoint, if any. For a connecrion, if a mux
|
|
* owns the connection ->detach() callback is called. Otherwise, it means the
|
|
* conn-stream owns the connection. In this case the connection is closed and
|
|
* released. For an applet, the appctx is released. At the end, the conn-stream
|
|
* is not released but some fields a reset.
|
|
*/
|
|
void cs_detach_endp(struct conn_stream *cs)
|
|
{
|
|
struct connection *conn;
|
|
struct appctx *appctx;
|
|
|
|
if ((conn = cs_conn(cs))) {
|
|
if (conn->mux) {
|
|
/* TODO: handle unsubscribe for healthchecks too */
|
|
if (cs->si && cs->si->wait_event.events != 0)
|
|
conn->mux->unsubscribe(cs, cs->si->wait_event.events, &cs->si->wait_event);
|
|
conn->mux->detach(cs);
|
|
}
|
|
else {
|
|
/* It's too early to have a mux, let's just destroy
|
|
* the connection
|
|
*/
|
|
conn_stop_tracking(conn);
|
|
conn_full_close(conn);
|
|
if (conn->destroy_cb)
|
|
conn->destroy_cb(conn);
|
|
conn_free(conn);
|
|
}
|
|
}
|
|
else if ((appctx = cs_appctx(cs))) {
|
|
if (cs->si)
|
|
si_applet_release(cs->si);
|
|
appctx_free(appctx);
|
|
}
|
|
|
|
/* FIXME: Rest CS for now but must be reviewed. CS flags are only
|
|
* connection related for now but this will evolved
|
|
*/
|
|
cs->flags = CS_FL_NONE;
|
|
cs->end = NULL;
|
|
cs->ctx = NULL;
|
|
if (cs->si)
|
|
cs->si->ops = &si_embedded_ops;
|
|
cs->data_cb = NULL;
|
|
|
|
if (cs->app == NULL)
|
|
cs_free(cs);
|
|
}
|
|
|
|
void cs_detach_app(struct conn_stream *cs)
|
|
{
|
|
si_free(cs->si);
|
|
cs->app = NULL;
|
|
cs->si = NULL;
|
|
cs->data_cb = NULL;
|
|
|
|
if (cs->end == NULL)
|
|
cs_free(cs);
|
|
}
|