diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 9653d8bdb..065a0ea4e 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -122,6 +122,20 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st enum http_meth_t find_http_meth(const char *str, const int len); +struct http_req_action_kw *action_http_req_custom(const char *kw); +struct http_res_action_kw *action_http_res_custom(const char *kw); + +static inline void http_req_keywords_register(struct http_req_action_kw_list *kw_list) +{ + LIST_ADDQ(&http_req_keywords.list, &kw_list->list); +} + +static inline void http_res_keywords_register(struct http_res_action_kw_list *kw_list) +{ + LIST_ADDQ(&http_res_keywords.list, &kw_list->list); +} + + /* to be used when contents change in an HTTP message */ #define http_msg_move_end(msg, bytes) do { \ unsigned int _bytes = (bytes); \ diff --git a/include/types/proto_http.h b/include/types/proto_http.h index f084ecd3b..a030fee37 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -256,6 +256,8 @@ enum { HTTP_REQ_ACT_DEL_ACL, HTTP_REQ_ACT_DEL_MAP, HTTP_REQ_ACT_SET_MAP, + HTTP_REQ_ACT_CUSTOM_STOP, + HTTP_REQ_ACT_CUSTOM_CONT, HTTP_REQ_ACT_MAX /* must always be last */ }; @@ -275,6 +277,8 @@ enum { HTTP_RES_ACT_DEL_ACL, HTTP_RES_ACT_DEL_MAP, HTTP_RES_ACT_SET_MAP, + HTTP_RES_ACT_CUSTOM_STOP, /* used for module keywords */ + HTTP_RES_ACT_CUSTOM_CONT, /* used for module keywords */ HTTP_RES_ACT_MAX /* must always be last */ }; @@ -394,10 +398,15 @@ struct http_auth_data { char *user, *pass; /* extracted username & password */ }; +struct proxy; +struct http_txn; +struct session; + struct http_req_rule { struct list list; struct acl_cond *cond; /* acl condition to meet */ unsigned int action; /* HTTP_REQ_* */ + int (*action_ptr)(struct http_req_rule *rule, struct proxy *px, struct session *s, struct http_txn *http_txn); /* ptr to custom action */ union { struct { char *realm; @@ -424,6 +433,7 @@ struct http_res_rule { struct list list; struct acl_cond *cond; /* acl condition to meet */ unsigned int action; /* HTTP_RES_* */ + int (*action_ptr)(struct http_res_rule *rule, struct proxy *px, struct session *s, struct http_txn *http_txn); /* ptr to custom action */ union { struct { char *name; /* header name */ @@ -464,6 +474,7 @@ struct http_txn { struct http_auth_data auth; /* HTTP auth data */ }; + /* This structure is used by http_find_header() to return values of headers. * The header starts at , the value (excluding leading and trailing white * spaces) at + for bytes, followed by optional trailing @@ -486,6 +497,31 @@ struct http_method_name { int len; }; +struct http_req_action_kw { + const char *kw; + int (*parse)(const char **args, int *cur_arg, struct proxy *px, struct http_req_rule *rule, char **err); +}; + +struct http_res_action_kw { + const char *kw; + int (*parse)(const char **args, int *cur_arg, struct proxy *px, struct http_res_rule *rule, char **err); +}; + +struct http_req_action_kw_list { + const char *scope; + struct list list; + struct http_req_action_kw kw[VAR_ARRAY]; +}; + +struct http_res_action_kw_list { + const char *scope; + struct list list; + struct http_res_action_kw kw[VAR_ARRAY]; +}; + +extern struct http_req_action_kw_list http_req_keywords; +extern struct http_res_action_kw_list http_res_keywords; + extern const struct http_method_name http_known_methods[HTTP_METH_OTHER]; #endif /* _TYPES_PROTO_HTTP_H */ diff --git a/src/proto_http.c b/src/proto_http.c index e9004f842..8467dbc09 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -217,6 +217,16 @@ const char *stat_status_codes[STAT_STATUS_SIZE] = { }; +/* List head of all known action keywords for "http-request" */ +struct http_req_action_kw_list http_req_keywords = { + .list = LIST_HEAD_INIT(http_req_keywords.list) +}; + +/* List head of all known action keywords for "http-response" */ +struct http_res_action_kw_list http_res_keywords = { + .list = LIST_HEAD_INIT(http_res_keywords.list) +}; + /* We must put the messages here since GCC cannot initialize consts depending * on strlen(). */ @@ -3289,6 +3299,14 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session break; } + + case HTTP_REQ_ACT_CUSTOM_CONT: + rule->action_ptr(rule, px, s, txn); + break; + + case HTTP_REQ_ACT_CUSTOM_STOP: + rule->action_ptr(rule, px, s, txn); + return rule; } } @@ -3462,6 +3480,14 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session break; } + + case HTTP_RES_ACT_CUSTOM_CONT: + rule->action_ptr(rule, px, s, txn); + break; + + case HTTP_RES_ACT_CUSTOM_STOP: + rule->action_ptr(rule, px, s, txn); + return rule; } } @@ -3959,6 +3985,11 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit, return 1; } + if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_CUSTOM_STOP) { + req->analyse_exp = TICK_ETERNITY; + return 1; + } + if (unlikely(objt_applet(s->target) == &http_stats_applet)) { /* process the stats request now */ if (s->fe == s->be) /* report it if the request was intercepted by the frontend */ @@ -8650,6 +8681,7 @@ void free_http_req_rules(struct list *r) { struct http_req_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy) { struct http_req_rule *rule; + struct http_req_action_kw *custom = NULL; int cur_arg; rule = (struct http_req_rule*)calloc(1, sizeof(struct http_req_rule)); @@ -8938,6 +8970,16 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i proxy->conf.lfs_line = proxy->conf.args.line; cur_arg += 2; + } else if (((custom = action_http_req_custom(args[0])) != NULL)) { + char *errmsg = NULL; + cur_arg = 1; + /* try in the module list */ + if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) < 0) { + Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + free(errmsg); + goto out_err; + } } else { Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); @@ -8973,6 +9015,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i struct http_res_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy) { struct http_res_rule *rule; + struct http_res_action_kw *custom = NULL; int cur_arg; rule = calloc(1, sizeof(*rule)); @@ -9230,6 +9273,16 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i proxy->conf.lfs_line = proxy->conf.args.line; cur_arg += 2; + } else if (((custom = action_http_res_custom(args[0])) != NULL)) { + char *errmsg = NULL; + cur_arg = 1; + /* try in the module list */ + if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) < 0) { + Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + free(errmsg); + goto out_err; + } } else { Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); @@ -11122,6 +11175,44 @@ expect_comma: return smp->data.str.len != 0; } +/* + * Return the struct http_req_action_kw associated to a keyword. + */ +struct http_req_action_kw *action_http_req_custom(const char *kw) +{ + if (!LIST_ISEMPTY(&http_req_keywords.list)) { + struct http_req_action_kw_list *kw_list; + int i; + + list_for_each_entry(kw_list, &http_req_keywords.list, list) { + for (i = 0; kw_list->kw[i].kw != NULL; i++) { + if (!strcmp(kw, kw_list->kw[i].kw)) + return &kw_list->kw[i]; + } + } + } + return NULL; +} + +/* + * Return the struct http_res_action_kw associated to a keyword. + */ +struct http_res_action_kw *action_http_res_custom(const char *kw) +{ + if (!LIST_ISEMPTY(&http_res_keywords.list)) { + struct http_res_action_kw_list *kw_list; + int i; + + list_for_each_entry(kw_list, &http_res_keywords.list, list) { + for (i = 0; kw_list->kw[i].kw != NULL; i++) { + if (!strcmp(kw, kw_list->kw[i].kw)) + return &kw_list->kw[i]; + } + } + } + return NULL; +} + /************************************************************************/ /* All supported ACL keywords must be declared here. */ /************************************************************************/