blob: 7756e821176cb1f7080f9f424cd50fb2b02ad2c7 [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 Lallemand33b0d092021-08-13 16:05:53 +020022#include <haproxy/h1_htx.h>
23#include <haproxy/http.h>
24#include <haproxy/http_client.h>
25#include <haproxy/http_htx.h>
26#include <haproxy/htx.h>
William Lallemand83614a92021-08-13 14:47:57 +020027#include <haproxy/log.h>
28#include <haproxy/proxy.h>
William Lallemand2a8fe8b2021-08-20 14:25:15 +020029#include <haproxy/server.h>
Willy Tarreau1df20422021-10-06 11:28:24 +020030#include <haproxy/ssl_sock-t.h>
William Lallemand33b0d092021-08-13 16:05:53 +020031#include <haproxy/stream_interface.h>
William Lallemand83614a92021-08-13 14:47:57 +020032#include <haproxy/tools.h>
33
34#include <string.h>
35
36
37static struct proxy *httpclient_proxy;
38static struct server *httpclient_srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +020039#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +020040static struct server *httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +020041#endif
William Lallemand33b0d092021-08-13 16:05:53 +020042static struct applet httpclient_applet;
43
William Lallemand03a4eb12021-08-18 16:46:21 +020044/* --- This part of the file implement an HTTP client over the CLI ---
45 * The functions will be starting by "hc_cli" for "httpclient cli"
46 */
47
48static struct http_hdr default_httpclient_hdrs[2] = {
William Lallemand2484da52021-08-19 15:55:19 +020049 { .n = IST("User-Agent"), .v = IST("HAProxy") },
William Lallemand03a4eb12021-08-18 16:46:21 +020050 { .n = IST_NULL, .v = IST_NULL },
51};
52
53
54/* What kind of data we need to read */
55#define HC_CLI_F_RES_STLINE 0x01
56#define HC_CLI_F_RES_HDR 0x02
57#define HC_CLI_F_RES_BODY 0x04
58#define HC_CLI_F_RES_END 0x08
59
60
61/* These are the callback used by the HTTP Client when it needs to notify new
62 * data, we only sets a flag in the IO handler */
63
64void hc_cli_res_stline_cb(struct httpclient *hc)
65{
66 struct appctx *appctx = hc->caller;
67
William Lallemanddfc3f892021-08-20 11:35:29 +020068 if (!appctx)
69 return;
70
William Lallemand03a4eb12021-08-18 16:46:21 +020071 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020072 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020073}
74
75void hc_cli_res_headers_cb(struct httpclient *hc)
76{
77 struct appctx *appctx = hc->caller;
78
William Lallemanddfc3f892021-08-20 11:35:29 +020079 if (!appctx)
80 return;
81
William Lallemand03a4eb12021-08-18 16:46:21 +020082 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020083 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020084}
85
86void hc_cli_res_body_cb(struct httpclient *hc)
87{
88 struct appctx *appctx = hc->caller;
89
William Lallemanddfc3f892021-08-20 11:35:29 +020090 if (!appctx)
91 return;
92
William Lallemand03a4eb12021-08-18 16:46:21 +020093 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020094 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020095}
96
97void hc_cli_res_end_cb(struct httpclient *hc)
98{
99 struct appctx *appctx = hc->caller;
100
William Lallemanddfc3f892021-08-20 11:35:29 +0200101 if (!appctx)
102 return;
103
William Lallemand03a4eb12021-08-18 16:46:21 +0200104 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200105 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200106}
107
108/*
109 * Parse an httpclient keyword on the cli:
110 * httpclient <ID> <method> <URI>
111 */
112static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
113{
114 struct httpclient *hc;
115 char *err = NULL;
116 enum http_meth_t meth;
117 char *meth_str;
118 struct ist uri;
William Lallemanddec25c32021-10-25 19:48:37 +0200119 struct ist body = IST_NULL;
William Lallemand03a4eb12021-08-18 16:46:21 +0200120
121 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
122 return 1;
123
124 if (!*args[1] || !*args[2]) {
125 memprintf(&err, ": not enough parameters");
126 goto err;
127 }
128
129 meth_str = args[1];
130 uri = ist(args[2]);
131
William Lallemanddec25c32021-10-25 19:48:37 +0200132 if (payload)
133 body = ist(payload);
134
William Lallemand03a4eb12021-08-18 16:46:21 +0200135 meth = find_http_meth(meth_str, strlen(meth_str));
136
137 hc = httpclient_new(appctx, meth, uri);
138 if (!hc) {
139 goto err;
140 }
141
142 /* update the httpclient callbacks */
143 hc->ops.res_stline = hc_cli_res_stline_cb;
144 hc->ops.res_headers = hc_cli_res_headers_cb;
145 hc->ops.res_payload = hc_cli_res_body_cb;
146 hc->ops.res_end = hc_cli_res_end_cb;
147
148 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
149 appctx->ctx.cli.i0 = 0;
150
William Lallemanddec25c32021-10-25 19:48:37 +0200151 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, default_httpclient_hdrs, body) != ERR_NONE)
William Lallemand03a4eb12021-08-18 16:46:21 +0200152 goto err;
153
154
155 if (!httpclient_start(hc))
156 goto err;
157
158 return 0;
159
160err:
161 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
162 return cli_err(appctx, err);
163}
164
165/* This function dumps the content of the httpclient receive buffer
166 * on the CLI output
167 *
168 * Return 1 when the processing is finished
169 * return 0 if it needs to be called again
170 */
171static int hc_cli_io_handler(struct appctx *appctx)
172{
173 struct stream_interface *si = appctx->owner;
174 struct buffer *trash = alloc_trash_chunk();
175 struct httpclient *hc = appctx->ctx.cli.p0;
176 struct http_hdr *hdrs, *hdr;
177
178 if (!trash)
179 goto out;
180 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
William Lallemand614e6832021-09-26 18:12:43 +0200181 chunk_appendf(trash, "%s %d %s\n",istptr(hc->res.vsn), hc->res.status, istptr(hc->res.reason));
William Lallemand03a4eb12021-08-18 16:46:21 +0200182 if (ci_putchk(si_ic(si), trash) == -1)
183 si_rx_room_blk(si);
184 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
185 goto out;
186 }
187
188 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
189 hdrs = hc->res.hdrs;
190 for (hdr = hdrs; isttest(hdr->v); hdr++) {
191 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
192 goto out;
193 }
194 if (!chunk_memcat(trash, "\r\n", 2))
195 goto out;
196 if (ci_putchk(si_ic(si), trash) == -1)
197 si_rx_room_blk(si);
198 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
199 goto out;
200 }
201
202 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
203 int ret;
204
205 ret = httpclient_res_xfer(hc, &si_ic(si)->buf);
206 channel_add_input(si_ic(si), ret); /* forward what we put in the buffer channel */
207
William Lallemand518878e2021-09-21 10:45:34 +0200208 if (!httpclient_data(hc)) {/* remove the flag if the buffer was emptied */
William Lallemand03a4eb12021-08-18 16:46:21 +0200209 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
210 }
211 goto out;
212 }
213
214 /* we must close only if F_END is the last flag */
215 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
216 si_shutw(si);
217 si_shutr(si);
218 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
219 goto out;
220 }
221
222out:
223 /* we didn't clear every flags, we should come back to finish things */
224 if (appctx->ctx.cli.i0)
225 si_rx_room_blk(si);
226
227 free_trash_chunk(trash);
228 return 0;
229}
230
231static void hc_cli_release(struct appctx *appctx)
232{
233 struct httpclient *hc = appctx->ctx.cli.p0;
234
235 /* Everything possible was printed on the CLI, we can destroy the client */
William Lallemandecb83e12021-09-28 11:00:43 +0200236 httpclient_stop_and_destroy(hc);
William Lallemand03a4eb12021-08-18 16:46:21 +0200237
238 return;
239}
240
241/* register cli keywords */
242static struct cli_kw_list cli_kws = {{ },{
William Lallemand34b3a932021-10-19 10:58:30 +0200243 { { "httpclient", NULL }, "httpclient <method> <URI> : launch an HTTP request", hc_cli_parse, hc_cli_io_handler, hc_cli_release, NULL, ACCESS_EXPERT},
William Lallemand03a4eb12021-08-18 16:46:21 +0200244 { { NULL }, NULL, NULL, NULL }
245}};
246
247INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
248
249
250/* --- This part of the file implements the actual HTTP client API --- */
251
William Lallemand33b0d092021-08-13 16:05:53 +0200252/*
253 * Generate a simple request and fill the httpclient request buffer with it.
254 * The request contains a request line generated from the absolute <url> and
255 * <meth> as well as list of headers <hdrs>.
256 *
257 * If the buffer was filled correctly the function returns 0, if not it returns
258 * an error_code but there is no guarantee that the buffer wasn't modified.
259 */
William Lallemanddec25c32021-10-25 19:48:37 +0200260int httpclient_req_gen(struct httpclient *hc, const struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs, const struct ist payload)
William Lallemand33b0d092021-08-13 16:05:53 +0200261{
262 struct htx_sl *sl;
263 struct htx *htx;
264 int err_code = 0;
265 struct ist meth_ist, vsn;
William Lallemanddec25c32021-10-25 19:48:37 +0200266 unsigned int flags = HTX_SL_F_VER_11 | HTX_SL_F_NORMALIZED_URI | HTX_SL_F_HAS_SCHM;
William Lallemand33b0d092021-08-13 16:05:53 +0200267
268 if (meth >= HTTP_METH_OTHER)
269 goto error;
270
271 meth_ist = http_known_methods[meth];
272
273 vsn = ist("HTTP/1.1");
274
275 htx = htx_from_buf(&hc->req.buf);
276 if (!htx)
277 goto error;
278 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
279 if (!sl) {
280 goto error;
281 }
282 sl->info.req.meth = meth;
283
284 /* Add Host Header from URL */
William Lallemand4463b172021-08-24 17:53:03 +0200285 if (!htx_add_header(htx, ist("Host"), IST_NULL))
286 goto error;
William Lallemand33b0d092021-08-13 16:05:53 +0200287 if (!http_update_host(htx, sl, url))
288 goto error;
289
290 /* add the headers and EOH */
William Lallemand79a34782021-09-20 16:19:15 +0200291 if (hdrs) {
292 if (!htx_add_all_headers(htx, hdrs))
293 goto error;
294 } else {
295 if (!htx_add_endof(htx, HTX_BLK_EOH))
296 goto error;
297 }
William Lallemand33b0d092021-08-13 16:05:53 +0200298
William Lallemanddec25c32021-10-25 19:48:37 +0200299 if (isttest(payload)) {
300 /* add the payload if it can feat in the buffer, no need to set
301 * the Content-Length, the data will be sent chunked */
302 if (!htx_add_data_atonce(htx, payload))
303 goto error;
304 }
305
William Lallemand33b0d092021-08-13 16:05:53 +0200306 htx->flags |= HTX_FL_EOM;
307
308 htx_to_buf(htx, &hc->req.buf);
309
310 return 0;
311error:
312 err_code |= ERR_ALERT | ERR_ABORT;
313 return err_code;
314}
315
316/*
317 * transfer the response to the destination buffer and wakeup the HTTP client
318 * applet so it could fill again its buffer.
319 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500320 * Return the number of bytes transferred.
William Lallemand33b0d092021-08-13 16:05:53 +0200321 */
322int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
323{
324 int ret;
325
William Lallemand4d601842021-09-30 10:07:57 +0200326 ret = b_force_xfer(dst, &hc->res.buf, MIN(1024, b_data(&hc->res.buf)));
William Lallemand33b0d092021-08-13 16:05:53 +0200327 /* call the client once we consumed all data */
328 if (!b_data(&hc->res.buf) && hc->appctx)
329 appctx_wakeup(hc->appctx);
330 return ret;
331}
332
333/*
334 * Start the HTTP client
335 * Create the appctx, session, stream and wakeup the applet
336 *
337 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
338 * in the URL are supported, it lacks a resolver.
339 *
340 * Return the <appctx> or NULL if it failed
341 */
342struct appctx *httpclient_start(struct httpclient *hc)
343{
344 struct applet *applet = &httpclient_applet;
345 struct appctx *appctx;
346 struct session *sess;
347 struct stream *s;
348 int len;
349 struct split_url out;
350
351 /* parse URI and fill sockaddr_storage */
352 /* FIXME: use a resolver */
William Lallemand614e6832021-09-26 18:12:43 +0200353 len = url2sa(istptr(hc->req.url), istlen(hc->req.url), &hc->dst, &out);
William Lallemand33b0d092021-08-13 16:05:53 +0200354 if (len == -1) {
William Lallemand614e6832021-09-26 18:12:43 +0200355 ha_alert("httpclient: cannot parse uri '%s'.\n", istptr(hc->req.url));
William Lallemand33b0d092021-08-13 16:05:53 +0200356 goto out;
357 }
358
359 /* The HTTP client will be created in the same thread as the caller,
360 * avoiding threading issues */
Willy Tarreaue6124462021-09-13 10:07:38 +0200361 appctx = appctx_new(applet);
William Lallemand33b0d092021-08-13 16:05:53 +0200362 if (!appctx)
363 goto out;
364
365 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
366 if (!sess) {
367 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
368 goto out_free_appctx;
369 }
370 if ((s = stream_new(sess, &appctx->obj_type, &BUF_NULL)) == NULL) {
371 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
372 goto out_free_appctx;
373 }
374
Christopher Faulet16f16af2021-10-27 09:34:56 +0200375 if (!sockaddr_alloc(&s->si[1].dst, &hc->dst, sizeof(hc->dst))) {
William Lallemand33b0d092021-08-13 16:05:53 +0200376 ha_alert("httpclient: Failed to initialize stream in %s:%d.\n", __FUNCTION__, __LINE__);
377 goto out_free_stream;
378 }
379
380 /* choose the SSL server or not */
381 switch (out.scheme) {
382 case SCH_HTTP:
383 s->target = &httpclient_srv_raw->obj_type;
384 break;
385 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200386#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200387 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200388#else
389 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
390 goto out_free_stream;
391#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200392 break;
393 }
394
395 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
396 s->si[1].flags |= SI_FL_NOLINGER;
397 s->res.flags |= CF_READ_DONTWAIT;
398
399 /* applet is waiting for data */
400 si_cant_get(&s->si[0]);
401 appctx_wakeup(appctx);
402
403 task_wakeup(s->task, TASK_WOKEN_INIT);
404 hc->appctx = appctx;
William Lallemandb8b13702021-09-28 12:15:37 +0200405 hc->flags |= HTTPCLIENT_FS_STARTED;
William Lallemand33b0d092021-08-13 16:05:53 +0200406 appctx->ctx.httpclient.ptr = hc;
407 appctx->st0 = HTTPCLIENT_S_REQ;
408
409 return appctx;
410
411out_free_stream:
412 LIST_DELETE(&s->list);
413 pool_free(pool_head_stream, s);
414out_free_sess:
415 session_free(sess);
416out_free_appctx:
417 appctx_free(appctx);
418out:
419
420 return NULL;
421}
422
William Lallemandecb83e12021-09-28 11:00:43 +0200423/*
424 * This function tries to destroy the httpclient if it wasn't running.
425 * If it was running, stop the client and ask it to autodestroy itself.
426 *
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500427 * Once this function is used, all pointer sto the client must be removed
William Lallemandecb83e12021-09-28 11:00:43 +0200428 *
429 */
430void httpclient_stop_and_destroy(struct httpclient *hc)
431{
432
William Lallemandb8b13702021-09-28 12:15:37 +0200433 /* The httpclient was already stopped or never started, we can safely destroy it */
434 if (hc->flags & HTTPCLIENT_FS_ENDED || !(hc->flags & HTTPCLIENT_FS_STARTED)) {
William Lallemandecb83e12021-09-28 11:00:43 +0200435 httpclient_destroy(hc);
436 } else {
437 /* if the client wasn't stopped, ask for a stop and destroy */
438 hc->flags |= (HTTPCLIENT_FA_AUTOKILL | HTTPCLIENT_FA_STOP);
439 if (hc->appctx)
440 appctx_wakeup(hc->appctx);
441 }
442}
443
William Lallemand33b0d092021-08-13 16:05:53 +0200444/* Free the httpclient */
445void httpclient_destroy(struct httpclient *hc)
446{
William Lallemand03f5a1c2021-09-27 15:17:47 +0200447 struct http_hdr *hdrs;
448
449
William Lallemand33b0d092021-08-13 16:05:53 +0200450 if (!hc)
451 return;
William Lallemandecb83e12021-09-28 11:00:43 +0200452
William Lallemand2a879002021-10-05 15:50:45 +0200453 /* we should never destroy a client which was started but not stopped */
454 BUG_ON(httpclient_started(hc) && !httpclient_ended(hc));
William Lallemandecb83e12021-09-28 11:00:43 +0200455
William Lallemand03f5a1c2021-09-27 15:17:47 +0200456 /* request */
457 istfree(&hc->req.url);
William Lallemand33b0d092021-08-13 16:05:53 +0200458 b_free(&hc->req.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200459 /* response */
460 istfree(&hc->res.vsn);
461 istfree(&hc->res.reason);
462 hdrs = hc->res.hdrs;
463 while (hdrs && isttest(hdrs->n)) {
464 istfree(&hdrs->n);
465 istfree(&hdrs->v);
466 hdrs++;
467 }
468 ha_free(&hc->res.hdrs);
William Lallemand33b0d092021-08-13 16:05:53 +0200469 b_free(&hc->res.buf);
William Lallemand03f5a1c2021-09-27 15:17:47 +0200470
471
William Lallemand33b0d092021-08-13 16:05:53 +0200472 free(hc);
473
474 return;
475}
476
477/* Allocate an httpclient and its buffers
478 * Return NULL on failure */
479struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
480{
481 struct httpclient *hc;
482 struct buffer *b;
483
484 hc = calloc(1, sizeof(*hc));
485 if (!hc)
486 goto err;
487
488 b = b_alloc(&hc->req.buf);
489 if (!b)
490 goto err;
491 b = b_alloc(&hc->res.buf);
492 if (!b)
493 goto err;
494
495 hc->caller = caller;
496 hc->req.url = url;
497 hc->req.meth = meth;
498
499 return hc;
500
501err:
502 httpclient_destroy(hc);
503 return NULL;
504}
505
506static void httpclient_applet_io_handler(struct appctx *appctx)
507{
508 struct httpclient *hc = appctx->ctx.httpclient.ptr;
509 struct stream_interface *si = appctx->owner;
510 struct stream *s = si_strm(si);
511 struct channel *req = &s->req;
512 struct channel *res = &s->res;
513 struct htx_blk *blk = NULL;
514 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200515 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200516 int32_t pos;
517 uint32_t hdr_num;
518
519
520 while (1) {
William Lallemandecb83e12021-09-28 11:00:43 +0200521
522 /* required to stop */
523 if (hc->flags & HTTPCLIENT_FA_STOP)
524 goto end;
525
William Lallemand33b0d092021-08-13 16:05:53 +0200526 switch(appctx->st0) {
527
528 case HTTPCLIENT_S_REQ:
529 /* copy the request from the hc->req.buf buffer */
530 htx = htx_from_buf(&req->buf);
531 /* We now that it fits the content of a buffer so can
532 * just push this entirely */
533 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
534 channel_add_input(req, b_data(&req->buf));
535 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
536 goto more; /* we need to leave the IO handler once we wrote the request */
537 break;
538
539 case HTTPCLIENT_S_RES_STLINE:
540 /* copy the start line in the hc structure,then remove the htx block */
541 if (!b_data(&res->buf))
542 goto more;
543 htx = htxbuf(&res->buf);
544 if (!htx)
545 goto more;
546 blk = htx_get_first_blk(htx);
547 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
548 sl = htx_get_blk_ptr(htx, blk);
549 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
550 goto more;
551
552 /* copy the status line in the httpclient */
553 hc->res.status = sl->info.res.status;
554 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
555 hc->res.reason = istdup(htx_sl_res_reason(sl));
556 co_htx_remove_blk(res, htx, blk);
557 /* caller callback */
558 if (hc->ops.res_stline)
559 hc->ops.res_stline(hc);
560
561 /* if there is no HTX data anymore and the EOM flag is
562 * set, leave (no body) */
563 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
564 appctx->st0 = HTTPCLIENT_S_RES_END;
565 else
566 appctx->st0 = HTTPCLIENT_S_RES_HDR;
567 break;
568
569 case HTTPCLIENT_S_RES_HDR:
570 /* first copy the headers in a local hdrs
571 * structure, once we the total numbers of the
572 * header we allocate the right size and copy
573 * them. The htx block of the headers are
574 * removed each time one is read */
575 {
576 struct http_hdr hdrs[global.tune.max_http_hdr];
577
578 if (!b_data(&res->buf))
579 goto more;
580 htx = htxbuf(&res->buf);
581 if (!htx)
582 goto more;
583
584 hdr_num = 0;
585
586 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
587 struct htx_blk *blk = htx_get_blk(htx, pos);
588 enum htx_blk_type type = htx_get_blk_type(blk);
589
590 if (type == HTX_BLK_EOH) {
591 hdrs[hdr_num].n = IST_NULL;
592 hdrs[hdr_num].v = IST_NULL;
593 co_htx_remove_blk(res, htx, blk);
594 break;
595 }
596
597 if (type != HTX_BLK_HDR)
598 continue;
599
600 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
601 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
602 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
603 goto end;
604 co_htx_remove_blk(res, htx, blk);
605 hdr_num++;
606 }
607
William Lallemand0d6f7792021-08-20 11:59:49 +0200608 if (hdr_num) {
609 /* alloc and copy the headers in the httpclient struct */
610 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
611 if (!hc->res.hdrs)
612 goto end;
613 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200614
William Lallemand0d6f7792021-08-20 11:59:49 +0200615 /* caller callback */
616 if (hc->ops.res_headers)
617 hc->ops.res_headers(hc);
618 }
William Lallemand33b0d092021-08-13 16:05:53 +0200619
620 /* if there is no HTX data anymore and the EOM flag is
621 * set, leave (no body) */
William Lallemand1123dde2021-09-21 10:58:10 +0200622 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM) {
William Lallemand33b0d092021-08-13 16:05:53 +0200623 appctx->st0 = HTTPCLIENT_S_RES_END;
William Lallemand1123dde2021-09-21 10:58:10 +0200624 } else {
William Lallemand33b0d092021-08-13 16:05:53 +0200625 appctx->st0 = HTTPCLIENT_S_RES_BODY;
William Lallemand1123dde2021-09-21 10:58:10 +0200626 }
William Lallemand33b0d092021-08-13 16:05:53 +0200627 }
628 break;
629
630 case HTTPCLIENT_S_RES_BODY:
631 /*
632 * The IO handler removes the htx blocks in the response buffer and
633 * push them in the hc->res.buf buffer in a raw format.
634 */
635 htx = htxbuf(&res->buf);
636 if (!htx || htx_is_empty(htx))
637 goto more;
638
639 if (b_full(&hc->res.buf))
640 goto process_data;
641
642 /* decapsule the htx data to raw data */
643 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
644 enum htx_blk_type type;
645
646 blk = htx_get_blk(htx, pos);
647 type = htx_get_blk_type(blk);
648 if (type == HTX_BLK_DATA) {
649 struct ist v = htx_get_blk_value(htx, blk);
650
651 if ((b_room(&hc->res.buf) < v.len) )
652 goto process_data;
653
654 __b_putblk(&hc->res.buf, v.ptr, v.len);
655 co_htx_remove_blk(res, htx, blk);
656 /* the data must be processed by the caller in the receive phase */
657 if (hc->ops.res_payload)
658 hc->ops.res_payload(hc);
659 } else {
660 /* remove any block which is not a data block */
661 co_htx_remove_blk(res, htx, blk);
662 }
663 }
664 /* if not finished, should be called again */
665 if (!(htx->flags & HTX_FL_EOM))
666 goto more;
667
668 /* end of message, we should quit */
669 appctx->st0 = HTTPCLIENT_S_RES_END;
670 break;
671
672 case HTTPCLIENT_S_RES_END:
673 goto end;
674 break;
675 }
676 }
677
678process_data:
679
680 si_rx_chan_rdy(si);
681
682 return;
683more:
684 /* There was not enough data in the response channel */
685
686 si_rx_room_blk(si);
687
688 if (appctx->st0 == HTTPCLIENT_S_RES_END)
689 goto end;
690
691 /* The state machine tries to handle as much data as possible, if there
692 * isn't any data to handle and a shutdown is detected, let's stop
693 * everything */
694 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
695 (res->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
696 goto end;
697 }
698 return;
699
700end:
701 if (hc->ops.res_end)
702 hc->ops.res_end(hc);
703 si_shutw(si);
704 si_shutr(si);
705 return;
706}
707
708static void httpclient_applet_release(struct appctx *appctx)
709{
710 struct httpclient *hc = appctx->ctx.httpclient.ptr;
711
William Lallemand1123dde2021-09-21 10:58:10 +0200712 /* mark the httpclient as ended */
William Lallemandecb83e12021-09-28 11:00:43 +0200713 hc->flags |= HTTPCLIENT_FS_ENDED;
William Lallemand33b0d092021-08-13 16:05:53 +0200714 /* the applet is leaving, remove the ptr so we don't try to call it
715 * again from the caller */
716 hc->appctx = NULL;
717
William Lallemandecb83e12021-09-28 11:00:43 +0200718
719 /* destroy the httpclient when set to autotokill */
720 if (hc->flags & HTTPCLIENT_FA_AUTOKILL) {
721 httpclient_destroy(hc);
722 }
723
William Lallemand33b0d092021-08-13 16:05:53 +0200724 return;
725}
726
727/* HTTP client applet */
728static struct applet httpclient_applet = {
729 .obj_type = OBJ_TYPE_APPLET,
730 .name = "<HTTPCLIENT>",
731 .fct = httpclient_applet_io_handler,
732 .release = httpclient_applet_release,
733};
734
William Lallemand83614a92021-08-13 14:47:57 +0200735/*
736 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
737 * the other for HTTPS.
738 */
739
740static int httpclient_init()
741{
742 int err_code = 0;
743 char *errmsg = NULL;
744
745 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
746 if (!httpclient_proxy) {
747 err_code |= ERR_ALERT | ERR_FATAL;
748 goto err;
749 }
750
Willy Tarreau0e72e402021-08-20 10:23:12 +0200751 proxy_preset_defaults(httpclient_proxy);
752
William Lallemand83614a92021-08-13 14:47:57 +0200753 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
754 httpclient_proxy->mode = PR_MODE_HTTP;
755 httpclient_proxy->maxconn = 0;
756 httpclient_proxy->accept = NULL;
757 httpclient_proxy->timeout.client = TICK_ETERNITY;
758 /* The HTTP Client use the "option httplog" with the global log server */
759 httpclient_proxy->conf.logformat_string = default_http_log_format;
760 httpclient_proxy->http_needed = 1;
761
762 /* clear HTTP server */
763 httpclient_srv_raw = new_server(httpclient_proxy);
764 if (!httpclient_srv_raw) {
765 err_code |= ERR_ALERT | ERR_FATAL;
766 memprintf(&errmsg, "out of memory.");
767 goto err;
768 }
769
770 httpclient_srv_raw->iweight = 0;
771 httpclient_srv_raw->uweight = 0;
772 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
773 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
774 if (!httpclient_srv_raw->id)
775 goto err;
776
William Lallemand957ab132021-08-24 18:33:28 +0200777#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +0200778 /* SSL HTTP server */
779 httpclient_srv_ssl = new_server(httpclient_proxy);
780 if (!httpclient_srv_ssl) {
781 memprintf(&errmsg, "out of memory.");
782 err_code |= ERR_ALERT | ERR_FATAL;
783 goto err;
784 }
785 httpclient_srv_ssl->iweight = 0;
786 httpclient_srv_ssl->uweight = 0;
787 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
788 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +0200789 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +0200790 if (!httpclient_srv_ssl->id)
791 goto err;
792
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200793 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +0200794#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200795
Ilya Shipitsinbd6b4be2021-10-15 16:18:21 +0500796 /* add the proxy in the proxy list only if everything is successful */
William Lallemand83614a92021-08-13 14:47:57 +0200797 httpclient_proxy->next = proxies_list;
798 proxies_list = httpclient_proxy;
799
William Lallemand211c9672021-08-24 17:18:13 +0200800 /* link the 2 servers in the proxy */
801 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +0200802 httpclient_proxy->srv = httpclient_srv_raw;
803
804#ifdef USE_OPENSSL
805 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +0200806 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +0200807#endif
808
William Lallemand211c9672021-08-24 17:18:13 +0200809
William Lallemand83614a92021-08-13 14:47:57 +0200810 return 0;
811
812err:
813 ha_alert("httpclient: cannot initialize.\n");
814 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200815 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +0200816#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200817 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +0200818#endif
William Lallemand83614a92021-08-13 14:47:57 +0200819 free_proxy(httpclient_proxy);
820 return err_code;
821}
822
William Lallemand83614a92021-08-13 14:47:57 +0200823static int httpclient_cfg_postparser()
824{
825 struct logsrv *logsrv;
826 struct proxy *curproxy = httpclient_proxy;
827
828 /* copy logs from "global" log list */
829 list_for_each_entry(logsrv, &global.logsrvs, list) {
830 struct logsrv *node = malloc(sizeof(*node));
831
832 if (!node) {
833 ha_alert("httpclient: cannot allocate memory.\n");
834 goto err;
835 }
836
837 memcpy(node, logsrv, sizeof(*node));
838 LIST_INIT(&node->list);
839 LIST_APPEND(&curproxy->logsrvs, &node->list);
840 }
841 if (curproxy->conf.logformat_string) {
842 char *err = NULL;
843
844 curproxy->conf.args.ctx = ARGC_LOG;
845 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
846 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
847 SMP_VAL_FE_LOG_END, &err)) {
848 ha_alert("httpclient: failed to parse log-format : %s.\n", err);
849 free(err);
850 goto err;
851 }
852 curproxy->conf.args.file = NULL;
853 curproxy->conf.args.line = 0;
854 }
855 return 0;
856err:
857 return 1;
858}
859
William Lallemand83614a92021-08-13 14:47:57 +0200860/* initialize the proxy and servers for the HTTP client */
861
862INITCALL0(STG_REGISTER, httpclient_init);
863REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);