[MINOR] http-request: allow/deny/auth support for frontend/backend/listen

Use the generic auth framework to control access to frontends/backends/listens
diff --git a/include/types/proxy.h b/include/types/proxy.h
index bdb42b9..fabc936 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -170,6 +170,7 @@
 		char *name;			/* default backend name during config parse */
 	} defbe;
 	struct list acl;                        /* ACL declared on this proxy */
+	struct list req_acl;			/* request ACL: allow/deny/http-auth */
 	struct list block_cond;                 /* early blocking conditions (chained) */
 	struct list redirect_rules;             /* content redirecting rules (chained) */
 	struct list switching_rules;            /* content switching rules (chained) */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5c181f1..ef34760 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -882,6 +882,7 @@
 	memset(p, 0, sizeof(struct proxy));
 	LIST_INIT(&p->pendconns);
 	LIST_INIT(&p->acl);
+	LIST_INIT(&p->req_acl);
 	LIST_INIT(&p->block_cond);
 	LIST_INIT(&p->redirect_rules);
 	LIST_INIT(&p->mon_fail_cond);
@@ -1927,6 +1928,31 @@
 		}
 		curproxy->conn_retries = atol(args[1]);
 	}
+	else if (!strcmp(args[0], "http-request")) {	/* request access control: allow/deny/auth */
+		struct req_acl_rule *req_acl;
+
+		if (curproxy == &defproxy) {
+			Alert("parsing [%s:%d]: '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+
+		if (!LIST_ISEMPTY(&curproxy->req_acl) && !LIST_PREV(&curproxy->req_acl, struct req_acl_rule *, list)->cond) {
+			Warning("parsing [%s:%d]: previous '%s' action has no condition attached, further entries are NOOP.\n",
+			        file, linenum, args[0]);
+			err_code |= ERR_WARN;
+		}
+
+		req_acl = parse_auth_cond((const char **)args + 1, file, linenum, &curproxy->acl, &curproxy->acl_requires);
+
+		if (!req_acl) {
+			err_code |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+
+		LIST_ADDQ(&curproxy->req_acl, &req_acl->list);
+	}
 	else if (!strcmp(args[0], "block")) {  /* early blocking based on ACLs */
 		if (curproxy == &defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
diff --git a/src/haproxy.c b/src/haproxy.c
index 0393ebf..905febf 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -859,6 +859,8 @@
 			l = l_next;
 		}/* end while(l) */
 
+		req_acl_free(&p->req_acl);
+
 		pool_destroy2(p->req_cap_pool);
 		pool_destroy2(p->rsp_cap_pool);
 		pool_destroy2(p->hdr_idx_pool);
diff --git a/src/proto_http.c b/src/proto_http.c
index b2dd232..59952a4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1351,7 +1351,6 @@
 	return NULL;
 }
 
-
 /*
  * This function parses a request line between <ptr> and <end>, starting with
  * parser state <state>. Only states HTTP_MSG_RQMETH, HTTP_MSG_RQMETH_SP,
@@ -2723,6 +2722,7 @@
 	struct http_txn *txn = &s->txn;
 	struct http_msg *msg = &txn->req;
 	struct acl_cond *cond;
+	struct req_acl_rule *req_acl, *req_acl_final = NULL;
 	struct redirect_rule *rule;
 	struct cond_wordlist *wl;
 	int del_ka, del_cl;
@@ -2762,6 +2762,29 @@
 		}
 	}
 
+	list_for_each_entry(req_acl, &px->req_acl, list) {
+		int ret = 1;
+
+		if (req_acl->action >= PR_REQ_ACL_ACT_MAX)
+			continue;
+
+		/* check condition, but only if attached */
+		if (req_acl->cond)
+			ret = acl_exec_cond(req_acl->cond, px, s, txn, ACL_DIR_REQ);
+
+		if (ret) {
+			req_acl_final = req_acl;
+			break;
+		}
+	}
+
+	if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_DENY) {
+			txn->status = 403;
+			s->logs.tv_request = now;
+			stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+			goto return_prx_cond;
+	}
+
 	/* try headers filters */
 	if (px->req_exp != NULL) {
 		if (apply_filters_to_request(s, req, px) < 0)
@@ -2864,6 +2887,16 @@
 			goto return_bad_req;
 	}
 
+	if (req_acl_final && req_acl_final->action == PR_REQ_ACL_ACT_HTTP_AUTH) {
+		struct chunk msg;
+
+		sprintf(trash, HTTP_401_fmt, req_acl->http_auth.realm?req_acl->http_auth.realm:px->id);
+		chunk_initlen(&msg, trash, sizeof(trash), strlen(trash));
+		txn->status = 401;
+		stream_int_retnclose(req->prod, &msg);
+		goto return_prx_cond;
+	}
+
 	/* check if stats URI was requested, and if an auth is needed */
 	if (px->uri_auth != NULL &&
 	    (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {