MEDIUM: httpclient: allow to use another proxy

httpclient_new_from_proxy() is a variant of httpclient_new() which
allows to create the requests from a different proxy.

The proxy and its 2 servers are now stored in the httpclient structure.

The proxy must have been created with httpclient_create_proxy() to be
used.

The httpclient_postcheck() callback will finish the initialization of
all proxies created with PR_CAP_HTTPCLIENT.
This commit is contained in:
William Lallemand 2022-09-12 17:39:04 +02:00
parent 54aec5f678
commit 992ad62e3c
3 changed files with 110 additions and 39 deletions

View File

@ -32,6 +32,11 @@ struct httpclient {
int timeout_server; /* server timeout in ms */
void *caller; /* ptr of the caller */
unsigned int flags; /* other flags */
struct proxy *px; /* proxy for special cases */
struct server *srv_raw; /* server for clear connections */
#ifdef USE_OPENSSL
struct server *srv_ssl; /* server for SSL connections */
#endif
};
/* Action (FA) to do */

View File

@ -6,6 +6,8 @@
void httpclient_destroy(struct httpclient *hc);
void httpclient_stop_and_destroy(struct httpclient *hc);
struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url);
struct httpclient *httpclient_new_from_proxy(struct proxy *px, void *caller, enum http_meth_t meth, struct ist url);
int httpclient_set_proxy(struct httpclient *hc, struct proxy *px);
struct appctx *httpclient_start(struct httpclient *hc);
int httpclient_set_dst(struct httpclient *hc, const char *dst);

View File

@ -610,6 +610,8 @@ void httpclient_destroy(struct httpclient *hc)
}
/* Allocate an httpclient and its buffers
* Use the default httpclient_proxy
*
* Return NULL on failure */
struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct ist url)
{
@ -624,6 +626,7 @@ struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct is
hc->caller = caller;
hc->req.url = istdup(url);
hc->req.meth = meth;
httpclient_set_proxy(hc, httpclient_proxy);
return hc;
@ -632,6 +635,49 @@ struct httpclient *httpclient_new(void *caller, enum http_meth_t meth, struct is
return NULL;
}
/* Allocate an httpclient and its buffers,
* Use the proxy <px>
*
* Return and httpclient or NULL.
*/
struct httpclient *httpclient_new_from_proxy(struct proxy *px, void *caller, enum http_meth_t meth, struct ist url)
{
struct httpclient *hc;
hc = httpclient_new(caller, meth, url);
if (!hc)
return NULL;
httpclient_set_proxy(hc, px);
return hc;
}
/*
* Configure an httpclient with a specific proxy <px>
*
* The proxy <px> must contains 2 srv, one configured for clear connections, the other for SSL.
*
*/
int httpclient_set_proxy(struct httpclient *hc, struct proxy *px)
{
struct server *srv;
hc->px = px;
for (srv = px->srv; srv != NULL; srv = srv->next) {
if (srv->xprt == xprt_get(XPRT_RAW)) {
hc->srv_raw = srv;
#ifdef USE_OPENSSL
} else if (srv->xprt == xprt_get(XPRT_SSL)) {
hc->srv_ssl = srv;
#endif
}
}
return 0;
}
static void httpclient_applet_io_handler(struct appctx *appctx)
{
struct httpclient *hc = appctx->svcctx;
@ -974,12 +1020,12 @@ static int httpclient_applet_init(struct appctx *appctx)
/* choose the SSL server or not */
switch (scheme) {
case SCH_HTTP:
target = &httpclient_srv_raw->obj_type;
target = &hc->srv_raw->obj_type;
break;
case SCH_HTTPS:
#ifdef USE_OPENSSL
if (httpclient_srv_ssl) {
target = &httpclient_srv_ssl->obj_type;
if (hc->srv_ssl) {
target = &hc->srv_ssl->obj_type;
} else {
ha_alert("httpclient: SSL was disabled (wrong verify/ca-file)!\n");
goto out_free_addr;
@ -991,7 +1037,7 @@ static int httpclient_applet_init(struct appctx *appctx)
break;
}
if (appctx_finalize_startup(appctx, httpclient_proxy, &hc->req.buf) == -1) {
if (appctx_finalize_startup(appctx, hc->px, &hc->req.buf) == -1) {
ha_alert("httpclient: Failed to initialize appctx %s:%d.\n", __FUNCTION__, __LINE__);
goto out_free_addr;
}
@ -1301,53 +1347,71 @@ static int httpclient_postcheck()
{
int err_code = ERR_NONE;
struct logsrv *logsrv;
struct proxy *curproxy = httpclient_proxy;
struct proxy *curproxy = NULL;
char *errmsg = NULL;
#ifdef USE_OPENSSL
struct server *srv = NULL;
struct server *srv_ssl = NULL;
#endif
if (global.mode & MODE_MWORKER_WAIT)
return ERR_NONE;
/* copy logs from "global" log list */
list_for_each_entry(logsrv, &global.logsrvs, list) {
struct logsrv *node = malloc(sizeof(*node));
/* Initialize the logs for every proxy dedicated to the httpclient */
for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
if (!node) {
memprintf(&errmsg, "out of memory.");
err_code |= ERR_ALERT | ERR_FATAL;
goto err;
}
if (!(curproxy->cap & PR_CAP_HTTPCLIENT))
continue;
memcpy(node, logsrv, sizeof(*node));
LIST_INIT(&node->list);
LIST_APPEND(&curproxy->logsrvs, &node->list);
node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
}
if (curproxy->conf.logformat_string) {
curproxy->conf.args.ctx = ARGC_LOG;
if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
SMP_VAL_FE_LOG_END, &errmsg)) {
memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
goto err;
/* copy logs from "global" log list */
list_for_each_entry(logsrv, &global.logsrvs, list) {
struct logsrv *node = malloc(sizeof(*node));
if (!node) {
memprintf(&errmsg, "out of memory.");
err_code |= ERR_ALERT | ERR_FATAL;
goto err;
}
memcpy(node, logsrv, sizeof(*node));
LIST_INIT(&node->list);
LIST_APPEND(&curproxy->logsrvs, &node->list);
node->ring_name = logsrv->ring_name ? strdup(logsrv->ring_name) : NULL;
node->conf.file = logsrv->conf.file ? strdup(logsrv->conf.file) : NULL;
}
if (curproxy->conf.logformat_string) {
curproxy->conf.args.ctx = ARGC_LOG;
if (!parse_logformat_string(curproxy->conf.logformat_string, curproxy, &curproxy->logformat,
LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES,
SMP_VAL_FE_LOG_END, &errmsg)) {
memprintf(&errmsg, "failed to parse log-format : %s.", errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
goto err;
}
curproxy->conf.args.file = NULL;
curproxy->conf.args.line = 0;
}
curproxy->conf.args.file = NULL;
curproxy->conf.args.line = 0;
}
#ifdef USE_OPENSSL
if (httpclient_srv_ssl) {
/* init the SNI expression */
/* always use the host header as SNI, without the port */
httpclient_srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
err_code |= server_parse_sni_expr(httpclient_srv_ssl, httpclient_proxy, &errmsg);
if (err_code & ERR_CODE) {
memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
goto err;
/* initialize the SNI for the SSL servers */
for (srv = curproxy->srv; srv != NULL; srv = srv->next) {
if (srv->xprt == xprt_get(XPRT_SSL)) {
srv_ssl = srv;
}
}
if (srv_ssl) {
/* init the SNI expression */
/* always use the host header as SNI, without the port */
srv_ssl->sni_expr = strdup("req.hdr(host),field(1,:)");
err_code |= server_parse_sni_expr(srv_ssl, curproxy, &errmsg);
if (err_code & ERR_CODE) {
memprintf(&errmsg, "failed to configure sni: %s.", errmsg);
goto err;
}
}
}
#endif
}
err:
if (err_code & ERR_CODE) {