blob: c2dec8bbe8045f32cad010d8cf7a100d57da5ed0 [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
William Lallemand83614a92021-08-13 14:47:57 +020042static struct proxy *httpclient_proxy;
William Lallemand6fce46a2022-05-04 14:53:41 +020043
William Lallemand957ab132021-08-24 18:33:28 +020044#ifdef USE_OPENSSL
William Lallemand6fce46a2022-05-04 14:53:41 +020045/* if the httpclient is not configured, error are ignored and features are limited */
46static int hard_error_ssl = 0;
William Lallemandf1344b32022-04-26 12:00:06 +020047static int httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemand683fbb82022-05-04 15:43:01 +020048static char *httpclient_ssl_ca_file = NULL;
William Lallemand957ab132021-08-24 18:33:28 +020049#endif
William Lallemand33b0d092021-08-13 16:05:53 +020050static struct applet httpclient_applet;
51
William Lallemand7c5a7ef2022-05-04 15:59:44 +020052/* if the httpclient is not configured, error are ignored and features are limited */
William Lallemand8a734cb2022-05-04 16:10:47 +020053static int hard_error_resolvers = 0;
54static char *resolvers_id = NULL;
William Lallemand7c5a7ef2022-05-04 15:59:44 +020055static char *resolvers_prefer = NULL;
William Lallemandeaa703e2022-04-22 17:52:33 +020056
William Lallemand03a4eb12021-08-18 16:46:21 +020057/* --- This part of the file implement an HTTP client over the CLI ---
58 * The functions will be starting by "hc_cli" for "httpclient cli"
59 */
60
Willy Tarreau89a7c412022-05-05 19:38:21 +020061/* the CLI context for the httpclient command */
62struct hcli_svc_ctx {
63 struct httpclient *hc; /* the httpclient instance */
64 uint flags; /* flags from HC_CLI_F_* above */
65};
William Lallemand03a4eb12021-08-18 16:46:21 +020066
67/* These are the callback used by the HTTP Client when it needs to notify new
Willy Tarreau89a7c412022-05-05 19:38:21 +020068 * data, we only sets a flag in the IO handler via the svcctx.
69 */
William Lallemand03a4eb12021-08-18 16:46:21 +020070void hc_cli_res_stline_cb(struct httpclient *hc)
71{
72 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020073 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020074
William Lallemanddfc3f892021-08-20 11:35:29 +020075 if (!appctx)
76 return;
77
Willy Tarreau89a7c412022-05-05 19:38:21 +020078 ctx = appctx->svcctx;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +010079 ctx->flags |= HC_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020080 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020081}
82
83void hc_cli_res_headers_cb(struct httpclient *hc)
84{
85 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020086 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020087
William Lallemanddfc3f892021-08-20 11:35:29 +020088 if (!appctx)
89 return;
90
Willy Tarreau89a7c412022-05-05 19:38:21 +020091 ctx = appctx->svcctx;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +010092 ctx->flags |= HC_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020093 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020094}
95
96void hc_cli_res_body_cb(struct httpclient *hc)
97{
98 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020099 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200100
William Lallemanddfc3f892021-08-20 11:35:29 +0200101 if (!appctx)
102 return;
103
Willy Tarreau89a7c412022-05-05 19:38:21 +0200104 ctx = appctx->svcctx;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100105 ctx->flags |= HC_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +0200106 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200107}
108
109void hc_cli_res_end_cb(struct httpclient *hc)
110{
111 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200112 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200113
William Lallemanddfc3f892021-08-20 11:35:29 +0200114 if (!appctx)
115 return;
116
Willy Tarreau89a7c412022-05-05 19:38:21 +0200117 ctx = appctx->svcctx;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100118 ctx->flags |= HC_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200119 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200120}
121
122/*
123 * Parse an httpclient keyword on the cli:
124 * httpclient <ID> <method> <URI>
125 */
126static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
127{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200128 struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemand03a4eb12021-08-18 16:46:21 +0200129 struct httpclient *hc;
130 char *err = NULL;
131 enum http_meth_t meth;
132 char *meth_str;
133 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200134 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200135
136 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
137 return 1;
138
139 if (!*args[1] || !*args[2]) {
140 memprintf(&err, ": not enough parameters");
141 goto err;
142 }
143
144 meth_str = args[1];
145 uri = ist(args[2]);
146
William Lallemanddec25c32021-10-25 19:48:37 +0200147 if (payload)
148 body = ist(payload);
149
William Lallemand03a4eb12021-08-18 16:46:21 +0200150 meth = find_http_meth(meth_str, strlen(meth_str));
151
152 hc = httpclient_new(appctx, meth, uri);
153 if (!hc) {
154 goto err;
155 }
156
157 /* update the httpclient callbacks */
158 hc->ops.res_stline = hc_cli_res_stline_cb;
159 hc->ops.res_headers = hc_cli_res_headers_cb;
160 hc->ops.res_payload = hc_cli_res_body_cb;
161 hc->ops.res_end = hc_cli_res_end_cb;
162
Willy Tarreau89a7c412022-05-05 19:38:21 +0200163 ctx->hc = hc; /* store the httpclient ptr in the applet */
164 ctx->flags = 0;
William Lallemand03a4eb12021-08-18 16:46:21 +0200165
William Lallemandbad9c8c2022-01-14 14:10:33 +0100166 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200167 goto err;
168
169
170 if (!httpclient_start(hc))
171 goto err;
172
173 return 0;
174
175err:
176 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
177 return cli_err(appctx, err);
178}
179
180/* This function dumps the content of the httpclient receive buffer
181 * on the CLI output
182 *
183 * Return 1 when the processing is finished
184 * return 0 if it needs to be called again
185 */
186static int hc_cli_io_handler(struct appctx *appctx)
187{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200188 struct hcli_svc_ctx *ctx = appctx->svcctx;
Willy Tarreauc12b3212022-05-27 11:08:15 +0200189 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200190 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200191 struct http_hdr *hdrs, *hdr;
192
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100193 if (ctx->flags & HC_F_RES_STLINE) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200194 chunk_printf(&trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
195 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
196 if (applet_putchk(appctx, &trash) == -1)
197 goto more;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100198 ctx->flags &= ~HC_F_RES_STLINE;
William Lallemand03a4eb12021-08-18 16:46:21 +0200199 }
200
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100201 if (ctx->flags & HC_F_RES_HDR) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200202 chunk_reset(&trash);
William Lallemand03a4eb12021-08-18 16:46:21 +0200203 hdrs = hc->res.hdrs;
204 for (hdr = hdrs; isttest(hdr->v); hdr++) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200205 if (!h1_format_htx_hdr(hdr->n, hdr->v, &trash))
206 goto too_many_hdrs;
William Lallemand03a4eb12021-08-18 16:46:21 +0200207 }
Christopher Faulet0158bb22022-06-01 17:08:19 +0200208 if (!chunk_memcat(&trash, "\r\n", 2))
209 goto too_many_hdrs;
210 if (applet_putchk(appctx, &trash) == -1)
211 goto more;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100212 ctx->flags &= ~HC_F_RES_HDR;
William Lallemand03a4eb12021-08-18 16:46:21 +0200213 }
214
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100215 if (ctx->flags & HC_F_RES_BODY) {
William Lallemand03a4eb12021-08-18 16:46:21 +0200216 int ret;
217
Willy Tarreau475e4632022-05-27 10:26:46 +0200218 ret = httpclient_res_xfer(hc, sc_ib(sc));
219 channel_add_input(sc_ic(sc), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200220
Christopher Faulet0158bb22022-06-01 17:08:19 +0200221 /* remove the flag if the buffer was emptied */
222 if (httpclient_data(hc))
223 goto more;
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100224 ctx->flags &= ~HC_F_RES_BODY;
William Lallemand03a4eb12021-08-18 16:46:21 +0200225 }
226
227 /* we must close only if F_END is the last flag */
Remi Tricot-Le Breton95e7cf12022-12-20 11:11:03 +0100228 if (ctx->flags == HC_F_RES_END) {
229 ctx->flags &= ~HC_F_RES_END;
Christopher Faulet89f26262022-06-01 17:17:24 +0200230 goto end;
William Lallemand03a4eb12021-08-18 16:46:21 +0200231 }
232
Christopher Faulet0158bb22022-06-01 17:08:19 +0200233more:
234 if (!ctx->flags)
235 applet_have_no_more_data(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200236 return 0;
Christopher Faulet89f26262022-06-01 17:17:24 +0200237end:
238 return 1;
Christopher Faulet0158bb22022-06-01 17:08:19 +0200239
240too_many_hdrs:
241 return cli_err(appctx, "Too many headers.\n");
William Lallemand03a4eb12021-08-18 16:46:21 +0200242}
243
244static void hc_cli_release(struct appctx *appctx)
245{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200246 struct hcli_svc_ctx *ctx = appctx->svcctx;
247 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200248
249 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200250 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200251
252 return;
253}
254
255/* register cli keywords */
256static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100257 { { "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 +0200258 { { NULL }, NULL, NULL, NULL }
259}};
260
261INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
262
263
264/* --- This part of the file implements the actual HTTP client API --- */
265
William Lallemand33b0d092021-08-13 16:05:53 +0200266/*
267 * Generate a simple request and fill the httpclient request buffer with it.
268 * The request contains a request line generated from the absolute <url> and
269 * <meth> as well as list of headers <hdrs>.
270 *
271 * If the buffer was filled correctly the function returns 0, if not it returns
272 * an error_code but there is no guarantee that the buffer wasn't modified.
273 */
William Lallemanddec25c32021-10-25 19:48:37 +0200274int 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 +0200275{
276 struct htx_sl *sl;
277 struct htx *htx;
278 int err_code = 0;
279 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200280 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 +0100281 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100282 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200283
Christopher Faulet600985d2022-01-12 11:14:08 +0100284 if (!b_alloc(&hc->req.buf))
285 goto error;
286
William Lallemand33b0d092021-08-13 16:05:53 +0200287 if (meth >= HTTP_METH_OTHER)
288 goto error;
289
290 meth_ist = http_known_methods[meth];
291
292 vsn = ist("HTTP/1.1");
293
294 htx = htx_from_buf(&hc->req.buf);
295 if (!htx)
296 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100297
298 if (!hc->ops.req_payload && !isttest(payload))
299 flags |= HTX_SL_F_BODYLESS;
300
William Lallemand33b0d092021-08-13 16:05:53 +0200301 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
302 if (!sl) {
303 goto error;
304 }
305 sl->info.req.meth = meth;
306
William Lallemandf03b53c2021-11-24 15:38:17 +0100307 for (i = 0; hdrs && hdrs[i].n.len; i++) {
308 /* Don't check the value length because a header value may be empty */
309 if (isttest(hdrs[i].v) == 0)
310 continue;
311
312 if (isteqi(hdrs[i].n, ist("host")))
313 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100314 else if (isteqi(hdrs[i].n, ist("accept")))
315 foundaccept = 1;
316 else if (isteqi(hdrs[i].n, ist("user-agent")))
317 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100318
319 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
320 goto error;
321 }
William Lallemand33b0d092021-08-13 16:05:53 +0200322
William Lallemandf03b53c2021-11-24 15:38:17 +0100323 if (!foundhost) {
324 /* Add Host Header from URL */
325 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200326 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100327 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200328 goto error;
329 }
William Lallemand33b0d092021-08-13 16:05:53 +0200330
William Lallemandbad9c8c2022-01-14 14:10:33 +0100331 if (!foundaccept) {
332 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
333 goto error;
334 }
335
336 if (!foundua) {
337 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
338 goto error;
339 }
340
341
William Lallemandf03b53c2021-11-24 15:38:17 +0100342 if (!htx_add_endof(htx, HTX_BLK_EOH))
343 goto error;
344
William Lallemanda80b22e2022-12-22 14:49:43 +0100345 if (isttest(payload) && istlen(payload)) {
William Lallemanddec25c32021-10-25 19:48:37 +0200346 /* add the payload if it can feat in the buffer, no need to set
347 * the Content-Length, the data will be sent chunked */
348 if (!htx_add_data_atonce(htx, payload))
349 goto error;
350 }
351
William Lallemand0da616e2021-10-28 15:34:26 +0200352 /* If req.payload was set, does not set the end of stream which *MUST*
353 * be set in the callback */
354 if (!hc->ops.req_payload)
355 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200356
357 htx_to_buf(htx, &hc->req.buf);
358
359 return 0;
360error:
361 err_code |= ERR_ALERT | ERR_ABORT;
362 return err_code;
363}
364
365/*
366 * transfer the response to the destination buffer and wakeup the HTTP client
367 * applet so it could fill again its buffer.
368 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500369 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200370 */
371int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
372{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100373 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200374 int ret;
375
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100376 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200377 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100378 if (!b_data(&hc->res.buf)) {
379 b_free(&hc->res.buf);
380 if (hc->appctx)
381 appctx_wakeup(hc->appctx);
382 }
William Lallemand33b0d092021-08-13 16:05:53 +0200383 return ret;
384}
385
386/*
William Lallemand0da616e2021-10-28 15:34:26 +0200387 * Transfer raw HTTP payload from src, and insert it into HTX format in the
388 * httpclient.
389 *
390 * Must be used to transfer the request body.
391 * Then wakeup the httpclient so it can transfer it.
392 *
393 * <end> tries to add the ending data flag if it succeed to copy all data.
394 *
395 * Return the number of bytes copied from src.
396 */
397int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
398{
399 int ret = 0;
400 struct htx *htx;
401
Christopher Faulet600985d2022-01-12 11:14:08 +0100402 if (!b_alloc(&hc->req.buf))
403 goto error;
404
William Lallemand0da616e2021-10-28 15:34:26 +0200405 htx = htx_from_buf(&hc->req.buf);
406 if (!htx)
407 goto error;
408
409 if (hc->appctx)
410 appctx_wakeup(hc->appctx);
411
412 ret += htx_add_data(htx, src);
413
414
415 /* if we copied all the data and the end flag is set */
416 if ((istlen(src) == ret) && end) {
Christopher Faulet48005de2022-10-14 15:10:24 +0200417 /* no more data are expected. If the HTX buffer is empty, be
418 * sure to add something (EOT block in this case) to have
419 * something to send. It is important to be sure the EOM flags
420 * will be handled by the endpoint. Because the message is
421 * empty, this should not fail. Otherwise it is an error
422 */
423 if (htx_is_empty(htx)) {
424 if (!htx_add_endof(htx, HTX_BLK_EOT))
425 goto error;
426 }
William Lallemand0da616e2021-10-28 15:34:26 +0200427 htx->flags |= HTX_FL_EOM;
428 }
429 htx_to_buf(htx, &hc->req.buf);
430
431error:
432
433 return ret;
434}
435
William Lallemandb4a4ef62022-02-23 14:18:16 +0100436/* Set the 'timeout server' in ms for the next httpclient request */
437void httpclient_set_timeout(struct httpclient *hc, int timeout)
438{
439 hc->timeout_server = timeout;
440}
441
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100442/*
443 * Sets a destination for the httpclient from an HAProxy addr format
444 * This will prevent to determine the destination from the URL
445 * Return 0 in case of success or -1 otherwise.
446 */
447int httpclient_set_dst(struct httpclient *hc, const char *dst)
448{
449 struct sockaddr_storage *sk;
450 char *errmsg = NULL;
451
452 sockaddr_free(&hc->dst);
453 /* 'sk' is statically allocated (no need to be freed). */
454 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
455 &errmsg, NULL, NULL,
456 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
457 if (!sk) {
458 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
459 free(errmsg);
460 return -1;
461 }
462
463 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
464 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
465 return -1;
466 }
467
468 return 0;
469}
William Lallemand0da616e2021-10-28 15:34:26 +0200470
471/*
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200472 * Split <url> in <scheme>, <host>, <port>
William Lallemand7f1df8f2022-04-14 17:50:20 +0200473 */
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200474static int httpclient_spliturl(struct ist url, enum http_scheme *scheme,
475 struct ist *host, int *port)
William Lallemand7f1df8f2022-04-14 17:50:20 +0200476{
477 enum http_scheme scheme_tmp = SCH_HTTP;
478 int port_tmp = 0;
479 struct ist scheme_ist, authority_ist, host_ist, port_ist;
480 char *p, *end;
481 struct http_uri_parser parser;
482
483 parser = http_uri_parser_init(url);
484 scheme_ist = http_parse_scheme(&parser);
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200485 if (!isttest(scheme_ist)) {
486 return 0;
487 }
William Lallemand7f1df8f2022-04-14 17:50:20 +0200488
489 if (isteqi(scheme_ist, ist("http://"))){
490 scheme_tmp = SCH_HTTP;
491 port_tmp = 80;
492 } else if (isteqi(scheme_ist, ist("https://"))) {
493 scheme_tmp = SCH_HTTPS;
494 port_tmp = 443;
495 }
496
497 authority_ist = http_parse_authority(&parser, 1);
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200498 if (!isttest(authority_ist)) {
499 return 0;
500 }
William Lallemand7f1df8f2022-04-14 17:50:20 +0200501 p = end = istend(authority_ist);
502
503 /* look for a port at the end of the authority */
504 while (p > istptr(authority_ist) && isdigit((unsigned char)*--p))
505 ;
506
507 if (*p == ':') {
508 host_ist = ist2(istptr(authority_ist), p - istptr(authority_ist));
509 port_ist = istnext(ist2(p, end - p));
510 ist2str(trash.area, port_ist);
511 port_tmp = atoi(trash.area);
512 } else {
513 host_ist = authority_ist;
514 }
515
516 if (scheme)
517 *scheme = scheme_tmp;
518 if (host)
519 *host = host_ist;
520 if (port)
521 *port = port_tmp;
522
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200523 return 1;
William Lallemand7f1df8f2022-04-14 17:50:20 +0200524}
525
526/*
William Lallemand33b0d092021-08-13 16:05:53 +0200527 * Start the HTTP client
528 * Create the appctx, session, stream and wakeup the applet
529 *
William Lallemand33b0d092021-08-13 16:05:53 +0200530 * Return the <appctx> or NULL if it failed
531 */
532struct appctx *httpclient_start(struct httpclient *hc)
533{
534 struct applet *applet = &httpclient_applet;
535 struct appctx *appctx;
William Lallemand33b0d092021-08-13 16:05:53 +0200536
William Lallemand5085bc32022-02-17 12:52:09 +0100537 /* if the client was started and not ended, an applet is already
538 * running, we shouldn't try anything */
539 if (httpclient_started(hc) && !httpclient_ended(hc))
540 return NULL;
541
William Lallemand33b0d092021-08-13 16:05:53 +0200542 /* The HTTP client will be created in the same thread as the caller,
543 * avoiding threading issues */
Christopher Faulet6095d572022-05-16 17:09:48 +0200544 appctx = appctx_new_here(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200545 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100546 goto out;
Christopher Fauletb1e08362022-05-12 15:33:14 +0200547 appctx->svcctx = hc;
548 hc->flags = 0;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100549
Christopher Fauletb1e08362022-05-12 15:33:14 +0200550 if (appctx_init(appctx) == -1) {
551 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet92202da2022-05-11 12:22:10 +0200552 goto out_free_appctx;
William Lallemand85332732022-05-04 10:59:51 +0200553 }
554
William Lallemand33b0d092021-08-13 16:05:53 +0200555 return appctx;
556
William Lallemand33b0d092021-08-13 16:05:53 +0200557out_free_appctx:
Christopher Fauletb1e08362022-05-12 15:33:14 +0200558 appctx_free_on_early_error(appctx);
William Lallemand33b0d092021-08-13 16:05:53 +0200559out:
560
561 return NULL;
562}
563
William Lallemandecb83e12021-09-28 11:00:43 +0200564/*
565 * This function tries to destroy the httpclient if it wasn't running.
566 * If it was running, stop the client and ask it to autodestroy itself.
567 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500568 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200569 *
570 */
571void httpclient_stop_and_destroy(struct httpclient *hc)
572{
573
William Lallemandb8b13702021-09-28 12:15:37 +0200574 /* The httpclient was already stopped or never started, we can safely destroy it */
575 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200576 httpclient_destroy(hc);
577 } else {
Willy Tarreaub4829202022-09-01 20:40:26 +0200578 /* if the client wasn't stopped, ask for a stop and destroy */
William Lallemandecb83e12021-09-28 11:00:43 +0200579 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
Willy Tarreaub4829202022-09-01 20:40:26 +0200580 /* the calling applet doesn't exist anymore */
581 hc->caller = NULL;
William Lallemandecb83e12021-09-28 11:00:43 +0200582 if (hc->appctx)
583 appctx_wakeup(hc->appctx);
584 }
585}
586
William Lallemand33b0d092021-08-13 16:05:53 +0200587/* Free the httpclient */
588void httpclient_destroy(struct httpclient *hc)
589{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200590 struct http_hdr *hdrs;
591
592
William Lallemand33b0d092021-08-13 16:05:53 +0200593 if (!hc)
594 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200595
William Lallemand2a879002021-10-05 15:50:45 +0200596 /* we should never destroy a client which was started but not stopped */
597 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200598
William Lallemand03f5a1c2021-09-27 15:17:47 +0200599 /* request */
600 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200601 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200602 /* response */
603 istfree(&hc->res.vsn);
604 istfree(&hc->res.reason);
605 hdrs = hc->res.hdrs;
606 while (hdrs && isttest(hdrs->n)) {
607 istfree(&hdrs->n);
608 istfree(&hdrs->v);
609 hdrs++;
610 }
611 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200612 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100613 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200614
William Lallemand33b0d092021-08-13 16:05:53 +0200615 free(hc);
616
617 return;
618}
619
620/* Allocate an httpclient and its buffers
William Lallemand992ad622022-09-12 17:39:04 +0200621 * Use the default httpclient_proxy
622 *
William Lallemand33b0d092021-08-13 16:05:53 +0200623 * Return NULL on failure */
624struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
625{
626 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200627
628 hc = calloc(1, sizeof(*hc));
629 if (!hc)
630 goto err;
631
Christopher Faulet600985d2022-01-12 11:14:08 +0100632 hc->req.buf = BUF_NULL;
633 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200634 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100635 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200636 hc->req.meth = meth;
William Lallemand992ad622022-09-12 17:39:04 +0200637 httpclient_set_proxy(hc, httpclient_proxy);
William Lallemand33b0d092021-08-13 16:05:53 +0200638
639 return hc;
640
641err:
642 httpclient_destroy(hc);
643 return NULL;
644}
645
William Lallemand992ad622022-09-12 17:39:04 +0200646/* Allocate an httpclient and its buffers,
647 * Use the proxy <px>
648 *
649 * Return and httpclient or NULL.
650 */
651struct httpclient *httpclient_new_from_proxy(struct proxy *px, void *caller, enum http_meth_t meth, struct ist url)
652{
653 struct httpclient *hc;
654
655 hc = httpclient_new(caller, meth, url);
656 if (!hc)
657 return NULL;
658
659 httpclient_set_proxy(hc, px);
660
661 return hc;
662}
663
664/*
665 * Configure an httpclient with a specific proxy <px>
666 *
667 * The proxy <px> must contains 2 srv, one configured for clear connections, the other for SSL.
668 *
669 */
670int httpclient_set_proxy(struct httpclient *hc, struct proxy *px)
671{
672 struct server *srv;
673
674 hc->px = px;
675
676 for (srv = px->srv; srv != NULL; srv = srv->next) {
677 if (srv->xprt == xprt_get(XPRT_RAW)) {
678 hc->srv_raw = srv;
679#ifdef USE_OPENSSL
680 } else if (srv->xprt == xprt_get(XPRT_SSL)) {
681 hc->srv_ssl = srv;
682#endif
683 }
684 }
685
686 return 0;
687}
688
William Lallemand33b0d092021-08-13 16:05:53 +0200689static void httpclient_applet_io_handler(struct appctx *appctx)
690{
Willy Tarreau1eea6652022-05-05 20:12:01 +0200691 struct httpclient *hc = appctx->svcctx;
Willy Tarreauc12b3212022-05-27 11:08:15 +0200692 struct stconn *sc = appctx_sc(appctx);
Willy Tarreaub89f8722022-05-27 10:37:32 +0200693 struct stream *s = __sc_strm(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200694 struct channel *req = &s->req;
695 struct channel *res = &s->res;
696 struct htx_blk *blk = NULL;
697 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200698 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200699 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100700 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100701 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200702
Christopher Faulet1901c1b2023-04-11 07:38:34 +0200703 if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW)))) {
704 if (co_data(res)) {
705 htx = htx_from_buf(&res->buf);
706 co_htx_skip(res, htx, co_data(res));
707 htx_to_buf(htx, &res->buf);
708 }
Christopher Fauletbe08df82023-03-31 11:30:32 +0200709 goto out;
Christopher Faulet1901c1b2023-04-11 07:38:34 +0200710 }
William Lallemanda93eac42022-10-20 18:36:03 +0200711 /* The IO handler could be called after the release, so we need to
712 * check if hc is still there to run the IO handler */
713 if (!hc)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200714 goto out;
William Lallemanda93eac42022-10-20 18:36:03 +0200715
William Lallemand33b0d092021-08-13 16:05:53 +0200716 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200717
718 /* required to stop */
719 if (hc->flags & HTTPCLIENT_FA_STOP)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200720 goto error;
William Lallemandecb83e12021-09-28 11:00:43 +0200721
William Lallemand33b0d092021-08-13 16:05:53 +0200722 switch(appctx->st0) {
723
724 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100725 /* we know that the buffer is empty here, since
726 * it's the first call, we can freely copy the
727 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100728 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100729 if (!ret)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200730 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100731
Christopher Faulet600985d2022-01-12 11:14:08 +0100732 if (!b_data(&hc->req.buf))
733 b_free(&hc->req.buf);
734
William Lallemanddb8a1f32021-11-08 16:55:14 +0100735 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100736 if (!htx)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200737 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100738
William Lallemanddb8a1f32021-11-08 16:55:14 +0100739 channel_add_input(req, htx->data);
740
William Lallemand933fe392021-11-04 09:45:58 +0100741 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
742 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
743 else
744 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
745
Christopher Fauletbe08df82023-03-31 11:30:32 +0200746 goto out; /* we need to leave the IO handler once we wrote the request */
747 break;
748
William Lallemand0da616e2021-10-28 15:34:26 +0200749 case HTTPCLIENT_S_REQ_BODY:
750 /* call the payload callback */
751 {
752 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100753 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200754
William Lallemand0da616e2021-10-28 15:34:26 +0200755 /* call the request callback */
756 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100757
William Lallemandccc7ee42022-03-18 17:57:15 +0100758 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100759 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100760
William Lallemandccc7ee42022-03-18 17:57:15 +0100761 if (htx_is_empty(hc_htx))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200762 goto out;
Christopher Faulet600985d2022-01-12 11:14:08 +0100763
William Lallemandccc7ee42022-03-18 17:57:15 +0100764 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200765 size_t data = hc_htx->data;
766
William Lallemandccc7ee42022-03-18 17:57:15 +0100767 /* Here htx_to_buf() will set buffer data to 0 because
768 * the HTX is empty, and allow us to do an xfer.
769 */
770 htx_to_buf(hc_htx, &hc->req.buf);
771 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100772 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200773 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100774 } else {
775 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100776
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200777 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100778 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100779
William Lallemandccc7ee42022-03-18 17:57:15 +0100780 /* we must copy the EOM if we empty the buffer */
781 if (htx_is_empty(hc_htx)) {
782 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
783 }
784 htx_to_buf(htx, &req->buf);
785 htx_to_buf(hc_htx, &hc->req.buf);
786 }
787
788
789 if (!b_data(&hc->req.buf))
790 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200791 }
792
William Lallemanddb8a1f32021-11-08 16:55:14 +0100793 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200794 if (!htx)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200795 goto out;
William Lallemand0da616e2021-10-28 15:34:26 +0200796
797 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Fauletbe08df82023-03-31 11:30:32 +0200798 if (htx->flags & HTX_FL_EOM)
William Lallemand0da616e2021-10-28 15:34:26 +0200799 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
800
William Lallemand1eca8942022-03-17 14:57:23 +0100801 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200802 }
Christopher Fauletbe08df82023-03-31 11:30:32 +0200803 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200804
805 case HTTPCLIENT_S_RES_STLINE:
Christopher Fauletbe08df82023-03-31 11:30:32 +0200806 /* Request is finished, report EOI */
807 se_fl_set(appctx->sedesc, SE_FL_EOI);
808
William Lallemand33b0d092021-08-13 16:05:53 +0200809 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100810 if (!co_data(res))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200811 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200812 htx = htxbuf(&res->buf);
813 if (!htx)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200814 goto out;
William Lallemand97f69c62022-03-10 17:23:40 +0100815 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200816 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
817 sl = htx_get_blk_ptr(htx, blk);
818 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200819 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200820
821 /* copy the status line in the httpclient */
822 hc->res.status = sl->info.res.status;
823 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
824 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100825 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200826 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100827 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200828 /* caller callback */
829 if (hc->ops.res_stline)
830 hc->ops.res_stline(hc);
831
832 /* if there is no HTX data anymore and the EOM flag is
833 * set, leave (no body) */
834 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
835 appctx->st0 = HTTPCLIENT_S_RES_END;
836 else
837 appctx->st0 = HTTPCLIENT_S_RES_HDR;
838 break;
839
840 case HTTPCLIENT_S_RES_HDR:
841 /* first copy the headers in a local hdrs
842 * structure, once we the total numbers of the
843 * header we allocate the right size and copy
844 * them. The htx block of the headers are
845 * removed each time one is read */
846 {
847 struct http_hdr hdrs[global.tune.max_http_hdr];
848
William Lallemanda625b032022-03-17 14:45:46 +0100849 if (!co_data(res))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200850 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200851 htx = htxbuf(&res->buf);
852 if (!htx)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200853 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200854
855 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200856 blk = htx_get_head_blk(htx);
857 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200858 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100859 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200860
Christopher Faulet534645d2022-04-29 13:44:46 +0200861 c_rew(res, sz);
William Lallemandc020b252022-03-09 18:56:02 +0100862
Christopher Faulet18de6f22022-06-01 16:37:49 +0200863 if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100864 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
865 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100866 hdr_num++;
867 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200868 else if (type == HTX_BLK_EOH) {
869 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200870 hdrs[hdr_num].n = IST_NULL;
871 hdrs[hdr_num].v = IST_NULL;
Christopher Faulet18de6f22022-06-01 16:37:49 +0200872 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200873 break;
874 }
Christopher Faulet18de6f22022-06-01 16:37:49 +0200875 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200876 }
877
William Lallemand0d6f7792021-08-20 11:59:49 +0200878 if (hdr_num) {
879 /* alloc and copy the headers in the httpclient struct */
880 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
881 if (!hc->res.hdrs)
Christopher Fauletbe08df82023-03-31 11:30:32 +0200882 goto error;
William Lallemand0d6f7792021-08-20 11:59:49 +0200883 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200884
William Lallemand0d6f7792021-08-20 11:59:49 +0200885 /* caller callback */
886 if (hc->ops.res_headers)
887 hc->ops.res_headers(hc);
888 }
William Lallemand33b0d092021-08-13 16:05:53 +0200889
890 /* if there is no HTX data anymore and the EOM flag is
891 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200892 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200893 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200894 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200895 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200896 }
William Lallemand33b0d092021-08-13 16:05:53 +0200897 }
Christopher Fauletbe08df82023-03-31 11:30:32 +0200898 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200899
900 case HTTPCLIENT_S_RES_BODY:
901 /*
902 * The IO handler removes the htx blocks in the response buffer and
903 * push them in the hc->res.buf buffer in a raw format.
904 */
William Lallemanda625b032022-03-17 14:45:46 +0100905 if (!co_data(res))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200906 goto out;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100907
William Lallemand33b0d092021-08-13 16:05:53 +0200908 htx = htxbuf(&res->buf);
909 if (!htx || htx_is_empty(htx))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200910 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200911
Christopher Faulet600985d2022-01-12 11:14:08 +0100912 if (!b_alloc(&hc->res.buf))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200913 goto out;
Christopher Faulet600985d2022-01-12 11:14:08 +0100914
William Lallemand33b0d092021-08-13 16:05:53 +0200915 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100916 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200917
918 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200919 blk = htx_get_head_blk(htx);
920 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100921 enum htx_blk_type type = htx_get_blk_type(blk);
922 size_t count = co_data(res);
923 uint32_t blksz = htx_get_blksz(blk);
924 uint32_t room = b_room(&hc->res.buf);
925 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200926
William Lallemandc8f1eb92022-03-09 11:58:51 +0100927 /* we should try to copy the maximum output data in a block, which fit
928 * the destination buffer */
929 vlen = MIN(count, blksz);
930 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100931
William Lallemandc8f1eb92022-03-09 11:58:51 +0100932 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100933 goto process_data;
934
William Lallemand33b0d092021-08-13 16:05:53 +0200935 if (type == HTX_BLK_DATA) {
936 struct ist v = htx_get_blk_value(htx, blk);
937
William Lallemandc8f1eb92022-03-09 11:58:51 +0100938 __b_putblk(&hc->res.buf, v.ptr, vlen);
939 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200940
William Lallemandc8f1eb92022-03-09 11:58:51 +0100941 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200942 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100943 else
944 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100945
William Lallemand33b0d092021-08-13 16:05:53 +0200946 /* the data must be processed by the caller in the receive phase */
947 if (hc->ops.res_payload)
948 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100949
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500950 /* cannot copy everything, need to process */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100951 if (vlen != blksz)
952 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200953 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100954 if (vlen != blksz)
955 goto process_data;
956
William Lallemand33b0d092021-08-13 16:05:53 +0200957 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100958 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200959 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200960 }
961 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100962
William Lallemand33b0d092021-08-13 16:05:53 +0200963 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100964 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
Christopher Fauletbe08df82023-03-31 11:30:32 +0200965 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200966
William Lallemandc8f1eb92022-03-09 11:58:51 +0100967
William Lallemand33b0d092021-08-13 16:05:53 +0200968 /* end of message, we should quit */
969 appctx->st0 = HTTPCLIENT_S_RES_END;
Christopher Fauletbe08df82023-03-31 11:30:32 +0200970 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200971
972 case HTTPCLIENT_S_RES_END:
Christopher Fauletbe08df82023-03-31 11:30:32 +0200973 se_fl_set(appctx->sedesc, SE_FL_EOS);
974 goto out;
975 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200976 }
977 }
978
Christopher Fauletbe08df82023-03-31 11:30:32 +0200979out:
980 return;
William Lallemand33b0d092021-08-13 16:05:53 +0200981
Christopher Fauletbe08df82023-03-31 11:30:32 +0200982process_data:
Willy Tarreaub89f8722022-05-27 10:37:32 +0200983 sc_will_read(sc);
Christopher Fauletbe08df82023-03-31 11:30:32 +0200984 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200985
Willy Tarreau6486ff82022-09-02 11:42:50 +0200986full:
987 /* There was not enough room in the response channel */
Willy Tarreaub89f8722022-05-27 10:37:32 +0200988 sc_need_room(sc);
Christopher Fauletbe08df82023-03-31 11:30:32 +0200989 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200990
Christopher Fauletbe08df82023-03-31 11:30:32 +0200991error:
992 se_fl_set(appctx->sedesc, SE_FL_ERROR);
993 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200994}
995
Christopher Fauletb1e08362022-05-12 15:33:14 +0200996static int httpclient_applet_init(struct appctx *appctx)
997{
998 struct httpclient *hc = appctx->svcctx;
999 struct stream *s;
1000 struct sockaddr_storage *addr = NULL;
1001 struct sockaddr_storage ss_url = {};
1002 struct sockaddr_storage *ss_dst;
1003 enum obj_type *target = NULL;
1004 struct ist host = IST_NULL;
1005 enum http_scheme scheme;
1006 int port;
1007 int doresolve = 0;
1008
1009
1010 /* parse the URL and */
Thierry Fournier74a9eb52022-10-10 12:46:38 +02001011 if (!httpclient_spliturl(hc->req.url, &scheme, &host, &port))
1012 goto out_error;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001013
1014 if (hc->dst) {
1015 /* if httpclient_set_dst() was used, sets the alternative address */
1016 ss_dst = hc->dst;
1017 } else {
1018 /* set the dst using the host, or 0.0.0.0 to resolve */
1019 ist2str(trash.area, host);
1020 ss_dst = str2ip2(trash.area, &ss_url, 0);
1021 if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
1022 doresolve = 1;
1023 ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
1024 }
1025 sock_inet_set_port(ss_dst, port);
1026 }
1027
Christopher Fauletb1e08362022-05-12 15:33:14 +02001028 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
1029 goto out_error;
1030
1031 /* choose the SSL server or not */
1032 switch (scheme) {
1033 case SCH_HTTP:
William Lallemand992ad622022-09-12 17:39:04 +02001034 target = &hc->srv_raw->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001035 break;
1036 case SCH_HTTPS:
1037#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001038 if (hc->srv_ssl) {
1039 target = &hc->srv_ssl->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001040 } else {
1041 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
1042 goto out_free_addr;
1043 }
1044#else
1045 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
1046 goto out_free_addr;
1047#endif
1048 break;
1049 }
1050
William Lallemand992ad622022-09-12 17:39:04 +02001051 if (appctx_finalize_startup(appctx, hc->px, &hc->req.buf) == -1) {
Christopher Fauletb1e08362022-05-12 15:33:14 +02001052 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
1053 goto out_free_addr;
1054 }
1055
1056 s = appctx_strm(appctx);
1057 s->target = target;
1058 /* set the "timeout server" */
Christopher Faulet5aaacfb2023-02-15 08:13:33 +01001059 s->scb->ioto = hc->timeout_server;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001060
1061 if (doresolve) {
1062 /* in order to do the set-dst we need to put the address on the front */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001063 s->scf->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001064 } else {
1065 /* in cases we don't use the resolve we already have the address
1066 * and must put it on the backend side, some of the cases are
1067 * not meant to be used on the frontend (sockpair, unix socket etc.) */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001068 s->scb->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001069 }
1070
Christopher Faulet9a790f62023-03-16 14:40:03 +01001071 s->scb->flags |= (SC_FL_RCV_ONCE|SC_FL_NOLINGER);
Christopher Fauletb1e08362022-05-12 15:33:14 +02001072 s->flags |= SF_ASSIGNED;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001073
1074 /* applet is waiting for data */
Willy Tarreau90e8b452022-05-25 18:21:43 +02001075 applet_need_more_data(appctx);
Christopher Fauletb1e08362022-05-12 15:33:14 +02001076 appctx_wakeup(appctx);
1077
1078 hc->appctx = appctx;
1079 hc->flags |= HTTPCLIENT_FS_STARTED;
1080
1081 /* The request was transferred when the stream was created. So switch
1082 * directly to REQ_BODY or RES_STLINE state
1083 */
1084 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
1085 return 0;
1086
1087 out_free_addr:
1088 sockaddr_free(&addr);
1089 out_error:
1090 return -1;
1091}
1092
William Lallemand33b0d092021-08-13 16:05:53 +02001093static void httpclient_applet_release(struct appctx *appctx)
1094{
Willy Tarreau1eea6652022-05-05 20:12:01 +02001095 struct httpclient *hc = appctx->svcctx;
William Lallemand33b0d092021-08-13 16:05:53 +02001096
William Lallemand1123dde2021-09-21 10:58:10 +02001097 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +02001098 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +02001099 /* the applet is leaving, remove the ptr so we don't try to call it
1100 * again from the caller */
1101 hc->appctx = NULL;
1102
William Lallemandeb0d4c42022-04-06 14:12:37 +02001103 if (hc->ops.res_end)
1104 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +02001105
1106 /* destroy the httpclient when set to autotokill */
1107 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
1108 httpclient_destroy(hc);
1109 }
1110
William Lallemanda93eac42022-10-20 18:36:03 +02001111 /* be sure not to use this ptr anymore if the IO handler is called a
1112 * last time */
1113 appctx->svcctx = NULL;
1114
William Lallemand33b0d092021-08-13 16:05:53 +02001115 return;
1116}
1117
1118/* HTTP client applet */
1119static struct applet httpclient_applet = {
1120 .obj_type = OBJ_TYPE_APPLET,
1121 .name = "<HTTPCLIENT>",
1122 .fct = httpclient_applet_io_handler,
Christopher Fauletb1e08362022-05-12 15:33:14 +02001123 .init = httpclient_applet_init,
William Lallemand33b0d092021-08-13 16:05:53 +02001124 .release = httpclient_applet_release,
1125};
1126
William Lallemand5392ff62022-04-28 16:55:02 +02001127
William Lallemand54aec5f2022-09-12 16:46:35 +02001128static int httpclient_resolve_init(struct proxy *px)
William Lallemand5392ff62022-04-28 16:55:02 +02001129{
1130 struct act_rule *rule;
1131 int i;
William Lallemand8a734cb2022-05-04 16:10:47 +02001132 char *do_resolve = NULL;
1133 char *http_rules[][11] = {
William Lallemand5392ff62022-04-28 16:55:02 +02001134 { "set-var(txn.hc_ip)", "dst", "" },
William Lallemandd78dfe72022-08-26 16:45:13 +02001135 { do_resolve, "hdr(Host),host_only", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
William Lallemand5392ff62022-04-28 16:55:02 +02001136 { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
1137 { "capture", "var(txn.hc_ip)", "len", "40", "" },
1138 { "set-dst", "var(txn.hc_ip)", "" },
1139 { "" }
1140 };
1141
William Lallemand8a734cb2022-05-04 16:10:47 +02001142 if (!resolvers_id)
1143 resolvers_id = strdup("default");
1144
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001145 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 +02001146 http_rules[1][0] = do_resolve;
1147
William Lallemand7867f632022-05-05 19:02:59 +02001148 /* Try to create the default resolvers section */
1149 resolvers_create_default();
1150
William Lallemand8a734cb2022-05-04 16:10:47 +02001151 /* if the resolver does not exist and no hard_error was set, simply ignore resolving */
1152 if (!find_resolvers_by_id(resolvers_id) && !hard_error_resolvers) {
1153 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001154 return 0;
William Lallemand8a734cb2022-05-04 16:10:47 +02001155 }
William Lallemand5392ff62022-04-28 16:55:02 +02001156
1157
1158 for (i = 0; *http_rules[i][0] != '\0'; i++) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001159 rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, px);
William Lallemand5392ff62022-04-28 16:55:02 +02001160 if (!rule) {
William Lallemand8a734cb2022-05-04 16:10:47 +02001161 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001162 ha_alert("Couldn't setup the httpclient resolver.\n");
1163 return 1;
1164 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001165 LIST_APPEND(&px->http_req_rules, &rule->list);
William Lallemand5392ff62022-04-28 16:55:02 +02001166 }
1167
William Lallemand8a734cb2022-05-04 16:10:47 +02001168 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001169 return 0;
1170}
1171
William Lallemand83614a92021-08-13 14:47:57 +02001172/*
William Lallemand54aec5f2022-09-12 16:46:35 +02001173 * Creates an internal proxy which will be used for httpclient.
1174 * This will allocate 2 servers (raw and ssl) and 1 proxy.
1175 *
1176 * This function must be called from a precheck callback.
1177 *
1178 * Return a proxy or NULL.
William Lallemand83614a92021-08-13 14:47:57 +02001179 */
William Lallemand54aec5f2022-09-12 16:46:35 +02001180struct proxy *httpclient_create_proxy(const char *id)
William Lallemand83614a92021-08-13 14:47:57 +02001181{
William Lallemand85af49c2022-05-04 14:33:57 +02001182 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001183 char *errmsg = NULL;
William Lallemand54aec5f2022-09-12 16:46:35 +02001184 struct proxy *px = NULL;
1185 struct server *srv_raw = NULL;
1186#ifdef USE_OPENSSL
1187 struct server *srv_ssl = NULL;
1188#endif
William Lallemand83614a92021-08-13 14:47:57 +02001189
William Lallemandc6ceba32022-04-22 16:49:53 +02001190 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001191 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001192
William Lallemand54aec5f2022-09-12 16:46:35 +02001193 px = alloc_new_proxy(id, PR_CAP_LISTEN|PR_CAP_INT|PR_CAP_HTTPCLIENT, &errmsg);
1194 if (!px) {
William Lallemand85af49c2022-05-04 14:33:57 +02001195 memprintf(&errmsg, "couldn't allocate proxy.");
William Lallemand83614a92021-08-13 14:47:57 +02001196 err_code |= ERR_ALERT | ERR_FATAL;
1197 goto err;
1198 }
1199
William Lallemand54aec5f2022-09-12 16:46:35 +02001200 proxy_preset_defaults(px);
Willy Tarreau0e72e402021-08-20 10:23:12 +02001201
William Lallemand54aec5f2022-09-12 16:46:35 +02001202 px->options |= PR_O_WREQ_BODY;
1203 px->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
1204 px->options2 |= PR_O2_INDEPSTR;
1205 px->mode = PR_MODE_HTTP;
1206 px->maxconn = 0;
1207 px->accept = NULL;
1208 px->conn_retries = CONN_RETRIES;
1209 px->timeout.client = TICK_ETERNITY;
William Lallemand83614a92021-08-13 14:47:57 +02001210 /* The HTTP Client use the "option httplog" with the global log server */
William Lallemandd793ca22022-12-22 15:13:59 +01001211 px->conf.logformat_string = httpclient_log_format;
William Lallemand54aec5f2022-09-12 16:46:35 +02001212 px->http_needed = 1;
William Lallemand83614a92021-08-13 14:47:57 +02001213
1214 /* clear HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001215 srv_raw = new_server(px);
1216 if (!srv_raw) {
William Lallemand83614a92021-08-13 14:47:57 +02001217 memprintf(&errmsg, "out of memory.");
William Lallemand85af49c2022-05-04 14:33:57 +02001218 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001219 goto err;
1220 }
1221
William Lallemand54aec5f2022-09-12 16:46:35 +02001222 srv_settings_cpy(srv_raw, &px->defsrv, 0);
1223 srv_raw->iweight = 0;
1224 srv_raw->uweight = 0;
1225 srv_raw->xprt = xprt_get(XPRT_RAW);
1226 srv_raw->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1227 srv_raw->id = strdup("<HTTPCLIENT>");
1228 if (!srv_raw->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001229 memprintf(&errmsg, "out of memory.");
1230 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001231 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001232 }
William Lallemand83614a92021-08-13 14:47:57 +02001233
William Lallemand957ab132021-08-24 18:33:28 +02001234#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001235 /* SSL HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001236 srv_ssl = new_server(px);
1237 if (!srv_ssl) {
William Lallemand83614a92021-08-13 14:47:57 +02001238 memprintf(&errmsg, "out of memory.");
1239 err_code |= ERR_ALERT | ERR_FATAL;
1240 goto err;
1241 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001242 srv_settings_cpy(srv_ssl, &px->defsrv, 0);
1243 srv_ssl->iweight = 0;
1244 srv_ssl->uweight = 0;
1245 srv_ssl->xprt = xprt_get(XPRT_SSL);
1246 srv_ssl->use_ssl = 1;
1247 srv_ssl->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1248 srv_ssl->id = strdup("<HTTPSCLIENT>");
1249 if (!srv_ssl->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001250 memprintf(&errmsg, "out of memory.");
1251 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001252 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001253 }
William Lallemand83614a92021-08-13 14:47:57 +02001254
Willy Tarreaudf3231c2022-09-02 09:02:21 +02001255#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
William Lallemand54aec5f2022-09-12 16:46:35 +02001256 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 +02001257 err_code |= ERR_ALERT | ERR_FATAL;
1258 goto err;
1259 }
1260#endif
William Lallemand54aec5f2022-09-12 16:46:35 +02001261 srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001262 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001263 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
William Lallemand683fbb82022-05-04 15:43:01 +02001264
Miroslav Zagoraca2ec1922022-11-02 16:11:50 +01001265 srv_ssl->ssl_ctx.ca_file = strdup(httpclient_ssl_ca_file ? httpclient_ssl_ca_file : "@system-ca");
William Lallemand0a2d6322022-11-24 19:14:19 +01001266 if (!__ssl_store_load_locations_file(srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT, !hard_error_ssl)) {
William Lallemand6fce46a2022-05-04 14:53:41 +02001267 /* if we failed to load the ca-file, only quits in
1268 * error with hard_error, otherwise just disable the
1269 * feature. */
1270 if (hard_error_ssl) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001271 memprintf(&errmsg, "cannot initialize SSL verify with 'ca-file \"%s\"'.", srv_ssl->ssl_ctx.ca_file);
William Lallemand6fce46a2022-05-04 14:53:41 +02001272 err_code |= ERR_ALERT | ERR_FATAL;
1273 goto err;
1274 } else {
William Lallemand54aec5f2022-09-12 16:46:35 +02001275 ha_free(&srv_ssl->ssl_ctx.ca_file);
1276 srv_drop(srv_ssl);
1277 srv_ssl = NULL;
William Lallemand6fce46a2022-05-04 14:53:41 +02001278 }
William Lallemand4006b0f2022-04-25 18:23:35 +02001279 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001280 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001281
William Lallemand957ab132021-08-24 18:33:28 +02001282#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001283
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001284 /* add the proxy in the proxy list only if everything is successful */
William Lallemand54aec5f2022-09-12 16:46:35 +02001285 px->next = proxies_list;
1286 proxies_list = px;
William Lallemand83614a92021-08-13 14:47:57 +02001287
William Lallemand54aec5f2022-09-12 16:46:35 +02001288 if (httpclient_resolve_init(px) != 0) {
William Lallemand85af49c2022-05-04 14:33:57 +02001289 memprintf(&errmsg, "cannot initialize resolvers.");
1290 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand5392ff62022-04-28 16:55:02 +02001291 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001292 }
William Lallemand5392ff62022-04-28 16:55:02 +02001293
William Lallemand211c9672021-08-24 17:18:13 +02001294 /* link the 2 servers in the proxy */
William Lallemand54aec5f2022-09-12 16:46:35 +02001295 srv_raw->next = px->srv;
1296 px->srv = srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +02001297
1298#ifdef USE_OPENSSL
William Lallemand54aec5f2022-09-12 16:46:35 +02001299 if (srv_ssl) {
1300 srv_ssl->next = px->srv;
1301 px->srv = srv_ssl;
William Lallemand4006b0f2022-04-25 18:23:35 +02001302 }
William Lallemand957ab132021-08-24 18:33:28 +02001303#endif
1304
William Lallemand211c9672021-08-24 17:18:13 +02001305
William Lallemand83614a92021-08-13 14:47:57 +02001306err:
William Lallemand85af49c2022-05-04 14:33:57 +02001307 if (err_code & ERR_CODE) {
1308 ha_alert("httpclient: cannot initialize: %s\n", errmsg);
1309 free(errmsg);
William Lallemand54aec5f2022-09-12 16:46:35 +02001310 srv_drop(srv_raw);
1311#ifdef USE_OPENSSL
1312 srv_drop(srv_ssl);
1313#endif
1314 free_proxy(px);
1315
1316 return NULL;
1317 }
1318 return px;
1319}
1320
1321/*
1322 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
1323 * the other for HTTPS.
1324 */
1325static int httpclient_precheck()
1326{
William Lallemand54aec5f2022-09-12 16:46:35 +02001327 /* initialize the default httpclient_proxy which is used for the CLI and the lua */
1328
1329 httpclient_proxy = httpclient_create_proxy("<HTTPCLIENT>");
1330 if (!httpclient_proxy)
1331 return 1;
William Lallemand54aec5f2022-09-12 16:46:35 +02001332
1333 return 0;
William Lallemand83614a92021-08-13 14:47:57 +02001334}
1335
William Lallemand2c8b0842022-04-22 15:16:09 +02001336static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001337{
William Lallemand85af49c2022-05-04 14:33:57 +02001338 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001339 struct logsrv *logsrv;
William Lallemand992ad622022-09-12 17:39:04 +02001340 struct proxy *curproxy = NULL;
William Lallemand71e31582022-03-16 15:47:47 +01001341 char *errmsg = NULL;
William Lallemand992ad622022-09-12 17:39:04 +02001342#ifdef USE_OPENSSL
1343 struct server *srv = NULL;
1344 struct server *srv_ssl = NULL;
1345#endif
William Lallemand83614a92021-08-13 14:47:57 +02001346
William Lallemandc6ceba32022-04-22 16:49:53 +02001347 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001348 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001349
William Lallemand992ad622022-09-12 17:39:04 +02001350 /* Initialize the logs for every proxy dedicated to the httpclient */
1351 for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
William Lallemand83614a92021-08-13 14:47:57 +02001352
William Lallemand992ad622022-09-12 17:39:04 +02001353 if (!(curproxy->cap & PR_CAP_HTTPCLIENT))
1354 continue;
William Lallemand83614a92021-08-13 14:47:57 +02001355
William Lallemand992ad622022-09-12 17:39:04 +02001356 /* copy logs from "global" log list */
1357 list_for_each_entry(logsrv, &global.logsrvs, list) {
1358 struct logsrv *node = malloc(sizeof(*node));
1359
1360 if (!node) {
1361 memprintf(&errmsg, "out of memory.");
1362 err_code |= ERR_ALERT | ERR_FATAL;
1363 goto err;
1364 }
1365
1366 memcpy(node, logsrv, sizeof(*node));
1367 LIST_INIT(&node->list);
1368 LIST_APPEND(&curproxy->logsrvs, &node->list);
1369 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1370 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001371 }
William Lallemand992ad622022-09-12 17:39:04 +02001372 if (curproxy->conf.logformat_string) {
1373 curproxy->conf.args.ctx = ARGC_LOG;
1374 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1375 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
1376 SMP_VAL_FE_LOG_END, &errmsg)) {
1377 memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
1378 err_code |= ERR_ALERT | ERR_FATAL;
1379 goto err;
1380 }
1381 curproxy->conf.args.file = NULL;
1382 curproxy->conf.args.line = 0;
1383 }
William Lallemand71e31582022-03-16 15:47:47 +01001384
1385#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001386 /* initialize the SNI for the SSL servers */
1387
1388 for (srv = curproxy->srv; srv != NULL; srv = srv->next) {
1389 if (srv->xprt == xprt_get(XPRT_SSL)) {
1390 srv_ssl = srv;
1391 }
William Lallemand715c1012022-03-16 16:39:23 +01001392 }
Miroslav Zagoraccbfee3a2022-09-19 12:20:29 +02001393 if (srv_ssl && !srv_ssl->sni_expr) {
William Lallemand992ad622022-09-12 17:39:04 +02001394 /* init the SNI expression */
1395 /* always use the host header as SNI, without the port */
1396 srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1397 err_code |= server_parse_sni_expr(srv_ssl, curproxy, &errmsg);
1398 if (err_code & ERR_CODE) {
1399 memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
1400 goto err;
1401 }
1402 }
William Lallemand71e31582022-03-16 15:47:47 +01001403#endif
William Lallemand992ad622022-09-12 17:39:04 +02001404 }
William Lallemand71e31582022-03-16 15:47:47 +01001405
William Lallemand83614a92021-08-13 14:47:57 +02001406err:
William Lallemand85af49c2022-05-04 14:33:57 +02001407 if (err_code & ERR_CODE) {
1408 ha_alert("httpclient: failed to initialize: %s\n", errmsg);
1409 free(errmsg);
1410
1411 }
1412 return err_code;
William Lallemand83614a92021-08-13 14:47:57 +02001413}
1414
William Lallemand83614a92021-08-13 14:47:57 +02001415/* initialize the proxy and servers for the HTTP client */
1416
William Lallemand2c8b0842022-04-22 15:16:09 +02001417REGISTER_PRE_CHECK(httpclient_precheck);
1418REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001419
William Lallemand8a734cb2022-05-04 16:10:47 +02001420static int httpclient_parse_global_resolvers(char **args, int section_type, struct proxy *curpx,
1421 const struct proxy *defpx, const char *file, int line,
1422 char **err)
1423{
1424 if (too_many_args(1, args, err, NULL))
1425 return -1;
1426
1427 /* any configuration should set the hard_error flag */
1428 hard_error_resolvers = 1;
1429
1430 free(resolvers_id);
1431 resolvers_id = strdup(args[1]);
1432
1433 return 0;
1434}
1435
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001436static int httpclient_parse_global_prefer(char **args, int section_type, struct proxy *curpx,
1437 const struct proxy *defpx, const char *file, int line,
1438 char **err)
1439{
1440 if (too_many_args(1, args, err, NULL))
1441 return -1;
1442
1443 /* any configuration should set the hard_error flag */
1444 hard_error_resolvers = 1;
1445
1446
1447 if (strcmp(args[1],"ipv4") == 0)
1448 resolvers_prefer = "ipv4";
1449 else if (strcmp(args[1],"ipv6") == 0)
1450 resolvers_prefer = "ipv6";
1451 else {
1452 ha_alert("parsing [%s:%d] : '%s' expects 'ipv4' or 'ipv6' as argument.\n", file, line, args[0]);
1453 return -1;
1454 }
1455
1456 return 0;
1457}
William Lallemand8a734cb2022-05-04 16:10:47 +02001458
1459
William Lallemandeaa703e2022-04-22 17:52:33 +02001460#ifdef USE_OPENSSL
William Lallemand683fbb82022-05-04 15:43:01 +02001461static int httpclient_parse_global_ca_file(char **args, int section_type, struct proxy *curpx,
1462 const struct proxy *defpx, const char *file, int line,
1463 char **err)
1464{
1465 if (too_many_args(1, args, err, NULL))
1466 return -1;
1467
1468 /* any configuration should set the hard_error flag */
1469 hard_error_ssl = 1;
1470
1471 free(httpclient_ssl_ca_file);
1472 httpclient_ssl_ca_file = strdup(args[1]);
1473
1474 return 0;
1475}
1476
William Lallemandeaa703e2022-04-22 17:52:33 +02001477static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1478 const struct proxy *defpx, const char *file, int line,
1479 char **err)
1480{
1481 if (too_many_args(1, args, err, NULL))
1482 return -1;
1483
William Lallemand6fce46a2022-05-04 14:53:41 +02001484 /* any configuration should set the hard_error flag */
1485 hard_error_ssl = 1;
1486
William Lallemandeaa703e2022-04-22 17:52:33 +02001487 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001488 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001489 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001490 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001491 else {
1492 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1493 return -1;
1494 }
1495
1496 return 0;
1497}
William Lallemand8a734cb2022-05-04 16:10:47 +02001498#endif /* ! USE_OPENSSL */
William Lallemandeaa703e2022-04-22 17:52:33 +02001499
1500static struct cfg_kw_list cfg_kws = {ILH, {
William Lallemand8a734cb2022-05-04 16:10:47 +02001501 { CFG_GLOBAL, "httpclient.resolvers.id", httpclient_parse_global_resolvers },
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001502 { CFG_GLOBAL, "httpclient.resolvers.prefer", httpclient_parse_global_prefer },
William Lallemand8a734cb2022-05-04 16:10:47 +02001503#ifdef USE_OPENSSL
William Lallemand9ff95e22022-05-04 13:52:29 +02001504 { CFG_GLOBAL, "httpclient.ssl.verify", httpclient_parse_global_verify },
William Lallemand683fbb82022-05-04 15:43:01 +02001505 { CFG_GLOBAL, "httpclient.ssl.ca-file", httpclient_parse_global_ca_file },
William Lallemand8a734cb2022-05-04 16:10:47 +02001506#endif
William Lallemandeaa703e2022-04-22 17:52:33 +02001507 { 0, NULL, NULL },
1508}};
1509
1510INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);