| /* |
| * 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); |
| } |