blob: 2f9bac55528ca59f980195371cb892e1bfacfb56 [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;
William Lallemand6fce46a2022-05-04 14:53:41 +020044
William Lallemand957ab132021-08-24 18:33:28 +020045#ifdef USE_OPENSSL
William Lallemand6fce46a2022-05-04 14:53:41 +020046/* if the httpclient is not configured, error are ignored and features are limited */
47static int hard_error_ssl = 0;
William Lallemandf1344b32022-04-26 12:00:06 +020048static int httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemand683fbb82022-05-04 15:43:01 +020049static char *httpclient_ssl_ca_file = NULL;
William Lallemand957ab132021-08-24 18:33:28 +020050#endif
William Lallemand33b0d092021-08-13 16:05:53 +020051static struct applet httpclient_applet;
52
William Lallemand7c5a7ef2022-05-04 15:59:44 +020053/* if the httpclient is not configured, error are ignored and features are limited */
William Lallemand8a734cb2022-05-04 16:10:47 +020054static int hard_error_resolvers = 0;
55static char *resolvers_id = NULL;
William Lallemand7c5a7ef2022-05-04 15:59:44 +020056static char *resolvers_prefer = NULL;
William Lallemandeaa703e2022-04-22 17:52:33 +020057
William Lallemand03a4eb12021-08-18 16:46:21 +020058/* --- This part of the file implement an HTTP client over the CLI ---
59 * The functions will be starting by "hc_cli" for "httpclient cli"
60 */
61
William Lallemand03a4eb12021-08-18 16:46:21 +020062/* What kind of data we need to read */
63#define HC_CLI_F_RES_STLINE 0x01
64#define HC_CLI_F_RES_HDR 0x02
65#define HC_CLI_F_RES_BODY 0x04
66#define HC_CLI_F_RES_END 0x08
67
Willy Tarreau89a7c412022-05-05 19:38:21 +020068/* the CLI context for the httpclient command */
69struct hcli_svc_ctx {
70 struct httpclient *hc; /* the httpclient instance */
71 uint flags; /* flags from HC_CLI_F_* above */
72};
William Lallemand03a4eb12021-08-18 16:46:21 +020073
74/* These are the callback used by the HTTP Client when it needs to notify new
Willy Tarreau89a7c412022-05-05 19:38:21 +020075 * data, we only sets a flag in the IO handler via the svcctx.
76 */
William Lallemand03a4eb12021-08-18 16:46:21 +020077void hc_cli_res_stline_cb(struct httpclient *hc)
78{
79 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020080 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020081
William Lallemanddfc3f892021-08-20 11:35:29 +020082 if (!appctx)
83 return;
84
Willy Tarreau89a7c412022-05-05 19:38:21 +020085 ctx = appctx->svcctx;
86 ctx->flags |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020087 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020088}
89
90void hc_cli_res_headers_cb(struct httpclient *hc)
91{
92 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +020093 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +020094
William Lallemanddfc3f892021-08-20 11:35:29 +020095 if (!appctx)
96 return;
97
Willy Tarreau89a7c412022-05-05 19:38:21 +020098 ctx = appctx->svcctx;
99 ctx->flags |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +0200100 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200101}
102
103void hc_cli_res_body_cb(struct httpclient *hc)
104{
105 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200106 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200107
William Lallemanddfc3f892021-08-20 11:35:29 +0200108 if (!appctx)
109 return;
110
Willy Tarreau89a7c412022-05-05 19:38:21 +0200111 ctx = appctx->svcctx;
112 ctx->flags |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +0200113 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200114}
115
116void hc_cli_res_end_cb(struct httpclient *hc)
117{
118 struct appctx *appctx = hc->caller;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200119 struct hcli_svc_ctx *ctx;
William Lallemand03a4eb12021-08-18 16:46:21 +0200120
William Lallemanddfc3f892021-08-20 11:35:29 +0200121 if (!appctx)
122 return;
123
Willy Tarreau89a7c412022-05-05 19:38:21 +0200124 ctx = appctx->svcctx;
125 ctx->flags |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200126 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200127}
128
129/*
130 * Parse an httpclient keyword on the cli:
131 * httpclient <ID> <method> <URI>
132 */
133static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
134{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200135 struct hcli_svc_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemand03a4eb12021-08-18 16:46:21 +0200136 struct httpclient *hc;
137 char *err = NULL;
138 enum http_meth_t meth;
139 char *meth_str;
140 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200141 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200142
143 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
144 return 1;
145
146 if (!*args[1] || !*args[2]) {
147 memprintf(&err, ": not enough parameters");
148 goto err;
149 }
150
151 meth_str = args[1];
152 uri = ist(args[2]);
153
William Lallemanddec25c32021-10-25 19:48:37 +0200154 if (payload)
155 body = ist(payload);
156
William Lallemand03a4eb12021-08-18 16:46:21 +0200157 meth = find_http_meth(meth_str, strlen(meth_str));
158
159 hc = httpclient_new(appctx, meth, uri);
160 if (!hc) {
161 goto err;
162 }
163
164 /* update the httpclient callbacks */
165 hc->ops.res_stline = hc_cli_res_stline_cb;
166 hc->ops.res_headers = hc_cli_res_headers_cb;
167 hc->ops.res_payload = hc_cli_res_body_cb;
168 hc->ops.res_end = hc_cli_res_end_cb;
169
Willy Tarreau89a7c412022-05-05 19:38:21 +0200170 ctx->hc = hc; /* store the httpclient ptr in the applet */
171 ctx->flags = 0;
William Lallemand03a4eb12021-08-18 16:46:21 +0200172
William Lallemandbad9c8c2022-01-14 14:10:33 +0100173 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200174 goto err;
175
176
177 if (!httpclient_start(hc))
178 goto err;
179
180 return 0;
181
182err:
183 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
184 return cli_err(appctx, err);
185}
186
187/* This function dumps the content of the httpclient receive buffer
188 * on the CLI output
189 *
190 * Return 1 when the processing is finished
191 * return 0 if it needs to be called again
192 */
193static int hc_cli_io_handler(struct appctx *appctx)
194{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200195 struct hcli_svc_ctx *ctx = appctx->svcctx;
Willy Tarreauc12b3212022-05-27 11:08:15 +0200196 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau89a7c412022-05-05 19:38:21 +0200197 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200198 struct http_hdr *hdrs, *hdr;
199
Willy Tarreau89a7c412022-05-05 19:38:21 +0200200 if (ctx->flags & HC_CLI_F_RES_STLINE) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200201 chunk_printf(&trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
202 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
203 if (applet_putchk(appctx, &trash) == -1)
204 goto more;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200205 ctx->flags &= ~HC_CLI_F_RES_STLINE;
William Lallemand03a4eb12021-08-18 16:46:21 +0200206 }
207
Willy Tarreau89a7c412022-05-05 19:38:21 +0200208 if (ctx->flags & HC_CLI_F_RES_HDR) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200209 chunk_reset(&trash);
William Lallemand03a4eb12021-08-18 16:46:21 +0200210 hdrs = hc->res.hdrs;
211 for (hdr = hdrs; isttest(hdr->v); hdr++) {
Christopher Faulet0158bb22022-06-01 17:08:19 +0200212 if (!h1_format_htx_hdr(hdr->n, hdr->v, &trash))
213 goto too_many_hdrs;
William Lallemand03a4eb12021-08-18 16:46:21 +0200214 }
Christopher Faulet0158bb22022-06-01 17:08:19 +0200215 if (!chunk_memcat(&trash, "\r\n", 2))
216 goto too_many_hdrs;
217 if (applet_putchk(appctx, &trash) == -1)
218 goto more;
Willy Tarreau89a7c412022-05-05 19:38:21 +0200219 ctx->flags &= ~HC_CLI_F_RES_HDR;
William Lallemand03a4eb12021-08-18 16:46:21 +0200220 }
221
Willy Tarreau89a7c412022-05-05 19:38:21 +0200222 if (ctx->flags & HC_CLI_F_RES_BODY) {
William Lallemand03a4eb12021-08-18 16:46:21 +0200223 int ret;
224
Willy Tarreau475e4632022-05-27 10:26:46 +0200225 ret = httpclient_res_xfer(hc, sc_ib(sc));
226 channel_add_input(sc_ic(sc), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200227
Christopher Faulet0158bb22022-06-01 17:08:19 +0200228 /* remove the flag if the buffer was emptied */
229 if (httpclient_data(hc))
230 goto more;
231 ctx->flags &= ~HC_CLI_F_RES_BODY;
William Lallemand03a4eb12021-08-18 16:46:21 +0200232 }
233
234 /* we must close only if F_END is the last flag */
Willy Tarreau89a7c412022-05-05 19:38:21 +0200235 if (ctx->flags == HC_CLI_F_RES_END) {
Willy Tarreau89a7c412022-05-05 19:38:21 +0200236 ctx->flags &= ~HC_CLI_F_RES_END;
Christopher Faulet89f26262022-06-01 17:17:24 +0200237 goto end;
William Lallemand03a4eb12021-08-18 16:46:21 +0200238 }
239
Christopher Faulet0158bb22022-06-01 17:08:19 +0200240more:
241 if (!ctx->flags)
242 applet_have_no_more_data(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200243 return 0;
Christopher Faulet89f26262022-06-01 17:17:24 +0200244end:
245 return 1;
Christopher Faulet0158bb22022-06-01 17:08:19 +0200246
247too_many_hdrs:
248 return cli_err(appctx, "Too many headers.\n");
William Lallemand03a4eb12021-08-18 16:46:21 +0200249}
250
251static void hc_cli_release(struct appctx *appctx)
252{
Willy Tarreau89a7c412022-05-05 19:38:21 +0200253 struct hcli_svc_ctx *ctx = appctx->svcctx;
254 struct httpclient *hc = ctx->hc;
William Lallemand03a4eb12021-08-18 16:46:21 +0200255
256 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200257 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200258
259 return;
260}
261
262/* register cli keywords */
263static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100264 { { "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 +0200265 { { NULL }, NULL, NULL, NULL }
266}};
267
268INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
269
270
271/* --- This part of the file implements the actual HTTP client API --- */
272
William Lallemand33b0d092021-08-13 16:05:53 +0200273/*
274 * Generate a simple request and fill the httpclient request buffer with it.
275 * The request contains a request line generated from the absolute <url> and
276 * <meth> as well as list of headers <hdrs>.
277 *
278 * If the buffer was filled correctly the function returns 0, if not it returns
279 * an error_code but there is no guarantee that the buffer wasn't modified.
280 */
William Lallemanddec25c32021-10-25 19:48:37 +0200281int 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 +0200282{
283 struct htx_sl *sl;
284 struct htx *htx;
285 int err_code = 0;
286 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200287 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 +0100288 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100289 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200290
Christopher Faulet600985d2022-01-12 11:14:08 +0100291 if (!b_alloc(&hc->req.buf))
292 goto error;
293
William Lallemand33b0d092021-08-13 16:05:53 +0200294 if (meth >= HTTP_METH_OTHER)
295 goto error;
296
297 meth_ist = http_known_methods[meth];
298
299 vsn = ist("HTTP/1.1");
300
301 htx = htx_from_buf(&hc->req.buf);
302 if (!htx)
303 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100304
305 if (!hc->ops.req_payload && !isttest(payload))
306 flags |= HTX_SL_F_BODYLESS;
307
William Lallemand33b0d092021-08-13 16:05:53 +0200308 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
309 if (!sl) {
310 goto error;
311 }
312 sl->info.req.meth = meth;
313
William Lallemandf03b53c2021-11-24 15:38:17 +0100314 for (i = 0; hdrs && hdrs[i].n.len; i++) {
315 /* Don't check the value length because a header value may be empty */
316 if (isttest(hdrs[i].v) == 0)
317 continue;
318
319 if (isteqi(hdrs[i].n, ist("host")))
320 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100321 else if (isteqi(hdrs[i].n, ist("accept")))
322 foundaccept = 1;
323 else if (isteqi(hdrs[i].n, ist("user-agent")))
324 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100325
326 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
327 goto error;
328 }
William Lallemand33b0d092021-08-13 16:05:53 +0200329
William Lallemandf03b53c2021-11-24 15:38:17 +0100330 if (!foundhost) {
331 /* Add Host Header from URL */
332 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200333 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100334 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200335 goto error;
336 }
William Lallemand33b0d092021-08-13 16:05:53 +0200337
William Lallemandbad9c8c2022-01-14 14:10:33 +0100338 if (!foundaccept) {
339 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
340 goto error;
341 }
342
343 if (!foundua) {
344 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
345 goto error;
346 }
347
348
William Lallemandf03b53c2021-11-24 15:38:17 +0100349 if (!htx_add_endof(htx, HTX_BLK_EOH))
350 goto error;
351
William Lallemanddec25c32021-10-25 19:48:37 +0200352 if (isttest(payload)) {
353 /* add the payload if it can feat in the buffer, no need to set
354 * the Content-Length, the data will be sent chunked */
355 if (!htx_add_data_atonce(htx, payload))
356 goto error;
357 }
358
William Lallemand0da616e2021-10-28 15:34:26 +0200359 /* If req.payload was set, does not set the end of stream which *MUST*
360 * be set in the callback */
361 if (!hc->ops.req_payload)
362 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200363
364 htx_to_buf(htx, &hc->req.buf);
365
366 return 0;
367error:
368 err_code |= ERR_ALERT | ERR_ABORT;
369 return err_code;
370}
371
372/*
373 * transfer the response to the destination buffer and wakeup the HTTP client
374 * applet so it could fill again its buffer.
375 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500376 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200377 */
378int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
379{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100380 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200381 int ret;
382
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100383 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200384 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100385 if (!b_data(&hc->res.buf)) {
386 b_free(&hc->res.buf);
387 if (hc->appctx)
388 appctx_wakeup(hc->appctx);
389 }
William Lallemand33b0d092021-08-13 16:05:53 +0200390 return ret;
391}
392
393/*
William Lallemand0da616e2021-10-28 15:34:26 +0200394 * Transfer raw HTTP payload from src, and insert it into HTX format in the
395 * httpclient.
396 *
397 * Must be used to transfer the request body.
398 * Then wakeup the httpclient so it can transfer it.
399 *
400 * <end> tries to add the ending data flag if it succeed to copy all data.
401 *
402 * Return the number of bytes copied from src.
403 */
404int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
405{
406 int ret = 0;
407 struct htx *htx;
408
Christopher Faulet600985d2022-01-12 11:14:08 +0100409 if (!b_alloc(&hc->req.buf))
410 goto error;
411
William Lallemand0da616e2021-10-28 15:34:26 +0200412 htx = htx_from_buf(&hc->req.buf);
413 if (!htx)
414 goto error;
415
416 if (hc->appctx)
417 appctx_wakeup(hc->appctx);
418
419 ret += htx_add_data(htx, src);
420
421
422 /* if we copied all the data and the end flag is set */
423 if ((istlen(src) == ret) && end) {
Christopher Faulet48005de2022-10-14 15:10:24 +0200424 /* no more data are expected. If the HTX buffer is empty, be
425 * sure to add something (EOT block in this case) to have
426 * something to send. It is important to be sure the EOM flags
427 * will be handled by the endpoint. Because the message is
428 * empty, this should not fail. Otherwise it is an error
429 */
430 if (htx_is_empty(htx)) {
431 if (!htx_add_endof(htx, HTX_BLK_EOT))
432 goto error;
433 }
William Lallemand0da616e2021-10-28 15:34:26 +0200434 htx->flags |= HTX_FL_EOM;
435 }
436 htx_to_buf(htx, &hc->req.buf);
437
438error:
439
440 return ret;
441}
442
William Lallemandb4a4ef62022-02-23 14:18:16 +0100443/* Set the 'timeout server' in ms for the next httpclient request */
444void httpclient_set_timeout(struct httpclient *hc, int timeout)
445{
446 hc->timeout_server = timeout;
447}
448
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100449/*
450 * Sets a destination for the httpclient from an HAProxy addr format
451 * This will prevent to determine the destination from the URL
452 * Return 0 in case of success or -1 otherwise.
453 */
454int httpclient_set_dst(struct httpclient *hc, const char *dst)
455{
456 struct sockaddr_storage *sk;
457 char *errmsg = NULL;
458
459 sockaddr_free(&hc->dst);
460 /* 'sk' is statically allocated (no need to be freed). */
461 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
462 &errmsg, NULL, NULL,
463 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
464 if (!sk) {
465 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
466 free(errmsg);
467 return -1;
468 }
469
470 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
471 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
472 return -1;
473 }
474
475 return 0;
476}
William Lallemand0da616e2021-10-28 15:34:26 +0200477
478/*
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200479 * Split <url> in <scheme>, <host>, <port>
William Lallemand7f1df8f2022-04-14 17:50:20 +0200480 */
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200481static int httpclient_spliturl(struct ist url, enum http_scheme *scheme,
482 struct ist *host, int *port)
William Lallemand7f1df8f2022-04-14 17:50:20 +0200483{
484 enum http_scheme scheme_tmp = SCH_HTTP;
485 int port_tmp = 0;
486 struct ist scheme_ist, authority_ist, host_ist, port_ist;
487 char *p, *end;
488 struct http_uri_parser parser;
489
490 parser = http_uri_parser_init(url);
491 scheme_ist = http_parse_scheme(&parser);
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200492 if (!isttest(scheme_ist)) {
493 return 0;
494 }
William Lallemand7f1df8f2022-04-14 17:50:20 +0200495
496 if (isteqi(scheme_ist, ist("http://"))){
497 scheme_tmp = SCH_HTTP;
498 port_tmp = 80;
499 } else if (isteqi(scheme_ist, ist("https://"))) {
500 scheme_tmp = SCH_HTTPS;
501 port_tmp = 443;
502 }
503
504 authority_ist = http_parse_authority(&parser, 1);
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200505 if (!isttest(authority_ist)) {
506 return 0;
507 }
William Lallemand7f1df8f2022-04-14 17:50:20 +0200508 p = end = istend(authority_ist);
509
510 /* look for a port at the end of the authority */
511 while (p > istptr(authority_ist) && isdigit((unsigned char)*--p))
512 ;
513
514 if (*p == ':') {
515 host_ist = ist2(istptr(authority_ist), p - istptr(authority_ist));
516 port_ist = istnext(ist2(p, end - p));
517 ist2str(trash.area, port_ist);
518 port_tmp = atoi(trash.area);
519 } else {
520 host_ist = authority_ist;
521 }
522
523 if (scheme)
524 *scheme = scheme_tmp;
525 if (host)
526 *host = host_ist;
527 if (port)
528 *port = port_tmp;
529
Thierry Fournier74a9eb52022-10-10 12:46:38 +0200530 return 1;
William Lallemand7f1df8f2022-04-14 17:50:20 +0200531}
532
533/*
William Lallemand33b0d092021-08-13 16:05:53 +0200534 * Start the HTTP client
535 * Create the appctx, session, stream and wakeup the applet
536 *
William Lallemand33b0d092021-08-13 16:05:53 +0200537 * Return the <appctx> or NULL if it failed
538 */
539struct appctx *httpclient_start(struct httpclient *hc)
540{
541 struct applet *applet = &httpclient_applet;
542 struct appctx *appctx;
William Lallemand33b0d092021-08-13 16:05:53 +0200543
William Lallemand5085bc32022-02-17 12:52:09 +0100544 /* if the client was started and not ended, an applet is already
545 * running, we shouldn't try anything */
546 if (httpclient_started(hc) && !httpclient_ended(hc))
547 return NULL;
548
William Lallemand33b0d092021-08-13 16:05:53 +0200549 /* The HTTP client will be created in the same thread as the caller,
550 * avoiding threading issues */
Christopher Faulet6095d572022-05-16 17:09:48 +0200551 appctx = appctx_new_here(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200552 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100553 goto out;
Christopher Fauletb1e08362022-05-12 15:33:14 +0200554 appctx->svcctx = hc;
555 hc->flags = 0;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100556
Christopher Fauletb1e08362022-05-12 15:33:14 +0200557 if (appctx_init(appctx) == -1) {
558 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet92202da2022-05-11 12:22:10 +0200559 goto out_free_appctx;
William Lallemand85332732022-05-04 10:59:51 +0200560 }
561
William Lallemand33b0d092021-08-13 16:05:53 +0200562 return appctx;
563
William Lallemand33b0d092021-08-13 16:05:53 +0200564out_free_appctx:
Christopher Fauletb1e08362022-05-12 15:33:14 +0200565 appctx_free_on_early_error(appctx);
William Lallemand33b0d092021-08-13 16:05:53 +0200566out:
567
568 return NULL;
569}
570
William Lallemandecb83e12021-09-28 11:00:43 +0200571/*
572 * This function tries to destroy the httpclient if it wasn't running.
573 * If it was running, stop the client and ask it to autodestroy itself.
574 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500575 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200576 *
577 */
578void httpclient_stop_and_destroy(struct httpclient *hc)
579{
580
William Lallemandb8b13702021-09-28 12:15:37 +0200581 /* The httpclient was already stopped or never started, we can safely destroy it */
582 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200583 httpclient_destroy(hc);
584 } else {
Willy Tarreaub4829202022-09-01 20:40:26 +0200585 /* if the client wasn't stopped, ask for a stop and destroy */
William Lallemandecb83e12021-09-28 11:00:43 +0200586 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
Willy Tarreaub4829202022-09-01 20:40:26 +0200587 /* the calling applet doesn't exist anymore */
588 hc->caller = NULL;
William Lallemandecb83e12021-09-28 11:00:43 +0200589 if (hc->appctx)
590 appctx_wakeup(hc->appctx);
591 }
592}
593
William Lallemand33b0d092021-08-13 16:05:53 +0200594/* Free the httpclient */
595void httpclient_destroy(struct httpclient *hc)
596{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200597 struct http_hdr *hdrs;
598
599
William Lallemand33b0d092021-08-13 16:05:53 +0200600 if (!hc)
601 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200602
William Lallemand2a879002021-10-05 15:50:45 +0200603 /* we should never destroy a client which was started but not stopped */
604 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200605
William Lallemand03f5a1c2021-09-27 15:17:47 +0200606 /* request */
607 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200608 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200609 /* response */
610 istfree(&hc->res.vsn);
611 istfree(&hc->res.reason);
612 hdrs = hc->res.hdrs;
613 while (hdrs && isttest(hdrs->n)) {
614 istfree(&hdrs->n);
615 istfree(&hdrs->v);
616 hdrs++;
617 }
618 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200619 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100620 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200621
William Lallemand33b0d092021-08-13 16:05:53 +0200622 free(hc);
623
624 return;
625}
626
627/* Allocate an httpclient and its buffers
William Lallemand992ad622022-09-12 17:39:04 +0200628 * Use the default httpclient_proxy
629 *
William Lallemand33b0d092021-08-13 16:05:53 +0200630 * Return NULL on failure */
631struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
632{
633 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200634
635 hc = calloc(1, sizeof(*hc));
636 if (!hc)
637 goto err;
638
Christopher Faulet600985d2022-01-12 11:14:08 +0100639 hc->req.buf = BUF_NULL;
640 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200641 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100642 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200643 hc->req.meth = meth;
William Lallemand992ad622022-09-12 17:39:04 +0200644 httpclient_set_proxy(hc, httpclient_proxy);
William Lallemand33b0d092021-08-13 16:05:53 +0200645
646 return hc;
647
648err:
649 httpclient_destroy(hc);
650 return NULL;
651}
652
William Lallemand992ad622022-09-12 17:39:04 +0200653/* Allocate an httpclient and its buffers,
654 * Use the proxy <px>
655 *
656 * Return and httpclient or NULL.
657 */
658struct httpclient *httpclient_new_from_proxy(struct proxy *px, void *caller, enum http_meth_t meth, struct ist url)
659{
660 struct httpclient *hc;
661
662 hc = httpclient_new(caller, meth, url);
663 if (!hc)
664 return NULL;
665
666 httpclient_set_proxy(hc, px);
667
668 return hc;
669}
670
671/*
672 * Configure an httpclient with a specific proxy <px>
673 *
674 * The proxy <px> must contains 2 srv, one configured for clear connections, the other for SSL.
675 *
676 */
677int httpclient_set_proxy(struct httpclient *hc, struct proxy *px)
678{
679 struct server *srv;
680
681 hc->px = px;
682
683 for (srv = px->srv; srv != NULL; srv = srv->next) {
684 if (srv->xprt == xprt_get(XPRT_RAW)) {
685 hc->srv_raw = srv;
686#ifdef USE_OPENSSL
687 } else if (srv->xprt == xprt_get(XPRT_SSL)) {
688 hc->srv_ssl = srv;
689#endif
690 }
691 }
692
693 return 0;
694}
695
William Lallemand33b0d092021-08-13 16:05:53 +0200696static void httpclient_applet_io_handler(struct appctx *appctx)
697{
Willy Tarreau1eea6652022-05-05 20:12:01 +0200698 struct httpclient *hc = appctx->svcctx;
Willy Tarreauc12b3212022-05-27 11:08:15 +0200699 struct stconn *sc = appctx_sc(appctx);
Willy Tarreaub89f8722022-05-27 10:37:32 +0200700 struct stream *s = __sc_strm(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200701 struct channel *req = &s->req;
702 struct channel *res = &s->res;
703 struct htx_blk *blk = NULL;
704 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200705 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200706 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100707 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100708 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200709
William Lallemand33b0d092021-08-13 16:05:53 +0200710 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200711
712 /* required to stop */
713 if (hc->flags & HTTPCLIENT_FA_STOP)
714 goto end;
715
William Lallemand33b0d092021-08-13 16:05:53 +0200716 switch(appctx->st0) {
717
718 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100719 /* we know that the buffer is empty here, since
720 * it's the first call, we can freely copy the
721 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100722 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100723 if (!ret)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200724 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100725
Christopher Faulet600985d2022-01-12 11:14:08 +0100726 if (!b_data(&hc->req.buf))
727 b_free(&hc->req.buf);
728
William Lallemanddb8a1f32021-11-08 16:55:14 +0100729 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100730 if (!htx)
Willy Tarreau6486ff82022-09-02 11:42:50 +0200731 goto full;
William Lallemand933fe392021-11-04 09:45:58 +0100732
William Lallemanddb8a1f32021-11-08 16:55:14 +0100733 channel_add_input(req, htx->data);
734
William Lallemand933fe392021-11-04 09:45:58 +0100735 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
736 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
737 else
738 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
739
William Lallemand33b0d092021-08-13 16:05:53 +0200740 goto more; /* we need to leave the IO handler once we wrote the request */
741 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200742 case HTTPCLIENT_S_REQ_BODY:
743 /* call the payload callback */
744 {
745 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100746 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200747
William Lallemand0da616e2021-10-28 15:34:26 +0200748 /* call the request callback */
749 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100750
William Lallemandccc7ee42022-03-18 17:57:15 +0100751 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100752 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100753
William Lallemandccc7ee42022-03-18 17:57:15 +0100754 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100755 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100756
William Lallemandccc7ee42022-03-18 17:57:15 +0100757 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200758 size_t data = hc_htx->data;
759
William Lallemandccc7ee42022-03-18 17:57:15 +0100760 /* Here htx_to_buf() will set buffer data to 0 because
761 * the HTX is empty, and allow us to do an xfer.
762 */
763 htx_to_buf(hc_htx, &hc->req.buf);
764 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100765 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200766 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100767 } else {
768 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100769
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200770 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100771 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100772
William Lallemandccc7ee42022-03-18 17:57:15 +0100773 /* we must copy the EOM if we empty the buffer */
774 if (htx_is_empty(hc_htx)) {
775 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
776 }
777 htx_to_buf(htx, &req->buf);
778 htx_to_buf(hc_htx, &hc->req.buf);
779 }
780
781
782 if (!b_data(&hc->req.buf))
783 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200784 }
785
William Lallemanddb8a1f32021-11-08 16:55:14 +0100786 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200787 if (!htx)
788 goto more;
789
790 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100791 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet3d433242022-03-03 15:38:39 +0100792 req->flags |= CF_EOI;
Willy Tarreaud869e132022-05-17 18:05:31 +0200793 se_fl_set(appctx->sedesc, SE_FL_EOI);
William Lallemand0da616e2021-10-28 15:34:26 +0200794 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100795 }
William Lallemand0da616e2021-10-28 15:34:26 +0200796
William Lallemand1eca8942022-03-17 14:57:23 +0100797 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200798 }
799 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200800
801 case HTTPCLIENT_S_RES_STLINE:
802 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100803 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200804 goto more;
805 htx = htxbuf(&res->buf);
806 if (!htx)
807 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100808 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200809 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
810 sl = htx_get_blk_ptr(htx, blk);
811 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
812 goto more;
813
814 /* copy the status line in the httpclient */
815 hc->res.status = sl->info.res.status;
816 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
817 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100818 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200819 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100820 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200821 /* caller callback */
822 if (hc->ops.res_stline)
823 hc->ops.res_stline(hc);
824
825 /* if there is no HTX data anymore and the EOM flag is
826 * set, leave (no body) */
827 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
828 appctx->st0 = HTTPCLIENT_S_RES_END;
829 else
830 appctx->st0 = HTTPCLIENT_S_RES_HDR;
831 break;
832
833 case HTTPCLIENT_S_RES_HDR:
834 /* first copy the headers in a local hdrs
835 * structure, once we the total numbers of the
836 * header we allocate the right size and copy
837 * them. The htx block of the headers are
838 * removed each time one is read */
839 {
840 struct http_hdr hdrs[global.tune.max_http_hdr];
841
William Lallemanda625b032022-03-17 14:45:46 +0100842 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200843 goto more;
844 htx = htxbuf(&res->buf);
845 if (!htx)
846 goto more;
847
848 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200849 blk = htx_get_head_blk(htx);
850 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200851 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100852 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200853
Christopher Faulet534645d2022-04-29 13:44:46 +0200854 c_rew(res, sz);
William Lallemandc020b252022-03-09 18:56:02 +0100855
Christopher Faulet18de6f22022-06-01 16:37:49 +0200856 if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100857 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
858 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100859 hdr_num++;
860 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200861 else if (type == HTX_BLK_EOH) {
862 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200863 hdrs[hdr_num].n = IST_NULL;
864 hdrs[hdr_num].v = IST_NULL;
Christopher Faulet18de6f22022-06-01 16:37:49 +0200865 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200866 break;
867 }
Christopher Faulet18de6f22022-06-01 16:37:49 +0200868 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200869 }
870
William Lallemand0d6f7792021-08-20 11:59:49 +0200871 if (hdr_num) {
872 /* alloc and copy the headers in the httpclient struct */
873 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
874 if (!hc->res.hdrs)
875 goto end;
876 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200877
William Lallemand0d6f7792021-08-20 11:59:49 +0200878 /* caller callback */
879 if (hc->ops.res_headers)
880 hc->ops.res_headers(hc);
881 }
William Lallemand33b0d092021-08-13 16:05:53 +0200882
883 /* if there is no HTX data anymore and the EOM flag is
884 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200885 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200886 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200887 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200888 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200889 }
William Lallemand33b0d092021-08-13 16:05:53 +0200890 }
891 break;
892
893 case HTTPCLIENT_S_RES_BODY:
894 /*
895 * The IO handler removes the htx blocks in the response buffer and
896 * push them in the hc->res.buf buffer in a raw format.
897 */
William Lallemanda625b032022-03-17 14:45:46 +0100898 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100899 goto more;
900
William Lallemand33b0d092021-08-13 16:05:53 +0200901 htx = htxbuf(&res->buf);
902 if (!htx || htx_is_empty(htx))
903 goto more;
904
Christopher Faulet600985d2022-01-12 11:14:08 +0100905 if (!b_alloc(&hc->res.buf))
906 goto more;
907
William Lallemand33b0d092021-08-13 16:05:53 +0200908 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100909 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200910
911 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200912 blk = htx_get_head_blk(htx);
913 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100914 enum htx_blk_type type = htx_get_blk_type(blk);
915 size_t count = co_data(res);
916 uint32_t blksz = htx_get_blksz(blk);
917 uint32_t room = b_room(&hc->res.buf);
918 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200919
William Lallemandc8f1eb92022-03-09 11:58:51 +0100920 /* we should try to copy the maximum output data in a block, which fit
921 * the destination buffer */
922 vlen = MIN(count, blksz);
923 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100924
William Lallemandc8f1eb92022-03-09 11:58:51 +0100925 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100926 goto process_data;
927
William Lallemand33b0d092021-08-13 16:05:53 +0200928 if (type == HTX_BLK_DATA) {
929 struct ist v = htx_get_blk_value(htx, blk);
930
William Lallemandc8f1eb92022-03-09 11:58:51 +0100931 __b_putblk(&hc->res.buf, v.ptr, vlen);
932 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200933
William Lallemandc8f1eb92022-03-09 11:58:51 +0100934 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200935 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100936 else
937 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100938
William Lallemand33b0d092021-08-13 16:05:53 +0200939 /* the data must be processed by the caller in the receive phase */
940 if (hc->ops.res_payload)
941 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100942
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500943 /* cannot copy everything, need to process */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100944 if (vlen != blksz)
945 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200946 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100947 if (vlen != blksz)
948 goto process_data;
949
William Lallemand33b0d092021-08-13 16:05:53 +0200950 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100951 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200952 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200953 }
954 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100955
William Lallemand33b0d092021-08-13 16:05:53 +0200956 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100957 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200958 goto more;
959
William Lallemandc8f1eb92022-03-09 11:58:51 +0100960
William Lallemand33b0d092021-08-13 16:05:53 +0200961 /* end of message, we should quit */
962 appctx->st0 = HTTPCLIENT_S_RES_END;
963 break;
964
965 case HTTPCLIENT_S_RES_END:
966 goto end;
967 break;
968 }
969 }
970
971process_data:
972
Willy Tarreaub89f8722022-05-27 10:37:32 +0200973 sc_will_read(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200974
975 return;
Willy Tarreau6486ff82022-09-02 11:42:50 +0200976full:
977 /* There was not enough room in the response channel */
Willy Tarreaub89f8722022-05-27 10:37:32 +0200978 sc_need_room(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200979
Willy Tarreau6486ff82022-09-02 11:42:50 +0200980more:
981 /* we'll automatically be called again on missing data */
William Lallemand33b0d092021-08-13 16:05:53 +0200982 if (appctx->st0 == HTTPCLIENT_S_RES_END)
983 goto end;
984
985 /* The state machine tries to handle as much data as possible, if there
986 * isn't any data to handle and a shutdown is detected, let's stop
987 * everything */
988 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100989 (res->flags & CF_SHUTW) ||
990 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200991 goto end;
992 }
993 return;
994
995end:
Willy Tarreaub89f8722022-05-27 10:37:32 +0200996 sc_shutw(sc);
997 sc_shutr(sc);
William Lallemand33b0d092021-08-13 16:05:53 +0200998 return;
999}
1000
Christopher Fauletb1e08362022-05-12 15:33:14 +02001001static int httpclient_applet_init(struct appctx *appctx)
1002{
1003 struct httpclient *hc = appctx->svcctx;
1004 struct stream *s;
1005 struct sockaddr_storage *addr = NULL;
1006 struct sockaddr_storage ss_url = {};
1007 struct sockaddr_storage *ss_dst;
1008 enum obj_type *target = NULL;
1009 struct ist host = IST_NULL;
1010 enum http_scheme scheme;
1011 int port;
1012 int doresolve = 0;
1013
1014
1015 /* parse the URL and */
Thierry Fournier74a9eb52022-10-10 12:46:38 +02001016 if (!httpclient_spliturl(hc->req.url, &scheme, &host, &port))
1017 goto out_error;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001018
1019 if (hc->dst) {
1020 /* if httpclient_set_dst() was used, sets the alternative address */
1021 ss_dst = hc->dst;
1022 } else {
1023 /* set the dst using the host, or 0.0.0.0 to resolve */
1024 ist2str(trash.area, host);
1025 ss_dst = str2ip2(trash.area, &ss_url, 0);
1026 if (!ss_dst) { /* couldn't get an IP from that, try to resolve */
1027 doresolve = 1;
1028 ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
1029 }
1030 sock_inet_set_port(ss_dst, port);
1031 }
1032
Christopher Fauletb1e08362022-05-12 15:33:14 +02001033 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*ss_dst)))
1034 goto out_error;
1035
1036 /* choose the SSL server or not */
1037 switch (scheme) {
1038 case SCH_HTTP:
William Lallemand992ad622022-09-12 17:39:04 +02001039 target = &hc->srv_raw->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001040 break;
1041 case SCH_HTTPS:
1042#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001043 if (hc->srv_ssl) {
1044 target = &hc->srv_ssl->obj_type;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001045 } else {
1046 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
1047 goto out_free_addr;
1048 }
1049#else
1050 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
1051 goto out_free_addr;
1052#endif
1053 break;
1054 }
1055
William Lallemand992ad622022-09-12 17:39:04 +02001056 if (appctx_finalize_startup(appctx, hc->px, &hc->req.buf) == -1) {
Christopher Fauletb1e08362022-05-12 15:33:14 +02001057 ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
1058 goto out_free_addr;
1059 }
1060
1061 s = appctx_strm(appctx);
1062 s->target = target;
1063 /* set the "timeout server" */
1064 s->req.wto = hc->timeout_server;
1065 s->res.rto = hc->timeout_server;
1066
1067 if (doresolve) {
1068 /* in order to do the set-dst we need to put the address on the front */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001069 s->scf->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001070 } else {
1071 /* in cases we don't use the resolve we already have the address
1072 * and must put it on the backend side, some of the cases are
1073 * not meant to be used on the frontend (sockpair, unix socket etc.) */
Willy Tarreau7cb9e6c2022-05-17 19:40:40 +02001074 s->scb->dst = addr;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001075 }
1076
Willy Tarreaucb041662022-05-17 19:44:42 +02001077 s->scb->flags |= SC_FL_NOLINGER;
Christopher Fauletb1e08362022-05-12 15:33:14 +02001078 s->flags |= SF_ASSIGNED;
1079 s->res.flags |= CF_READ_DONTWAIT;
1080
1081 /* applet is waiting for data */
Willy Tarreau90e8b452022-05-25 18:21:43 +02001082 applet_need_more_data(appctx);
Christopher Fauletb1e08362022-05-12 15:33:14 +02001083 appctx_wakeup(appctx);
1084
1085 hc->appctx = appctx;
1086 hc->flags |= HTTPCLIENT_FS_STARTED;
1087
1088 /* The request was transferred when the stream was created. So switch
1089 * directly to REQ_BODY or RES_STLINE state
1090 */
1091 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
1092 return 0;
1093
1094 out_free_addr:
1095 sockaddr_free(&addr);
1096 out_error:
1097 return -1;
1098}
1099
William Lallemand33b0d092021-08-13 16:05:53 +02001100static void httpclient_applet_release(struct appctx *appctx)
1101{
Willy Tarreau1eea6652022-05-05 20:12:01 +02001102 struct httpclient *hc = appctx->svcctx;
William Lallemand33b0d092021-08-13 16:05:53 +02001103
William Lallemand1123dde2021-09-21 10:58:10 +02001104 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +02001105 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +02001106 /* the applet is leaving, remove the ptr so we don't try to call it
1107 * again from the caller */
1108 hc->appctx = NULL;
1109
William Lallemandeb0d4c42022-04-06 14:12:37 +02001110 if (hc->ops.res_end)
1111 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +02001112
1113 /* destroy the httpclient when set to autotokill */
1114 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
1115 httpclient_destroy(hc);
1116 }
1117
William Lallemand33b0d092021-08-13 16:05:53 +02001118 return;
1119}
1120
1121/* HTTP client applet */
1122static struct applet httpclient_applet = {
1123 .obj_type = OBJ_TYPE_APPLET,
1124 .name = "<HTTPCLIENT>",
1125 .fct = httpclient_applet_io_handler,
Christopher Fauletb1e08362022-05-12 15:33:14 +02001126 .init = httpclient_applet_init,
William Lallemand33b0d092021-08-13 16:05:53 +02001127 .release = httpclient_applet_release,
1128};
1129
William Lallemand5392ff62022-04-28 16:55:02 +02001130
William Lallemand54aec5f2022-09-12 16:46:35 +02001131static int httpclient_resolve_init(struct proxy *px)
William Lallemand5392ff62022-04-28 16:55:02 +02001132{
1133 struct act_rule *rule;
1134 int i;
William Lallemand8a734cb2022-05-04 16:10:47 +02001135 char *do_resolve = NULL;
1136 char *http_rules[][11] = {
William Lallemand5392ff62022-04-28 16:55:02 +02001137 { "set-var(txn.hc_ip)", "dst", "" },
William Lallemandd78dfe72022-08-26 16:45:13 +02001138 { do_resolve, "hdr(Host),host_only", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
William Lallemand5392ff62022-04-28 16:55:02 +02001139 { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
1140 { "capture", "var(txn.hc_ip)", "len", "40", "" },
1141 { "set-dst", "var(txn.hc_ip)", "" },
1142 { "" }
1143 };
1144
William Lallemand8a734cb2022-05-04 16:10:47 +02001145 if (!resolvers_id)
1146 resolvers_id = strdup("default");
1147
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001148 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 +02001149 http_rules[1][0] = do_resolve;
1150
William Lallemand7867f632022-05-05 19:02:59 +02001151 /* Try to create the default resolvers section */
1152 resolvers_create_default();
1153
William Lallemand8a734cb2022-05-04 16:10:47 +02001154 /* if the resolver does not exist and no hard_error was set, simply ignore resolving */
1155 if (!find_resolvers_by_id(resolvers_id) && !hard_error_resolvers) {
1156 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001157 return 0;
William Lallemand8a734cb2022-05-04 16:10:47 +02001158 }
William Lallemand5392ff62022-04-28 16:55:02 +02001159
1160
1161 for (i = 0; *http_rules[i][0] != '\0'; i++) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001162 rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, px);
William Lallemand5392ff62022-04-28 16:55:02 +02001163 if (!rule) {
William Lallemand8a734cb2022-05-04 16:10:47 +02001164 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001165 ha_alert("Couldn't setup the httpclient resolver.\n");
1166 return 1;
1167 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001168 LIST_APPEND(&px->http_req_rules, &rule->list);
William Lallemand5392ff62022-04-28 16:55:02 +02001169 }
1170
William Lallemand8a734cb2022-05-04 16:10:47 +02001171 free(do_resolve);
William Lallemand5392ff62022-04-28 16:55:02 +02001172 return 0;
1173}
1174
William Lallemand83614a92021-08-13 14:47:57 +02001175/*
William Lallemand54aec5f2022-09-12 16:46:35 +02001176 * Creates an internal proxy which will be used for httpclient.
1177 * This will allocate 2 servers (raw and ssl) and 1 proxy.
1178 *
1179 * This function must be called from a precheck callback.
1180 *
1181 * Return a proxy or NULL.
William Lallemand83614a92021-08-13 14:47:57 +02001182 */
William Lallemand54aec5f2022-09-12 16:46:35 +02001183struct proxy *httpclient_create_proxy(const char *id)
William Lallemand83614a92021-08-13 14:47:57 +02001184{
William Lallemand85af49c2022-05-04 14:33:57 +02001185 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001186 char *errmsg = NULL;
William Lallemand54aec5f2022-09-12 16:46:35 +02001187 struct proxy *px = NULL;
1188 struct server *srv_raw = NULL;
1189#ifdef USE_OPENSSL
1190 struct server *srv_ssl = NULL;
1191#endif
William Lallemand83614a92021-08-13 14:47:57 +02001192
William Lallemandc6ceba32022-04-22 16:49:53 +02001193 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001194 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001195
William Lallemand54aec5f2022-09-12 16:46:35 +02001196 px = alloc_new_proxy(id, PR_CAP_LISTEN|PR_CAP_INT|PR_CAP_HTTPCLIENT, &errmsg);
1197 if (!px) {
William Lallemand85af49c2022-05-04 14:33:57 +02001198 memprintf(&errmsg, "couldn't allocate proxy.");
William Lallemand83614a92021-08-13 14:47:57 +02001199 err_code |= ERR_ALERT | ERR_FATAL;
1200 goto err;
1201 }
1202
William Lallemand54aec5f2022-09-12 16:46:35 +02001203 proxy_preset_defaults(px);
Willy Tarreau0e72e402021-08-20 10:23:12 +02001204
William Lallemand54aec5f2022-09-12 16:46:35 +02001205 px->options |= PR_O_WREQ_BODY;
1206 px->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
1207 px->options2 |= PR_O2_INDEPSTR;
1208 px->mode = PR_MODE_HTTP;
1209 px->maxconn = 0;
1210 px->accept = NULL;
1211 px->conn_retries = CONN_RETRIES;
1212 px->timeout.client = TICK_ETERNITY;
William Lallemand83614a92021-08-13 14:47:57 +02001213 /* The HTTP Client use the "option httplog" with the global log server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001214 px->conf.logformat_string = default_http_log_format;
1215 px->http_needed = 1;
William Lallemand83614a92021-08-13 14:47:57 +02001216
1217 /* clear HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001218 srv_raw = new_server(px);
1219 if (!srv_raw) {
William Lallemand83614a92021-08-13 14:47:57 +02001220 memprintf(&errmsg, "out of memory.");
William Lallemand85af49c2022-05-04 14:33:57 +02001221 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001222 goto err;
1223 }
1224
William Lallemand54aec5f2022-09-12 16:46:35 +02001225 srv_settings_cpy(srv_raw, &px->defsrv, 0);
1226 srv_raw->iweight = 0;
1227 srv_raw->uweight = 0;
1228 srv_raw->xprt = xprt_get(XPRT_RAW);
1229 srv_raw->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1230 srv_raw->id = strdup("<HTTPCLIENT>");
1231 if (!srv_raw->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001232 memprintf(&errmsg, "out of memory.");
1233 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001234 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001235 }
William Lallemand83614a92021-08-13 14:47:57 +02001236
William Lallemand957ab132021-08-24 18:33:28 +02001237#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001238 /* SSL HTTP server */
William Lallemand54aec5f2022-09-12 16:46:35 +02001239 srv_ssl = new_server(px);
1240 if (!srv_ssl) {
William Lallemand83614a92021-08-13 14:47:57 +02001241 memprintf(&errmsg, "out of memory.");
1242 err_code |= ERR_ALERT | ERR_FATAL;
1243 goto err;
1244 }
William Lallemand54aec5f2022-09-12 16:46:35 +02001245 srv_settings_cpy(srv_ssl, &px->defsrv, 0);
1246 srv_ssl->iweight = 0;
1247 srv_ssl->uweight = 0;
1248 srv_ssl->xprt = xprt_get(XPRT_SSL);
1249 srv_ssl->use_ssl = 1;
1250 srv_ssl->flags |= SRV_F_MAPPORTS; /* needed to apply the port change with resolving */
1251 srv_ssl->id = strdup("<HTTPSCLIENT>");
1252 if (!srv_ssl->id) {
William Lallemand85af49c2022-05-04 14:33:57 +02001253 memprintf(&errmsg, "out of memory.");
1254 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand83614a92021-08-13 14:47:57 +02001255 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001256 }
William Lallemand83614a92021-08-13 14:47:57 +02001257
Willy Tarreaudf3231c2022-09-02 09:02:21 +02001258#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
William Lallemand54aec5f2022-09-12 16:46:35 +02001259 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 +02001260 err_code |= ERR_ALERT | ERR_FATAL;
1261 goto err;
1262 }
1263#endif
William Lallemand54aec5f2022-09-12 16:46:35 +02001264 srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001265 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001266 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
William Lallemand683fbb82022-05-04 15:43:01 +02001267
1268 if (!httpclient_ssl_ca_file)
1269 httpclient_ssl_ca_file = strdup("@system-ca");
1270
William Lallemand54aec5f2022-09-12 16:46:35 +02001271 srv_ssl->ssl_ctx.ca_file = httpclient_ssl_ca_file;
1272 if (!ssl_store_load_locations_file(srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT)) {
William Lallemand6fce46a2022-05-04 14:53:41 +02001273 /* if we failed to load the ca-file, only quits in
1274 * error with hard_error, otherwise just disable the
1275 * feature. */
1276 if (hard_error_ssl) {
William Lallemand54aec5f2022-09-12 16:46:35 +02001277 memprintf(&errmsg, "cannot initialize SSL verify with 'ca-file \"%s\"'.", srv_ssl->ssl_ctx.ca_file);
William Lallemand6fce46a2022-05-04 14:53:41 +02001278 err_code |= ERR_ALERT | ERR_FATAL;
1279 goto err;
1280 } else {
William Lallemand54aec5f2022-09-12 16:46:35 +02001281 ha_free(&srv_ssl->ssl_ctx.ca_file);
1282 srv_drop(srv_ssl);
1283 srv_ssl = NULL;
William Lallemand6fce46a2022-05-04 14:53:41 +02001284 }
William Lallemand4006b0f2022-04-25 18:23:35 +02001285 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001286 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001287
William Lallemand957ab132021-08-24 18:33:28 +02001288#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001289
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001290 /* add the proxy in the proxy list only if everything is successful */
William Lallemand54aec5f2022-09-12 16:46:35 +02001291 px->next = proxies_list;
1292 proxies_list = px;
William Lallemand83614a92021-08-13 14:47:57 +02001293
William Lallemand54aec5f2022-09-12 16:46:35 +02001294 if (httpclient_resolve_init(px) != 0) {
William Lallemand85af49c2022-05-04 14:33:57 +02001295 memprintf(&errmsg, "cannot initialize resolvers.");
1296 err_code |= ERR_ALERT | ERR_FATAL;
William Lallemand5392ff62022-04-28 16:55:02 +02001297 goto err;
William Lallemand85af49c2022-05-04 14:33:57 +02001298 }
William Lallemand5392ff62022-04-28 16:55:02 +02001299
William Lallemand211c9672021-08-24 17:18:13 +02001300 /* link the 2 servers in the proxy */
William Lallemand54aec5f2022-09-12 16:46:35 +02001301 srv_raw->next = px->srv;
1302 px->srv = srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +02001303
1304#ifdef USE_OPENSSL
William Lallemand54aec5f2022-09-12 16:46:35 +02001305 if (srv_ssl) {
1306 srv_ssl->next = px->srv;
1307 px->srv = srv_ssl;
William Lallemand4006b0f2022-04-25 18:23:35 +02001308 }
William Lallemand957ab132021-08-24 18:33:28 +02001309#endif
1310
William Lallemand211c9672021-08-24 17:18:13 +02001311
William Lallemand83614a92021-08-13 14:47:57 +02001312err:
William Lallemand85af49c2022-05-04 14:33:57 +02001313 if (err_code & ERR_CODE) {
1314 ha_alert("httpclient: cannot initialize: %s\n", errmsg);
1315 free(errmsg);
William Lallemand54aec5f2022-09-12 16:46:35 +02001316 srv_drop(srv_raw);
1317#ifdef USE_OPENSSL
1318 srv_drop(srv_ssl);
1319#endif
1320 free_proxy(px);
1321
1322 return NULL;
1323 }
1324 return px;
1325}
1326
1327/*
1328 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
1329 * the other for HTTPS.
1330 */
1331static int httpclient_precheck()
1332{
William Lallemand54aec5f2022-09-12 16:46:35 +02001333 /* initialize the default httpclient_proxy which is used for the CLI and the lua */
1334
1335 httpclient_proxy = httpclient_create_proxy("<HTTPCLIENT>");
1336 if (!httpclient_proxy)
1337 return 1;
William Lallemand54aec5f2022-09-12 16:46:35 +02001338
1339 return 0;
William Lallemand83614a92021-08-13 14:47:57 +02001340}
1341
William Lallemand2c8b0842022-04-22 15:16:09 +02001342static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001343{
William Lallemand85af49c2022-05-04 14:33:57 +02001344 int err_code = ERR_NONE;
William Lallemand83614a92021-08-13 14:47:57 +02001345 struct logsrv *logsrv;
William Lallemand992ad622022-09-12 17:39:04 +02001346 struct proxy *curproxy = NULL;
William Lallemand71e31582022-03-16 15:47:47 +01001347 char *errmsg = NULL;
William Lallemand992ad622022-09-12 17:39:04 +02001348#ifdef USE_OPENSSL
1349 struct server *srv = NULL;
1350 struct server *srv_ssl = NULL;
1351#endif
William Lallemand83614a92021-08-13 14:47:57 +02001352
William Lallemandc6ceba32022-04-22 16:49:53 +02001353 if (global.mode & MODE_MWORKER_WAIT)
William Lallemand85af49c2022-05-04 14:33:57 +02001354 return ERR_NONE;
William Lallemandc6ceba32022-04-22 16:49:53 +02001355
William Lallemand992ad622022-09-12 17:39:04 +02001356 /* Initialize the logs for every proxy dedicated to the httpclient */
1357 for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
William Lallemand83614a92021-08-13 14:47:57 +02001358
William Lallemand992ad622022-09-12 17:39:04 +02001359 if (!(curproxy->cap & PR_CAP_HTTPCLIENT))
1360 continue;
William Lallemand83614a92021-08-13 14:47:57 +02001361
William Lallemand992ad622022-09-12 17:39:04 +02001362 /* copy logs from "global" log list */
1363 list_for_each_entry(logsrv, &global.logsrvs, list) {
1364 struct logsrv *node = malloc(sizeof(*node));
1365
1366 if (!node) {
1367 memprintf(&errmsg, "out of memory.");
1368 err_code |= ERR_ALERT | ERR_FATAL;
1369 goto err;
1370 }
1371
1372 memcpy(node, logsrv, sizeof(*node));
1373 LIST_INIT(&node->list);
1374 LIST_APPEND(&curproxy->logsrvs, &node->list);
1375 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1376 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001377 }
William Lallemand992ad622022-09-12 17:39:04 +02001378 if (curproxy->conf.logformat_string) {
1379 curproxy->conf.args.ctx = ARGC_LOG;
1380 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1381 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
1382 SMP_VAL_FE_LOG_END, &errmsg)) {
1383 memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
1384 err_code |= ERR_ALERT | ERR_FATAL;
1385 goto err;
1386 }
1387 curproxy->conf.args.file = NULL;
1388 curproxy->conf.args.line = 0;
1389 }
William Lallemand71e31582022-03-16 15:47:47 +01001390
1391#ifdef USE_OPENSSL
William Lallemand992ad622022-09-12 17:39:04 +02001392 /* initialize the SNI for the SSL servers */
1393
1394 for (srv = curproxy->srv; srv != NULL; srv = srv->next) {
1395 if (srv->xprt == xprt_get(XPRT_SSL)) {
1396 srv_ssl = srv;
1397 }
William Lallemand715c1012022-03-16 16:39:23 +01001398 }
Miroslav Zagoraccbfee3a2022-09-19 12:20:29 +02001399 if (srv_ssl && !srv_ssl->sni_expr) {
William Lallemand992ad622022-09-12 17:39:04 +02001400 /* init the SNI expression */
1401 /* always use the host header as SNI, without the port */
1402 srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1403 err_code |= server_parse_sni_expr(srv_ssl, curproxy, &errmsg);
1404 if (err_code & ERR_CODE) {
1405 memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
1406 goto err;
1407 }
1408 }
William Lallemand71e31582022-03-16 15:47:47 +01001409#endif
William Lallemand992ad622022-09-12 17:39:04 +02001410 }
William Lallemand71e31582022-03-16 15:47:47 +01001411
William Lallemand83614a92021-08-13 14:47:57 +02001412err:
William Lallemand85af49c2022-05-04 14:33:57 +02001413 if (err_code & ERR_CODE) {
1414 ha_alert("httpclient: failed to initialize: %s\n", errmsg);
1415 free(errmsg);
1416
1417 }
1418 return err_code;
William Lallemand83614a92021-08-13 14:47:57 +02001419}
1420
William Lallemand83614a92021-08-13 14:47:57 +02001421/* initialize the proxy and servers for the HTTP client */
1422
William Lallemand2c8b0842022-04-22 15:16:09 +02001423REGISTER_PRE_CHECK(httpclient_precheck);
1424REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001425
William Lallemand8a734cb2022-05-04 16:10:47 +02001426static int httpclient_parse_global_resolvers(char **args, int section_type, struct proxy *curpx,
1427 const struct proxy *defpx, const char *file, int line,
1428 char **err)
1429{
1430 if (too_many_args(1, args, err, NULL))
1431 return -1;
1432
1433 /* any configuration should set the hard_error flag */
1434 hard_error_resolvers = 1;
1435
1436 free(resolvers_id);
1437 resolvers_id = strdup(args[1]);
1438
1439 return 0;
1440}
1441
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001442static int httpclient_parse_global_prefer(char **args, int section_type, struct proxy *curpx,
1443 const struct proxy *defpx, const char *file, int line,
1444 char **err)
1445{
1446 if (too_many_args(1, args, err, NULL))
1447 return -1;
1448
1449 /* any configuration should set the hard_error flag */
1450 hard_error_resolvers = 1;
1451
1452
1453 if (strcmp(args[1],"ipv4") == 0)
1454 resolvers_prefer = "ipv4";
1455 else if (strcmp(args[1],"ipv6") == 0)
1456 resolvers_prefer = "ipv6";
1457 else {
1458 ha_alert("parsing [%s:%d] : '%s' expects 'ipv4' or 'ipv6' as argument.\n", file, line, args[0]);
1459 return -1;
1460 }
1461
1462 return 0;
1463}
William Lallemand8a734cb2022-05-04 16:10:47 +02001464
1465
William Lallemandeaa703e2022-04-22 17:52:33 +02001466#ifdef USE_OPENSSL
William Lallemand683fbb82022-05-04 15:43:01 +02001467static int httpclient_parse_global_ca_file(char **args, int section_type, struct proxy *curpx,
1468 const struct proxy *defpx, const char *file, int line,
1469 char **err)
1470{
1471 if (too_many_args(1, args, err, NULL))
1472 return -1;
1473
1474 /* any configuration should set the hard_error flag */
1475 hard_error_ssl = 1;
1476
1477 free(httpclient_ssl_ca_file);
1478 httpclient_ssl_ca_file = strdup(args[1]);
1479
1480 return 0;
1481}
1482
William Lallemandeaa703e2022-04-22 17:52:33 +02001483static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1484 const struct proxy *defpx, const char *file, int line,
1485 char **err)
1486{
1487 if (too_many_args(1, args, err, NULL))
1488 return -1;
1489
William Lallemand6fce46a2022-05-04 14:53:41 +02001490 /* any configuration should set the hard_error flag */
1491 hard_error_ssl = 1;
1492
William Lallemandeaa703e2022-04-22 17:52:33 +02001493 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001494 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001495 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001496 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001497 else {
1498 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1499 return -1;
1500 }
1501
1502 return 0;
1503}
William Lallemand8a734cb2022-05-04 16:10:47 +02001504#endif /* ! USE_OPENSSL */
William Lallemandeaa703e2022-04-22 17:52:33 +02001505
1506static struct cfg_kw_list cfg_kws = {ILH, {
William Lallemand8a734cb2022-05-04 16:10:47 +02001507 { CFG_GLOBAL, "httpclient.resolvers.id", httpclient_parse_global_resolvers },
William Lallemand7c5a7ef2022-05-04 15:59:44 +02001508 { CFG_GLOBAL, "httpclient.resolvers.prefer", httpclient_parse_global_prefer },
William Lallemand8a734cb2022-05-04 16:10:47 +02001509#ifdef USE_OPENSSL
William Lallemand9ff95e22022-05-04 13:52:29 +02001510 { CFG_GLOBAL, "httpclient.ssl.verify", httpclient_parse_global_verify },
William Lallemand683fbb82022-05-04 15:43:01 +02001511 { CFG_GLOBAL, "httpclient.ssl.ca-file", httpclient_parse_global_ca_file },
William Lallemand8a734cb2022-05-04 16:10:47 +02001512#endif
William Lallemandeaa703e2022-04-22 17:52:33 +02001513 { 0, NULL, NULL },
1514}};
1515
1516INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);