]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
CLEANUP: stats/cli: take the "show stat" context definition out of the appctx
authorWilly Tarreau <w@1wt.eu>
Tue, 3 May 2022 15:08:29 +0000 (17:08 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 6 May 2022 16:13:35 +0000 (18:13 +0200)
This makes use of the generic command context allocation so that the
appctx doesn't have to declare a specific one anymore. The context is
created during parsing (both in the CLI and HTTP).

The change looks large but it's particularly mechanical. The context
initialization appears in stats.c and http_ana.c. The context is used
in stats.c and resolvers.c since "show stat resolvers" points there.
That's the reason why the definition moved to stats.h. "show info"
and "show stat" continue to share the same state definition for now.

Nothing else was modified.

include/haproxy/applet-t.h
include/haproxy/stats-t.h
src/http_ana.c
src/resolvers.c
src/stats.c

index 8fe2bc2cd43ddafd5a96c5099362da6b41f33287..bc1e7908f4c0ed9516df3ce2215a6b5c68bf8669 100644 (file)
@@ -138,17 +138,6 @@ struct appctx {
                        /* all entries below are used by various CLI commands, please
                         * keep the grouped together and avoid adding new ones.
                         */
-                       struct {
-                               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 */
-                               int scope_str;          /* limit scope to a frontend/backend substring */
-                               int scope_len;          /* length of the string above in the buffer */
-                               int px_st;              /* STAT_PX_ST* */
-                               unsigned int flags;     /* STAT_* */
-                               int iid, type, sid;     /* proxy id, type and service id if bounding of stats is enabled */
-                               int st_code;            /* the status code returned by an action */
-                       } stats;
                        struct {
                                struct hlua *hlua;
                                struct task *task;
index c3388b56095e58659115616e3c1c1e06a9f39113..f512419f2a83eccdbf31d84fa11b9cd6091fb2c3 100644 (file)
@@ -518,6 +518,19 @@ enum stats_domain_px_cap {
        STATS_PX_CAP_MASK = 0xff
 };
 
+/* the context of a "show stat" command in progress on the CLI or the stats applet */
+struct show_stat_ctx {
+       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 */
+       int scope_str;          /* limit scope to a frontend/backend substring */
+       int scope_len;          /* length of the string above in the buffer */
+       int px_st;              /* STAT_PX_ST* */
+       unsigned int flags;     /* STAT_* from stats-t.h */
+       int iid, type, sid;     /* proxy id, type and service id if bounding of stats is enabled */
+       int st_code;            /* the status code returned by an action */
+};
+
 extern THREAD_LOCAL void *trash_counters;
 
 #define EXTRA_COUNTERS(name) \
index a81c53d428f94de031c2a3e105a403123d372c16..1968f848c45b799a11fb38445f1b8ef0ba08acea 100644 (file)
@@ -13,6 +13,7 @@
 #include <haproxy/acl.h>
 #include <haproxy/action-t.h>
 #include <haproxy/api.h>
+#include <haproxy/applet.h>
 #include <haproxy/backend.h>
 #include <haproxy/base64.h>
 #include <haproxy/capture-t.h>
@@ -3922,16 +3923,16 @@ static int http_handle_stats(struct stream *s, struct channel *req)
        struct uri_auth *uri_auth = s->be->uri_auth;
        const char *h, *lookup, *end;
        struct appctx *appctx = __cs_appctx(s->csb);
+       struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
        struct htx *htx;
        struct htx_sl *sl;
 
-       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.flags |= uri_auth->flags;
-       appctx->ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
+       ctx->st_code = STAT_STATUS_INIT;
+       ctx->flags |= uri_auth->flags;
+       ctx->flags |= STAT_FMT_HTML; /* assume HTML mode by default */
        if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD))
-               appctx->ctx.stats.flags |= STAT_CHUNKED;
+               ctx->flags |= STAT_CHUNKED;
 
        htx = htxbuf(&req->buf);
        sl = http_get_stline(htx);
@@ -3940,14 +3941,14 @@ static int http_handle_stats(struct stream *s, struct channel *req)
 
        for (h = lookup; h <= end - 3; h++) {
                if (memcmp(h, ";up", 3) == 0) {
-                       appctx->ctx.stats.flags |= STAT_HIDE_DOWN;
+                       ctx->flags |= STAT_HIDE_DOWN;
                        break;
                }
        }
 
        for (h = lookup; h <= end - 9; h++) {
                if (memcmp(h, ";no-maint", 9) == 0) {
-                       appctx->ctx.stats.flags |= STAT_HIDE_MAINT;
+                       ctx->flags |= STAT_HIDE_MAINT;
                        break;
                }
        }
@@ -3955,7 +3956,7 @@ static int http_handle_stats(struct stream *s, struct channel *req)
        if (uri_auth->refresh) {
                for (h = lookup; h <= end - 10; h++) {
                        if (memcmp(h, ";norefresh", 10) == 0) {
-                               appctx->ctx.stats.flags |= STAT_NO_REFRESH;
+                               ctx->flags |= STAT_NO_REFRESH;
                                break;
                        }
                }
@@ -3963,31 +3964,31 @@ static int http_handle_stats(struct stream *s, struct channel *req)
 
        for (h = lookup; h <= end - 4; h++) {
                if (memcmp(h, ";csv", 4) == 0) {
-                       appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
+                       ctx->flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
                        break;
                }
        }
 
        for (h = lookup; h <= end - 6; h++) {
                if (memcmp(h, ";typed", 6) == 0) {
-                       appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
-                       appctx->ctx.stats.flags |= STAT_FMT_TYPED;
+                       ctx->flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
+                       ctx->flags |= STAT_FMT_TYPED;
                        break;
                }
        }
 
        for (h = lookup; h <= end - 5; h++) {
                if (memcmp(h, ";json", 5) == 0) {
-                       appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
-                       appctx->ctx.stats.flags |= STAT_FMT_JSON;
+                       ctx->flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
+                       ctx->flags |= STAT_FMT_JSON;
                        break;
                }
        }
 
        for (h = lookup; h <= end - 12; h++) {
                if (memcmp(h, ";json-schema", 12) == 0) {
-                       appctx->ctx.stats.flags &= ~STAT_FMT_MASK;
-                       appctx->ctx.stats.flags |= STAT_JSON_SCHM;
+                       ctx->flags &= ~STAT_FMT_MASK;
+                       ctx->flags |= STAT_JSON_SCHM;
                        break;
                }
        }
@@ -3996,10 +3997,10 @@ static int http_handle_stats(struct stream *s, struct channel *req)
                if (memcmp(h, ";st=", 4) == 0) {
                        int i;
                        h += 4;
-                       appctx->ctx.stats.st_code = STAT_STATUS_UNKN;
+                       ctx->st_code = STAT_STATUS_UNKN;
                        for (i = STAT_STATUS_INIT + 1; i < STAT_STATUS_SIZE; i++) {
                                if (strncmp(stat_status_codes[i], h, 4) == 0) {
-                                       appctx->ctx.stats.st_code = i;
+                                       ctx->st_code = i;
                                        break;
                                }
                        }
@@ -4007,8 +4008,8 @@ static int http_handle_stats(struct stream *s, struct channel *req)
                }
        }
 
-       appctx->ctx.stats.scope_str = 0;
-       appctx->ctx.stats.scope_len = 0;
+       ctx->scope_str = 0;
+       ctx->scope_len = 0;
        for (h = lookup; h <= end - 8; h++) {
                if (memcmp(h, STAT_SCOPE_INPUT_NAME "=", strlen(STAT_SCOPE_INPUT_NAME) + 1) == 0) {
                        int itx = 0;
@@ -4018,7 +4019,7 @@ static int http_handle_stats(struct stream *s, struct channel *req)
 
                        h += strlen(STAT_SCOPE_INPUT_NAME) + 1;
                        h2 = h;
-                       appctx->ctx.stats.scope_str = h2 - HTX_SL_REQ_UPTR(sl);
+                       ctx->scope_str = h2 - HTX_SL_REQ_UPTR(sl);
                        while (h < end) {
                                if (*h == ';' || *h == '&' || *h == ' ')
                                        break;
@@ -4028,16 +4029,16 @@ static int http_handle_stats(struct stream *s, struct channel *req)
 
                        if (itx > STAT_SCOPE_TXT_MAXLEN)
                                itx = STAT_SCOPE_TXT_MAXLEN;
-                       appctx->ctx.stats.scope_len = itx;
+                       ctx->scope_len = itx;
 
-                       /* scope_txt = search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+                       /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
                        memcpy(scope_txt, h2, itx);
                        scope_txt[itx] = '\0';
                        err = invalid_char(scope_txt);
                        if (err) {
                                /* bad char in search text => clear scope */
-                               appctx->ctx.stats.scope_str = 0;
-                               appctx->ctx.stats.scope_len = 0;
+                               ctx->scope_str = 0;
+                               ctx->scope_len = 0;
                        }
                        break;
                }
@@ -4056,7 +4057,7 @@ static int http_handle_stats(struct stream *s, struct channel *req)
 
                if (ret) {
                        /* no rule, or the rule matches */
-                       appctx->ctx.stats.flags |= STAT_ADMIN;
+                       ctx->flags |= STAT_ADMIN;
                        break;
                }
        }
@@ -4064,22 +4065,22 @@ static int http_handle_stats(struct stream *s, struct channel *req)
        if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
                appctx->st0 = STAT_HTTP_HEAD;
        else if (txn->meth == HTTP_METH_POST) {
-               if (appctx->ctx.stats.flags & STAT_ADMIN) {
+               if (ctx->flags & STAT_ADMIN) {
                        appctx->st0 = STAT_HTTP_POST;
                        if (msg->msg_state < HTTP_MSG_DATA)
                                req->analysers |= AN_REQ_HTTP_BODY;
                }
                else {
                        /* POST without admin level */
-                       appctx->ctx.stats.flags &= ~STAT_CHUNKED;
-                       appctx->ctx.stats.st_code = STAT_STATUS_DENY;
+                       ctx->flags &= ~STAT_CHUNKED;
+                       ctx->st_code = STAT_STATUS_DENY;
                        appctx->st0 = STAT_HTTP_LAST;
                }
        }
        else {
                /* Unsupported method */
-               appctx->ctx.stats.flags &= ~STAT_CHUNKED;
-               appctx->ctx.stats.st_code = STAT_STATUS_IVAL;
+               ctx->flags &= ~STAT_CHUNKED;
+               ctx->st_code = STAT_STATUS_IVAL;
                appctx->st0 = STAT_HTTP_LAST;
        }
 
index 7d7ced6fdab7db69b810328c37b1831a2ccd4b7e..79d6c94eee2d56686ea18b999df1298d032a5932 100644 (file)
@@ -2622,23 +2622,24 @@ int stats_dump_resolvers(struct conn_stream *cs,
                          struct list *stat_modules)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct channel *rep = cs_ic(cs);
-       struct resolvers *resolver = appctx->ctx.stats.obj1;
-       struct dns_nameserver *ns = appctx->ctx.stats.obj2;
+       struct resolvers *resolver = ctx->obj1;
+       struct dns_nameserver *ns = ctx->obj2;
 
        if (!resolver)
                resolver = LIST_NEXT(&sec_resolvers, struct resolvers *, list);
 
        /* dump resolvers */
        list_for_each_entry_from(resolver, &sec_resolvers, list) {
-               appctx->ctx.stats.obj1 = resolver;
+               ctx->obj1 = resolver;
 
-               ns = appctx->ctx.stats.obj2 ?
-                    appctx->ctx.stats.obj2 :
+               ns = ctx->obj2 ?
+                    ctx->obj2 :
                     LIST_NEXT(&resolver->nameservers, struct dns_nameserver *, list);
 
                list_for_each_entry_from(ns, &resolver->nameservers, list) {
-                       appctx->ctx.stats.obj2 = ns;
+                       ctx->obj2 = ns;
 
                        if (buffer_almost_full(&rep->buf))
                                goto full;
@@ -2650,7 +2651,7 @@ int stats_dump_resolvers(struct conn_stream *cs,
                        }
                }
 
-               appctx->ctx.stats.obj2 = NULL;
+               ctx->obj2 = NULL;
        }
 
        return 1;
index d918ee68e1ee73e1457a8f0a78f81e286ea725b8..ea43275be1e29501729e3b23f692e61cef88a690 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <haproxy/api.h>
 #include <haproxy/activity.h>
-#include <haproxy/applet-t.h>
+#include <haproxy/applet.h>
 #include <haproxy/backend.h>
 #include <haproxy/base64.h>
 #include <haproxy/cfgparse.h>
@@ -311,6 +311,7 @@ int stats_putchk(struct channel *chn, struct htx *htx, struct buffer *chk)
 
 static const char *stats_scope_ptr(struct appctx *appctx, struct conn_stream *cs)
 {
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct channel *req = cs_oc(cs);
        struct htx *htx = htxbuf(&req->buf);
        struct htx_blk *blk;
@@ -320,7 +321,7 @@ static const char *stats_scope_ptr(struct appctx *appctx, struct conn_stream *cs
        BUG_ON(!blk || htx_get_blk_type(blk) != HTX_BLK_REQ_SL);
        ALREADY_CHECKED(blk);
        uri = htx_sl_req_uri(htx_get_blk_ptr(htx, blk));
-       return uri.ptr + appctx->ctx.stats.scope_str;
+       return uri.ptr + ctx->scope_str;
 }
 
 /*
@@ -1618,19 +1619,20 @@ static int stats_dump_fields_html(struct buffer *out,
 int stats_dump_one_line(const struct field *stats, size_t stats_count,
                         struct appctx *appctx)
 {
+       struct show_stat_ctx *ctx = appctx->svcctx;
        int ret;
 
-       if (appctx->ctx.stats.flags & STAT_FMT_HTML)
-               ret = stats_dump_fields_html(&trash, stats, appctx->ctx.stats.flags);
-       else if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
-               ret = stats_dump_fields_typed(&trash, stats, stats_count, appctx->ctx.stats.flags, appctx->ctx.stats.domain);
-       else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
-               ret = stats_dump_fields_json(&trash, stats, stats_count, appctx->ctx.stats.flags, appctx->ctx.stats.domain);
+       if (ctx->flags & STAT_FMT_HTML)
+               ret = stats_dump_fields_html(&trash, stats, ctx->flags);
+       else if (ctx->flags & STAT_FMT_TYPED)
+               ret = stats_dump_fields_typed(&trash, stats, stats_count, ctx->flags, ctx->domain);
+       else if (ctx->flags & STAT_FMT_JSON)
+               ret = stats_dump_fields_json(&trash, stats, stats_count, ctx->flags, ctx->domain);
        else
-               ret = stats_dump_fields_csv(&trash, stats, stats_count, appctx->ctx.stats.flags, appctx->ctx.stats.domain);
+               ret = stats_dump_fields_csv(&trash, stats, stats_count, ctx->flags, ctx->domain);
 
        if (ret)
-               appctx->ctx.stats.flags |= STAT_STARTED;
+               ctx->flags |= STAT_STARTED;
 
        return ret;
 }
@@ -1813,6 +1815,7 @@ int stats_fill_fe_stats(struct proxy *px, struct field *stats, int len,
 static int stats_dump_fe_stats(struct conn_stream *cs, struct proxy *px)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct field *stats = stat_l[STATS_DOMAIN_PROXY];
        struct stats_module *mod;
        size_t stats_count = ST_F_TOTAL_FIELDS;
@@ -1820,7 +1823,7 @@ static int stats_dump_fe_stats(struct conn_stream *cs, struct proxy *px)
        if (!(px->cap & PR_CAP_FE))
                return 0;
 
-       if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_FE)))
+       if ((ctx->flags & STAT_BOUND) && !(ctx->type & (1 << STATS_TYPE_FE)))
                return 0;
 
        memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
@@ -1980,13 +1983,14 @@ int stats_fill_li_stats(struct proxy *px, struct listener *l, int flags,
 static int stats_dump_li_stats(struct conn_stream *cs, struct proxy *px, struct listener *l)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct field *stats = stat_l[STATS_DOMAIN_PROXY];
        struct stats_module *mod;
        size_t stats_count = ST_F_TOTAL_FIELDS;
 
        memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
 
-       if (!stats_fill_li_stats(px, l, appctx->ctx.stats.flags, stats,
+       if (!stats_fill_li_stats(px, l, ctx->flags, stats,
                                 ST_F_TOTAL_FIELDS, NULL))
                return 0;
 
@@ -2491,13 +2495,14 @@ int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
 static int stats_dump_sv_stats(struct conn_stream *cs, struct proxy *px, struct server *sv)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct stats_module *mod;
        struct field *stats = stat_l[STATS_DOMAIN_PROXY];
        size_t stats_count = ST_F_TOTAL_FIELDS;
 
        memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
 
-       if (!stats_fill_sv_stats(px, sv, appctx->ctx.stats.flags, stats,
+       if (!stats_fill_sv_stats(px, sv, ctx->flags, stats,
                                 ST_F_TOTAL_FIELDS, NULL))
                return 0;
 
@@ -2816,6 +2821,7 @@ int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int le
 static int stats_dump_be_stats(struct conn_stream *cs, struct proxy *px)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct field *stats = stat_l[STATS_DOMAIN_PROXY];
        struct stats_module *mod;
        size_t stats_count = ST_F_TOTAL_FIELDS;
@@ -2823,12 +2829,12 @@ static int stats_dump_be_stats(struct conn_stream *cs, struct proxy *px)
        if (!(px->cap & PR_CAP_BE))
                return 0;
 
-       if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_BE)))
+       if ((ctx->flags & STAT_BOUND) && !(ctx->type & (1 << STATS_TYPE_BE)))
                return 0;
 
        memset(stats, 0, sizeof(struct field) * stat_count[STATS_DOMAIN_PROXY]);
 
-       if (!stats_fill_be_stats(px, appctx->ctx.stats.flags, stats, ST_F_TOTAL_FIELDS, NULL))
+       if (!stats_fill_be_stats(px, ctx->flags, stats, ST_F_TOTAL_FIELDS, NULL))
                return 0;
 
        list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
@@ -2857,21 +2863,22 @@ static int stats_dump_be_stats(struct conn_stream *cs, struct proxy *px)
 static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
        struct stats_module *mod;
        int stats_module_len = 0;
 
-       if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
+       if (px->cap & PR_CAP_BE && px->srv && (ctx->flags & STAT_ADMIN)) {
                /* A form to enable/disable this proxy servers */
 
-               /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+               /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
                scope_txt[0] = 0;
-               if (appctx->ctx.stats.scope_len) {
+               if (ctx->scope_len) {
                        const char *scope_ptr = stats_scope_ptr(appctx, cs);
 
                        strcpy(scope_txt, STAT_SCOPE_PATTERN);
-                       memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, appctx->ctx.stats.scope_len);
-                       scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
+                       memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+                       scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
                }
 
                chunk_appendf(&trash,
@@ -2888,10 +2895,10 @@ static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
                      "<a name=\"%s\"></a>%s"
                      "<a class=px href=\"#%s\">%s</a>",
                      px->id,
-                     (appctx->ctx.stats.flags & STAT_SHLGNDS) ? "<u>":"",
+                     (ctx->flags & STAT_SHLGNDS) ? "<u>":"",
                      px->id, px->id);
 
-       if (appctx->ctx.stats.flags & STAT_SHLGNDS) {
+       if (ctx->flags & STAT_SHLGNDS) {
                /* cap, mode, id */
                chunk_appendf(&trash, "<div class=tips>cap: %s, mode: %s, id: %d",
                              proxy_cap_str(px->cap), proxy_mode_str(px->mode),
@@ -2906,10 +2913,10 @@ static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
                      "</table>\n"
                      "<table class=\"tbl\" width=\"100%%\">\n"
                      "<tr class=\"titre\">",
-                     (appctx->ctx.stats.flags & STAT_SHLGNDS) ? "</u>":"",
+                     (ctx->flags & STAT_SHLGNDS) ? "</u>":"",
                      px->desc ? "desc" : "empty", px->desc ? px->desc : "");
 
-       if (appctx->ctx.stats.flags & STAT_ADMIN) {
+       if (ctx->flags & STAT_ADMIN) {
                /* Column heading for Enable or Disable server */
                if ((px->cap & PR_CAP_BE) && px->srv)
                        chunk_appendf(&trash,
@@ -2930,7 +2937,7 @@ static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
                      "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
                      "<th colspan=9>Server</th>");
 
-       if (appctx->ctx.stats.flags & STAT_SHMODULES) {
+       if (ctx->flags & STAT_SHMODULES) {
                // calculate the count of module for colspan attribute
                list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
                        ++stats_module_len;
@@ -2951,7 +2958,7 @@ static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
                      "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
                      "<th>Thrtle</th>\n");
 
-       if (appctx->ctx.stats.flags & STAT_SHMODULES) {
+       if (ctx->flags & STAT_SHMODULES) {
                list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
                        chunk_appendf(&trash, "<th>%s</th>", mod->name);
                }
@@ -2966,10 +2973,11 @@ static void stats_dump_html_px_hdr(struct conn_stream *cs, struct proxy *px)
 static void stats_dump_html_px_end(struct conn_stream *cs, struct proxy *px)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
 
        chunk_appendf(&trash, "</table>");
 
-       if ((px->cap & PR_CAP_BE) && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
+       if ((px->cap & PR_CAP_BE) && px->srv && (ctx->flags & STAT_ADMIN)) {
                /* close the form used to enable/disable this proxy servers */
                chunk_appendf(&trash,
                              "Choose the action to perform on the checked servers : "
@@ -3009,6 +3017,7 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                               struct proxy *px, struct uri_auth *uri)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct stream *s = __cs_strm(cs);
        struct channel *rep = cs_ic(cs);
        struct server *sv, *svs;        /* server and server-state, server-state=server or server->track */
@@ -3016,7 +3025,7 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
 
        chunk_reset(&trash);
 
-       switch (appctx->ctx.stats.px_st) {
+       switch (ctx->px_st) {
        case STAT_PX_ST_INIT:
                /* we are on a new proxy */
                if (uri && uri->scope) {
@@ -3046,29 +3055,29 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                /* if the user has requested a limited output and the proxy
                 * name does not match, skip it.
                 */
-               if (appctx->ctx.stats.scope_len) {
+               if (ctx->scope_len) {
                        const char *scope_ptr = stats_scope_ptr(appctx, cs);
 
-                       if (strnistr(px->id, strlen(px->id), scope_ptr, appctx->ctx.stats.scope_len) == NULL)
+                       if (strnistr(px->id, strlen(px->id), scope_ptr, ctx->scope_len) == NULL)
                                return 1;
                }
 
-               if ((appctx->ctx.stats.flags & STAT_BOUND) &&
-                   (appctx->ctx.stats.iid != -1) &&
-                   (px->uuid != appctx->ctx.stats.iid))
+               if ((ctx->flags & STAT_BOUND) &&
+                   (ctx->iid != -1) &&
+                   (px->uuid != ctx->iid))
                        return 1;
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_TH;
+               ctx->px_st = STAT_PX_ST_TH;
                /* fall through */
 
        case STAT_PX_ST_TH:
-               if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
+               if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_px_hdr(cs, px);
                        if (!stats_putchk(rep, htx, &trash))
                                goto full;
                }
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_FE;
+               ctx->px_st = STAT_PX_ST_FE;
                /* fall through */
 
        case STAT_PX_ST_FE:
@@ -3078,13 +3087,13 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                                goto full;
                }
 
-               appctx->ctx.stats.obj2 = px->conf.listeners.n;
-               appctx->ctx.stats.px_st = STAT_PX_ST_LI;
+               ctx->obj2 = px->conf.listeners.n;
+               ctx->px_st = STAT_PX_ST_LI;
                /* fall through */
 
        case STAT_PX_ST_LI:
                /* obj2 points to listeners list as initialized above */
-               for (; appctx->ctx.stats.obj2 != &px->conf.listeners; appctx->ctx.stats.obj2 = l->by_fe.n) {
+               for (; ctx->obj2 != &px->conf.listeners; ctx->obj2 = l->by_fe.n) {
                        if (htx) {
                                if (htx_almost_full(htx))
                                        goto full;
@@ -3094,15 +3103,15 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                                        goto full;
                        }
 
-                       l = LIST_ELEM(appctx->ctx.stats.obj2, struct listener *, by_fe);
+                       l = LIST_ELEM(ctx->obj2, struct listener *, by_fe);
                        if (!l->counters)
                                continue;
 
-                       if (appctx->ctx.stats.flags & STAT_BOUND) {
-                               if (!(appctx->ctx.stats.type & (1 << STATS_TYPE_SO)))
+                       if (ctx->flags & STAT_BOUND) {
+                               if (!(ctx->type & (1 << STATS_TYPE_SO)))
                                        break;
 
-                               if (appctx->ctx.stats.sid != -1 && l->luid != appctx->ctx.stats.sid)
+                               if (ctx->sid != -1 && l->luid != ctx->sid)
                                        continue;
                        }
 
@@ -3113,8 +3122,8 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                        }
                }
 
-               appctx->ctx.stats.obj2 = px->srv; /* may be NULL */
-               appctx->ctx.stats.px_st = STAT_PX_ST_SV;
+               ctx->obj2 = px->srv; /* may be NULL */
+               ctx->px_st = STAT_PX_ST_SV;
                /* fall through */
 
        case STAT_PX_ST_SV:
@@ -3124,10 +3133,10 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                 * Temporarily increment its refcount to prevent its
                 * anticipated cleaning. Call free_server to release it.
                 */
-               for (; appctx->ctx.stats.obj2 != NULL;
-                      appctx->ctx.stats.obj2 = srv_drop(sv)) {
+               for (; ctx->obj2 != NULL;
+                      ctx->obj2 = srv_drop(sv)) {
 
-                       sv = appctx->ctx.stats.obj2;
+                       sv = ctx->obj2;
                        srv_take(sv);
 
                        if (htx) {
@@ -3139,18 +3148,18 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                                        goto full;
                        }
 
-                       if (appctx->ctx.stats.flags & STAT_BOUND) {
-                               if (!(appctx->ctx.stats.type & (1 << STATS_TYPE_SV))) {
+                       if (ctx->flags & STAT_BOUND) {
+                               if (!(ctx->type & (1 << STATS_TYPE_SV))) {
                                        srv_drop(sv);
                                        break;
                                }
 
-                               if (appctx->ctx.stats.sid != -1 && sv->puid != appctx->ctx.stats.sid)
+                               if (ctx->sid != -1 && sv->puid != ctx->sid)
                                        continue;
                        }
 
                        /* do not report disabled servers */
-                       if (appctx->ctx.stats.flags & STAT_HIDE_MAINT &&
+                       if (ctx->flags & STAT_HIDE_MAINT &&
                            sv->cur_admin & SRV_ADMF_MAINT) {
                                continue;
                        }
@@ -3160,7 +3169,7 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                                svs = svs->track;
 
                        /* do not report servers which are DOWN and not changing state */
-                       if ((appctx->ctx.stats.flags & STAT_HIDE_DOWN) &&
+                       if ((ctx->flags & STAT_HIDE_DOWN) &&
                            ((sv->cur_admin & SRV_ADMF_MAINT) || /* server is in maintenance */
                             (sv->cur_state == SRV_ST_STOPPED && /* server is down */
                              (!((svs->agent.state | svs->check.state) & CHK_ST_ENABLED) ||
@@ -3175,7 +3184,7 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                        }
                } /* for sv */
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_BE;
+               ctx->px_st = STAT_PX_ST_BE;
                /* fall through */
 
        case STAT_PX_ST_BE:
@@ -3185,17 +3194,17 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
                                goto full;
                }
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_END;
+               ctx->px_st = STAT_PX_ST_END;
                /* fall through */
 
        case STAT_PX_ST_END:
-               if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
+               if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_px_end(cs, px);
                        if (!stats_putchk(rep, htx, &trash))
                                goto full;
                }
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_FIN;
+               ctx->px_st = STAT_PX_ST_FIN;
                /* fall through */
 
        case STAT_PX_ST_FIN:
@@ -3216,6 +3225,8 @@ int stats_dump_proxy_to_buffer(struct conn_stream *cs, struct htx *htx,
  */
 static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
 {
+       struct show_stat_ctx *ctx = appctx->svcctx;
+
        /* WARNING! This must fit in the first buffer !!! */
        chunk_appendf(&trash,
                      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
@@ -3372,8 +3383,8 @@ static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
                      "}\n"
                      "-->\n"
                      "</style></head>\n",
-                     (appctx->ctx.stats.flags & STAT_SHNODE) ? " on " : "",
-                     (appctx->ctx.stats.flags & STAT_SHNODE) ? (uri && uri->node ? uri->node : global.node) : ""
+                     (ctx->flags & STAT_SHNODE) ? " on " : "",
+                     (ctx->flags & STAT_SHNODE) ? (uri && uri->node ? uri->node : global.node) : ""
                      );
 }
 
@@ -3384,6 +3395,7 @@ static void stats_dump_html_head(struct appctx *appctx, struct uri_auth *uri)
 static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        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, cs);
@@ -3440,11 +3452,11 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                      "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
                      "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
                      "",
-                     (appctx->ctx.stats.flags & STAT_HIDEVER) ? "" : (stats_version_string),
-                     pid, (appctx->ctx.stats.flags & STAT_SHNODE) ? " on " : "",
-                     (appctx->ctx.stats.flags & STAT_SHNODE) ? (uri->node ? uri->node : global.node) : "",
-                     (appctx->ctx.stats.flags & STAT_SHDESC) ? ": " : "",
-                     (appctx->ctx.stats.flags & STAT_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
+                     (ctx->flags & STAT_HIDEVER) ? "" : (stats_version_string),
+                     pid, (ctx->flags & STAT_SHNODE) ? " on " : "",
+                     (ctx->flags & STAT_SHNODE) ? (uri->node ? uri->node : global.node) : "",
+                     (ctx->flags & STAT_SHDESC) ? ": " : "",
+                     (ctx->flags & STAT_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
                      pid, 1, 1, global.nbthread,
                      up / 86400, (up % 86400) / 3600,
                      (up % 3600) / 60, (up % 60),
@@ -3458,51 +3470,51 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                      total_run_queues(), total_allocated_tasks(), clock_report_idle()
                      );
 
-       /* scope_txt = search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
-       memcpy(scope_txt, scope_ptr, appctx->ctx.stats.scope_len);
-       scope_txt[appctx->ctx.stats.scope_len] = '\0';
+       /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       memcpy(scope_txt, scope_ptr, ctx->scope_len);
+       scope_txt[ctx->scope_len] = '\0';
 
        chunk_appendf(&trash,
                      "<li><form method=\"GET\">Scope : <input value=\"%s\" name=\"" STAT_SCOPE_INPUT_NAME "\" size=\"8\" maxlength=\"%d\" tabindex=\"1\"/></form>\n",
-                     (appctx->ctx.stats.scope_len > 0) ? scope_txt : "",
+                     (ctx->scope_len > 0) ? scope_txt : "",
                      STAT_SCOPE_TXT_MAXLEN);
 
-       /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
        scope_txt[0] = 0;
-       if (appctx->ctx.stats.scope_len) {
+       if (ctx->scope_len) {
                strcpy(scope_txt, STAT_SCOPE_PATTERN);
-               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, appctx->ctx.stats.scope_len);
-               scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
+               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
        }
 
-       if (appctx->ctx.stats.flags & STAT_HIDE_DOWN)
+       if (ctx->flags & STAT_HIDE_DOWN)
                chunk_appendf(&trash,
                              "<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
                              uri->uri_prefix,
                              "",
-                             (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                              scope_txt);
        else
                chunk_appendf(&trash,
                              "<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
                              uri->uri_prefix,
                              ";up",
-                             (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                              scope_txt);
 
        if (uri->refresh > 0) {
-               if (appctx->ctx.stats.flags & STAT_NO_REFRESH)
+               if (ctx->flags & STAT_NO_REFRESH)
                        chunk_appendf(&trash,
                                      "<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
                                      uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
                                      "",
                                      scope_txt);
                else
                        chunk_appendf(&trash,
                                      "<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
                                      uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
                                      ";norefresh",
                                      scope_txt);
        }
@@ -3510,8 +3522,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
        chunk_appendf(&trash,
                      "<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
                      uri->uri_prefix,
-                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                      scope_txt);
 
        chunk_appendf(&trash,
@@ -3539,16 +3551,16 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                      ""
                      );
 
-       if (appctx->ctx.stats.st_code) {
-               switch (appctx->ctx.stats.st_code) {
+       if (ctx->st_code) {
+               switch (ctx->st_code) {
                case STAT_STATUS_DONE:
                        chunk_appendf(&trash,
                                      "<p><div class=active_up>"
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
                                      "Action processed successfully."
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_NONE:
@@ -3557,8 +3569,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
                                      "Nothing has changed."
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_PART:
@@ -3568,8 +3580,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "Action partially processed.<br>"
                                      "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_ERRP:
@@ -3584,8 +3596,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
                                      "</ul>"
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_EXCD:
@@ -3595,8 +3607,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<b>Action not processed : the buffer couldn't store all the data.<br>"
                                      "You should retry with less servers at a time.</b>"
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_DENY:
@@ -3605,8 +3617,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
                                      "<b>Action denied.</b>"
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                case STAT_STATUS_IVAL:
@@ -3615,8 +3627,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
                                      "<b>Invalid requests (unsupported method or chunked encoded request).</b>"
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                        break;
                default:
@@ -3625,8 +3637,8 @@ static void stats_dump_html_info(struct conn_stream *cs, struct uri_auth *uri)
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
                                      "Unexpected result."
                                      "</div>\n", uri->uri_prefix,
-                                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                                      scope_txt);
                }
                chunk_appendf(&trash, "<p>\n");
@@ -3666,11 +3678,12 @@ static int stats_dump_proxies(struct conn_stream *cs,
                               struct uri_auth *uri)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct channel *rep = cs_ic(cs);
        struct proxy *px;
 
        /* dump proxies */
-       while (appctx->ctx.stats.obj1) {
+       while (ctx->obj1) {
                if (htx) {
                        if (htx_almost_full(htx))
                                goto full;
@@ -3680,7 +3693,7 @@ static int stats_dump_proxies(struct conn_stream *cs,
                                goto full;
                }
 
-               px = appctx->ctx.stats.obj1;
+               px = ctx->obj1;
                /* Skip the global frontend proxies and non-networked ones.
                 * Also skip proxies that were disabled in the configuration
                 * This change allows retrieving stats from "old" proxies after a reload.
@@ -3691,8 +3704,8 @@ static int stats_dump_proxies(struct conn_stream *cs,
                                return 0;
                }
 
-               appctx->ctx.stats.obj1 = px->next;
-               appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
+               ctx->obj1 = px->next;
+               ctx->px_st = STAT_PX_ST_INIT;
        }
 
        return 1;
@@ -3713,8 +3726,9 @@ static int stats_dump_stat_to_buffer(struct conn_stream *cs, struct htx *htx,
                                     struct uri_auth *uri)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct channel *rep = cs_ic(cs);
-       enum stats_domain domain = appctx->ctx.stats.domain;
+       enum stats_domain domain = ctx->domain;
 
        chunk_reset(&trash);
 
@@ -3724,19 +3738,19 @@ static int stats_dump_stat_to_buffer(struct conn_stream *cs, struct htx *htx,
                /* fall through */
 
        case STAT_ST_HEAD:
-               if (appctx->ctx.stats.flags & STAT_FMT_HTML)
+               if (ctx->flags & STAT_FMT_HTML)
                        stats_dump_html_head(appctx, uri);
-               else if (appctx->ctx.stats.flags & STAT_JSON_SCHM)
+               else if (ctx->flags & STAT_JSON_SCHM)
                        stats_dump_json_schema(&trash);
-               else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
+               else if (ctx->flags & STAT_FMT_JSON)
                        stats_dump_json_header();
-               else if (!(appctx->ctx.stats.flags & STAT_FMT_TYPED))
-                       stats_dump_csv_header(appctx->ctx.stats.domain);
+               else if (!(ctx->flags & STAT_FMT_TYPED))
+                       stats_dump_csv_header(ctx->domain);
 
                if (!stats_putchk(rep, htx, &trash))
                        goto full;
 
-               if (appctx->ctx.stats.flags & STAT_JSON_SCHM) {
+               if (ctx->flags & STAT_JSON_SCHM) {
                        appctx->st2 = STAT_ST_FIN;
                        return 1;
                }
@@ -3744,16 +3758,16 @@ static int stats_dump_stat_to_buffer(struct conn_stream *cs, struct htx *htx,
                /* fall through */
 
        case STAT_ST_INFO:
-               if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
+               if (ctx->flags & STAT_FMT_HTML) {
                        stats_dump_html_info(cs, uri);
                        if (!stats_putchk(rep, htx, &trash))
                                goto full;
                }
 
                if (domain == STATS_DOMAIN_PROXY)
-                       appctx->ctx.stats.obj1 = proxies_list;
+                       ctx->obj1 = proxies_list;
 
-               appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
+               ctx->px_st = STAT_PX_ST_INIT;
                appctx->st2 = STAT_ST_LIST;
                /* fall through */
 
@@ -3779,8 +3793,8 @@ static int stats_dump_stat_to_buffer(struct conn_stream *cs, struct htx *htx,
                /* fall through */
 
        case STAT_ST_END:
-               if (appctx->ctx.stats.flags & (STAT_FMT_HTML|STAT_FMT_JSON)) {
-                       if (appctx->ctx.stats.flags & STAT_FMT_HTML)
+               if (ctx->flags & (STAT_FMT_HTML|STAT_FMT_JSON)) {
+                       if (ctx->flags & STAT_FMT_HTML)
                                stats_dump_html_end();
                        else
                                stats_dump_json_end();
@@ -3815,6 +3829,7 @@ static int stats_process_http_post(struct conn_stream *cs)
 {
        struct stream *s = __cs_strm(cs);
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
 
        struct proxy *px = NULL;
        struct server *sv = NULL;
@@ -3839,7 +3854,7 @@ static int stats_process_http_post(struct conn_stream *cs)
        if (s->txn->req.msg_state < HTTP_MSG_DONE) {
                /* check if we can receive more */
                if (htx_free_data_space(htx) <= global.tune.maxrewrite) {
-                       appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
+                       ctx->st_code = STAT_STATUS_EXCD;
                        goto out;
                }
                goto wait;
@@ -3856,7 +3871,7 @@ static int stats_process_http_post(struct conn_stream *cs)
                        struct ist v = htx_get_blk_value(htx, blk);
 
                        if (!chunk_memcat(temp, v.ptr, v.len)) {
-                               appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
+                               ctx->st_code = STAT_STATUS_EXCD;
                                goto out;
                        }
                }
@@ -3868,7 +3883,7 @@ static int stats_process_http_post(struct conn_stream *cs)
        cur_param = next_param = end_params;
        *end_params = '\0';
 
-       appctx->ctx.stats.st_code = STAT_STATUS_NONE;
+       ctx->st_code = STAT_STATUS_NONE;
 
        /*
         * Parse the parameters in reverse order to only store the last value.
@@ -3889,7 +3904,7 @@ static int stats_process_http_post(struct conn_stream *cs)
                                strncpy(key, cur_param + poffset, plen);
                                key[plen - 1] = '\0';
                        } else {
-                               appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
+                               ctx->st_code = STAT_STATUS_ERRP;
                                goto out;
                        }
 
@@ -3909,7 +3924,7 @@ static int stats_process_http_post(struct conn_stream *cs)
                        if (!px && (strcmp(key, "b") == 0)) {
                                if ((px = proxy_be_by_name(value)) == NULL) {
                                        /* the backend name is unknown or ambiguous (duplicate names) */
-                                       appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
+                                       ctx->st_code = STAT_STATUS_ERRP;
                                        goto out;
                                }
                        }
@@ -3967,7 +3982,7 @@ static int stats_process_http_post(struct conn_stream *cs)
                                        action = ST_ADM_ACTION_START;
                                }
                                else {
-                                       appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
+                                       ctx->st_code = STAT_STATUS_ERRP;
                                        goto out;
                                }
                        }
@@ -4127,21 +4142,21 @@ static int stats_process_http_post(struct conn_stream *cs)
        }
 
        if (total_servers == 0) {
-               appctx->ctx.stats.st_code = STAT_STATUS_NONE;
+               ctx->st_code = STAT_STATUS_NONE;
        }
        else if (altered_servers == 0) {
-               appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
+               ctx->st_code = STAT_STATUS_ERRP;
        }
        else if (altered_servers == total_servers) {
-               appctx->ctx.stats.st_code = STAT_STATUS_DONE;
+               ctx->st_code = STAT_STATUS_DONE;
        }
        else {
-               appctx->ctx.stats.st_code = STAT_STATUS_PART;
+               ctx->st_code = STAT_STATUS_PART;
        }
  out:
        return 1;
  wait:
-       appctx->ctx.stats.st_code = STAT_STATUS_NONE;
+       ctx->st_code = STAT_STATUS_NONE;
        return 0;
 }
 
@@ -4151,6 +4166,7 @@ static int stats_send_http_headers(struct conn_stream *cs, struct htx *htx)
        struct stream *s = __cs_strm(cs);
        struct uri_auth *uri = s->be->uri_auth;
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct htx_sl *sl;
        unsigned int flags;
 
@@ -4162,11 +4178,11 @@ static int stats_send_http_headers(struct conn_stream *cs, struct htx *htx)
 
        if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")))
                goto full;
-       if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
+       if (ctx->flags & STAT_FMT_HTML) {
                if (!htx_add_header(htx, ist("Content-Type"), ist("text/html")))
                        goto full;
        }
-       else if (appctx->ctx.stats.flags & (STAT_FMT_JSON|STAT_JSON_SCHM)) {
+       else if (ctx->flags & (STAT_FMT_JSON|STAT_JSON_SCHM)) {
                if (!htx_add_header(htx, ist("Content-Type"), ist("application/json")))
                        goto full;
        }
@@ -4175,13 +4191,13 @@ static int stats_send_http_headers(struct conn_stream *cs, struct htx *htx)
                        goto full;
        }
 
-       if (uri->refresh > 0 && !(appctx->ctx.stats.flags & STAT_NO_REFRESH)) {
+       if (uri->refresh > 0 && !(ctx->flags & STAT_NO_REFRESH)) {
                const char *refresh = U2A(uri->refresh);
                if (!htx_add_header(htx, ist("Refresh"), ist(refresh)))
                        goto full;
        }
 
-       if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+       if (ctx->flags & STAT_CHUNKED) {
                if (!htx_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
                        goto full;
        }
@@ -4205,17 +4221,18 @@ static int stats_send_http_redirect(struct conn_stream *cs, struct htx *htx)
        struct stream *s = __cs_strm(cs);
        struct uri_auth *uri = s->be->uri_auth;
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct htx_sl *sl;
        unsigned int flags;
 
-       /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
        scope_txt[0] = 0;
-       if (appctx->ctx.stats.scope_len) {
+       if (ctx->scope_len) {
                const char *scope_ptr = stats_scope_ptr(appctx, cs);
 
                strcpy(scope_txt, STAT_SCOPE_PATTERN);
-               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, appctx->ctx.stats.scope_len);
-               scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
+               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
        }
 
        /* We don't want to land on the posted stats page because a refresh will
@@ -4224,13 +4241,13 @@ static int stats_send_http_redirect(struct conn_stream *cs, struct htx *htx)
         */
        chunk_printf(&trash, "%s;st=%s%s%s%s",
                     uri->uri_prefix,
-                    ((appctx->ctx.stats.st_code > STAT_STATUS_INIT) &&
-                     (appctx->ctx.stats.st_code < STAT_STATUS_SIZE) &&
-                     stat_status_codes[appctx->ctx.stats.st_code]) ?
-                    stat_status_codes[appctx->ctx.stats.st_code] :
+                    ((ctx->st_code > STAT_STATUS_INIT) &&
+                     (ctx->st_code < STAT_STATUS_SIZE) &&
+                     stat_status_codes[ctx->st_code]) ?
+                    stat_status_codes[ctx->st_code] :
                     stat_status_codes[STAT_STATUS_UNKN],
-                    (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
-                    (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                    (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                    (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
                     scope_txt);
 
        flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
@@ -4265,6 +4282,7 @@ full:
  */
 static void http_stats_io_handler(struct appctx *appctx)
 {
+       struct show_stat_ctx *ctx = appctx->svcctx;
        struct conn_stream *cs = appctx->owner;
        struct stream *s = __cs_strm(cs);
        struct channel *req = cs_oc(cs);
@@ -4272,7 +4290,7 @@ static void http_stats_io_handler(struct appctx *appctx)
        struct htx *req_htx, *res_htx;
 
        /* only proxy stats are available via http */
-       appctx->ctx.stats.domain = STATS_DOMAIN_PROXY;
+       ctx->domain = STATS_DOMAIN_PROXY;
 
        res_htx = htx_from_buf(&res->buf);
 
@@ -4533,18 +4551,19 @@ int stats_fill_info(struct field *info, int len, uint flags)
 static int stats_dump_info_to_buffer(struct conn_stream *cs)
 {
        struct appctx *appctx = __cs_appctx(cs);
+       struct show_stat_ctx *ctx = appctx->svcctx;
 
-       if (!stats_fill_info(info, INF_TOTAL_FIELDS, appctx->ctx.stats.flags))
+       if (!stats_fill_info(info, INF_TOTAL_FIELDS, ctx->flags))
                return 0;
 
        chunk_reset(&trash);
 
-       if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
-               stats_dump_typed_info_fields(&trash, info, appctx->ctx.stats.flags);
-       else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
-               stats_dump_json_info_fields(&trash, info, appctx->ctx.stats.flags);
+       if (ctx->flags & STAT_FMT_TYPED)
+               stats_dump_typed_info_fields(&trash, info, ctx->flags);
+       else if (ctx->flags & STAT_FMT_JSON)
+               stats_dump_json_info_fields(&trash, info, ctx->flags);
        else
-               stats_dump_info_fields(&trash, info, appctx->ctx.stats.flags);
+               stats_dump_info_fields(&trash, info, ctx->flags);
 
        if (ci_putchk(cs_ic(cs), &trash) == -1) {
                cs_rx_room_blk(cs);
@@ -4898,21 +4917,22 @@ static int cli_parse_clear_counters(char **args, char *payload, struct appctx *a
 
 static int cli_parse_show_info(char **args, char *payload, struct appctx *appctx, void *private)
 {
+       struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
        int arg = 2;
 
-       appctx->ctx.stats.scope_str = 0;
-       appctx->ctx.stats.scope_len = 0;
-       appctx->ctx.stats.flags = 0;
+       ctx->scope_str = 0;
+       ctx->scope_len = 0;
+       ctx->flags = 0;
 
        while (*args[arg]) {
                if (strcmp(args[arg], "typed") == 0)
-                       appctx->ctx.stats.flags = (appctx->ctx.stats.flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
+                       ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
                else if (strcmp(args[arg], "json") == 0)
-                       appctx->ctx.stats.flags = (appctx->ctx.stats.flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
+                       ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
                else if (strcmp(args[arg], "desc") == 0)
-                       appctx->ctx.stats.flags |= STAT_SHOW_FDESC;
+                       ctx->flags |= STAT_SHOW_FDESC;
                else if (strcmp(args[arg], "float") == 0)
-                       appctx->ctx.stats.flags |= STAT_USE_FLOAT;
+                       ctx->flags |= STAT_USE_FLOAT;
                arg++;
        }
        return 0;
@@ -4921,60 +4941,61 @@ static int cli_parse_show_info(char **args, char *payload, struct appctx *appctx
 
 static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx, void *private)
 {
+       struct show_stat_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
        int arg = 2;
 
-       appctx->ctx.stats.scope_str = 0;
-       appctx->ctx.stats.scope_len = 0;
-       appctx->ctx.stats.flags = STAT_SHNODE | STAT_SHDESC;
+       ctx->scope_str = 0;
+       ctx->scope_len = 0;
+       ctx->flags = STAT_SHNODE | STAT_SHDESC;
 
        if ((strm_li(__cs_strm(appctx->owner))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
-               appctx->ctx.stats.flags |= STAT_SHLGNDS;
+               ctx->flags |= STAT_SHLGNDS;
 
        /* proxy is the default domain */
-       appctx->ctx.stats.domain = STATS_DOMAIN_PROXY;
+       ctx->domain = STATS_DOMAIN_PROXY;
        if (strcmp(args[arg], "domain") == 0) {
                ++args;
 
                if (strcmp(args[arg], "proxy") == 0) {
                        ++args;
                } else if (strcmp(args[arg], "resolvers") == 0) {
-                       appctx->ctx.stats.domain = STATS_DOMAIN_RESOLVERS;
+                       ctx->domain = STATS_DOMAIN_RESOLVERS;
                        ++args;
                } else {
                        return cli_err(appctx, "Invalid statistics domain.\n");
                }
        }
 
-       if (appctx->ctx.stats.domain == STATS_DOMAIN_PROXY
+       if (ctx->domain == STATS_DOMAIN_PROXY
            && *args[arg] && *args[arg+1] && *args[arg+2]) {
                struct proxy *px;
 
                px = proxy_find_by_name(args[arg], 0, 0);
                if (px)
-                       appctx->ctx.stats.iid = px->uuid;
+                       ctx->iid = px->uuid;
                else
-                       appctx->ctx.stats.iid = atoi(args[arg]);
+                       ctx->iid = atoi(args[arg]);
 
-               if (!appctx->ctx.stats.iid)
+               if (!ctx->iid)
                        return cli_err(appctx, "No such proxy.\n");
 
-               appctx->ctx.stats.flags |= STAT_BOUND;
-               appctx->ctx.stats.type = atoi(args[arg+1]);
-               appctx->ctx.stats.sid = atoi(args[arg+2]);
+               ctx->flags |= STAT_BOUND;
+               ctx->type = atoi(args[arg+1]);
+               ctx->sid = atoi(args[arg+2]);
                arg += 3;
        }
 
        while (*args[arg]) {
                if (strcmp(args[arg], "typed") == 0)
-                       appctx->ctx.stats.flags = (appctx->ctx.stats.flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
+                       ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_TYPED;
                else if (strcmp(args[arg], "json") == 0)
-                       appctx->ctx.stats.flags = (appctx->ctx.stats.flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
+                       ctx->flags = (ctx->flags & ~STAT_FMT_MASK) | STAT_FMT_JSON;
                else if (strcmp(args[arg], "desc") == 0)
-                       appctx->ctx.stats.flags |= STAT_SHOW_FDESC;
+                       ctx->flags |= STAT_SHOW_FDESC;
                else if (strcmp(args[arg], "no-maint") == 0)
-                       appctx->ctx.stats.flags |= STAT_HIDE_MAINT;
+                       ctx->flags |= STAT_HIDE_MAINT;
                else if (strcmp(args[arg], "up") == 0)
-                       appctx->ctx.stats.flags |= STAT_HIDE_DOWN;
+                       ctx->flags |= STAT_HIDE_DOWN;
                arg++;
        }