MEDIUM: http/tcp: permit to resume http and tcp custom actions

Later, the processing of some actions needs to be interrupted and resumed
later. This patch permit to resume the actions. The actions that needs
to run with the resume mode are not yet avalaible. It will be soon with
Lua patches. So the code added by this patch is untestable for the moment.

The list of "tcp_exec_req_rules" cannot resme because is called by the
unresumable function "accept_session".
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index d9cda71..5a4489d 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -294,6 +294,7 @@
 /* final results for http-request rules */
 enum rule_result {
 	HTTP_RULE_RES_CONT = 0,  /* nothing special, continue rules evaluation */
+	HTTP_RULE_RES_YIELD,     /* call me later because some data is missing. */
 	HTTP_RULE_RES_STOP,      /* stopped processing on an accept */
 	HTTP_RULE_RES_DENY,      /* deny (or tarpit if TX_CLTARPIT)  */
 	HTTP_RULE_RES_ABRT,      /* abort request, msg already sent (eg: auth) */
diff --git a/include/types/session.h b/include/types/session.h
index 1f3ba48..0fc2622 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -156,6 +156,10 @@
 	struct comp_ctx *comp_ctx;		/* HTTP compression context */
 	struct comp_algo *comp_algo;		/* HTTP compression algorithm if not NULL */
 	char *unique_id;			/* custom unique ID */
+
+	/* These two pointers are used to resume the execution of the rule lists. */
+	struct list *current_rule_list;		/* this is used to store the current executed rule list. */
+	struct list *current_rule;		/* this is used to store the current rule to be resumed. */
 };
 
 #endif /* _TYPES_SESSION_H */
diff --git a/src/proto_http.c b/src/proto_http.c
index fae53fc..f8ad754 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3370,6 +3370,16 @@
 	struct hdr_ctx ctx;
 	const char *auth_realm;
 
+	/* If "the current_rule_list" match the executed rule list, we are in
+	 * resume condition. If a resume is needed it is always in the action
+	 * and never in the ACL or converters. In this case, we initialise the
+	 * current rule, and go to the action execution point.
+	 */
+	if (s->current_rule_list == rules) {
+		rule = LIST_ELEM(s->current_rule, typeof(rule), list);
+		goto resume_execution;
+	}
+	s->current_rule_list = rules;
 	list_for_each_entry(rule, rules, list) {
 		if (rule->action >= HTTP_REQ_ACT_MAX)
 			continue;
@@ -3388,6 +3398,7 @@
 				continue;
 		}
 
+resume_execution:
 
 		switch (rule->action) {
 		case HTTP_REQ_ACT_ALLOW:
@@ -3567,7 +3578,10 @@
 			}
 
 		case HTTP_REQ_ACT_CUSTOM_CONT:
-			rule->action_ptr(rule, px, s, txn);
+			if (!rule->action_ptr(rule, px, s, txn)) {
+				s->current_rule = &rule->list;
+				return HTTP_RULE_RES_YIELD;
+			}
 			break;
 
 		case HTTP_REQ_ACT_CUSTOM_STOP:
@@ -3629,6 +3643,16 @@
 	struct http_res_rule *rule;
 	struct hdr_ctx ctx;
 
+	/* If "the current_rule_list" match the executed rule list, we are in
+	 * resume condition. If a resume is needed it is always in the action
+	 * and never in the ACL or converters. In this case, we initialise the
+	 * current rule, and go to the action execution point.
+	 */
+	if (s->current_rule_list == rules) {
+		rule = LIST_ELEM(s->current_rule, typeof(rule), list);
+		goto resume_execution;
+	}
+	s->current_rule_list = rules;
 	list_for_each_entry(rule, rules, list) {
 		if (rule->action >= HTTP_RES_ACT_MAX)
 			continue;
@@ -3647,6 +3671,7 @@
 				continue;
 		}
 
+resume_execution:
 
 		switch (rule->action) {
 		case HTTP_RES_ACT_ALLOW:
@@ -3798,7 +3823,10 @@
 			}
 
 		case HTTP_RES_ACT_CUSTOM_CONT:
-			rule->action_ptr(rule, px, s, txn);
+			if (!rule->action_ptr(rule, px, s, txn)) {
+				s->current_rule = &rule->list;
+				return HTTP_RULE_RES_YIELD;
+			}
 			break;
 
 		case HTTP_RES_ACT_CUSTOM_STOP:
@@ -4085,8 +4113,7 @@
 
 	if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
 		/* we need more data */
-		channel_dont_connect(req);
-		return 0;
+		goto return_prx_yield;
 	}
 
 	DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
@@ -4106,6 +4133,9 @@
 		verdict = http_req_get_intercept_rule(px, &px->http_req_rules, s, txn);
 
 		switch (verdict) {
+		case HTTP_RULE_RES_YIELD: /* some data miss, call the function later. */
+			goto return_prx_yield;
+
 		case HTTP_RULE_RES_CONT:
 		case HTTP_RULE_RES_STOP: /* nothing to do */
 			break;
@@ -4302,6 +4332,10 @@
 	req->analysers = 0;
 	req->analyse_exp = TICK_ETERNITY;
 	return 0;
+
+ return_prx_yield:
+	channel_dont_connect(req);
+	return 0;
 }
 
 /* This function performs all the processing enabled for the current request.
@@ -6300,9 +6334,6 @@
 	if (unlikely(msg->msg_state < HTTP_MSG_BODY))	/* we need more data */
 		return 0;
 
-	rep->analysers &= ~an_bit;
-	rep->analyse_exp = TICK_ETERNITY;
-
 	/* The stats applet needs to adjust the Connection header but we don't
 	 * apply any filter there.
 	 */
@@ -6317,9 +6348,20 @@
 	 *
 	 * Filters are tried with ->be first, then with ->fe if it is
 	 * different from ->be.
+	 *
+	 * Maybe we are in resume condiion. In this case I choose the
+	 * "struct proxy" which contains the rule list matching the resume
+	 * pointer. If none of theses "struct proxy" match, I initialise
+	 * the process with the first one.
+	 *
+	 * In fact, I check only correspondance betwwen the current list
+	 * pointer and the ->fe rule list. If it doesn't match, I initialize
+	 * the loop with the ->be.
 	 */
-
-	cur_proxy = s->be;
+	if (s->current_rule_list == &s->fe->http_res_rules)
+		cur_proxy = s->fe;
+	else
+		cur_proxy = s->be;
 	while (1) {
 		struct proxy *rule_set = cur_proxy;
 
@@ -6327,6 +6369,12 @@
 		if (ret == HTTP_RULE_RES_CONT)
 			ret = http_res_get_intercept_rule(cur_proxy, &cur_proxy->http_res_rules, s, txn);
 
+		/* we need to be called again. */
+		if (ret == HTTP_RULE_RES_YIELD) {
+			channel_dont_close(rep);
+			return 0;
+		}
+
 		/* try headers filters */
 		if (rule_set->rsp_exp != NULL) {
 			if (apply_filters_to_response(s, rep, rule_set) < 0) {
@@ -6386,6 +6434,15 @@
 		cur_proxy = s->fe;
 	}
 
+	/* After this point, this anayzer can't return yield, so we can
+	 * remove the bit corresponding to this analyzer from the list.
+	 *
+	 * Note that the intermediate returns and goto found previously
+	 * reset the analyzers.
+	 */
+	rep->analysers &= ~an_bit;
+	rep->analyse_exp = TICK_ETERNITY;
+
 	/* OK that's all we can do for 1xx responses */
 	if (unlikely(txn->status < 200 && txn->status != 101))
 		goto skip_header_mangling;
@@ -8842,6 +8899,11 @@
 	http_end_txn(s);
 	http_init_txn(s);
 
+	/* reinitialise the current rule list pointer to NULL. We are sure that
+	 * any rulelist match the NULL pointer.
+	 */
+	s->current_rule_list = NULL;
+
 	s->be = s->fe;
 	s->logs.logwait = s->fe->to_log;
 	s->logs.level = 0;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f085497..f2272a4 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1119,6 +1119,16 @@
 	else
 		partial = 0;
 
+	/* If "the current_rule_list" match the executed rule list, we are in
+	 * resume condition. If a resume is needed it is always in the action
+	 * and never in the ACL or converters. In this case, we initialise the
+	 * current rule, and go to the action execution point.
+	 */
+	if (s->current_rule_list == &s->be->tcp_req.inspect_rules) {
+		rule = LIST_ELEM(s->current_rule, typeof(rule), list);
+		goto resume_execution;
+	}
+	s->current_rule_list = &s->be->tcp_req.inspect_rules;
 	list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
 		enum acl_test_res ret = ACL_TEST_PASS;
 
@@ -1133,6 +1143,9 @@
 		}
 
 		if (ret) {
+
+resume_execution:
+
 			/* we have a matching rule. */
 			if (rule->action == TCP_ACT_REJECT) {
 				channel_abort(req);
@@ -1201,8 +1214,10 @@
 			}
 			else {
 				/* Custom keywords. */
-				if (rule->action_ptr(rule, s->be, s) == 0)
+				if (rule->action_ptr(rule, s->be, s) == 0) {
+					s->current_rule = &rule->list;
 					goto missing_data;
+				}
 
 				/* otherwise accept */
 				break;
@@ -1261,6 +1276,16 @@
 	else
 		partial = 0;
 
+	/* If "the current_rule_list" match the executed rule list, we are in
+	 * resume condition. If a resume is needed it is always in the action
+	 * and never in the ACL or converters. In this case, we initialise the
+	 * current rule, and go to the action execution point.
+	 */
+	if (s->current_rule_list == &s->be->tcp_rep.inspect_rules) {
+		rule = LIST_ELEM(s->current_rule, typeof(rule), list);
+		goto resume_execution;
+	}
+	s->current_rule_list = &s->be->tcp_rep.inspect_rules;
 	list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
 		enum acl_test_res ret = ACL_TEST_PASS;
 
@@ -1279,6 +1304,9 @@
 		}
 
 		if (ret) {
+
+resume_execution:
+
 			/* we have a matching rule. */
 			if (rule->action == TCP_ACT_REJECT) {
 				channel_abort(rep);
@@ -1304,7 +1332,11 @@
 			}
 			else {
 				/* Custom keywords. */
-				rule->action_ptr(rule, s->be, s);
+				if (!rule->action_ptr(rule, s->be, s)) {
+					channel_dont_close(rep);
+					s->current_rule = &rule->list;
+					return 0;
+				}
 
 				/* otherwise accept */
 				break;
diff --git a/src/session.c b/src/session.c
index 2e3b83f..aee816c 100644
--- a/src/session.c
+++ b/src/session.c
@@ -108,6 +108,11 @@
 	s->logs.logwait = p->to_log;
 	s->logs.level = 0;
 
+	/* Initialise the current rule list pointer to NULL. We are sure that
+	 * any rulelist match the NULL pointer.
+	 */
+	s->current_rule_list = NULL;
+
 	memset(s->stkctr, 0, sizeof(s->stkctr));
 
 	s->listener = l;