blob: 53086a3aa1b037ce26772f66cc66eb651b369c87 [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
Willy Tarreau89a7c412022-05-05 19:38:21 +020070/* the CLI context for the httpclient command */
71struct hcli_svc_ctx {
72 struct httpclient *hc; /* the httpclient instance */
73 uint flags; /* flags from HC_CLI_F_* above */
74};
William Lallemand03a4eb12021-08-18 16:46:21 +020075
76/* These are the callback used by the HTTP Client when it needs to notify new
Willy Tarreau89a7c412022-05-05 19:38:21 +020077 * data, we only sets a flag in the IO handler via the svcctx.
78 */
William Lallemand03a4eb12021-08-18 16:46:21 +020079void hc_cli_res_stline_cb(struct httpclient *hc)
80{
81 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020082 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020083
William Lallemanddfc3f892021-08-20 11:35:29 +020084 if (!appctx)
85 return;
86
Willy Tarreau89a7c412022-05-05 19:38:21 +020087 ctx = appctx->svcctx;
88 ctx->flags |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020089 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020090}
91
92void hc_cli_res_headers_cb(struct httpclient *hc)
93{
94 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020095 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020096
William Lallemanddfc3f892021-08-20 11:35:29 +020097 if (!appctx)
98 return;
99
Willy Tarreau89a7c412022-05-05 19:38:21 +0200100 ctx = appctx->svcctx;
101 ctx->flags |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +0200102 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200103}
104
105void hc_cli_res_body_cb(struct httpclient *hc)
106{
107 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200108 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200109
William Lallemanddfc3f892021-08-20 11:35:29 +0200110 if (!appctx)
111 return;
112
Willy Tarreau89a7c412022-05-05 19:38:21 +0200113 ctx = appctx->svcctx;
114 ctx->flags |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +0200115 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200116}
117
118void hc_cli_res_end_cb(struct httpclient *hc)
119{
120 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200121 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200122
William Lallemanddfc3f892021-08-20 11:35:29 +0200123 if (!appctx)
124 return;
125
Willy Tarreau89a7c412022-05-05 19:38:21 +0200126 ctx = appctx->svcctx;
127 ctx->flags |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200128 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200129}
130
131/*
132 * Parse an httpclient keyword on the cli:
133 * httpclient <ID> <method> <URI>
134 */
135static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
136{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200137 struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemand03a4eb12021-08-18 16:46:21 +0200138 struct httpclient *hc;
139 char *err = NULL;
140 enum http_meth_t meth;
141 char *meth_str;
142 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200143 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200144
145 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
146 return 1;
147
148 if (!*args[1] || !*args[2]) {
149 memprintf(&err, ": not enough parameters");
150 goto err;
151 }
152
153 meth_str = args[1];
154 uri = ist(args[2]);
155
William Lallemanddec25c32021-10-25 19:48:37 +0200156 if (payload)
157 body = ist(payload);
158
William Lallemand03a4eb12021-08-18 16:46:21 +0200159 meth = find_http_meth(meth_str, strlen(meth_str));
160
161 hc = httpclient_new(appctx, meth, uri);
162 if (!hc) {
163 goto err;
164 }
165
166 /* update the httpclient callbacks */
167 hc->ops.res_stline = hc_cli_res_stline_cb;
168 hc->ops.res_headers = hc_cli_res_headers_cb;
169 hc->ops.res_payload = hc_cli_res_body_cb;
170 hc->ops.res_end = hc_cli_res_end_cb;
171
Willy Tarreau89a7c412022-05-05 19:38:21 +0200172 ctx->hc = hc; /* store the httpclient ptr in the applet */
173 ctx->flags = 0;
William Lallemand03a4eb12021-08-18 16:46:21 +0200174
William Lallemandbad9c8c2022-01-14 14:10:33 +0100175 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200176 goto err;
177
178
179 if (!httpclient_start(hc))
180 goto err;
181
182 return 0;
183
184err:
185 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
186 return cli_err(appctx, err);
187}
188
189/* This function dumps the content of the httpclient receive buffer
190 * on the CLI output
191 *
192 * Return 1 when the processing is finished
193 * return 0 if it needs to be called again
194 */
195static int hc_cli_io_handler(struct appctx *appctx)
196{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200197 struct hcli_svc_ctx *ctx = appctx->svcctx;
Willy Tarreau0698c802022-05-11 14:09:57 +0200198 struct conn_stream *cs = appctx_cs(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200199 struct buffer *trash = alloc_trash_chunk();
Willy Tarreau89a7c412022-05-05 19:38:21 +0200200 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200201 struct http_hdr *hdrs, *hdr;
202
203 if (!trash)
204 goto out;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200205
206 if (ctx->flags & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100207 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
208 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
Christopher Faulet908628c2022-03-25 16:43:49 +0100209 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200210 cs_rx_room_blk(cs);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200211 ctx->flags &= ~HC_CLI_F_RES_STLINE;
William Lallemand03a4eb12021-08-18 16:46:21 +0200212 goto out;
213 }
214
Willy Tarreau89a7c412022-05-05 19:38:21 +0200215 if (ctx->flags & HC_CLI_F_RES_HDR) {
William Lallemand03a4eb12021-08-18 16:46:21 +0200216 hdrs = hc->res.hdrs;
217 for (hdr = hdrs; isttest(hdr->v); hdr++) {
218 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
219 goto out;
220 }
221 if (!chunk_memcat(trash, "\r\n", 2))
222 goto out;
Christopher Faulet908628c2022-03-25 16:43:49 +0100223 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200224 cs_rx_room_blk(cs);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200225 ctx->flags &= ~HC_CLI_F_RES_HDR;
William Lallemand03a4eb12021-08-18 16:46:21 +0200226 goto out;
227 }
228
Willy Tarreau89a7c412022-05-05 19:38:21 +0200229 if (ctx->flags & HC_CLI_F_RES_BODY) {
William Lallemand03a4eb12021-08-18 16:46:21 +0200230 int ret;
231
Christopher Faulet908628c2022-03-25 16:43:49 +0100232 ret = httpclient_res_xfer(hc, cs_ib(cs));
233 channel_add_input(cs_ic(cs), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200234
William Lallemand518878e2021-09-21 10:45:34 +0200235 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
Willy Tarreau89a7c412022-05-05 19:38:21 +0200236 ctx->flags &= ~HC_CLI_F_RES_BODY;
William Lallemand03a4eb12021-08-18 16:46:21 +0200237 }
238 goto out;
239 }
240
241 /* we must close only if F_END is the last flag */
Willy Tarreau89a7c412022-05-05 19:38:21 +0200242 if (ctx->flags == HC_CLI_F_RES_END) {
Christopher Fauletda098e62022-03-31 17:44:45 +0200243 cs_shutw(cs);
244 cs_shutr(cs);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200245 ctx->flags &= ~HC_CLI_F_RES_END;
William Lallemand03a4eb12021-08-18 16:46:21 +0200246 goto out;
247 }
248
249out:
250 /* we didn't clear every flags, we should come back to finish things */
Willy Tarreau89a7c412022-05-05 19:38:21 +0200251 if (ctx->flags)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200252 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200253
254 free_trash_chunk(trash);
255 return 0;
256}
257
258static void hc_cli_release(struct appctx *appctx)
259{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200260 struct hcli_svc_ctx *ctx = appctx->svcctx;
261 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200262
263 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200264 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200265
266 return;
267}
268
269/* register cli keywords */
270static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100271 { { "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 +0200272 { { NULL }, NULL, NULL, NULL }
273}};
274
275INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
276
277
278/* --- This part of the file implements the actual HTTP client API --- */
279
William Lallemand33b0d092021-08-13 16:05:53 +0200280/*
281 * Generate a simple request and fill the httpclient request buffer with it.
282 * The request contains a request line generated from the absolute <url> and
283 * <meth> as well as list of headers <hdrs>.
284 *
285 * If the buffer was filled correctly the function returns 0, if not it returns
286 * an error_code but there is no guarantee that the buffer wasn't modified.
287 */
William Lallemanddec25c32021-10-25 19:48:37 +0200288int 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 +0200289{
290 struct htx_sl *sl;
291 struct htx *htx;
292 int err_code = 0;
293 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200294 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 +0100295 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100296 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200297
Christopher Faulet600985d2022-01-12 11:14:08 +0100298 if (!b_alloc(&hc->req.buf))
299 goto error;
300
William Lallemand33b0d092021-08-13 16:05:53 +0200301 if (meth >= HTTP_METH_OTHER)
302 goto error;
303
304 meth_ist = http_known_methods[meth];
305
306 vsn = ist("HTTP/1.1");
307
308 htx = htx_from_buf(&hc->req.buf);
309 if (!htx)
310 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100311
312 if (!hc->ops.req_payload && !isttest(payload))
313 flags |= HTX_SL_F_BODYLESS;
314
William Lallemand33b0d092021-08-13 16:05:53 +0200315 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
316 if (!sl) {
317 goto error;
318 }
319 sl->info.req.meth = meth;
320
William Lallemandf03b53c2021-11-24 15:38:17 +0100321 for (i = 0; hdrs && hdrs[i].n.len; i++) {
322 /* Don't check the value length because a header value may be empty */
323 if (isttest(hdrs[i].v) == 0)
324 continue;
325
326 if (isteqi(hdrs[i].n, ist("host")))
327 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100328 else if (isteqi(hdrs[i].n, ist("accept")))
329 foundaccept = 1;
330 else if (isteqi(hdrs[i].n, ist("user-agent")))
331 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100332
333 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
334 goto error;
335 }
William Lallemand33b0d092021-08-13 16:05:53 +0200336
William Lallemandf03b53c2021-11-24 15:38:17 +0100337 if (!foundhost) {
338 /* Add Host Header from URL */
339 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200340 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100341 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200342 goto error;
343 }
William Lallemand33b0d092021-08-13 16:05:53 +0200344
William Lallemandbad9c8c2022-01-14 14:10:33 +0100345 if (!foundaccept) {
346 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
347 goto error;
348 }
349
350 if (!foundua) {
351 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
352 goto error;
353 }
354
355
William Lallemandf03b53c2021-11-24 15:38:17 +0100356 if (!htx_add_endof(htx, HTX_BLK_EOH))
357 goto error;
358
William Lallemanddec25c32021-10-25 19:48:37 +0200359 if (isttest(payload)) {
360 /* add the payload if it can feat in the buffer, no need to set
361 * the Content-Length, the data will be sent chunked */
362 if (!htx_add_data_atonce(htx, payload))
363 goto error;
364 }
365
William Lallemand0da616e2021-10-28 15:34:26 +0200366 /* If req.payload was set, does not set the end of stream which *MUST*
367 * be set in the callback */
368 if (!hc->ops.req_payload)
369 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200370
371 htx_to_buf(htx, &hc->req.buf);
372
373 return 0;
374error:
375 err_code |= ERR_ALERT | ERR_ABORT;
376 return err_code;
377}
378
379/*
380 * transfer the response to the destination buffer and wakeup the HTTP client
381 * applet so it could fill again its buffer.
382 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500383 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200384 */
385int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
386{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100387 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200388 int ret;
389
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100390 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200391 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100392 if (!b_data(&hc->res.buf)) {
393 b_free(&hc->res.buf);
394 if (hc->appctx)
395 appctx_wakeup(hc->appctx);
396 }
William Lallemand33b0d092021-08-13 16:05:53 +0200397 return ret;
398}
399
400/*
William Lallemand0da616e2021-10-28 15:34:26 +0200401 * Transfer raw HTTP payload from src, and insert it into HTX format in the
402 * httpclient.
403 *
404 * Must be used to transfer the request body.
405 * Then wakeup the httpclient so it can transfer it.
406 *
407 * <end> tries to add the ending data flag if it succeed to copy all data.
408 *
409 * Return the number of bytes copied from src.
410 */
411int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
412{
413 int ret = 0;
414 struct htx *htx;
415
Christopher Faulet600985d2022-01-12 11:14:08 +0100416 if (!b_alloc(&hc->req.buf))
417 goto error;
418
William Lallemand0da616e2021-10-28 15:34:26 +0200419 htx = htx_from_buf(&hc->req.buf);
420 if (!htx)
421 goto error;
422
423 if (hc->appctx)
424 appctx_wakeup(hc->appctx);
425
426 ret += htx_add_data(htx, src);
427
428
429 /* if we copied all the data and the end flag is set */
430 if ((istlen(src) == ret) && end) {
431 htx->flags |= HTX_FL_EOM;
432 }
433 htx_to_buf(htx, &hc->req.buf);
434
435error:
436
437 return ret;
438}
439
William Lallemandb4a4ef62022-02-23 14:18:16 +0100440/* Set the 'timeout server' in ms for the next httpclient request */
441void httpclient_set_timeout(struct httpclient *hc, int timeout)
442{
443 hc->timeout_server = timeout;
444}
445
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100446/*
447 * Sets a destination for the httpclient from an HAProxy addr format
448 * This will prevent to determine the destination from the URL
449 * Return 0 in case of success or -1 otherwise.
450 */
451int httpclient_set_dst(struct httpclient *hc, const char *dst)
452{
453 struct sockaddr_storage *sk;
454 char *errmsg = NULL;
455
456 sockaddr_free(&hc->dst);
457 /* 'sk' is statically allocated (no need to be freed). */
458 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
459 &errmsg, NULL, NULL,
460 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
461 if (!sk) {
462 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
463 free(errmsg);
464 return -1;
465 }
466
467 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
468 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
469 return -1;
470 }
471
472 return 0;
473}
William Lallemand0da616e2021-10-28 15:34:26 +0200474
475/*
William Lallemand7f1df8f2022-04-14 17:50:20 +0200476 * Return a splitted URL in <scheme>, <host>, <port>
477 */
478static void httpclient_spliturl(struct ist url, enum http_scheme *scheme,
479 struct ist *host, int *port)
480{
481 enum http_scheme scheme_tmp = SCH_HTTP;
482 int port_tmp = 0;
483 struct ist scheme_ist, authority_ist, host_ist, port_ist;
484 char *p, *end;
485 struct http_uri_parser parser;
486
487 parser = http_uri_parser_init(url);
488 scheme_ist = http_parse_scheme(&parser);
489
490 if (isteqi(scheme_ist, ist("http://"))){
491 scheme_tmp = SCH_HTTP;
492 port_tmp = 80;
493 } else if (isteqi(scheme_ist, ist("https://"))) {
494 scheme_tmp = SCH_HTTPS;
495 port_tmp = 443;
496 }
497
498 authority_ist = http_parse_authority(&parser, 1);
499 p = end = istend(authority_ist);
500
501 /* look for a port at the end of the authority */
502 while (p > istptr(authority_ist) && isdigit((unsigned char)*--p))
503 ;
504
505 if (*p == ':') {
506 host_ist = ist2(istptr(authority_ist), p - istptr(authority_ist));
507 port_ist = istnext(ist2(p, end - p));
508 ist2str(trash.area, port_ist);
509 port_tmp = atoi(trash.area);
510 } else {
511 host_ist = authority_ist;
512 }
513
514 if (scheme)
515 *scheme = scheme_tmp;
516 if (host)
517 *host = host_ist;
518 if (port)
519 *port = port_tmp;
520
521}
522
523/*
William Lallemand33b0d092021-08-13 16:05:53 +0200524 * Start the HTTP client
525 * Create the appctx, session, stream and wakeup the applet
526 *
William Lallemand33b0d092021-08-13 16:05:53 +0200527 * Return the <appctx> or NULL if it failed
528 */
529struct appctx *httpclient_start(struct httpclient *hc)
530{
531 struct applet *applet = &httpclient_applet;
532 struct appctx *appctx;
William Lallemand33b0d092021-08-13 16:05:53 +0200533
William Lallemand5085bc32022-02-17 12:52:09 +0100534 /* if the client was started and not ended, an applet is already
535 * running, we shouldn't try anything */
536 if (httpclient_started(hc) && !httpclient_ended(hc))
537 return NULL;
538
William Lallemand33b0d092021-08-13 16:05:53 +0200539 /* The HTTP client will be created in the same thread as the caller,
540 * avoiding threading issues */
Christopher Faulet6095d572022-05-16 17:09:48 +0200541 appctx = appctx_new_here(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200542 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100543 goto out;
Christopher Fauletb1e08362022-05-12 15:33:14 +0200544 appctx->svcctx = hc;
545 hc->flags = 0;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100546
Christopher Fauletb1e08362022-05-12 15:33:14 +0200547 if (appctx_init(appctx) == -1) {
548 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet92202da2022-05-11 12:22:10 +0200549 goto out_free_appctx;
William Lallemand85332732022-05-04 10:59:51 +0200550 }
551
William Lallemand33b0d092021-08-13 16:05:53 +0200552 return appctx;
553
William Lallemand33b0d092021-08-13 16:05:53 +0200554out_free_appctx:
Christopher Fauletb1e08362022-05-12 15:33:14 +0200555 appctx_free_on_early_error(appctx);
William Lallemand33b0d092021-08-13 16:05:53 +0200556out:
557
558 return NULL;
559}
560
William Lallemandecb83e12021-09-28 11:00:43 +0200561/*
562 * This function tries to destroy the httpclient if it wasn't running.
563 * If it was running, stop the client and ask it to autodestroy itself.
564 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500565 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200566 *
567 */
568void httpclient_stop_and_destroy(struct httpclient *hc)
569{
570
William Lallemandb8b13702021-09-28 12:15:37 +0200571 /* The httpclient was already stopped or never started, we can safely destroy it */
572 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200573 httpclient_destroy(hc);
574 } else {
575 /* if the client wasn't stopped, ask for a stop and destroy */
576 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
577 if (hc->appctx)
578 appctx_wakeup(hc->appctx);
579 }
580}
581
William Lallemand33b0d092021-08-13 16:05:53 +0200582/* Free the httpclient */
583void httpclient_destroy(struct httpclient *hc)
584{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200585 struct http_hdr *hdrs;
586
587
William Lallemand33b0d092021-08-13 16:05:53 +0200588 if (!hc)
589 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200590
William Lallemand2a879002021-10-05 15:50:45 +0200591 /* we should never destroy a client which was started but not stopped */
592 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200593
William Lallemand03f5a1c2021-09-27 15:17:47 +0200594 /* request */
595 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200596 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200597 /* response */
598 istfree(&hc->res.vsn);
599 istfree(&hc->res.reason);
600 hdrs = hc->res.hdrs;
601 while (hdrs && isttest(hdrs->n)) {
602 istfree(&hdrs->n);
603 istfree(&hdrs->v);
604 hdrs++;
605 }
606 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200607 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100608 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200609
William Lallemand33b0d092021-08-13 16:05:53 +0200610 free(hc);
611
612 return;
613}
614
615/* Allocate an httpclient and its buffers
616 * Return NULL on failure */
617struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
618{
619 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200620
621 hc = calloc(1, sizeof(*hc));
622 if (!hc)
623 goto err;
624
Christopher Faulet600985d2022-01-12 11:14:08 +0100625 hc->req.buf = BUF_NULL;
626 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200627 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100628 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200629 hc->req.meth = meth;
630
631 return hc;
632
633err:
634 httpclient_destroy(hc);
635 return NULL;
636}
637
638static void httpclient_applet_io_handler(struct appctx *appctx)
639{
Willy Tarreau1eea6652022-05-05 20:12:01 +0200640 struct httpclient *hc = appctx->svcctx;
Willy Tarreau0698c802022-05-11 14:09:57 +0200641 struct conn_stream *cs = appctx_cs(appctx);
Christopher Faulet908628c2022-03-25 16:43:49 +0100642 struct stream *s = __cs_strm(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200643 struct channel *req = &s->req;
644 struct channel *res = &s->res;
645 struct htx_blk *blk = NULL;
646 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200647 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200648 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100649 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100650 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200651
William Lallemand33b0d092021-08-13 16:05:53 +0200652 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200653
654 /* required to stop */
655 if (hc->flags & HTTPCLIENT_FA_STOP)
656 goto end;
657
William Lallemand33b0d092021-08-13 16:05:53 +0200658 switch(appctx->st0) {
659
660 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100661 /* we know that the buffer is empty here, since
662 * it's the first call, we can freely copy the
663 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100664 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100665 if (!ret)
666 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100667
Christopher Faulet600985d2022-01-12 11:14:08 +0100668 if (!b_data(&hc->req.buf))
669 b_free(&hc->req.buf);
670
William Lallemanddb8a1f32021-11-08 16:55:14 +0100671 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100672 if (!htx)
673 goto more;
674
William Lallemanddb8a1f32021-11-08 16:55:14 +0100675 channel_add_input(req, htx->data);
676
William Lallemand933fe392021-11-04 09:45:58 +0100677 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
678 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
679 else
680 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
681
William Lallemand33b0d092021-08-13 16:05:53 +0200682 goto more; /* we need to leave the IO handler once we wrote the request */
683 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200684 case HTTPCLIENT_S_REQ_BODY:
685 /* call the payload callback */
686 {
687 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100688 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200689
William Lallemand0da616e2021-10-28 15:34:26 +0200690 /* call the request callback */
691 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100692
William Lallemandccc7ee42022-03-18 17:57:15 +0100693 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100694 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100695
William Lallemandccc7ee42022-03-18 17:57:15 +0100696 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100697 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100698
William Lallemandccc7ee42022-03-18 17:57:15 +0100699 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200700 size_t data = hc_htx->data;
701
William Lallemandccc7ee42022-03-18 17:57:15 +0100702 /* Here htx_to_buf() will set buffer data to 0 because
703 * the HTX is empty, and allow us to do an xfer.
704 */
705 htx_to_buf(hc_htx, &hc->req.buf);
706 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100707 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200708 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100709 } else {
710 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100711
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200712 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100713 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100714
William Lallemandccc7ee42022-03-18 17:57:15 +0100715 /* we must copy the EOM if we empty the buffer */
716 if (htx_is_empty(hc_htx)) {
717 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
718 }
719 htx_to_buf(htx, &req->buf);
720 htx_to_buf(hc_htx, &hc->req.buf);
721 }
722
723
724 if (!b_data(&hc->req.buf))
725 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200726 }
727
William Lallemanddb8a1f32021-11-08 16:55:14 +0100728 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200729 if (!htx)
730 goto more;
731
732 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100733 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet3d433242022-03-03 15:38:39 +0100734 req->flags |= CF_EOI;
Willy Tarreau66435e52022-05-10 11:32:31 +0200735 appctx->endp->flags |= CS_EP_EOI;
William Lallemand0da616e2021-10-28 15:34:26 +0200736 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100737 }
William Lallemand0da616e2021-10-28 15:34:26 +0200738
William Lallemand1eca8942022-03-17 14:57:23 +0100739 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200740 }
741 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200742
743 case HTTPCLIENT_S_RES_STLINE:
744 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100745 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200746 goto more;
747 htx = htxbuf(&res->buf);
748 if (!htx)
749 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100750 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200751 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
752 sl = htx_get_blk_ptr(htx, blk);
753 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
754 goto more;
755
756 /* copy the status line in the httpclient */
757 hc->res.status = sl->info.res.status;
758 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
759 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100760 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200761 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100762 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200763 /* caller callback */
764 if (hc->ops.res_stline)
765 hc->ops.res_stline(hc);
766
767 /* if there is no HTX data anymore and the EOM flag is
768 * set, leave (no body) */
769 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
770 appctx->st0 = HTTPCLIENT_S_RES_END;
771 else
772 appctx->st0 = HTTPCLIENT_S_RES_HDR;
773 break;
774
775 case HTTPCLIENT_S_RES_HDR:
776 /* first copy the headers in a local hdrs
777 * structure, once we the total numbers of the
778 * header we allocate the right size and copy
779 * them. The htx block of the headers are
780 * removed each time one is read */
781 {
782 struct http_hdr hdrs[global.tune.max_http_hdr];
783
William Lallemanda625b032022-03-17 14:45:46 +0100784 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200785 goto more;
786 htx = htxbuf(&res->buf);
787 if (!htx)
788 goto more;
789
790 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200791 blk = htx_get_head_blk(htx);
792 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200793 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100794 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200795
Christopher Faulet534645d2022-04-29 13:44:46 +0200796 c_rew(res, sz);
797 blk = htx_remove_blk(htx, blk);
William Lallemandc020b252022-03-09 18:56:02 +0100798
Christopher Faulet534645d2022-04-29 13:44:46 +0200799 if (type == HTX_BLK_UNUSED)
800 continue;
801 else if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100802 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
803 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100804 hdr_num++;
805 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200806 else if (type == HTX_BLK_EOH) {
807 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200808 hdrs[hdr_num].n = IST_NULL;
809 hdrs[hdr_num].v = IST_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200810 break;
811 }
William Lallemand33b0d092021-08-13 16:05:53 +0200812 }
813
William Lallemand0d6f7792021-08-20 11:59:49 +0200814 if (hdr_num) {
815 /* alloc and copy the headers in the httpclient struct */
816 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
817 if (!hc->res.hdrs)
818 goto end;
819 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200820
William Lallemand0d6f7792021-08-20 11:59:49 +0200821 /* caller callback */
822 if (hc->ops.res_headers)
823 hc->ops.res_headers(hc);
824 }
William Lallemand33b0d092021-08-13 16:05:53 +0200825
826 /* if there is no HTX data anymore and the EOM flag is
827 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200828 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200829 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200830 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200831 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200832 }
William Lallemand33b0d092021-08-13 16:05:53 +0200833 }
834 break;
835
836 case HTTPCLIENT_S_RES_BODY:
837 /*
838 * The IO handler removes the htx blocks in the response buffer and
839 * push them in the hc->res.buf buffer in a raw format.
840 */
William Lallemanda625b032022-03-17 14:45:46 +0100841 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100842 goto more;
843
William Lallemand33b0d092021-08-13 16:05:53 +0200844 htx = htxbuf(&res->buf);
845 if (!htx || htx_is_empty(htx))
846 goto more;
847
Christopher Faulet600985d2022-01-12 11:14:08 +0100848 if (!b_alloc(&hc->res.buf))
849 goto more;
850
William Lallemand33b0d092021-08-13 16:05:53 +0200851 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100852 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200853
854 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200855 blk = htx_get_head_blk(htx);
856 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100857 enum htx_blk_type type = htx_get_blk_type(blk);
858 size_t count = co_data(res);
859 uint32_t blksz = htx_get_blksz(blk);
860 uint32_t room = b_room(&hc->res.buf);
861 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200862
William Lallemandc8f1eb92022-03-09 11:58:51 +0100863 /* we should try to copy the maximum output data in a block, which fit
864 * the destination buffer */
865 vlen = MIN(count, blksz);
866 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100867
William Lallemandc8f1eb92022-03-09 11:58:51 +0100868 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100869 goto process_data;
870
William Lallemand33b0d092021-08-13 16:05:53 +0200871 if (type == HTX_BLK_DATA) {
872 struct ist v = htx_get_blk_value(htx, blk);
873
William Lallemandc8f1eb92022-03-09 11:58:51 +0100874 __b_putblk(&hc->res.buf, v.ptr, vlen);
875 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200876
William Lallemandc8f1eb92022-03-09 11:58:51 +0100877 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200878 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100879 else
880 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100881
William Lallemand33b0d092021-08-13 16:05:53 +0200882 /* the data must be processed by the caller in the receive phase */
883 if (hc->ops.res_payload)
884 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100885
886 /* cannot copy everything, need to processs */
887 if (vlen != blksz)
888 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200889 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100890 if (vlen != blksz)
891 goto process_data;
892
William Lallemand33b0d092021-08-13 16:05:53 +0200893 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100894 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200895 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200896 }
897 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100898
William Lallemand33b0d092021-08-13 16:05:53 +0200899 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100900 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200901 goto more;
902
William Lallemandc8f1eb92022-03-09 11:58:51 +0100903
William Lallemand33b0d092021-08-13 16:05:53 +0200904 /* end of message, we should quit */
905 appctx->st0 = HTTPCLIENT_S_RES_END;
906 break;
907
908 case HTTPCLIENT_S_RES_END:
909 goto end;
910 break;
911 }
912 }
913
914process_data:
915
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200916 cs_rx_chan_rdy(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200917
918 return;
919more:
920 /* There was not enough data in the response channel */
921
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200922 cs_rx_room_blk(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200923
924 if (appctx->st0 == HTTPCLIENT_S_RES_END)
925 goto end;
926
927 /* The state machine tries to handle as much data as possible, if there
928 * isn't any data to handle and a shutdown is detected, let's stop
929 * everything */
930 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100931 (res->flags & CF_SHUTW) ||
932 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200933 goto end;
934 }
935 return;
936
937end:
Christopher Fauletda098e62022-03-31 17:44:45 +0200938 cs_shutw(cs);
939 cs_shutr(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200940 return;
941}
942
Christopher Fauletb1e08362022-05-12 15:33:14 +0200943static int httpclient_applet_init(struct appctx *appctx)
944{
945 struct httpclient *hc = appctx->svcctx;
946 struct stream *s;
947 struct sockaddr_storage *addr = NULL;
948 struct sockaddr_storage ss_url = {};
949 struct sockaddr_storage *ss_dst;
950 enum obj_type *target = NULL;
951 struct ist host = IST_NULL;
952 enum http_scheme scheme;
953 int port;
954 int doresolve = 0;
955
956
957 /* parse the URL and */
958 httpclient_spliturl(hc->req.url, &scheme, &host, &port);
959
960 if (hc->dst) {
961 /* if httpclient_set_dst() was used, sets the alternative address */
962 ss_dst = hc->dst;
963 } else {
964 /* set the dst using the host, or 0.0.0.0 to resolve */
965 ist2str(trash.area, host);
966 ss_dst = str2ip2(trash.area, &ss_url, 0);
967 if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
968 doresolve = 1;
969 ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
970 }
971 sock_inet_set_port(ss_dst, port);
972 }
973
974 if (!ss_dst) {
975 ha_alert("httpclient: Failed to initialize address %s:%d.\n", __FUNCTION__, __LINE__);
976 goto out_error;
977 }
978
979 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
980 goto out_error;
981
982 /* choose the SSL server or not */
983 switch (scheme) {
984 case SCH_HTTP:
985 target = &httpclient_srv_raw->obj_type;
986 break;
987 case SCH_HTTPS:
988#ifdef USE_OPENSSL
989 if (httpclient_srv_ssl) {
990 target = &httpclient_srv_ssl->obj_type;
991 } else {
992 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
993 goto out_free_addr;
994 }
995#else
996 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
997 goto out_free_addr;
998#endif
999 break;
1000 }
1001
1002 if (appctx_finalize_startup(appctx, httpclient_proxy, &hc->req.buf) == -1) {
1003 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
1004 goto out_free_addr;
1005 }
1006
1007 s = appctx_strm(appctx);
1008 s->target = target;
1009 /* set the "timeout server" */
1010 s->req.wto = hc->timeout_server;
1011 s->res.rto = hc->timeout_server;
1012
1013 if (doresolve) {
1014 /* in order to do the set-dst we need to put the address on the front */
1015 s->csf->dst = addr;
1016 } else {
1017 /* in cases we don't use the resolve we already have the address
1018 * and must put it on the backend side, some of the cases are
1019 * not meant to be used on the frontend (sockpair, unix socket etc.) */
1020 s->csb->dst = addr;
1021 }
1022
1023 s->csb->flags |= CS_FL_NOLINGER;
1024 s->flags |= SF_ASSIGNED;
1025 s->res.flags |= CF_READ_DONTWAIT;
1026
1027 /* applet is waiting for data */
1028 cs_cant_get(s->csf);
1029 appctx_wakeup(appctx);
1030
1031 hc->appctx = appctx;
1032 hc->flags |= HTTPCLIENT_FS_STARTED;
1033
1034 /* The request was transferred when the stream was created. So switch
1035 * directly to REQ_BODY or RES_STLINE state
1036 */
1037 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
1038 return 0;
1039
1040 out_free_addr:
1041 sockaddr_free(&addr);
1042 out_error:
1043 return -1;
1044}
1045
William Lallemand33b0d092021-08-13 16:05:53 +02001046static void httpclient_applet_release(struct appctx *appctx)
1047{
Willy Tarreau1eea6652022-05-05 20:12:01 +02001048 struct httpclient *hc = appctx->svcctx;
William Lallemand33b0d092021-08-13 16:05:53 +02001049
William Lallemand1123dde2021-09-21 10:58:10 +02001050 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +02001051 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +02001052 /* the applet is leaving, remove the ptr so we don't try to call it
1053 * again from the caller */
1054 hc->appctx = NULL;
1055
William Lallemandeb0d4c42022-04-06 14:12:37 +02001056 if (hc->ops.res_end)
1057 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +02001058
1059 /* destroy the httpclient when set to autotokill */
1060 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
1061 httpclient_destroy(hc);
1062 }
1063
William Lallemand33b0d092021-08-13 16:05:53 +02001064 return;
1065}
1066
1067/* HTTP client applet */
1068static struct applet httpclient_applet = {
1069 .obj_type = OBJ_TYPE_APPLET,
1070 .name = "<HTTPCLIENT>",
1071 .fct = httpclient_applet_io_handler,
Christopher Fauletb1e08362022-05-12 15:33:14 +02001072 .init = httpclient_applet_init,
William Lallemand33b0d092021-08-13 16:05:53 +02001073 .release = httpclient_applet_release,
1074};
1075
William Lallemand5392ff62022-04-28 16:55:02 +02001076
1077static int httpclient_resolve_init()
1078{
1079 struct act_rule *rule;
1080 int i;
William Lallemand8a734cb2022-05-04 16:10:47 +02001081 char *do_resolve = NULL;
1082 char *http_rules[][11] = {
William Lallemand5392ff62022-04-28 16:55:02 +02001083 { "set-var(txn.hc_ip)", "dst", "" },
William Lallemand8a734cb2022-05-04 16:10:47 +02001084 { do_resolve, "hdr(Host),lower", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
William Lallemand5392ff62022-04-28 16:55:02 +02001085 { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
1086 { "capture", "var(txn.hc_ip)", "len", "40", "" },
1087 { "set-dst", "var(txn.hc_ip)", "" },
1088 { "" }
1089 };
1090
William Lallemand8a734cb2022-05-04 16:10:47 +02001091 if (!resolvers_id)
1092 resolvers_id = strdup("default");
1093
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001094 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 +02001095 http_rules[1][0] = do_resolve;
1096
William Lallemand7867f632022-05-05 19:02:59 +02001097 /* Try to create the default resolvers section */
1098 resolvers_create_default();
1099
William Lallemand8a734cb2022-05-04 16:10:47 +02001100 /* if the resolver does not exist and no hard_error was set, simply ignore resolving */
1101 if (!find_resolvers_by_id(resolvers_id) && !hard_error_resolvers) {
1102 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001103 return 0;
William Lallemand8a734cb2022-05-04 16:10:47 +02001104 }
William Lallemand5392ff62022-04-28 16:55:02 +02001105
1106
1107 for (i = 0; *http_rules[i][0] != '\0'; i++) {
1108 rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, httpclient_proxy);
1109 if (!rule) {
William Lallemand8a734cb2022-05-04 16:10:47 +02001110 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001111 ha_alert("Couldn't setup the httpclient resolver.\n");
1112 return 1;
1113 }
1114 LIST_APPEND(&httpclient_proxy->http_req_rules, &rule->list);
1115 }
1116
William Lallemand8a734cb2022-05-04 16:10:47 +02001117 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001118 return 0;
1119}
1120
1121
1122
William Lallemand83614a92021-08-13 14:47:57 +02001123/*
1124 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
1125 * the other for HTTPS.
1126 */
William Lallemand2c8b0842022-04-22 15:16:09 +02001127static int httpclient_precheck()
William Lallemand83614a92021-08-13 14:47:57 +02001128{
William Lallemand85af49c2022-05-04 14:33:57 +02001129 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001130 char *errmsg = NULL;
1131
William Lallemandc6ceba32022-04-22 16:49:53 +02001132 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001133 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001134
William Lallemand83614a92021-08-13 14:47:57 +02001135 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
1136 if (!httpclient_proxy) {
William Lallemand85af49c2022-05-04 14:33:57 +02001137 memprintf(&errmsg, "couldn't allocate proxy.");
William Lallemand83614a92021-08-13 14:47:57 +02001138 err_code |= ERR_ALERT | ERR_FATAL;
1139 goto err;
1140 }
1141
Willy Tarreau0e72e402021-08-20 10:23:12 +02001142 proxy_preset_defaults(httpclient_proxy);
1143
William Lallemandccc7ee42022-03-18 17:57:15 +01001144 httpclient_proxy->options |= PR_O_WREQ_BODY;
William Lallemand71abad02022-03-17 15:24:28 +01001145 httpclient_proxy->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
William Lallemand83614a92021-08-13 14:47:57 +02001146 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
1147 httpclient_proxy->mode = PR_MODE_HTTP;
1148 httpclient_proxy->maxconn = 0;
1149 httpclient_proxy->accept = NULL;
William Lallemand71abad02022-03-17 15:24:28 +01001150 httpclient_proxy->conn_retries = CONN_RETRIES;
William Lallemand83614a92021-08-13 14:47:57 +02001151 httpclient_proxy->timeout.client = TICK_ETERNITY;
1152 /* The HTTP Client use the "option httplog" with the global log server */
1153 httpclient_proxy->conf.logformat_string = default_http_log_format;
1154 httpclient_proxy->http_needed = 1;
1155
1156 /* clear HTTP server */
1157 httpclient_srv_raw = new_server(httpclient_proxy);
1158 if (!httpclient_srv_raw) {
William Lallemand83614a92021-08-13 14:47:57 +02001159 memprintf(&errmsg, "out of memory.");
William Lallemand85af49c2022-05-04 14:33:57 +02001160 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001161 goto err;
1162 }
1163
1164 httpclient_srv_raw->iweight = 0;
1165 httpclient_srv_raw->uweight = 0;
1166 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
William Lallemand1218d192022-05-03 14:09:06 +02001167 httpclient_srv_raw->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
William Lallemand83614a92021-08-13 14:47:57 +02001168 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
William Lallemand85af49c2022-05-04 14:33:57 +02001169 if (!httpclient_srv_raw->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 Lallemand957ab132021-08-24 18:33:28 +02001175#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001176 /* SSL HTTP server */
1177 httpclient_srv_ssl = new_server(httpclient_proxy);
1178 if (!httpclient_srv_ssl) {
1179 memprintf(&errmsg, "out of memory.");
1180 err_code |= ERR_ALERT | ERR_FATAL;
1181 goto err;
1182 }
1183 httpclient_srv_ssl->iweight = 0;
1184 httpclient_srv_ssl->uweight = 0;
1185 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
1186 httpclient_srv_ssl->use_ssl = 1;
William Lallemand1218d192022-05-03 14:09:06 +02001187 httpclient_srv_ssl->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
William Lallemand211c9672021-08-24 17:18:13 +02001188 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand85af49c2022-05-04 14:33:57 +02001189 if (!httpclient_srv_ssl->id) {
1190 memprintf(&errmsg, "out of memory.");
1191 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001192 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001193 }
William Lallemand83614a92021-08-13 14:47:57 +02001194
William Lallemandeaa703e2022-04-22 17:52:33 +02001195 httpclient_srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001196 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001197 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
William Lallemand683fbb82022-05-04 15:43:01 +02001198
1199 if (!httpclient_ssl_ca_file)
1200 httpclient_ssl_ca_file = strdup("@system-ca");
1201
1202 httpclient_srv_ssl->ssl_ctx.ca_file = httpclient_ssl_ca_file;
William Lallemand4006b0f2022-04-25 18:23:35 +02001203 if (!ssl_store_load_locations_file(httpclient_srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT)) {
William Lallemand6fce46a2022-05-04 14:53:41 +02001204 /* if we failed to load the ca-file, only quits in
1205 * error with hard_error, otherwise just disable the
1206 * feature. */
1207 if (hard_error_ssl) {
1208 memprintf(&errmsg, "cannot initialize SSL verify with 'ca-file \"%s\"'.", httpclient_srv_ssl->ssl_ctx.ca_file);
1209 err_code |= ERR_ALERT | ERR_FATAL;
1210 goto err;
1211 } else {
1212 ha_free(&httpclient_srv_ssl->ssl_ctx.ca_file);
1213 srv_drop(httpclient_srv_ssl);
1214 httpclient_srv_ssl = NULL;
1215 }
William Lallemand4006b0f2022-04-25 18:23:35 +02001216 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001217 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001218
William Lallemand957ab132021-08-24 18:33:28 +02001219#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001220
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001221 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +02001222 httpclient_proxy->next = proxies_list;
1223 proxies_list = httpclient_proxy;
1224
William Lallemand85af49c2022-05-04 14:33:57 +02001225 if (httpclient_resolve_init() != 0) {
1226 memprintf(&errmsg, "cannot initialize resolvers.");
1227 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand5392ff62022-04-28 16:55:02 +02001228 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001229 }
William Lallemand5392ff62022-04-28 16:55:02 +02001230
William Lallemand211c9672021-08-24 17:18:13 +02001231 /* link the 2 servers in the proxy */
1232 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +02001233 httpclient_proxy->srv = httpclient_srv_raw;
1234
1235#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001236 if (httpclient_srv_ssl) {
1237 httpclient_srv_ssl->next = httpclient_proxy->srv;
1238 httpclient_proxy->srv = httpclient_srv_ssl;
1239 }
William Lallemand957ab132021-08-24 18:33:28 +02001240#endif
1241
William Lallemand211c9672021-08-24 17:18:13 +02001242
William Lallemand83614a92021-08-13 14:47:57 +02001243err:
William Lallemand85af49c2022-05-04 14:33:57 +02001244 if (err_code & ERR_CODE) {
1245 ha_alert("httpclient: cannot initialize: %s\n", errmsg);
1246 free(errmsg);
1247 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001248#ifdef USE_OPENSSL
William Lallemand85af49c2022-05-04 14:33:57 +02001249 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001250#endif
William Lallemand85af49c2022-05-04 14:33:57 +02001251 free_proxy(httpclient_proxy);
1252 }
William Lallemand83614a92021-08-13 14:47:57 +02001253 return err_code;
1254}
1255
William Lallemand2c8b0842022-04-22 15:16:09 +02001256static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001257{
William Lallemand85af49c2022-05-04 14:33:57 +02001258 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001259 struct logsrv *logsrv;
1260 struct proxy *curproxy = httpclient_proxy;
William Lallemand71e31582022-03-16 15:47:47 +01001261 char *errmsg = NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001262
William Lallemandc6ceba32022-04-22 16:49:53 +02001263 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001264 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001265
William Lallemand83614a92021-08-13 14:47:57 +02001266 /* copy logs from "global" log list */
1267 list_for_each_entry(logsrv, &global.logsrvs, list) {
1268 struct logsrv *node = malloc(sizeof(*node));
1269
1270 if (!node) {
William Lallemand85af49c2022-05-04 14:33:57 +02001271 memprintf(&errmsg, "out of memory.");
1272 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001273 goto err;
1274 }
1275
1276 memcpy(node, logsrv, sizeof(*node));
1277 LIST_INIT(&node->list);
1278 LIST_APPEND(&curproxy->logsrvs, &node->list);
Willy Tarreaue1e9f6b2022-04-21 14:14:28 +02001279 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1280 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001281 }
1282 if (curproxy->conf.logformat_string) {
William Lallemand83614a92021-08-13 14:47:57 +02001283 curproxy->conf.args.ctx = ARGC_LOG;
1284 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1285 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
William Lallemand715c1012022-03-16 16:39:23 +01001286 SMP_VAL_FE_LOG_END, &errmsg)) {
William Lallemand85af49c2022-05-04 14:33:57 +02001287 memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
1288 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001289 goto err;
1290 }
1291 curproxy->conf.args.file = NULL;
1292 curproxy->conf.args.line = 0;
1293 }
William Lallemand71e31582022-03-16 15:47:47 +01001294
1295#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001296 if (httpclient_srv_ssl) {
William Lallemand715c1012022-03-16 16:39:23 +01001297 /* init the SNI expression */
1298 /* always use the host header as SNI, without the port */
1299 httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1300 err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
1301 if (err_code & ERR_CODE) {
William Lallemand85af49c2022-05-04 14:33:57 +02001302 memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
William Lallemand715c1012022-03-16 16:39:23 +01001303 goto err;
1304 }
William Lallemand71e31582022-03-16 15:47:47 +01001305 }
1306#endif
1307
William Lallemand83614a92021-08-13 14:47:57 +02001308err:
William Lallemand85af49c2022-05-04 14:33:57 +02001309 if (err_code & ERR_CODE) {
1310 ha_alert("httpclient: failed to initialize: %s\n", errmsg);
1311 free(errmsg);
1312
1313 }
1314 return err_code;
William Lallemand83614a92021-08-13 14:47:57 +02001315}
1316
William Lallemand83614a92021-08-13 14:47:57 +02001317/* initialize the proxy and servers for the HTTP client */
1318
William Lallemand2c8b0842022-04-22 15:16:09 +02001319REGISTER_PRE_CHECK(httpclient_precheck);
1320REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001321
William Lallemand8a734cb2022-05-04 16:10:47 +02001322static int httpclient_parse_global_resolvers(char **args, int section_type, struct proxy *curpx,
1323 const struct proxy *defpx, const char *file, int line,
1324 char **err)
1325{
1326 if (too_many_args(1, args, err, NULL))
1327 return -1;
1328
1329 /* any configuration should set the hard_error flag */
1330 hard_error_resolvers = 1;
1331
1332 free(resolvers_id);
1333 resolvers_id = strdup(args[1]);
1334
1335 return 0;
1336}
1337
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001338static int httpclient_parse_global_prefer(char **args, int section_type, struct proxy *curpx,
1339 const struct proxy *defpx, const char *file, int line,
1340 char **err)
1341{
1342 if (too_many_args(1, args, err, NULL))
1343 return -1;
1344
1345 /* any configuration should set the hard_error flag */
1346 hard_error_resolvers = 1;
1347
1348
1349 if (strcmp(args[1],"ipv4") == 0)
1350 resolvers_prefer = "ipv4";
1351 else if (strcmp(args[1],"ipv6") == 0)
1352 resolvers_prefer = "ipv6";
1353 else {
1354 ha_alert("parsing [%s:%d] : '%s' expects 'ipv4' or 'ipv6' as argument.\n", file, line, args[0]);
1355 return -1;
1356 }
1357
1358 return 0;
1359}
William Lallemand8a734cb2022-05-04 16:10:47 +02001360
1361
William Lallemandeaa703e2022-04-22 17:52:33 +02001362#ifdef USE_OPENSSL
William Lallemand683fbb82022-05-04 15:43:01 +02001363static int httpclient_parse_global_ca_file(char **args, int section_type, struct proxy *curpx,
1364 const struct proxy *defpx, const char *file, int line,
1365 char **err)
1366{
1367 if (too_many_args(1, args, err, NULL))
1368 return -1;
1369
1370 /* any configuration should set the hard_error flag */
1371 hard_error_ssl = 1;
1372
1373 free(httpclient_ssl_ca_file);
1374 httpclient_ssl_ca_file = strdup(args[1]);
1375
1376 return 0;
1377}
1378
William Lallemandeaa703e2022-04-22 17:52:33 +02001379static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1380 const struct proxy *defpx, const char *file, int line,
1381 char **err)
1382{
1383 if (too_many_args(1, args, err, NULL))
1384 return -1;
1385
William Lallemand6fce46a2022-05-04 14:53:41 +02001386 /* any configuration should set the hard_error flag */
1387 hard_error_ssl = 1;
1388
William Lallemandeaa703e2022-04-22 17:52:33 +02001389 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001390 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001391 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001392 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001393 else {
1394 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1395 return -1;
1396 }
1397
1398 return 0;
1399}
William Lallemand8a734cb2022-05-04 16:10:47 +02001400#endif /* ! USE_OPENSSL */
William Lallemandeaa703e2022-04-22 17:52:33 +02001401
1402static struct cfg_kw_list cfg_kws = {ILH, {
William Lallemand8a734cb2022-05-04 16:10:47 +02001403 { CFG_GLOBAL, "httpclient.resolvers.id", httpclient_parse_global_resolvers },
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001404 { CFG_GLOBAL, "httpclient.resolvers.prefer", httpclient_parse_global_prefer },
William Lallemand8a734cb2022-05-04 16:10:47 +02001405#ifdef USE_OPENSSL
William Lallemand9ff95e22022-05-04 13:52:29 +02001406 { CFG_GLOBAL, "httpclient.ssl.verify", httpclient_parse_global_verify },
William Lallemand683fbb82022-05-04 15:43:01 +02001407 { CFG_GLOBAL, "httpclient.ssl.ca-file", httpclient_parse_global_ca_file },
William Lallemand8a734cb2022-05-04 16:10:47 +02001408#endif
William Lallemandeaa703e2022-04-22 17:52:33 +02001409 { 0, NULL, NULL },
1410}};
1411
1412INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);