blob: 9cbecf068683e600a60009aca935b69a7411d193 [file] [log] [blame]
William Lallemand83614a92021-08-13 14:47:57 +02001/*
2 * HTTP Client
3 *
4 * Copyright (C) 2021 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * This file implements an HTTP Client API.
12 *
13 */
William Lallemand83614a92021-08-13 14:47:57 +020014
William Lallemand2a8fe8b2021-08-20 14:25:15 +020015#include <haproxy/api.h>
William Lallemand33b0d092021-08-13 16:05:53 +020016#include <haproxy/applet.h>
17#include <haproxy/cli.h>
18#include <haproxy/dynbuf.h>
William Lallemand83614a92021-08-13 14:47:57 +020019#include <haproxy/cfgparse.h>
20#include <haproxy/connection.h>
21#include <haproxy/global.h>
William Lallemand0da616e2021-10-28 15:34:26 +020022#include <haproxy/istbuf.h>
William Lallemand33b0d092021-08-13 16:05:53 +020023#include <haproxy/h1_htx.h>
24#include <haproxy/http.h>
25#include <haproxy/http_client.h>
26#include <haproxy/http_htx.h>
27#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020028#include <haproxy/log.h>
29#include <haproxy/proxy.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020030#include <haproxy/server.h>
Willy Tarreau1df20422021-10-06 11:28:24 +020031#include <haproxy/ssl_sock-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020032#include <haproxy/stream_interface.h>
William Lallemand83614a92021-08-13 14:47:57 +020033#include <haproxy/tools.h>
34
35#include <string.h>
36
37
38static struct proxy *httpclient_proxy;
39static struct server *httpclient_srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +020040#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +020041static struct server *httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +020042#endif
William Lallemand33b0d092021-08-13 16:05:53 +020043static struct applet httpclient_applet;
44
William Lallemand03a4eb12021-08-18 16:46:21 +020045/* --- This part of the file implement an HTTP client over the CLI ---
46 * The functions will be starting by "hc_cli" for "httpclient cli"
47 */
48
William Lallemand03a4eb12021-08-18 16:46:21 +020049/* What kind of data we need to read */
50#define HC_CLI_F_RES_STLINE 0x01
51#define HC_CLI_F_RES_HDR 0x02
52#define HC_CLI_F_RES_BODY 0x04
53#define HC_CLI_F_RES_END 0x08
54
55
56/* These are the callback used by the HTTP Client when it needs to notify new
57 * data, we only sets a flag in the IO handler */
58
59void hc_cli_res_stline_cb(struct httpclient *hc)
60{
61 struct appctx *appctx = hc->caller;
62
William Lallemanddfc3f892021-08-20 11:35:29 +020063 if (!appctx)
64 return;
65
William Lallemand03a4eb12021-08-18 16:46:21 +020066 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020067 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020068}
69
70void hc_cli_res_headers_cb(struct httpclient *hc)
71{
72 struct appctx *appctx = hc->caller;
73
William Lallemanddfc3f892021-08-20 11:35:29 +020074 if (!appctx)
75 return;
76
William Lallemand03a4eb12021-08-18 16:46:21 +020077 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020078 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020079}
80
81void hc_cli_res_body_cb(struct httpclient *hc)
82{
83 struct appctx *appctx = hc->caller;
84
William Lallemanddfc3f892021-08-20 11:35:29 +020085 if (!appctx)
86 return;
87
William Lallemand03a4eb12021-08-18 16:46:21 +020088 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020089 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020090}
91
92void hc_cli_res_end_cb(struct httpclient *hc)
93{
94 struct appctx *appctx = hc->caller;
95
William Lallemanddfc3f892021-08-20 11:35:29 +020096 if (!appctx)
97 return;
98
William Lallemand03a4eb12021-08-18 16:46:21 +020099 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200100 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200101}
102
103/*
104 * Parse an httpclient keyword on the cli:
105 * httpclient <ID> <method> <URI>
106 */
107static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
108{
109 struct httpclient *hc;
110 char *err = NULL;
111 enum http_meth_t meth;
112 char *meth_str;
113 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200114 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200115
116 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
117 return 1;
118
119 if (!*args[1] || !*args[2]) {
120 memprintf(&err, ": not enough parameters");
121 goto err;
122 }
123
124 meth_str = args[1];
125 uri = ist(args[2]);
126
William Lallemanddec25c32021-10-25 19:48:37 +0200127 if (payload)
128 body = ist(payload);
129
William Lallemand03a4eb12021-08-18 16:46:21 +0200130 meth = find_http_meth(meth_str, strlen(meth_str));
131
132 hc = httpclient_new(appctx, meth, uri);
133 if (!hc) {
134 goto err;
135 }
136
137 /* update the httpclient callbacks */
138 hc->ops.res_stline = hc_cli_res_stline_cb;
139 hc->ops.res_headers = hc_cli_res_headers_cb;
140 hc->ops.res_payload = hc_cli_res_body_cb;
141 hc->ops.res_end = hc_cli_res_end_cb;
142
143 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
144 appctx->ctx.cli.i0 = 0;
145
William Lallemandbad9c8c2022-01-14 14:10:33 +0100146 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200147 goto err;
148
149
150 if (!httpclient_start(hc))
151 goto err;
152
153 return 0;
154
155err:
156 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
157 return cli_err(appctx, err);
158}
159
160/* This function dumps the content of the httpclient receive buffer
161 * on the CLI output
162 *
163 * Return 1 when the processing is finished
164 * return 0 if it needs to be called again
165 */
166static int hc_cli_io_handler(struct appctx *appctx)
167{
Christopher Faulet86e1c332021-12-20 17:09:39 +0100168 struct stream_interface *si = cs_si(appctx->owner);
William Lallemand03a4eb12021-08-18 16:46:21 +0200169 struct buffer *trash = alloc_trash_chunk();
170 struct httpclient *hc = appctx->ctx.cli.p0;
171 struct http_hdr *hdrs, *hdr;
172
173 if (!trash)
174 goto out;
175 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100176 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
177 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
William Lallemand03a4eb12021-08-18 16:46:21 +0200178 if (ci_putchk(si_ic(si), trash) == -1)
179 si_rx_room_blk(si);
180 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
181 goto out;
182 }
183
184 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
185 hdrs = hc->res.hdrs;
186 for (hdr = hdrs; isttest(hdr->v); hdr++) {
187 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
188 goto out;
189 }
190 if (!chunk_memcat(trash, "\r\n", 2))
191 goto out;
192 if (ci_putchk(si_ic(si), trash) == -1)
193 si_rx_room_blk(si);
194 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
195 goto out;
196 }
197
198 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
199 int ret;
200
201 ret = httpclient_res_xfer(hc, &si_ic(si)->buf);
202 channel_add_input(si_ic(si), ret); /* forward what we put in the buffer channel */
203
William Lallemand518878e2021-09-21 10:45:34 +0200204 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200205 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
206 }
207 goto out;
208 }
209
210 /* we must close only if F_END is the last flag */
211 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
212 si_shutw(si);
213 si_shutr(si);
214 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
215 goto out;
216 }
217
218out:
219 /* we didn't clear every flags, we should come back to finish things */
220 if (appctx->ctx.cli.i0)
221 si_rx_room_blk(si);
222
223 free_trash_chunk(trash);
224 return 0;
225}
226
227static void hc_cli_release(struct appctx *appctx)
228{
229 struct httpclient *hc = appctx->ctx.cli.p0;
230
231 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200232 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200233
234 return;
235}
236
237/* register cli keywords */
238static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100239 { { "httpclient", NULL }, "httpclient <method> <URI> : launch an HTTP request", hc_cli_parse, hc_cli_io_handler, hc_cli_release, NULL, ACCESS_EXPERT},
William Lallemand03a4eb12021-08-18 16:46:21 +0200240 { { NULL }, NULL, NULL, NULL }
241}};
242
243INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
244
245
246/* --- This part of the file implements the actual HTTP client API --- */
247
William Lallemand33b0d092021-08-13 16:05:53 +0200248/*
249 * Generate a simple request and fill the httpclient request buffer with it.
250 * The request contains a request line generated from the absolute <url> and
251 * <meth> as well as list of headers <hdrs>.
252 *
253 * If the buffer was filled correctly the function returns 0, if not it returns
254 * an error_code but there is no guarantee that the buffer wasn't modified.
255 */
William Lallemanddec25c32021-10-25 19:48:37 +0200256int httpclient_req_gen(struct httpclient *hc, const struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs, const struct ist payload)
William Lallemand33b0d092021-08-13 16:05:53 +0200257{
258 struct htx_sl *sl;
259 struct htx *htx;
260 int err_code = 0;
261 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200262 unsigned int flags = HTX_SL_F_VER_11 | HTX_SL_F_NORMALIZED_URI | HTX_SL_F_HAS_SCHM;
William Lallemandf03b53c2021-11-24 15:38:17 +0100263 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100264 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200265
Christopher Faulet600985d2022-01-12 11:14:08 +0100266 if (!b_alloc(&hc->req.buf))
267 goto error;
268
William Lallemand33b0d092021-08-13 16:05:53 +0200269 if (meth >= HTTP_METH_OTHER)
270 goto error;
271
272 meth_ist = http_known_methods[meth];
273
274 vsn = ist("HTTP/1.1");
275
276 htx = htx_from_buf(&hc->req.buf);
277 if (!htx)
278 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100279
280 if (!hc->ops.req_payload && !isttest(payload))
281 flags |= HTX_SL_F_BODYLESS;
282
William Lallemand33b0d092021-08-13 16:05:53 +0200283 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
284 if (!sl) {
285 goto error;
286 }
287 sl->info.req.meth = meth;
288
William Lallemandf03b53c2021-11-24 15:38:17 +0100289 for (i = 0; hdrs && hdrs[i].n.len; i++) {
290 /* Don't check the value length because a header value may be empty */
291 if (isttest(hdrs[i].v) == 0)
292 continue;
293
294 if (isteqi(hdrs[i].n, ist("host")))
295 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100296 else if (isteqi(hdrs[i].n, ist("accept")))
297 foundaccept = 1;
298 else if (isteqi(hdrs[i].n, ist("user-agent")))
299 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100300
301 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
302 goto error;
303 }
William Lallemand33b0d092021-08-13 16:05:53 +0200304
William Lallemandf03b53c2021-11-24 15:38:17 +0100305 if (!foundhost) {
306 /* Add Host Header from URL */
307 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200308 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100309 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200310 goto error;
311 }
William Lallemand33b0d092021-08-13 16:05:53 +0200312
William Lallemandbad9c8c2022-01-14 14:10:33 +0100313 if (!foundaccept) {
314 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
315 goto error;
316 }
317
318 if (!foundua) {
319 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
320 goto error;
321 }
322
323
William Lallemandf03b53c2021-11-24 15:38:17 +0100324 if (!htx_add_endof(htx, HTX_BLK_EOH))
325 goto error;
326
William Lallemanddec25c32021-10-25 19:48:37 +0200327 if (isttest(payload)) {
328 /* add the payload if it can feat in the buffer, no need to set
329 * the Content-Length, the data will be sent chunked */
330 if (!htx_add_data_atonce(htx, payload))
331 goto error;
332 }
333
William Lallemand0da616e2021-10-28 15:34:26 +0200334 /* If req.payload was set, does not set the end of stream which *MUST*
335 * be set in the callback */
336 if (!hc->ops.req_payload)
337 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200338
339 htx_to_buf(htx, &hc->req.buf);
340
341 return 0;
342error:
343 err_code |= ERR_ALERT | ERR_ABORT;
344 return err_code;
345}
346
347/*
348 * transfer the response to the destination buffer and wakeup the HTTP client
349 * applet so it could fill again its buffer.
350 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500351 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200352 */
353int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
354{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100355 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200356 int ret;
357
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100358 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200359 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100360 if (!b_data(&hc->res.buf)) {
361 b_free(&hc->res.buf);
362 if (hc->appctx)
363 appctx_wakeup(hc->appctx);
364 }
William Lallemand33b0d092021-08-13 16:05:53 +0200365 return ret;
366}
367
368/*
William Lallemand0da616e2021-10-28 15:34:26 +0200369 * Transfer raw HTTP payload from src, and insert it into HTX format in the
370 * httpclient.
371 *
372 * Must be used to transfer the request body.
373 * Then wakeup the httpclient so it can transfer it.
374 *
375 * <end> tries to add the ending data flag if it succeed to copy all data.
376 *
377 * Return the number of bytes copied from src.
378 */
379int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
380{
381 int ret = 0;
382 struct htx *htx;
383
Christopher Faulet600985d2022-01-12 11:14:08 +0100384 if (!b_alloc(&hc->req.buf))
385 goto error;
386
William Lallemand0da616e2021-10-28 15:34:26 +0200387 htx = htx_from_buf(&hc->req.buf);
388 if (!htx)
389 goto error;
390
391 if (hc->appctx)
392 appctx_wakeup(hc->appctx);
393
394 ret += htx_add_data(htx, src);
395
396
397 /* if we copied all the data and the end flag is set */
398 if ((istlen(src) == ret) && end) {
399 htx->flags |= HTX_FL_EOM;
400 }
401 htx_to_buf(htx, &hc->req.buf);
402
403error:
404
405 return ret;
406}
407
William Lallemandb4a4ef62022-02-23 14:18:16 +0100408/* Set the 'timeout server' in ms for the next httpclient request */
409void httpclient_set_timeout(struct httpclient *hc, int timeout)
410{
411 hc->timeout_server = timeout;
412}
413
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100414/*
415 * Sets a destination for the httpclient from an HAProxy addr format
416 * This will prevent to determine the destination from the URL
417 * Return 0 in case of success or -1 otherwise.
418 */
419int httpclient_set_dst(struct httpclient *hc, const char *dst)
420{
421 struct sockaddr_storage *sk;
422 char *errmsg = NULL;
423
424 sockaddr_free(&hc->dst);
425 /* 'sk' is statically allocated (no need to be freed). */
426 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
427 &errmsg, NULL, NULL,
428 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
429 if (!sk) {
430 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
431 free(errmsg);
432 return -1;
433 }
434
435 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
436 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
437 return -1;
438 }
439
440 return 0;
441}
William Lallemand0da616e2021-10-28 15:34:26 +0200442
443/*
William Lallemand33b0d092021-08-13 16:05:53 +0200444 * Start the HTTP client
445 * Create the appctx, session, stream and wakeup the applet
446 *
447 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
448 * in the URL are supported, it lacks a resolver.
449 *
450 * Return the <appctx> or NULL if it failed
451 */
452struct appctx *httpclient_start(struct httpclient *hc)
453{
454 struct applet *applet = &httpclient_applet;
455 struct appctx *appctx;
456 struct session *sess;
Christopher Faulet13a35e52021-12-20 15:34:16 +0100457 struct conn_stream *cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200458 struct stream *s;
459 int len;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100460 struct sockaddr_storage ss_url;
461 struct sockaddr_storage* ss_dst;
William Lallemand33b0d092021-08-13 16:05:53 +0200462 struct split_url out;
463
William Lallemand5085bc32022-02-17 12:52:09 +0100464 /* if the client was started and not ended, an applet is already
465 * running, we shouldn't try anything */
466 if (httpclient_started(hc) && !httpclient_ended(hc))
467 return NULL;
468
469 hc->flags = 0;
470
William Lallemand33b0d092021-08-13 16:05:53 +0200471 /* parse URI and fill sockaddr_storage */
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100472 len = url2sa(istptr(hc->req.url), istlen(hc->req.url), &ss_url, &out);
William Lallemand33b0d092021-08-13 16:05:53 +0200473 if (len == -1) {
William Lallemand614e6832021-09-26 18:12:43 +0200474 ha_alert("httpclient: cannot parse uri '%s'.\n", istptr(hc->req.url));
William Lallemand33b0d092021-08-13 16:05:53 +0200475 goto out;
476 }
477
478 /* The HTTP client will be created in the same thread as the caller,
479 * avoiding threading issues */
Willy Tarreaue6124462021-09-13 10:07:38 +0200480 appctx = appctx_new(applet);
William Lallemand33b0d092021-08-13 16:05:53 +0200481 if (!appctx)
482 goto out;
483
484 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
485 if (!sess) {
486 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
487 goto out_free_appctx;
488 }
Christopher Fauletf835dea2021-12-21 14:35:17 +0100489 cs = cs_new(&appctx->obj_type, appctx, NULL, NULL, NULL);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100490 if (!cs) {
491 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
492 goto out_free_sess;
493 }
494 if ((s = stream_new(sess, cs, &hc->req.buf)) == NULL) {
William Lallemand33b0d092021-08-13 16:05:53 +0200495 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100496 goto out_free_cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200497 }
498
William Lallemandb4a4ef62022-02-23 14:18:16 +0100499 /* set the "timeout server" */
500 s->req.wto = hc->timeout_server;
501 s->res.rto = hc->timeout_server;
502
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100503 /* if httpclient_set_dst() was used, sets the alternative address */
504 if (hc->dst)
505 ss_dst = hc->dst;
506 else
507 ss_dst = &ss_url;
508
Christopher Fauletb91afea2021-12-23 13:58:12 +0100509 if (!sockaddr_alloc(&cs_si(s->csb)->dst, ss_dst, sizeof(*hc->dst))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200510 ha_alert("httpclient: Failed to initialize stream in %s:%d.\n", __FUNCTION__, __LINE__);
511 goto out_free_stream;
512 }
513
514 /* choose the SSL server or not */
515 switch (out.scheme) {
516 case SCH_HTTP:
517 s->target = &httpclient_srv_raw->obj_type;
518 break;
519 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200520#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200521 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200522#else
523 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
524 goto out_free_stream;
525#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200526 break;
527 }
528
529 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
Christopher Fauletb91afea2021-12-23 13:58:12 +0100530 cs_si(s->csb)->flags |= SI_FL_NOLINGER;
William Lallemand33b0d092021-08-13 16:05:53 +0200531 s->res.flags |= CF_READ_DONTWAIT;
532
533 /* applet is waiting for data */
Christopher Fauletb91afea2021-12-23 13:58:12 +0100534 si_cant_get(cs_si(s->csf));
William Lallemand33b0d092021-08-13 16:05:53 +0200535 appctx_wakeup(appctx);
536
William Lallemand33b0d092021-08-13 16:05:53 +0200537 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200538 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200539 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100540
541 /* The request was transferred when the stream was created. So switch
542 * directly to REQ_BODY or RES_STLINE state
543 */
544 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200545
546 return appctx;
547
548out_free_stream:
549 LIST_DELETE(&s->list);
550 pool_free(pool_head_stream, s);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100551out_free_cs:
552 cs_free(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200553out_free_sess:
554 session_free(sess);
555out_free_appctx:
556 appctx_free(appctx);
557out:
558
559 return NULL;
560}
561
William Lallemandecb83e12021-09-28 11:00:43 +0200562/*
563 * This function tries to destroy the httpclient if it wasn't running.
564 * If it was running, stop the client and ask it to autodestroy itself.
565 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500566 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200567 *
568 */
569void httpclient_stop_and_destroy(struct httpclient *hc)
570{
571
William Lallemandb8b13702021-09-28 12:15:37 +0200572 /* The httpclient was already stopped or never started, we can safely destroy it */
573 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200574 httpclient_destroy(hc);
575 } else {
576 /* if the client wasn't stopped, ask for a stop and destroy */
577 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
578 if (hc->appctx)
579 appctx_wakeup(hc->appctx);
580 }
581}
582
William Lallemand33b0d092021-08-13 16:05:53 +0200583/* Free the httpclient */
584void httpclient_destroy(struct httpclient *hc)
585{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200586 struct http_hdr *hdrs;
587
588
William Lallemand33b0d092021-08-13 16:05:53 +0200589 if (!hc)
590 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200591
William Lallemand2a879002021-10-05 15:50:45 +0200592 /* we should never destroy a client which was started but not stopped */
593 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200594
William Lallemand03f5a1c2021-09-27 15:17:47 +0200595 /* request */
596 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200597 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200598 /* response */
599 istfree(&hc->res.vsn);
600 istfree(&hc->res.reason);
601 hdrs = hc->res.hdrs;
602 while (hdrs && isttest(hdrs->n)) {
603 istfree(&hdrs->n);
604 istfree(&hdrs->v);
605 hdrs++;
606 }
607 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200608 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100609 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200610
William Lallemand33b0d092021-08-13 16:05:53 +0200611 free(hc);
612
613 return;
614}
615
616/* Allocate an httpclient and its buffers
617 * Return NULL on failure */
618struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
619{
620 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200621
622 hc = calloc(1, sizeof(*hc));
623 if (!hc)
624 goto err;
625
Christopher Faulet600985d2022-01-12 11:14:08 +0100626 hc->req.buf = BUF_NULL;
627 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200628 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100629 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200630 hc->req.meth = meth;
631
632 return hc;
633
634err:
635 httpclient_destroy(hc);
636 return NULL;
637}
638
639static void httpclient_applet_io_handler(struct appctx *appctx)
640{
641 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet86e1c332021-12-20 17:09:39 +0100642 struct stream_interface *si = cs_si(appctx->owner);
William Lallemand33b0d092021-08-13 16:05:53 +0200643 struct stream *s = si_strm(si);
644 struct channel *req = &s->req;
645 struct channel *res = &s->res;
646 struct htx_blk *blk = NULL;
647 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200648 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200649 int32_t pos;
650 uint32_t hdr_num;
William Lallemand933fe392021-11-04 09:45:58 +0100651 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200652
653
654 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200655
656 /* required to stop */
657 if (hc->flags & HTTPCLIENT_FA_STOP)
658 goto end;
659
William Lallemand33b0d092021-08-13 16:05:53 +0200660 switch(appctx->st0) {
661
662 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100663 /* we know that the buffer is empty here, since
664 * it's the first call, we can freely copy the
665 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100666 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100667 if (!ret)
668 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100669
Christopher Faulet600985d2022-01-12 11:14:08 +0100670 if (!b_data(&hc->req.buf))
671 b_free(&hc->req.buf);
672
William Lallemanddb8a1f32021-11-08 16:55:14 +0100673 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100674 if (!htx)
675 goto more;
676
William Lallemanddb8a1f32021-11-08 16:55:14 +0100677 channel_add_input(req, htx->data);
678
William Lallemand933fe392021-11-04 09:45:58 +0100679 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
680 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
681 else
682 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
683
William Lallemand33b0d092021-08-13 16:05:53 +0200684 goto more; /* we need to leave the IO handler once we wrote the request */
685 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200686 case HTTPCLIENT_S_REQ_BODY:
687 /* call the payload callback */
688 {
689 if (hc->ops.req_payload) {
William Lallemand0da616e2021-10-28 15:34:26 +0200690
William Lallemand0da616e2021-10-28 15:34:26 +0200691 /* call the request callback */
692 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100693 /* check if the request buffer is empty */
694
695 htx = htx_from_buf(&req->buf);
696 if (!htx_is_empty(htx))
697 goto more;
698 /* Here htx_to_buf() will set buffer data to 0 because
699 * the HTX is empty, and allow us to do an xfer.
700 */
701 htx_to_buf(htx, &req->buf);
702
703 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
704 if (!ret)
705 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100706
707 if (!b_data(&hc->req.buf))
708 b_free(&hc->req.buf);
709
William Lallemanddb8a1f32021-11-08 16:55:14 +0100710 htx = htx_from_buf(&req->buf);
711 if (!htx)
712 goto more;
713
714 channel_add_input(req, htx->data);
William Lallemand0da616e2021-10-28 15:34:26 +0200715 }
716
William Lallemanddb8a1f32021-11-08 16:55:14 +0100717 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200718 if (!htx)
719 goto more;
720
721 /* if the request contains the HTX_FL_EOM, we finished the request part. */
722 if (htx->flags & HTX_FL_EOM)
723 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
724
725 goto more; /* we need to leave the IO handler once we wrote the request */
726 }
727 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200728
729 case HTTPCLIENT_S_RES_STLINE:
730 /* copy the start line in the hc structure,then remove the htx block */
731 if (!b_data(&res->buf))
732 goto more;
733 htx = htxbuf(&res->buf);
734 if (!htx)
735 goto more;
736 blk = htx_get_first_blk(htx);
737 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
738 sl = htx_get_blk_ptr(htx, blk);
739 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
740 goto more;
741
742 /* copy the status line in the httpclient */
743 hc->res.status = sl->info.res.status;
744 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
745 hc->res.reason = istdup(htx_sl_res_reason(sl));
746 co_htx_remove_blk(res, htx, blk);
747 /* caller callback */
748 if (hc->ops.res_stline)
749 hc->ops.res_stline(hc);
750
751 /* if there is no HTX data anymore and the EOM flag is
752 * set, leave (no body) */
753 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
754 appctx->st0 = HTTPCLIENT_S_RES_END;
755 else
756 appctx->st0 = HTTPCLIENT_S_RES_HDR;
757 break;
758
759 case HTTPCLIENT_S_RES_HDR:
760 /* first copy the headers in a local hdrs
761 * structure, once we the total numbers of the
762 * header we allocate the right size and copy
763 * them. The htx block of the headers are
764 * removed each time one is read */
765 {
766 struct http_hdr hdrs[global.tune.max_http_hdr];
767
768 if (!b_data(&res->buf))
769 goto more;
770 htx = htxbuf(&res->buf);
771 if (!htx)
772 goto more;
773
774 hdr_num = 0;
775
776 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
777 struct htx_blk *blk = htx_get_blk(htx, pos);
778 enum htx_blk_type type = htx_get_blk_type(blk);
779
780 if (type == HTX_BLK_EOH) {
781 hdrs[hdr_num].n = IST_NULL;
782 hdrs[hdr_num].v = IST_NULL;
783 co_htx_remove_blk(res, htx, blk);
784 break;
785 }
786
787 if (type != HTX_BLK_HDR)
788 continue;
789
790 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
791 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
792 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
793 goto end;
794 co_htx_remove_blk(res, htx, blk);
795 hdr_num++;
796 }
797
William Lallemand0d6f7792021-08-20 11:59:49 +0200798 if (hdr_num) {
799 /* alloc and copy the headers in the httpclient struct */
800 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
801 if (!hc->res.hdrs)
802 goto end;
803 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200804
William Lallemand0d6f7792021-08-20 11:59:49 +0200805 /* caller callback */
806 if (hc->ops.res_headers)
807 hc->ops.res_headers(hc);
808 }
William Lallemand33b0d092021-08-13 16:05:53 +0200809
810 /* if there is no HTX data anymore and the EOM flag is
811 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200812 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200813 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200814 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200815 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200816 }
William Lallemand33b0d092021-08-13 16:05:53 +0200817 }
818 break;
819
820 case HTTPCLIENT_S_RES_BODY:
821 /*
822 * The IO handler removes the htx blocks in the response buffer and
823 * push them in the hc->res.buf buffer in a raw format.
824 */
825 htx = htxbuf(&res->buf);
826 if (!htx || htx_is_empty(htx))
827 goto more;
828
Christopher Faulet600985d2022-01-12 11:14:08 +0100829 if (!b_alloc(&hc->res.buf))
830 goto more;
831
William Lallemand33b0d092021-08-13 16:05:53 +0200832 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100833 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200834
835 /* decapsule the htx data to raw data */
836 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
837 enum htx_blk_type type;
838
839 blk = htx_get_blk(htx, pos);
840 type = htx_get_blk_type(blk);
841 if (type == HTX_BLK_DATA) {
842 struct ist v = htx_get_blk_value(htx, blk);
843
844 if ((b_room(&hc->res.buf) < v.len) )
845 goto process_data;
846
847 __b_putblk(&hc->res.buf, v.ptr, v.len);
848 co_htx_remove_blk(res, htx, blk);
849 /* the data must be processed by the caller in the receive phase */
850 if (hc->ops.res_payload)
851 hc->ops.res_payload(hc);
852 } else {
853 /* remove any block which is not a data block */
854 co_htx_remove_blk(res, htx, blk);
855 }
856 }
857 /* if not finished, should be called again */
858 if (!(htx->flags & HTX_FL_EOM))
859 goto more;
860
861 /* end of message, we should quit */
862 appctx->st0 = HTTPCLIENT_S_RES_END;
863 break;
864
865 case HTTPCLIENT_S_RES_END:
866 goto end;
867 break;
868 }
869 }
870
871process_data:
872
873 si_rx_chan_rdy(si);
874
875 return;
876more:
877 /* There was not enough data in the response channel */
878
879 si_rx_room_blk(si);
880
881 if (appctx->st0 == HTTPCLIENT_S_RES_END)
882 goto end;
883
884 /* The state machine tries to handle as much data as possible, if there
885 * isn't any data to handle and a shutdown is detected, let's stop
886 * everything */
887 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
888 (res->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
889 goto end;
890 }
891 return;
892
893end:
894 if (hc->ops.res_end)
895 hc->ops.res_end(hc);
896 si_shutw(si);
897 si_shutr(si);
898 return;
899}
900
901static void httpclient_applet_release(struct appctx *appctx)
902{
903 struct httpclient *hc = appctx->ctx.httpclient.ptr;
904
William Lallemand1123dde2021-09-21 10:58:10 +0200905 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200906 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200907 /* the applet is leaving, remove the ptr so we don't try to call it
908 * again from the caller */
909 hc->appctx = NULL;
910
William Lallemandecb83e12021-09-28 11:00:43 +0200911
912 /* destroy the httpclient when set to autotokill */
913 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
914 httpclient_destroy(hc);
915 }
916
William Lallemand33b0d092021-08-13 16:05:53 +0200917 return;
918}
919
920/* HTTP client applet */
921static struct applet httpclient_applet = {
922 .obj_type = OBJ_TYPE_APPLET,
923 .name = "<HTTPCLIENT>",
924 .fct = httpclient_applet_io_handler,
925 .release = httpclient_applet_release,
926};
927
William Lallemand83614a92021-08-13 14:47:57 +0200928/*
929 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
930 * the other for HTTPS.
931 */
932
933static int httpclient_init()
934{
935 int err_code = 0;
936 char *errmsg = NULL;
937
938 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
939 if (!httpclient_proxy) {
940 err_code |= ERR_ALERT | ERR_FATAL;
941 goto err;
942 }
943
Willy Tarreau0e72e402021-08-20 10:23:12 +0200944 proxy_preset_defaults(httpclient_proxy);
945
William Lallemand83614a92021-08-13 14:47:57 +0200946 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
947 httpclient_proxy->mode = PR_MODE_HTTP;
948 httpclient_proxy->maxconn = 0;
949 httpclient_proxy->accept = NULL;
950 httpclient_proxy->timeout.client = TICK_ETERNITY;
951 /* The HTTP Client use the "option httplog" with the global log server */
952 httpclient_proxy->conf.logformat_string = default_http_log_format;
953 httpclient_proxy->http_needed = 1;
954
955 /* clear HTTP server */
956 httpclient_srv_raw = new_server(httpclient_proxy);
957 if (!httpclient_srv_raw) {
958 err_code |= ERR_ALERT | ERR_FATAL;
959 memprintf(&errmsg, "out of memory.");
960 goto err;
961 }
962
963 httpclient_srv_raw->iweight = 0;
964 httpclient_srv_raw->uweight = 0;
965 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
966 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
967 if (!httpclient_srv_raw->id)
968 goto err;
969
William Lallemand957ab132021-08-24 18:33:28 +0200970#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +0200971 /* SSL HTTP server */
972 httpclient_srv_ssl = new_server(httpclient_proxy);
973 if (!httpclient_srv_ssl) {
974 memprintf(&errmsg, "out of memory.");
975 err_code |= ERR_ALERT | ERR_FATAL;
976 goto err;
977 }
978 httpclient_srv_ssl->iweight = 0;
979 httpclient_srv_ssl->uweight = 0;
980 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
981 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +0200982 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +0200983 if (!httpclient_srv_ssl->id)
984 goto err;
985
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200986 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +0200987#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200988
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500989 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +0200990 httpclient_proxy->next = proxies_list;
991 proxies_list = httpclient_proxy;
992
William Lallemand211c9672021-08-24 17:18:13 +0200993 /* link the 2 servers in the proxy */
994 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +0200995 httpclient_proxy->srv = httpclient_srv_raw;
996
997#ifdef USE_OPENSSL
998 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +0200999 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +02001000#endif
1001
William Lallemand211c9672021-08-24 17:18:13 +02001002
William Lallemand83614a92021-08-13 14:47:57 +02001003 return 0;
1004
1005err:
1006 ha_alert("httpclient: cannot initialize.\n");
1007 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001008 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001009#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001010 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001011#endif
William Lallemand83614a92021-08-13 14:47:57 +02001012 free_proxy(httpclient_proxy);
1013 return err_code;
1014}
1015
William Lallemand83614a92021-08-13 14:47:57 +02001016static int httpclient_cfg_postparser()
1017{
1018 struct logsrv *logsrv;
1019 struct proxy *curproxy = httpclient_proxy;
1020
1021 /* copy logs from "global" log list */
1022 list_for_each_entry(logsrv, &global.logsrvs, list) {
1023 struct logsrv *node = malloc(sizeof(*node));
1024
1025 if (!node) {
1026 ha_alert("httpclient: cannot allocate memory.\n");
1027 goto err;
1028 }
1029
1030 memcpy(node, logsrv, sizeof(*node));
1031 LIST_INIT(&node->list);
1032 LIST_APPEND(&curproxy->logsrvs, &node->list);
1033 }
1034 if (curproxy->conf.logformat_string) {
1035 char *err = NULL;
1036
1037 curproxy->conf.args.ctx = ARGC_LOG;
1038 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1039 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
1040 SMP_VAL_FE_LOG_END, &err)) {
1041 ha_alert("httpclient: failed to parse log-format : %s.\n", err);
1042 free(err);
1043 goto err;
1044 }
1045 curproxy->conf.args.file = NULL;
1046 curproxy->conf.args.line = 0;
1047 }
1048 return 0;
1049err:
1050 return 1;
1051}
1052
William Lallemand83614a92021-08-13 14:47:57 +02001053/* initialize the proxy and servers for the HTTP client */
1054
Willy Tarreau5b4b6ca2022-02-18 16:23:14 +01001055INITCALL0(STG_INIT, httpclient_init);
William Lallemand83614a92021-08-13 14:47:57 +02001056REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);