blob: 0614ae77057c09c3fb8b899cd7c7371783ac0a8b [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 Lallemand957ab132021-08-24 18:33:28 +020044#endif
William Lallemand33b0d092021-08-13 16:05:53 +020045static struct applet httpclient_applet;
46
William Lallemand03a4eb12021-08-18 16:46:21 +020047/* --- This part of the file implement an HTTP client over the CLI ---
48 * The functions will be starting by "hc_cli" for "httpclient cli"
49 */
50
William Lallemand03a4eb12021-08-18 16:46:21 +020051/* What kind of data we need to read */
52#define HC_CLI_F_RES_STLINE 0x01
53#define HC_CLI_F_RES_HDR 0x02
54#define HC_CLI_F_RES_BODY 0x04
55#define HC_CLI_F_RES_END 0x08
56
57
58/* These are the callback used by the HTTP Client when it needs to notify new
59 * data, we only sets a flag in the IO handler */
60
61void hc_cli_res_stline_cb(struct httpclient *hc)
62{
63 struct appctx *appctx = hc->caller;
64
William Lallemanddfc3f892021-08-20 11:35:29 +020065 if (!appctx)
66 return;
67
William Lallemand03a4eb12021-08-18 16:46:21 +020068 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020069 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020070}
71
72void hc_cli_res_headers_cb(struct httpclient *hc)
73{
74 struct appctx *appctx = hc->caller;
75
William Lallemanddfc3f892021-08-20 11:35:29 +020076 if (!appctx)
77 return;
78
William Lallemand03a4eb12021-08-18 16:46:21 +020079 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020080 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020081}
82
83void hc_cli_res_body_cb(struct httpclient *hc)
84{
85 struct appctx *appctx = hc->caller;
86
William Lallemanddfc3f892021-08-20 11:35:29 +020087 if (!appctx)
88 return;
89
William Lallemand03a4eb12021-08-18 16:46:21 +020090 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020091 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020092}
93
94void hc_cli_res_end_cb(struct httpclient *hc)
95{
96 struct appctx *appctx = hc->caller;
97
William Lallemanddfc3f892021-08-20 11:35:29 +020098 if (!appctx)
99 return;
100
William Lallemand03a4eb12021-08-18 16:46:21 +0200101 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200102 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200103}
104
105/*
106 * Parse an httpclient keyword on the cli:
107 * httpclient <ID> <method> <URI>
108 */
109static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
110{
111 struct httpclient *hc;
112 char *err = NULL;
113 enum http_meth_t meth;
114 char *meth_str;
115 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200116 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200117
118 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
119 return 1;
120
121 if (!*args[1] || !*args[2]) {
122 memprintf(&err, ": not enough parameters");
123 goto err;
124 }
125
126 meth_str = args[1];
127 uri = ist(args[2]);
128
William Lallemanddec25c32021-10-25 19:48:37 +0200129 if (payload)
130 body = ist(payload);
131
William Lallemand03a4eb12021-08-18 16:46:21 +0200132 meth = find_http_meth(meth_str, strlen(meth_str));
133
134 hc = httpclient_new(appctx, meth, uri);
135 if (!hc) {
136 goto err;
137 }
138
139 /* update the httpclient callbacks */
140 hc->ops.res_stline = hc_cli_res_stline_cb;
141 hc->ops.res_headers = hc_cli_res_headers_cb;
142 hc->ops.res_payload = hc_cli_res_body_cb;
143 hc->ops.res_end = hc_cli_res_end_cb;
144
145 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
146 appctx->ctx.cli.i0 = 0;
147
William Lallemandbad9c8c2022-01-14 14:10:33 +0100148 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, NULL, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200149 goto err;
150
151
152 if (!httpclient_start(hc))
153 goto err;
154
155 return 0;
156
157err:
158 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
159 return cli_err(appctx, err);
160}
161
162/* This function dumps the content of the httpclient receive buffer
163 * on the CLI output
164 *
165 * Return 1 when the processing is finished
166 * return 0 if it needs to be called again
167 */
168static int hc_cli_io_handler(struct appctx *appctx)
169{
Christopher Faulet908628c2022-03-25 16:43:49 +0100170 struct conn_stream *cs = appctx->owner;
William Lallemand03a4eb12021-08-18 16:46:21 +0200171 struct buffer *trash = alloc_trash_chunk();
172 struct httpclient *hc = appctx->ctx.cli.p0;
173 struct http_hdr *hdrs, *hdr;
174
175 if (!trash)
176 goto out;
177 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemandde6ecc32022-02-16 11:37:02 +0100178 chunk_appendf(trash, "%.*s %d %.*s\n", (unsigned int)istlen(hc->res.vsn), istptr(hc->res.vsn),
179 hc->res.status, (unsigned int)istlen(hc->res.reason), istptr(hc->res.reason));
Christopher Faulet908628c2022-03-25 16:43:49 +0100180 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200181 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200182 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
183 goto out;
184 }
185
186 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
187 hdrs = hc->res.hdrs;
188 for (hdr = hdrs; isttest(hdr->v); hdr++) {
189 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
190 goto out;
191 }
192 if (!chunk_memcat(trash, "\r\n", 2))
193 goto out;
Christopher Faulet908628c2022-03-25 16:43:49 +0100194 if (ci_putchk(cs_ic(cs), trash) == -1)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200195 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200196 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
197 goto out;
198 }
199
200 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
201 int ret;
202
Christopher Faulet908628c2022-03-25 16:43:49 +0100203 ret = httpclient_res_xfer(hc, cs_ib(cs));
204 channel_add_input(cs_ic(cs), ret); /* forward what we put in the buffer channel */
William Lallemand03a4eb12021-08-18 16:46:21 +0200205
William Lallemand518878e2021-09-21 10:45:34 +0200206 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200207 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
208 }
209 goto out;
210 }
211
212 /* we must close only if F_END is the last flag */
213 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
Christopher Fauletda098e62022-03-31 17:44:45 +0200214 cs_shutw(cs);
215 cs_shutr(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200216 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
217 goto out;
218 }
219
220out:
221 /* we didn't clear every flags, we should come back to finish things */
222 if (appctx->ctx.cli.i0)
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200223 cs_rx_room_blk(cs);
William Lallemand03a4eb12021-08-18 16:46:21 +0200224
225 free_trash_chunk(trash);
226 return 0;
227}
228
229static void hc_cli_release(struct appctx *appctx)
230{
231 struct httpclient *hc = appctx->ctx.cli.p0;
232
233 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200234 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200235
236 return;
237}
238
239/* register cli keywords */
240static struct cli_kw_list cli_kws = {{ },{
Willy Tarreau2c8f9842022-02-18 16:26:36 +0100241 { { "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 +0200242 { { NULL }, NULL, NULL, NULL }
243}};
244
245INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
246
247
248/* --- This part of the file implements the actual HTTP client API --- */
249
William Lallemand33b0d092021-08-13 16:05:53 +0200250/*
251 * Generate a simple request and fill the httpclient request buffer with it.
252 * The request contains a request line generated from the absolute <url> and
253 * <meth> as well as list of headers <hdrs>.
254 *
255 * If the buffer was filled correctly the function returns 0, if not it returns
256 * an error_code but there is no guarantee that the buffer wasn't modified.
257 */
William Lallemanddec25c32021-10-25 19:48:37 +0200258int 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 +0200259{
260 struct htx_sl *sl;
261 struct htx *htx;
262 int err_code = 0;
263 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200264 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 +0100265 int i;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100266 int foundhost = 0, foundaccept = 0, foundua = 0;
William Lallemand33b0d092021-08-13 16:05:53 +0200267
Christopher Faulet600985d2022-01-12 11:14:08 +0100268 if (!b_alloc(&hc->req.buf))
269 goto error;
270
William Lallemand33b0d092021-08-13 16:05:53 +0200271 if (meth >= HTTP_METH_OTHER)
272 goto error;
273
274 meth_ist = http_known_methods[meth];
275
276 vsn = ist("HTTP/1.1");
277
278 htx = htx_from_buf(&hc->req.buf);
279 if (!htx)
280 goto error;
William Lallemande1e045f2022-01-14 14:08:34 +0100281
282 if (!hc->ops.req_payload && !isttest(payload))
283 flags |= HTX_SL_F_BODYLESS;
284
William Lallemand33b0d092021-08-13 16:05:53 +0200285 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
286 if (!sl) {
287 goto error;
288 }
289 sl->info.req.meth = meth;
290
William Lallemandf03b53c2021-11-24 15:38:17 +0100291 for (i = 0; hdrs && hdrs[i].n.len; i++) {
292 /* Don't check the value length because a header value may be empty */
293 if (isttest(hdrs[i].v) == 0)
294 continue;
295
296 if (isteqi(hdrs[i].n, ist("host")))
297 foundhost = 1;
William Lallemandbad9c8c2022-01-14 14:10:33 +0100298 else if (isteqi(hdrs[i].n, ist("accept")))
299 foundaccept = 1;
300 else if (isteqi(hdrs[i].n, ist("user-agent")))
301 foundua = 1;
William Lallemandf03b53c2021-11-24 15:38:17 +0100302
303 if (!htx_add_header(htx, hdrs[i].n, hdrs[i].v))
304 goto error;
305 }
William Lallemand33b0d092021-08-13 16:05:53 +0200306
William Lallemandf03b53c2021-11-24 15:38:17 +0100307 if (!foundhost) {
308 /* Add Host Header from URL */
309 if (!htx_add_header(htx, ist("Host"), ist("h")))
William Lallemand79a34782021-09-20 16:19:15 +0200310 goto error;
William Lallemandf03b53c2021-11-24 15:38:17 +0100311 if (!http_update_host(htx, sl, url))
William Lallemand79a34782021-09-20 16:19:15 +0200312 goto error;
313 }
William Lallemand33b0d092021-08-13 16:05:53 +0200314
William Lallemandbad9c8c2022-01-14 14:10:33 +0100315 if (!foundaccept) {
316 if (!htx_add_header(htx, ist("Accept"), ist("*/*")))
317 goto error;
318 }
319
320 if (!foundua) {
321 if (!htx_add_header(htx, ist("User-Agent"), ist(HTTPCLIENT_USERAGENT)))
322 goto error;
323 }
324
325
William Lallemandf03b53c2021-11-24 15:38:17 +0100326 if (!htx_add_endof(htx, HTX_BLK_EOH))
327 goto error;
328
William Lallemanddec25c32021-10-25 19:48:37 +0200329 if (isttest(payload)) {
330 /* add the payload if it can feat in the buffer, no need to set
331 * the Content-Length, the data will be sent chunked */
332 if (!htx_add_data_atonce(htx, payload))
333 goto error;
334 }
335
William Lallemand0da616e2021-10-28 15:34:26 +0200336 /* If req.payload was set, does not set the end of stream which *MUST*
337 * be set in the callback */
338 if (!hc->ops.req_payload)
339 htx->flags |= HTX_FL_EOM;
William Lallemand33b0d092021-08-13 16:05:53 +0200340
341 htx_to_buf(htx, &hc->req.buf);
342
343 return 0;
344error:
345 err_code |= ERR_ALERT | ERR_ABORT;
346 return err_code;
347}
348
349/*
350 * transfer the response to the destination buffer and wakeup the HTTP client
351 * applet so it could fill again its buffer.
352 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500353 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200354 */
355int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
356{
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100357 size_t room = b_room(dst);
William Lallemand33b0d092021-08-13 16:05:53 +0200358 int ret;
359
Willy Tarreau11adb1d2022-02-18 17:28:25 +0100360 ret = b_force_xfer(dst, &hc->res.buf, MIN(room, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200361 /* call the client once we consumed all data */
Christopher Faulet600985d2022-01-12 11:14:08 +0100362 if (!b_data(&hc->res.buf)) {
363 b_free(&hc->res.buf);
364 if (hc->appctx)
365 appctx_wakeup(hc->appctx);
366 }
William Lallemand33b0d092021-08-13 16:05:53 +0200367 return ret;
368}
369
370/*
William Lallemand0da616e2021-10-28 15:34:26 +0200371 * Transfer raw HTTP payload from src, and insert it into HTX format in the
372 * httpclient.
373 *
374 * Must be used to transfer the request body.
375 * Then wakeup the httpclient so it can transfer it.
376 *
377 * <end> tries to add the ending data flag if it succeed to copy all data.
378 *
379 * Return the number of bytes copied from src.
380 */
381int httpclient_req_xfer(struct httpclient *hc, struct ist src, int end)
382{
383 int ret = 0;
384 struct htx *htx;
385
Christopher Faulet600985d2022-01-12 11:14:08 +0100386 if (!b_alloc(&hc->req.buf))
387 goto error;
388
William Lallemand0da616e2021-10-28 15:34:26 +0200389 htx = htx_from_buf(&hc->req.buf);
390 if (!htx)
391 goto error;
392
393 if (hc->appctx)
394 appctx_wakeup(hc->appctx);
395
396 ret += htx_add_data(htx, src);
397
398
399 /* if we copied all the data and the end flag is set */
400 if ((istlen(src) == ret) && end) {
401 htx->flags |= HTX_FL_EOM;
402 }
403 htx_to_buf(htx, &hc->req.buf);
404
405error:
406
407 return ret;
408}
409
William Lallemandb4a4ef62022-02-23 14:18:16 +0100410/* Set the 'timeout server' in ms for the next httpclient request */
411void httpclient_set_timeout(struct httpclient *hc, int timeout)
412{
413 hc->timeout_server = timeout;
414}
415
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100416/*
417 * Sets a destination for the httpclient from an HAProxy addr format
418 * This will prevent to determine the destination from the URL
419 * Return 0 in case of success or -1 otherwise.
420 */
421int httpclient_set_dst(struct httpclient *hc, const char *dst)
422{
423 struct sockaddr_storage *sk;
424 char *errmsg = NULL;
425
426 sockaddr_free(&hc->dst);
427 /* 'sk' is statically allocated (no need to be freed). */
428 sk = str2sa_range(dst, NULL, NULL, NULL, NULL, NULL,
429 &errmsg, NULL, NULL,
430 PA_O_PORT_OK | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT);
431 if (!sk) {
432 ha_alert("httpclient: Failed to parse destination address in %s\n", errmsg);
433 free(errmsg);
434 return -1;
435 }
436
437 if (!sockaddr_alloc(&hc->dst, sk, sizeof(*sk))) {
438 ha_alert("httpclient: Failed to allocate sockaddr in %s:%d.\n", __FUNCTION__, __LINE__);
439 return -1;
440 }
441
442 return 0;
443}
William Lallemand0da616e2021-10-28 15:34:26 +0200444
445/*
William Lallemand33b0d092021-08-13 16:05:53 +0200446 * Start the HTTP client
447 * Create the appctx, session, stream and wakeup the applet
448 *
449 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
450 * in the URL are supported, it lacks a resolver.
451 *
452 * Return the <appctx> or NULL if it failed
453 */
454struct appctx *httpclient_start(struct httpclient *hc)
455{
456 struct applet *applet = &httpclient_applet;
457 struct appctx *appctx;
458 struct session *sess;
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 Faulet9ec2f4d2022-03-23 15:15:29 +0100483 appctx = appctx_new(applet, NULL);
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
Christopher Faulet9ec2f4d2022-03-23 15:15:29 +0100502 cs = cs_new_from_applet(appctx->endp, sess, &hc->req.buf);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100503 if (!cs) {
504 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100505 goto out_free_addr;
William Lallemand33b0d092021-08-13 16:05:53 +0200506 }
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100507 s = DISGUISE(cs_strm(cs));
508
509 /* set the "timeout server" */
510 s->req.wto = hc->timeout_server;
511 s->res.rto = hc->timeout_server;
William Lallemand33b0d092021-08-13 16:05:53 +0200512
513 /* choose the SSL server or not */
514 switch (out.scheme) {
515 case SCH_HTTP:
516 s->target = &httpclient_srv_raw->obj_type;
517 break;
518 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200519#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200520 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200521#else
522 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
Christopher Faulet67df95a2022-04-14 12:02:34 +0200523 LIST_DELETE(&s->list);
524 pool_free(pool_head_stream, s);
525 cs_free(cs);
526 goto out_free_addr;
William Lallemand957ab132021-08-24 18:33:28 +0200527#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200528 break;
529 }
530
Christopher Faulet8da67aa2022-03-29 17:53:09 +0200531 s->csb->dst = addr;
Christopher Faulet8abe7122022-03-30 15:10:18 +0200532 s->csb->flags |= CS_FL_NOLINGER;
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100533 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
William Lallemand33b0d092021-08-13 16:05:53 +0200534 s->res.flags |= CF_READ_DONTWAIT;
535
536 /* applet is waiting for data */
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200537 cs_cant_get(s->csf);
William Lallemand33b0d092021-08-13 16:05:53 +0200538 appctx_wakeup(appctx);
539
William Lallemand33b0d092021-08-13 16:05:53 +0200540 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200541 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200542 appctx->ctx.httpclient.ptr = hc;
Christopher Faulet6ced61d2022-01-12 15:27:41 +0100543
544 /* The request was transferred when the stream was created. So switch
545 * directly to REQ_BODY or RES_STLINE state
546 */
547 appctx->st0 = (hc->ops.req_payload ? HTTPCLIENT_S_REQ_BODY : HTTPCLIENT_S_RES_STLINE);
William Lallemand33b0d092021-08-13 16:05:53 +0200548
549 return appctx;
550
Christopher Fauleta9e8b392022-03-23 11:01:09 +0100551out_free_addr:
552 sockaddr_free(&addr);
William Lallemand33b0d092021-08-13 16:05:53 +0200553out_free_sess:
554 session_free(sess);
555out_free_appctx:
556 appctx_free(appctx);
557out:
558
559 return NULL;
560}
561
William Lallemandecb83e12021-09-28 11:00:43 +0200562/*
563 * This function tries to destroy the httpclient if it wasn't running.
564 * If it was running, stop the client and ask it to autodestroy itself.
565 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500566 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200567 *
568 */
569void httpclient_stop_and_destroy(struct httpclient *hc)
570{
571
William Lallemandb8b13702021-09-28 12:15:37 +0200572 /* The httpclient was already stopped or never started, we can safely destroy it */
573 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200574 httpclient_destroy(hc);
575 } else {
576 /* if the client wasn't stopped, ask for a stop and destroy */
577 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
578 if (hc->appctx)
579 appctx_wakeup(hc->appctx);
580 }
581}
582
William Lallemand33b0d092021-08-13 16:05:53 +0200583/* Free the httpclient */
584void httpclient_destroy(struct httpclient *hc)
585{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200586 struct http_hdr *hdrs;
587
588
William Lallemand33b0d092021-08-13 16:05:53 +0200589 if (!hc)
590 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200591
William Lallemand2a879002021-10-05 15:50:45 +0200592 /* we should never destroy a client which was started but not stopped */
593 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200594
William Lallemand03f5a1c2021-09-27 15:17:47 +0200595 /* request */
596 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200597 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200598 /* response */
599 istfree(&hc->res.vsn);
600 istfree(&hc->res.reason);
601 hdrs = hc->res.hdrs;
602 while (hdrs && isttest(hdrs->n)) {
603 istfree(&hdrs->n);
604 istfree(&hdrs->v);
605 hdrs++;
606 }
607 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200608 b_free(&hc->res.buf);
William Lallemand7b2e0ee2022-02-17 19:10:55 +0100609 sockaddr_free(&hc->dst);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200610
William Lallemand33b0d092021-08-13 16:05:53 +0200611 free(hc);
612
613 return;
614}
615
616/* Allocate an httpclient and its buffers
617 * Return NULL on failure */
618struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
619{
620 struct httpclient *hc;
William Lallemand33b0d092021-08-13 16:05:53 +0200621
622 hc = calloc(1, sizeof(*hc));
623 if (!hc)
624 goto err;
625
Christopher Faulet600985d2022-01-12 11:14:08 +0100626 hc->req.buf = BUF_NULL;
627 hc->res.buf = BUF_NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200628 hc->caller = caller;
William Lallemand67b77842021-11-10 16:57:25 +0100629 hc->req.url = istdup(url);
William Lallemand33b0d092021-08-13 16:05:53 +0200630 hc->req.meth = meth;
631
632 return hc;
633
634err:
635 httpclient_destroy(hc);
636 return NULL;
637}
638
639static void httpclient_applet_io_handler(struct appctx *appctx)
640{
641 struct httpclient *hc = appctx->ctx.httpclient.ptr;
Christopher Faulet908628c2022-03-25 16:43:49 +0100642 struct conn_stream *cs = appctx->owner;
643 struct stream *s = __cs_strm(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200644 struct channel *req = &s->req;
645 struct channel *res = &s->res;
646 struct htx_blk *blk = NULL;
647 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200648 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200649 int32_t pos;
650 uint32_t hdr_num;
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100651 uint32_t sz;
William Lallemand933fe392021-11-04 09:45:58 +0100652 int ret;
William Lallemand33b0d092021-08-13 16:05:53 +0200653
654
655 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200656
657 /* required to stop */
658 if (hc->flags & HTTPCLIENT_FA_STOP)
659 goto end;
660
William Lallemand33b0d092021-08-13 16:05:53 +0200661 switch(appctx->st0) {
662
663 case HTTPCLIENT_S_REQ:
William Lallemanddb8a1f32021-11-08 16:55:14 +0100664 /* we know that the buffer is empty here, since
665 * it's the first call, we can freely copy the
666 * request from the httpclient buffer */
William Lallemand933fe392021-11-04 09:45:58 +0100667 ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
William Lallemanddb8a1f32021-11-08 16:55:14 +0100668 if (!ret)
669 goto more;
William Lallemand933fe392021-11-04 09:45:58 +0100670
Christopher Faulet600985d2022-01-12 11:14:08 +0100671 if (!b_data(&hc->req.buf))
672 b_free(&hc->req.buf);
673
William Lallemanddb8a1f32021-11-08 16:55:14 +0100674 htx = htx_from_buf(&req->buf);
William Lallemand933fe392021-11-04 09:45:58 +0100675 if (!htx)
676 goto more;
677
William Lallemanddb8a1f32021-11-08 16:55:14 +0100678 channel_add_input(req, htx->data);
679
William Lallemand933fe392021-11-04 09:45:58 +0100680 if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
681 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
682 else
683 appctx->st0 = HTTPCLIENT_S_REQ_BODY;
684
William Lallemand33b0d092021-08-13 16:05:53 +0200685 goto more; /* we need to leave the IO handler once we wrote the request */
686 break;
William Lallemand0da616e2021-10-28 15:34:26 +0200687 case HTTPCLIENT_S_REQ_BODY:
688 /* call the payload callback */
689 {
690 if (hc->ops.req_payload) {
William Lallemandccc7ee42022-03-18 17:57:15 +0100691 struct htx *hc_htx;
William Lallemand0da616e2021-10-28 15:34:26 +0200692
William Lallemand0da616e2021-10-28 15:34:26 +0200693 /* call the request callback */
694 hc->ops.req_payload(hc);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100695
William Lallemandccc7ee42022-03-18 17:57:15 +0100696 hc_htx = htx_from_buf(&hc->req.buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100697 htx = htx_from_buf(&req->buf);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100698
William Lallemandccc7ee42022-03-18 17:57:15 +0100699 if (htx_is_empty(hc_htx))
William Lallemanddb8a1f32021-11-08 16:55:14 +0100700 goto more;
Christopher Faulet600985d2022-01-12 11:14:08 +0100701
William Lallemandccc7ee42022-03-18 17:57:15 +0100702 if (htx_is_empty(htx)) {
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200703 size_t data = hc_htx->data;
704
William Lallemandccc7ee42022-03-18 17:57:15 +0100705 /* Here htx_to_buf() will set buffer data to 0 because
706 * the HTX is empty, and allow us to do an xfer.
707 */
708 htx_to_buf(hc_htx, &hc->req.buf);
709 htx_to_buf(htx, &req->buf);
William Lallemandccc7ee42022-03-18 17:57:15 +0100710 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
Christopher Fauletdca3b5b2022-04-07 10:47:07 +0200711 channel_add_input(req, data);
William Lallemandccc7ee42022-03-18 17:57:15 +0100712 } else {
713 struct htx_ret ret;
Christopher Faulet600985d2022-01-12 11:14:08 +0100714
William Lallemandccc7ee42022-03-18 17:57:15 +0100715 ret = htx_xfer_blks(htx, hc_htx, hc_htx->data, HTX_BLK_UNUSED);
716 channel_add_input(req, ret.ret);
William Lallemanddb8a1f32021-11-08 16:55:14 +0100717
William Lallemandccc7ee42022-03-18 17:57:15 +0100718 /* we must copy the EOM if we empty the buffer */
719 if (htx_is_empty(hc_htx)) {
720 htx->flags |= (hc_htx->flags & HTX_FL_EOM);
721 }
722 htx_to_buf(htx, &req->buf);
723 htx_to_buf(hc_htx, &hc->req.buf);
724 }
725
726
727 if (!b_data(&hc->req.buf))
728 b_free(&hc->req.buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200729 }
730
William Lallemanddb8a1f32021-11-08 16:55:14 +0100731 htx = htx_from_buf(&req->buf);
William Lallemand0da616e2021-10-28 15:34:26 +0200732 if (!htx)
733 goto more;
734
735 /* if the request contains the HTX_FL_EOM, we finished the request part. */
Christopher Faulet3d433242022-03-03 15:38:39 +0100736 if (htx->flags & HTX_FL_EOM) {
Christopher Faulet908628c2022-03-25 16:43:49 +0100737 cs->endp->flags |= CS_EP_EOI;
Christopher Faulet3d433242022-03-03 15:38:39 +0100738 req->flags |= CF_EOI;
William Lallemand0da616e2021-10-28 15:34:26 +0200739 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
Christopher Faulet3d433242022-03-03 15:38:39 +0100740 }
William Lallemand0da616e2021-10-28 15:34:26 +0200741
William Lallemand1eca8942022-03-17 14:57:23 +0100742 goto process_data; /* we need to leave the IO handler once we wrote the request */
William Lallemand0da616e2021-10-28 15:34:26 +0200743 }
744 break;
William Lallemand33b0d092021-08-13 16:05:53 +0200745
746 case HTTPCLIENT_S_RES_STLINE:
747 /* copy the start line in the hc structure,then remove the htx block */
William Lallemanda625b032022-03-17 14:45:46 +0100748 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200749 goto more;
750 htx = htxbuf(&res->buf);
751 if (!htx)
752 goto more;
William Lallemand97f69c62022-03-10 17:23:40 +0100753 blk = htx_get_head_blk(htx);
William Lallemand33b0d092021-08-13 16:05:53 +0200754 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
755 sl = htx_get_blk_ptr(htx, blk);
756 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
757 goto more;
758
759 /* copy the status line in the httpclient */
760 hc->res.status = sl->info.res.status;
761 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
762 hc->res.reason = istdup(htx_sl_res_reason(sl));
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100763 sz = htx_get_blksz(blk);
764 co_set_data(res, co_data(res) - sz);
765 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200766 /* caller callback */
767 if (hc->ops.res_stline)
768 hc->ops.res_stline(hc);
769
770 /* if there is no HTX data anymore and the EOM flag is
771 * set, leave (no body) */
772 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
773 appctx->st0 = HTTPCLIENT_S_RES_END;
774 else
775 appctx->st0 = HTTPCLIENT_S_RES_HDR;
776 break;
777
778 case HTTPCLIENT_S_RES_HDR:
779 /* first copy the headers in a local hdrs
780 * structure, once we the total numbers of the
781 * header we allocate the right size and copy
782 * them. The htx block of the headers are
783 * removed each time one is read */
784 {
785 struct http_hdr hdrs[global.tune.max_http_hdr];
786
William Lallemanda625b032022-03-17 14:45:46 +0100787 if (!co_data(res))
William Lallemand33b0d092021-08-13 16:05:53 +0200788 goto more;
789 htx = htxbuf(&res->buf);
790 if (!htx)
791 goto more;
792
793 hdr_num = 0;
794
William Lallemand97f69c62022-03-10 17:23:40 +0100795 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
William Lallemand33b0d092021-08-13 16:05:53 +0200796 struct htx_blk *blk = htx_get_blk(htx, pos);
797 enum htx_blk_type type = htx_get_blk_type(blk);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100798 uint32_t sz = htx_get_blksz(blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200799
William Lallemandc020b252022-03-09 18:56:02 +0100800 if (type == HTX_BLK_UNUSED) {
801 c_rew(res, sz);
802 htx_remove_blk(htx, blk);
803 }
804
805 if (type == HTX_BLK_HDR) {
806 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
807 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
808 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
809 goto end;
810 c_rew(res, sz);
811 htx_remove_blk(htx, blk);
812 hdr_num++;
813 }
814
815 /* create a NULL end of array and leave the loop */
William Lallemand33b0d092021-08-13 16:05:53 +0200816 if (type == HTX_BLK_EOH) {
817 hdrs[hdr_num].n = IST_NULL;
818 hdrs[hdr_num].v = IST_NULL;
William Lallemandc020b252022-03-09 18:56:02 +0100819 c_rew(res, sz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100820 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200821 break;
822 }
William Lallemand33b0d092021-08-13 16:05:53 +0200823 }
824
William Lallemand0d6f7792021-08-20 11:59:49 +0200825 if (hdr_num) {
826 /* alloc and copy the headers in the httpclient struct */
827 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
828 if (!hc->res.hdrs)
829 goto end;
830 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200831
William Lallemand0d6f7792021-08-20 11:59:49 +0200832 /* caller callback */
833 if (hc->ops.res_headers)
834 hc->ops.res_headers(hc);
835 }
William Lallemand33b0d092021-08-13 16:05:53 +0200836
837 /* if there is no HTX data anymore and the EOM flag is
838 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200839 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200840 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200841 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200842 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200843 }
William Lallemand33b0d092021-08-13 16:05:53 +0200844 }
845 break;
846
847 case HTTPCLIENT_S_RES_BODY:
848 /*
849 * The IO handler removes the htx blocks in the response buffer and
850 * push them in the hc->res.buf buffer in a raw format.
851 */
William Lallemanda625b032022-03-17 14:45:46 +0100852 if (!co_data(res))
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100853 goto more;
854
William Lallemand33b0d092021-08-13 16:05:53 +0200855 htx = htxbuf(&res->buf);
856 if (!htx || htx_is_empty(htx))
857 goto more;
858
Christopher Faulet600985d2022-01-12 11:14:08 +0100859 if (!b_alloc(&hc->res.buf))
860 goto more;
861
William Lallemand33b0d092021-08-13 16:05:53 +0200862 if (b_full(&hc->res.buf))
Christopher Faulet600985d2022-01-12 11:14:08 +0100863 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200864
865 /* decapsule the htx data to raw data */
William Lallemand97f69c62022-03-10 17:23:40 +0100866 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100867 struct htx_blk *blk = htx_get_blk(htx, pos);
868 enum htx_blk_type type = htx_get_blk_type(blk);
869 size_t count = co_data(res);
870 uint32_t blksz = htx_get_blksz(blk);
871 uint32_t room = b_room(&hc->res.buf);
872 uint32_t vlen;
William Lallemand33b0d092021-08-13 16:05:53 +0200873
William Lallemandc8f1eb92022-03-09 11:58:51 +0100874 /* we should try to copy the maximum output data in a block, which fit
875 * the destination buffer */
876 vlen = MIN(count, blksz);
877 vlen = MIN(vlen, room);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100878
William Lallemandc8f1eb92022-03-09 11:58:51 +0100879 if (vlen == 0)
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100880 goto process_data;
881
William Lallemand33b0d092021-08-13 16:05:53 +0200882 if (type == HTX_BLK_DATA) {
883 struct ist v = htx_get_blk_value(htx, blk);
884
William Lallemandc8f1eb92022-03-09 11:58:51 +0100885 __b_putblk(&hc->res.buf, v.ptr, vlen);
886 c_rew(res, vlen);
William Lallemand33b0d092021-08-13 16:05:53 +0200887
William Lallemandc8f1eb92022-03-09 11:58:51 +0100888 if (vlen == blksz)
889 htx_remove_blk(htx, blk);
890 else
891 htx_cut_data_blk(htx, blk, vlen);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100892
William Lallemand33b0d092021-08-13 16:05:53 +0200893 /* the data must be processed by the caller in the receive phase */
894 if (hc->ops.res_payload)
895 hc->ops.res_payload(hc);
William Lallemandc8f1eb92022-03-09 11:58:51 +0100896
897 /* cannot copy everything, need to processs */
898 if (vlen != blksz)
899 goto process_data;
William Lallemand33b0d092021-08-13 16:05:53 +0200900 } else {
William Lallemandc8f1eb92022-03-09 11:58:51 +0100901 if (vlen != blksz)
902 goto process_data;
903
William Lallemand33b0d092021-08-13 16:05:53 +0200904 /* remove any block which is not a data block */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100905 c_rew(res, blksz);
William Lallemand2b7dc4e2022-02-24 16:55:41 +0100906 htx_remove_blk(htx, blk);
William Lallemand33b0d092021-08-13 16:05:53 +0200907 }
908 }
William Lallemandc8f1eb92022-03-09 11:58:51 +0100909
William Lallemand33b0d092021-08-13 16:05:53 +0200910 /* if not finished, should be called again */
William Lallemandc8f1eb92022-03-09 11:58:51 +0100911 if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
William Lallemand33b0d092021-08-13 16:05:53 +0200912 goto more;
913
William Lallemandc8f1eb92022-03-09 11:58:51 +0100914
William Lallemand33b0d092021-08-13 16:05:53 +0200915 /* end of message, we should quit */
916 appctx->st0 = HTTPCLIENT_S_RES_END;
917 break;
918
919 case HTTPCLIENT_S_RES_END:
920 goto end;
921 break;
922 }
923 }
924
925process_data:
926
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200927 cs_rx_chan_rdy(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200928
929 return;
930more:
931 /* There was not enough data in the response channel */
932
Christopher Fauleta0bdec32022-04-04 07:51:21 +0200933 cs_rx_room_blk(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200934
935 if (appctx->st0 == HTTPCLIENT_S_RES_END)
936 goto end;
937
938 /* The state machine tries to handle as much data as possible, if there
939 * isn't any data to handle and a shutdown is detected, let's stop
940 * everything */
941 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
William Lallemand58a81ae2022-03-17 15:14:15 +0100942 (res->flags & CF_SHUTW) ||
943 ((res->flags & CF_SHUTW_NOW) && channel_is_empty(res))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200944 goto end;
945 }
946 return;
947
948end:
Christopher Fauletda098e62022-03-31 17:44:45 +0200949 cs_shutw(cs);
950 cs_shutr(cs);
William Lallemand33b0d092021-08-13 16:05:53 +0200951 return;
952}
953
954static void httpclient_applet_release(struct appctx *appctx)
955{
956 struct httpclient *hc = appctx->ctx.httpclient.ptr;
957
William Lallemand1123dde2021-09-21 10:58:10 +0200958 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200959 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200960 /* the applet is leaving, remove the ptr so we don't try to call it
961 * again from the caller */
962 hc->appctx = NULL;
963
William Lallemandeb0d4c42022-04-06 14:12:37 +0200964 if (hc->ops.res_end)
965 hc->ops.res_end(hc);
William Lallemandecb83e12021-09-28 11:00:43 +0200966
967 /* destroy the httpclient when set to autotokill */
968 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
969 httpclient_destroy(hc);
970 }
971
William Lallemand33b0d092021-08-13 16:05:53 +0200972 return;
973}
974
975/* HTTP client applet */
976static struct applet httpclient_applet = {
977 .obj_type = OBJ_TYPE_APPLET,
978 .name = "<HTTPCLIENT>",
979 .fct = httpclient_applet_io_handler,
980 .release = httpclient_applet_release,
981};
982
William Lallemand83614a92021-08-13 14:47:57 +0200983/*
984 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
985 * the other for HTTPS.
986 */
William Lallemand2c8b0842022-04-22 15:16:09 +0200987static int httpclient_precheck()
William Lallemand83614a92021-08-13 14:47:57 +0200988{
989 int err_code = 0;
990 char *errmsg = NULL;
991
William Lallemandc6ceba32022-04-22 16:49:53 +0200992 if (global.mode & MODE_MWORKER_WAIT)
993 return 0;
994
William Lallemand83614a92021-08-13 14:47:57 +0200995 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
996 if (!httpclient_proxy) {
997 err_code |= ERR_ALERT | ERR_FATAL;
998 goto err;
999 }
1000
Willy Tarreau0e72e402021-08-20 10:23:12 +02001001 proxy_preset_defaults(httpclient_proxy);
1002
William Lallemandccc7ee42022-03-18 17:57:15 +01001003 httpclient_proxy->options |= PR_O_WREQ_BODY;
William Lallemand71abad02022-03-17 15:24:28 +01001004 httpclient_proxy->retry_type |= PR_RE_CONN_FAILED | PR_RE_DISCONNECTED | PR_RE_TIMEOUT;
William Lallemand83614a92021-08-13 14:47:57 +02001005 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
1006 httpclient_proxy->mode = PR_MODE_HTTP;
1007 httpclient_proxy->maxconn = 0;
1008 httpclient_proxy->accept = NULL;
William Lallemand71abad02022-03-17 15:24:28 +01001009 httpclient_proxy->conn_retries = CONN_RETRIES;
William Lallemand83614a92021-08-13 14:47:57 +02001010 httpclient_proxy->timeout.client = TICK_ETERNITY;
1011 /* The HTTP Client use the "option httplog" with the global log server */
1012 httpclient_proxy->conf.logformat_string = default_http_log_format;
1013 httpclient_proxy->http_needed = 1;
1014
1015 /* clear HTTP server */
1016 httpclient_srv_raw = new_server(httpclient_proxy);
1017 if (!httpclient_srv_raw) {
1018 err_code |= ERR_ALERT | ERR_FATAL;
1019 memprintf(&errmsg, "out of memory.");
1020 goto err;
1021 }
1022
1023 httpclient_srv_raw->iweight = 0;
1024 httpclient_srv_raw->uweight = 0;
1025 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
1026 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
1027 if (!httpclient_srv_raw->id)
1028 goto err;
1029
William Lallemand957ab132021-08-24 18:33:28 +02001030#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +02001031 /* SSL HTTP server */
1032 httpclient_srv_ssl = new_server(httpclient_proxy);
1033 if (!httpclient_srv_ssl) {
1034 memprintf(&errmsg, "out of memory.");
1035 err_code |= ERR_ALERT | ERR_FATAL;
1036 goto err;
1037 }
1038 httpclient_srv_ssl->iweight = 0;
1039 httpclient_srv_ssl->uweight = 0;
1040 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
1041 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +02001042 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +02001043 if (!httpclient_srv_ssl->id)
1044 goto err;
1045
William Lallemandcf5cb0b2022-04-22 14:48:45 +02001046 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_REQUIRED;
1047 httpclient_srv_ssl->ssl_ctx.ca_file = strdup("@system-ca");
1048 if (!ssl_store_load_locations_file(httpclient_srv_ssl->ssl_ctx.ca_file, 1, CAFILE_CERT))
1049 goto err;
1050
William Lallemand957ab132021-08-24 18:33:28 +02001051#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +02001052
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +05001053 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +02001054 httpclient_proxy->next = proxies_list;
1055 proxies_list = httpclient_proxy;
1056
William Lallemand211c9672021-08-24 17:18:13 +02001057 /* link the 2 servers in the proxy */
1058 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +02001059 httpclient_proxy->srv = httpclient_srv_raw;
1060
1061#ifdef USE_OPENSSL
1062 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +02001063 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +02001064#endif
1065
William Lallemand211c9672021-08-24 17:18:13 +02001066
William Lallemand83614a92021-08-13 14:47:57 +02001067 return 0;
1068
1069err:
1070 ha_alert("httpclient: cannot initialize.\n");
1071 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001072 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +02001073#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +02001074 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +02001075#endif
William Lallemand83614a92021-08-13 14:47:57 +02001076 free_proxy(httpclient_proxy);
1077 return err_code;
1078}
1079
William Lallemand2c8b0842022-04-22 15:16:09 +02001080static int httpclient_postcheck()
William Lallemand83614a92021-08-13 14:47:57 +02001081{
1082 struct logsrv *logsrv;
1083 struct proxy *curproxy = httpclient_proxy;
William Lallemand71e31582022-03-16 15:47:47 +01001084 char *errmsg = NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001085
William Lallemandc6ceba32022-04-22 16:49:53 +02001086 if (global.mode & MODE_MWORKER_WAIT)
1087 return 0;
1088
William Lallemand83614a92021-08-13 14:47:57 +02001089 /* copy logs from "global" log list */
1090 list_for_each_entry(logsrv, &global.logsrvs, list) {
1091 struct logsrv *node = malloc(sizeof(*node));
1092
1093 if (!node) {
1094 ha_alert("httpclient: cannot allocate memory.\n");
1095 goto err;
1096 }
1097
1098 memcpy(node, logsrv, sizeof(*node));
1099 LIST_INIT(&node->list);
1100 LIST_APPEND(&curproxy->logsrvs, &node->list);
Willy Tarreaue1e9f6b2022-04-21 14:14:28 +02001101 node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
1102 node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
William Lallemand83614a92021-08-13 14:47:57 +02001103 }
1104 if (curproxy->conf.logformat_string) {
William Lallemand83614a92021-08-13 14:47:57 +02001105 curproxy->conf.args.ctx = ARGC_LOG;
1106 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
1107 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
William Lallemand715c1012022-03-16 16:39:23 +01001108 SMP_VAL_FE_LOG_END, &errmsg)) {
1109 ha_alert("httpclient: failed to parse log-format : %s.\n", errmsg);
1110 free(errmsg);
William Lallemand83614a92021-08-13 14:47:57 +02001111 goto err;
1112 }
1113 curproxy->conf.args.file = NULL;
1114 curproxy->conf.args.line = 0;
1115 }
William Lallemand71e31582022-03-16 15:47:47 +01001116
1117#ifdef USE_OPENSSL
William Lallemand715c1012022-03-16 16:39:23 +01001118 {
1119 int err_code = 0;
1120
1121 /* init the SNI expression */
1122 /* always use the host header as SNI, without the port */
1123 httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
1124 err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
1125 if (err_code & ERR_CODE) {
1126 ha_alert("httpclient: failed to configure sni: %s.\n", errmsg);
1127 free(errmsg);
1128 goto err;
1129 }
William Lallemand71e31582022-03-16 15:47:47 +01001130 }
1131#endif
1132
William Lallemand83614a92021-08-13 14:47:57 +02001133 return 0;
1134err:
1135 return 1;
1136}
1137
William Lallemand83614a92021-08-13 14:47:57 +02001138/* initialize the proxy and servers for the HTTP client */
1139
William Lallemand2c8b0842022-04-22 15:16:09 +02001140REGISTER_PRE_CHECK(httpclient_precheck);
1141REGISTER_POST_CHECK(httpclient_postcheck);