blob: 55701e5eabd7ecb458b2fbbb002efe3be0508e7c [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>
18#include <haproxy/dynbuf.h>
William Lallemand83614a92021-08-13 14:47:57 +020019#include <haproxy/cfgparse.h>
20#include <haproxy/connection.h>
21#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>
28#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020029#include <haproxy/log.h>
30#include <haproxy/proxy.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020031#include <haproxy/server.h>
Willy Tarreau1df20422021-10-06 11:28:24 +020032#include <haproxy/ssl_sock-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020033#include <haproxy/stream_interface.h>
William Lallemand83614a92021-08-13 14:47:57 +020034#include <haproxy/tools.h>
35
36#include <string.h>
37
38
39static struct proxy *httpclient_proxy;
40static struct server *httpclient_srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +020041#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +020042static struct server *httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +020043#endif
William Lallemand33b0d092021-08-13 16:05:53 +020044static struct applet httpclient_applet;
45
William Lallemand03a4eb12021-08-18 16:46:21 +020046/* --- This part of the file implement an HTTP client over the CLI ---
47 * The functions will be starting by "hc_cli" for "httpclient cli"
48 */
49
William Lallemand03a4eb12021-08-18 16:46:21 +020050/* What kind of data we need to read */
51#define HC_CLI_F_RES_STLINE 0x01
52#define HC_CLI_F_RES_HDR 0x02
53#define HC_CLI_F_RES_BODY 0x04
54#define HC_CLI_F_RES_END 0x08
55
56
57/* These are the callback used by the HTTP Client when it needs to notify new
58 * data, we only sets a flag in the IO handler */
59
60void hc_cli_res_stline_cb(struct httpclient *hc)
61{
62 struct appctx *appctx = hc->caller;
63
William Lallemanddfc3f892021-08-20 11:35:29 +020064 if (!appctx)
65 return;
66
William Lallemand03a4eb12021-08-18 16:46:21 +020067 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020068 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020069}
70
71void hc_cli_res_headers_cb(struct httpclient *hc)
72{
73 struct appctx *appctx = hc->caller;
74
William Lallemanddfc3f892021-08-20 11:35:29 +020075 if (!appctx)
76 return;
77
William Lallemand03a4eb12021-08-18 16:46:21 +020078 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020079 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020080}
81
82void hc_cli_res_body_cb(struct httpclient *hc)
83{
84 struct appctx *appctx = hc->caller;
85
William Lallemanddfc3f892021-08-20 11:35:29 +020086 if (!appctx)
87 return;
88
William Lallemand03a4eb12021-08-18 16:46:21 +020089 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020090 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020091}
92
93void hc_cli_res_end_cb(struct httpclient *hc)
94{
95 struct appctx *appctx = hc->caller;
96
William Lallemanddfc3f892021-08-20 11:35:29 +020097 if (!appctx)
98 return;
99
William Lallemand03a4eb12021-08-18 16:46:21 +0200100 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200101 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200102}
103
104/*
105 * Parse an httpclient keyword on the cli:
106 * httpclient <ID> <method> <URI>
107 */
108static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
109{
110 struct httpclient *hc;
111 char *err = NULL;
112 enum http_meth_t meth;
113 char *meth_str;
114 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200115 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200116
117 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
118 return 1;
119
120 if (!*args[1] || !*args[2]) {
121 memprintf(&err, ": not enough parameters");
122 goto err;
123 }
124
125 meth_str = args[1];
126 uri = ist(args[2]);
127
William Lallemanddec25c32021-10-25 19:48:37 +0200128 if (payload)
129 body = ist(payload);
130
William Lallemand03a4eb12021-08-18 16:46:21 +0200131 meth = find_http_meth(meth_str, strlen(meth_str));
132
133 hc = httpclient_new(appctx, meth, uri);
134 if (!hc) {
135 goto err;
136 }
137
138 /* update the httpclient callbacks */
139 hc->ops.res_stline = hc_cli_res_stline_cb;
140 hc->ops.res_headers = hc_cli_res_headers_cb;
141 hc->ops.res_payload = hc_cli_res_body_cb;
142 hc->ops.res_end = hc_cli_res_end_cb;
143
144 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
145 appctx->ctx.cli.i0 = 0;
146
William Lallemandbad9c8c2022-01-14 14:10:33 +0100147 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200148 goto err;
149
150
151 if (!httpclient_start(hc))
152 goto err;
153
154 return 0;
155
156err:
157 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
158 return cli_err(appctx, err);
159}
160
161/* This function dumps the content of the httpclient receive buffer
162 * on the CLI output
163 *
164 * Return 1 when the processing is finished
165 * return 0 if it needs to be called again
166 */
167static int hc_cli_io_handler(struct appctx *appctx)
168{
Christopher Faulet86e1c332021-12-20 17:09:39 +0100169 struct stream_interface *si = cs_si(appctx->owner);
William Lallemand03a4eb12021-08-18 16:46:21 +0200170 struct buffer *trash = alloc_trash_chunk();
171 struct httpclient *hc = appctx->ctx.cli.p0;
172 struct http_hdr *hdrs, *hdr;
173
174 if (!trash)
175 goto out;
176 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100177 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
178 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
William Lallemand03a4eb12021-08-18 16:46:21 +0200179 if (ci_putchk(si_ic(si), trash) == -1)
180 si_rx_room_blk(si);
181 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
182 goto out;
183 }
184
185 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
186 hdrs = hc->res.hdrs;
187 for (hdr = hdrs; isttest(hdr->v); hdr++) {
188 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
189 goto out;
190 }
191 if (!chunk_memcat(trash, "\r\n", 2))
192 goto out;
193 if (ci_putchk(si_ic(si), trash) == -1)
194 si_rx_room_blk(si);
195 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
196 goto out;
197 }
198
199 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
200 int ret;
201
202 ret = httpclient_res_xfer(hc, &si_ic(si)->buf);
203 channel_add_input(si_ic(si), ret); /* forward what we put in the buffer channel */
204
William Lallemand518878e2021-09-21 10:45:34 +0200205 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200206 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
207 }
208 goto out;
209 }
210
211 /* we must close only if F_END is the last flag */
212 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
213 si_shutw(si);
214 si_shutr(si);
215 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
216 goto out;
217 }
218
219out:
220 /* we didn't clear every flags, we should come back to finish things */
221 if (appctx->ctx.cli.i0)
222 si_rx_room_blk(si);
223
224 free_trash_chunk(trash);
225 return 0;
226}
227
228static void hc_cli_release(struct appctx *appctx)
229{
230 struct httpclient *hc = appctx->ctx.cli.p0;
231
232 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200233 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200234
235 return;
236}
237
238/* register cli keywords */
239static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100240 { { "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 +0200241 { { NULL }, NULL, NULL, NULL }
242}};
243
244INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
245
246
247/* --- This part of the file implements the actual HTTP client API --- */
248
William Lallemand33b0d092021-08-13 16:05:53 +0200249/*
250 * Generate a simple request and fill the httpclient request buffer with it.
251 * The request contains a request line generated from the absolute <url> and
252 * <meth> as well as list of headers <hdrs>.
253 *
254 * If the buffer was filled correctly the function returns 0, if not it returns
255 * an error_code but there is no guarantee that the buffer wasn't modified.
256 */
William Lallemanddec25c32021-10-25 19:48:37 +0200257int 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 +0200258{
259 struct htx_sl *sl;
260 struct htx *htx;
261 int err_code = 0;
262 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200263 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 +0100264 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100265 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200266
Christopher Faulet600985d2022-01-12 11:14:08 +0100267 if (!b_alloc(&hc->req.buf))
268 goto error;
269
William Lallemand33b0d092021-08-13 16:05:53 +0200270 if (meth >= HTTP_METH_OTHER)
271 goto error;
272
273 meth_ist = http_known_methods[meth];
274
275 vsn = ist("HTTP/1.1");
276
277 htx = htx_from_buf(&hc->req.buf);
278 if (!htx)
279 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100280
281 if (!hc->ops.req_payload && !isttest(payload))
282 flags |= HTX_SL_F_BODYLESS;
283
William Lallemand33b0d092021-08-13 16:05:53 +0200284 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
285 if (!sl) {
286 goto error;
287 }
288 sl->info.req.meth = meth;
289
William Lallemandf03b53c2021-11-24 15:38:17 +0100290 for (i = 0; hdrs && hdrs[i].n.len; i++) {
291 /* Don't check the value length because a header value may be empty */
292 if (isttest(hdrs[i].v) == 0)
293 continue;
294
295 if (isteqi(hdrs[i].n, ist("host")))
296 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100297 else if (isteqi(hdrs[i].n, ist("accept")))
298 foundaccept = 1;
299 else if (isteqi(hdrs[i].n, ist("user-agent")))
300 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100301
302 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
303 goto error;
304 }
William Lallemand33b0d092021-08-13 16:05:53 +0200305
William Lallemandf03b53c2021-11-24 15:38:17 +0100306 if (!foundhost) {
307 /* Add Host Header from URL */
308 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200309 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100310 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200311 goto error;
312 }
William Lallemand33b0d092021-08-13 16:05:53 +0200313
William Lallemandbad9c8c2022-01-14 14:10:33 +0100314 if (!foundaccept) {
315 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
316 goto error;
317 }
318
319 if (!foundua) {
320 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
321 goto error;
322 }
323
324
William Lallemandf03b53c2021-11-24 15:38:17 +0100325 if (!htx_add_endof(htx, HTX_BLK_EOH))
326 goto error;
327
William Lallemanddec25c32021-10-25 19:48:37 +0200328 if (isttest(payload)) {
329 /* add the payload if it can feat in the buffer, no need to set
330 * the Content-Length, the data will be sent chunked */
331 if (!htx_add_data_atonce(htx, payload))
332 goto error;
333 }
334
William Lallemand0da616e2021-10-28 15:34:26 +0200335 /* If req.payload was set, does not set the end of stream which *MUST*
336 * be set in the callback */
337 if (!hc->ops.req_payload)
338 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200339
340 htx_to_buf(htx, &hc->req.buf);
341
342 return 0;
343error:
344 err_code |= ERR_ALERT | ERR_ABORT;
345 return err_code;
346}
347
348/*
349 * transfer the response to the destination buffer and wakeup the HTTP client
350 * applet so it could fill again its buffer.
351 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500352 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200353 */
354int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
355{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100356 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200357 int ret;
358
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100359 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200360 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100361 if (!b_data(&hc->res.buf)) {
362 b_free(&hc->res.buf);
363 if (hc->appctx)
364 appctx_wakeup(hc->appctx);
365 }
William Lallemand33b0d092021-08-13 16:05:53 +0200366 return ret;
367}
368
369/*
William Lallemand0da616e2021-10-28 15:34:26 +0200370 * Transfer raw HTTP payload from src, and insert it into HTX format in the
371 * httpclient.
372 *
373 * Must be used to transfer the request body.
374 * Then wakeup the httpclient so it can transfer it.
375 *
376 * <end> tries to add the ending data flag if it succeed to copy all data.
377 *
378 * Return the number of bytes copied from src.
379 */
380int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
381{
382 int ret = 0;
383 struct htx *htx;
384
Christopher Faulet600985d2022-01-12 11:14:08 +0100385 if (!b_alloc(&hc->req.buf))
386 goto error;
387
William Lallemand0da616e2021-10-28 15:34:26 +0200388 htx = htx_from_buf(&hc->req.buf);
389 if (!htx)
390 goto error;
391
392 if (hc->appctx)
393 appctx_wakeup(hc->appctx);
394
395 ret += htx_add_data(htx, src);
396
397
398 /* if we copied all the data and the end flag is set */
399 if ((istlen(src) == ret) && end) {
400 htx->flags |= HTX_FL_EOM;
401 }
402 htx_to_buf(htx, &hc->req.buf);
403
404error:
405
406 return ret;
407}
408
William Lallemandb4a4ef62022-02-23 14:18:16 +0100409/* Set the 'timeout server' in ms for the next httpclient request */
410void httpclient_set_timeout(struct httpclient *hc, int timeout)
411{
412 hc->timeout_server = timeout;
413}
414
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100415/*
416 * Sets a destination for the httpclient from an HAProxy addr format
417 * This will prevent to determine the destination from the URL
418 * Return 0 in case of success or -1 otherwise.
419 */
420int httpclient_set_dst(struct httpclient *hc, const char *dst)
421{
422 struct sockaddr_storage *sk;
423 char *errmsg = NULL;
424
425 sockaddr_free(&hc->dst);
426 /* 'sk' is statically allocated (no need to be freed). */
427 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
428 &errmsg, NULL, NULL,
429 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
430 if (!sk) {
431 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
432 free(errmsg);
433 return -1;
434 }
435
436 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
437 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
438 return -1;
439 }
440
441 return 0;
442}
William Lallemand0da616e2021-10-28 15:34:26 +0200443
444/*
William Lallemand33b0d092021-08-13 16:05:53 +0200445 * Start the HTTP client
446 * Create the appctx, session, stream and wakeup the applet
447 *
448 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
449 * in the URL are supported, it lacks a resolver.
450 *
451 * Return the <appctx> or NULL if it failed
452 */
453struct appctx *httpclient_start(struct httpclient *hc)
454{
455 struct applet *applet = &httpclient_applet;
456 struct appctx *appctx;
457 struct session *sess;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100458 struct cs_endpoint *endp;
Christopher Faulet13a35e52021-12-20 15:34:16 +0100459 struct conn_stream *cs;
William Lallemand33b0d092021-08-13 16:05:53 +0200460 struct stream *s;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100461 struct sockaddr_storage *addr = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200462 int len;
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100463 struct sockaddr_storage ss_url;
464 struct sockaddr_storage* ss_dst;
William Lallemand33b0d092021-08-13 16:05:53 +0200465 struct split_url out;
466
William Lallemand5085bc32022-02-17 12:52:09 +0100467 /* if the client was started and not ended, an applet is already
468 * running, we shouldn't try anything */
469 if (httpclient_started(hc) && !httpclient_ended(hc))
470 return NULL;
471
472 hc->flags = 0;
473
William Lallemand33b0d092021-08-13 16:05:53 +0200474 /* parse URI and fill sockaddr_storage */
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100475 len = url2sa(istptr(hc->req.url), istlen(hc->req.url), &ss_url, &out);
William Lallemand33b0d092021-08-13 16:05:53 +0200476 if (len == -1) {
William Lallemand614e6832021-09-26 18:12:43 +0200477 ha_alert("httpclient: cannot parse uri '%s'.\n", istptr(hc->req.url));
William Lallemand33b0d092021-08-13 16:05:53 +0200478 goto out;
479 }
480
481 /* The HTTP client will be created in the same thread as the caller,
482 * avoiding threading issues */
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100483 appctx = appctx_new(applet);
William Lallemand33b0d092021-08-13 16:05:53 +0200484 if (!appctx)
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100485 goto out;
William Lallemand33b0d092021-08-13 16:05:53 +0200486
487 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
488 if (!sess) {
489 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
490 goto out_free_appctx;
491 }
Christopher Faulet2479e5f2022-01-19 14:50:11 +0100492
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100493 /* if httpclient_set_dst() was used, sets the alternative address */
494 if (hc->dst)
495 ss_dst = hc->dst;
496 else
497 ss_dst = &ss_url;
498
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100499 if (!sockaddr_alloc(&addr, ss_dst, sizeof(*hc->dst)))
500 goto out_free_sess;
501
502 endp = cs_endpoint_new();
503 if (!endp)
504 goto out_free_addr;
505 endp->target = appctx;
506 endp->ctx = appctx;
507 endp->flags |= CS_EP_T_APPLET;
508
509 cs = cs_new_from_applet(endp, sess, &hc->req.buf);
510 if (!cs) {
511 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
512 cs_endpoint_free(endp);
513 goto out_free_addr;
William Lallemand33b0d092021-08-13 16:05:53 +0200514 }
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100515 s = DISGUISE(cs_strm(cs));
516
517 /* set the "timeout server" */
518 s->req.wto = hc->timeout_server;
519 s->res.rto = hc->timeout_server;
William Lallemand33b0d092021-08-13 16:05:53 +0200520
521 /* choose the SSL server or not */
522 switch (out.scheme) {
523 case SCH_HTTP:
524 s->target = &httpclient_srv_raw->obj_type;
525 break;
526 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200527#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200528 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200529#else
530 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
531 goto out_free_stream;
532#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200533 break;
534 }
535
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100536 cs_si(s->csb)->dst = addr;
Christopher Fauletb91afea2021-12-23 13:58:12 +0100537 cs_si(s->csb)->flags |= SI_FL_NOLINGER;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100538 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
William Lallemand33b0d092021-08-13 16:05:53 +0200539 s->res.flags |= CF_READ_DONTWAIT;
540
541 /* applet is waiting for data */
Christopher Fauletb91afea2021-12-23 13:58:12 +0100542 si_cant_get(cs_si(s->csf));
William Lallemand33b0d092021-08-13 16:05:53 +0200543 appctx_wakeup(appctx);
544
William Lallemand33b0d092021-08-13 16:05:53 +0200545 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200546 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200547 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100548
549 /* The request was transferred when the stream was created. So switch
550 * directly to REQ_BODY or RES_STLINE state
551 */
552 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200553
554 return appctx;
555
556out_free_stream:
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100557 cs_detach_app(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200558 LIST_DELETE(&s->list);
559 pool_free(pool_head_stream, s);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100560 cs_free(cs);
561out_free_addr:
562 sockaddr_free(&addr);
William Lallemand33b0d092021-08-13 16:05:53 +0200563out_free_sess:
564 session_free(sess);
565out_free_appctx:
566 appctx_free(appctx);
567out:
568
569 return NULL;
570}
571
William Lallemandecb83e12021-09-28 11:00:43 +0200572/*
573 * This function tries to destroy the httpclient if it wasn't running.
574 * If it was running, stop the client and ask it to autodestroy itself.
575 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500576 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200577 *
578 */
579void httpclient_stop_and_destroy(struct httpclient *hc)
580{
581
William Lallemandb8b13702021-09-28 12:15:37 +0200582 /* The httpclient was already stopped or never started, we can safely destroy it */
583 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200584 httpclient_destroy(hc);
585 } else {
586 /* if the client wasn't stopped, ask for a stop and destroy */
587 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
588 if (hc->appctx)
589 appctx_wakeup(hc->appctx);
590 }
591}
592
William Lallemand33b0d092021-08-13 16:05:53 +0200593/* Free the httpclient */
594void httpclient_destroy(struct httpclient *hc)
595{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200596 struct http_hdr *hdrs;
597
598
William Lallemand33b0d092021-08-13 16:05:53 +0200599 if (!hc)
600 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200601
William Lallemand2a879002021-10-05 15:50:45 +0200602 /* we should never destroy a client which was started but not stopped */
603 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200604
William Lallemand03f5a1c2021-09-27 15:17:47 +0200605 /* request */
606 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200607 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200608 /* response */
609 istfree(&hc->res.vsn);
610 istfree(&hc->res.reason);
611 hdrs = hc->res.hdrs;
612 while (hdrs && isttest(hdrs->n)) {
613 istfree(&hdrs->n);
614 istfree(&hdrs->v);
615 hdrs++;
616 }
617 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200618 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100619 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200620
William Lallemand33b0d092021-08-13 16:05:53 +0200621 free(hc);
622
623 return;
624}
625
626/* Allocate an httpclient and its buffers
627 * Return NULL on failure */
628struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
629{
630 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200631
632 hc = calloc(1, sizeof(*hc));
633 if (!hc)
634 goto err;
635
Christopher Faulet600985d2022-01-12 11:14:08 +0100636 hc->req.buf = BUF_NULL;
637 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200638 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100639 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200640 hc->req.meth = meth;
641
642 return hc;
643
644err:
645 httpclient_destroy(hc);
646 return NULL;
647}
648
649static void httpclient_applet_io_handler(struct appctx *appctx)
650{
651 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet86e1c332021-12-20 17:09:39 +0100652 struct stream_interface *si = cs_si(appctx->owner);
William Lallemand33b0d092021-08-13 16:05:53 +0200653 struct stream *s = si_strm(si);
654 struct channel *req = &s->req;
655 struct channel *res = &s->res;
656 struct htx_blk *blk = NULL;
657 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200658 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200659 int32_t pos;
660 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100661 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100662 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200663
664
665 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200666
667 /* required to stop */
668 if (hc->flags & HTTPCLIENT_FA_STOP)
669 goto end;
670
William Lallemand33b0d092021-08-13 16:05:53 +0200671 switch(appctx->st0) {
672
673 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100674 /* we know that the buffer is empty here, since
675 * it's the first call, we can freely copy the
676 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100677 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100678 if (!ret)
679 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100680
Christopher Faulet600985d2022-01-12 11:14:08 +0100681 if (!b_data(&hc->req.buf))
682 b_free(&hc->req.buf);
683
William Lallemanddb8a1f32021-11-08 16:55:14 +0100684 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100685 if (!htx)
686 goto more;
687
William Lallemanddb8a1f32021-11-08 16:55:14 +0100688 channel_add_input(req, htx->data);
689
William Lallemand933fe392021-11-04 09:45:58 +0100690 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
691 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
692 else
693 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
694
William Lallemand33b0d092021-08-13 16:05:53 +0200695 goto more; /* we need to leave the IO handler once we wrote the request */
696 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200697 case HTTPCLIENT_S_REQ_BODY:
698 /* call the payload callback */
699 {
700 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100701 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200702
William Lallemand0da616e2021-10-28 15:34:26 +0200703 /* call the request callback */
704 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100705
William Lallemandccc7ee42022-03-18 17:57:15 +0100706 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100707 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100708
William Lallemandccc7ee42022-03-18 17:57:15 +0100709 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100710 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100711
William Lallemandccc7ee42022-03-18 17:57:15 +0100712 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200713 size_t data = hc_htx->data;
714
William Lallemandccc7ee42022-03-18 17:57:15 +0100715 /* Here htx_to_buf() will set buffer data to 0 because
716 * the HTX is empty, and allow us to do an xfer.
717 */
718 htx_to_buf(hc_htx, &hc->req.buf);
719 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100720 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200721 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100722 } else {
723 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100724
William Lallemandccc7ee42022-03-18 17:57:15 +0100725 ret = htx_xfer_blks(htx, hc_htx, hc_htx->data, HTX_BLK_UNUSED);
726 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100727
William Lallemandccc7ee42022-03-18 17:57:15 +0100728 /* we must copy the EOM if we empty the buffer */
729 if (htx_is_empty(hc_htx)) {
730 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
731 }
732 htx_to_buf(htx, &req->buf);
733 htx_to_buf(hc_htx, &hc->req.buf);
734 }
735
736
737 if (!b_data(&hc->req.buf))
738 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200739 }
740
William Lallemanddb8a1f32021-11-08 16:55:14 +0100741 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200742 if (!htx)
743 goto more;
744
745 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100746 if (htx->flags & HTX_FL_EOM) {
747 si->cs->flags |= CS_FL_EOI;
748 req->flags |= CF_EOI;
William Lallemand0da616e2021-10-28 15:34:26 +0200749 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100750 }
William Lallemand0da616e2021-10-28 15:34:26 +0200751
William Lallemand1eca8942022-03-17 14:57:23 +0100752 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200753 }
754 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200755
756 case HTTPCLIENT_S_RES_STLINE:
757 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100758 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200759 goto more;
760 htx = htxbuf(&res->buf);
761 if (!htx)
762 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100763 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200764 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
765 sl = htx_get_blk_ptr(htx, blk);
766 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
767 goto more;
768
769 /* copy the status line in the httpclient */
770 hc->res.status = sl->info.res.status;
771 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
772 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100773 sz = htx_get_blksz(blk);
774 co_set_data(res, co_data(res) - sz);
775 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200776 /* caller callback */
777 if (hc->ops.res_stline)
778 hc->ops.res_stline(hc);
779
780 /* if there is no HTX data anymore and the EOM flag is
781 * set, leave (no body) */
782 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
783 appctx->st0 = HTTPCLIENT_S_RES_END;
784 else
785 appctx->st0 = HTTPCLIENT_S_RES_HDR;
786 break;
787
788 case HTTPCLIENT_S_RES_HDR:
789 /* first copy the headers in a local hdrs
790 * structure, once we the total numbers of the
791 * header we allocate the right size and copy
792 * them. The htx block of the headers are
793 * removed each time one is read */
794 {
795 struct http_hdr hdrs[global.tune.max_http_hdr];
796
William Lallemanda625b032022-03-17 14:45:46 +0100797 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200798 goto more;
799 htx = htxbuf(&res->buf);
800 if (!htx)
801 goto more;
802
803 hdr_num = 0;
804
William Lallemand97f69c62022-03-10 17:23:40 +0100805 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
William Lallemand33b0d092021-08-13 16:05:53 +0200806 struct htx_blk *blk = htx_get_blk(htx, pos);
807 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100808 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200809
William Lallemandc020b252022-03-09 18:56:02 +0100810 if (type == HTX_BLK_UNUSED) {
811 c_rew(res, sz);
812 htx_remove_blk(htx, blk);
813 }
814
815 if (type == HTX_BLK_HDR) {
816 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
817 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
818 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
819 goto end;
820 c_rew(res, sz);
821 htx_remove_blk(htx, blk);
822 hdr_num++;
823 }
824
825 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200826 if (type == HTX_BLK_EOH) {
827 hdrs[hdr_num].n = IST_NULL;
828 hdrs[hdr_num].v = IST_NULL;
William Lallemandc020b252022-03-09 18:56:02 +0100829 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100830 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200831 break;
832 }
William Lallemand33b0d092021-08-13 16:05:53 +0200833 }
834
William Lallemand0d6f7792021-08-20 11:59:49 +0200835 if (hdr_num) {
836 /* alloc and copy the headers in the httpclient struct */
837 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
838 if (!hc->res.hdrs)
839 goto end;
840 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200841
William Lallemand0d6f7792021-08-20 11:59:49 +0200842 /* caller callback */
843 if (hc->ops.res_headers)
844 hc->ops.res_headers(hc);
845 }
William Lallemand33b0d092021-08-13 16:05:53 +0200846
847 /* if there is no HTX data anymore and the EOM flag is
848 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200849 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200850 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200851 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200852 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200853 }
William Lallemand33b0d092021-08-13 16:05:53 +0200854 }
855 break;
856
857 case HTTPCLIENT_S_RES_BODY:
858 /*
859 * The IO handler removes the htx blocks in the response buffer and
860 * push them in the hc->res.buf buffer in a raw format.
861 */
William Lallemanda625b032022-03-17 14:45:46 +0100862 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100863 goto more;
864
William Lallemand33b0d092021-08-13 16:05:53 +0200865 htx = htxbuf(&res->buf);
866 if (!htx || htx_is_empty(htx))
867 goto more;
868
Christopher Faulet600985d2022-01-12 11:14:08 +0100869 if (!b_alloc(&hc->res.buf))
870 goto more;
871
William Lallemand33b0d092021-08-13 16:05:53 +0200872 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100873 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200874
875 /* decapsule the htx data to raw data */
William Lallemand97f69c62022-03-10 17:23:40 +0100876 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100877 struct htx_blk *blk = htx_get_blk(htx, pos);
878 enum htx_blk_type type = htx_get_blk_type(blk);
879 size_t count = co_data(res);
880 uint32_t blksz = htx_get_blksz(blk);
881 uint32_t room = b_room(&hc->res.buf);
882 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200883
William Lallemandc8f1eb92022-03-09 11:58:51 +0100884 /* we should try to copy the maximum output data in a block, which fit
885 * the destination buffer */
886 vlen = MIN(count, blksz);
887 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100888
William Lallemandc8f1eb92022-03-09 11:58:51 +0100889 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100890 goto process_data;
891
William Lallemand33b0d092021-08-13 16:05:53 +0200892 if (type == HTX_BLK_DATA) {
893 struct ist v = htx_get_blk_value(htx, blk);
894
William Lallemandc8f1eb92022-03-09 11:58:51 +0100895 __b_putblk(&hc->res.buf, v.ptr, vlen);
896 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200897
William Lallemandc8f1eb92022-03-09 11:58:51 +0100898 if (vlen == blksz)
899 htx_remove_blk(htx, blk);
900 else
901 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100902
William Lallemand33b0d092021-08-13 16:05:53 +0200903 /* the data must be processed by the caller in the receive phase */
904 if (hc->ops.res_payload)
905 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100906
907 /* cannot copy everything, need to processs */
908 if (vlen != blksz)
909 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200910 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100911 if (vlen != blksz)
912 goto process_data;
913
William Lallemand33b0d092021-08-13 16:05:53 +0200914 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100915 c_rew(res, blksz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100916 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200917 }
918 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100919
William Lallemand33b0d092021-08-13 16:05:53 +0200920 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100921 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200922 goto more;
923
William Lallemandc8f1eb92022-03-09 11:58:51 +0100924
William Lallemand33b0d092021-08-13 16:05:53 +0200925 /* end of message, we should quit */
926 appctx->st0 = HTTPCLIENT_S_RES_END;
927 break;
928
929 case HTTPCLIENT_S_RES_END:
930 goto end;
931 break;
932 }
933 }
934
935process_data:
936
937 si_rx_chan_rdy(si);
938
939 return;
940more:
941 /* There was not enough data in the response channel */
942
943 si_rx_room_blk(si);
944
945 if (appctx->st0 == HTTPCLIENT_S_RES_END)
946 goto end;
947
948 /* The state machine tries to handle as much data as possible, if there
949 * isn't any data to handle and a shutdown is detected, let's stop
950 * everything */
951 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100952 (res->flags & CF_SHUTW) ||
953 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200954 goto end;
955 }
956 return;
957
958end:
William Lallemand33b0d092021-08-13 16:05:53 +0200959 si_shutw(si);
960 si_shutr(si);
961 return;
962}
963
964static void httpclient_applet_release(struct appctx *appctx)
965{
966 struct httpclient *hc = appctx->ctx.httpclient.ptr;
967
William Lallemand1123dde2021-09-21 10:58:10 +0200968 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200969 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200970 /* the applet is leaving, remove the ptr so we don't try to call it
971 * again from the caller */
972 hc->appctx = NULL;
973
William Lallemandeb0d4c42022-04-06 14:12:37 +0200974 if (hc->ops.res_end)
975 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +0200976
977 /* destroy the httpclient when set to autotokill */
978 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
979 httpclient_destroy(hc);
980 }
981
William Lallemand33b0d092021-08-13 16:05:53 +0200982 return;
983}
984
985/* HTTP client applet */
986static struct applet httpclient_applet = {
987 .obj_type = OBJ_TYPE_APPLET,
988 .name = "<HTTPCLIENT>",
989 .fct = httpclient_applet_io_handler,
990 .release = httpclient_applet_release,
991};
992
William Lallemand83614a92021-08-13 14:47:57 +0200993/*
994 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
995 * the other for HTTPS.
996 */
997
998static int httpclient_init()
999{
1000 int err_code = 0;
1001 char *errmsg = NULL;
1002
1003 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
1004 if (!httpclient_proxy) {
1005 err_code |= ERR_ALERT | ERR_FATAL;
1006 goto err;
1007 }
1008
Willy Tarreau0e72e402021-08-20 10:23:12 +02001009 proxy_preset_defaults(httpclient_proxy);
1010
William Lallemandccc7ee42022-03-18 17:57:15 +01001011 httpclient_proxy->options |= PR_O_WREQ_BODY;
William Lallemand71abad02022-03-17 15:24:28 +01001012 httpclient_proxy->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
William Lallemand83614a92021-08-13 14:47:57 +02001013 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
1014 httpclient_proxy->mode = PR_MODE_HTTP;
1015 httpclient_proxy->maxconn = 0;
1016 httpclient_proxy->accept = NULL;
William Lallemand71abad02022-03-17 15:24:28 +01001017 httpclient_proxy->conn_retries = CONN_RETRIES;
William Lallemand83614a92021-08-13 14:47:57 +02001018 httpclient_proxy->timeout.client = TICK_ETERNITY;
1019 /* The HTTP Client use the "option httplog" with the global log server */
1020 httpclient_proxy->conf.logformat_string = default_http_log_format;
1021 httpclient_proxy->http_needed = 1;
1022
1023 /* clear HTTP server */
1024 httpclient_srv_raw = new_server(httpclient_proxy);
1025 if (!httpclient_srv_raw) {
1026 err_code |= ERR_ALERT | ERR_FATAL;
1027 memprintf(&errmsg, "out of memory.");
1028 goto err;
1029 }
1030
1031 httpclient_srv_raw->iweight = 0;
1032 httpclient_srv_raw->uweight = 0;
1033 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
1034 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
1035 if (!httpclient_srv_raw->id)
1036 goto err;
1037
William Lallemand957ab132021-08-24 18:33:28 +02001038#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001039 /* SSL HTTP server */
1040 httpclient_srv_ssl = new_server(httpclient_proxy);
1041 if (!httpclient_srv_ssl) {
1042 memprintf(&errmsg, "out of memory.");
1043 err_code |= ERR_ALERT | ERR_FATAL;
1044 goto err;
1045 }
1046 httpclient_srv_ssl->iweight = 0;
1047 httpclient_srv_ssl->uweight = 0;
1048 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
1049 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +02001050 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +02001051 if (!httpclient_srv_ssl->id)
1052 goto err;
1053
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001054 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +02001055#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001056
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001057 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +02001058 httpclient_proxy->next = proxies_list;
1059 proxies_list = httpclient_proxy;
1060
William Lallemand211c9672021-08-24 17:18:13 +02001061 /* link the 2 servers in the proxy */
1062 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +02001063 httpclient_proxy->srv = httpclient_srv_raw;
1064
1065#ifdef USE_OPENSSL
1066 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +02001067 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +02001068#endif
1069
William Lallemand211c9672021-08-24 17:18:13 +02001070
William Lallemand83614a92021-08-13 14:47:57 +02001071 return 0;
1072
1073err:
1074 ha_alert("httpclient: cannot initialize.\n");
1075 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001076 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001077#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001078 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001079#endif
William Lallemand83614a92021-08-13 14:47:57 +02001080 free_proxy(httpclient_proxy);
1081 return err_code;
1082}
1083
William Lallemand83614a92021-08-13 14:47:57 +02001084static int httpclient_cfg_postparser()
1085{
1086 struct logsrv *logsrv;
1087 struct proxy *curproxy = httpclient_proxy;
William Lallemand71e31582022-03-16 15:47:47 +01001088 char *errmsg = NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001089
1090 /* copy logs from "global" log list */
1091 list_for_each_entry(logsrv, &global.logsrvs, list) {
1092 struct logsrv *node = malloc(sizeof(*node));
1093
1094 if (!node) {
1095 ha_alert("httpclient: cannot allocate memory.\n");
1096 goto err;
1097 }
1098
1099 memcpy(node, logsrv, sizeof(*node));
1100 LIST_INIT(&node->list);
1101 LIST_APPEND(&curproxy->logsrvs, &node->list);
Willy Tarreau211ea252022-03-17 19:47:33 +01001102 node->ring_name = NULL;
1103 node->conf.file = NULL;
1104 node->conf.line = 0;
William Lallemand83614a92021-08-13 14:47:57 +02001105 }
1106 if (curproxy->conf.logformat_string) {
William Lallemand83614a92021-08-13 14:47:57 +02001107 curproxy->conf.args.ctx = ARGC_LOG;
1108 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1109 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
William Lallemand715c1012022-03-16 16:39:23 +01001110 SMP_VAL_FE_LOG_END, &errmsg)) {
1111 ha_alert("httpclient: failed to parse log-format : %s.\n", errmsg);
1112 free(errmsg);
William Lallemand83614a92021-08-13 14:47:57 +02001113 goto err;
1114 }
1115 curproxy->conf.args.file = NULL;
1116 curproxy->conf.args.line = 0;
1117 }
William Lallemand71e31582022-03-16 15:47:47 +01001118
1119#ifdef USE_OPENSSL
William Lallemand715c1012022-03-16 16:39:23 +01001120 {
1121 int err_code = 0;
1122
1123 /* init the SNI expression */
1124 /* always use the host header as SNI, without the port */
1125 httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1126 err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
1127 if (err_code & ERR_CODE) {
1128 ha_alert("httpclient: failed to configure sni: %s.\n", errmsg);
1129 free(errmsg);
1130 goto err;
1131 }
William Lallemand71e31582022-03-16 15:47:47 +01001132 }
1133#endif
1134
William Lallemand83614a92021-08-13 14:47:57 +02001135 return 0;
1136err:
1137 return 1;
1138}
1139
William Lallemand83614a92021-08-13 14:47:57 +02001140/* initialize the proxy and servers for the HTTP client */
1141
Willy Tarreau5b4b6ca2022-02-18 16:23:14 +01001142INITCALL0(STG_INIT, httpclient_init);
William Lallemand83614a92021-08-13 14:47:57 +02001143REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);