diff --git a/include/types/proto_http.h b/include/types/proto_http.h index d9cda713d..5a4489d33 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -294,6 +294,7 @@ enum { /* 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 1f3ba487c..0fc26224e 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -156,6 +156,10 @@ struct session { 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 fae53fc92..f8ad754b1 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3370,6 +3370,16 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session 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 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session continue; } +resume_execution: switch (rule->action) { case HTTP_REQ_ACT_ALLOW: @@ -3567,7 +3578,10 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session } 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 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session 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 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session continue; } +resume_execution: switch (rule->action) { case HTTP_RES_ACT_ALLOW: @@ -3798,7 +3823,10 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session } 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 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit, 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 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit, 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 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit, 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 @@ int http_process_res_common(struct session *s, struct channel *rep, int an_bit, 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 @@ int http_process_res_common(struct session *s, struct channel *rep, int an_bit, * * 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 @@ int http_process_res_common(struct session *s, struct channel *rep, int an_bit, 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 @@ int http_process_res_common(struct session *s, struct channel *rep, int an_bit, 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 @@ void http_reset_txn(struct session *s) 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 f0854974a..f2272a402 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -1119,6 +1119,16 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) 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 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) } if (ret) { + +resume_execution: + /* we have a matching rule. */ if (rule->action == TCP_ACT_REJECT) { channel_abort(req); @@ -1201,8 +1214,10 @@ int tcp_inspect_request(struct session *s, struct channel *req, int an_bit) } 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 @@ int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit) 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 @@ int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit) } if (ret) { + +resume_execution: + /* we have a matching rule. */ if (rule->action == TCP_ACT_REJECT) { channel_abort(rep); @@ -1304,7 +1332,11 @@ int tcp_inspect_response(struct session *s, struct channel *rep, int an_bit) } 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 2e3b83f35..aee816c26 100644 --- a/src/session.c +++ b/src/session.c @@ -108,6 +108,11 @@ int session_accept(struct listener *l, int cfd, struct sockaddr_storage *addr) 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;