From: Christopher Faulet Date: Tue, 17 Dec 2019 08:20:34 +0000 (+0100) Subject: MINOR: http-rules: Make replace-header and replace-value custom actions X-Git-Tag: v2.2-dev1~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92d34fe38d6ab504724cca9cda093917dacc9210;p=thirdparty%2Fhaproxy.git MINOR: http-rules: Make replace-header and replace-value custom actions Now, these actions use their own dedicated function and are no longer handled "in place" during the HTTP rules evaluation. Thus the action names ACT_HTTP_REPLACE_HDR and ACT_HTTP_REPLACE_VAL are removed. The action type is now set to 0 to evaluate the whole header or to 1 to evaluate every comma-delimited values. The function http_transform_header_str() is renamed to http_replace_hdrs() to be more explicit and the function http_transform_header() is removed. In fact, this last one is now more or less the new action function. The lua code has been updated accordingly to use http_replace_hdrs(). --- diff --git a/include/proto/http_ana.h b/include/proto/http_ana.h index 10ee78aefb..6bef553c9e 100644 --- a/include/proto/http_ana.h +++ b/include/proto/http_ana.h @@ -40,8 +40,7 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s int http_request_forward_body(struct stream *s, struct channel *req, int an_bit); int http_response_forward_body(struct stream *s, struct channel *res, int an_bit); int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn); -int http_transform_header_str(struct stream* s, struct channel *chn, struct htx *htx, - struct ist name, const char *str, struct my_regex *re, int action); +int http_replace_hdrs(struct stream* s, struct htx *htx, struct ist name, const char *str, struct my_regex *re, int full); int http_req_replace_stline(int action, const char *replace, int len, struct proxy *px, struct stream *s); int http_res_set_status(unsigned int status, struct ist reason, struct stream *s); diff --git a/include/types/action.h b/include/types/action.h index 2d854ae67d..d41ed77afe 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -79,8 +79,6 @@ enum act_name { /* common http actions .*/ ACT_HTTP_ADD_HDR, - ACT_HTTP_REPLACE_HDR, - ACT_HTTP_REPLACE_VAL, ACT_HTTP_SET_HDR, ACT_HTTP_DEL_HDR, ACT_HTTP_REDIR, diff --git a/src/hlua.c b/src/hlua.c index 694b3ffc22..900bad71b7 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -4795,7 +4795,7 @@ __LJMP static int hlua_http_res_get_headers(lua_State *L) * 4 following functions. */ __LJMP static inline int hlua_http_rep_hdr(lua_State *L, struct hlua_txn *htxn, - struct http_msg *msg, int action) + struct http_msg *msg, int full) { size_t name_len; const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len)); @@ -4808,7 +4808,7 @@ __LJMP static inline int hlua_http_rep_hdr(lua_State *L, struct hlua_txn *htxn, WILL_LJMP(luaL_argerror(L, 3, "invalid regex")); htx = htxbuf(&msg->chn->buf); - http_transform_header_str(htxn->s, msg->chn, htx, ist2(name, name_len), value, re, action); + http_replace_hdrs(htxn->s, htx, ist2(name, name_len), value, re, full); regex_free(re); return 0; } @@ -4823,7 +4823,7 @@ __LJMP static int hlua_http_req_rep_hdr(lua_State *L) if (htxn->dir != SMP_OPT_DIR_REQ || !(htxn->flags & HLUA_TXN_HTTP_RDY)) WILL_LJMP(lua_error(L)); - return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_HDR)); + return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, 1)); } __LJMP static int hlua_http_res_rep_hdr(lua_State *L) @@ -4836,7 +4836,7 @@ __LJMP static int hlua_http_res_rep_hdr(lua_State *L) if (htxn->dir != SMP_OPT_DIR_RES || !(htxn->flags & HLUA_TXN_HTTP_RDY)) WILL_LJMP(lua_error(L)); - return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, ACT_HTTP_REPLACE_HDR)); + return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, 1)); } __LJMP static int hlua_http_req_rep_val(lua_State *L) @@ -4849,7 +4849,7 @@ __LJMP static int hlua_http_req_rep_val(lua_State *L) if (htxn->dir != SMP_OPT_DIR_REQ || !(htxn->flags & HLUA_TXN_HTTP_RDY)) WILL_LJMP(lua_error(L)); - return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_VAL)); + return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, 0)); } __LJMP static int hlua_http_res_rep_val(lua_State *L) @@ -4862,7 +4862,7 @@ __LJMP static int hlua_http_res_rep_val(lua_State *L) if (htxn->dir != SMP_OPT_DIR_RES || !(htxn->flags & HLUA_TXN_HTTP_RDY)) WILL_LJMP(lua_error(L)); - return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, ACT_HTTP_REPLACE_VAL)); + return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, 0)); } /* This function deletes all the occurrences of an header. diff --git a/src/http_act.c b/src/http_act.c index 9fe6a68897..33e3ff4d97 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -990,6 +990,56 @@ static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg return ACT_RET_PRS_OK; } +/* This function executes a replace-header or replace-value actions. It + * builds a string in the trash from the specified format string. It finds + * the action to be performed in <.action>, previously filled by function + * parse_replace_header(). The replacement action is excuted by the function + * http_action_replace_header(). On success, it returns ACT_RET_CONT. If an error + * occurs while soft rewrites are enabled, the action is canceled, but the rule + * processing continue. Otherwsize ACT_RET_ERR is returned. + */ +static enum act_return http_action_replace_header(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + struct htx *htx = htxbuf((rule->from == ACT_F_HTTP_REQ) ? &s->req.buf : &s->res.buf); + enum act_return ret = ACT_RET_CONT; + struct buffer *replace; + int r; + + replace = alloc_trash_chunk(); + if (!replace) + goto fail_alloc; + + replace->data = build_logline(s, replace->area, replace->size, &rule->arg.http.fmt); + + r = http_replace_hdrs(s, htx, rule->arg.http.str, replace->area, rule->arg.http.re, (rule->action == 0)); + if (r == -1) + goto fail_rewrite; + + leave: + free_trash_chunk(replace); + return ret; + + fail_alloc: + if (!(s->flags & SF_ERR_MASK)) + s->flags |= SF_ERR_RESOURCE; + ret = ACT_RET_ERR; + goto leave; + + fail_rewrite: + _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1); + if (s->flags & SF_BE_ASSIGNED) + _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1); + if (sess->listener->counters) + _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1); + if (objt_server(s->target)) + _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1); + + if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) + ret = ACT_RET_ERR; + goto leave; +} + /* Parse a "replace-header" or "replace-value" actions. It takes an header name, * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on * success, ACT_RET_PRS_ERR on error. @@ -999,7 +1049,11 @@ static enum act_parse_ret parse_http_replace_header(const char **args, int *orig { int cap, cur_arg; - rule->action = args[*orig_arg-1][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL; + if (args[*orig_arg-1][8] == 'h') + rule->action = 0; // replace-header + else + rule->action = 1; // replace-value + rule->action_ptr = http_action_replace_header; cur_arg = *orig_arg; if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) { diff --git a/src/http_ana.c b/src/http_ana.c index 171ddfdc26..a9961feec1 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2704,15 +2704,19 @@ int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struc return 0; } -int http_transform_header_str(struct stream* s, struct channel *chn, struct htx *htx, - struct ist name, const char *str, struct my_regex *re, int action) +/* Replace all headers matching the name . The header value is replaced if + * it matches the regex . is used for the replacement. If is + * set to 1, the full-line is matched and replaced. Otherwise, comma-separated + * values are evaluated one by one. It returns 0 on success and -1 on error. + */ +int http_replace_hdrs(struct stream* s, struct htx *htx, struct ist name, + const char *str, struct my_regex *re, int full) { struct http_hdr_ctx ctx; struct buffer *output = get_trash_chunk(); - /* find full header is action is ACT_HTTP_REPLACE_HDR */ ctx.blk = NULL; - while (http_find_header(htx, name, &ctx, (action == ACT_HTTP_REPLACE_HDR))) { + while (http_find_header(htx, name, &ctx, full)) { if (!regex_exec_match2(re, ctx.value.ptr, ctx.value.len, MAX_MATCH, pmatch, 0)) continue; @@ -2725,48 +2729,6 @@ int http_transform_header_str(struct stream* s, struct channel *chn, struct htx return 0; } -static int http_transform_header(struct stream* s, struct channel *chn, struct htx *htx, - const struct ist name, struct list *fmt, struct my_regex *re, int action) -{ - struct buffer *replace; - int ret = 0; - - replace = alloc_trash_chunk(); - if (!replace) - goto fail_alloc; - - replace->data = build_logline(s, replace->area, replace->size, fmt); - if (replace->data >= replace->size - 1) - goto fail_rewrite; - - if (http_transform_header_str(s, chn, htx, name, replace->area, re, action) == -1) - goto fail_rewrite; - - leave: - free_trash_chunk(replace); - return ret; - - fail_alloc: - if (!(s->flags & SF_ERR_MASK)) - s->flags |= SF_ERR_RESOURCE; - ret = -1; - goto leave; - - fail_rewrite: - _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.failed_rewrites, 1); - if (s->flags & SF_BE_ASSIGNED) - _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1); - if (s->sess->listener->counters) - _HA_ATOMIC_ADD(&s->sess->listener->counters->failed_rewrites, 1); - if (objt_server(s->target)) - _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_rewrites, 1); - - if (!(s->txn->req.flags & HTTP_MSGF_SOFT_RW)) - ret = -1; - goto leave; -} - - /* Terminate a 103-Erly-hints response and send it to the client. It returns 0 * on success and -1 on error. The response channel is updated accordingly. */ @@ -3069,16 +3031,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis s->logs.level = rule->arg.http.i; break; - case ACT_HTTP_REPLACE_HDR: - case ACT_HTTP_REPLACE_VAL: - if (http_transform_header(s, &s->req, htx, rule->arg.http.str, - &rule->arg.http.fmt, - rule->arg.http.re, rule->action)) { - rule_ret = HTTP_RULE_RES_ERROR; - goto end; - } - break; - case ACT_HTTP_DEL_HDR: /* remove all occurrences of the header */ ctx.blk = NULL; @@ -3441,16 +3393,6 @@ resume_execution: s->logs.level = rule->arg.http.i; break; - case ACT_HTTP_REPLACE_HDR: - case ACT_HTTP_REPLACE_VAL: - if (http_transform_header(s, &s->res, htx, rule->arg.http.str, - &rule->arg.http.fmt, - rule->arg.http.re, rule->action)) { - rule_ret = HTTP_RULE_RES_ERROR; - goto end; - } - break; - case ACT_HTTP_DEL_HDR: /* remove all occurrences of the header */ ctx.blk = NULL;