From: Christopher Faulet Date: Fri, 17 Jan 2020 21:30:06 +0000 (+0100) Subject: MEDIUM: http-rules: Make early-hint custom actions X-Git-Tag: v2.2-dev1~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=91b3ec13c680a66843138fb10a3846578fccfb5a;p=thirdparty%2Fhaproxy.git MEDIUM: http-rules: Make early-hint custom actions Now, the early-hint action uses its own dedicated action and is no longer handled "in place" during the HTTP rules evaluation. Thus the action name ACT_HTTP_EARLY_HINT is removed. In additionn, http_add_early_hint_header() and http_reply_103_early_hints() are also removed. This part is now handled in the new action_ptr callback function. --- diff --git a/include/types/action.h b/include/types/action.h index 6d77f9cd2e..0016796b8b 100644 --- a/include/types/action.h +++ b/include/types/action.h @@ -84,7 +84,6 @@ enum act_name { ACT_HTTP_SET_LOGL, ACT_HTTP_SET_TOS, ACT_HTTP_SET_MARK, - ACT_HTTP_EARLY_HINT, /* http request actions. */ ACT_HTTP_REQ_TARPIT, diff --git a/src/http_act.c b/src/http_act.c index 537b1d04a5..60b65c4f0f 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -944,6 +944,86 @@ static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_ return ACT_RET_PRS_OK; } +/* This function executes a early-hint action. It adds an HTTP Early Hint HTTP + * 103 response header with <.arg.http.str> name and with a value built + * according to <.arg.http.fmt> log line format. If it is the first early-hint + * rule of a serie, the 103 response start-line is added first. At the end, if + * the next rule is not an early-hint rule or if it is the last rule, the EOH + * block is added to terminate the response. 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_early_hint(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + struct act_rule *prev_rule, *next_rule; + struct channel *res = &s->res; + struct htx *htx = htx_from_buf(&res->buf); + struct buffer *value = alloc_trash_chunk(); + enum act_return ret = ACT_RET_CONT; + + if (!(s->txn->req.flags & HTTP_MSGF_VER_11)) + goto leave; + + if (!value) { + if (!(s->flags & SF_ERR_MASK)) + s->flags |= SF_ERR_RESOURCE; + goto error; + } + + /* get previous and next rules */ + prev_rule = LIST_PREV(&rule->list, typeof(rule), list); + next_rule = LIST_NEXT(&rule->list, typeof(rule), list); + + /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise, + * continue to add link to a previously started response */ + if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) { + struct htx_sl *sl; + unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11| + HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS); + + sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, + ist("HTTP/1.1"), ist("103"), ist("Early Hints")); + if (!sl) + goto error; + sl->info.res.status = 103; + } + + /* Add the HTTP Early Hint HTTP 103 response heade */ + value->data = build_logline(s, b_tail(value), b_room(value), &rule->arg.http.fmt); + if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value)))) + goto error; + + /* if it is the last rule or the next one is not an early-hint, terminate the current + * response. */ + if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) { + size_t data; + + if (!htx_add_endof(htx, HTX_BLK_EOH)) { + /* If an error occurred during an Early-hint rule, + * remove the incomplete HTTP 103 response from the + * buffer */ + goto error; + } + + data = htx->data - co_data(res); + c_adv(res, data); + res->total += data; + } + + leave: + free_trash_chunk(value); + return ret; + + error: + /* If an error occurred during an Early-hint rule, remove the incomplete + * HTTP 103 response from the buffer */ + channel_htx_truncate(res, htx); + ret = ACT_RET_ERR; + goto leave; +} + /* This function executes a set-header or add-header 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_set_header(). The @@ -1016,8 +1096,10 @@ static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg { int cap, cur_arg; - if (args[*orig_arg-1][0] == 'e') - rule->action = ACT_HTTP_EARLY_HINT; + if (args[*orig_arg-1][0] == 'e') { + rule->action = ACT_CUSTOM; + rule->action_ptr = http_action_early_hint; + } else { if (args[*orig_arg-1][0] == 's') rule->action = 0; // set-header diff --git a/src/http_ana.c b/src/http_ana.c index 9f6c90e0b7..d21d19e1fd 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2728,75 +2728,6 @@ int http_replace_hdrs(struct stream* s, struct htx *htx, struct ist name, return 0; } -/* 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. - */ -static int http_reply_103_early_hints(struct channel *res) -{ - struct htx *htx = htx_from_buf(&res->buf); - size_t data; - - if (!htx_add_endof(htx, HTX_BLK_EOH)) { - /* If an error occurred during an Early-hint rule, - * remove the incomplete HTTP 103 response from the - * buffer */ - channel_htx_truncate(res, htx); - return -1; - } - - data = htx->data - co_data(res); - c_adv(res, data); - res->total += data; - return 0; -} - -/* - * Build an HTTP Early Hint HTTP 103 response header with as name and with a value - * built according to log line format. - * If is 0, it is starts a new response by adding the start - * line. If an error occurred -1 is returned. On success 0 is returned. The - * channel is not updated here. It must be done calling the function - * http_reply_103_early_hints(). - */ -static int http_add_early_hint_header(struct stream *s, int early_hints, const struct ist name, struct list *fmt) -{ - struct channel *res = &s->res; - struct htx *htx = htx_from_buf(&res->buf); - struct buffer *value = alloc_trash_chunk(); - - if (!value) { - if (!(s->flags & SF_ERR_MASK)) - s->flags |= SF_ERR_RESOURCE; - goto fail; - } - - if (!early_hints) { - struct htx_sl *sl; - unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11| - HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS); - - sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, - ist("HTTP/1.1"), ist("103"), ist("Early Hints")); - if (!sl) - goto fail; - sl->info.res.status = 103; - } - - value->data = build_logline(s, b_tail(value), b_room(value), fmt); - if (!htx_add_header(htx, name, ist2(b_head(value), b_data(value)))) - goto fail; - - free_trash_chunk(value); - return 0; - - fail: - /* If an error occurred during an Early-hint rule, remove the incomplete - * HTTP 103 response from the buffer */ - channel_htx_truncate(res, htx); - free_trash_chunk(value); - return -1; -} - /* This function executes one of the set-{method,path,query,uri} actions. It * takes the string from the variable 'replace' with length 'len', then modifies * the relevant part of the request line accordingly. Then it updates various @@ -3028,36 +2959,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis http_remove_header(htx, &ctx); break; - case ACT_HTTP_EARLY_HINT: { - struct act_rule *prev_rule, *next_rule; - int early_hints; - - if (!(txn->req.flags & HTTP_MSGF_VER_11)) - break; - - /* get previous and next rules */ - prev_rule = LIST_PREV(&rule->list, typeof(rule), list); - next_rule = LIST_NEXT(&rule->list, typeof(rule), list); - - /* if no previous rule or previous rule is not early-hint, start a new response. Otherwise, - * continue to add link to a previously started response */ - early_hints = (&prev_rule->list != rules && prev_rule->action == ACT_HTTP_EARLY_HINT); - - if (http_add_early_hint_header(s, early_hints, rule->arg.http.str, &rule->arg.http.fmt) == -1) { - rule_ret = HTTP_RULE_RES_ERROR; - goto end; - } - /* if it is the last rule or the next one is not an early-hint, terminate the current - * response. */ - if (&next_rule->list == rules || next_rule->action != ACT_HTTP_EARLY_HINT) { - if (http_reply_103_early_hints(&s->res) == -1) { - rule_ret = HTTP_RULE_RES_ERROR; - goto end; - } - } - break; - } - case ACT_ACTION_TRK_SC0 ... ACT_ACTION_TRK_SCMAX: /* Note: only the first valid tracking parameter of each * applies.