MEDIUM: http: register http-request and http-response keywords

The http_(res|req)_keywords_register() functions allow to register
new keywords.

You need to declare a keyword list:

struct http_req_action_kw_list test_kws = {
	.scope = "testscope",
	.kw = {
		{ "test", parse_test },
		{ NULL, NULL },
	}
};

and a parsing function:

int parse_test(const char **args, int *cur_arg, struct proxy *px, struct http_req_rule *rule, char **err)
{
	rule->action = HTTP_REQ_ACT_CUSTOM_STOP;
	rule->action_ptr = action_function;

	return 0;
}

http_req_keywords_register(&test_kws);

The HTTP_REQ_ACT_CUSTOM_STOP action stops evaluation of rules after
your rule, HTTP_REQ_ACT_CUSTOM_CONT permits the evaluation of rules
after your rule.
This commit is contained in:
William Lallemand 2014-04-24 14:38:37 +02:00 committed by Willy Tarreau
parent fabcbe0de6
commit 73025dd7e2
3 changed files with 141 additions and 0 deletions

View File

@ -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); \

View File

@ -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 <line>, the value (excluding leading and trailing white
* spaces) at <line>+<val> for <vlen> bytes, followed by optional <tws> 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 */

View File

@ -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. */
/************************************************************************/