blob: facdfed84d8fb9013611f9dbeb814adf0ae6aca1 [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>
William Lallemandcf5cb0b2022-04-22 14:48:45 +020018#include <haproxy/ssl_ckch.h>
William Lallemand33b0d092021-08-13 16:05:53 +020019#include <haproxy/dynbuf.h>
William Lallemand83614a92021-08-13 14:47:57 +020020#include <haproxy/cfgparse.h>
Christopher Faulet908628c2022-03-25 16:43:49 +010021#include <haproxy/conn_stream.h>
22#include <haproxy/cs_utils.h>
William Lallemand83614a92021-08-13 14:47:57 +020023#include <haproxy/global.h>
William Lallemand0da616e2021-10-28 15:34:26 +020024#include <haproxy/istbuf.h>
William Lallemand33b0d092021-08-13 16:05:53 +020025#include <haproxy/h1_htx.h>
26#include <haproxy/http.h>
William Lallemand2b7dc4e2022-02-24 16:55:41 +010027#include <haproxy/http_ana-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020028#include <haproxy/http_client.h>
29#include <haproxy/http_htx.h>
William Lallemand5392ff62022-04-28 16:55:02 +020030#include <haproxy/http_rules.h>
William Lallemand33b0d092021-08-13 16:05:53 +020031#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020032#include <haproxy/log.h>
33#include <haproxy/proxy.h>
William Lallemand5392ff62022-04-28 16:55:02 +020034#include <haproxy/resolvers.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020035#include <haproxy/server.h>
Willy Tarreau1df20422021-10-06 11:28:24 +020036#include <haproxy/ssl_sock-t.h>
William Lallemand7f1df8f2022-04-14 17:50:20 +020037#include <haproxy/sock_inet.h>
William Lallemand83614a92021-08-13 14:47:57 +020038#include <haproxy/tools.h>
39
40#include <string.h>
41
42
43static struct proxy *httpclient_proxy;
44static struct server *httpclient_srv_raw;
William Lallemand6fce46a2022-05-04 14:53:41 +020045
William Lallemand957ab132021-08-24 18:33:28 +020046#ifdef USE_OPENSSL
William Lallemand6fce46a2022-05-04 14:53:41 +020047/* if the httpclient is not configured, error are ignored and features are limited */
48static int hard_error_ssl = 0;
William Lallemand83614a92021-08-13 14:47:57 +020049static struct server *httpclient_srv_ssl;
William Lallemandf1344b32022-04-26 12:00:06 +020050static int httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemand683fbb82022-05-04 15:43:01 +020051static char *httpclient_ssl_ca_file = NULL;
William Lallemand957ab132021-08-24 18:33:28 +020052#endif
William Lallemand33b0d092021-08-13 16:05:53 +020053static struct applet httpclient_applet;
54
William Lallemand7c5a7ef2022-05-04 15:59:44 +020055/* if the httpclient is not configured, error are ignored and features are limited */
William Lallemand8a734cb2022-05-04 16:10:47 +020056static int hard_error_resolvers = 0;
57static char *resolvers_id = NULL;
William Lallemand7c5a7ef2022-05-04 15:59:44 +020058static char *resolvers_prefer = NULL;
William Lallemandeaa703e2022-04-22 17:52:33 +020059
William Lallemand03a4eb12021-08-18 16:46:21 +020060/* --- This part of the file implement an HTTP client over the CLI ---
61 * The functions will be starting by "hc_cli" for "httpclient cli"
62 */
63
William Lallemand03a4eb12021-08-18 16:46:21 +020064/* What kind of data we need to read */
65#define HC_CLI_F_RES_STLINE 0x01
66#define HC_CLI_F_RES_HDR 0x02
67#define HC_CLI_F_RES_BODY 0x04
68#define HC_CLI_F_RES_END 0x08
69
70
71/* These are the callback used by the HTTP Client when it needs to notify new
72 * data, we only sets a flag in the IO handler */
73
74void hc_cli_res_stline_cb(struct httpclient *hc)
75{
76 struct appctx *appctx = hc->caller;
77
William Lallemanddfc3f892021-08-20 11:35:29 +020078 if (!appctx)
79 return;
80
William Lallemand03a4eb12021-08-18 16:46:21 +020081 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020082 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020083}
84
85void hc_cli_res_headers_cb(struct httpclient *hc)
86{
87 struct appctx *appctx = hc->caller;
88
William Lallemanddfc3f892021-08-20 11:35:29 +020089 if (!appctx)
90 return;
91
William Lallemand03a4eb12021-08-18 16:46:21 +020092 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020093 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020094}
95
96void hc_cli_res_body_cb(struct httpclient *hc)
97{
98 struct appctx *appctx = hc->caller;
99
William Lallemanddfc3f892021-08-20 11:35:29 +0200100 if (!appctx)
101 return;
102
William Lallemand03a4eb12021-08-18 16:46:21 +0200103 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +0200104 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200105}
106
107void hc_cli_res_end_cb(struct httpclient *hc)
108{
109 struct appctx *appctx = hc->caller;
110
William Lallemanddfc3f892021-08-20 11:35:29 +0200111 if (!appctx)
112 return;
113
William Lallemand03a4eb12021-08-18 16:46:21 +0200114 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200115 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200116}
117
118/*
119 * Parse an httpclient keyword on the cli:
120 * httpclient <ID> <method> <URI>
121 */
122static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
123{
124 struct httpclient *hc;
125 char *err = NULL;
126 enum http_meth_t meth;
127 char *meth_str;
128 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200129 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200130
131 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
132 return 1;
133
134 if (!*args[1] || !*args[2]) {
135 memprintf(&err, ": not enough parameters");
136 goto err;
137 }
138
139 meth_str = args[1];
140 uri = ist(args[2]);
141
William Lallemanddec25c32021-10-25 19:48:37 +0200142 if (payload)
143 body = ist(payload);
144
William Lallemand03a4eb12021-08-18 16:46:21 +0200145 meth = find_http_meth(meth_str, strlen(meth_str));
146
147 hc = httpclient_new(appctx, meth, uri);
148 if (!hc) {
149 goto err;
150 }
151
152 /* update the httpclient callbacks */
153 hc->ops.res_stline = hc_cli_res_stline_cb;
154 hc->ops.res_headers = hc_cli_res_headers_cb;
155 hc->ops.res_payload = hc_cli_res_body_cb;
156 hc->ops.res_end = hc_cli_res_end_cb;
157
158 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
159 appctx->ctx.cli.i0 = 0;
160
William Lallemandbad9c8c2022-01-14 14:10:33 +0100161 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200162 goto err;
163
164
165 if (!httpclient_start(hc))
166 goto err;
167
168 return 0;
169
170err:
171 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
172 return cli_err(appctx, err);
173}
174
175/* This function dumps the content of the httpclient receive buffer
176 * on the CLI output
177 *
178 * Return 1 when the processing is finished
179 * return 0 if it needs to be called again
180 */
181static int hc_cli_io_handler(struct appctx *appctx)
182{
Christopher Faulet908628c2022-03-25 16:43:49 +0100183 struct conn_stream *cs = appctx->owner;
William Lallemand03a4eb12021-08-18 16:46:21 +0200184 struct buffer *trash = alloc_trash_chunk();
185 struct httpclient *hc = appctx->ctx.cli.p0;
186 struct http_hdr *hdrs, *hdr;
187
188 if (!trash)
189 goto out;
190 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100191 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
192 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
Christopher Faulet908628c2022-03-25 16:43:49 +0100193 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200194 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200195 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
196 goto out;
197 }
198
199 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
200 hdrs = hc->res.hdrs;
201 for (hdr = hdrs; isttest(hdr->v); hdr++) {
202 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
203 goto out;
204 }
205 if (!chunk_memcat(trash, "\r\n", 2))
206 goto out;
Christopher Faulet908628c2022-03-25 16:43:49 +0100207 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200208 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200209 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
210 goto out;
211 }
212
213 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
214 int ret;
215
Christopher Faulet908628c2022-03-25 16:43:49 +0100216 ret = httpclient_res_xfer(hc, cs_ib(cs));
217 channel_add_input(cs_ic(cs), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200218
William Lallemand518878e2021-09-21 10:45:34 +0200219 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200220 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
221 }
222 goto out;
223 }
224
225 /* we must close only if F_END is the last flag */
226 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
Christopher Fauletda098e62022-03-31 17:44:45 +0200227 cs_shutw(cs);
228 cs_shutr(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200229 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
230 goto out;
231 }
232
233out:
234 /* we didn't clear every flags, we should come back to finish things */
235 if (appctx->ctx.cli.i0)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200236 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200237
238 free_trash_chunk(trash);
239 return 0;
240}
241
242static void hc_cli_release(struct appctx *appctx)
243{
244 struct httpclient *hc = appctx->ctx.cli.p0;
245
246 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200247 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200248
249 return;
250}
251
252/* register cli keywords */
253static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100254 { { "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 +0200255 { { NULL }, NULL, NULL, NULL }
256}};
257
258INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
259
260
261/* --- This part of the file implements the actual HTTP client API --- */
262
William Lallemand33b0d092021-08-13 16:05:53 +0200263/*
264 * Generate a simple request and fill the httpclient request buffer with it.
265 * The request contains a request line generated from the absolute <url> and
266 * <meth> as well as list of headers <hdrs>.
267 *
268 * If the buffer was filled correctly the function returns 0, if not it returns
269 * an error_code but there is no guarantee that the buffer wasn't modified.
270 */
William Lallemanddec25c32021-10-25 19:48:37 +0200271int 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 +0200272{
273 struct htx_sl *sl;
274 struct htx *htx;
275 int err_code = 0;
276 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200277 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 +0100278 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100279 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200280
Christopher Faulet600985d2022-01-12 11:14:08 +0100281 if (!b_alloc(&hc->req.buf))
282 goto error;
283
William Lallemand33b0d092021-08-13 16:05:53 +0200284 if (meth >= HTTP_METH_OTHER)
285 goto error;
286
287 meth_ist = http_known_methods[meth];
288
289 vsn = ist("HTTP/1.1");
290
291 htx = htx_from_buf(&hc->req.buf);
292 if (!htx)
293 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100294
295 if (!hc->ops.req_payload && !isttest(payload))
296 flags |= HTX_SL_F_BODYLESS;
297
William Lallemand33b0d092021-08-13 16:05:53 +0200298 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
299 if (!sl) {
300 goto error;
301 }
302 sl->info.req.meth = meth;
303
William Lallemandf03b53c2021-11-24 15:38:17 +0100304 for (i = 0; hdrs && hdrs[i].n.len; i++) {
305 /* Don't check the value length because a header value may be empty */
306 if (isttest(hdrs[i].v) == 0)
307 continue;
308
309 if (isteqi(hdrs[i].n, ist("host")))
310 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100311 else if (isteqi(hdrs[i].n, ist("accept")))
312 foundaccept = 1;
313 else if (isteqi(hdrs[i].n, ist("user-agent")))
314 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100315
316 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
317 goto error;
318 }
William Lallemand33b0d092021-08-13 16:05:53 +0200319
William Lallemandf03b53c2021-11-24 15:38:17 +0100320 if (!foundhost) {
321 /* Add Host Header from URL */
322 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200323 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100324 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200325 goto error;
326 }
William Lallemand33b0d092021-08-13 16:05:53 +0200327
William Lallemandbad9c8c2022-01-14 14:10:33 +0100328 if (!foundaccept) {
329 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
330 goto error;
331 }
332
333 if (!foundua) {
334 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
335 goto error;
336 }
337
338
William Lallemandf03b53c2021-11-24 15:38:17 +0100339 if (!htx_add_endof(htx, HTX_BLK_EOH))
340 goto error;
341
William Lallemanddec25c32021-10-25 19:48:37 +0200342 if (isttest(payload)) {
343 /* add the payload if it can feat in the buffer, no need to set
344 * the Content-Length, the data will be sent chunked */
345 if (!htx_add_data_atonce(htx, payload))
346 goto error;
347 }
348
William Lallemand0da616e2021-10-28 15:34:26 +0200349 /* If req.payload was set, does not set the end of stream which *MUST*
350 * be set in the callback */
351 if (!hc->ops.req_payload)
352 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200353
354 htx_to_buf(htx, &hc->req.buf);
355
356 return 0;
357error:
358 err_code |= ERR_ALERT | ERR_ABORT;
359 return err_code;
360}
361
362/*
363 * transfer the response to the destination buffer and wakeup the HTTP client
364 * applet so it could fill again its buffer.
365 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500366 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200367 */
368int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
369{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100370 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200371 int ret;
372
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100373 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200374 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100375 if (!b_data(&hc->res.buf)) {
376 b_free(&hc->res.buf);
377 if (hc->appctx)
378 appctx_wakeup(hc->appctx);
379 }
William Lallemand33b0d092021-08-13 16:05:53 +0200380 return ret;
381}
382
383/*
William Lallemand0da616e2021-10-28 15:34:26 +0200384 * Transfer raw HTTP payload from src, and insert it into HTX format in the
385 * httpclient.
386 *
387 * Must be used to transfer the request body.
388 * Then wakeup the httpclient so it can transfer it.
389 *
390 * <end> tries to add the ending data flag if it succeed to copy all data.
391 *
392 * Return the number of bytes copied from src.
393 */
394int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
395{
396 int ret = 0;
397 struct htx *htx;
398
Christopher Faulet600985d2022-01-12 11:14:08 +0100399 if (!b_alloc(&hc->req.buf))
400 goto error;
401
William Lallemand0da616e2021-10-28 15:34:26 +0200402 htx = htx_from_buf(&hc->req.buf);
403 if (!htx)
404 goto error;
405
406 if (hc->appctx)
407 appctx_wakeup(hc->appctx);
408
409 ret += htx_add_data(htx, src);
410
411
412 /* if we copied all the data and the end flag is set */
413 if ((istlen(src) == ret) && end) {
414 htx->flags |= HTX_FL_EOM;
415 }
416 htx_to_buf(htx, &hc->req.buf);
417
418error:
419
420 return ret;
421}
422
William Lallemandb4a4ef62022-02-23 14:18:16 +0100423/* Set the 'timeout server' in ms for the next httpclient request */
424void httpclient_set_timeout(struct httpclient *hc, int timeout)
425{
426 hc->timeout_server = timeout;
427}
428
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100429/*
430 * Sets a destination for the httpclient from an HAProxy addr format
431 * This will prevent to determine the destination from the URL
432 * Return 0 in case of success or -1 otherwise.
433 */
434int httpclient_set_dst(struct httpclient *hc, const char *dst)
435{
436 struct sockaddr_storage *sk;
437 char *errmsg = NULL;
438
439 sockaddr_free(&hc->dst);
440 /* 'sk' is statically allocated (no need to be freed). */
441 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
442 &errmsg, NULL, NULL,
443 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
444 if (!sk) {
445 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
446 free(errmsg);
447 return -1;
448 }
449
450 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
451 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
452 return -1;
453 }
454
455 return 0;
456}
William Lallemand0da616e2021-10-28 15:34:26 +0200457
458/*
William Lallemand7f1df8f2022-04-14 17:50:20 +0200459 * Return a splitted URL in <scheme>, <host>, <port>
460 */
461static void httpclient_spliturl(struct ist url, enum http_scheme *scheme,
462 struct ist *host, int *port)
463{
464 enum http_scheme scheme_tmp = SCH_HTTP;
465 int port_tmp = 0;
466 struct ist scheme_ist, authority_ist, host_ist, port_ist;
467 char *p, *end;
468 struct http_uri_parser parser;
469
470 parser = http_uri_parser_init(url);
471 scheme_ist = http_parse_scheme(&parser);
472
473 if (isteqi(scheme_ist, ist("http://"))){
474 scheme_tmp = SCH_HTTP;
475 port_tmp = 80;
476 } else if (isteqi(scheme_ist, ist("https://"))) {
477 scheme_tmp = SCH_HTTPS;
478 port_tmp = 443;
479 }
480
481 authority_ist = http_parse_authority(&parser, 1);
482 p = end = istend(authority_ist);
483
484 /* look for a port at the end of the authority */
485 while (p > istptr(authority_ist) && isdigit((unsigned char)*--p))
486 ;
487
488 if (*p == ':') {
489 host_ist = ist2(istptr(authority_ist), p - istptr(authority_ist));
490 port_ist = istnext(ist2(p, end - p));
491 ist2str(trash.area, port_ist);
492 port_tmp = atoi(trash.area);
493 } else {
494 host_ist = authority_ist;
495 }
496
497 if (scheme)
498 *scheme = scheme_tmp;
499 if (host)
500 *host = host_ist;
501 if (port)
502 *port = port_tmp;
503
504}
505
506/*
William Lallemand33b0d092021-08-13 16:05:53 +0200507 * Start the HTTP client
508 * Create the appctx, session, stream and wakeup the applet
509 *
William Lallemand33b0d092021-08-13 16:05:53 +0200510 * Return the <appctx> or NULL if it failed
511 */
512struct appctx *httpclient_start(struct httpclient *hc)
513{
514 struct applet *applet = &httpclient_applet;
515 struct appctx *appctx;
516 struct session *sess;
Christopher Faulet13a35e52021-12-20 15:34:16 +0100517 struct conn_stream *cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200518 struct stream *s;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100519 struct sockaddr_storage *addr = NULL;
William Lallemand7f1df8f2022-04-14 17:50:20 +0200520 struct sockaddr_storage ss_url = {};
521 struct sockaddr_storage *ss_dst;
William Lallemand4006b0f2022-04-25 18:23:35 +0200522 enum obj_type *target = NULL;
William Lallemand7f1df8f2022-04-14 17:50:20 +0200523 struct ist host = IST_NULL;
524 enum http_scheme scheme;
525 int port;
William Lallemand5392ff62022-04-28 16:55:02 +0200526 int doresolve = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200527
William Lallemand5085bc32022-02-17 12:52:09 +0100528 /* if the client was started and not ended, an applet is already
529 * running, we shouldn't try anything */
530 if (httpclient_started(hc) && !httpclient_ended(hc))
531 return NULL;
532
533 hc->flags = 0;
534
William Lallemand7f1df8f2022-04-14 17:50:20 +0200535 /* parse the URL and */
536 httpclient_spliturl(hc->req.url, &scheme, &host, &port);
537
William Lallemand85332732022-05-04 10:59:51 +0200538 if (hc->dst) {
539 /* if httpclient_set_dst() was used, sets the alternative address */
540 ss_dst = hc->dst;
541 } else {
542 /* set the dst using the host, or 0.0.0.0 to resolve */
543
544 ist2str(trash.area, host);
545 ss_dst = str2ip2(trash.area, &ss_url, 0);
546 if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
547 doresolve = 1;
548 ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
549 }
550 sock_inet_set_port(ss_dst, port);
551 }
William Lallemand33b0d092021-08-13 16:05:53 +0200552
553 /* The HTTP client will be created in the same thread as the caller,
554 * avoiding threading issues */
Christopher Faulet9ec2f4d2022-03-23 15:15:29 +0100555 appctx = appctx_new(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200556 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100557 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200558
559 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
560 if (!sess) {
561 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
562 goto out_free_appctx;
563 }
Christopher Faulet2479e5f2022-01-19 14:50:11 +0100564
William Lallemand4006b0f2022-04-25 18:23:35 +0200565 /* choose the SSL server or not */
William Lallemand7f1df8f2022-04-14 17:50:20 +0200566 switch (scheme) {
William Lallemand4006b0f2022-04-25 18:23:35 +0200567 case SCH_HTTP:
568 target = &httpclient_srv_raw->obj_type;
569 break;
570 case SCH_HTTPS:
571#ifdef USE_OPENSSL
572 if (httpclient_srv_ssl) {
573 target = &httpclient_srv_ssl->obj_type;
574 } else {
575 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
576 goto out_free_sess;
577 }
578#else
579 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
580 goto out_free_sess;
581#endif
582 break;
583 }
584
William Lallemand5392ff62022-04-28 16:55:02 +0200585 if (!ss_dst) {
586 ha_alert("httpclient: Failed to initialize address %s:%d.\n", __FUNCTION__, __LINE__);
587 goto out_free_sess;
588 }
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100589
William Lallemand85332732022-05-04 10:59:51 +0200590 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100591 goto out_free_sess;
592
Christopher Faulet9ec2f4d2022-03-23 15:15:29 +0100593 cs = cs_new_from_applet(appctx->endp, sess, &hc->req.buf);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100594 if (!cs) {
595 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100596 goto out_free_addr;
William Lallemand33b0d092021-08-13 16:05:53 +0200597 }
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100598 s = DISGUISE(cs_strm(cs));
599
William Lallemand4006b0f2022-04-25 18:23:35 +0200600 s->target = target;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100601 /* set the "timeout server" */
602 s->req.wto = hc->timeout_server;
603 s->res.rto = hc->timeout_server;
William Lallemand33b0d092021-08-13 16:05:53 +0200604
William Lallemand85332732022-05-04 10:59:51 +0200605 if (doresolve) {
606 /* in order to do the set-dst we need to put the address on the front */
607 s->csf->dst = addr;
608 } else {
609 /* in cases we don't use the resolve we already have the address
610 * and must put it on the backend side, some of the cases are
611 * not meant to be used on the frontend (sockpair, unix socket etc.) */
612 s->csb->dst = addr;
613 }
614
615 s->csb->flags |= CS_FL_NOLINGER;
Willy Tarreau03bd3952022-05-02 16:36:47 +0200616 s->flags |= SF_ASSIGNED;
William Lallemand33b0d092021-08-13 16:05:53 +0200617 s->res.flags |= CF_READ_DONTWAIT;
618
619 /* applet is waiting for data */
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200620 cs_cant_get(s->csf);
William Lallemand33b0d092021-08-13 16:05:53 +0200621 appctx_wakeup(appctx);
622
William Lallemand33b0d092021-08-13 16:05:53 +0200623 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200624 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200625 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100626
627 /* The request was transferred when the stream was created. So switch
628 * directly to REQ_BODY or RES_STLINE state
629 */
630 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200631
632 return appctx;
633
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100634out_free_addr:
635 sockaddr_free(&addr);
William Lallemand33b0d092021-08-13 16:05:53 +0200636out_free_sess:
637 session_free(sess);
638out_free_appctx:
639 appctx_free(appctx);
640out:
641
642 return NULL;
643}
644
William Lallemandecb83e12021-09-28 11:00:43 +0200645/*
646 * This function tries to destroy the httpclient if it wasn't running.
647 * If it was running, stop the client and ask it to autodestroy itself.
648 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500649 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200650 *
651 */
652void httpclient_stop_and_destroy(struct httpclient *hc)
653{
654
William Lallemandb8b13702021-09-28 12:15:37 +0200655 /* The httpclient was already stopped or never started, we can safely destroy it */
656 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200657 httpclient_destroy(hc);
658 } else {
659 /* if the client wasn't stopped, ask for a stop and destroy */
660 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
661 if (hc->appctx)
662 appctx_wakeup(hc->appctx);
663 }
664}
665
William Lallemand33b0d092021-08-13 16:05:53 +0200666/* Free the httpclient */
667void httpclient_destroy(struct httpclient *hc)
668{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200669 struct http_hdr *hdrs;
670
671
William Lallemand33b0d092021-08-13 16:05:53 +0200672 if (!hc)
673 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200674
William Lallemand2a879002021-10-05 15:50:45 +0200675 /* we should never destroy a client which was started but not stopped */
676 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200677
William Lallemand03f5a1c2021-09-27 15:17:47 +0200678 /* request */
679 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200680 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200681 /* response */
682 istfree(&hc->res.vsn);
683 istfree(&hc->res.reason);
684 hdrs = hc->res.hdrs;
685 while (hdrs && isttest(hdrs->n)) {
686 istfree(&hdrs->n);
687 istfree(&hdrs->v);
688 hdrs++;
689 }
690 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200691 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100692 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200693
William Lallemand33b0d092021-08-13 16:05:53 +0200694 free(hc);
695
696 return;
697}
698
699/* Allocate an httpclient and its buffers
700 * Return NULL on failure */
701struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
702{
703 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200704
705 hc = calloc(1, sizeof(*hc));
706 if (!hc)
707 goto err;
708
Christopher Faulet600985d2022-01-12 11:14:08 +0100709 hc->req.buf = BUF_NULL;
710 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200711 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100712 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200713 hc->req.meth = meth;
714
715 return hc;
716
717err:
718 httpclient_destroy(hc);
719 return NULL;
720}
721
722static void httpclient_applet_io_handler(struct appctx *appctx)
723{
724 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet908628c2022-03-25 16:43:49 +0100725 struct conn_stream *cs = appctx->owner;
726 struct stream *s = __cs_strm(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200727 struct channel *req = &s->req;
728 struct channel *res = &s->res;
729 struct htx_blk *blk = NULL;
730 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200731 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200732 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100733 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100734 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200735
William Lallemand33b0d092021-08-13 16:05:53 +0200736 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200737
738 /* required to stop */
739 if (hc->flags & HTTPCLIENT_FA_STOP)
740 goto end;
741
William Lallemand33b0d092021-08-13 16:05:53 +0200742 switch(appctx->st0) {
743
744 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100745 /* we know that the buffer is empty here, since
746 * it's the first call, we can freely copy the
747 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100748 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100749 if (!ret)
750 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100751
Christopher Faulet600985d2022-01-12 11:14:08 +0100752 if (!b_data(&hc->req.buf))
753 b_free(&hc->req.buf);
754
William Lallemanddb8a1f32021-11-08 16:55:14 +0100755 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100756 if (!htx)
757 goto more;
758
William Lallemanddb8a1f32021-11-08 16:55:14 +0100759 channel_add_input(req, htx->data);
760
William Lallemand933fe392021-11-04 09:45:58 +0100761 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
762 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
763 else
764 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
765
William Lallemand33b0d092021-08-13 16:05:53 +0200766 goto more; /* we need to leave the IO handler once we wrote the request */
767 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200768 case HTTPCLIENT_S_REQ_BODY:
769 /* call the payload callback */
770 {
771 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100772 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200773
William Lallemand0da616e2021-10-28 15:34:26 +0200774 /* call the request callback */
775 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100776
William Lallemandccc7ee42022-03-18 17:57:15 +0100777 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100778 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100779
William Lallemandccc7ee42022-03-18 17:57:15 +0100780 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100781 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100782
William Lallemandccc7ee42022-03-18 17:57:15 +0100783 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200784 size_t data = hc_htx->data;
785
William Lallemandccc7ee42022-03-18 17:57:15 +0100786 /* Here htx_to_buf() will set buffer data to 0 because
787 * the HTX is empty, and allow us to do an xfer.
788 */
789 htx_to_buf(hc_htx, &hc->req.buf);
790 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100791 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200792 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100793 } else {
794 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100795
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200796 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100797 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100798
William Lallemandccc7ee42022-03-18 17:57:15 +0100799 /* we must copy the EOM if we empty the buffer */
800 if (htx_is_empty(hc_htx)) {
801 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
802 }
803 htx_to_buf(htx, &req->buf);
804 htx_to_buf(hc_htx, &hc->req.buf);
805 }
806
807
808 if (!b_data(&hc->req.buf))
809 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200810 }
811
William Lallemanddb8a1f32021-11-08 16:55:14 +0100812 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200813 if (!htx)
814 goto more;
815
816 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100817 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet908628c2022-03-25 16:43:49 +0100818 cs->endp->flags |= CS_EP_EOI;
Christopher Faulet3d433242022-03-03 15:38:39 +0100819 req->flags |= CF_EOI;
William Lallemand0da616e2021-10-28 15:34:26 +0200820 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100821 }
William Lallemand0da616e2021-10-28 15:34:26 +0200822
William Lallemand1eca8942022-03-17 14:57:23 +0100823 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200824 }
825 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200826
827 case HTTPCLIENT_S_RES_STLINE:
828 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100829 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200830 goto more;
831 htx = htxbuf(&res->buf);
832 if (!htx)
833 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100834 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200835 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
836 sl = htx_get_blk_ptr(htx, blk);
837 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
838 goto more;
839
840 /* copy the status line in the httpclient */
841 hc->res.status = sl->info.res.status;
842 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
843 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100844 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200845 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100846 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200847 /* caller callback */
848 if (hc->ops.res_stline)
849 hc->ops.res_stline(hc);
850
851 /* if there is no HTX data anymore and the EOM flag is
852 * set, leave (no body) */
853 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
854 appctx->st0 = HTTPCLIENT_S_RES_END;
855 else
856 appctx->st0 = HTTPCLIENT_S_RES_HDR;
857 break;
858
859 case HTTPCLIENT_S_RES_HDR:
860 /* first copy the headers in a local hdrs
861 * structure, once we the total numbers of the
862 * header we allocate the right size and copy
863 * them. The htx block of the headers are
864 * removed each time one is read */
865 {
866 struct http_hdr hdrs[global.tune.max_http_hdr];
867
William Lallemanda625b032022-03-17 14:45:46 +0100868 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200869 goto more;
870 htx = htxbuf(&res->buf);
871 if (!htx)
872 goto more;
873
874 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200875 blk = htx_get_head_blk(htx);
876 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200877 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100878 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200879
Christopher Faulet534645d2022-04-29 13:44:46 +0200880 c_rew(res, sz);
881 blk = htx_remove_blk(htx, blk);
William Lallemandc020b252022-03-09 18:56:02 +0100882
Christopher Faulet534645d2022-04-29 13:44:46 +0200883 if (type == HTX_BLK_UNUSED)
884 continue;
885 else if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100886 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
887 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100888 hdr_num++;
889 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200890 else if (type == HTX_BLK_EOH) {
891 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200892 hdrs[hdr_num].n = IST_NULL;
893 hdrs[hdr_num].v = IST_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200894 break;
895 }
William Lallemand33b0d092021-08-13 16:05:53 +0200896 }
897
William Lallemand0d6f7792021-08-20 11:59:49 +0200898 if (hdr_num) {
899 /* alloc and copy the headers in the httpclient struct */
900 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
901 if (!hc->res.hdrs)
902 goto end;
903 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200904
William Lallemand0d6f7792021-08-20 11:59:49 +0200905 /* caller callback */
906 if (hc->ops.res_headers)
907 hc->ops.res_headers(hc);
908 }
William Lallemand33b0d092021-08-13 16:05:53 +0200909
910 /* if there is no HTX data anymore and the EOM flag is
911 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200912 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200913 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200914 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200915 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200916 }
William Lallemand33b0d092021-08-13 16:05:53 +0200917 }
918 break;
919
920 case HTTPCLIENT_S_RES_BODY:
921 /*
922 * The IO handler removes the htx blocks in the response buffer and
923 * push them in the hc->res.buf buffer in a raw format.
924 */
William Lallemanda625b032022-03-17 14:45:46 +0100925 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100926 goto more;
927
William Lallemand33b0d092021-08-13 16:05:53 +0200928 htx = htxbuf(&res->buf);
929 if (!htx || htx_is_empty(htx))
930 goto more;
931
Christopher Faulet600985d2022-01-12 11:14:08 +0100932 if (!b_alloc(&hc->res.buf))
933 goto more;
934
William Lallemand33b0d092021-08-13 16:05:53 +0200935 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100936 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200937
938 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200939 blk = htx_get_head_blk(htx);
940 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100941 enum htx_blk_type type = htx_get_blk_type(blk);
942 size_t count = co_data(res);
943 uint32_t blksz = htx_get_blksz(blk);
944 uint32_t room = b_room(&hc->res.buf);
945 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200946
William Lallemandc8f1eb92022-03-09 11:58:51 +0100947 /* we should try to copy the maximum output data in a block, which fit
948 * the destination buffer */
949 vlen = MIN(count, blksz);
950 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100951
William Lallemandc8f1eb92022-03-09 11:58:51 +0100952 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100953 goto process_data;
954
William Lallemand33b0d092021-08-13 16:05:53 +0200955 if (type == HTX_BLK_DATA) {
956 struct ist v = htx_get_blk_value(htx, blk);
957
William Lallemandc8f1eb92022-03-09 11:58:51 +0100958 __b_putblk(&hc->res.buf, v.ptr, vlen);
959 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200960
William Lallemandc8f1eb92022-03-09 11:58:51 +0100961 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200962 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100963 else
964 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100965
William Lallemand33b0d092021-08-13 16:05:53 +0200966 /* the data must be processed by the caller in the receive phase */
967 if (hc->ops.res_payload)
968 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100969
970 /* cannot copy everything, need to processs */
971 if (vlen != blksz)
972 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200973 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100974 if (vlen != blksz)
975 goto process_data;
976
William Lallemand33b0d092021-08-13 16:05:53 +0200977 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100978 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200979 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200980 }
981 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100982
William Lallemand33b0d092021-08-13 16:05:53 +0200983 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100984 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200985 goto more;
986
William Lallemandc8f1eb92022-03-09 11:58:51 +0100987
William Lallemand33b0d092021-08-13 16:05:53 +0200988 /* end of message, we should quit */
989 appctx->st0 = HTTPCLIENT_S_RES_END;
990 break;
991
992 case HTTPCLIENT_S_RES_END:
993 goto end;
994 break;
995 }
996 }
997
998process_data:
999
Christopher Fauleta0bdec32022-04-04 07:51:21 +02001000 cs_rx_chan_rdy(cs);
William Lallemand33b0d092021-08-13 16:05:53 +02001001
1002 return;
1003more:
1004 /* There was not enough data in the response channel */
1005
Christopher Fauleta0bdec32022-04-04 07:51:21 +02001006 cs_rx_room_blk(cs);
William Lallemand33b0d092021-08-13 16:05:53 +02001007
1008 if (appctx->st0 == HTTPCLIENT_S_RES_END)
1009 goto end;
1010
1011 /* The state machine tries to handle as much data as possible, if there
1012 * isn't any data to handle and a shutdown is detected, let's stop
1013 * everything */
1014 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +01001015 (res->flags & CF_SHUTW) ||
1016 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +02001017 goto end;
1018 }
1019 return;
1020
1021end:
Christopher Fauletda098e62022-03-31 17:44:45 +02001022 cs_shutw(cs);
1023 cs_shutr(cs);
William Lallemand33b0d092021-08-13 16:05:53 +02001024 return;
1025}
1026
1027static void httpclient_applet_release(struct appctx *appctx)
1028{
1029 struct httpclient *hc = appctx->ctx.httpclient.ptr;
1030
William Lallemand1123dde2021-09-21 10:58:10 +02001031 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +02001032 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +02001033 /* the applet is leaving, remove the ptr so we don't try to call it
1034 * again from the caller */
1035 hc->appctx = NULL;
1036
William Lallemandeb0d4c42022-04-06 14:12:37 +02001037 if (hc->ops.res_end)
1038 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +02001039
1040 /* destroy the httpclient when set to autotokill */
1041 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
1042 httpclient_destroy(hc);
1043 }
1044
William Lallemand33b0d092021-08-13 16:05:53 +02001045 return;
1046}
1047
1048/* HTTP client applet */
1049static struct applet httpclient_applet = {
1050 .obj_type = OBJ_TYPE_APPLET,
1051 .name = "<HTTPCLIENT>",
1052 .fct = httpclient_applet_io_handler,
1053 .release = httpclient_applet_release,
1054};
1055
William Lallemand5392ff62022-04-28 16:55:02 +02001056
1057static int httpclient_resolve_init()
1058{
1059 struct act_rule *rule;
1060 int i;
William Lallemand8a734cb2022-05-04 16:10:47 +02001061 char *do_resolve = NULL;
1062 char *http_rules[][11] = {
William Lallemand5392ff62022-04-28 16:55:02 +02001063 { "set-var(txn.hc_ip)", "dst", "" },
William Lallemand8a734cb2022-05-04 16:10:47 +02001064 { do_resolve, "hdr(Host),lower", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
William Lallemand5392ff62022-04-28 16:55:02 +02001065 { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
1066 { "capture", "var(txn.hc_ip)", "len", "40", "" },
1067 { "set-dst", "var(txn.hc_ip)", "" },
1068 { "" }
1069 };
1070
William Lallemand8a734cb2022-05-04 16:10:47 +02001071 if (!resolvers_id)
1072 resolvers_id = strdup("default");
1073
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001074 memprintf(&do_resolve, "do-resolve(txn.hc_ip,%s%s%s)", resolvers_id, resolvers_prefer ? "," : "", resolvers_prefer ? resolvers_prefer : "");
William Lallemand8a734cb2022-05-04 16:10:47 +02001075 http_rules[1][0] = do_resolve;
1076
William Lallemand7867f632022-05-05 19:02:59 +02001077 /* Try to create the default resolvers section */
1078 resolvers_create_default();
1079
William Lallemand8a734cb2022-05-04 16:10:47 +02001080 /* if the resolver does not exist and no hard_error was set, simply ignore resolving */
1081 if (!find_resolvers_by_id(resolvers_id) && !hard_error_resolvers) {
1082 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001083 return 0;
William Lallemand8a734cb2022-05-04 16:10:47 +02001084 }
William Lallemand5392ff62022-04-28 16:55:02 +02001085
1086
1087 for (i = 0; *http_rules[i][0] != '\0'; i++) {
1088 rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, httpclient_proxy);
1089 if (!rule) {
William Lallemand8a734cb2022-05-04 16:10:47 +02001090 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001091 ha_alert("Couldn't setup the httpclient resolver.\n");
1092 return 1;
1093 }
1094 LIST_APPEND(&httpclient_proxy->http_req_rules, &rule->list);
1095 }
1096
William Lallemand8a734cb2022-05-04 16:10:47 +02001097 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001098 return 0;
1099}
1100
1101
1102
William Lallemand83614a92021-08-13 14:47:57 +02001103/*
1104 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
1105 * the other for HTTPS.
1106 */
William Lallemand2c8b0842022-04-22 15:16:09 +02001107static int httpclient_precheck()
William Lallemand83614a92021-08-13 14:47:57 +02001108{
William Lallemand85af49c2022-05-04 14:33:57 +02001109 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001110 char *errmsg = NULL;
1111
William Lallemandc6ceba32022-04-22 16:49:53 +02001112 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001113 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001114
William Lallemand83614a92021-08-13 14:47:57 +02001115 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
1116 if (!httpclient_proxy) {
William Lallemand85af49c2022-05-04 14:33:57 +02001117 memprintf(&errmsg, "couldn't allocate proxy.");
William Lallemand83614a92021-08-13 14:47:57 +02001118 err_code |= ERR_ALERT | ERR_FATAL;
1119 goto err;
1120 }
1121
Willy Tarreau0e72e402021-08-20 10:23:12 +02001122 proxy_preset_defaults(httpclient_proxy);
1123
William Lallemandccc7ee42022-03-18 17:57:15 +01001124 httpclient_proxy->options |= PR_O_WREQ_BODY;
William Lallemand71abad02022-03-17 15:24:28 +01001125 httpclient_proxy->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
William Lallemand83614a92021-08-13 14:47:57 +02001126 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
1127 httpclient_proxy->mode = PR_MODE_HTTP;
1128 httpclient_proxy->maxconn = 0;
1129 httpclient_proxy->accept = NULL;
William Lallemand71abad02022-03-17 15:24:28 +01001130 httpclient_proxy->conn_retries = CONN_RETRIES;
William Lallemand83614a92021-08-13 14:47:57 +02001131 httpclient_proxy->timeout.client = TICK_ETERNITY;
1132 /* The HTTP Client use the "option httplog" with the global log server */
1133 httpclient_proxy->conf.logformat_string = default_http_log_format;
1134 httpclient_proxy->http_needed = 1;
1135
1136 /* clear HTTP server */
1137 httpclient_srv_raw = new_server(httpclient_proxy);
1138 if (!httpclient_srv_raw) {
William Lallemand83614a92021-08-13 14:47:57 +02001139 memprintf(&errmsg, "out of memory.");
William Lallemand85af49c2022-05-04 14:33:57 +02001140 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001141 goto err;
1142 }
1143
1144 httpclient_srv_raw->iweight = 0;
1145 httpclient_srv_raw->uweight = 0;
1146 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
William Lallemand1218d192022-05-03 14:09:06 +02001147 httpclient_srv_raw->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
William Lallemand83614a92021-08-13 14:47:57 +02001148 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
William Lallemand85af49c2022-05-04 14:33:57 +02001149 if (!httpclient_srv_raw->id) {
1150 memprintf(&errmsg, "out of memory.");
1151 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001152 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001153 }
William Lallemand83614a92021-08-13 14:47:57 +02001154
William Lallemand957ab132021-08-24 18:33:28 +02001155#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001156 /* SSL HTTP server */
1157 httpclient_srv_ssl = new_server(httpclient_proxy);
1158 if (!httpclient_srv_ssl) {
1159 memprintf(&errmsg, "out of memory.");
1160 err_code |= ERR_ALERT | ERR_FATAL;
1161 goto err;
1162 }
1163 httpclient_srv_ssl->iweight = 0;
1164 httpclient_srv_ssl->uweight = 0;
1165 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
1166 httpclient_srv_ssl->use_ssl = 1;
William Lallemand1218d192022-05-03 14:09:06 +02001167 httpclient_srv_ssl->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
William Lallemand211c9672021-08-24 17:18:13 +02001168 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand85af49c2022-05-04 14:33:57 +02001169 if (!httpclient_srv_ssl->id) {
1170 memprintf(&errmsg, "out of memory.");
1171 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001172 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001173 }
William Lallemand83614a92021-08-13 14:47:57 +02001174
William Lallemandeaa703e2022-04-22 17:52:33 +02001175 httpclient_srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001176 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001177 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
William Lallemand683fbb82022-05-04 15:43:01 +02001178
1179 if (!httpclient_ssl_ca_file)
1180 httpclient_ssl_ca_file = strdup("@system-ca");
1181
1182 httpclient_srv_ssl->ssl_ctx.ca_file = httpclient_ssl_ca_file;
William Lallemand4006b0f2022-04-25 18:23:35 +02001183 if (!ssl_store_load_locations_file(httpclient_srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT)) {
William Lallemand6fce46a2022-05-04 14:53:41 +02001184 /* if we failed to load the ca-file, only quits in
1185 * error with hard_error, otherwise just disable the
1186 * feature. */
1187 if (hard_error_ssl) {
1188 memprintf(&errmsg, "cannot initialize SSL verify with 'ca-file \"%s\"'.", httpclient_srv_ssl->ssl_ctx.ca_file);
1189 err_code |= ERR_ALERT | ERR_FATAL;
1190 goto err;
1191 } else {
1192 ha_free(&httpclient_srv_ssl->ssl_ctx.ca_file);
1193 srv_drop(httpclient_srv_ssl);
1194 httpclient_srv_ssl = NULL;
1195 }
William Lallemand4006b0f2022-04-25 18:23:35 +02001196 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001197 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001198
William Lallemand957ab132021-08-24 18:33:28 +02001199#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001200
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001201 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +02001202 httpclient_proxy->next = proxies_list;
1203 proxies_list = httpclient_proxy;
1204
William Lallemand85af49c2022-05-04 14:33:57 +02001205 if (httpclient_resolve_init() != 0) {
1206 memprintf(&errmsg, "cannot initialize resolvers.");
1207 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand5392ff62022-04-28 16:55:02 +02001208 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001209 }
William Lallemand5392ff62022-04-28 16:55:02 +02001210
William Lallemand211c9672021-08-24 17:18:13 +02001211 /* link the 2 servers in the proxy */
1212 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +02001213 httpclient_proxy->srv = httpclient_srv_raw;
1214
1215#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001216 if (httpclient_srv_ssl) {
1217 httpclient_srv_ssl->next = httpclient_proxy->srv;
1218 httpclient_proxy->srv = httpclient_srv_ssl;
1219 }
William Lallemand957ab132021-08-24 18:33:28 +02001220#endif
1221
William Lallemand211c9672021-08-24 17:18:13 +02001222
William Lallemand83614a92021-08-13 14:47:57 +02001223err:
William Lallemand85af49c2022-05-04 14:33:57 +02001224 if (err_code & ERR_CODE) {
1225 ha_alert("httpclient: cannot initialize: %s\n", errmsg);
1226 free(errmsg);
1227 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001228#ifdef USE_OPENSSL
William Lallemand85af49c2022-05-04 14:33:57 +02001229 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001230#endif
William Lallemand85af49c2022-05-04 14:33:57 +02001231 free_proxy(httpclient_proxy);
1232 }
William Lallemand83614a92021-08-13 14:47:57 +02001233 return err_code;
1234}
1235
William Lallemand2c8b0842022-04-22 15:16:09 +02001236static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001237{
William Lallemand85af49c2022-05-04 14:33:57 +02001238 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001239 struct logsrv *logsrv;
1240 struct proxy *curproxy = httpclient_proxy;
William Lallemand71e31582022-03-16 15:47:47 +01001241 char *errmsg = NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001242
William Lallemandc6ceba32022-04-22 16:49:53 +02001243 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001244 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001245
William Lallemand83614a92021-08-13 14:47:57 +02001246 /* copy logs from "global" log list */
1247 list_for_each_entry(logsrv, &global.logsrvs, list) {
1248 struct logsrv *node = malloc(sizeof(*node));
1249
1250 if (!node) {
William Lallemand85af49c2022-05-04 14:33:57 +02001251 memprintf(&errmsg, "out of memory.");
1252 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001253 goto err;
1254 }
1255
1256 memcpy(node, logsrv, sizeof(*node));
1257 LIST_INIT(&node->list);
1258 LIST_APPEND(&curproxy->logsrvs, &node->list);
Willy Tarreaue1e9f6b2022-04-21 14:14:28 +02001259 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1260 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001261 }
1262 if (curproxy->conf.logformat_string) {
William Lallemand83614a92021-08-13 14:47:57 +02001263 curproxy->conf.args.ctx = ARGC_LOG;
1264 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1265 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
William Lallemand715c1012022-03-16 16:39:23 +01001266 SMP_VAL_FE_LOG_END, &errmsg)) {
William Lallemand85af49c2022-05-04 14:33:57 +02001267 memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
1268 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001269 goto err;
1270 }
1271 curproxy->conf.args.file = NULL;
1272 curproxy->conf.args.line = 0;
1273 }
William Lallemand71e31582022-03-16 15:47:47 +01001274
1275#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001276 if (httpclient_srv_ssl) {
William Lallemand715c1012022-03-16 16:39:23 +01001277 /* init the SNI expression */
1278 /* always use the host header as SNI, without the port */
1279 httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1280 err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
1281 if (err_code & ERR_CODE) {
William Lallemand85af49c2022-05-04 14:33:57 +02001282 memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
William Lallemand715c1012022-03-16 16:39:23 +01001283 goto err;
1284 }
William Lallemand71e31582022-03-16 15:47:47 +01001285 }
1286#endif
1287
William Lallemand83614a92021-08-13 14:47:57 +02001288err:
William Lallemand85af49c2022-05-04 14:33:57 +02001289 if (err_code & ERR_CODE) {
1290 ha_alert("httpclient: failed to initialize: %s\n", errmsg);
1291 free(errmsg);
1292
1293 }
1294 return err_code;
William Lallemand83614a92021-08-13 14:47:57 +02001295}
1296
William Lallemand83614a92021-08-13 14:47:57 +02001297/* initialize the proxy and servers for the HTTP client */
1298
William Lallemand2c8b0842022-04-22 15:16:09 +02001299REGISTER_PRE_CHECK(httpclient_precheck);
1300REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001301
William Lallemand8a734cb2022-05-04 16:10:47 +02001302static int httpclient_parse_global_resolvers(char **args, int section_type, struct proxy *curpx,
1303 const struct proxy *defpx, const char *file, int line,
1304 char **err)
1305{
1306 if (too_many_args(1, args, err, NULL))
1307 return -1;
1308
1309 /* any configuration should set the hard_error flag */
1310 hard_error_resolvers = 1;
1311
1312 free(resolvers_id);
1313 resolvers_id = strdup(args[1]);
1314
1315 return 0;
1316}
1317
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001318static int httpclient_parse_global_prefer(char **args, int section_type, struct proxy *curpx,
1319 const struct proxy *defpx, const char *file, int line,
1320 char **err)
1321{
1322 if (too_many_args(1, args, err, NULL))
1323 return -1;
1324
1325 /* any configuration should set the hard_error flag */
1326 hard_error_resolvers = 1;
1327
1328
1329 if (strcmp(args[1],"ipv4") == 0)
1330 resolvers_prefer = "ipv4";
1331 else if (strcmp(args[1],"ipv6") == 0)
1332 resolvers_prefer = "ipv6";
1333 else {
1334 ha_alert("parsing [%s:%d] : '%s' expects 'ipv4' or 'ipv6' as argument.\n", file, line, args[0]);
1335 return -1;
1336 }
1337
1338 return 0;
1339}
William Lallemand8a734cb2022-05-04 16:10:47 +02001340
1341
William Lallemandeaa703e2022-04-22 17:52:33 +02001342#ifdef USE_OPENSSL
William Lallemand683fbb82022-05-04 15:43:01 +02001343static int httpclient_parse_global_ca_file(char **args, int section_type, struct proxy *curpx,
1344 const struct proxy *defpx, const char *file, int line,
1345 char **err)
1346{
1347 if (too_many_args(1, args, err, NULL))
1348 return -1;
1349
1350 /* any configuration should set the hard_error flag */
1351 hard_error_ssl = 1;
1352
1353 free(httpclient_ssl_ca_file);
1354 httpclient_ssl_ca_file = strdup(args[1]);
1355
1356 return 0;
1357}
1358
William Lallemandeaa703e2022-04-22 17:52:33 +02001359static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1360 const struct proxy *defpx, const char *file, int line,
1361 char **err)
1362{
1363 if (too_many_args(1, args, err, NULL))
1364 return -1;
1365
William Lallemand6fce46a2022-05-04 14:53:41 +02001366 /* any configuration should set the hard_error flag */
1367 hard_error_ssl = 1;
1368
William Lallemandeaa703e2022-04-22 17:52:33 +02001369 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001370 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001371 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001372 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001373 else {
1374 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1375 return -1;
1376 }
1377
1378 return 0;
1379}
William Lallemand8a734cb2022-05-04 16:10:47 +02001380#endif /* ! USE_OPENSSL */
William Lallemandeaa703e2022-04-22 17:52:33 +02001381
1382static struct cfg_kw_list cfg_kws = {ILH, {
William Lallemand8a734cb2022-05-04 16:10:47 +02001383 { CFG_GLOBAL, "httpclient.resolvers.id", httpclient_parse_global_resolvers },
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001384 { CFG_GLOBAL, "httpclient.resolvers.prefer", httpclient_parse_global_prefer },
William Lallemand8a734cb2022-05-04 16:10:47 +02001385#ifdef USE_OPENSSL
William Lallemand9ff95e22022-05-04 13:52:29 +02001386 { CFG_GLOBAL, "httpclient.ssl.verify", httpclient_parse_global_verify },
William Lallemand683fbb82022-05-04 15:43:01 +02001387 { CFG_GLOBAL, "httpclient.ssl.ca-file", httpclient_parse_global_ca_file },
William Lallemand8a734cb2022-05-04 16:10:47 +02001388#endif
William Lallemandeaa703e2022-04-22 17:52:33 +02001389 { 0, NULL, NULL },
1390}};
1391
1392INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);