src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
- src/version.o
+ src/httpclient_cli.o src/version.o
ifneq ($(TRACE),)
OBJS += src/calltrace.o
#define HTTPCLIENT_USERAGENT "HAProxy"
-/* What kind of data we need to read */
-/* flags meant for the httpclient CLI API */
-#define HC_F_RES_STLINE 0x01
-#define HC_F_RES_HDR 0x02
-#define HC_F_RES_BODY 0x04
-#define HC_F_RES_END 0x08
-
-
#endif /* ! _HAPROXY_HTTCLIENT__T_H */
static int httpclient_retries = CONN_RETRIES;
static int httpclient_timeout_connect = MS_TO_TICKS(5000);
-/* --- This part of the file implement an HTTP client over the CLI ---
- * The functions will be starting by "hc_cli" for "httpclient cli"
- */
-
-/* the CLI context for the httpclient command */
-struct hcli_svc_ctx {
- struct httpclient *hc; /* the httpclient instance */
- uint flags; /* flags from HC_CLI_F_* above */
-};
-
-/* These are the callback used by the HTTP Client when it needs to notify new
- * data, we only sets a flag in the IO handler via the svcctx.
- */
-void hc_cli_res_stline_cb(struct httpclient *hc)
-{
- struct appctx *appctx = hc->caller;
- struct hcli_svc_ctx *ctx;
-
- if (!appctx)
- return;
-
- ctx = appctx->svcctx;
- ctx->flags |= HC_F_RES_STLINE;
- appctx_wakeup(appctx);
-}
-
-void hc_cli_res_headers_cb(struct httpclient *hc)
-{
- struct appctx *appctx = hc->caller;
- struct hcli_svc_ctx *ctx;
-
- if (!appctx)
- return;
-
- ctx = appctx->svcctx;
- ctx->flags |= HC_F_RES_HDR;
- appctx_wakeup(appctx);
-}
-
-void hc_cli_res_body_cb(struct httpclient *hc)
-{
- struct appctx *appctx = hc->caller;
- struct hcli_svc_ctx *ctx;
-
- if (!appctx)
- return;
-
- ctx = appctx->svcctx;
- ctx->flags |= HC_F_RES_BODY;
- appctx_wakeup(appctx);
-}
-
-void hc_cli_res_end_cb(struct httpclient *hc)
-{
- struct appctx *appctx = hc->caller;
- struct hcli_svc_ctx *ctx;
-
- if (!appctx)
- return;
-
- ctx = appctx->svcctx;
- ctx->flags |= HC_F_RES_END;
- appctx_wakeup(appctx);
-}
-
-/*
- * Parse an httpclient keyword on the cli:
- * httpclient <ID> <method> <URI>
- */
-static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
-{
- struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
- struct httpclient *hc;
- char *err = NULL;
- enum http_meth_t meth;
- char *meth_str;
- struct ist uri;
- struct ist body = IST_NULL;
-
- if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
- return 1;
-
- if (!*args[1] || !*args[2]) {
- memprintf(&err, ": not enough parameters");
- goto err;
- }
-
- meth_str = args[1];
- uri = ist(args[2]);
-
- if (payload)
- body = ist(payload);
-
- meth = find_http_meth(meth_str, strlen(meth_str));
-
- hc = httpclient_new(appctx, meth, uri);
- if (!hc) {
- goto err;
- }
-
- /* update the httpclient callbacks */
- hc->ops.res_stline = hc_cli_res_stline_cb;
- hc->ops.res_headers = hc_cli_res_headers_cb;
- hc->ops.res_payload = hc_cli_res_body_cb;
- hc->ops.res_end = hc_cli_res_end_cb;
-
- ctx->hc = hc; /* store the httpclient ptr in the applet */
- ctx->flags = 0;
-
- if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
- goto err;
-
-
- if (!httpclient_start(hc))
- goto err;
-
- return 0;
-
-err:
- memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
- return cli_err(appctx, err);
-}
-
-/* This function dumps the content of the httpclient receive buffer
- * on the CLI output
- *
- * Return 1 when the processing is finished
- * return 0 if it needs to be called again
- */
-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;
-
- 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));
- if (applet_putchk(appctx, &trash) == -1)
- goto more;
- ctx->flags &= ~HC_F_RES_STLINE;
- }
-
- 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))
- 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;
- ctx->flags &= ~HC_F_RES_BODY;
- }
-
- /* we must close only if F_END is the last flag */
- if (ctx->flags == HC_F_RES_END) {
- ctx->flags &= ~HC_F_RES_END;
- goto end;
- }
-
-more:
- if (!ctx->flags)
- applet_have_no_more_data(appctx);
- return 0;
-end:
- return 1;
-
-too_many_hdrs:
- return cli_err(appctx, "Too many headers.\n");
-}
-
-static void hc_cli_release(struct appctx *appctx)
-{
- struct hcli_svc_ctx *ctx = appctx->svcctx;
- struct httpclient *hc = ctx->hc;
-
- /* Everything possible was printed on the CLI, we can destroy the client */
- httpclient_stop_and_destroy(hc);
-
- return;
-}
-
-/* register cli keywords */
-static struct cli_kw_list cli_kws = {{ },{
- { { "httpclient", NULL }, "httpclient <method> <URI> : launch an HTTP request", hc_cli_parse, hc_cli_io_handler, hc_cli_release, NULL, ACCESS_EXPERT},
- { { NULL }, NULL, NULL, NULL }
-}};
-
-INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
-
-
-/* --- This part of the file implements the actual HTTP client API --- */
-
/*
* Generate a simple request and fill the httpclient request buffer with it.
* The request contains a request line generated from the absolute <url> and
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* "httpclient" CLI command */
+
+
+#include <haproxy/cli.h>
+#include <haproxy/cfgparse.h>
+#include <haproxy/global.h>
+#include <haproxy/istbuf.h>
+#include <haproxy/h1_htx.h>
+#include <haproxy/http_client.h>
+#include <haproxy/tools.h>
+
+#include <string.h>
+
+/* --- This part of the file implement an HTTP client over the CLI ---
+ * The functions will be starting by "hc_cli" for "httpclient cli"
+ */
+
+#define HC_F_RES_STLINE 0x01
+#define HC_F_RES_HDR 0x02
+#define HC_F_RES_BODY 0x04
+#define HC_F_RES_END 0x08
+
+/* the CLI context for the httpclient command */
+struct hcli_svc_ctx {
+ struct httpclient *hc; /* the httpclient instance */
+ uint flags; /* flags from HC_CLI_F_* above */
+};
+
+/* These are the callback used by the HTTP Client when it needs to notify new
+ * data, we only sets a flag in the IO handler via the svcctx.
+ */
+void hc_cli_res_stline_cb(struct httpclient *hc)
+{
+ struct appctx *appctx = hc->caller;
+ struct hcli_svc_ctx *ctx;
+
+ if (!appctx)
+ return;
+
+ ctx = appctx->svcctx;
+ ctx->flags |= HC_F_RES_STLINE;
+ appctx_wakeup(appctx);
+}
+
+void hc_cli_res_headers_cb(struct httpclient *hc)
+{
+ struct appctx *appctx = hc->caller;
+ struct hcli_svc_ctx *ctx;
+
+ if (!appctx)
+ return;
+
+ ctx = appctx->svcctx;
+ ctx->flags |= HC_F_RES_HDR;
+ appctx_wakeup(appctx);
+}
+
+void hc_cli_res_body_cb(struct httpclient *hc)
+{
+ struct appctx *appctx = hc->caller;
+ struct hcli_svc_ctx *ctx;
+
+ if (!appctx)
+ return;
+
+ ctx = appctx->svcctx;
+ ctx->flags |= HC_F_RES_BODY;
+ appctx_wakeup(appctx);
+}
+
+void hc_cli_res_end_cb(struct httpclient *hc)
+{
+ struct appctx *appctx = hc->caller;
+ struct hcli_svc_ctx *ctx;
+
+ if (!appctx)
+ return;
+
+ ctx = appctx->svcctx;
+ ctx->flags |= HC_F_RES_END;
+ appctx_wakeup(appctx);
+}
+
+/*
+ * Parse an httpclient keyword on the cli:
+ * httpclient <ID> <method> <URI>
+ */
+static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
+ struct httpclient *hc;
+ char *err = NULL;
+ enum http_meth_t meth;
+ char *meth_str;
+ struct ist uri;
+ struct ist body = IST_NULL;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ if (!*args[1] || !*args[2]) {
+ memprintf(&err, ": not enough parameters");
+ goto err;
+ }
+
+ meth_str = args[1];
+ uri = ist(args[2]);
+
+ if (payload)
+ body = ist(payload);
+
+ meth = find_http_meth(meth_str, strlen(meth_str));
+
+ hc = httpclient_new(appctx, meth, uri);
+ if (!hc) {
+ goto err;
+ }
+
+ /* update the httpclient callbacks */
+ hc->ops.res_stline = hc_cli_res_stline_cb;
+ hc->ops.res_headers = hc_cli_res_headers_cb;
+ hc->ops.res_payload = hc_cli_res_body_cb;
+ hc->ops.res_end = hc_cli_res_end_cb;
+
+ ctx->hc = hc; /* store the httpclient ptr in the applet */
+ ctx->flags = 0;
+
+ if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
+ goto err;
+
+
+ if (!httpclient_start(hc))
+ goto err;
+
+ return 0;
+
+err:
+ memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
+ return cli_err(appctx, err);
+}
+
+/* This function dumps the content of the httpclient receive buffer
+ * on the CLI output
+ *
+ * Return 1 when the processing is finished
+ * return 0 if it needs to be called again
+ */
+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;
+
+ 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));
+ if (applet_putchk(appctx, &trash) == -1)
+ goto more;
+ ctx->flags &= ~HC_F_RES_STLINE;
+ }
+
+ 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))
+ 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;
+ ctx->flags &= ~HC_F_RES_BODY;
+ }
+
+ /* we must close only if F_END is the last flag */
+ if (ctx->flags == HC_F_RES_END) {
+ ctx->flags &= ~HC_F_RES_END;
+ goto end;
+ }
+
+more:
+ if (!ctx->flags)
+ applet_have_no_more_data(appctx);
+ return 0;
+end:
+ return 1;
+
+too_many_hdrs:
+ return cli_err(appctx, "Too many headers.\n");
+}
+
+static void hc_cli_release(struct appctx *appctx)
+{
+ struct hcli_svc_ctx *ctx = appctx->svcctx;
+ struct httpclient *hc = ctx->hc;
+
+ /* Everything possible was printed on the CLI, we can destroy the client */
+ httpclient_stop_and_destroy(hc);
+
+ return;
+}
+
+/* register cli keywords */
+static struct cli_kw_list cli_kws = {{ },{
+ { { "httpclient", NULL }, "httpclient <method> <URI> : launch an HTTP request", hc_cli_parse, hc_cli_io_handler, hc_cli_release, NULL, ACCESS_EXPERT},
+ { { NULL }, NULL, NULL, NULL }
+}};
+
+INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
+