blob: b84e6178ef19374ded0b9f25b890350faff420b5 [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 Fauletcda94ac2021-12-23 17:28:17 +0100489 cs = cs_new();
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 }
Christopher Fauletcda94ac2021-12-23 17:28:17 +0100494 cs_attach_endp(cs, &appctx->obj_type, appctx);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100495 if ((s = stream_new(sess, cs, &hc->req.buf)) == NULL) {
William Lallemand33b0d092021-08-13 16:05:53 +0200496 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100497 goto out_free_cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200498 }
499
William Lallemandb4a4ef62022-02-23 14:18:16 +0100500 /* set the "timeout server" */
501 s->req.wto = hc->timeout_server;
502 s->res.rto = hc->timeout_server;
503
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100504 /* if httpclient_set_dst() was used, sets the alternative address */
505 if (hc->dst)
506 ss_dst = hc->dst;
507 else
508 ss_dst = &ss_url;
509
Christopher Fauletb91afea2021-12-23 13:58:12 +0100510 if (!sockaddr_alloc(&cs_si(s->csb)->dst, ss_dst, sizeof(*hc->dst))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200511 ha_alert("httpclient: Failed to initialize stream in %s:%d.\n", __FUNCTION__, __LINE__);
512 goto out_free_stream;
513 }
514
515 /* choose the SSL server or not */
516 switch (out.scheme) {
517 case SCH_HTTP:
518 s->target = &httpclient_srv_raw->obj_type;
519 break;
520 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200521#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200522 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200523#else
524 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
525 goto out_free_stream;
526#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200527 break;
528 }
529
530 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
Christopher Fauletb91afea2021-12-23 13:58:12 +0100531 cs_si(s->csb)->flags |= SI_FL_NOLINGER;
William Lallemand33b0d092021-08-13 16:05:53 +0200532 s->res.flags |= CF_READ_DONTWAIT;
533
534 /* applet is waiting for data */
Christopher Fauletb91afea2021-12-23 13:58:12 +0100535 si_cant_get(cs_si(s->csf));
William Lallemand33b0d092021-08-13 16:05:53 +0200536 appctx_wakeup(appctx);
537
William Lallemand33b0d092021-08-13 16:05:53 +0200538 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200539 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200540 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100541
542 /* The request was transferred when the stream was created. So switch
543 * directly to REQ_BODY or RES_STLINE state
544 */
545 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200546
547 return appctx;
548
549out_free_stream:
550 LIST_DELETE(&s->list);
551 pool_free(pool_head_stream, s);
Christopher Faulet13a35e52021-12-20 15:34:16 +0100552out_free_cs:
553 cs_free(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200554out_free_sess:
555 session_free(sess);
556out_free_appctx:
557 appctx_free(appctx);
558out:
559
560 return NULL;
561}
562
William Lallemandecb83e12021-09-28 11:00:43 +0200563/*
564 * This function tries to destroy the httpclient if it wasn't running.
565 * If it was running, stop the client and ask it to autodestroy itself.
566 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500567 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200568 *
569 */
570void httpclient_stop_and_destroy(struct httpclient *hc)
571{
572
William Lallemandb8b13702021-09-28 12:15:37 +0200573 /* The httpclient was already stopped or never started, we can safely destroy it */
574 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200575 httpclient_destroy(hc);
576 } else {
577 /* if the client wasn't stopped, ask for a stop and destroy */
578 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
579 if (hc->appctx)
580 appctx_wakeup(hc->appctx);
581 }
582}
583
William Lallemand33b0d092021-08-13 16:05:53 +0200584/* Free the httpclient */
585void httpclient_destroy(struct httpclient *hc)
586{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200587 struct http_hdr *hdrs;
588
589
William Lallemand33b0d092021-08-13 16:05:53 +0200590 if (!hc)
591 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200592
William Lallemand2a879002021-10-05 15:50:45 +0200593 /* we should never destroy a client which was started but not stopped */
594 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200595
William Lallemand03f5a1c2021-09-27 15:17:47 +0200596 /* request */
597 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200598 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200599 /* response */
600 istfree(&hc->res.vsn);
601 istfree(&hc->res.reason);
602 hdrs = hc->res.hdrs;
603 while (hdrs && isttest(hdrs->n)) {
604 istfree(&hdrs->n);
605 istfree(&hdrs->v);
606 hdrs++;
607 }
608 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200609 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100610 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200611
William Lallemand33b0d092021-08-13 16:05:53 +0200612 free(hc);
613
614 return;
615}
616
617/* Allocate an httpclient and its buffers
618 * Return NULL on failure */
619struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
620{
621 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200622
623 hc = calloc(1, sizeof(*hc));
624 if (!hc)
625 goto err;
626
Christopher Faulet600985d2022-01-12 11:14:08 +0100627 hc->req.buf = BUF_NULL;
628 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200629 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100630 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200631 hc->req.meth = meth;
632
633 return hc;
634
635err:
636 httpclient_destroy(hc);
637 return NULL;
638}
639
640static void httpclient_applet_io_handler(struct appctx *appctx)
641{
642 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet86e1c332021-12-20 17:09:39 +0100643 struct stream_interface *si = cs_si(appctx->owner);
William Lallemand33b0d092021-08-13 16:05:53 +0200644 struct stream *s = si_strm(si);
645 struct channel *req = &s->req;
646 struct channel *res = &s->res;
647 struct htx_blk *blk = NULL;
648 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200649 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200650 int32_t pos;
651 uint32_t hdr_num;
William Lallemand933fe392021-11-04 09:45:58 +0100652 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200653
654
655 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200656
657 /* required to stop */
658 if (hc->flags & HTTPCLIENT_FA_STOP)
659 goto end;
660
William Lallemand33b0d092021-08-13 16:05:53 +0200661 switch(appctx->st0) {
662
663 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100664 /* we know that the buffer is empty here, since
665 * it's the first call, we can freely copy the
666 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100667 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100668 if (!ret)
669 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100670
Christopher Faulet600985d2022-01-12 11:14:08 +0100671 if (!b_data(&hc->req.buf))
672 b_free(&hc->req.buf);
673
William Lallemanddb8a1f32021-11-08 16:55:14 +0100674 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100675 if (!htx)
676 goto more;
677
William Lallemanddb8a1f32021-11-08 16:55:14 +0100678 channel_add_input(req, htx->data);
679
William Lallemand933fe392021-11-04 09:45:58 +0100680 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
681 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
682 else
683 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
684
William Lallemand33b0d092021-08-13 16:05:53 +0200685 goto more; /* we need to leave the IO handler once we wrote the request */
686 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200687 case HTTPCLIENT_S_REQ_BODY:
688 /* call the payload callback */
689 {
690 if (hc->ops.req_payload) {
William Lallemand0da616e2021-10-28 15:34:26 +0200691
William Lallemand0da616e2021-10-28 15:34:26 +0200692 /* call the request callback */
693 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100694 /* check if the request buffer is empty */
695
696 htx = htx_from_buf(&req->buf);
697 if (!htx_is_empty(htx))
698 goto more;
699 /* Here htx_to_buf() will set buffer data to 0 because
700 * the HTX is empty, and allow us to do an xfer.
701 */
702 htx_to_buf(htx, &req->buf);
703
704 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
705 if (!ret)
706 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100707
708 if (!b_data(&hc->req.buf))
709 b_free(&hc->req.buf);
710
William Lallemanddb8a1f32021-11-08 16:55:14 +0100711 htx = htx_from_buf(&req->buf);
712 if (!htx)
713 goto more;
714
715 channel_add_input(req, htx->data);
William Lallemand0da616e2021-10-28 15:34:26 +0200716 }
717
William Lallemanddb8a1f32021-11-08 16:55:14 +0100718 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200719 if (!htx)
720 goto more;
721
722 /* if the request contains the HTX_FL_EOM, we finished the request part. */
723 if (htx->flags & HTX_FL_EOM)
724 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
725
726 goto more; /* we need to leave the IO handler once we wrote the request */
727 }
728 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200729
730 case HTTPCLIENT_S_RES_STLINE:
731 /* copy the start line in the hc structure,then remove the htx block */
732 if (!b_data(&res->buf))
733 goto more;
734 htx = htxbuf(&res->buf);
735 if (!htx)
736 goto more;
737 blk = htx_get_first_blk(htx);
738 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
739 sl = htx_get_blk_ptr(htx, blk);
740 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
741 goto more;
742
743 /* copy the status line in the httpclient */
744 hc->res.status = sl->info.res.status;
745 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
746 hc->res.reason = istdup(htx_sl_res_reason(sl));
747 co_htx_remove_blk(res, htx, blk);
748 /* caller callback */
749 if (hc->ops.res_stline)
750 hc->ops.res_stline(hc);
751
752 /* if there is no HTX data anymore and the EOM flag is
753 * set, leave (no body) */
754 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
755 appctx->st0 = HTTPCLIENT_S_RES_END;
756 else
757 appctx->st0 = HTTPCLIENT_S_RES_HDR;
758 break;
759
760 case HTTPCLIENT_S_RES_HDR:
761 /* first copy the headers in a local hdrs
762 * structure, once we the total numbers of the
763 * header we allocate the right size and copy
764 * them. The htx block of the headers are
765 * removed each time one is read */
766 {
767 struct http_hdr hdrs[global.tune.max_http_hdr];
768
769 if (!b_data(&res->buf))
770 goto more;
771 htx = htxbuf(&res->buf);
772 if (!htx)
773 goto more;
774
775 hdr_num = 0;
776
777 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
778 struct htx_blk *blk = htx_get_blk(htx, pos);
779 enum htx_blk_type type = htx_get_blk_type(blk);
780
781 if (type == HTX_BLK_EOH) {
782 hdrs[hdr_num].n = IST_NULL;
783 hdrs[hdr_num].v = IST_NULL;
784 co_htx_remove_blk(res, htx, blk);
785 break;
786 }
787
788 if (type != HTX_BLK_HDR)
789 continue;
790
791 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
792 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
793 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
794 goto end;
795 co_htx_remove_blk(res, htx, blk);
796 hdr_num++;
797 }
798
William Lallemand0d6f7792021-08-20 11:59:49 +0200799 if (hdr_num) {
800 /* alloc and copy the headers in the httpclient struct */
801 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
802 if (!hc->res.hdrs)
803 goto end;
804 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200805
William Lallemand0d6f7792021-08-20 11:59:49 +0200806 /* caller callback */
807 if (hc->ops.res_headers)
808 hc->ops.res_headers(hc);
809 }
William Lallemand33b0d092021-08-13 16:05:53 +0200810
811 /* if there is no HTX data anymore and the EOM flag is
812 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200813 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200814 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200815 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200816 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200817 }
William Lallemand33b0d092021-08-13 16:05:53 +0200818 }
819 break;
820
821 case HTTPCLIENT_S_RES_BODY:
822 /*
823 * The IO handler removes the htx blocks in the response buffer and
824 * push them in the hc->res.buf buffer in a raw format.
825 */
826 htx = htxbuf(&res->buf);
827 if (!htx || htx_is_empty(htx))
828 goto more;
829
Christopher Faulet600985d2022-01-12 11:14:08 +0100830 if (!b_alloc(&hc->res.buf))
831 goto more;
832
William Lallemand33b0d092021-08-13 16:05:53 +0200833 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100834 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200835
836 /* decapsule the htx data to raw data */
837 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
838 enum htx_blk_type type;
839
840 blk = htx_get_blk(htx, pos);
841 type = htx_get_blk_type(blk);
842 if (type == HTX_BLK_DATA) {
843 struct ist v = htx_get_blk_value(htx, blk);
844
845 if ((b_room(&hc->res.buf) < v.len) )
846 goto process_data;
847
848 __b_putblk(&hc->res.buf, v.ptr, v.len);
849 co_htx_remove_blk(res, htx, blk);
850 /* the data must be processed by the caller in the receive phase */
851 if (hc->ops.res_payload)
852 hc->ops.res_payload(hc);
853 } else {
854 /* remove any block which is not a data block */
855 co_htx_remove_blk(res, htx, blk);
856 }
857 }
858 /* if not finished, should be called again */
859 if (!(htx->flags & HTX_FL_EOM))
860 goto more;
861
862 /* end of message, we should quit */
863 appctx->st0 = HTTPCLIENT_S_RES_END;
864 break;
865
866 case HTTPCLIENT_S_RES_END:
867 goto end;
868 break;
869 }
870 }
871
872process_data:
873
874 si_rx_chan_rdy(si);
875
876 return;
877more:
878 /* There was not enough data in the response channel */
879
880 si_rx_room_blk(si);
881
882 if (appctx->st0 == HTTPCLIENT_S_RES_END)
883 goto end;
884
885 /* The state machine tries to handle as much data as possible, if there
886 * isn't any data to handle and a shutdown is detected, let's stop
887 * everything */
888 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
889 (res->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
890 goto end;
891 }
892 return;
893
894end:
895 if (hc->ops.res_end)
896 hc->ops.res_end(hc);
897 si_shutw(si);
898 si_shutr(si);
899 return;
900}
901
902static void httpclient_applet_release(struct appctx *appctx)
903{
904 struct httpclient *hc = appctx->ctx.httpclient.ptr;
905
William Lallemand1123dde2021-09-21 10:58:10 +0200906 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200907 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200908 /* the applet is leaving, remove the ptr so we don't try to call it
909 * again from the caller */
910 hc->appctx = NULL;
911
William Lallemandecb83e12021-09-28 11:00:43 +0200912
913 /* destroy the httpclient when set to autotokill */
914 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
915 httpclient_destroy(hc);
916 }
917
William Lallemand33b0d092021-08-13 16:05:53 +0200918 return;
919}
920
921/* HTTP client applet */
922static struct applet httpclient_applet = {
923 .obj_type = OBJ_TYPE_APPLET,
924 .name = "<HTTPCLIENT>",
925 .fct = httpclient_applet_io_handler,
926 .release = httpclient_applet_release,
927};
928
William Lallemand83614a92021-08-13 14:47:57 +0200929/*
930 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
931 * the other for HTTPS.
932 */
933
934static int httpclient_init()
935{
936 int err_code = 0;
937 char *errmsg = NULL;
938
939 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
940 if (!httpclient_proxy) {
941 err_code |= ERR_ALERT | ERR_FATAL;
942 goto err;
943 }
944
Willy Tarreau0e72e402021-08-20 10:23:12 +0200945 proxy_preset_defaults(httpclient_proxy);
946
William Lallemand83614a92021-08-13 14:47:57 +0200947 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
948 httpclient_proxy->mode = PR_MODE_HTTP;
949 httpclient_proxy->maxconn = 0;
950 httpclient_proxy->accept = NULL;
951 httpclient_proxy->timeout.client = TICK_ETERNITY;
952 /* The HTTP Client use the "option httplog" with the global log server */
953 httpclient_proxy->conf.logformat_string = default_http_log_format;
954 httpclient_proxy->http_needed = 1;
955
956 /* clear HTTP server */
957 httpclient_srv_raw = new_server(httpclient_proxy);
958 if (!httpclient_srv_raw) {
959 err_code |= ERR_ALERT | ERR_FATAL;
960 memprintf(&errmsg, "out of memory.");
961 goto err;
962 }
963
964 httpclient_srv_raw->iweight = 0;
965 httpclient_srv_raw->uweight = 0;
966 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
967 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
968 if (!httpclient_srv_raw->id)
969 goto err;
970
William Lallemand957ab132021-08-24 18:33:28 +0200971#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +0200972 /* SSL HTTP server */
973 httpclient_srv_ssl = new_server(httpclient_proxy);
974 if (!httpclient_srv_ssl) {
975 memprintf(&errmsg, "out of memory.");
976 err_code |= ERR_ALERT | ERR_FATAL;
977 goto err;
978 }
979 httpclient_srv_ssl->iweight = 0;
980 httpclient_srv_ssl->uweight = 0;
981 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
982 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +0200983 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +0200984 if (!httpclient_srv_ssl->id)
985 goto err;
986
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200987 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +0200988#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200989
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500990 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +0200991 httpclient_proxy->next = proxies_list;
992 proxies_list = httpclient_proxy;
993
William Lallemand211c9672021-08-24 17:18:13 +0200994 /* link the 2 servers in the proxy */
995 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +0200996 httpclient_proxy->srv = httpclient_srv_raw;
997
998#ifdef USE_OPENSSL
999 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +02001000 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +02001001#endif
1002
William Lallemand211c9672021-08-24 17:18:13 +02001003
William Lallemand83614a92021-08-13 14:47:57 +02001004 return 0;
1005
1006err:
1007 ha_alert("httpclient: cannot initialize.\n");
1008 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001009 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001010#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001011 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001012#endif
William Lallemand83614a92021-08-13 14:47:57 +02001013 free_proxy(httpclient_proxy);
1014 return err_code;
1015}
1016
William Lallemand83614a92021-08-13 14:47:57 +02001017static int httpclient_cfg_postparser()
1018{
1019 struct logsrv *logsrv;
1020 struct proxy *curproxy = httpclient_proxy;
1021
1022 /* copy logs from "global" log list */
1023 list_for_each_entry(logsrv, &global.logsrvs, list) {
1024 struct logsrv *node = malloc(sizeof(*node));
1025
1026 if (!node) {
1027 ha_alert("httpclient: cannot allocate memory.\n");
1028 goto err;
1029 }
1030
1031 memcpy(node, logsrv, sizeof(*node));
1032 LIST_INIT(&node->list);
1033 LIST_APPEND(&curproxy->logsrvs, &node->list);
1034 }
1035 if (curproxy->conf.logformat_string) {
1036 char *err = NULL;
1037
1038 curproxy->conf.args.ctx = ARGC_LOG;
1039 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1040 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
1041 SMP_VAL_FE_LOG_END, &err)) {
1042 ha_alert("httpclient: failed to parse log-format : %s.\n", err);
1043 free(err);
1044 goto err;
1045 }
1046 curproxy->conf.args.file = NULL;
1047 curproxy->conf.args.line = 0;
1048 }
1049 return 0;
1050err:
1051 return 1;
1052}
1053
William Lallemand83614a92021-08-13 14:47:57 +02001054/* initialize the proxy and servers for the HTTP client */
1055
Willy Tarreau5b4b6ca2022-02-18 16:23:14 +01001056INITCALL0(STG_INIT, httpclient_init);
William Lallemand83614a92021-08-13 14:47:57 +02001057REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);