mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 07:07:04 +02:00
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 <empty>
This commit is contained in:
parent
3e05e20029
commit
0f1c206b8f
@ -2309,7 +2309,7 @@ help [<command>]
|
||||
the requested one. The same help screen is also displayed for unknown
|
||||
commands.
|
||||
|
||||
httpclient <method> <URI>
|
||||
httpclient [--htx] <method> <URI>
|
||||
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 <method> <URI>
|
||||
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 <cafile>
|
||||
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
|
||||
|
@ -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 <ID> <method> <URI>
|
||||
* httpclient [--htx] <method> <URI>
|
||||
*/
|
||||
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,27 +198,44 @@ 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);
|
||||
|
||||
/* remove the flag if the buffer was emptied */
|
||||
if (httpclient_data(hc))
|
||||
goto more;
|
||||
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;
|
||||
|
||||
chunk_reset(&trash);
|
||||
htx_dump(&trash, hc_htx, 1);
|
||||
if (applet_putchk(appctx, &trash) == -1)
|
||||
goto more;
|
||||
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 */
|
||||
if (ctx->flags == HC_F_RES_END) {
|
||||
ctx->flags &= ~HC_F_RES_END;
|
||||
@ -199,6 +251,8 @@ static int hc_cli_io_handler(struct appctx *appctx)
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user