blob: 155045d43ff974c3968d3036cadfe878584ed29d [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>
William Lallemand83614a92021-08-13 14:47:57 +020021#include <haproxy/global.h>
William Lallemand0da616e2021-10-28 15:34:26 +020022#include <haproxy/istbuf.h>
William Lallemand33b0d092021-08-13 16:05:53 +020023#include <haproxy/h1_htx.h>
24#include <haproxy/http.h>
William Lallemand2b7dc4e2022-02-24 16:55:41 +010025#include <haproxy/http_ana-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020026#include <haproxy/http_client.h>
27#include <haproxy/http_htx.h>
William Lallemand5392ff62022-04-28 16:55:02 +020028#include <haproxy/http_rules.h>
William Lallemand33b0d092021-08-13 16:05:53 +020029#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020030#include <haproxy/log.h>
31#include <haproxy/proxy.h>
William Lallemand5392ff62022-04-28 16:55:02 +020032#include <haproxy/resolvers.h>
Willy Tarreau5edca2f2022-05-27 09:25:10 +020033#include <haproxy/sc_strm.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020034#include <haproxy/server.h>
Willy Tarreaudf3231c2022-09-02 09:02:21 +020035#include <haproxy/ssl_sock.h>
William Lallemand7f1df8f2022-04-14 17:50:20 +020036#include <haproxy/sock_inet.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020037#include <haproxy/stconn.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 Tarreauc12b3212022-05-27 11:08:15 +0200198 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200199 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200200 struct http_hdr *hdrs, *hdr;
201
Willy Tarreau89a7c412022-05-05 19:38:21 +0200202 if (ctx->flags & HC_CLI_F_RES_STLINE) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200203 chunk_printf(&trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
204 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
205 if (applet_putchk(appctx, &trash) == -1)
206 goto more;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200207 ctx->flags &= ~HC_CLI_F_RES_STLINE;
William Lallemand03a4eb12021-08-18 16:46:21 +0200208 }
209
Willy Tarreau89a7c412022-05-05 19:38:21 +0200210 if (ctx->flags & HC_CLI_F_RES_HDR) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200211 chunk_reset(&trash);
William Lallemand03a4eb12021-08-18 16:46:21 +0200212 hdrs = hc->res.hdrs;
213 for (hdr = hdrs; isttest(hdr->v); hdr++) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200214 if (!h1_format_htx_hdr(hdr->n, hdr->v, &trash))
215 goto too_many_hdrs;
William Lallemand03a4eb12021-08-18 16:46:21 +0200216 }
Christopher Faulet0158bb22022-06-01 17:08:19 +0200217 if (!chunk_memcat(&trash, "\r\n", 2))
218 goto too_many_hdrs;
219 if (applet_putchk(appctx, &trash) == -1)
220 goto more;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200221 ctx->flags &= ~HC_CLI_F_RES_HDR;
William Lallemand03a4eb12021-08-18 16:46:21 +0200222 }
223
Willy Tarreau89a7c412022-05-05 19:38:21 +0200224 if (ctx->flags & HC_CLI_F_RES_BODY) {
William Lallemand03a4eb12021-08-18 16:46:21 +0200225 int ret;
226
Willy Tarreau475e4632022-05-27 10:26:46 +0200227 ret = httpclient_res_xfer(hc, sc_ib(sc));
228 channel_add_input(sc_ic(sc), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200229
Christopher Faulet0158bb22022-06-01 17:08:19 +0200230 /* remove the flag if the buffer was emptied */
231 if (httpclient_data(hc))
232 goto more;
233 ctx->flags &= ~HC_CLI_F_RES_BODY;
William Lallemand03a4eb12021-08-18 16:46:21 +0200234 }
235
236 /* we must close only if F_END is the last flag */
Willy Tarreau89a7c412022-05-05 19:38:21 +0200237 if (ctx->flags == HC_CLI_F_RES_END) {
Willy Tarreau89a7c412022-05-05 19:38:21 +0200238 ctx->flags &= ~HC_CLI_F_RES_END;
Christopher Faulet89f26262022-06-01 17:17:24 +0200239 goto end;
William Lallemand03a4eb12021-08-18 16:46:21 +0200240 }
241
Christopher Faulet0158bb22022-06-01 17:08:19 +0200242more:
243 if (!ctx->flags)
244 applet_have_no_more_data(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200245 return 0;
Christopher Faulet89f26262022-06-01 17:17:24 +0200246end:
247 return 1;
Christopher Faulet0158bb22022-06-01 17:08:19 +0200248
249too_many_hdrs:
250 return cli_err(appctx, "Too many headers.\n");
William Lallemand03a4eb12021-08-18 16:46:21 +0200251}
252
253static void hc_cli_release(struct appctx *appctx)
254{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200255 struct hcli_svc_ctx *ctx = appctx->svcctx;
256 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200257
258 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200259 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200260
261 return;
262}
263
264/* register cli keywords */
265static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100266 { { "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 +0200267 { { NULL }, NULL, NULL, NULL }
268}};
269
270INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
271
272
273/* --- This part of the file implements the actual HTTP client API --- */
274
William Lallemand33b0d092021-08-13 16:05:53 +0200275/*
276 * Generate a simple request and fill the httpclient request buffer with it.
277 * The request contains a request line generated from the absolute <url> and
278 * <meth> as well as list of headers <hdrs>.
279 *
280 * If the buffer was filled correctly the function returns 0, if not it returns
281 * an error_code but there is no guarantee that the buffer wasn't modified.
282 */
William Lallemanddec25c32021-10-25 19:48:37 +0200283int 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 +0200284{
285 struct htx_sl *sl;
286 struct htx *htx;
287 int err_code = 0;
288 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200289 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 +0100290 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100291 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200292
Christopher Faulet600985d2022-01-12 11:14:08 +0100293 if (!b_alloc(&hc->req.buf))
294 goto error;
295
William Lallemand33b0d092021-08-13 16:05:53 +0200296 if (meth >= HTTP_METH_OTHER)
297 goto error;
298
299 meth_ist = http_known_methods[meth];
300
301 vsn = ist("HTTP/1.1");
302
303 htx = htx_from_buf(&hc->req.buf);
304 if (!htx)
305 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100306
307 if (!hc->ops.req_payload && !isttest(payload))
308 flags |= HTX_SL_F_BODYLESS;
309
William Lallemand33b0d092021-08-13 16:05:53 +0200310 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
311 if (!sl) {
312 goto error;
313 }
314 sl->info.req.meth = meth;
315
William Lallemandf03b53c2021-11-24 15:38:17 +0100316 for (i = 0; hdrs && hdrs[i].n.len; i++) {
317 /* Don't check the value length because a header value may be empty */
318 if (isttest(hdrs[i].v) == 0)
319 continue;
320
321 if (isteqi(hdrs[i].n, ist("host")))
322 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100323 else if (isteqi(hdrs[i].n, ist("accept")))
324 foundaccept = 1;
325 else if (isteqi(hdrs[i].n, ist("user-agent")))
326 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100327
328 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
329 goto error;
330 }
William Lallemand33b0d092021-08-13 16:05:53 +0200331
William Lallemandf03b53c2021-11-24 15:38:17 +0100332 if (!foundhost) {
333 /* Add Host Header from URL */
334 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200335 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100336 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200337 goto error;
338 }
William Lallemand33b0d092021-08-13 16:05:53 +0200339
William Lallemandbad9c8c2022-01-14 14:10:33 +0100340 if (!foundaccept) {
341 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
342 goto error;
343 }
344
345 if (!foundua) {
346 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
347 goto error;
348 }
349
350
William Lallemandf03b53c2021-11-24 15:38:17 +0100351 if (!htx_add_endof(htx, HTX_BLK_EOH))
352 goto error;
353
William Lallemanddec25c32021-10-25 19:48:37 +0200354 if (isttest(payload)) {
355 /* add the payload if it can feat in the buffer, no need to set
356 * the Content-Length, the data will be sent chunked */
357 if (!htx_add_data_atonce(htx, payload))
358 goto error;
359 }
360
William Lallemand0da616e2021-10-28 15:34:26 +0200361 /* If req.payload was set, does not set the end of stream which *MUST*
362 * be set in the callback */
363 if (!hc->ops.req_payload)
364 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200365
366 htx_to_buf(htx, &hc->req.buf);
367
368 return 0;
369error:
370 err_code |= ERR_ALERT | ERR_ABORT;
371 return err_code;
372}
373
374/*
375 * transfer the response to the destination buffer and wakeup the HTTP client
376 * applet so it could fill again its buffer.
377 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500378 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200379 */
380int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
381{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100382 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200383 int ret;
384
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100385 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200386 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100387 if (!b_data(&hc->res.buf)) {
388 b_free(&hc->res.buf);
389 if (hc->appctx)
390 appctx_wakeup(hc->appctx);
391 }
William Lallemand33b0d092021-08-13 16:05:53 +0200392 return ret;
393}
394
395/*
William Lallemand0da616e2021-10-28 15:34:26 +0200396 * Transfer raw HTTP payload from src, and insert it into HTX format in the
397 * httpclient.
398 *
399 * Must be used to transfer the request body.
400 * Then wakeup the httpclient so it can transfer it.
401 *
402 * <end> tries to add the ending data flag if it succeed to copy all data.
403 *
404 * Return the number of bytes copied from src.
405 */
406int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
407{
408 int ret = 0;
409 struct htx *htx;
410
Christopher Faulet600985d2022-01-12 11:14:08 +0100411 if (!b_alloc(&hc->req.buf))
412 goto error;
413
William Lallemand0da616e2021-10-28 15:34:26 +0200414 htx = htx_from_buf(&hc->req.buf);
415 if (!htx)
416 goto error;
417
418 if (hc->appctx)
419 appctx_wakeup(hc->appctx);
420
421 ret += htx_add_data(htx, src);
422
423
424 /* if we copied all the data and the end flag is set */
425 if ((istlen(src) == ret) && end) {
426 htx->flags |= HTX_FL_EOM;
427 }
428 htx_to_buf(htx, &hc->req.buf);
429
430error:
431
432 return ret;
433}
434
William Lallemandb4a4ef62022-02-23 14:18:16 +0100435/* Set the 'timeout server' in ms for the next httpclient request */
436void httpclient_set_timeout(struct httpclient *hc, int timeout)
437{
438 hc->timeout_server = timeout;
439}
440
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100441/*
442 * Sets a destination for the httpclient from an HAProxy addr format
443 * This will prevent to determine the destination from the URL
444 * Return 0 in case of success or -1 otherwise.
445 */
446int httpclient_set_dst(struct httpclient *hc, const char *dst)
447{
448 struct sockaddr_storage *sk;
449 char *errmsg = NULL;
450
451 sockaddr_free(&hc->dst);
452 /* 'sk' is statically allocated (no need to be freed). */
453 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
454 &errmsg, NULL, NULL,
455 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
456 if (!sk) {
457 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
458 free(errmsg);
459 return -1;
460 }
461
462 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
463 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
464 return -1;
465 }
466
467 return 0;
468}
William Lallemand0da616e2021-10-28 15:34:26 +0200469
470/*
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500471 * Return a split URL in <scheme>, <host>, <port>
William Lallemand7f1df8f2022-04-14 17:50:20 +0200472 */
473static void httpclient_spliturl(struct ist url, enum http_scheme *scheme,
474 struct ist *host, int *port)
475{
476 enum http_scheme scheme_tmp = SCH_HTTP;
477 int port_tmp = 0;
478 struct ist scheme_ist, authority_ist, host_ist, port_ist;
479 char *p, *end;
480 struct http_uri_parser parser;
481
482 parser = http_uri_parser_init(url);
483 scheme_ist = http_parse_scheme(&parser);
484
485 if (isteqi(scheme_ist, ist("http://"))){
486 scheme_tmp = SCH_HTTP;
487 port_tmp = 80;
488 } else if (isteqi(scheme_ist, ist("https://"))) {
489 scheme_tmp = SCH_HTTPS;
490 port_tmp = 443;
491 }
492
493 authority_ist = http_parse_authority(&parser, 1);
494 p = end = istend(authority_ist);
495
496 /* look for a port at the end of the authority */
497 while (p > istptr(authority_ist) && isdigit((unsigned char)*--p))
498 ;
499
500 if (*p == ':') {
501 host_ist = ist2(istptr(authority_ist), p - istptr(authority_ist));
502 port_ist = istnext(ist2(p, end - p));
503 ist2str(trash.area, port_ist);
504 port_tmp = atoi(trash.area);
505 } else {
506 host_ist = authority_ist;
507 }
508
509 if (scheme)
510 *scheme = scheme_tmp;
511 if (host)
512 *host = host_ist;
513 if (port)
514 *port = port_tmp;
515
516}
517
518/*
William Lallemand33b0d092021-08-13 16:05:53 +0200519 * Start the HTTP client
520 * Create the appctx, session, stream and wakeup the applet
521 *
William Lallemand33b0d092021-08-13 16:05:53 +0200522 * Return the <appctx> or NULL if it failed
523 */
524struct appctx *httpclient_start(struct httpclient *hc)
525{
526 struct applet *applet = &httpclient_applet;
527 struct appctx *appctx;
William Lallemand33b0d092021-08-13 16:05:53 +0200528
William Lallemand5085bc32022-02-17 12:52:09 +0100529 /* if the client was started and not ended, an applet is already
530 * running, we shouldn't try anything */
531 if (httpclient_started(hc) && !httpclient_ended(hc))
532 return NULL;
533
William Lallemand33b0d092021-08-13 16:05:53 +0200534 /* The HTTP client will be created in the same thread as the caller,
535 * avoiding threading issues */
Christopher Faulet6095d572022-05-16 17:09:48 +0200536 appctx = appctx_new_here(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200537 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100538 goto out;
Christopher Fauletb1e08362022-05-12 15:33:14 +0200539 appctx->svcctx = hc;
540 hc->flags = 0;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100541
Christopher Fauletb1e08362022-05-12 15:33:14 +0200542 if (appctx_init(appctx) == -1) {
543 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet92202da2022-05-11 12:22:10 +0200544 goto out_free_appctx;
William Lallemand85332732022-05-04 10:59:51 +0200545 }
546
William Lallemand33b0d092021-08-13 16:05:53 +0200547 return appctx;
548
William Lallemand33b0d092021-08-13 16:05:53 +0200549out_free_appctx:
Christopher Fauletb1e08362022-05-12 15:33:14 +0200550 appctx_free_on_early_error(appctx);
William Lallemand33b0d092021-08-13 16:05:53 +0200551out:
552
553 return NULL;
554}
555
William Lallemandecb83e12021-09-28 11:00:43 +0200556/*
557 * This function tries to destroy the httpclient if it wasn't running.
558 * If it was running, stop the client and ask it to autodestroy itself.
559 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500560 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200561 *
562 */
563void httpclient_stop_and_destroy(struct httpclient *hc)
564{
565
William Lallemandb8b13702021-09-28 12:15:37 +0200566 /* The httpclient was already stopped or never started, we can safely destroy it */
567 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200568 httpclient_destroy(hc);
569 } else {
Willy Tarreaub4829202022-09-01 20:40:26 +0200570 /* if the client wasn't stopped, ask for a stop and destroy */
William Lallemandecb83e12021-09-28 11:00:43 +0200571 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
Willy Tarreaub4829202022-09-01 20:40:26 +0200572 /* the calling applet doesn't exist anymore */
573 hc->caller = NULL;
William Lallemandecb83e12021-09-28 11:00:43 +0200574 if (hc->appctx)
575 appctx_wakeup(hc->appctx);
576 }
577}
578
William Lallemand33b0d092021-08-13 16:05:53 +0200579/* Free the httpclient */
580void httpclient_destroy(struct httpclient *hc)
581{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200582 struct http_hdr *hdrs;
583
584
William Lallemand33b0d092021-08-13 16:05:53 +0200585 if (!hc)
586 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200587
William Lallemand2a879002021-10-05 15:50:45 +0200588 /* we should never destroy a client which was started but not stopped */
589 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200590
William Lallemand03f5a1c2021-09-27 15:17:47 +0200591 /* request */
592 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200593 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200594 /* response */
595 istfree(&hc->res.vsn);
596 istfree(&hc->res.reason);
597 hdrs = hc->res.hdrs;
598 while (hdrs && isttest(hdrs->n)) {
599 istfree(&hdrs->n);
600 istfree(&hdrs->v);
601 hdrs++;
602 }
603 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200604 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100605 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200606
William Lallemand33b0d092021-08-13 16:05:53 +0200607 free(hc);
608
609 return;
610}
611
612/* Allocate an httpclient and its buffers
William Lallemand992ad622022-09-12 17:39:04 +0200613 * Use the default httpclient_proxy
614 *
William Lallemand33b0d092021-08-13 16:05:53 +0200615 * Return NULL on failure */
616struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
617{
618 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200619
620 hc = calloc(1, sizeof(*hc));
621 if (!hc)
622 goto err;
623
Christopher Faulet600985d2022-01-12 11:14:08 +0100624 hc->req.buf = BUF_NULL;
625 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200626 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100627 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200628 hc->req.meth = meth;
William Lallemand992ad622022-09-12 17:39:04 +0200629 httpclient_set_proxy(hc, httpclient_proxy);
William Lallemand33b0d092021-08-13 16:05:53 +0200630
631 return hc;
632
633err:
634 httpclient_destroy(hc);
635 return NULL;
636}
637
William Lallemand992ad622022-09-12 17:39:04 +0200638/* Allocate an httpclient and its buffers,
639 * Use the proxy <px>
640 *
641 * Return and httpclient or NULL.
642 */
643struct httpclient *httpclient_new_from_proxy(struct proxy *px, void *caller, enum http_meth_t meth, struct ist url)
644{
645 struct httpclient *hc;
646
647 hc = httpclient_new(caller, meth, url);
648 if (!hc)
649 return NULL;
650
651 httpclient_set_proxy(hc, px);
652
653 return hc;
654}
655
656/*
657 * Configure an httpclient with a specific proxy <px>
658 *
659 * The proxy <px> must contains 2 srv, one configured for clear connections, the other for SSL.
660 *
661 */
662int httpclient_set_proxy(struct httpclient *hc, struct proxy *px)
663{
664 struct server *srv;
665
666 hc->px = px;
667
668 for (srv = px->srv; srv != NULL; srv = srv->next) {
669 if (srv->xprt == xprt_get(XPRT_RAW)) {
670 hc->srv_raw = srv;
671#ifdef USE_OPENSSL
672 } else if (srv->xprt == xprt_get(XPRT_SSL)) {
673 hc->srv_ssl = srv;
674#endif
675 }
676 }
677
678 return 0;
679}
680
William Lallemand33b0d092021-08-13 16:05:53 +0200681static void httpclient_applet_io_handler(struct appctx *appctx)
682{
Willy Tarreau1eea6652022-05-05 20:12:01 +0200683 struct httpclient *hc = appctx->svcctx;
Willy Tarreauc12b3212022-05-27 11:08:15 +0200684 struct stconn *sc = appctx_sc(appctx);
Willy Tarreaub89f8722022-05-27 10:37:32 +0200685 struct stream *s = __sc_strm(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200686 struct channel *req = &s->req;
687 struct channel *res = &s->res;
688 struct htx_blk *blk = NULL;
689 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200690 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200691 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100692 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100693 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200694
William Lallemand33b0d092021-08-13 16:05:53 +0200695 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200696
697 /* required to stop */
698 if (hc->flags & HTTPCLIENT_FA_STOP)
699 goto end;
700
William Lallemand33b0d092021-08-13 16:05:53 +0200701 switch(appctx->st0) {
702
703 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100704 /* we know that the buffer is empty here, since
705 * it's the first call, we can freely copy the
706 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100707 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100708 if (!ret)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200709 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100710
Christopher Faulet600985d2022-01-12 11:14:08 +0100711 if (!b_data(&hc->req.buf))
712 b_free(&hc->req.buf);
713
William Lallemanddb8a1f32021-11-08 16:55:14 +0100714 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100715 if (!htx)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200716 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100717
William Lallemanddb8a1f32021-11-08 16:55:14 +0100718 channel_add_input(req, htx->data);
719
William Lallemand933fe392021-11-04 09:45:58 +0100720 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
721 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
722 else
723 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
724
William Lallemand33b0d092021-08-13 16:05:53 +0200725 goto more; /* we need to leave the IO handler once we wrote the request */
726 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200727 case HTTPCLIENT_S_REQ_BODY:
728 /* call the payload callback */
729 {
730 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100731 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200732
William Lallemand0da616e2021-10-28 15:34:26 +0200733 /* call the request callback */
734 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100735
William Lallemandccc7ee42022-03-18 17:57:15 +0100736 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100737 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100738
William Lallemandccc7ee42022-03-18 17:57:15 +0100739 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100740 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100741
William Lallemandccc7ee42022-03-18 17:57:15 +0100742 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200743 size_t data = hc_htx->data;
744
William Lallemandccc7ee42022-03-18 17:57:15 +0100745 /* Here htx_to_buf() will set buffer data to 0 because
746 * the HTX is empty, and allow us to do an xfer.
747 */
748 htx_to_buf(hc_htx, &hc->req.buf);
749 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100750 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200751 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100752 } else {
753 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100754
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200755 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100756 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100757
William Lallemandccc7ee42022-03-18 17:57:15 +0100758 /* we must copy the EOM if we empty the buffer */
759 if (htx_is_empty(hc_htx)) {
760 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
761 }
762 htx_to_buf(htx, &req->buf);
763 htx_to_buf(hc_htx, &hc->req.buf);
764 }
765
766
767 if (!b_data(&hc->req.buf))
768 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200769 }
770
William Lallemanddb8a1f32021-11-08 16:55:14 +0100771 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200772 if (!htx)
773 goto more;
774
775 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100776 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet3d433242022-03-03 15:38:39 +0100777 req->flags |= CF_EOI;
Willy Tarreaud869e132022-05-17 18:05:31 +0200778 se_fl_set(appctx->sedesc, SE_FL_EOI);
William Lallemand0da616e2021-10-28 15:34:26 +0200779 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100780 }
William Lallemand0da616e2021-10-28 15:34:26 +0200781
William Lallemand1eca8942022-03-17 14:57:23 +0100782 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200783 }
784 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200785
786 case HTTPCLIENT_S_RES_STLINE:
787 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100788 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200789 goto more;
790 htx = htxbuf(&res->buf);
791 if (!htx)
792 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100793 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200794 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
795 sl = htx_get_blk_ptr(htx, blk);
796 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
797 goto more;
798
799 /* copy the status line in the httpclient */
800 hc->res.status = sl->info.res.status;
801 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
802 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100803 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200804 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100805 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200806 /* caller callback */
807 if (hc->ops.res_stline)
808 hc->ops.res_stline(hc);
809
810 /* if there is no HTX data anymore and the EOM flag is
811 * set, leave (no body) */
812 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
813 appctx->st0 = HTTPCLIENT_S_RES_END;
814 else
815 appctx->st0 = HTTPCLIENT_S_RES_HDR;
816 break;
817
818 case HTTPCLIENT_S_RES_HDR:
819 /* first copy the headers in a local hdrs
820 * structure, once we the total numbers of the
821 * header we allocate the right size and copy
822 * them. The htx block of the headers are
823 * removed each time one is read */
824 {
825 struct http_hdr hdrs[global.tune.max_http_hdr];
826
William Lallemanda625b032022-03-17 14:45:46 +0100827 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200828 goto more;
829 htx = htxbuf(&res->buf);
830 if (!htx)
831 goto more;
832
833 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200834 blk = htx_get_head_blk(htx);
835 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200836 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100837 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200838
Christopher Faulet534645d2022-04-29 13:44:46 +0200839 c_rew(res, sz);
William Lallemandc020b252022-03-09 18:56:02 +0100840
Christopher Faulet18de6f22022-06-01 16:37:49 +0200841 if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100842 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
843 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100844 hdr_num++;
845 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200846 else if (type == HTX_BLK_EOH) {
847 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200848 hdrs[hdr_num].n = IST_NULL;
849 hdrs[hdr_num].v = IST_NULL;
Christopher Faulet18de6f22022-06-01 16:37:49 +0200850 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200851 break;
852 }
Christopher Faulet18de6f22022-06-01 16:37:49 +0200853 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200854 }
855
William Lallemand0d6f7792021-08-20 11:59:49 +0200856 if (hdr_num) {
857 /* alloc and copy the headers in the httpclient struct */
858 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
859 if (!hc->res.hdrs)
860 goto end;
861 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200862
William Lallemand0d6f7792021-08-20 11:59:49 +0200863 /* caller callback */
864 if (hc->ops.res_headers)
865 hc->ops.res_headers(hc);
866 }
William Lallemand33b0d092021-08-13 16:05:53 +0200867
868 /* if there is no HTX data anymore and the EOM flag is
869 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200870 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200871 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200872 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200873 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200874 }
William Lallemand33b0d092021-08-13 16:05:53 +0200875 }
876 break;
877
878 case HTTPCLIENT_S_RES_BODY:
879 /*
880 * The IO handler removes the htx blocks in the response buffer and
881 * push them in the hc->res.buf buffer in a raw format.
882 */
William Lallemanda625b032022-03-17 14:45:46 +0100883 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100884 goto more;
885
William Lallemand33b0d092021-08-13 16:05:53 +0200886 htx = htxbuf(&res->buf);
887 if (!htx || htx_is_empty(htx))
888 goto more;
889
Christopher Faulet600985d2022-01-12 11:14:08 +0100890 if (!b_alloc(&hc->res.buf))
891 goto more;
892
William Lallemand33b0d092021-08-13 16:05:53 +0200893 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100894 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200895
896 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200897 blk = htx_get_head_blk(htx);
898 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100899 enum htx_blk_type type = htx_get_blk_type(blk);
900 size_t count = co_data(res);
901 uint32_t blksz = htx_get_blksz(blk);
902 uint32_t room = b_room(&hc->res.buf);
903 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200904
William Lallemandc8f1eb92022-03-09 11:58:51 +0100905 /* we should try to copy the maximum output data in a block, which fit
906 * the destination buffer */
907 vlen = MIN(count, blksz);
908 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100909
William Lallemandc8f1eb92022-03-09 11:58:51 +0100910 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100911 goto process_data;
912
William Lallemand33b0d092021-08-13 16:05:53 +0200913 if (type == HTX_BLK_DATA) {
914 struct ist v = htx_get_blk_value(htx, blk);
915
William Lallemandc8f1eb92022-03-09 11:58:51 +0100916 __b_putblk(&hc->res.buf, v.ptr, vlen);
917 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200918
William Lallemandc8f1eb92022-03-09 11:58:51 +0100919 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200920 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100921 else
922 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100923
William Lallemand33b0d092021-08-13 16:05:53 +0200924 /* the data must be processed by the caller in the receive phase */
925 if (hc->ops.res_payload)
926 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100927
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500928 /* cannot copy everything, need to process */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100929 if (vlen != blksz)
930 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200931 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100932 if (vlen != blksz)
933 goto process_data;
934
William Lallemand33b0d092021-08-13 16:05:53 +0200935 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100936 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200937 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200938 }
939 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100940
William Lallemand33b0d092021-08-13 16:05:53 +0200941 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100942 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200943 goto more;
944
William Lallemandc8f1eb92022-03-09 11:58:51 +0100945
William Lallemand33b0d092021-08-13 16:05:53 +0200946 /* end of message, we should quit */
947 appctx->st0 = HTTPCLIENT_S_RES_END;
948 break;
949
950 case HTTPCLIENT_S_RES_END:
951 goto end;
952 break;
953 }
954 }
955
956process_data:
957
Willy Tarreaub89f8722022-05-27 10:37:32 +0200958 sc_will_read(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200959
960 return;
Willy Tarreau6486ff82022-09-02 11:42:50 +0200961full:
962 /* There was not enough room in the response channel */
Willy Tarreaub89f8722022-05-27 10:37:32 +0200963 sc_need_room(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200964
Willy Tarreau6486ff82022-09-02 11:42:50 +0200965more:
966 /* we'll automatically be called again on missing data */
William Lallemand33b0d092021-08-13 16:05:53 +0200967 if (appctx->st0 == HTTPCLIENT_S_RES_END)
968 goto end;
969
970 /* The state machine tries to handle as much data as possible, if there
971 * isn't any data to handle and a shutdown is detected, let's stop
972 * everything */
973 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100974 (res->flags & CF_SHUTW) ||
975 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200976 goto end;
977 }
978 return;
979
980end:
Willy Tarreaub89f8722022-05-27 10:37:32 +0200981 sc_shutw(sc);
982 sc_shutr(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200983 return;
984}
985
Christopher Fauletb1e08362022-05-12 15:33:14 +0200986static int httpclient_applet_init(struct appctx *appctx)
987{
988 struct httpclient *hc = appctx->svcctx;
989 struct stream *s;
990 struct sockaddr_storage *addr = NULL;
991 struct sockaddr_storage ss_url = {};
992 struct sockaddr_storage *ss_dst;
993 enum obj_type *target = NULL;
994 struct ist host = IST_NULL;
995 enum http_scheme scheme;
996 int port;
997 int doresolve = 0;
998
999
1000 /* parse the URL and */
1001 httpclient_spliturl(hc->req.url, &scheme, &host, &port);
1002
1003 if (hc->dst) {
1004 /* if httpclient_set_dst() was used, sets the alternative address */
1005 ss_dst = hc->dst;
1006 } else {
1007 /* set the dst using the host, or 0.0.0.0 to resolve */
1008 ist2str(trash.area, host);
1009 ss_dst = str2ip2(trash.area, &ss_url, 0);
1010 if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
1011 doresolve = 1;
1012 ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
1013 }
1014 sock_inet_set_port(ss_dst, port);
1015 }
1016
Christopher Fauletb1e08362022-05-12 15:33:14 +02001017 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
1018 goto out_error;
1019
1020 /* choose the SSL server or not */
1021 switch (scheme) {
1022 case SCH_HTTP:
William Lallemand992ad622022-09-12 17:39:04 +02001023 target = &hc->srv_raw->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001024 break;
1025 case SCH_HTTPS:
1026#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001027 if (hc->srv_ssl) {
1028 target = &hc->srv_ssl->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001029 } else {
1030 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
1031 goto out_free_addr;
1032 }
1033#else
1034 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
1035 goto out_free_addr;
1036#endif
1037 break;
1038 }
1039
William Lallemand992ad622022-09-12 17:39:04 +02001040 if (appctx_finalize_startup(appctx, hc->px, &hc->req.buf) == -1) {
Christopher Fauletb1e08362022-05-12 15:33:14 +02001041 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
1042 goto out_free_addr;
1043 }
1044
1045 s = appctx_strm(appctx);
1046 s->target = target;
1047 /* set the "timeout server" */
1048 s->req.wto = hc->timeout_server;
1049 s->res.rto = hc->timeout_server;
1050
1051 if (doresolve) {
1052 /* in order to do the set-dst we need to put the address on the front */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001053 s->scf->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001054 } else {
1055 /* in cases we don't use the resolve we already have the address
1056 * and must put it on the backend side, some of the cases are
1057 * not meant to be used on the frontend (sockpair, unix socket etc.) */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001058 s->scb->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001059 }
1060
Willy Tarreaucb041662022-05-17 19:44:42 +02001061 s->scb->flags |= SC_FL_NOLINGER;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001062 s->flags |= SF_ASSIGNED;
1063 s->res.flags |= CF_READ_DONTWAIT;
1064
1065 /* applet is waiting for data */
Willy Tarreau90e8b452022-05-25 18:21:43 +02001066 applet_need_more_data(appctx);
Christopher Fauletb1e08362022-05-12 15:33:14 +02001067 appctx_wakeup(appctx);
1068
1069 hc->appctx = appctx;
1070 hc->flags |= HTTPCLIENT_FS_STARTED;
1071
1072 /* The request was transferred when the stream was created. So switch
1073 * directly to REQ_BODY or RES_STLINE state
1074 */
1075 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
1076 return 0;
1077
1078 out_free_addr:
1079 sockaddr_free(&addr);
1080 out_error:
1081 return -1;
1082}
1083
William Lallemand33b0d092021-08-13 16:05:53 +02001084static void httpclient_applet_release(struct appctx *appctx)
1085{
Willy Tarreau1eea6652022-05-05 20:12:01 +02001086 struct httpclient *hc = appctx->svcctx;
William Lallemand33b0d092021-08-13 16:05:53 +02001087
William Lallemand1123dde2021-09-21 10:58:10 +02001088 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +02001089 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +02001090 /* the applet is leaving, remove the ptr so we don't try to call it
1091 * again from the caller */
1092 hc->appctx = NULL;
1093
William Lallemandeb0d4c42022-04-06 14:12:37 +02001094 if (hc->ops.res_end)
1095 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +02001096
1097 /* destroy the httpclient when set to autotokill */
1098 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
1099 httpclient_destroy(hc);
1100 }
1101
William Lallemand33b0d092021-08-13 16:05:53 +02001102 return;
1103}
1104
1105/* HTTP client applet */
1106static struct applet httpclient_applet = {
1107 .obj_type = OBJ_TYPE_APPLET,
1108 .name = "<HTTPCLIENT>",
1109 .fct = httpclient_applet_io_handler,
Christopher Fauletb1e08362022-05-12 15:33:14 +02001110 .init = httpclient_applet_init,
William Lallemand33b0d092021-08-13 16:05:53 +02001111 .release = httpclient_applet_release,
1112};
1113
William Lallemand5392ff62022-04-28 16:55:02 +02001114
William Lallemand54aec5f2022-09-12 16:46:35 +02001115static int httpclient_resolve_init(struct proxy *px)
William Lallemand5392ff62022-04-28 16:55:02 +02001116{
1117 struct act_rule *rule;
1118 int i;
William Lallemand8a734cb2022-05-04 16:10:47 +02001119 char *do_resolve = NULL;
1120 char *http_rules[][11] = {
William Lallemand5392ff62022-04-28 16:55:02 +02001121 { "set-var(txn.hc_ip)", "dst", "" },
William Lallemandd78dfe72022-08-26 16:45:13 +02001122 { do_resolve, "hdr(Host),host_only", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
William Lallemand5392ff62022-04-28 16:55:02 +02001123 { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
1124 { "capture", "var(txn.hc_ip)", "len", "40", "" },
1125 { "set-dst", "var(txn.hc_ip)", "" },
1126 { "" }
1127 };
1128
William Lallemand8a734cb2022-05-04 16:10:47 +02001129 if (!resolvers_id)
1130 resolvers_id = strdup("default");
1131
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001132 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 +02001133 http_rules[1][0] = do_resolve;
1134
William Lallemand7867f632022-05-05 19:02:59 +02001135 /* Try to create the default resolvers section */
1136 resolvers_create_default();
1137
William Lallemand8a734cb2022-05-04 16:10:47 +02001138 /* if the resolver does not exist and no hard_error was set, simply ignore resolving */
1139 if (!find_resolvers_by_id(resolvers_id) && !hard_error_resolvers) {
1140 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001141 return 0;
William Lallemand8a734cb2022-05-04 16:10:47 +02001142 }
William Lallemand5392ff62022-04-28 16:55:02 +02001143
1144
1145 for (i = 0; *http_rules[i][0] != '\0'; i++) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001146 rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, px);
William Lallemand5392ff62022-04-28 16:55:02 +02001147 if (!rule) {
William Lallemand8a734cb2022-05-04 16:10:47 +02001148 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001149 ha_alert("Couldn't setup the httpclient resolver.\n");
1150 return 1;
1151 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001152 LIST_APPEND(&px->http_req_rules, &rule->list);
William Lallemand5392ff62022-04-28 16:55:02 +02001153 }
1154
William Lallemand8a734cb2022-05-04 16:10:47 +02001155 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001156 return 0;
1157}
1158
William Lallemand83614a92021-08-13 14:47:57 +02001159/*
William Lallemand54aec5f2022-09-12 16:46:35 +02001160 * Creates an internal proxy which will be used for httpclient.
1161 * This will allocate 2 servers (raw and ssl) and 1 proxy.
1162 *
1163 * This function must be called from a precheck callback.
1164 *
1165 * Return a proxy or NULL.
William Lallemand83614a92021-08-13 14:47:57 +02001166 */
William Lallemand54aec5f2022-09-12 16:46:35 +02001167struct proxy *httpclient_create_proxy(const char *id)
William Lallemand83614a92021-08-13 14:47:57 +02001168{
William Lallemand85af49c2022-05-04 14:33:57 +02001169 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001170 char *errmsg = NULL;
William Lallemand54aec5f2022-09-12 16:46:35 +02001171 struct proxy *px = NULL;
1172 struct server *srv_raw = NULL;
1173#ifdef USE_OPENSSL
1174 struct server *srv_ssl = NULL;
1175#endif
William Lallemand83614a92021-08-13 14:47:57 +02001176
William Lallemandc6ceba32022-04-22 16:49:53 +02001177 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001178 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001179
William Lallemand54aec5f2022-09-12 16:46:35 +02001180 px = alloc_new_proxy(id, PR_CAP_LISTEN|PR_CAP_INT|PR_CAP_HTTPCLIENT, &errmsg);
1181 if (!px) {
William Lallemand85af49c2022-05-04 14:33:57 +02001182 memprintf(&errmsg, "couldn't allocate proxy.");
William Lallemand83614a92021-08-13 14:47:57 +02001183 err_code |= ERR_ALERT | ERR_FATAL;
1184 goto err;
1185 }
1186
William Lallemand54aec5f2022-09-12 16:46:35 +02001187 proxy_preset_defaults(px);
Willy Tarreau0e72e402021-08-20 10:23:12 +02001188
William Lallemand54aec5f2022-09-12 16:46:35 +02001189 px->options |= PR_O_WREQ_BODY;
1190 px->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
1191 px->options2 |= PR_O2_INDEPSTR;
1192 px->mode = PR_MODE_HTTP;
1193 px->maxconn = 0;
1194 px->accept = NULL;
1195 px->conn_retries = CONN_RETRIES;
1196 px->timeout.client = TICK_ETERNITY;
William Lallemand83614a92021-08-13 14:47:57 +02001197 /* The HTTP Client use the "option httplog" with the global log server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001198 px->conf.logformat_string = default_http_log_format;
1199 px->http_needed = 1;
William Lallemand83614a92021-08-13 14:47:57 +02001200
1201 /* clear HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001202 srv_raw = new_server(px);
1203 if (!srv_raw) {
William Lallemand83614a92021-08-13 14:47:57 +02001204 memprintf(&errmsg, "out of memory.");
William Lallemand85af49c2022-05-04 14:33:57 +02001205 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001206 goto err;
1207 }
1208
William Lallemand54aec5f2022-09-12 16:46:35 +02001209 srv_settings_cpy(srv_raw, &px->defsrv, 0);
1210 srv_raw->iweight = 0;
1211 srv_raw->uweight = 0;
1212 srv_raw->xprt = xprt_get(XPRT_RAW);
1213 srv_raw->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1214 srv_raw->id = strdup("<HTTPCLIENT>");
1215 if (!srv_raw->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001216 memprintf(&errmsg, "out of memory.");
1217 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001218 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001219 }
William Lallemand83614a92021-08-13 14:47:57 +02001220
William Lallemand957ab132021-08-24 18:33:28 +02001221#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001222 /* SSL HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001223 srv_ssl = new_server(px);
1224 if (!srv_ssl) {
William Lallemand83614a92021-08-13 14:47:57 +02001225 memprintf(&errmsg, "out of memory.");
1226 err_code |= ERR_ALERT | ERR_FATAL;
1227 goto err;
1228 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001229 srv_settings_cpy(srv_ssl, &px->defsrv, 0);
1230 srv_ssl->iweight = 0;
1231 srv_ssl->uweight = 0;
1232 srv_ssl->xprt = xprt_get(XPRT_SSL);
1233 srv_ssl->use_ssl = 1;
1234 srv_ssl->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1235 srv_ssl->id = strdup("<HTTPSCLIENT>");
1236 if (!srv_ssl->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001237 memprintf(&errmsg, "out of memory.");
1238 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001239 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001240 }
William Lallemand83614a92021-08-13 14:47:57 +02001241
Willy Tarreaudf3231c2022-09-02 09:02:21 +02001242#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
William Lallemand54aec5f2022-09-12 16:46:35 +02001243 if (ssl_sock_parse_alpn("h2,http/1.1", &srv_ssl->ssl_ctx.alpn_str, &srv_ssl->ssl_ctx.alpn_len, &errmsg) != 0) {
Willy Tarreaudf3231c2022-09-02 09:02:21 +02001244 err_code |= ERR_ALERT | ERR_FATAL;
1245 goto err;
1246 }
1247#endif
William Lallemand54aec5f2022-09-12 16:46:35 +02001248 srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001249 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001250 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
William Lallemand683fbb82022-05-04 15:43:01 +02001251
1252 if (!httpclient_ssl_ca_file)
1253 httpclient_ssl_ca_file = strdup("@system-ca");
1254
William Lallemand54aec5f2022-09-12 16:46:35 +02001255 srv_ssl->ssl_ctx.ca_file = httpclient_ssl_ca_file;
1256 if (!ssl_store_load_locations_file(srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT)) {
William Lallemand6fce46a2022-05-04 14:53:41 +02001257 /* if we failed to load the ca-file, only quits in
1258 * error with hard_error, otherwise just disable the
1259 * feature. */
1260 if (hard_error_ssl) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001261 memprintf(&errmsg, "cannot initialize SSL verify with 'ca-file \"%s\"'.", srv_ssl->ssl_ctx.ca_file);
William Lallemand6fce46a2022-05-04 14:53:41 +02001262 err_code |= ERR_ALERT | ERR_FATAL;
1263 goto err;
1264 } else {
William Lallemand54aec5f2022-09-12 16:46:35 +02001265 ha_free(&srv_ssl->ssl_ctx.ca_file);
1266 srv_drop(srv_ssl);
1267 srv_ssl = NULL;
William Lallemand6fce46a2022-05-04 14:53:41 +02001268 }
William Lallemand4006b0f2022-04-25 18:23:35 +02001269 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001270 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001271
William Lallemand957ab132021-08-24 18:33:28 +02001272#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001273
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001274 /* add the proxy in the proxy list only if everything is successful */
William Lallemand54aec5f2022-09-12 16:46:35 +02001275 px->next = proxies_list;
1276 proxies_list = px;
William Lallemand83614a92021-08-13 14:47:57 +02001277
William Lallemand54aec5f2022-09-12 16:46:35 +02001278 if (httpclient_resolve_init(px) != 0) {
William Lallemand85af49c2022-05-04 14:33:57 +02001279 memprintf(&errmsg, "cannot initialize resolvers.");
1280 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand5392ff62022-04-28 16:55:02 +02001281 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001282 }
William Lallemand5392ff62022-04-28 16:55:02 +02001283
William Lallemand211c9672021-08-24 17:18:13 +02001284 /* link the 2 servers in the proxy */
William Lallemand54aec5f2022-09-12 16:46:35 +02001285 srv_raw->next = px->srv;
1286 px->srv = srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +02001287
1288#ifdef USE_OPENSSL
William Lallemand54aec5f2022-09-12 16:46:35 +02001289 if (srv_ssl) {
1290 srv_ssl->next = px->srv;
1291 px->srv = srv_ssl;
William Lallemand4006b0f2022-04-25 18:23:35 +02001292 }
William Lallemand957ab132021-08-24 18:33:28 +02001293#endif
1294
William Lallemand211c9672021-08-24 17:18:13 +02001295
William Lallemand83614a92021-08-13 14:47:57 +02001296err:
William Lallemand85af49c2022-05-04 14:33:57 +02001297 if (err_code & ERR_CODE) {
1298 ha_alert("httpclient: cannot initialize: %s\n", errmsg);
1299 free(errmsg);
William Lallemand54aec5f2022-09-12 16:46:35 +02001300 srv_drop(srv_raw);
1301#ifdef USE_OPENSSL
1302 srv_drop(srv_ssl);
1303#endif
1304 free_proxy(px);
1305
1306 return NULL;
1307 }
1308 return px;
1309}
1310
1311/*
1312 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
1313 * the other for HTTPS.
1314 */
1315static int httpclient_precheck()
1316{
1317 struct server *srv;
1318
1319 /* initialize the default httpclient_proxy which is used for the CLI and the lua */
1320
1321 httpclient_proxy = httpclient_create_proxy("<HTTPCLIENT>");
1322 if (!httpclient_proxy)
1323 return 1;
1324
1325 /* store the ptr of the 2 servers */
1326 for (srv = httpclient_proxy->srv; srv != NULL; srv = srv->next) {
1327 if (srv->xprt == xprt_get(XPRT_RAW)) {
1328 httpclient_srv_raw = srv;
William Lallemand957ab132021-08-24 18:33:28 +02001329#ifdef USE_OPENSSL
William Lallemand54aec5f2022-09-12 16:46:35 +02001330 } else if (srv->xprt == xprt_get(XPRT_SSL)) {
1331 httpclient_srv_ssl = srv;
William Lallemand957ab132021-08-24 18:33:28 +02001332#endif
William Lallemand54aec5f2022-09-12 16:46:35 +02001333 }
William Lallemand85af49c2022-05-04 14:33:57 +02001334 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001335
1336 if (!httpclient_srv_raw)
1337 return 1;
1338#ifdef USE_OPENSSL
1339 if (!httpclient_srv_ssl)
1340 return 1;
1341#endif
1342
1343 return 0;
William Lallemand83614a92021-08-13 14:47:57 +02001344}
1345
William Lallemand2c8b0842022-04-22 15:16:09 +02001346static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001347{
William Lallemand85af49c2022-05-04 14:33:57 +02001348 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001349 struct logsrv *logsrv;
William Lallemand992ad622022-09-12 17:39:04 +02001350 struct proxy *curproxy = NULL;
William Lallemand71e31582022-03-16 15:47:47 +01001351 char *errmsg = NULL;
William Lallemand992ad622022-09-12 17:39:04 +02001352#ifdef USE_OPENSSL
1353 struct server *srv = NULL;
1354 struct server *srv_ssl = NULL;
1355#endif
William Lallemand83614a92021-08-13 14:47:57 +02001356
William Lallemandc6ceba32022-04-22 16:49:53 +02001357 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001358 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001359
William Lallemand992ad622022-09-12 17:39:04 +02001360 /* Initialize the logs for every proxy dedicated to the httpclient */
1361 for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
William Lallemand83614a92021-08-13 14:47:57 +02001362
William Lallemand992ad622022-09-12 17:39:04 +02001363 if (!(curproxy->cap & PR_CAP_HTTPCLIENT))
1364 continue;
William Lallemand83614a92021-08-13 14:47:57 +02001365
William Lallemand992ad622022-09-12 17:39:04 +02001366 /* copy logs from "global" log list */
1367 list_for_each_entry(logsrv, &global.logsrvs, list) {
1368 struct logsrv *node = malloc(sizeof(*node));
1369
1370 if (!node) {
1371 memprintf(&errmsg, "out of memory.");
1372 err_code |= ERR_ALERT | ERR_FATAL;
1373 goto err;
1374 }
1375
1376 memcpy(node, logsrv, sizeof(*node));
1377 LIST_INIT(&node->list);
1378 LIST_APPEND(&curproxy->logsrvs, &node->list);
1379 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1380 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001381 }
William Lallemand992ad622022-09-12 17:39:04 +02001382 if (curproxy->conf.logformat_string) {
1383 curproxy->conf.args.ctx = ARGC_LOG;
1384 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1385 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
1386 SMP_VAL_FE_LOG_END, &errmsg)) {
1387 memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
1388 err_code |= ERR_ALERT | ERR_FATAL;
1389 goto err;
1390 }
1391 curproxy->conf.args.file = NULL;
1392 curproxy->conf.args.line = 0;
1393 }
William Lallemand71e31582022-03-16 15:47:47 +01001394
1395#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001396 /* initialize the SNI for the SSL servers */
1397
1398 for (srv = curproxy->srv; srv != NULL; srv = srv->next) {
1399 if (srv->xprt == xprt_get(XPRT_SSL)) {
1400 srv_ssl = srv;
1401 }
William Lallemand715c1012022-03-16 16:39:23 +01001402 }
William Lallemand992ad622022-09-12 17:39:04 +02001403 if (srv_ssl) {
1404 /* init the SNI expression */
1405 /* always use the host header as SNI, without the port */
1406 srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1407 err_code |= server_parse_sni_expr(srv_ssl, curproxy, &errmsg);
1408 if (err_code & ERR_CODE) {
1409 memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
1410 goto err;
1411 }
1412 }
William Lallemand71e31582022-03-16 15:47:47 +01001413#endif
William Lallemand992ad622022-09-12 17:39:04 +02001414 }
William Lallemand71e31582022-03-16 15:47:47 +01001415
William Lallemand83614a92021-08-13 14:47:57 +02001416err:
William Lallemand85af49c2022-05-04 14:33:57 +02001417 if (err_code & ERR_CODE) {
1418 ha_alert("httpclient: failed to initialize: %s\n", errmsg);
1419 free(errmsg);
1420
1421 }
1422 return err_code;
William Lallemand83614a92021-08-13 14:47:57 +02001423}
1424
William Lallemand83614a92021-08-13 14:47:57 +02001425/* initialize the proxy and servers for the HTTP client */
1426
William Lallemand2c8b0842022-04-22 15:16:09 +02001427REGISTER_PRE_CHECK(httpclient_precheck);
1428REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001429
William Lallemand8a734cb2022-05-04 16:10:47 +02001430static int httpclient_parse_global_resolvers(char **args, int section_type, struct proxy *curpx,
1431 const struct proxy *defpx, const char *file, int line,
1432 char **err)
1433{
1434 if (too_many_args(1, args, err, NULL))
1435 return -1;
1436
1437 /* any configuration should set the hard_error flag */
1438 hard_error_resolvers = 1;
1439
1440 free(resolvers_id);
1441 resolvers_id = strdup(args[1]);
1442
1443 return 0;
1444}
1445
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001446static int httpclient_parse_global_prefer(char **args, int section_type, struct proxy *curpx,
1447 const struct proxy *defpx, const char *file, int line,
1448 char **err)
1449{
1450 if (too_many_args(1, args, err, NULL))
1451 return -1;
1452
1453 /* any configuration should set the hard_error flag */
1454 hard_error_resolvers = 1;
1455
1456
1457 if (strcmp(args[1],"ipv4") == 0)
1458 resolvers_prefer = "ipv4";
1459 else if (strcmp(args[1],"ipv6") == 0)
1460 resolvers_prefer = "ipv6";
1461 else {
1462 ha_alert("parsing [%s:%d] : '%s' expects 'ipv4' or 'ipv6' as argument.\n", file, line, args[0]);
1463 return -1;
1464 }
1465
1466 return 0;
1467}
William Lallemand8a734cb2022-05-04 16:10:47 +02001468
1469
William Lallemandeaa703e2022-04-22 17:52:33 +02001470#ifdef USE_OPENSSL
William Lallemand683fbb82022-05-04 15:43:01 +02001471static int httpclient_parse_global_ca_file(char **args, int section_type, struct proxy *curpx,
1472 const struct proxy *defpx, const char *file, int line,
1473 char **err)
1474{
1475 if (too_many_args(1, args, err, NULL))
1476 return -1;
1477
1478 /* any configuration should set the hard_error flag */
1479 hard_error_ssl = 1;
1480
1481 free(httpclient_ssl_ca_file);
1482 httpclient_ssl_ca_file = strdup(args[1]);
1483
1484 return 0;
1485}
1486
William Lallemandeaa703e2022-04-22 17:52:33 +02001487static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1488 const struct proxy *defpx, const char *file, int line,
1489 char **err)
1490{
1491 if (too_many_args(1, args, err, NULL))
1492 return -1;
1493
William Lallemand6fce46a2022-05-04 14:53:41 +02001494 /* any configuration should set the hard_error flag */
1495 hard_error_ssl = 1;
1496
William Lallemandeaa703e2022-04-22 17:52:33 +02001497 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001498 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001499 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001500 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001501 else {
1502 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1503 return -1;
1504 }
1505
1506 return 0;
1507}
William Lallemand8a734cb2022-05-04 16:10:47 +02001508#endif /* ! USE_OPENSSL */
William Lallemandeaa703e2022-04-22 17:52:33 +02001509
1510static struct cfg_kw_list cfg_kws = {ILH, {
William Lallemand8a734cb2022-05-04 16:10:47 +02001511 { CFG_GLOBAL, "httpclient.resolvers.id", httpclient_parse_global_resolvers },
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001512 { CFG_GLOBAL, "httpclient.resolvers.prefer", httpclient_parse_global_prefer },
William Lallemand8a734cb2022-05-04 16:10:47 +02001513#ifdef USE_OPENSSL
William Lallemand9ff95e22022-05-04 13:52:29 +02001514 { CFG_GLOBAL, "httpclient.ssl.verify", httpclient_parse_global_verify },
William Lallemand683fbb82022-05-04 15:43:01 +02001515 { CFG_GLOBAL, "httpclient.ssl.ca-file", httpclient_parse_global_ca_file },
William Lallemand8a734cb2022-05-04 16:10:47 +02001516#endif
William Lallemandeaa703e2022-04-22 17:52:33 +02001517 { 0, NULL, NULL },
1518}};
1519
1520INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);