blob: 69297e1363ab6e0f549ab10974807b8b212a68ec [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>
William Lallemand33b0d092021-08-13 16:05:53 +020030#include <haproxy/stream_interface.h>
William Lallemand83614a92021-08-13 14:47:57 +020031#include <haproxy/tools.h>
32
33#include <string.h>
34
35
36static struct proxy *httpclient_proxy;
37static struct server *httpclient_srv_raw;
William Lallemand957ab132021-08-24 18:33:28 +020038#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +020039static struct server *httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +020040#endif
William Lallemand33b0d092021-08-13 16:05:53 +020041static struct applet httpclient_applet;
42
William Lallemand03a4eb12021-08-18 16:46:21 +020043/* --- This part of the file implement an HTTP client over the CLI ---
44 * The functions will be starting by "hc_cli" for "httpclient cli"
45 */
46
47static struct http_hdr default_httpclient_hdrs[2] = {
William Lallemand2484da52021-08-19 15:55:19 +020048 { .n = IST("User-Agent"), .v = IST("HAProxy") },
William Lallemand03a4eb12021-08-18 16:46:21 +020049 { .n = IST_NULL, .v = IST_NULL },
50};
51
52
53/* What kind of data we need to read */
54#define HC_CLI_F_RES_STLINE 0x01
55#define HC_CLI_F_RES_HDR 0x02
56#define HC_CLI_F_RES_BODY 0x04
57#define HC_CLI_F_RES_END 0x08
58
59
60/* These are the callback used by the HTTP Client when it needs to notify new
61 * data, we only sets a flag in the IO handler */
62
63void hc_cli_res_stline_cb(struct httpclient *hc)
64{
65 struct appctx *appctx = hc->caller;
66
William Lallemanddfc3f892021-08-20 11:35:29 +020067 if (!appctx)
68 return;
69
William Lallemand03a4eb12021-08-18 16:46:21 +020070 appctx->ctx.cli.i0 |= HC_CLI_F_RES_STLINE;
William Lallemanddfc3f892021-08-20 11:35:29 +020071 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020072}
73
74void hc_cli_res_headers_cb(struct httpclient *hc)
75{
76 struct appctx *appctx = hc->caller;
77
William Lallemanddfc3f892021-08-20 11:35:29 +020078 if (!appctx)
79 return;
80
William Lallemand03a4eb12021-08-18 16:46:21 +020081 appctx->ctx.cli.i0 |= HC_CLI_F_RES_HDR;
William Lallemanddfc3f892021-08-20 11:35:29 +020082 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020083}
84
85void hc_cli_res_body_cb(struct httpclient *hc)
86{
87 struct appctx *appctx = hc->caller;
88
William Lallemanddfc3f892021-08-20 11:35:29 +020089 if (!appctx)
90 return;
91
William Lallemand03a4eb12021-08-18 16:46:21 +020092 appctx->ctx.cli.i0 |= HC_CLI_F_RES_BODY;
William Lallemanddfc3f892021-08-20 11:35:29 +020093 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +020094}
95
96void hc_cli_res_end_cb(struct httpclient *hc)
97{
98 struct appctx *appctx = hc->caller;
99
William Lallemanddfc3f892021-08-20 11:35:29 +0200100 if (!appctx)
101 return;
102
William Lallemand03a4eb12021-08-18 16:46:21 +0200103 appctx->ctx.cli.i0 |= HC_CLI_F_RES_END;
William Lallemanddfc3f892021-08-20 11:35:29 +0200104 appctx_wakeup(appctx);
William Lallemand03a4eb12021-08-18 16:46:21 +0200105}
106
107/*
108 * Parse an httpclient keyword on the cli:
109 * httpclient <ID> <method> <URI>
110 */
111static int hc_cli_parse(char **args, char *payload, struct appctx *appctx, void *private)
112{
113 struct httpclient *hc;
114 char *err = NULL;
115 enum http_meth_t meth;
116 char *meth_str;
117 struct ist uri;
118
119 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
120 return 1;
121
122 if (!*args[1] || !*args[2]) {
123 memprintf(&err, ": not enough parameters");
124 goto err;
125 }
126
127 meth_str = args[1];
128 uri = ist(args[2]);
129
130 meth = find_http_meth(meth_str, strlen(meth_str));
131
132 hc = httpclient_new(appctx, meth, uri);
133 if (!hc) {
134 goto err;
135 }
136
137 /* update the httpclient callbacks */
138 hc->ops.res_stline = hc_cli_res_stline_cb;
139 hc->ops.res_headers = hc_cli_res_headers_cb;
140 hc->ops.res_payload = hc_cli_res_body_cb;
141 hc->ops.res_end = hc_cli_res_end_cb;
142
143 appctx->ctx.cli.p0 = hc; /* store the httpclient ptr in the applet */
144 appctx->ctx.cli.i0 = 0;
145
146 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, default_httpclient_hdrs) != ERR_NONE)
147 goto err;
148
149
150 if (!httpclient_start(hc))
151 goto err;
152
153 return 0;
154
155err:
156 memprintf(&err, "Can't start the HTTP client%s.\n", err ? err : "");
157 return cli_err(appctx, err);
158}
159
160/* This function dumps the content of the httpclient receive buffer
161 * on the CLI output
162 *
163 * Return 1 when the processing is finished
164 * return 0 if it needs to be called again
165 */
166static int hc_cli_io_handler(struct appctx *appctx)
167{
168 struct stream_interface *si = appctx->owner;
169 struct buffer *trash = alloc_trash_chunk();
170 struct httpclient *hc = appctx->ctx.cli.p0;
171 struct http_hdr *hdrs, *hdr;
172
173 if (!trash)
174 goto out;
175 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_STLINE) {
176 chunk_appendf(trash, "%s %d %s\n",ist0(hc->res.vsn), hc->res.status, ist0(hc->res.reason));
177 if (ci_putchk(si_ic(si), trash) == -1)
178 si_rx_room_blk(si);
179 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_STLINE;
180 goto out;
181 }
182
183 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_HDR) {
184 hdrs = hc->res.hdrs;
185 for (hdr = hdrs; isttest(hdr->v); hdr++) {
186 if (!h1_format_htx_hdr(hdr->n, hdr->v, trash))
187 goto out;
188 }
189 if (!chunk_memcat(trash, "\r\n", 2))
190 goto out;
191 if (ci_putchk(si_ic(si), trash) == -1)
192 si_rx_room_blk(si);
193 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_HDR;
194 goto out;
195 }
196
197 if (appctx->ctx.cli.i0 & HC_CLI_F_RES_BODY) {
198 int ret;
199
200 ret = httpclient_res_xfer(hc, &si_ic(si)->buf);
201 channel_add_input(si_ic(si), ret); /* forward what we put in the buffer channel */
202
203 if (!b_data(&hc->res.buf)) {/* remove the flag if the buffer was emptied */
204 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_BODY;
205 }
206 goto out;
207 }
208
209 /* we must close only if F_END is the last flag */
210 if (appctx->ctx.cli.i0 == HC_CLI_F_RES_END) {
211 si_shutw(si);
212 si_shutr(si);
213 appctx->ctx.cli.i0 &= ~HC_CLI_F_RES_END;
214 goto out;
215 }
216
217out:
218 /* we didn't clear every flags, we should come back to finish things */
219 if (appctx->ctx.cli.i0)
220 si_rx_room_blk(si);
221
222 free_trash_chunk(trash);
223 return 0;
224}
225
226static void hc_cli_release(struct appctx *appctx)
227{
228 struct httpclient *hc = appctx->ctx.cli.p0;
229
230 /* Everything possible was printed on the CLI, we can destroy the client */
231 httpclient_destroy(hc);
232
233 return;
234}
235
236/* register cli keywords */
237static struct cli_kw_list cli_kws = {{ },{
238 { { "httpclient", NULL }, "httpclient <method> <URI> : launch an HTTP request", hc_cli_parse, hc_cli_io_handler, hc_cli_release},
239 { { NULL }, NULL, NULL, NULL }
240}};
241
242INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
243
244
245/* --- This part of the file implements the actual HTTP client API --- */
246
William Lallemand33b0d092021-08-13 16:05:53 +0200247/*
248 * Generate a simple request and fill the httpclient request buffer with it.
249 * The request contains a request line generated from the absolute <url> and
250 * <meth> as well as list of headers <hdrs>.
251 *
252 * If the buffer was filled correctly the function returns 0, if not it returns
253 * an error_code but there is no guarantee that the buffer wasn't modified.
254 */
255int httpclient_req_gen(struct httpclient *hc, const struct ist url, enum http_meth_t meth, const struct http_hdr *hdrs)
256{
257 struct htx_sl *sl;
258 struct htx *htx;
259 int err_code = 0;
260 struct ist meth_ist, vsn;
261 unsigned int flags = HTX_SL_F_VER_11 | HTX_SL_F_BODYLESS | HTX_SL_F_XFER_LEN | HTX_SL_F_NORMALIZED_URI | HTX_SL_F_HAS_SCHM;
262
263 if (meth >= HTTP_METH_OTHER)
264 goto error;
265
266 meth_ist = http_known_methods[meth];
267
268 vsn = ist("HTTP/1.1");
269
270 htx = htx_from_buf(&hc->req.buf);
271 if (!htx)
272 goto error;
273 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth_ist, url, vsn);
274 if (!sl) {
275 goto error;
276 }
277 sl->info.req.meth = meth;
278
279 /* Add Host Header from URL */
William Lallemand4463b172021-08-24 17:53:03 +0200280 if (!htx_add_header(htx, ist("Host"), IST_NULL))
281 goto error;
William Lallemand33b0d092021-08-13 16:05:53 +0200282 if (!http_update_host(htx, sl, url))
283 goto error;
284
285 /* add the headers and EOH */
William Lallemand79a34782021-09-20 16:19:15 +0200286 if (hdrs) {
287 if (!htx_add_all_headers(htx, hdrs))
288 goto error;
289 } else {
290 if (!htx_add_endof(htx, HTX_BLK_EOH))
291 goto error;
292 }
William Lallemand33b0d092021-08-13 16:05:53 +0200293
294 htx->flags |= HTX_FL_EOM;
295
296 htx_to_buf(htx, &hc->req.buf);
297
298 return 0;
299error:
300 err_code |= ERR_ALERT | ERR_ABORT;
301 return err_code;
302}
303
304/*
305 * transfer the response to the destination buffer and wakeup the HTTP client
306 * applet so it could fill again its buffer.
307 *
308 * Return the number of bytes transfered.
309 */
310int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
311{
312 int ret;
313
314 ret = b_xfer(dst, &hc->res.buf, MIN(1024, b_data(&hc->res.buf)));
315 /* call the client once we consumed all data */
316 if (!b_data(&hc->res.buf) && hc->appctx)
317 appctx_wakeup(hc->appctx);
318 return ret;
319}
320
321/*
322 * Start the HTTP client
323 * Create the appctx, session, stream and wakeup the applet
324 *
325 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
326 * in the URL are supported, it lacks a resolver.
327 *
328 * Return the <appctx> or NULL if it failed
329 */
330struct appctx *httpclient_start(struct httpclient *hc)
331{
332 struct applet *applet = &httpclient_applet;
333 struct appctx *appctx;
334 struct session *sess;
335 struct stream *s;
336 int len;
337 struct split_url out;
338
339 /* parse URI and fill sockaddr_storage */
340 /* FIXME: use a resolver */
341 len = url2sa(ist0(hc->req.url), istlen(hc->req.url), &hc->dst, &out);
342 if (len == -1) {
343 ha_alert("httpclient: cannot parse uri '%s'.\n", ist0(hc->req.url));
344 goto out;
345 }
346
347 /* The HTTP client will be created in the same thread as the caller,
348 * avoiding threading issues */
Willy Tarreaue6124462021-09-13 10:07:38 +0200349 appctx = appctx_new(applet);
William Lallemand33b0d092021-08-13 16:05:53 +0200350 if (!appctx)
351 goto out;
352
353 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
354 if (!sess) {
355 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
356 goto out_free_appctx;
357 }
358 if ((s = stream_new(sess, &appctx->obj_type, &BUF_NULL)) == NULL) {
359 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
360 goto out_free_appctx;
361 }
362
363 if (!sockaddr_alloc(&s->target_addr, &hc->dst, sizeof(hc->dst))) {
364 ha_alert("httpclient: Failed to initialize stream in %s:%d.\n", __FUNCTION__, __LINE__);
365 goto out_free_stream;
366 }
367
368 /* choose the SSL server or not */
369 switch (out.scheme) {
370 case SCH_HTTP:
371 s->target = &httpclient_srv_raw->obj_type;
372 break;
373 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200374#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200375 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200376#else
377 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
378 goto out_free_stream;
379#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200380 break;
381 }
382
383 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
384 s->si[1].flags |= SI_FL_NOLINGER;
385 s->res.flags |= CF_READ_DONTWAIT;
386
387 /* applet is waiting for data */
388 si_cant_get(&s->si[0]);
389 appctx_wakeup(appctx);
390
391 task_wakeup(s->task, TASK_WOKEN_INIT);
392 hc->appctx = appctx;
393 appctx->ctx.httpclient.ptr = hc;
394 appctx->st0 = HTTPCLIENT_S_REQ;
395
396 return appctx;
397
398out_free_stream:
399 LIST_DELETE(&s->list);
400 pool_free(pool_head_stream, s);
401out_free_sess:
402 session_free(sess);
403out_free_appctx:
404 appctx_free(appctx);
405out:
406
407 return NULL;
408}
409
410/* Free the httpclient */
411void httpclient_destroy(struct httpclient *hc)
412{
413 if (!hc)
414 return;
415 b_free(&hc->req.buf);
416 b_free(&hc->res.buf);
417 free(hc);
418
419 return;
420}
421
422/* Allocate an httpclient and its buffers
423 * Return NULL on failure */
424struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
425{
426 struct httpclient *hc;
427 struct buffer *b;
428
429 hc = calloc(1, sizeof(*hc));
430 if (!hc)
431 goto err;
432
433 b = b_alloc(&hc->req.buf);
434 if (!b)
435 goto err;
436 b = b_alloc(&hc->res.buf);
437 if (!b)
438 goto err;
439
440 hc->caller = caller;
441 hc->req.url = url;
442 hc->req.meth = meth;
443
444 return hc;
445
446err:
447 httpclient_destroy(hc);
448 return NULL;
449}
450
451static void httpclient_applet_io_handler(struct appctx *appctx)
452{
453 struct httpclient *hc = appctx->ctx.httpclient.ptr;
454 struct stream_interface *si = appctx->owner;
455 struct stream *s = si_strm(si);
456 struct channel *req = &s->req;
457 struct channel *res = &s->res;
458 struct htx_blk *blk = NULL;
459 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200460 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200461 int32_t pos;
462 uint32_t hdr_num;
463
464
465 while (1) {
466 switch(appctx->st0) {
467
468 case HTTPCLIENT_S_REQ:
469 /* copy the request from the hc->req.buf buffer */
470 htx = htx_from_buf(&req->buf);
471 /* We now that it fits the content of a buffer so can
472 * just push this entirely */
473 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
474 channel_add_input(req, b_data(&req->buf));
475 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
476 goto more; /* we need to leave the IO handler once we wrote the request */
477 break;
478
479 case HTTPCLIENT_S_RES_STLINE:
480 /* copy the start line in the hc structure,then remove the htx block */
481 if (!b_data(&res->buf))
482 goto more;
483 htx = htxbuf(&res->buf);
484 if (!htx)
485 goto more;
486 blk = htx_get_first_blk(htx);
487 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
488 sl = htx_get_blk_ptr(htx, blk);
489 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
490 goto more;
491
492 /* copy the status line in the httpclient */
493 hc->res.status = sl->info.res.status;
494 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
495 hc->res.reason = istdup(htx_sl_res_reason(sl));
496 co_htx_remove_blk(res, htx, blk);
497 /* caller callback */
498 if (hc->ops.res_stline)
499 hc->ops.res_stline(hc);
500
501 /* if there is no HTX data anymore and the EOM flag is
502 * set, leave (no body) */
503 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
504 appctx->st0 = HTTPCLIENT_S_RES_END;
505 else
506 appctx->st0 = HTTPCLIENT_S_RES_HDR;
507 break;
508
509 case HTTPCLIENT_S_RES_HDR:
510 /* first copy the headers in a local hdrs
511 * structure, once we the total numbers of the
512 * header we allocate the right size and copy
513 * them. The htx block of the headers are
514 * removed each time one is read */
515 {
516 struct http_hdr hdrs[global.tune.max_http_hdr];
517
518 if (!b_data(&res->buf))
519 goto more;
520 htx = htxbuf(&res->buf);
521 if (!htx)
522 goto more;
523
524 hdr_num = 0;
525
526 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
527 struct htx_blk *blk = htx_get_blk(htx, pos);
528 enum htx_blk_type type = htx_get_blk_type(blk);
529
530 if (type == HTX_BLK_EOH) {
531 hdrs[hdr_num].n = IST_NULL;
532 hdrs[hdr_num].v = IST_NULL;
533 co_htx_remove_blk(res, htx, blk);
534 break;
535 }
536
537 if (type != HTX_BLK_HDR)
538 continue;
539
540 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
541 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
542 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
543 goto end;
544 co_htx_remove_blk(res, htx, blk);
545 hdr_num++;
546 }
547
William Lallemand0d6f7792021-08-20 11:59:49 +0200548 if (hdr_num) {
549 /* alloc and copy the headers in the httpclient struct */
550 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
551 if (!hc->res.hdrs)
552 goto end;
553 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200554
William Lallemand0d6f7792021-08-20 11:59:49 +0200555 /* caller callback */
556 if (hc->ops.res_headers)
557 hc->ops.res_headers(hc);
558 }
William Lallemand33b0d092021-08-13 16:05:53 +0200559
560 /* if there is no HTX data anymore and the EOM flag is
561 * set, leave (no body) */
562 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
563 appctx->st0 = HTTPCLIENT_S_RES_END;
564 else
565 appctx->st0 = HTTPCLIENT_S_RES_BODY;
566 }
567 break;
568
569 case HTTPCLIENT_S_RES_BODY:
570 /*
571 * The IO handler removes the htx blocks in the response buffer and
572 * push them in the hc->res.buf buffer in a raw format.
573 */
574 htx = htxbuf(&res->buf);
575 if (!htx || htx_is_empty(htx))
576 goto more;
577
578 if (b_full(&hc->res.buf))
579 goto process_data;
580
581 /* decapsule the htx data to raw data */
582 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
583 enum htx_blk_type type;
584
585 blk = htx_get_blk(htx, pos);
586 type = htx_get_blk_type(blk);
587 if (type == HTX_BLK_DATA) {
588 struct ist v = htx_get_blk_value(htx, blk);
589
590 if ((b_room(&hc->res.buf) < v.len) )
591 goto process_data;
592
593 __b_putblk(&hc->res.buf, v.ptr, v.len);
594 co_htx_remove_blk(res, htx, blk);
595 /* the data must be processed by the caller in the receive phase */
596 if (hc->ops.res_payload)
597 hc->ops.res_payload(hc);
598 } else {
599 /* remove any block which is not a data block */
600 co_htx_remove_blk(res, htx, blk);
601 }
602 }
603 /* if not finished, should be called again */
604 if (!(htx->flags & HTX_FL_EOM))
605 goto more;
606
607 /* end of message, we should quit */
608 appctx->st0 = HTTPCLIENT_S_RES_END;
609 break;
610
611 case HTTPCLIENT_S_RES_END:
612 goto end;
613 break;
614 }
615 }
616
617process_data:
618
619 si_rx_chan_rdy(si);
620
621 return;
622more:
623 /* There was not enough data in the response channel */
624
625 si_rx_room_blk(si);
626
627 if (appctx->st0 == HTTPCLIENT_S_RES_END)
628 goto end;
629
630 /* The state machine tries to handle as much data as possible, if there
631 * isn't any data to handle and a shutdown is detected, let's stop
632 * everything */
633 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
634 (res->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
635 goto end;
636 }
637 return;
638
639end:
640 if (hc->ops.res_end)
641 hc->ops.res_end(hc);
642 si_shutw(si);
643 si_shutr(si);
644 return;
645}
646
647static void httpclient_applet_release(struct appctx *appctx)
648{
649 struct httpclient *hc = appctx->ctx.httpclient.ptr;
650
651 /* the applet is leaving, remove the ptr so we don't try to call it
652 * again from the caller */
653 hc->appctx = NULL;
654
655 return;
656}
657
658/* HTTP client applet */
659static struct applet httpclient_applet = {
660 .obj_type = OBJ_TYPE_APPLET,
661 .name = "<HTTPCLIENT>",
662 .fct = httpclient_applet_io_handler,
663 .release = httpclient_applet_release,
664};
665
William Lallemand83614a92021-08-13 14:47:57 +0200666/*
667 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
668 * the other for HTTPS.
669 */
670
671static int httpclient_init()
672{
673 int err_code = 0;
674 char *errmsg = NULL;
675
676 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
677 if (!httpclient_proxy) {
678 err_code |= ERR_ALERT | ERR_FATAL;
679 goto err;
680 }
681
Willy Tarreau0e72e402021-08-20 10:23:12 +0200682 proxy_preset_defaults(httpclient_proxy);
683
William Lallemand83614a92021-08-13 14:47:57 +0200684 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
685 httpclient_proxy->mode = PR_MODE_HTTP;
686 httpclient_proxy->maxconn = 0;
687 httpclient_proxy->accept = NULL;
688 httpclient_proxy->timeout.client = TICK_ETERNITY;
689 /* The HTTP Client use the "option httplog" with the global log server */
690 httpclient_proxy->conf.logformat_string = default_http_log_format;
691 httpclient_proxy->http_needed = 1;
692
693 /* clear HTTP server */
694 httpclient_srv_raw = new_server(httpclient_proxy);
695 if (!httpclient_srv_raw) {
696 err_code |= ERR_ALERT | ERR_FATAL;
697 memprintf(&errmsg, "out of memory.");
698 goto err;
699 }
700
701 httpclient_srv_raw->iweight = 0;
702 httpclient_srv_raw->uweight = 0;
703 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
704 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
705 if (!httpclient_srv_raw->id)
706 goto err;
707
William Lallemand957ab132021-08-24 18:33:28 +0200708#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +0200709 /* SSL HTTP server */
710 httpclient_srv_ssl = new_server(httpclient_proxy);
711 if (!httpclient_srv_ssl) {
712 memprintf(&errmsg, "out of memory.");
713 err_code |= ERR_ALERT | ERR_FATAL;
714 goto err;
715 }
716 httpclient_srv_ssl->iweight = 0;
717 httpclient_srv_ssl->uweight = 0;
718 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
719 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +0200720 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +0200721 if (!httpclient_srv_ssl->id)
722 goto err;
723
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200724 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +0200725#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200726
William Lallemand83614a92021-08-13 14:47:57 +0200727 /* add the proxy in the proxy list only if everything successed */
728 httpclient_proxy->next = proxies_list;
729 proxies_list = httpclient_proxy;
730
William Lallemand211c9672021-08-24 17:18:13 +0200731 /* link the 2 servers in the proxy */
732 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +0200733 httpclient_proxy->srv = httpclient_srv_raw;
734
735#ifdef USE_OPENSSL
736 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +0200737 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +0200738#endif
739
William Lallemand211c9672021-08-24 17:18:13 +0200740
William Lallemand83614a92021-08-13 14:47:57 +0200741 return 0;
742
743err:
744 ha_alert("httpclient: cannot initialize.\n");
745 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200746 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +0200747#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200748 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +0200749#endif
William Lallemand83614a92021-08-13 14:47:57 +0200750 free_proxy(httpclient_proxy);
751 return err_code;
752}
753
William Lallemand83614a92021-08-13 14:47:57 +0200754static int httpclient_cfg_postparser()
755{
756 struct logsrv *logsrv;
757 struct proxy *curproxy = httpclient_proxy;
758
759 /* copy logs from "global" log list */
760 list_for_each_entry(logsrv, &global.logsrvs, list) {
761 struct logsrv *node = malloc(sizeof(*node));
762
763 if (!node) {
764 ha_alert("httpclient: cannot allocate memory.\n");
765 goto err;
766 }
767
768 memcpy(node, logsrv, sizeof(*node));
769 LIST_INIT(&node->list);
770 LIST_APPEND(&curproxy->logsrvs, &node->list);
771 }
772 if (curproxy->conf.logformat_string) {
773 char *err = NULL;
774
775 curproxy->conf.args.ctx = ARGC_LOG;
776 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
777 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
778 SMP_VAL_FE_LOG_END, &err)) {
779 ha_alert("httpclient: failed to parse log-format : %s.\n", err);
780 free(err);
781 goto err;
782 }
783 curproxy->conf.args.file = NULL;
784 curproxy->conf.args.line = 0;
785 }
786 return 0;
787err:
788 return 1;
789}
790
William Lallemand83614a92021-08-13 14:47:57 +0200791/* initialize the proxy and servers for the HTTP client */
792
793INITCALL0(STG_REGISTER, httpclient_init);
794REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);