blob: 0d34ca8c6dc6f3aa368c5ea43fecb8b5e36929e1 [file] [log] [blame]
William Lallemand83614a92021-08-13 14:47:57 +02001/*
2 * HTTP Client
3 *
4 * Copyright (C) 2021 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * This file implements an HTTP Client API.
12 *
13 */
William Lallemand83614a92021-08-13 14:47:57 +020014
William Lallemand2a8fe8b2021-08-20 14:25:15 +020015#include <haproxy/api.h>
William Lallemand33b0d092021-08-13 16:05:53 +020016#include <haproxy/applet.h>
17#include <haproxy/cli.h>
William Lallemandcf5cb0b2022-04-22 14:48:45 +020018#include <haproxy/ssl_ckch.h>
William Lallemand33b0d092021-08-13 16:05:53 +020019#include <haproxy/dynbuf.h>
William Lallemand83614a92021-08-13 14:47:57 +020020#include <haproxy/cfgparse.h>
Christopher Faulet908628c2022-03-25 16:43:49 +010021#include <haproxy/conn_stream.h>
22#include <haproxy/cs_utils.h>
William Lallemand83614a92021-08-13 14:47:57 +020023#include <haproxy/global.h>
William Lallemand0da616e2021-10-28 15:34:26 +020024#include <haproxy/istbuf.h>
William Lallemand33b0d092021-08-13 16:05:53 +020025#include <haproxy/h1_htx.h>
26#include <haproxy/http.h>
William Lallemand2b7dc4e2022-02-24 16:55:41 +010027#include <haproxy/http_ana-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020028#include <haproxy/http_client.h>
29#include <haproxy/http_htx.h>
30#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020031#include <haproxy/log.h>
32#include <haproxy/proxy.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020033#include <haproxy/server.h>
Willy Tarreau1df20422021-10-06 11:28:24 +020034#include <haproxy/ssl_sock-t.h>
William Lallemand83614a92021-08-13 14:47:57 +020035#include <haproxy/tools.h>
36
37#include <string.h>
38
39
40static struct proxy *httpclient_proxy;
41static struct server *httpclient_srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +020042#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +020043static struct server *httpclient_srv_ssl;
William Lallemandf1344b32022-04-26 12:00:06 +020044static int httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemand957ab132021-08-24 18:33:28 +020045#endif
William Lallemand33b0d092021-08-13 16:05:53 +020046static struct applet httpclient_applet;
47
William Lallemandeaa703e2022-04-22 17:52:33 +020048
William Lallemand03a4eb12021-08-18 16:46:21 +020049/* --- This part of the file implement an HTTP client over the CLI ---
50 * The functions will be starting by "hc_cli" for "httpclient cli"
51 */
52
William Lallemand03a4eb12021-08-18 16:46:21 +020053/* What kind of data we need to read */
54#define HC_CLI_F_RES_STLINE 0x01
55#define HC_CLI_F_RES_HDR 0x02
56#define HC_CLI_F_RES_BODY 0x04
57#define HC_CLI_F_RES_END 0x08
58
59
60/* These are the callback used by the HTTP Client when it needs to notify new
61 * data, we only sets a flag in the IO handler */
62
63void hc_cli_res_stline_cb(struct httpclient *hc)
64{
65 struct appctx *appctx = hc->caller;
66
William Lallemanddfc3f892021-08-20 11:35:29 +020067 if (!appctx)
68 return;
69
William Lallemand03a4eb12021-08-18 16:46:21 +020070 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020071 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020072}
73
74void hc_cli_res_headers_cb(struct httpclient *hc)
75{
76 struct appctx *appctx = hc->caller;
77
William Lallemanddfc3f892021-08-20 11:35:29 +020078 if (!appctx)
79 return;
80
William Lallemand03a4eb12021-08-18 16:46:21 +020081 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020082 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020083}
84
85void hc_cli_res_body_cb(struct httpclient *hc)
86{
87 struct appctx *appctx = hc->caller;
88
William Lallemanddfc3f892021-08-20 11:35:29 +020089 if (!appctx)
90 return;
91
William Lallemand03a4eb12021-08-18 16:46:21 +020092 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020093 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020094}
95
96void hc_cli_res_end_cb(struct httpclient *hc)
97{
98 struct appctx *appctx = hc->caller;
99
William Lallemanddfc3f892021-08-20 11:35:29 +0200100 if (!appctx)
101 return;
102
William Lallemand03a4eb12021-08-18 16:46:21 +0200103 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200104 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200105}
106
107/*
108 * Parse an httpclient keyword on the cli:
109 * httpclient <ID> <method> <URI>
110 */
111static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
112{
113 struct httpclient *hc;
114 char *err = NULL;
115 enum http_meth_t meth;
116 char *meth_str;
117 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200118 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200119
120 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
121 return 1;
122
123 if (!*args[1] || !*args[2]) {
124 memprintf(&err, ": not enough parameters");
125 goto err;
126 }
127
128 meth_str = args[1];
129 uri = ist(args[2]);
130
William Lallemanddec25c32021-10-25 19:48:37 +0200131 if (payload)
132 body = ist(payload);
133
William Lallemand03a4eb12021-08-18 16:46:21 +0200134 meth = find_http_meth(meth_str, strlen(meth_str));
135
136 hc = httpclient_new(appctx, meth, uri);
137 if (!hc) {
138 goto err;
139 }
140
141 /* update the httpclient callbacks */
142 hc->ops.res_stline = hc_cli_res_stline_cb;
143 hc->ops.res_headers = hc_cli_res_headers_cb;
144 hc->ops.res_payload = hc_cli_res_body_cb;
145 hc->ops.res_end = hc_cli_res_end_cb;
146
147 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
148 appctx->ctx.cli.i0 = 0;
149
William Lallemandbad9c8c2022-01-14 14:10:33 +0100150 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200151 goto err;
152
153
154 if (!httpclient_start(hc))
155 goto err;
156
157 return 0;
158
159err:
160 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
161 return cli_err(appctx, err);
162}
163
164/* This function dumps the content of the httpclient receive buffer
165 * on the CLI output
166 *
167 * Return 1 when the processing is finished
168 * return 0 if it needs to be called again
169 */
170static int hc_cli_io_handler(struct appctx *appctx)
171{
Christopher Faulet908628c2022-03-25 16:43:49 +0100172 struct conn_stream *cs = appctx->owner;
William Lallemand03a4eb12021-08-18 16:46:21 +0200173 struct buffer *trash = alloc_trash_chunk();
174 struct httpclient *hc = appctx->ctx.cli.p0;
175 struct http_hdr *hdrs, *hdr;
176
177 if (!trash)
178 goto out;
179 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100180 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
181 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
Christopher Faulet908628c2022-03-25 16:43:49 +0100182 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200183 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200184 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
185 goto out;
186 }
187
188 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
189 hdrs = hc->res.hdrs;
190 for (hdr = hdrs; isttest(hdr->v); hdr++) {
191 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
192 goto out;
193 }
194 if (!chunk_memcat(trash, "\r\n", 2))
195 goto out;
Christopher Faulet908628c2022-03-25 16:43:49 +0100196 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200197 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200198 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
199 goto out;
200 }
201
202 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
203 int ret;
204
Christopher Faulet908628c2022-03-25 16:43:49 +0100205 ret = httpclient_res_xfer(hc, cs_ib(cs));
206 channel_add_input(cs_ic(cs), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200207
William Lallemand518878e2021-09-21 10:45:34 +0200208 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200209 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
210 }
211 goto out;
212 }
213
214 /* we must close only if F_END is the last flag */
215 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
Christopher Fauletda098e62022-03-31 17:44:45 +0200216 cs_shutw(cs);
217 cs_shutr(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200218 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
219 goto out;
220 }
221
222out:
223 /* we didn't clear every flags, we should come back to finish things */
224 if (appctx->ctx.cli.i0)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200225 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200226
227 free_trash_chunk(trash);
228 return 0;
229}
230
231static void hc_cli_release(struct appctx *appctx)
232{
233 struct httpclient *hc = appctx->ctx.cli.p0;
234
235 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200236 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200237
238 return;
239}
240
241/* register cli keywords */
242static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100243 { { "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 +0200244 { { NULL }, NULL, NULL, NULL }
245}};
246
247INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
248
249
250/* --- This part of the file implements the actual HTTP client API --- */
251
William Lallemand33b0d092021-08-13 16:05:53 +0200252/*
253 * Generate a simple request and fill the httpclient request buffer with it.
254 * The request contains a request line generated from the absolute <url> and
255 * <meth> as well as list of headers <hdrs>.
256 *
257 * If the buffer was filled correctly the function returns 0, if not it returns
258 * an error_code but there is no guarantee that the buffer wasn't modified.
259 */
William Lallemanddec25c32021-10-25 19:48:37 +0200260int 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 +0200261{
262 struct htx_sl *sl;
263 struct htx *htx;
264 int err_code = 0;
265 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200266 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 +0100267 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100268 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200269
Christopher Faulet600985d2022-01-12 11:14:08 +0100270 if (!b_alloc(&hc->req.buf))
271 goto error;
272
William Lallemand33b0d092021-08-13 16:05:53 +0200273 if (meth >= HTTP_METH_OTHER)
274 goto error;
275
276 meth_ist = http_known_methods[meth];
277
278 vsn = ist("HTTP/1.1");
279
280 htx = htx_from_buf(&hc->req.buf);
281 if (!htx)
282 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100283
284 if (!hc->ops.req_payload && !isttest(payload))
285 flags |= HTX_SL_F_BODYLESS;
286
William Lallemand33b0d092021-08-13 16:05:53 +0200287 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
288 if (!sl) {
289 goto error;
290 }
291 sl->info.req.meth = meth;
292
William Lallemandf03b53c2021-11-24 15:38:17 +0100293 for (i = 0; hdrs && hdrs[i].n.len; i++) {
294 /* Don't check the value length because a header value may be empty */
295 if (isttest(hdrs[i].v) == 0)
296 continue;
297
298 if (isteqi(hdrs[i].n, ist("host")))
299 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100300 else if (isteqi(hdrs[i].n, ist("accept")))
301 foundaccept = 1;
302 else if (isteqi(hdrs[i].n, ist("user-agent")))
303 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100304
305 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
306 goto error;
307 }
William Lallemand33b0d092021-08-13 16:05:53 +0200308
William Lallemandf03b53c2021-11-24 15:38:17 +0100309 if (!foundhost) {
310 /* Add Host Header from URL */
311 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200312 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100313 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200314 goto error;
315 }
William Lallemand33b0d092021-08-13 16:05:53 +0200316
William Lallemandbad9c8c2022-01-14 14:10:33 +0100317 if (!foundaccept) {
318 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
319 goto error;
320 }
321
322 if (!foundua) {
323 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
324 goto error;
325 }
326
327
William Lallemandf03b53c2021-11-24 15:38:17 +0100328 if (!htx_add_endof(htx, HTX_BLK_EOH))
329 goto error;
330
William Lallemanddec25c32021-10-25 19:48:37 +0200331 if (isttest(payload)) {
332 /* add the payload if it can feat in the buffer, no need to set
333 * the Content-Length, the data will be sent chunked */
334 if (!htx_add_data_atonce(htx, payload))
335 goto error;
336 }
337
William Lallemand0da616e2021-10-28 15:34:26 +0200338 /* If req.payload was set, does not set the end of stream which *MUST*
339 * be set in the callback */
340 if (!hc->ops.req_payload)
341 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200342
343 htx_to_buf(htx, &hc->req.buf);
344
345 return 0;
346error:
347 err_code |= ERR_ALERT | ERR_ABORT;
348 return err_code;
349}
350
351/*
352 * transfer the response to the destination buffer and wakeup the HTTP client
353 * applet so it could fill again its buffer.
354 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500355 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200356 */
357int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
358{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100359 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200360 int ret;
361
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100362 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200363 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100364 if (!b_data(&hc->res.buf)) {
365 b_free(&hc->res.buf);
366 if (hc->appctx)
367 appctx_wakeup(hc->appctx);
368 }
William Lallemand33b0d092021-08-13 16:05:53 +0200369 return ret;
370}
371
372/*
William Lallemand0da616e2021-10-28 15:34:26 +0200373 * Transfer raw HTTP payload from src, and insert it into HTX format in the
374 * httpclient.
375 *
376 * Must be used to transfer the request body.
377 * Then wakeup the httpclient so it can transfer it.
378 *
379 * <end> tries to add the ending data flag if it succeed to copy all data.
380 *
381 * Return the number of bytes copied from src.
382 */
383int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
384{
385 int ret = 0;
386 struct htx *htx;
387
Christopher Faulet600985d2022-01-12 11:14:08 +0100388 if (!b_alloc(&hc->req.buf))
389 goto error;
390
William Lallemand0da616e2021-10-28 15:34:26 +0200391 htx = htx_from_buf(&hc->req.buf);
392 if (!htx)
393 goto error;
394
395 if (hc->appctx)
396 appctx_wakeup(hc->appctx);
397
398 ret += htx_add_data(htx, src);
399
400
401 /* if we copied all the data and the end flag is set */
402 if ((istlen(src) == ret) && end) {
403 htx->flags |= HTX_FL_EOM;
404 }
405 htx_to_buf(htx, &hc->req.buf);
406
407error:
408
409 return ret;
410}
411
William Lallemandb4a4ef62022-02-23 14:18:16 +0100412/* Set the 'timeout server' in ms for the next httpclient request */
413void httpclient_set_timeout(struct httpclient *hc, int timeout)
414{
415 hc->timeout_server = timeout;
416}
417
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100418/*
419 * Sets a destination for the httpclient from an HAProxy addr format
420 * This will prevent to determine the destination from the URL
421 * Return 0 in case of success or -1 otherwise.
422 */
423int httpclient_set_dst(struct httpclient *hc, const char *dst)
424{
425 struct sockaddr_storage *sk;
426 char *errmsg = NULL;
427
428 sockaddr_free(&hc->dst);
429 /* 'sk' is statically allocated (no need to be freed). */
430 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
431 &errmsg, NULL, NULL,
432 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
433 if (!sk) {
434 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
435 free(errmsg);
436 return -1;
437 }
438
439 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
440 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
441 return -1;
442 }
443
444 return 0;
445}
William Lallemand0da616e2021-10-28 15:34:26 +0200446
447/*
William Lallemand33b0d092021-08-13 16:05:53 +0200448 * Start the HTTP client
449 * Create the appctx, session, stream and wakeup the applet
450 *
451 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
452 * in the URL are supported, it lacks a resolver.
453 *
454 * Return the <appctx> or NULL if it failed
455 */
456struct appctx *httpclient_start(struct httpclient *hc)
457{
458 struct applet *applet = &httpclient_applet;
459 struct appctx *appctx;
460 struct session *sess;
Christopher Faulet13a35e52021-12-20 15:34:16 +0100461 struct conn_stream *cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200462 struct stream *s;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100463 struct sockaddr_storage *addr = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200464 int len;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100465 struct sockaddr_storage ss_url;
466 struct sockaddr_storage* ss_dst;
William Lallemand33b0d092021-08-13 16:05:53 +0200467 struct split_url out;
William Lallemand4006b0f2022-04-25 18:23:35 +0200468 enum obj_type *target = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200469
William Lallemand5085bc32022-02-17 12:52:09 +0100470 /* if the client was started and not ended, an applet is already
471 * running, we shouldn't try anything */
472 if (httpclient_started(hc) && !httpclient_ended(hc))
473 return NULL;
474
475 hc->flags = 0;
476
William Lallemand33b0d092021-08-13 16:05:53 +0200477 /* parse URI and fill sockaddr_storage */
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100478 len = url2sa(istptr(hc->req.url), istlen(hc->req.url), &ss_url, &out);
William Lallemand33b0d092021-08-13 16:05:53 +0200479 if (len == -1) {
William Lallemand614e6832021-09-26 18:12:43 +0200480 ha_alert("httpclient: cannot parse uri '%s'.\n", istptr(hc->req.url));
William Lallemand33b0d092021-08-13 16:05:53 +0200481 goto out;
482 }
483
484 /* The HTTP client will be created in the same thread as the caller,
485 * avoiding threading issues */
Christopher Faulet9ec2f4d2022-03-23 15:15:29 +0100486 appctx = appctx_new(applet, NULL);
William Lallemand33b0d092021-08-13 16:05:53 +0200487 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100488 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200489
490 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
491 if (!sess) {
492 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
493 goto out_free_appctx;
494 }
Christopher Faulet2479e5f2022-01-19 14:50:11 +0100495
William Lallemand4006b0f2022-04-25 18:23:35 +0200496 /* choose the SSL server or not */
497 switch (out.scheme) {
498 case SCH_HTTP:
499 target = &httpclient_srv_raw->obj_type;
500 break;
501 case SCH_HTTPS:
502#ifdef USE_OPENSSL
503 if (httpclient_srv_ssl) {
504 target = &httpclient_srv_ssl->obj_type;
505 } else {
506 ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
507 goto out_free_sess;
508 }
509#else
510 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
511 goto out_free_sess;
512#endif
513 break;
514 }
515
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100516 /* if httpclient_set_dst() was used, sets the alternative address */
517 if (hc->dst)
518 ss_dst = hc->dst;
519 else
520 ss_dst = &ss_url;
521
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100522 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*hc->dst)))
523 goto out_free_sess;
524
Christopher Faulet9ec2f4d2022-03-23 15:15:29 +0100525 cs = cs_new_from_applet(appctx->endp, sess, &hc->req.buf);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100526 if (!cs) {
527 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100528 goto out_free_addr;
William Lallemand33b0d092021-08-13 16:05:53 +0200529 }
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100530 s = DISGUISE(cs_strm(cs));
531
William Lallemand4006b0f2022-04-25 18:23:35 +0200532 s->target = target;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100533 /* set the "timeout server" */
534 s->req.wto = hc->timeout_server;
535 s->res.rto = hc->timeout_server;
William Lallemand33b0d092021-08-13 16:05:53 +0200536
Christopher Faulet8da67aa2022-03-29 17:53:09 +0200537 s->csb->dst = addr;
Christopher Faulet8abe7122022-03-30 15:10:18 +0200538 s->csb->flags |= CS_FL_NOLINGER;
Willy Tarreau03bd3952022-05-02 16:36:47 +0200539 s->flags |= SF_ASSIGNED;
William Lallemand33b0d092021-08-13 16:05:53 +0200540 s->res.flags |= CF_READ_DONTWAIT;
541
542 /* applet is waiting for data */
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200543 cs_cant_get(s->csf);
William Lallemand33b0d092021-08-13 16:05:53 +0200544 appctx_wakeup(appctx);
545
William Lallemand33b0d092021-08-13 16:05:53 +0200546 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200547 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200548 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100549
550 /* The request was transferred when the stream was created. So switch
551 * directly to REQ_BODY or RES_STLINE state
552 */
553 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200554
555 return appctx;
556
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100557out_free_addr:
558 sockaddr_free(&addr);
William Lallemand33b0d092021-08-13 16:05:53 +0200559out_free_sess:
560 session_free(sess);
561out_free_appctx:
562 appctx_free(appctx);
563out:
564
565 return NULL;
566}
567
William Lallemandecb83e12021-09-28 11:00:43 +0200568/*
569 * This function tries to destroy the httpclient if it wasn't running.
570 * If it was running, stop the client and ask it to autodestroy itself.
571 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500572 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200573 *
574 */
575void httpclient_stop_and_destroy(struct httpclient *hc)
576{
577
William Lallemandb8b13702021-09-28 12:15:37 +0200578 /* The httpclient was already stopped or never started, we can safely destroy it */
579 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200580 httpclient_destroy(hc);
581 } else {
582 /* if the client wasn't stopped, ask for a stop and destroy */
583 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
584 if (hc->appctx)
585 appctx_wakeup(hc->appctx);
586 }
587}
588
William Lallemand33b0d092021-08-13 16:05:53 +0200589/* Free the httpclient */
590void httpclient_destroy(struct httpclient *hc)
591{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200592 struct http_hdr *hdrs;
593
594
William Lallemand33b0d092021-08-13 16:05:53 +0200595 if (!hc)
596 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200597
William Lallemand2a879002021-10-05 15:50:45 +0200598 /* we should never destroy a client which was started but not stopped */
599 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200600
William Lallemand03f5a1c2021-09-27 15:17:47 +0200601 /* request */
602 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200603 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200604 /* response */
605 istfree(&hc->res.vsn);
606 istfree(&hc->res.reason);
607 hdrs = hc->res.hdrs;
608 while (hdrs && isttest(hdrs->n)) {
609 istfree(&hdrs->n);
610 istfree(&hdrs->v);
611 hdrs++;
612 }
613 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200614 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100615 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200616
William Lallemand33b0d092021-08-13 16:05:53 +0200617 free(hc);
618
619 return;
620}
621
622/* Allocate an httpclient and its buffers
623 * 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;
637
638 return hc;
639
640err:
641 httpclient_destroy(hc);
642 return NULL;
643}
644
645static void httpclient_applet_io_handler(struct appctx *appctx)
646{
647 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet908628c2022-03-25 16:43:49 +0100648 struct conn_stream *cs = appctx->owner;
649 struct stream *s = __cs_strm(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200650 struct channel *req = &s->req;
651 struct channel *res = &s->res;
652 struct htx_blk *blk = NULL;
653 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200654 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200655 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100656 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100657 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200658
William Lallemand33b0d092021-08-13 16:05:53 +0200659 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200660
661 /* required to stop */
662 if (hc->flags & HTTPCLIENT_FA_STOP)
663 goto end;
664
William Lallemand33b0d092021-08-13 16:05:53 +0200665 switch(appctx->st0) {
666
667 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100668 /* we know that the buffer is empty here, since
669 * it's the first call, we can freely copy the
670 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100671 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100672 if (!ret)
673 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100674
Christopher Faulet600985d2022-01-12 11:14:08 +0100675 if (!b_data(&hc->req.buf))
676 b_free(&hc->req.buf);
677
William Lallemanddb8a1f32021-11-08 16:55:14 +0100678 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100679 if (!htx)
680 goto more;
681
William Lallemanddb8a1f32021-11-08 16:55:14 +0100682 channel_add_input(req, htx->data);
683
William Lallemand933fe392021-11-04 09:45:58 +0100684 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
685 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
686 else
687 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
688
William Lallemand33b0d092021-08-13 16:05:53 +0200689 goto more; /* we need to leave the IO handler once we wrote the request */
690 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200691 case HTTPCLIENT_S_REQ_BODY:
692 /* call the payload callback */
693 {
694 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100695 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200696
William Lallemand0da616e2021-10-28 15:34:26 +0200697 /* call the request callback */
698 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100699
William Lallemandccc7ee42022-03-18 17:57:15 +0100700 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100701 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100702
William Lallemandccc7ee42022-03-18 17:57:15 +0100703 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100704 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100705
William Lallemandccc7ee42022-03-18 17:57:15 +0100706 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200707 size_t data = hc_htx->data;
708
William Lallemandccc7ee42022-03-18 17:57:15 +0100709 /* Here htx_to_buf() will set buffer data to 0 because
710 * the HTX is empty, and allow us to do an xfer.
711 */
712 htx_to_buf(hc_htx, &hc->req.buf);
713 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100714 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200715 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100716 } else {
717 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100718
Christopher Faulet6b4f1f62022-04-29 13:56:12 +0200719 ret = htx_xfer_blks(htx, hc_htx, htx_used_space(hc_htx), HTX_BLK_UNUSED);
William Lallemandccc7ee42022-03-18 17:57:15 +0100720 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100721
William Lallemandccc7ee42022-03-18 17:57:15 +0100722 /* we must copy the EOM if we empty the buffer */
723 if (htx_is_empty(hc_htx)) {
724 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
725 }
726 htx_to_buf(htx, &req->buf);
727 htx_to_buf(hc_htx, &hc->req.buf);
728 }
729
730
731 if (!b_data(&hc->req.buf))
732 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200733 }
734
William Lallemanddb8a1f32021-11-08 16:55:14 +0100735 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200736 if (!htx)
737 goto more;
738
739 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100740 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet908628c2022-03-25 16:43:49 +0100741 cs->endp->flags |= CS_EP_EOI;
Christopher Faulet3d433242022-03-03 15:38:39 +0100742 req->flags |= CF_EOI;
William Lallemand0da616e2021-10-28 15:34:26 +0200743 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100744 }
William Lallemand0da616e2021-10-28 15:34:26 +0200745
William Lallemand1eca8942022-03-17 14:57:23 +0100746 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200747 }
748 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200749
750 case HTTPCLIENT_S_RES_STLINE:
751 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100752 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200753 goto more;
754 htx = htxbuf(&res->buf);
755 if (!htx)
756 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100757 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200758 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
759 sl = htx_get_blk_ptr(htx, blk);
760 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
761 goto more;
762
763 /* copy the status line in the httpclient */
764 hc->res.status = sl->info.res.status;
765 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
766 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100767 sz = htx_get_blksz(blk);
Christopher Faulet0055d562022-04-29 14:09:03 +0200768 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100769 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200770 /* caller callback */
771 if (hc->ops.res_stline)
772 hc->ops.res_stline(hc);
773
774 /* if there is no HTX data anymore and the EOM flag is
775 * set, leave (no body) */
776 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
777 appctx->st0 = HTTPCLIENT_S_RES_END;
778 else
779 appctx->st0 = HTTPCLIENT_S_RES_HDR;
780 break;
781
782 case HTTPCLIENT_S_RES_HDR:
783 /* first copy the headers in a local hdrs
784 * structure, once we the total numbers of the
785 * header we allocate the right size and copy
786 * them. The htx block of the headers are
787 * removed each time one is read */
788 {
789 struct http_hdr hdrs[global.tune.max_http_hdr];
790
William Lallemanda625b032022-03-17 14:45:46 +0100791 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200792 goto more;
793 htx = htxbuf(&res->buf);
794 if (!htx)
795 goto more;
796
797 hdr_num = 0;
Christopher Faulet534645d2022-04-29 13:44:46 +0200798 blk = htx_get_head_blk(htx);
799 while (blk) {
William Lallemand33b0d092021-08-13 16:05:53 +0200800 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100801 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200802
Christopher Faulet534645d2022-04-29 13:44:46 +0200803 c_rew(res, sz);
804 blk = htx_remove_blk(htx, blk);
William Lallemandc020b252022-03-09 18:56:02 +0100805
Christopher Faulet534645d2022-04-29 13:44:46 +0200806 if (type == HTX_BLK_UNUSED)
807 continue;
808 else if (type == HTX_BLK_HDR) {
William Lallemandc020b252022-03-09 18:56:02 +0100809 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
810 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
William Lallemandc020b252022-03-09 18:56:02 +0100811 hdr_num++;
812 }
Christopher Faulet534645d2022-04-29 13:44:46 +0200813 else if (type == HTX_BLK_EOH) {
814 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200815 hdrs[hdr_num].n = IST_NULL;
816 hdrs[hdr_num].v = IST_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200817 break;
818 }
William Lallemand33b0d092021-08-13 16:05:53 +0200819 }
820
William Lallemand0d6f7792021-08-20 11:59:49 +0200821 if (hdr_num) {
822 /* alloc and copy the headers in the httpclient struct */
823 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
824 if (!hc->res.hdrs)
825 goto end;
826 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200827
William Lallemand0d6f7792021-08-20 11:59:49 +0200828 /* caller callback */
829 if (hc->ops.res_headers)
830 hc->ops.res_headers(hc);
831 }
William Lallemand33b0d092021-08-13 16:05:53 +0200832
833 /* if there is no HTX data anymore and the EOM flag is
834 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200835 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200836 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200837 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200838 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200839 }
William Lallemand33b0d092021-08-13 16:05:53 +0200840 }
841 break;
842
843 case HTTPCLIENT_S_RES_BODY:
844 /*
845 * The IO handler removes the htx blocks in the response buffer and
846 * push them in the hc->res.buf buffer in a raw format.
847 */
William Lallemanda625b032022-03-17 14:45:46 +0100848 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100849 goto more;
850
William Lallemand33b0d092021-08-13 16:05:53 +0200851 htx = htxbuf(&res->buf);
852 if (!htx || htx_is_empty(htx))
853 goto more;
854
Christopher Faulet600985d2022-01-12 11:14:08 +0100855 if (!b_alloc(&hc->res.buf))
856 goto more;
857
William Lallemand33b0d092021-08-13 16:05:53 +0200858 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100859 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200860
861 /* decapsule the htx data to raw data */
Christopher Faulet534645d2022-04-29 13:44:46 +0200862 blk = htx_get_head_blk(htx);
863 while (blk) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100864 enum htx_blk_type type = htx_get_blk_type(blk);
865 size_t count = co_data(res);
866 uint32_t blksz = htx_get_blksz(blk);
867 uint32_t room = b_room(&hc->res.buf);
868 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200869
William Lallemandc8f1eb92022-03-09 11:58:51 +0100870 /* we should try to copy the maximum output data in a block, which fit
871 * the destination buffer */
872 vlen = MIN(count, blksz);
873 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100874
William Lallemandc8f1eb92022-03-09 11:58:51 +0100875 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100876 goto process_data;
877
William Lallemand33b0d092021-08-13 16:05:53 +0200878 if (type == HTX_BLK_DATA) {
879 struct ist v = htx_get_blk_value(htx, blk);
880
William Lallemandc8f1eb92022-03-09 11:58:51 +0100881 __b_putblk(&hc->res.buf, v.ptr, vlen);
882 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200883
William Lallemandc8f1eb92022-03-09 11:58:51 +0100884 if (vlen == blksz)
Christopher Faulet534645d2022-04-29 13:44:46 +0200885 blk = htx_remove_blk(htx, blk);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100886 else
887 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100888
William Lallemand33b0d092021-08-13 16:05:53 +0200889 /* the data must be processed by the caller in the receive phase */
890 if (hc->ops.res_payload)
891 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100892
893 /* cannot copy everything, need to processs */
894 if (vlen != blksz)
895 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200896 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100897 if (vlen != blksz)
898 goto process_data;
899
William Lallemand33b0d092021-08-13 16:05:53 +0200900 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100901 c_rew(res, blksz);
Christopher Faulet534645d2022-04-29 13:44:46 +0200902 blk = htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200903 }
904 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100905
William Lallemand33b0d092021-08-13 16:05:53 +0200906 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100907 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200908 goto more;
909
William Lallemandc8f1eb92022-03-09 11:58:51 +0100910
William Lallemand33b0d092021-08-13 16:05:53 +0200911 /* end of message, we should quit */
912 appctx->st0 = HTTPCLIENT_S_RES_END;
913 break;
914
915 case HTTPCLIENT_S_RES_END:
916 goto end;
917 break;
918 }
919 }
920
921process_data:
922
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200923 cs_rx_chan_rdy(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200924
925 return;
926more:
927 /* There was not enough data in the response channel */
928
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200929 cs_rx_room_blk(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200930
931 if (appctx->st0 == HTTPCLIENT_S_RES_END)
932 goto end;
933
934 /* The state machine tries to handle as much data as possible, if there
935 * isn't any data to handle and a shutdown is detected, let's stop
936 * everything */
937 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100938 (res->flags & CF_SHUTW) ||
939 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200940 goto end;
941 }
942 return;
943
944end:
Christopher Fauletda098e62022-03-31 17:44:45 +0200945 cs_shutw(cs);
946 cs_shutr(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200947 return;
948}
949
950static void httpclient_applet_release(struct appctx *appctx)
951{
952 struct httpclient *hc = appctx->ctx.httpclient.ptr;
953
William Lallemand1123dde2021-09-21 10:58:10 +0200954 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200955 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200956 /* the applet is leaving, remove the ptr so we don't try to call it
957 * again from the caller */
958 hc->appctx = NULL;
959
William Lallemandeb0d4c42022-04-06 14:12:37 +0200960 if (hc->ops.res_end)
961 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +0200962
963 /* destroy the httpclient when set to autotokill */
964 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
965 httpclient_destroy(hc);
966 }
967
William Lallemand33b0d092021-08-13 16:05:53 +0200968 return;
969}
970
971/* HTTP client applet */
972static struct applet httpclient_applet = {
973 .obj_type = OBJ_TYPE_APPLET,
974 .name = "<HTTPCLIENT>",
975 .fct = httpclient_applet_io_handler,
976 .release = httpclient_applet_release,
977};
978
William Lallemand83614a92021-08-13 14:47:57 +0200979/*
980 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
981 * the other for HTTPS.
982 */
William Lallemand2c8b0842022-04-22 15:16:09 +0200983static int httpclient_precheck()
William Lallemand83614a92021-08-13 14:47:57 +0200984{
985 int err_code = 0;
986 char *errmsg = NULL;
987
William Lallemandc6ceba32022-04-22 16:49:53 +0200988 if (global.mode & MODE_MWORKER_WAIT)
989 return 0;
990
William Lallemand83614a92021-08-13 14:47:57 +0200991 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
992 if (!httpclient_proxy) {
993 err_code |= ERR_ALERT | ERR_FATAL;
994 goto err;
995 }
996
Willy Tarreau0e72e402021-08-20 10:23:12 +0200997 proxy_preset_defaults(httpclient_proxy);
998
William Lallemandccc7ee42022-03-18 17:57:15 +0100999 httpclient_proxy->options |= PR_O_WREQ_BODY;
William Lallemand71abad02022-03-17 15:24:28 +01001000 httpclient_proxy->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
William Lallemand83614a92021-08-13 14:47:57 +02001001 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
1002 httpclient_proxy->mode = PR_MODE_HTTP;
1003 httpclient_proxy->maxconn = 0;
1004 httpclient_proxy->accept = NULL;
William Lallemand71abad02022-03-17 15:24:28 +01001005 httpclient_proxy->conn_retries = CONN_RETRIES;
William Lallemand83614a92021-08-13 14:47:57 +02001006 httpclient_proxy->timeout.client = TICK_ETERNITY;
1007 /* The HTTP Client use the "option httplog" with the global log server */
1008 httpclient_proxy->conf.logformat_string = default_http_log_format;
1009 httpclient_proxy->http_needed = 1;
1010
1011 /* clear HTTP server */
1012 httpclient_srv_raw = new_server(httpclient_proxy);
1013 if (!httpclient_srv_raw) {
1014 err_code |= ERR_ALERT | ERR_FATAL;
1015 memprintf(&errmsg, "out of memory.");
1016 goto err;
1017 }
1018
1019 httpclient_srv_raw->iweight = 0;
1020 httpclient_srv_raw->uweight = 0;
1021 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
1022 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
1023 if (!httpclient_srv_raw->id)
1024 goto err;
1025
William Lallemand957ab132021-08-24 18:33:28 +02001026#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001027 /* SSL HTTP server */
1028 httpclient_srv_ssl = new_server(httpclient_proxy);
1029 if (!httpclient_srv_ssl) {
1030 memprintf(&errmsg, "out of memory.");
1031 err_code |= ERR_ALERT | ERR_FATAL;
1032 goto err;
1033 }
1034 httpclient_srv_ssl->iweight = 0;
1035 httpclient_srv_ssl->uweight = 0;
1036 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
1037 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +02001038 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +02001039 if (!httpclient_srv_ssl->id)
1040 goto err;
1041
William Lallemandeaa703e2022-04-22 17:52:33 +02001042 httpclient_srv_ssl->ssl_ctx.verify = httpclient_ssl_verify;
William Lallemand4006b0f2022-04-25 18:23:35 +02001043 /* if the verify is required, try to load the system CA */
William Lallemandeaa703e2022-04-22 17:52:33 +02001044 if (httpclient_ssl_verify == SSL_SOCK_VERIFY_REQUIRED) {
1045 httpclient_srv_ssl->ssl_ctx.ca_file = strdup("@system-ca");
William Lallemand4006b0f2022-04-25 18:23:35 +02001046 if (!ssl_store_load_locations_file(httpclient_srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT)) {
1047 ha_warning("httpclient: cannot initialize SSL verify with 'ca-file \"%s\"'. Disabling SSL.\n", httpclient_srv_ssl->ssl_ctx.ca_file);
1048 ha_free(&httpclient_srv_ssl->ssl_ctx.ca_file);
1049 srv_drop(httpclient_srv_ssl);
1050 httpclient_srv_ssl = NULL;
1051 }
William Lallemandeaa703e2022-04-22 17:52:33 +02001052 }
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001053
William Lallemand957ab132021-08-24 18:33:28 +02001054#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001055
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001056 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +02001057 httpclient_proxy->next = proxies_list;
1058 proxies_list = httpclient_proxy;
1059
William Lallemand211c9672021-08-24 17:18:13 +02001060 /* link the 2 servers in the proxy */
1061 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +02001062 httpclient_proxy->srv = httpclient_srv_raw;
1063
1064#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001065 if (httpclient_srv_ssl) {
1066 httpclient_srv_ssl->next = httpclient_proxy->srv;
1067 httpclient_proxy->srv = httpclient_srv_ssl;
1068 }
William Lallemand957ab132021-08-24 18:33:28 +02001069#endif
1070
William Lallemand211c9672021-08-24 17:18:13 +02001071
William Lallemand83614a92021-08-13 14:47:57 +02001072 return 0;
1073
1074err:
1075 ha_alert("httpclient: cannot initialize.\n");
1076 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001077 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001078#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001079 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001080#endif
William Lallemand83614a92021-08-13 14:47:57 +02001081 free_proxy(httpclient_proxy);
1082 return err_code;
1083}
1084
William Lallemand2c8b0842022-04-22 15:16:09 +02001085static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001086{
1087 struct logsrv *logsrv;
1088 struct proxy *curproxy = httpclient_proxy;
William Lallemand71e31582022-03-16 15:47:47 +01001089 char *errmsg = NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001090
William Lallemandc6ceba32022-04-22 16:49:53 +02001091 if (global.mode & MODE_MWORKER_WAIT)
1092 return 0;
1093
William Lallemand83614a92021-08-13 14:47:57 +02001094 /* copy logs from "global" log list */
1095 list_for_each_entry(logsrv, &global.logsrvs, list) {
1096 struct logsrv *node = malloc(sizeof(*node));
1097
1098 if (!node) {
1099 ha_alert("httpclient: cannot allocate memory.\n");
1100 goto err;
1101 }
1102
1103 memcpy(node, logsrv, sizeof(*node));
1104 LIST_INIT(&node->list);
1105 LIST_APPEND(&curproxy->logsrvs, &node->list);
Willy Tarreaue1e9f6b2022-04-21 14:14:28 +02001106 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1107 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001108 }
1109 if (curproxy->conf.logformat_string) {
William Lallemand83614a92021-08-13 14:47:57 +02001110 curproxy->conf.args.ctx = ARGC_LOG;
1111 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1112 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
William Lallemand715c1012022-03-16 16:39:23 +01001113 SMP_VAL_FE_LOG_END, &errmsg)) {
1114 ha_alert("httpclient: failed to parse log-format : %s.\n", errmsg);
1115 free(errmsg);
William Lallemand83614a92021-08-13 14:47:57 +02001116 goto err;
1117 }
1118 curproxy->conf.args.file = NULL;
1119 curproxy->conf.args.line = 0;
1120 }
William Lallemand71e31582022-03-16 15:47:47 +01001121
1122#ifdef USE_OPENSSL
William Lallemand4006b0f2022-04-25 18:23:35 +02001123 if (httpclient_srv_ssl) {
William Lallemand715c1012022-03-16 16:39:23 +01001124 int err_code = 0;
1125
1126 /* init the SNI expression */
1127 /* always use the host header as SNI, without the port */
1128 httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1129 err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
1130 if (err_code & ERR_CODE) {
1131 ha_alert("httpclient: failed to configure sni: %s.\n", errmsg);
1132 free(errmsg);
1133 goto err;
1134 }
William Lallemand71e31582022-03-16 15:47:47 +01001135 }
1136#endif
1137
William Lallemand83614a92021-08-13 14:47:57 +02001138 return 0;
1139err:
1140 return 1;
1141}
1142
William Lallemand83614a92021-08-13 14:47:57 +02001143/* initialize the proxy and servers for the HTTP client */
1144
William Lallemand2c8b0842022-04-22 15:16:09 +02001145REGISTER_PRE_CHECK(httpclient_precheck);
1146REGISTER_POST_CHECK(httpclient_postcheck);
William Lallemandeaa703e2022-04-22 17:52:33 +02001147
1148#ifdef USE_OPENSSL
1149static int httpclient_parse_global_verify(char **args, int section_type, struct proxy *curpx,
1150 const struct proxy *defpx, const char *file, int line,
1151 char **err)
1152{
1153 if (too_many_args(1, args, err, NULL))
1154 return -1;
1155
1156 if (strcmp(args[1],"none") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001157 httpclient_ssl_verify = SSL_SOCK_VERIFY_NONE;
William Lallemandeaa703e2022-04-22 17:52:33 +02001158 else if (strcmp(args[1],"required") == 0)
William Lallemand04994de2022-04-28 19:35:21 +02001159 httpclient_ssl_verify = SSL_SOCK_VERIFY_REQUIRED;
William Lallemandeaa703e2022-04-22 17:52:33 +02001160 else {
1161 ha_alert("parsing [%s:%d] : '%s' expects 'none' or 'required' as argument.\n", file, line, args[0]);
1162 return -1;
1163 }
1164
1165 return 0;
1166}
1167
1168static struct cfg_kw_list cfg_kws = {ILH, {
1169 { CFG_GLOBAL, "httpclient-ssl-verify", httpclient_parse_global_verify },
1170 { 0, NULL, NULL },
1171}};
1172
1173INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1174#endif