Commit Graph

23295 Commits

Author SHA1 Message Date
Aurelien DARRAGON
4fe0cca305 CLEANUP: resolvers: remove duplicate func prototype
dns_dgram_init() function prototype was found in both resolvers and dns
header files, but it should belong to the dns header file, so the
duplicate entry was simply removed.
2023-12-21 14:22:27 +01:00
Aurelien DARRAGON
ab6fef4882 CLEANUP: server: remove unused server_parse_addr_change_request() function
server_parse_addr_change_request() was completely replaced by the newer
srv_update_addr_port() function. Considering the function doesn't offer
useful features that srv_update_addr_port() couldn't do, we simply
remove the function.
2023-12-21 14:22:27 +01:00
Aurelien DARRAGON
f1f4b93a67 MEDIUM: server: merge srv_update_addr() and srv_update_addr_port() logic
Both functions are performing the similar tasks, except that the _port()
version is doing a bit more work.

In this patch, we add the server_set_inetaddr() function that works like
the srv_update_addr_port() but it takes parsed inputs instead of raw
strings as arguments.

Then, server_set_inetaddr() is used as underlying helper function for
both srv_update_addr() and srv_update_addr_port() to make them easier
to maintain.

Also, helper functions were added:
 - server_set_inetaddr_warn() -> same as server_set_inetaddr() but report
   a warning on updates.
 - server_get_inetaddr() -> fills a struct server_inetaddr from srv

Since the feedback message generation part was slightly reworked, some
minor changes in the way addr:svc_port updates are reported in the logs
or cli messages should be expected (no loss of information though).
2023-12-21 14:22:27 +01:00
Aurelien DARRAGON
2d0c7f5935 CLEANUP: server/event_hdl: remove purge_conn hint in INETADDR event
Now that purge_conn hint is now being ignored thanks to previous commit,
we can simply get rid of it.
2023-12-21 14:22:27 +01:00
Aurelien DARRAGON
2e3a163e47 MINOR: server: ensure connection cleanup on server addr changes
Previously, in srv_update_addr_port(), we forced connection cleanup on
server changes.
This was done in 6318d33ce ("BUG/MEDIUM: connections: force connections
cleanup on server changes").

However, there is no reason we shouldn't have done the same in
srv_update_addr() function, because the end goal is the same: perform
runtime changes on server's address.

The purge_conn hint propagated through the INETADDR server event was
simply there to keep the original behavior (only purge the connection
for events originating from srv_update_addr_port()), but to ensure the
address change is handled the same way for both code paths, we simply
ignore this hint.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
545e72546c BUG/MINOR: server/event_hdl: propagate map port info through inetaddr event
server addr:svc_port updates during runtime might set or clear the
SRV_F_MAPPORTS flag. Unfortunately, the flag update is still directly
performed by srv_update_addr_port() function while the addr:svc_port
update is being scheduled for atomic update. Given that existing readers
don't take server's lock to read addr:svc_port, they also check the
SRV_F_MAPPORTS flag right after without the lock.

So we could cause the readers to incorrectly interpret the svc_port from
the server struct because the mapport information is not published
atomically, resulting in inconsistencies between svc_port / mapport flag.
(MAPPORTS flag causes svc_port to be used differently by the reader)

To fix this, we publish the mapport information within the INETADDR server
event and we let the task responsible for updating server's addr and port
position or clear the flag depending on the mapport hint.

This patch depends on:
 - MINOR: server/event_hdl: add server_inetaddr struct to facilitate event data usage
 - MINOR: server/event_hdl: update _srv_event_hdl_prepare_inetaddr prototype

This should be backported in 2.9 with 683b2ae01 ("MINOR: server/event_hdl:
add SERVER_INETADDR event")
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
4e50c31eab MINOR: server/event_hdl: update _srv_event_hdl_prepare_inetaddr prototype
Slightly change _srv_event_hdl_prepare_inetaddr() function prototype to
reduce the input arguments by learning some settings directly from the
server. Also taking this opportunity to make the function static inline
since it's relatively simple and not meant to be used directly.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
14893a6a00 MINOR: server/event_hdl: add server_inetaddr struct to facilitate event data usage
event_hdl_cb_data_server_inetaddr struct had some anonymous structs
defined in it, making it impossible to pass as a function argument and
harder to maintain since changes must be performed at multiple places
at once. So instead we define a storage struct named server_inetaddr
that helps to save addr:port server information in INET context.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
835263047e OPTIM: server: ebtree lookups for findserver_unique_* functions
4e5e2664 ("MINOR: proxy: add findserver_unique_id() and findserver_unique_name()")
added findserver_unique_id() and findserver_unique_name() functions that
were inspired from the historical findserver() function, so unfortunately
they don't perform well when used on large backend farms because they scan
the whole server list linearly.

I was about to provide a patch to optimize such functions when I stumbled
on Baptiste's work:
  19a106d24 ("MINOR: server: server_find functions: id, name, best_match")

It turns out Baptiste already implemented helper functions to supersed
the unoptimized findserver() function (at least at runtime when servers
have been assigned their final IDs and inserted in the lookup trees): they
offer more matching options and rely on eb lookups so they are much more
suitable for fast queries. I don't know how I missed that, but they are a
perfect base for the server rid matching functions.

So in this patch, we essentially revert 4e5e2664 to provide the optimized
equivalent functions named server_find_by_id_unique() and
server_find_by_name_unique(), then we force existing findserver_unique_*()
callers to switch to the new functions.

This patch depends on:
 - "OPTIM: server: eb lookup for server_find_by_name()"

This could be backported up to 2.8.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
4bcfe30414 OPTIM: server: eb lookup for server_find_by_name()
server_find_by_name() function was added in 19a106d24 ("MINOR: server:
server_find functions: id, name, best_match").

At that time, only the used_server_id proxy tree was available, thus the
name lookup was performed as a linear search.

However, used_server_name proxy tree was added in 84d6046a ("MINOR: proxy:
Add a "server by name" tree to proxy."), so we may safely rely on it to
perform server name lookups now. This will hopefully make the function
quite faster, especially when performing lookups in huge backend farms.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
e35fa36360 MINOR: proxy: monitor-uri works with tcp->http upgrades
Currently, we have a check in proxy_cfg_ensure_no_http() that generates a
warning if the monitor-uri is configured on a proxy that doesn't have
mode HTTP enabled.

However, when we give a look at monitor-uri implementation, it's not
100% correct. Indeed, despite the warning message, the directive will
still be evaluated when HTTP upgrade occurs from a TCP frontend.
Thus the error is misleading.

To make the error message comply with the actual behavior, the check was
moved alongside other checks that accept both native HTTP mode or HTTP
upgrades in cfgparse.c.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
8a6cc6e3ea MEDIUM: proxy: set PR_O_HTTP_UPG on implicit upgrades
When a TCP frontend uses an HTTP backend, the stream is automatically
upgraded and it results in a similar behavior as if a switch-mode http
rule was evaluated since stream_set_http_mode() gets called in both
situations and minimal HTTP analyzers are set.

In the current implementation, some postparsing checks are generating
errors or warnings when the frontend is in TCP mode with some HTTP options
set and no upgrade is expected (no switch-rule http). But as you can guess,
unfortunately this leads in issues when such "HTTP" only options are used
in a frontend that has implicit switching rules (that is, when the
frontend uses an HTTP backend for example), because in this case the
PR_O_HTTP_UPG will not be set, so the postparsing checks will consider
that some options are not relevant and will raise some warnings.

Consider the following example:

  backend back
    mode http
    server s1 git.haproxy.org:80
  frontend front
    mode tcp
    bind localhost:8080
    http-request set-var(txn.test) str(TRUE),debug(WORKING,stderr)
    use_backend back

By starting an haproxy instance with the above example conf, we end up
having this warning:

  [WARNING]  (400280) : config : 'http-request' rules ignored for frontend 'front' as they require HTTP mode.

However, by making a request on the frontend, we notice that the request
rules are still executed, and that's because the stream is effectively
upgraded as a result of an implicit upgrade:

  [debug] WORKING: type=str <TRUE>

So this confirms the previous description: since implicit and explicit
upgrades result in approximately the same behavior on the frontend side,
we should consider them both when doing postparsing checks.

This is what we try to address in the following commit: PR_O_HTTP_UPG
flag is now more generic in the sense that it refers to either implicit
(through default_backend or use_backend rules) or explicit (switch-mode
rules) upgrades. Indeed, everytime an HTTP or dynamic backend (where the
mode cannot be assumed during parsing) is encountered in default_backend
directive or use_backend rules, we explicitly position the upgrade flag
so that further checks that depend on the proxy being in HTTP context
don't report false warnings.
2023-12-21 14:22:26 +01:00
Aurelien DARRAGON
64b7d8e173 BUG/MEDIUM: stats: unhandled switching rules with TCP frontend
Consider the following configuration:

  backend back
    mode http

  frontend front
    mode tcp
    bind localhost:8080
    stats enable
    stats uri /stats
    tcp-request content switch-mode http if FALSE
    use_backend back

Firing a request to /stats on the haproxy process started with the above
configuration will cause a segfault in http_handle_stats().

The cause for the crash is that in this case, the upgrade doesn't simply
switches to HTTP mode, but also changes the stream backend (changing from
the frontend itself to the targeted HTTP backend).

However, there is an inconsitency in the stats logic between the check
for the stats URI and the actual handling of the stats page.

Indeed, http_stats_check_uri() checks uri parameters from the proxy
undergoing the http analyzers, whereas http_handle_stats() uses s->be
instead.

During stream analysis, from the frontend perspective: s->be defaults to
the frontend. But if the frontend is in TCP mode and the stream is
upgraded to HTTP via backend switching rules, then s->be will be assigned
to the actual HTTP-capable backend in stream_set_backend().

What this means is that when the http analyzer first checks if the current
URI matches the one from the "stats uri" directive, it will check against
the "stats uri" directive from the frontend, but later since the stats
handlers reads the uri from s->be it wil actually use the value from the
backend and the previous safety checks are thus garbage, resulting in
unexpected behavior. (In our test case since the backend didn't define
"stats uri" it is set to NULL, and http_handle_stats() dereferences it)

To fix this, we should ensure that prechecks and actual stats processing
always rely on the same proxy source for stats config directives.

This is what is done in this patch, thanks to the previous commit, since
we can make sure that the stat applet will use ->http_px as its parent
proxy. So here we simply propagate the current proxy being analyzed
through all the stats processing functions.

This patch depends on:
 - MINOR: stats: store the parent proxy in stats ctx (http)

It should be backported up to 2.4.
For 2.4: the fix is less trivial since stats ctx was directly stored
within the applet struct at that time, so this alternative patch must be
used instead (without "MINOR: stats: store the parent proxy in stats ctx
(http)" dependency):

    diff --git a/include/haproxy/applet-t.h b/include/haproxy/applet-t.h
    index 014e01ed9..1d9a63359 100644
    --- a/include/haproxy/applet-t.h
    +++ b/include/haproxy/applet-t.h
    @@ -121,6 +121,7 @@ struct appctx {
     		 * keep the grouped together and avoid adding new ones.
     		 */
     		struct {
    +			struct proxy *http_px;  /* parent proxy of the current applet (only relevant for HTTP applet) */
     			void *obj1;             /* context pointer used in stats dump */
     			void *obj2;             /* context pointer used in stats dump */
     			uint32_t domain;        /* set the stats to used, for now only proxy stats are supported */
    diff --git a/src/http_ana.c b/src/http_ana.c
    index b557da89d..1025d7711 100644
    --- a/src/http_ana.c
    +++ b/src/http_ana.c
    @@ -63,8 +63,8 @@ static enum rule_result http_req_restrict_header_names(struct stream *s, struct
     static void http_manage_client_side_cookies(struct stream *s, struct channel *req);
     static void http_manage_server_side_cookies(struct stream *s, struct channel *res);

    -static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *backend);
    -static int http_handle_stats(struct stream *s, struct channel *req);
    +static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *px);
    +static int http_handle_stats(struct stream *s, struct channel *req, struct proxy *px);

     static int http_handle_expect_hdr(struct stream *s, struct htx *htx, struct http_msg *msg);
     static int http_reply_100_continue(struct stream *s);
    @@ -428,7 +428,7 @@ int http_process_req_common(struct stream *s, struct channel *req, int an_bit, s
     		}

     		/* parse the whole stats request and extract the relevant information */
    -		http_handle_stats(s, req);
    +		http_handle_stats(s, req, px);
     		verdict = http_req_get_intercept_rule(px, &px->uri_auth->http_req_rules, s);
     		/* not all actions implemented: deny, allow, auth */

    @@ -3959,16 +3959,16 @@ void http_check_response_for_cacheability(struct stream *s, struct channel *res)

     /*
      * In a GET, HEAD or POST request, check if the requested URI matches the stats uri
    - * for the current backend.
    + * for the current proxy.
      *
      * It is assumed that the request is either a HEAD, GET, or POST and that the
      * uri_auth field is valid.
      *
      * Returns 1 if stats should be provided, otherwise 0.
      */
    -static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *backend)
    +static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct proxy *px)
     {
    -	struct uri_auth *uri_auth = backend->uri_auth;
    +	struct uri_auth *uri_auth = px->uri_auth;
     	struct htx *htx;
     	struct htx_sl *sl;
     	struct ist uri;
    @@ -4003,14 +4003,14 @@ static int http_stats_check_uri(struct stream *s, struct http_txn *txn, struct p
      * s->target which is supposed to already point to the stats applet. The caller
      * is expected to have already assigned an appctx to the stream.
      */
    -static int http_handle_stats(struct stream *s, struct channel *req)
    +static int http_handle_stats(struct stream *s, struct channel *req, struct proxy *px)
     {
     	struct stats_admin_rule *stats_admin_rule;
     	struct stream_interface *si = &s->si[1];
     	struct session *sess = s->sess;
     	struct http_txn *txn = s->txn;
     	struct http_msg *msg = &txn->req;
    -	struct uri_auth *uri_auth = s->be->uri_auth;
    +	struct uri_auth *uri_auth = px->uri_auth;
     	const char *h, *lookup, *end;
     	struct appctx *appctx;
     	struct htx *htx;
    @@ -4020,6 +4020,7 @@ static int http_handle_stats(struct stream *s, struct channel *req)
     	memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
     	appctx->st1 = appctx->st2 = 0;
     	appctx->ctx.stats.st_code = STAT_STATUS_INIT;
    +	appctx->ctx.stats.http_px = px;
     	appctx->ctx.stats.flags |= uri_auth->flags;
     	appctx->ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
     	if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD))
    diff --git a/src/stats.c b/src/stats.c
    index d1f3daa98..1f0b2bff7 100644
    --- a/src/stats.c
    +++ b/src/stats.c
    @@ -2863,9 +2863,9 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px)
     	return stats_dump_one_line(stats, stats_count, appctx);
     }

    -/* Dumps the HTML table header for proxy <px> to the trash for and uses the state from
    - * stream interface <si> and per-uri parameters <uri>. The caller is responsible
    - * for clearing the trash if needed.
    +/* Dumps the HTML table header for proxy <px> to the trash and uses the state from
    + * stream interface <si>. The caller is responsible for clearing the trash if
    + * needed.
      */
     static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px)
     {
    @@ -3015,17 +3015,19 @@ static void stats_dump_html_px_end(struct stream_interface *si, struct proxy *px
      * input buffer. Returns 0 if it had to stop dumping data because of lack of
      * buffer space, or non-zero if everything completed. This function is used
      * both by the CLI and the HTTP entry points, and is able to dump the output
    - * in HTML or CSV formats. If the later, <uri> must be NULL.
    + * in HTML or CSV formats.
      */
     int stats_dump_proxy_to_buffer(struct stream_interface *si, struct htx *htx,
    -			       struct proxy *px, struct uri_auth *uri)
    +			       struct proxy *px)
     {
     	struct appctx *appctx = __objt_appctx(si->end);
    -	struct stream *s = si_strm(si);
     	struct channel *rep = si_ic(si);
     	struct server *sv, *svs;	/* server and server-state, server-state=server or server->track */
     	struct listener *l;
    +	struct uri_auth *uri = NULL;

    +	if (appctx->ctx.stats.http_px)
    +		uri = appctx->ctx.stats.http_px->uri_auth;
     	chunk_reset(&trash);

     	switch (appctx->ctx.stats.px_st) {
    @@ -3045,7 +3047,7 @@ int stats_dump_proxy_to_buffer(struct stream_interface *si, struct htx *htx,
     					break;

     				/* match '.' which means 'self' proxy */
    -				if (strcmp(scope->px_id, ".") == 0 && px == s->be)
    +				if (strcmp(scope->px_id, ".") == 0 && px == appctx->ctx.stats.http_px)
     					break;
     				scope = scope->next;
     			}
    @@ -3227,10 +3229,16 @@ int stats_dump_proxy_to_buffer(struct stream_interface *si, struct htx *htx,
     }

     /* Dumps the HTTP stats head block to the trash for and uses the per-uri
    - * parameters <uri>. The caller is responsible for clearing the trash if needed.
    + * parameters from the parent proxy. The caller is responsible for clearing
    + * the trash if needed.
      */
    -static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
    +static void stats_dump_html_head(struct appctx *appctx)
     {
    +	struct uri_auth *uri;
    +
    +	BUG_ON(!appctx->ctx.stats.http_px);
    +	uri = appctx->ctx.stats.http_px->uri_auth;
    +
     	/* WARNING! This must fit in the first buffer !!! */
     	chunk_appendf(&trash,
     	              "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
    @@ -3345,17 +3353,21 @@ static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
     }

     /* Dumps the HTML stats information block to the trash for and uses the state from
    - * stream interface <si> and per-uri parameters <uri>. The caller is responsible
    - * for clearing the trash if needed.
    + * stream interface <si> and per-uri parameters from the parent proxy. The caller
    + * is responsible for clearing the trash if needed.
      */
    -static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *uri)
    +static void stats_dump_html_info(struct stream_interface *si)
     {
     	struct appctx *appctx = __objt_appctx(si->end);
     	unsigned int up = (now.tv_sec - start_date.tv_sec);
     	char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
     	const char *scope_ptr = stats_scope_ptr(appctx, si);
    +	struct uri_auth *uri;
     	unsigned long long bps = (unsigned long long)read_freq_ctr(&global.out_32bps) * 32;

    +	BUG_ON(!appctx->ctx.stats.http_px);
    +	uri = appctx->ctx.stats.http_px->uri_auth;
    +
     	/* Turn the bytes per second to bits per second and take care of the
     	 * usual ethernet overhead in order to help figure how far we are from
     	 * interface saturation since it's the only case which usually matters.
    @@ -3629,8 +3641,7 @@ static void stats_dump_json_end()
      * a pointer to the current server/listener.
      */
     static int stats_dump_proxies(struct stream_interface *si,
    -                              struct htx *htx,
    -                              struct uri_auth *uri)
    +                              struct htx *htx)
     {
     	struct appctx *appctx = __objt_appctx(si->end);
     	struct channel *rep = si_ic(si);
    @@ -3650,7 +3661,7 @@ static int stats_dump_proxies(struct stream_interface *si,
     		px = appctx->ctx.stats.obj1;
     		/* skip the disabled proxies, global frontend and non-networked ones */
     		if (!px->disabled && px->uuid > 0 && (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
    -			if (stats_dump_proxy_to_buffer(si, htx, px, uri) == 0)
    +			if (stats_dump_proxy_to_buffer(si, htx, px) == 0)
     				return 0;
     		}

    @@ -3666,14 +3677,12 @@ static int stats_dump_proxies(struct stream_interface *si,
     }

     /* This function dumps statistics onto the stream interface's read buffer in
    - * either CSV or HTML format. <uri> contains some HTML-specific parameters that
    - * are ignored for CSV format (hence <uri> may be NULL there). It returns 0 if
    - * it had to stop writing data and an I/O is needed, 1 if the dump is finished
    - * and the stream must be closed, or -1 in case of any error. This function is
    - * used by both the CLI and the HTTP handlers.
    + * either CSV or HTML format. It returns 0 if it had to stop writing data and
    + * an I/O is needed, 1 if the dump is finished and the stream must be closed,
    + * or -1 in case of any error. This function is used by both the CLI and the
    + * HTTP handlers.
      */
    -static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *htx,
    -				     struct uri_auth *uri)
    +static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *htx)
     {
     	struct appctx *appctx = __objt_appctx(si->end);
     	struct channel *rep = si_ic(si);
    @@ -3688,7 +3697,7 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht

     	case STAT_ST_HEAD:
     		if (appctx->ctx.stats.flags & STAT_FMT_HTML)
    -			stats_dump_html_head(appctx, uri);
    +			stats_dump_html_head(appctx);
     		else if (appctx->ctx.stats.flags & STAT_JSON_SCHM)
     			stats_dump_json_schema(&trash);
     		else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
    @@ -3708,7 +3717,7 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht

     	case STAT_ST_INFO:
     		if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
    -			stats_dump_html_info(si, uri);
    +			stats_dump_html_info(si);
     			if (!stats_putchk(rep, htx, &trash))
     				goto full;
     		}
    @@ -3733,7 +3742,7 @@ static int stats_dump_stat_to_buffer(struct stream_interface *si, struct htx *ht
     		case STATS_DOMAIN_PROXY:
     		default:
     			/* dump proxies */
    -			if (!stats_dump_proxies(si, htx, uri))
    +			if (!stats_dump_proxies(si, htx))
     				return 0;
     			break;
     		}
    @@ -4112,11 +4121,14 @@ static int stats_process_http_post(struct stream_interface *si)
     static int stats_send_http_headers(struct stream_interface *si, struct htx *htx)
     {
     	struct stream *s = si_strm(si);
    -	struct uri_auth *uri = s->be->uri_auth;
    +	struct uri_auth *uri;
     	struct appctx *appctx = __objt_appctx(si->end);
     	struct htx_sl *sl;
     	unsigned int flags;

    +	BUG_ON(!appctx->ctx.stats.http_px);
    +	uri = appctx->ctx.stats.http_px->uri_auth;
    +
     	flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
     	sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
     	if (!sl)
    @@ -4166,11 +4178,14 @@ static int stats_send_http_redirect(struct stream_interface *si, struct htx *htx
     {
     	char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
     	struct stream *s = si_strm(si);
    -	struct uri_auth *uri = s->be->uri_auth;
    +	struct uri_auth *uri;
     	struct appctx *appctx = __objt_appctx(si->end);
     	struct htx_sl *sl;
     	unsigned int flags;

    +	BUG_ON(!appctx->ctx.stats.http_px);
    +	uri = appctx->ctx.stats.http_px->uri_auth;
    +
     	/* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
     	scope_txt[0] = 0;
     	if (appctx->ctx.stats.scope_len) {
    @@ -4263,7 +4278,7 @@ static void http_stats_io_handler(struct appctx *appctx)
     	}

     	if (appctx->st0 == STAT_HTTP_DUMP) {
    -		if (stats_dump_stat_to_buffer(si, res_htx, s->be->uri_auth))
    +		if (stats_dump_stat_to_buffer(si, res_htx))
     			appctx->st0 = STAT_HTTP_DONE;
     	}

    @@ -4888,6 +4903,7 @@ static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx

     	appctx->ctx.stats.scope_str = 0;
     	appctx->ctx.stats.scope_len = 0;
    +	appctx->ctx.stats.http_px = NULL; // not under http context
     	appctx->ctx.stats.flags = STAT_SHNODE | STAT_SHDESC;

     	if ((strm_li(si_strm(appctx->owner))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
    @@ -4954,7 +4970,7 @@ static int cli_io_handler_dump_info(struct appctx *appctx)
      */
     static int cli_io_handler_dump_stat(struct appctx *appctx)
     {
    -	return stats_dump_stat_to_buffer(appctx->owner, NULL, NULL);
    +	return stats_dump_stat_to_buffer(appctx->owner, NULL);
     }

     static int cli_io_handler_dump_json_schema(struct appctx *appctx)
2023-12-21 14:21:53 +01:00
Aurelien DARRAGON
ef9d692544 MINOR: stats: store the parent proxy in stats ctx (http)
Some HTTP related stats functions need to know the parent proxy, mainly
to get a pointer on the related uri_auth set by the proxy or to check
scope settings.

The current design (probably historical as only the http context existed
by then) took the other approach: it propagates the uri pointer from the
http context deep down the calling stack up to the relevant functions.
For non-http contexts (cli), the pointer is set to NULL.

Doing so is not very pretty and not easy to maintain. Moreover, there were
still some places in the code were the uri pointer was learned directly
from the stream proxy because the argument was not available as argument
from those functions. This is error-prone, because if one day we decide to
change the source proxy in the parent function, we might still have some
functions down the stack that ignore the top most argument and still do
on their own, and we'll probably end up with inconsistencies.

So in this patch, we take a safer approach: the caller responsible for
creating the stats applet should set the http_px pointer so that any stats
function running under the applet that needs to know if it's running in
http context or needs to access parent proxy info may do so thanks to
the dedicated ctx->http_px pointer.
2023-12-21 14:20:03 +01:00
Christopher Faulet
123a9e7d83 BUG/MAJOR: stconn: Disable zero-copy forwarding if consumer is shut or in error
A regression was introduced by commit 2421c6fa7d ("BUG/MEDIUM: stconn: Block
zero-copy forwarding if EOS/ERROR on consumer side"). When zero-copy
forwarding is inuse and the consumer side is shut or in error, we declare it
as blocked and it is woken up. The idea is to handle this state at the
stream-connector level. However this definitly blocks receives on the
producer side. So if the mux is unable to close by itself, but instead wait
the peer to shut, this can lead to a wake up loop. And indeed, with the
passthrough multiplexer this may happen.

To fix the issue and prevent any loop, instead of blocking the zero-copy
forwarding, we now disable it. This way, the stream-connector on producer
side will fallback on classical receives and will be able to handle peer
shutdown properly. In addition, the wakeup of the consumer side was
removed. This will be handled, if necessary, by sc_notify().

This patch should fix the issue #2395. It must be backported to 2.9.
2023-12-21 11:00:57 +01:00
Amaury Denoyelle
9ab107b84b MINOR: h3: use INTERNAL_ERROR code for init failure
Consider that application layer is responsible to set proper error code
on init or finalize operation failure. In case of H3, use INTERNAL_ERROR
application error code. This allows to remove qcc_set_error() invocation
from qmux_init().

In case application layer would not specify any error code, fallback
INTERNAL_ERROR transport error code would be used thanks to the recent
change introduced for error management in qmux_init().
2023-12-20 15:40:02 +01:00
Amaury Denoyelle
7a3602a1f5 BUG/MINOR: h3: properly handle alloc failure on finalize
If H3 control stream Tx buffer cannot be allocated, return a proper
errur through h3_finalize(). This will cause the emission of a
CONNECTION_CLOSE with error H3_INTERNAL_ERROR and closure of the whole
connection.

This should be backported up to 2.6. Note that 2.9 has some difference
which will cause conflict. The main one is that qcc_get_stream_txbuf()
does not exist in this version. Instead the check in h3_control_send()
should be made after mux_get_buf(). Finally, it may be useful to first
pick previous commit (MINOR: h3: add traces for connection init stage)
to improve context similarity.
2023-12-20 15:39:51 +01:00
Amaury Denoyelle
a2dbd6d916 MINOR: h3: add traces for connection init stage
Add traces H3_EV_H3C_NEW. These are used for h3_init() and h3_finalize()
functions.
2023-12-20 15:27:11 +01:00
Amaury Denoyelle
403492af8e MINOR: mux-quic: adjust error code in init failure
If QUIC MUX cannot be initialized for any reason, the connection is shut
down with a CONNECTION_CLOSE frame. Previously, no error code was
explicitely specified, resulting in "no error" code.

Change this by always set error code in case of QUIC MUX failure. Use
the already defined QUIC MUX error code or "internal error" if unset.
Call quic_set_connection_close() on error label to register it to the
quic_conn layer.

This should help to improve error reporting in case of MUX
initialization failure.
2023-12-20 15:27:11 +01:00
Amaury Denoyelle
bcade776c2 MINOR: mux-quic: use qcc_release in case of init failure
qmux_init() may fail at different stage. In this case, an error is
returned and QCC allocated element are freed. Previously, extra care was
taken using different label to only liberate already allocate elements.

This patch removes the multi label and uses qcc_release(). This will be
simpler to ensure a QCC is always properly freed. The only important
thing is to ensure that mandatory fields are properly initialized to
NULL or equivalent to be able to use qcc_release() safely.
2023-12-20 15:27:11 +01:00
Amaury Denoyelle
3c38bb7ee1 MINOR: mux-quic: remove qcc_shutdown() from qcc_release()
Render qcc_release() more generic by removing qcc_shutdown(). This
prevents systematic graceful shutdown/CONNECTION_CLOSE emission if only
QCC resource deallocation is necessary.

For now, qcc_shutdown() is used before every qcc_release() invocation.
The only exception is on qmux_destroy stream layer callback.

This commit will be useful to reuse qcc_release() in other contexts to
simply deallocate a QCC instance.
2023-12-20 15:27:11 +01:00
Christopher Faulet
3811c1de25 BUG/MINOR: server: Use the configured address family for the initial resolution
A regression was introduced by the commit c886fb58eb ("MINOR: server/ip:
centralize server ip updates"). The configured address family is lost when the
server address is initialized during the startup, for the resolution based on
the libc or based on the server state-file. Thus, "ipv4@" and "ipv6@" prefixed
are ignored.

To fix the bug, we take care to use the configured address family before calling
str2ip2() in srv_apply_lastaddr() and srv_apply_via_libc() functions.

This patch should fix the issue #2393. It must be backported to 2.9.
2023-12-20 12:21:59 +01:00
Amaury Denoyelle
d2540b2f72 MINOR: h3: remove quic_conn only reference
H3 uses a direct reference to quic_conn to access the listener instance.
This can be replaced by using qcc->conn->target. This allows to remove
quic_conn-t.h header include from it.
2023-12-20 10:38:30 +01:00
Willy Tarreau
71f626e3e2 DEV: patchbot: allow to show/hide backported patches
In order to spot old patches marked "wait" that have not yet been
backported, it's convenient to be able to click "all" to start the
review from the first patch, deselect "no", "uncertain" and "backported",
leaving only "wait" and "yes". This will reveal all pending patches that
have still not yet been backported, including those prior to the last
review, allowing to reconsider older patches marked "wait" that have
not yet been picked.
2023-12-19 17:01:35 +01:00
Willy Tarreau
896d452015 DEV: patchbot: use checked buttons as reference instead of internal table
The statuses[] table was pre-filled from the shell code during
initialization based on the evaluation and the buttons pre-checked
accordingly, but upon reload, the checked buttons are preserved and
the statuses reinitialized, leading to a different status and color
on lines that were changed.

In practice we don't need this table and we can directly check each
button's state. This makes sure that displayed state is consistent
with checked buttons and allows to preserve the statuses upon reloads
to benefit from updates. Only the start of the review is reset upon
reload now (this allows to consider latest backport state). Of course,
a full reload (shift-ctrl-R) continues to reset the form.
2023-12-19 16:22:04 +01:00
Christopher Faulet
0a203c1d3f DOC: config: Update documentation about local haproxy response
Documentation about 'L' state in the termination state was outdated. Today,
not only the request may be intercepted, but also the response.
Documentation about 'L' must be more generic.

However, documentation about possible 2-letter termination states was also
extended to add 'LC' and 'LH' in the list. And 'LR' was adapted too.

This patch should fix the issue #2384. It may be backported to every stable
versions. Note that on 2.8 and lowers, we talk about session and not stream.
2023-12-19 10:53:22 +01:00
Christopher Faulet
d9eb6d6680 BUG/MEDIUM: mux-h2: Don't report error on SE for closed H2 streams
An error on the H2 connection was always reported as an error to the
stream-endpoint descriptor, independently on the H2 stream state. But it is
a bug to do so for closed streams. And indeed, it leads to report "SD--"
termination state for some streams while the response was fully received and
forwarded to the client, at least for the backend side point of view.

Now, errors are no longer reported for H2 streams in closed state.

This patch is related to the three previous ones:

 * "BUG/MEDIUM: mux-h2: Don't report error on SE for closed H2 streams"
 * "BUG/MEDIUM: mux-h2: Don't report error on SE if error is only pending on H2C"
 * "BUG/MEDIUM: mux-h2: Only Report H2C error on read error if demux buffer is empty"

The series should fix a bug reported in issue #2388
(#2388#issuecomment-1855735144). The series should be backported to 2.9 but
only after a period of observation. In theory, older versions are also
affected but this part is pretty sensitive. So don't backport it further
except if someone ask for it.
2023-12-18 21:15:32 +01:00
Christopher Faulet
580ffd6123 BUG/MEDIUM: mux-h2: Don't report error on SE if error is only pending on H2C
In h2s_wake_one_stream(), we must not report an error on the stream-endpoint
descriptor if the error is not definitive on the H2 connection. A pending
error on the H2 connection means there are potentially remaining data to be
demux. It is important to not truncate a message for a stream.

This patch is part of a series that should fix a bug reported in issue #2388
(#2388#issuecomment-1855735144). Backport instructions will be shipped in
the last commit of the series.
2023-12-18 21:15:32 +01:00
Christopher Faulet
19fb19976f BUG/MEDIUM: mux-h2: Only Report H2C error on read error if demux buffer is empty
It is similar to the previous fix ("BUG/MEDIUM: mux-h2: Don't report H2C
error on read error if dmux buffer is not empty"), but on receive side. If
the demux buffer is not empty, an error on the TCP connection must not be
immediately reported as an error on the H2 connection. We must be sure to
have tried to demux all data first. Otherwise, messages for one or more
streams may be truncated while all data were already received and are
waiting to be demux.

This patch is part of a series that should fix a bug reported in issue #2388
(#2388#issuecomment-1855735144). Backport instructions will be shipped in
the last commit of the series.
2023-12-18 21:15:32 +01:00
Christopher Faulet
5b78cbae77 BUG/MEDIUM: mux-h2: Switch pending error to error if demux buffer is empty
When an error on the H2 connection is detected when sending data, only a
pending error is reported, waiting for an error or a shutdown on the read
side. However if a shutdown was already received, the pending error is
switched to a definitive error.

At this stage, we must also wait to have flushed the demux
buffer. Otherwise, if some data must still be demux, messages for one or
more streams may be truncated. There is already the flag H2_CF_END_REACHED
to know a shutdown was received and we no longer progress on demux side
(buffer empty or data truncated). On sending side, we should use this flag
instead to report a definitive error.

This patch is part of a series that should fix a bug reported in issue #2388
(#2388#issuecomment-1855735144). Backport instructions will be shipped in
the last commit of the series.
2023-12-18 21:15:32 +01:00
Willy Tarreau
693da29ab7 DEV: patchbot: add the AI-based bot to pre-select candidate patches to backport
This is a set of scripts, prompts and howtos to have an LLM read commit
messages and determine with great accuracy whether the patch's author
intended for the patch to be backported ASAP, backported after some time,
not backported, or unknown state. It provides all this in an interactive
interface making it easy to adjust choices and proceed with what was
selected. This has been improving over the last 9 months, as helped to
spot patches for a handful of backport sessions, and was only limited by
usability issues (UI). Now that these issues are solved, let's commit the
tool in its current working state. It currently runs every hour in a
crontab for me and started to prove useful since the last update, so it
should be considered in a usable state now, especially since this latest
update reaches close to 100% accuracy compared to a human choice, so it
saves precious development time and may allow stable releases to be
emitted more regularly.

There's detailed readme, please read it before complaining about the
ugliness of the UI :-)
2023-12-18 20:50:51 +01:00
Willy Tarreau
57c5ae10f6 SCRIPTS: mk-patch-list: produce a list of patches
There does not seem to be a convenient way to tell git-show-backports to
produce individual patches with numbers. That's what this script does by
calling git-format-patch for each specified commit ID, letting git do all
the painful work (formatting etc). This has been mostly used during
backport sessions but was apparently never committed!
2023-12-18 20:50:51 +01:00
William Lallemand
0d2ebb53f7 BUG/MINOR: resolvers: default resolvers fails when network not configured
Bug #1740 was opened again, this time a user is complaining about the
"can't create socket for nameserver". This can happen if the resolv.conf
file contains a class of address which was not configured on the
machine, for example IPv6.

The fix does the same as b10b1196b ("MINOR: resolvers: shut the warning
when "default" resolvers is implicit"), and uses the
"resolvers->conf.implicit" variable to emit the error.

Though it is not needed to convert the explicit behavior with a
ERR_WARN, because this is supposed to be an unrecoverable error, unlike
the connect().

Should fix issue #1740.

Must be backported were b10b1196b was backported. (as far as 2.6)
2023-12-18 15:50:07 +01:00
Willy Tarreau
c5bde03a0a DOC: config: also add arguments to the converters in the table
Now that dconv supports linking to keywords with arguments from tables,
let's mention the arguments in the summary table of converters.
2023-12-15 11:18:27 +01:00
Willy Tarreau
0d261dd13a DOC: config: add arguments to sample fetch methods in the table
Now that dconv supports linking to keywords with arguments from tables,
let's mention the arguments in the summary tables of sample fetch methods.
2023-12-15 11:18:08 +01:00
Amaury Denoyelle
af297f19f6 BUG/MEDIUM: mux-quic: report early error on stream
On STOP_SENDING reception, an error is notified to the stream layer as
no more data can be responded. However, this is not done if the stream
instance is not allocated (already freed for example).

The issue occurs if STOP_SENDING is received and the stream instance is
instantiated after it. It happens if a STREAM frame is received after it
with H3 HEADERS, which is valid in QUIC protocol due to UDP packet
reordering. In this case, stream layer is never notified about the
underlying error. Instead, reponse buffers are silently purged by the
MUX in qmux_strm_snd_buf().

This is suboptimal as there is no point in exchanging data from the
server if it cannot be eventually transferred back to the client.
However, aside from this consideration, no other issue occured. However,
this is not the case with QUIC mux-to-mux implementation. Now, if
mux-to-mux is used, qmux_strm_snd_buf() is bypassed and response if
transferred via nego_ff/done_ff callbacks. However, these functions did
not checked if QCS is already locally closed. This causes a crash when
qcc_send_stream() is called via done_ff.

To fix this crash, there is several approach, one of them would be to
adjust nego_ff/done_ff QUIC callbacks. However, another method has been
chosen. Now stream layer is flagged on error just after its
instantiation if the stream is already locally closed. This ensures that
mux-to-mux won't try to emit data as se_nego_ff() check if the opposide
SD is not on error before continuing.

Note that an alternative solution could be to not instantiate at all
stream layer if QCS is already locally closed. This is the most optimal
solution as it reduce unnecessary allocations and task processing.
However, it's not easy to implement so the easier bug fix has been
chosen for the moment.

This patch is labelled as MEDIUM as it can change behavior of all QCS
instances, wheter mux-to-mux is used or not, and thus could reveal other
architecture issues.

This should fix latest crash occurence on github issue #2392.

It should be backported up to 2.6, until a necessary period of
observation.
2023-12-14 11:15:46 +01:00
Christopher Faulet
682f73b4fa BUG/MEDIUM: mux-h2: Report too large HEADERS frame only when rxbuf is empty
During HEADERS frames decoding, if a frame is too large to fit in a buffer,
an internal error is reported and a RST_STREAM is emitted. On the other
hand, we wait to have an empty rxbuf to decode the frame because we cannot
retry a failed HPACK decompression.

When we are decoding headers, it is valid to return an error if dbuf buffer
is full because no data can be blocked in the rxbuf (which hosts the HTX
message).

However, during the trailers decoding, it is possible to have some data not
sent yet for the current stream in the rxbug and data for another stream
fully filling the dbuf buffer. In this case, we don't decode the trailers
but we must not return an error. We must wait to empty the rxbuf first.

Now, a HEADERS frame is considered as too large if the dbuf buffer is full
and if the rxbuf is empty (the HTX message to be accurate).

This patch should fix the issue #2382. It must be backported to all stable
versions.
2023-12-13 16:45:29 +01:00
Christopher Faulet
65ca444240 CLEANUP: mux-h1: Fix a trace message about C-L header addition
This fixes a cut-paste error on a trace message notifying a 'Content-Length'
header was added during the HTTP message formatting.
2023-12-13 16:45:29 +01:00
Christopher Faulet
966a18e2b4 BUG/MEDIUM: mux-h1: Explicitly skip request's C-L header if not set originally
Commit f89ba27caa ("BUG/MEDIUM: mux-h1; Ignore headers modifications about
payload representation") introduced a regression. The Content-Length is no
longer sent to the server for requests without payload but with a
'Content-Lnegth' header explicitly set to 0, like POST request with no
payload. It is of course unexpected. In some cases, depending on the server,
such requests are considered as invalid and a 411-Length-Required is returned.

The above commit is not directly responsible for the bug, it only reveals a too
lax condition to skip the 'Content-Length' header of bodyless requests. We must
only skip this header if none was originally found, during the parsing.

This patch should fix the issue #2386. It must be backported to 2.9.
2023-12-13 16:45:29 +01:00
Christopher Faulet
eed1e8733c BUG/MEDIUM: mux-h1: Cound data from input buf during zero-copy forwarding
During zero-copy forwarding, we first try to forward data found in the input
buffer before trying to receive more data. These data must be removed from
the amount of data to forward (the cound variable).

Otherwise, on an internal retry, in h1_fastfwd(), we can be lead to read
more data than expected. It is especially a problem on the end of a
chunk. An error is erroneously reported because more data than announced are
received.

This patch should fix the issue #2382. It must be backported to 2.9.
2023-12-13 16:45:29 +01:00
Christopher Faulet
2421c6fa7d BUG/MEDIUM: stconn: Block zero-copy forwarding if EOS/ERROR on consumer side
When the producer side (h1 for now) negociates with the consumer side to
perform a zero-copy forwarding, we now consider the consumer side as blocked
if it is closed and this was reported to the SE via a end-of-stream or a
(pending) error.

It is performed before calling ->nego_ff callback function, in se_nego_ff().
This way, all consumer are concerned automatically. The aim of this patch is
to fix an issue with the QUIC mux. Indeed, it is unexpected to send a frame
on an closed stream. This triggers a BUG_ON(). Other muxes are not affected
but it remains useless to try to send data if the stream is closed.

This patch should fix the issue #2372. It must be backported to 2.9.
2023-12-13 16:45:29 +01:00
Frédéric Lécaille
dd58dff1e6 BUG/MEDIUM: quic: QUIC CID removed from tree without locking
This bug arrived with this commit:

   BUG/MINOR: quic: Wrong RETIRE_CONNECTION_ID sequence number chec

Every connection ID manipulations against the by thread trees used to store the
connection IDs must be done under the trees locks. These trees are accessed by
the low level connection identification code.

When receiving a RETIRE_CONNECTION_ID frame, the concerned connection ID must
be deleted from the its underlying by thread tree but not without locking!
Add a WR lock around ebmb_delete() call to do so.

Must be backported as far as 2.7.
2023-12-13 14:42:50 +01:00
Amaury Denoyelle
f8e095b058 MINOR: hq-interop: use zero-copy to transfer single HTX data block
Similarly to H3, hq-interop now uses zero-copy when dealing with a HTX
message with only a single data block. Exchange HTX and QCS buffer, and
use the HTX data block for HTTP payload. This is only possible if QCS
buffer is empty. Contrary to HTTP/3, no extra frame header is needed
before transferring HTTP payload.

hq-interop is only implemented for testing purpose so this change should
not be noticeable by users. However, it will be useful to be able to
test zero-copy transfer on QUIC interop testing.
2023-12-12 10:31:22 +01:00
Amaury Denoyelle
d3987b69c3 MINOR: h3: adjust zero-copy sending related code
Adjust HTTP/3 data emission. First, add HTX as argument to the function
as this is used for other frames emission function. Keep the buffer
argument as this is mandatory for zero-copy. Extend comments related to
this, in particular to explain purposes of both HTX and buffer
arguments.

No function change here. This should however be useful to port a code
equivalent to hq-interop protocol.
2023-12-12 10:31:22 +01:00
Amaury Denoyelle
0e632fc9b4 MINOR: h3: complete traces for sending
Add data level traces for each encoded H3 frame. Of notable interest,
traces will be useful to detect if standard emission, zero-copy or fast
forward is used. Also add the generic filter H3_EV_TX_FRAME to be able
to filter these messages.
2023-12-12 10:31:22 +01:00
Amaury Denoyelle
1adadc4d3f MINOR: mux-quic: factorize QC_SF_UNKNOWN_PL_LENGTH set
When dealing with HTTP/1 responses without Content-Length nor chunked
encoding, flag QC_SF_UNKNOWN_PL_LENGTH is set on QCS. This prevent the
emission of a RESET_STREAM on shutw, instead resorting to a proper FIN
emission.

This code was duplicated both in H3 and hq-interop. Move it in common
qcs_http_snd_buf() to factorize it.
2023-12-12 10:14:22 +01:00
Amaury Denoyelle
e772d3f40f CLEANUP: mux-quic: clean up app ops callback definitions
qcc_app_ops is a set of callbacks used to unify application protocol
running over QUIC. This commit introduces some changes to clarify its
API :
* write simple comment to reflect each callback purpose
* rename decode_qcs to rcv_buf as this name is more common and is
  similar to already existing snd_buf
* finalize is moved up as it is used during connection init stage

All these changes are ported to HTTP/3 layer. Also function comments
have been extended to highlight HTTP/3 special characteristics.
2023-12-11 16:15:13 +01:00
Amaury Denoyelle
f496c7469b MINOR: mux-quic: clean up qcs Tx buffer allocation API
This function is similar to the previous one, but this time for QCS
sending buffer.

Previously, each application layer redefine their own version of
mux_get_buf() which was used to allocate <qcs.tx.buf>. Unify it under a
single function renamed qcc_get_stream_txbuf().
2023-12-11 16:08:51 +01:00
Amaury Denoyelle
b526ffbfb9 MINOR: mux-quic: clean up qcs Rx buffer allocation API
Replaces qcs_get_buf() function which naming does not reflect its
purpose. Add a new function qcc_get_stream_rxbuf() which allocate if
needed <qcs.rx.app_buf> and returns the buffer pointer. This function is
reserved for application protocol layer. This buffer is then accessed by
stconn layer.

For other qcs_get_buf() invocation which was used in effect for a local
buffer, replace these by a plain b_alloc().
2023-12-11 16:02:30 +01:00
Amaury Denoyelle
14d968f2f2 CLEANUP: mux-quic: remove unused prototype
Remove qcc_emit_cc_app() prototype from header file. This function was
removed by a previous commit and does not exist anymore.
2023-12-11 15:12:57 +01:00