From: William Lallemand Date: Thu, 30 Jan 2025 16:15:36 +0000 (+0100) Subject: MINOR: httpclient/cli: add --htx option X-Git-Tag: v3.3-dev3~51 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0f1c206b8fe2ba19f71337f7c48cc937233266c8;p=thirdparty%2Fhaproxy.git MINOR: httpclient/cli: add --htx option Use the new HTTPCLIENT_O_RES_HTX flag when using the CLI httpclient with --htx. It allows to process directly the response in HTX, then the htx_dump() function is used to display a debug output. Example: echo "httpclient --htx GET https://haproxy.org" | socat /tmp/haproxy.sock htx=0x79fd72a2e200(size=16336,data=139,used=6,wrap=NO,flags=0x00000010,extra=0,first=0,head=0,tail=5,tail_addr=139,head_addr=0,end_addr=0) [0] type=HTX_BLK_RES_SL - size=31 - addr=0 HTTP/2.0 301 [1] type=HTX_BLK_HDR - size=15 - addr=31 content-length: 0 [2] type=HTX_BLK_HDR - size=32 - addr=46 location: https://www.haproxy.org/ [3] type=HTX_BLK_HDR - size=25 - addr=78 alt-svc: h3=":443"; ma=3600 [4] type=HTX_BLK_HDR - size=35 - addr=103 set-cookie: served=2:TLSv1.3+TCP:IPv4 [5] type=HTX_BLK_EOH - size=1 - addr=138 --- diff --git a/doc/management.txt b/doc/management.txt index 133b2ea0f..9b6586132 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -2309,7 +2309,7 @@ help [] the requested one. The same help screen is also displayed for unknown commands. -httpclient +httpclient [--htx] Launch an HTTP client request and print the response on the CLI. Only supported on a CLI connection running in expert mode (see "expert-mode on"). It's only meant for debugging. The httpclient is able to resolve a server @@ -2318,6 +2318,9 @@ httpclient able to resolve an host from /etc/hosts if you don't use a local dns daemon which can resolve those. + The --htx option allow to use the haproxy internal htx representation using + the htx_dump() function, mainly used for debugging. + new ssl ca-file Create a new empty CA file tree entry to be filled with a set of CA certificates and added to a crt-list. This command should be used in diff --git a/src/httpclient_cli.c b/src/httpclient_cli.c index d81b728ce..1b41c9214 100644 --- a/src/httpclient_cli.c +++ b/src/httpclient_cli.c @@ -26,6 +26,7 @@ struct hcli_svc_ctx { struct httpclient *hc; /* the httpclient instance */ uint flags; /* flags from HC_CLI_F_* above */ + uint is_htx:1; /* is the response an htx buffer */ }; /* These are the callback used by the HTTP Client when it needs to notify new @@ -85,7 +86,7 @@ void hc_cli_res_end_cb(struct httpclient *hc) /* * Parse an httpclient keyword on the cli: - * httpclient + * httpclient [--htx] */ static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private) { @@ -96,17 +97,33 @@ static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void char *meth_str; struct ist uri; struct ist body = IST_NULL; + int cur_arg = 1; if (!cli_has_level(appctx, ACCESS_LVL_ADMIN)) return 1; - if (!*args[1] || !*args[2]) { + /* look at optional keywords */ + while (*args[cur_arg] == '-') { + if (strcmp(args[cur_arg], "--htx") == 0) { + ctx->is_htx = 1; + } + else if (strcmp(args[cur_arg], "--") == 0) { + cur_arg++; + break; + } else { + memprintf(&err, ": Unknown '%s' optional keyword", args[cur_arg]); + goto err; + } + cur_arg++; + } + + if (!*args[cur_arg] || !*args[cur_arg+1]) { memprintf(&err, ": not enough parameters"); goto err; } - meth_str = args[1]; - uri = ist(args[2]); + meth_str = args[cur_arg]; + uri = ist(args[cur_arg+1]); if (payload) body = ist(payload); @@ -127,6 +144,10 @@ static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void ctx->hc = hc; /* store the httpclient ptr in the applet */ ctx->flags = 0; + /* enable the HTX mode for reception */ + if (ctx->is_htx) + hc->options |= HTTPCLIENT_O_RES_HTX; + if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE) goto err; @@ -152,10 +173,24 @@ static int hc_cli_io_handler(struct appctx *appctx) struct hcli_svc_ctx *ctx = appctx->svcctx; struct httpclient *hc = ctx->hc; struct http_hdr *hdrs, *hdr; + struct htx *hc_htx = NULL; + + if (ctx->is_htx && ctx->flags & (HC_F_RES_STLINE|HC_F_RES_HDR|HC_F_RES_BODY)) { + hc_htx = htxbuf(&hc->res.buf); + + if (!hc_htx) + goto error; + + if (htx_is_empty(hc_htx)) + goto error; + } if (ctx->flags & HC_F_RES_STLINE) { - chunk_printf(&trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn), - hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason)); + chunk_reset(&trash); + if (!ctx->is_htx) { + chunk_printf(&trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn), + hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason)); + } if (applet_putchk(appctx, &trash) == -1) goto more; ctx->flags &= ~HC_F_RES_STLINE; @@ -163,25 +198,42 @@ static int hc_cli_io_handler(struct appctx *appctx) if (ctx->flags & HC_F_RES_HDR) { chunk_reset(&trash); - hdrs = hc->res.hdrs; - for (hdr = hdrs; isttest(hdr->v); hdr++) { - if (!h1_format_htx_hdr(hdr->n, hdr->v, &trash)) + if (!ctx->is_htx) { + hdrs = hc->res.hdrs; + for (hdr = hdrs; isttest(hdr->v); hdr++) { + if (!h1_format_htx_hdr(hdr->n, hdr->v, &trash)) + goto too_many_hdrs; + } + if (!chunk_memcat(&trash, "\r\n", 2)) goto too_many_hdrs; } - if (!chunk_memcat(&trash, "\r\n", 2)) - goto too_many_hdrs; if (applet_putchk(appctx, &trash) == -1) goto more; ctx->flags &= ~HC_F_RES_HDR; } if (ctx->flags & HC_F_RES_BODY) { - httpclient_res_xfer(hc, &appctx->outbuf); + if (!ctx->is_htx) { + httpclient_res_xfer(hc, &appctx->outbuf); + /* remove the flag if the buffer was emptied */ + if (httpclient_data(hc)) + goto more; + } + ctx->flags &= ~HC_F_RES_BODY; + } + + if (ctx->is_htx && hc_htx) { + struct htx_blk *blk = NULL; - /* remove the flag if the buffer was emptied */ - if (httpclient_data(hc)) + chunk_reset(&trash); + htx_dump(&trash, hc_htx, 1); + if (applet_putchk(appctx, &trash) == -1) goto more; - ctx->flags &= ~HC_F_RES_BODY; + blk = htx_get_head_blk(hc_htx); + while (blk) + blk = htx_remove_blk(hc_htx, blk); + htx_to_buf(hc_htx, &hc->res.buf); + } /* we must close only if F_END is the last flag */ @@ -199,6 +251,8 @@ end: too_many_hdrs: return cli_err(appctx, "Too many headers.\n"); +error: + return cli_err(appctx, "Unknown error.\n"); } static void hc_cli_release(struct appctx *appctx)