MEDIUM: httpclient: http-request rules for resolving

The httpclient_resolve_init() adds http-request actions which does the
resolving using the Host header of the HTTP request.

The parse_http_req_cond function is directly used over an array of http
rules.

The do-resolve rule uses the "default" resolvers section. If this
section does not exist in the configuration, the resolving is disabling.
diff --git a/src/http_client.c b/src/http_client.c
index 2829635..e45653f 100644
--- a/src/http_client.c
+++ b/src/http_client.c
@@ -27,9 +27,11 @@
 #include <haproxy/http_ana-t.h>
 #include <haproxy/http_client.h>
 #include <haproxy/http_htx.h>
+#include <haproxy/http_rules.h>
 #include <haproxy/htx.h>
 #include <haproxy/log.h>
 #include <haproxy/proxy.h>
+#include <haproxy/resolvers.h>
 #include <haproxy/server.h>
 #include <haproxy/ssl_sock-t.h>
 #include <haproxy/sock_inet.h>
@@ -516,6 +518,7 @@
 	struct ist host = IST_NULL;
 	enum http_scheme scheme;
 	int port;
+	int doresolve = 0;
 
 	/* if the client was started and not ended, an applet is already
 	 * running, we shouldn't try anything */
@@ -529,7 +532,8 @@
 
 	ist2str(trash.area, host);
 	ss_dst = str2ip2(trash.area, &ss_url, 0);
-	sock_inet_set_port(&ss_url, port);
+	if (!ss_dst)  /* couldn't get an IP from that, try to resolve */
+		doresolve = 1;
 
 	/* The HTTP client will be created in the same thread as the caller,
 	 * avoiding threading issues */
@@ -567,8 +571,15 @@
 	if (hc->dst)
 		ss_dst = hc->dst;
 
-	/* TODO: if ss_dst == NULL, must resolve the domain */
+	if (doresolve) /* when resolving, must set-dst from 0.0.0.0  */
+		ss_dst = str2ip2("0.0.0.0", &ss_url, 0);
+
+	sock_inet_set_port(&ss_url, port);
 
+	if (!ss_dst) {
+		ha_alert("httpclient: Failed to initialize address %s:%d.\n", __FUNCTION__, __LINE__);
+		goto out_free_sess;
+	}
 
 	if (!sockaddr_alloc(&addr, ss_dst, sizeof(*hc->dst)))
 		goto out_free_sess;
@@ -1027,6 +1038,39 @@
 	.release = httpclient_applet_release,
 };
 
+
+static int httpclient_resolve_init()
+{
+	struct act_rule *rule;
+	int i;
+	const char *http_rules[][11] = {
+	       { "set-var(txn.hc_ip)", "dst", "" },
+	       { "do-resolve(txn.hc_ip,default)", "hdr(Host),lower", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
+	       { "return", "status", "503", "if", "{", "var(txn.hc_ip)", "-m", "ip", "0.0.0.0", "}", "" },
+	       { "capture", "var(txn.hc_ip)", "len", "40", "" },
+	       { "set-dst", "var(txn.hc_ip)", "" },
+	       { "" }
+	};
+
+	/* if the "default" resolver does not exist, simply ignore resolving */
+	if (!find_resolvers_by_id("default"))
+		return 0;
+
+
+	for (i = 0; *http_rules[i][0] != '\0'; i++) {
+		rule = parse_http_req_cond((const char **)http_rules[i], "httpclient", 0, httpclient_proxy);
+		if (!rule) {
+			ha_alert("Couldn't setup the httpclient resolver.\n");
+			return 1;
+		}
+		LIST_APPEND(&httpclient_proxy->http_req_rules, &rule->list);
+	}
+
+	return 0;
+}
+
+
+
 /*
  * Initialize the proxy for the HTTP client with 2 servers, one for raw HTTP,
  * the other for HTTPS.
@@ -1108,6 +1152,9 @@
 	httpclient_proxy->next = proxies_list;
 	proxies_list = httpclient_proxy;
 
+	if (httpclient_resolve_init() != 0)
+		goto err;
+
 	/* link the 2 servers in the proxy */
 	httpclient_srv_raw->next = httpclient_proxy->srv;
 	httpclient_proxy->srv = httpclient_srv_raw;