blob: debdee5d36be05f2fafbe4caebbd78a4087683e2 [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 */
286 if (hdrs && !htx_add_all_headers(htx, hdrs))
287 goto error;
288
289 htx->flags |= HTX_FL_EOM;
290
291 htx_to_buf(htx, &hc->req.buf);
292
293 return 0;
294error:
295 err_code |= ERR_ALERT | ERR_ABORT;
296 return err_code;
297}
298
299/*
300 * transfer the response to the destination buffer and wakeup the HTTP client
301 * applet so it could fill again its buffer.
302 *
303 * Return the number of bytes transfered.
304 */
305int httpclient_res_xfer(struct httpclient *hc, struct buffer *dst)
306{
307 int ret;
308
309 ret = b_xfer(dst, &hc->res.buf, MIN(1024, b_data(&hc->res.buf)));
310 /* call the client once we consumed all data */
311 if (!b_data(&hc->res.buf) && hc->appctx)
312 appctx_wakeup(hc->appctx);
313 return ret;
314}
315
316/*
317 * Start the HTTP client
318 * Create the appctx, session, stream and wakeup the applet
319 *
320 * FIXME: It also fill the sockaddr with the IP address, but currently only IP
321 * in the URL are supported, it lacks a resolver.
322 *
323 * Return the <appctx> or NULL if it failed
324 */
325struct appctx *httpclient_start(struct httpclient *hc)
326{
327 struct applet *applet = &httpclient_applet;
328 struct appctx *appctx;
329 struct session *sess;
330 struct stream *s;
331 int len;
332 struct split_url out;
333
334 /* parse URI and fill sockaddr_storage */
335 /* FIXME: use a resolver */
336 len = url2sa(ist0(hc->req.url), istlen(hc->req.url), &hc->dst, &out);
337 if (len == -1) {
338 ha_alert("httpclient: cannot parse uri '%s'.\n", ist0(hc->req.url));
339 goto out;
340 }
341
342 /* The HTTP client will be created in the same thread as the caller,
343 * avoiding threading issues */
Willy Tarreaue6124462021-09-13 10:07:38 +0200344 appctx = appctx_new(applet);
William Lallemand33b0d092021-08-13 16:05:53 +0200345 if (!appctx)
346 goto out;
347
348 sess = session_new(httpclient_proxy, NULL, &appctx->obj_type);
349 if (!sess) {
350 ha_alert("httpclient: out of memory in %s:%d.\n", __FUNCTION__, __LINE__);
351 goto out_free_appctx;
352 }
353 if ((s = stream_new(sess, &appctx->obj_type, &BUF_NULL)) == NULL) {
354 ha_alert("httpclient: Failed to initialize stream %s:%d.\n", __FUNCTION__, __LINE__);
355 goto out_free_appctx;
356 }
357
358 if (!sockaddr_alloc(&s->target_addr, &hc->dst, sizeof(hc->dst))) {
359 ha_alert("httpclient: Failed to initialize stream in %s:%d.\n", __FUNCTION__, __LINE__);
360 goto out_free_stream;
361 }
362
363 /* choose the SSL server or not */
364 switch (out.scheme) {
365 case SCH_HTTP:
366 s->target = &httpclient_srv_raw->obj_type;
367 break;
368 case SCH_HTTPS:
William Lallemand957ab132021-08-24 18:33:28 +0200369#ifdef USE_OPENSSL
William Lallemand33b0d092021-08-13 16:05:53 +0200370 s->target = &httpclient_srv_ssl->obj_type;
William Lallemand957ab132021-08-24 18:33:28 +0200371#else
372 ha_alert("httpclient: OpenSSL is not available %s:%d.\n", __FUNCTION__, __LINE__);
373 goto out_free_stream;
374#endif
William Lallemand33b0d092021-08-13 16:05:53 +0200375 break;
376 }
377
378 s->flags |= SF_ASSIGNED|SF_ADDR_SET;
379 s->si[1].flags |= SI_FL_NOLINGER;
380 s->res.flags |= CF_READ_DONTWAIT;
381
382 /* applet is waiting for data */
383 si_cant_get(&s->si[0]);
384 appctx_wakeup(appctx);
385
386 task_wakeup(s->task, TASK_WOKEN_INIT);
387 hc->appctx = appctx;
388 appctx->ctx.httpclient.ptr = hc;
389 appctx->st0 = HTTPCLIENT_S_REQ;
390
391 return appctx;
392
393out_free_stream:
394 LIST_DELETE(&s->list);
395 pool_free(pool_head_stream, s);
396out_free_sess:
397 session_free(sess);
398out_free_appctx:
399 appctx_free(appctx);
400out:
401
402 return NULL;
403}
404
405/* Free the httpclient */
406void httpclient_destroy(struct httpclient *hc)
407{
408 if (!hc)
409 return;
410 b_free(&hc->req.buf);
411 b_free(&hc->res.buf);
412 free(hc);
413
414 return;
415}
416
417/* Allocate an httpclient and its buffers
418 * Return NULL on failure */
419struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
420{
421 struct httpclient *hc;
422 struct buffer *b;
423
424 hc = calloc(1, sizeof(*hc));
425 if (!hc)
426 goto err;
427
428 b = b_alloc(&hc->req.buf);
429 if (!b)
430 goto err;
431 b = b_alloc(&hc->res.buf);
432 if (!b)
433 goto err;
434
435 hc->caller = caller;
436 hc->req.url = url;
437 hc->req.meth = meth;
438
439 return hc;
440
441err:
442 httpclient_destroy(hc);
443 return NULL;
444}
445
446static void httpclient_applet_io_handler(struct appctx *appctx)
447{
448 struct httpclient *hc = appctx->ctx.httpclient.ptr;
449 struct stream_interface *si = appctx->owner;
450 struct stream *s = si_strm(si);
451 struct channel *req = &s->req;
452 struct channel *res = &s->res;
453 struct htx_blk *blk = NULL;
454 struct htx *htx;
William Lallemandb7020302021-08-20 11:24:13 +0200455 struct htx_sl *sl = NULL;
William Lallemand33b0d092021-08-13 16:05:53 +0200456 int32_t pos;
457 uint32_t hdr_num;
458
459
460 while (1) {
461 switch(appctx->st0) {
462
463 case HTTPCLIENT_S_REQ:
464 /* copy the request from the hc->req.buf buffer */
465 htx = htx_from_buf(&req->buf);
466 /* We now that it fits the content of a buffer so can
467 * just push this entirely */
468 b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
469 channel_add_input(req, b_data(&req->buf));
470 appctx->st0 = HTTPCLIENT_S_RES_STLINE;
471 goto more; /* we need to leave the IO handler once we wrote the request */
472 break;
473
474 case HTTPCLIENT_S_RES_STLINE:
475 /* copy the start line in the hc structure,then remove the htx block */
476 if (!b_data(&res->buf))
477 goto more;
478 htx = htxbuf(&res->buf);
479 if (!htx)
480 goto more;
481 blk = htx_get_first_blk(htx);
482 if (blk && (htx_get_blk_type(blk) == HTX_BLK_RES_SL))
483 sl = htx_get_blk_ptr(htx, blk);
484 if (!sl || (!(sl->flags & HTX_SL_F_IS_RESP)))
485 goto more;
486
487 /* copy the status line in the httpclient */
488 hc->res.status = sl->info.res.status;
489 hc->res.vsn = istdup(htx_sl_res_vsn(sl));
490 hc->res.reason = istdup(htx_sl_res_reason(sl));
491 co_htx_remove_blk(res, htx, blk);
492 /* caller callback */
493 if (hc->ops.res_stline)
494 hc->ops.res_stline(hc);
495
496 /* if there is no HTX data anymore and the EOM flag is
497 * set, leave (no body) */
498 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
499 appctx->st0 = HTTPCLIENT_S_RES_END;
500 else
501 appctx->st0 = HTTPCLIENT_S_RES_HDR;
502 break;
503
504 case HTTPCLIENT_S_RES_HDR:
505 /* first copy the headers in a local hdrs
506 * structure, once we the total numbers of the
507 * header we allocate the right size and copy
508 * them. The htx block of the headers are
509 * removed each time one is read */
510 {
511 struct http_hdr hdrs[global.tune.max_http_hdr];
512
513 if (!b_data(&res->buf))
514 goto more;
515 htx = htxbuf(&res->buf);
516 if (!htx)
517 goto more;
518
519 hdr_num = 0;
520
521 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
522 struct htx_blk *blk = htx_get_blk(htx, pos);
523 enum htx_blk_type type = htx_get_blk_type(blk);
524
525 if (type == HTX_BLK_EOH) {
526 hdrs[hdr_num].n = IST_NULL;
527 hdrs[hdr_num].v = IST_NULL;
528 co_htx_remove_blk(res, htx, blk);
529 break;
530 }
531
532 if (type != HTX_BLK_HDR)
533 continue;
534
535 hdrs[hdr_num].n = istdup(htx_get_blk_name(htx, blk));
536 hdrs[hdr_num].v = istdup(htx_get_blk_value(htx, blk));
537 if (!isttest(hdrs[hdr_num].v) || !isttest(hdrs[hdr_num].n))
538 goto end;
539 co_htx_remove_blk(res, htx, blk);
540 hdr_num++;
541 }
542
William Lallemand0d6f7792021-08-20 11:59:49 +0200543 if (hdr_num) {
544 /* alloc and copy the headers in the httpclient struct */
545 hc->res.hdrs = calloc((hdr_num + 1), sizeof(*hc->res.hdrs));
546 if (!hc->res.hdrs)
547 goto end;
548 memcpy(hc->res.hdrs, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));
William Lallemand33b0d092021-08-13 16:05:53 +0200549
William Lallemand0d6f7792021-08-20 11:59:49 +0200550 /* caller callback */
551 if (hc->ops.res_headers)
552 hc->ops.res_headers(hc);
553 }
William Lallemand33b0d092021-08-13 16:05:53 +0200554
555 /* if there is no HTX data anymore and the EOM flag is
556 * set, leave (no body) */
557 if (htx_is_empty(htx) && htx->flags & HTX_FL_EOM)
558 appctx->st0 = HTTPCLIENT_S_RES_END;
559 else
560 appctx->st0 = HTTPCLIENT_S_RES_BODY;
561 }
562 break;
563
564 case HTTPCLIENT_S_RES_BODY:
565 /*
566 * The IO handler removes the htx blocks in the response buffer and
567 * push them in the hc->res.buf buffer in a raw format.
568 */
569 htx = htxbuf(&res->buf);
570 if (!htx || htx_is_empty(htx))
571 goto more;
572
573 if (b_full(&hc->res.buf))
574 goto process_data;
575
576 /* decapsule the htx data to raw data */
577 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
578 enum htx_blk_type type;
579
580 blk = htx_get_blk(htx, pos);
581 type = htx_get_blk_type(blk);
582 if (type == HTX_BLK_DATA) {
583 struct ist v = htx_get_blk_value(htx, blk);
584
585 if ((b_room(&hc->res.buf) < v.len) )
586 goto process_data;
587
588 __b_putblk(&hc->res.buf, v.ptr, v.len);
589 co_htx_remove_blk(res, htx, blk);
590 /* the data must be processed by the caller in the receive phase */
591 if (hc->ops.res_payload)
592 hc->ops.res_payload(hc);
593 } else {
594 /* remove any block which is not a data block */
595 co_htx_remove_blk(res, htx, blk);
596 }
597 }
598 /* if not finished, should be called again */
599 if (!(htx->flags & HTX_FL_EOM))
600 goto more;
601
602 /* end of message, we should quit */
603 appctx->st0 = HTTPCLIENT_S_RES_END;
604 break;
605
606 case HTTPCLIENT_S_RES_END:
607 goto end;
608 break;
609 }
610 }
611
612process_data:
613
614 si_rx_chan_rdy(si);
615
616 return;
617more:
618 /* There was not enough data in the response channel */
619
620 si_rx_room_blk(si);
621
622 if (appctx->st0 == HTTPCLIENT_S_RES_END)
623 goto end;
624
625 /* The state machine tries to handle as much data as possible, if there
626 * isn't any data to handle and a shutdown is detected, let's stop
627 * everything */
628 if ((req->flags & (CF_SHUTR|CF_SHUTR_NOW)) ||
629 (res->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
630 goto end;
631 }
632 return;
633
634end:
635 if (hc->ops.res_end)
636 hc->ops.res_end(hc);
637 si_shutw(si);
638 si_shutr(si);
639 return;
640}
641
642static void httpclient_applet_release(struct appctx *appctx)
643{
644 struct httpclient *hc = appctx->ctx.httpclient.ptr;
645
646 /* the applet is leaving, remove the ptr so we don't try to call it
647 * again from the caller */
648 hc->appctx = NULL;
649
650 return;
651}
652
653/* HTTP client applet */
654static struct applet httpclient_applet = {
655 .obj_type = OBJ_TYPE_APPLET,
656 .name = "<HTTPCLIENT>",
657 .fct = httpclient_applet_io_handler,
658 .release = httpclient_applet_release,
659};
660
William Lallemand83614a92021-08-13 14:47:57 +0200661/*
662 * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
663 * the other for HTTPS.
664 */
665
666static int httpclient_init()
667{
668 int err_code = 0;
669 char *errmsg = NULL;
670
671 httpclient_proxy = alloc_new_proxy("<HTTPCLIENT>", PR_CAP_LISTEN|PR_CAP_INT, &errmsg);
672 if (!httpclient_proxy) {
673 err_code |= ERR_ALERT | ERR_FATAL;
674 goto err;
675 }
676
Willy Tarreau0e72e402021-08-20 10:23:12 +0200677 proxy_preset_defaults(httpclient_proxy);
678
William Lallemand83614a92021-08-13 14:47:57 +0200679 httpclient_proxy->options2 |= PR_O2_INDEPSTR;
680 httpclient_proxy->mode = PR_MODE_HTTP;
681 httpclient_proxy->maxconn = 0;
682 httpclient_proxy->accept = NULL;
683 httpclient_proxy->timeout.client = TICK_ETERNITY;
684 /* The HTTP Client use the "option httplog" with the global log server */
685 httpclient_proxy->conf.logformat_string = default_http_log_format;
686 httpclient_proxy->http_needed = 1;
687
688 /* clear HTTP server */
689 httpclient_srv_raw = new_server(httpclient_proxy);
690 if (!httpclient_srv_raw) {
691 err_code |= ERR_ALERT | ERR_FATAL;
692 memprintf(&errmsg, "out of memory.");
693 goto err;
694 }
695
696 httpclient_srv_raw->iweight = 0;
697 httpclient_srv_raw->uweight = 0;
698 httpclient_srv_raw->xprt = xprt_get(XPRT_RAW);
699 httpclient_srv_raw->id = strdup("<HTTPCLIENT>");
700 if (!httpclient_srv_raw->id)
701 goto err;
702
William Lallemand957ab132021-08-24 18:33:28 +0200703#ifdef USE_OPENSSL
William Lallemand83614a92021-08-13 14:47:57 +0200704 /* SSL HTTP server */
705 httpclient_srv_ssl = new_server(httpclient_proxy);
706 if (!httpclient_srv_ssl) {
707 memprintf(&errmsg, "out of memory.");
708 err_code |= ERR_ALERT | ERR_FATAL;
709 goto err;
710 }
711 httpclient_srv_ssl->iweight = 0;
712 httpclient_srv_ssl->uweight = 0;
713 httpclient_srv_ssl->xprt = xprt_get(XPRT_SSL);
714 httpclient_srv_ssl->use_ssl = 1;
William Lallemand211c9672021-08-24 17:18:13 +0200715 httpclient_srv_ssl->id = strdup("<HTTPSCLIENT>");
William Lallemand83614a92021-08-13 14:47:57 +0200716 if (!httpclient_srv_ssl->id)
717 goto err;
718
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200719 httpclient_srv_ssl->ssl_ctx.verify = SSL_SOCK_VERIFY_NONE;
William Lallemand957ab132021-08-24 18:33:28 +0200720#endif
William Lallemandcfcbe9e2021-08-24 17:15:58 +0200721
William Lallemand83614a92021-08-13 14:47:57 +0200722 /* add the proxy in the proxy list only if everything successed */
723 httpclient_proxy->next = proxies_list;
724 proxies_list = httpclient_proxy;
725
William Lallemand211c9672021-08-24 17:18:13 +0200726 /* link the 2 servers in the proxy */
727 httpclient_srv_raw->next = httpclient_proxy->srv;
William Lallemand957ab132021-08-24 18:33:28 +0200728 httpclient_proxy->srv = httpclient_srv_raw;
729
730#ifdef USE_OPENSSL
731 httpclient_srv_ssl->next = httpclient_proxy->srv;
William Lallemand211c9672021-08-24 17:18:13 +0200732 httpclient_proxy->srv = httpclient_srv_ssl;
William Lallemand957ab132021-08-24 18:33:28 +0200733#endif
734
William Lallemand211c9672021-08-24 17:18:13 +0200735
William Lallemand83614a92021-08-13 14:47:57 +0200736 return 0;
737
738err:
739 ha_alert("httpclient: cannot initialize.\n");
740 free(errmsg);
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200741 srv_drop(httpclient_srv_raw);
William Lallemand957ab132021-08-24 18:33:28 +0200742#ifdef USE_OPENSSL
Amaury Denoyellebc2ebfa2021-08-25 15:34:53 +0200743 srv_drop(httpclient_srv_ssl);
William Lallemand957ab132021-08-24 18:33:28 +0200744#endif
William Lallemand83614a92021-08-13 14:47:57 +0200745 free_proxy(httpclient_proxy);
746 return err_code;
747}
748
William Lallemand83614a92021-08-13 14:47:57 +0200749static int httpclient_cfg_postparser()
750{
751 struct logsrv *logsrv;
752 struct proxy *curproxy = httpclient_proxy;
753
754 /* copy logs from "global" log list */
755 list_for_each_entry(logsrv, &global.logsrvs, list) {
756 struct logsrv *node = malloc(sizeof(*node));
757
758 if (!node) {
759 ha_alert("httpclient: cannot allocate memory.\n");
760 goto err;
761 }
762
763 memcpy(node, logsrv, sizeof(*node));
764 LIST_INIT(&node->list);
765 LIST_APPEND(&curproxy->logsrvs, &node->list);
766 }
767 if (curproxy->conf.logformat_string) {
768 char *err = NULL;
769
770 curproxy->conf.args.ctx = ARGC_LOG;
771 if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
772 LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
773 SMP_VAL_FE_LOG_END, &err)) {
774 ha_alert("httpclient: failed to parse log-format : %s.\n", err);
775 free(err);
776 goto err;
777 }
778 curproxy->conf.args.file = NULL;
779 curproxy->conf.args.line = 0;
780 }
781 return 0;
782err:
783 return 1;
784}
785
William Lallemand83614a92021-08-13 14:47:57 +0200786/* initialize the proxy and servers for the HTTP client */
787
788INITCALL0(STG_REGISTER, httpclient_init);
789REGISTER_CONFIG_POSTPARSER("httpclient", httpclient_cfg_postparser);