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