MEDIUM: http-rules: Add the return action to HTTP rules

Thanks to this new action, it is now possible to return any responses from
HAProxy, with any status code, based on an errorfile, a file or a string. Unlike
the other internal messages generated by HAProxy, these ones are not interpreted
as errors. And it is not necessary to use a file containing a full HTTP
response, although it is still possible. In addition, using a log-format string
or a log-format file, it is possible to have responses with a dynamic
content. This action can be used on the request path or the response path. The
only constraint is to have a responses smaller than a buffer. And to avoid any
warning the buffer space reserved to the headers rewritting should also be free.

When a response is returned with a file or a string as payload, it only contains
the content-length header and the content-type header, if applicable. Here are
examples:

  http-request return content-type image/x-icon file /var/www/favicon.ico  \
      if { path /favicon.ico }

  http-request return status 403 content-type text/plain    \
      lf-string "Access denied. IP %[src] is blacklisted."  \
      if { src -f /etc/haproxy/blacklist.lst }
This commit is contained in:
Christopher Faulet 2020-01-24 17:44:23 +01:00
parent 6d0c3dfac6
commit 24231ab61f
3 changed files with 655 additions and 1 deletions

View File

@ -4840,6 +4840,73 @@ http-request replace-value <name> <match-regex> <replace-fmt>
# outputs: # outputs:
X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37 X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
http-request return [status <code>] [content-type <type>]
[ { default-errorfiles | errorfile <file> | errorfiles <name> |
file <file> | lf-file <file> | string <str> | lf-string <fmt> } ]
[ { if | unless } <condition> ]
This stops the evaluation of the rules and immediatly returns a response. The
default status code used for the response is 200. It can be optionally
specified as an arguments to "status". The response content-type may also be
specified as an argument to "content-type". Finally the response itselft may
be defined. If can be a full HTTP response specifying the errorfile to use,
or the response payload specifing the file or the string to use. These rules
are followed to create the response :
* If neither the errorfile nor the payload to use is defined, a dummy
response is returned. Only the "status" argument is considered. It can be
any code in the range [200, 599]. The "content-type" argument, if any, is
ignored.
* If "default-errorfiles" argument is set, the proxy's errorfiles are
considered. If the "status" argument is defined, it must be one of the
status code handled by hparoxy (200, 400, 403, 404, 405, 408, 410, 425,
429, 500, 502, 503, and 504). The "content-type" argument, if any, is
ignored.
* If a specific errorfile is defined, with an "errorfile" argument, the
corresponding file, containing a full HTTP response, is returned. Only the
"status" argument is considered. It must be one of the status code handled
by hparoxy (200, 400, 403, 404, 405, 408, 410, 425, 429, 500, 502, 503, and
504). The "content-type" argument, if any, is ignored.
* If an http-errors section is defined, with an "errorfiles" argument, the
corresponding file in the specified http-errors section, containing a full
HTTP response, is returned. Only the "status" argument is considered. It
must be one of the status code handled by hparoxy (200, 400, 403, 404, 405,
408, 410, 425, 429, 500, 502, 503, and 504). The "content-type" argument,
if any, is ignored.
* If a "file" or a "lf-file" argument is specified, the file's content is
used as the response payload. If the file is not empty, its content-type
must be set as argument to "content-type". Otherwise, any "content-type"
argument is ignored. With a "lf-file" argument, the file's content is
evaluated as a log-format string. With a "file" argument, it is considered
as a raw content.
* If a "string" or "lf-string" argument is specified, the defined string is
used as the response payload. The content-type must always be set as
argument to "content-type". With a "lf-string" argument, the string is
evaluated as a log-format string. With a "string" argument, it is
considered as a raw string.
Note that the generated response must be smaller than a buffer. And to avoid
any warning, when an errorfile or a raw file is loaded, the buffer space
reserved to the headers rewritting should also be free.
No further "http-request" rules are evaluated.
Example:
http-request return errorfile /etc/haproy/errorfiles/200.http \
if { path /ping }
http-request return content-type image/x-icon file /var/www/favicon.ico \
if { path /favicon.ico }
http-request return status 403 content-type text/plain \
lf-string "Access denied. IP %[src] is blacklisted." \
if { src -f /etc/haproxy/blacklist.lst }
http-request sc-inc-gpc0(<sc-id>) [ { if | unless } <condition> ] http-request sc-inc-gpc0(<sc-id>) [ { if | unless } <condition> ]
http-request sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ] http-request sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ]
@ -5397,6 +5464,70 @@ http-response replace-value <name> <regex-match> <replace-fmt>
# outputs: # outputs:
Cache-Control: max-age=3600, private Cache-Control: max-age=3600, private
http-response return [status <code>] [content-type <type>]
[ { default-errorfiles | errorfile <file> | errorfiles <name> |
file <file> | lf-file <file> | string <str> | lf-string <fmt> } ]
[ { if | unless } <condition> ]
This stops the evaluation of the rules and immediatly returns a response. The
default status code used for the response is 200. It can be optionally
specified as an arguments to "status". The response content-type may also be
specified as an argument to "content-type". Finally the response itselft may
be defined. If can be a full HTTP response specifying the errorfile to use,
or the response payload specifing the file or the string to use. These rules
are followed to create the response :
* If neither the errorfile nor the payload to use is defined, a dummy
response is returned. Only the "status" argument is considered. It can be
any code in the range [200, 599]. The "content-type" argument, if any, is
ignored.
* If "default-errorfiles" argument is set, the proxy's errorfiles are
considered. If the "status" argument is defined, it must be one of the
status code handled by hparoxy (200, 400, 403, 404, 405, 408, 410, 425,
429, 500, 502, 503, and 504). The "content-type" argument, if any, is
ignored.
* If a specific errorfile is defined, with an "errorfile" argument, the
corresponding file, containing a full HTTP response, is returned. Only the
"status" argument is considered. It must be one of the status code handled
by hparoxy (200, 400, 403, 404, 405, 408, 410, 425, 429, 500, 502, 503, and
504). The "content-type" argument, if any, is ignored.
* If an http-errors section is defined, with an "errorfiles" argument, the
corresponding file in the specified http-errors section, containing a full
HTTP response, is returned. Only the "status" argument is considered. It
must be one of the status code handled by hparoxy (200, 400, 403, 404, 405,
408, 410, 425, 429, 500, 502, 503, and 504). The "content-type" argument,
if any, is ignored.
* If a "file" or a "lf-file" argument is specified, the file's content is
used as the response payload. If the file is not empty, its content-type
must be set as argument to "content-type". Otherwise, any "content-type"
argument is ignored. With a "lf-file" argument, the file's content is
evaluated as a log-format string. With a "file" argument, it is considered
as a raw content.
* If a "string" or "lf-string" argument is specified, the defined string is
used as the response payload. The content-type must always be set as
argument to "content-type". With a "lf-string" argument, the string is
evaluated as a log-format string. With a "string" argument, it is
considered as a raw string.
Note that the generated response must be smaller than a buffer. And to avoid
any warning, when an errorfile or a raw file is loaded, the buffer space
reserved to the headers rewritting should also be free.
No further "http-response" rules are evaluated.
Example:
http-response return errorfile /etc/haproy/errorfiles/200.http \
if { status eq 404 }
http-response return content-type text/plain \
string "This is the end !" \
if { status eq 500 }
http-response sc-inc-gpc0(<sc-id>) [ { if | unless } <condition> ] http-response sc-inc-gpc0(<sc-id>) [ { if | unless } <condition> ]
http-response sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ] http-response sc-inc-gpc1(<sc-id>) [ { if | unless } <condition> ]

View File

@ -127,6 +127,15 @@ struct act_rule {
int status; /* status code */ int status; /* status code */
struct buffer *errmsg; /* HTTP error message, may be NULL */ struct buffer *errmsg; /* HTTP error message, may be NULL */
} http_deny; /* args used by HTTP deny rules */ } http_deny; /* args used by HTTP deny rules */
struct {
int status;
char *ctype;
union {
struct list fmt;
struct buffer obj;
struct buffer *errmsg;
} body;
} http_return;
struct redirect_rule *redir; /* redirect rule or "http-request redirect" */ struct redirect_rule *redir; /* redirect rule or "http-request redirect" */
struct { struct {
char *ref; /* MAP or ACL file name to update */ char *ref; /* MAP or ACL file name to update */

View File

@ -11,6 +11,8 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
@ -1784,7 +1786,6 @@ static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_ar
{ {
int cur_arg; int cur_arg;
cur_arg = *orig_arg; cur_arg = *orig_arg;
if (!*args[cur_arg]) { if (!*args[cur_arg]) {
memprintf(err, "expects exactly 1 arguments"); memprintf(err, "expects exactly 1 arguments");
@ -1805,6 +1806,517 @@ static enum act_parse_ret parse_http_strict_mode(const char **args, int *orig_ar
return ACT_RET_PRS_OK; return ACT_RET_PRS_OK;
} }
/* Release <.arg.http_return> */
static void release_http_return(struct act_rule *rule)
{
struct logformat_node *lf, *lfb;
free(rule->arg.http_return.ctype);
if (rule->action == 2) {
chunk_destroy(&rule->arg.http_return.body.obj);
}
else if (rule->action == 3) {
list_for_each_entry_safe(lf, lfb, &rule->arg.http_return.body.fmt, list) {
LIST_DEL(&lf->list);
release_sample_expr(lf->expr);
free(lf->arg);
free(lf);
}
}
}
/* This function executes a return action. It builds an HTX message from an
* errorfile, an raw file or a log-format string, depending on <.action>
* value. On success, it returns ACT_RET_ABRT. If an error occurs ACT_RET_ERR is
* returned.
*/
static enum act_return http_action_return(struct act_rule *rule, struct proxy *px,
struct session *sess, struct stream *s, int flags)
{
struct channel *req = &s->req;
struct channel *res = &s->res;
struct buffer *errmsg;
struct htx *htx = htx_from_buf(&res->buf);
struct htx_sl *sl;
struct buffer *body = NULL;
const char *status, *reason, *clen, *ctype;
unsigned int slflags;
enum act_return ret = ACT_RET_DONE;
s->txn->status = rule->arg.http_return.status;
channel_htx_truncate(res, htx);
if (rule->action == 1) {
/* implicit or explicit error message*/
errmsg = rule->arg.http_return.body.errmsg;
if (!errmsg) {
/* get default error message */
errmsg = http_error_message(s);
}
if (b_is_null(errmsg))
goto end;
if (!channel_htx_copy_msg(res, htx, errmsg))
goto fail;
}
else {
/* no payload, file or log-format string */
if (rule->action == 2) {
/* file */
body = &rule->arg.http_return.body.obj;
}
else if (rule->action == 3) {
/* log-format string */
body = alloc_trash_chunk();
if (!body)
goto fail_alloc;
body->data = build_logline(s, body->area, body->size, &rule->arg.http_return.body.fmt);
}
/* else no payload */
status = ultoa(rule->arg.http_return.status);
reason = http_get_reason(rule->arg.http_return.status);
slflags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
if (!body || !b_data(body))
slflags |= HTX_SL_F_BODYLESS;
sl = htx_add_stline(htx, HTX_BLK_RES_SL, slflags, ist("HTTP/1.1"), ist(status), ist(reason));
if (!sl)
goto fail;
sl->info.res.status = rule->arg.http_return.status;
clen = (body ? ultoa(b_data(body)) : "0");
ctype = rule->arg.http_return.ctype;
if (!htx_add_header(htx, ist("content-length"), ist(clen)) ||
(body && b_data(body) && ctype && !htx_add_header(htx, ist("content-type"), ist(ctype))) ||
!htx_add_endof(htx, HTX_BLK_EOH) ||
(body && b_data(body) && !htx_add_data_atonce(htx, ist2(b_head(body), b_data(body)))) ||
!htx_add_endof(htx, HTX_BLK_EOM))
goto fail;
}
htx_to_buf(htx, &s->res.buf);
if (!http_forward_proxy_resp(s, 1))
goto fail;
end:
if (rule->from == ACT_F_HTTP_REQ) {
/* let's log the request time */
s->logs.tv_request = now;
req->analysers &= AN_REQ_FLT_END;
if (s->sess->fe == s->be) /* report it if the request was intercepted by the frontend */
_HA_ATOMIC_ADD(&s->sess->fe->fe_counters.intercepted_req, 1);
}
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_LOCAL;
if (!(s->flags & SF_FINST_MASK))
s->flags |= ((rule->from == ACT_F_HTTP_REQ) ? SF_FINST_R : SF_FINST_H);
leave:
if (rule->action == 3)
free_trash_chunk(body);
return ret;
fail_alloc:
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_RESOURCE;
ret = ACT_RET_ERR;
goto leave;
fail:
/* If an error occurred, remove the incomplete HTTP response from the
* buffer */
channel_htx_truncate(res, htx);
ret = ACT_RET_ERR;
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND;
goto leave;
}
/* Check an "http-request return" action when an http-errors section is referenced.
*
* The function returns 1 in success case, otherwise, it returns 0 and err is
* filled.
*/
static int check_http_return_action(struct act_rule *rule, struct proxy *px, char **err)
{
struct http_errors *http_errs;
int status = (intptr_t)(rule->arg.act.p[0]);
int ret = 1;
list_for_each_entry(http_errs, &http_errors_list, list) {
if (strcmp(http_errs->id, (char *)rule->arg.act.p[1]) == 0) {
free(rule->arg.act.p[1]);
rule->arg.http_return.status = status;
rule->arg.http_return.ctype = NULL;
rule->action = 1;
rule->arg.http_return.body.errmsg = http_errs->errmsg[http_get_status_idx(status)];
rule->action_ptr = http_action_return;
rule->release_ptr = release_http_return;
if (!rule->arg.http_return.body.errmsg)
ha_warning("Proxy '%s': status '%d' referenced by http return rule "
"not declared in http-errors section '%s'.\n",
px->id, status, http_errs->id);
break;
}
}
if (&http_errs->list == &http_errors_list) {
memprintf(err, "unknown http-errors section '%s' referenced by http return rule",
(char *)rule->arg.act.p[1]);
free(rule->arg.act.p[1]);
ret = 0;
}
return ret;
}
/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
* ACT_RET_PRS_ERR on error. This function creates 4 action types:
*
* - action 0 : dummy response, no payload
* - action 1 : implicit error message depending on the status code or explicit one
* - action 2 : explicit file oject ('file' argument)
* - action 3 : explicit log-format string ('content' argument)
*
* The content-type must be defined for non-empty payload. It is ignored for
* error messages (implicit or explicit). When an http-errors section is
* referenced, action is set to -1 and the real action is resolved during the
* configuration validity check.
*/
static enum act_parse_ret parse_http_return(const char **args, int *orig_arg, struct proxy *px,
struct act_rule *rule, char **err)
{
struct logformat_node *lf, *lfb;
struct stat stat;
const char *file = NULL, *act_arg = NULL;
char *obj = NULL, *ctype = NULL, *name = NULL;
int cur_arg, cap, objlen = 0, action = 0, status = 200, fd = -1;
cur_arg = *orig_arg;
while (*args[cur_arg]) {
if (strcmp(args[cur_arg], "status") == 0) {
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <status_code> as argument", args[cur_arg-1]);
return ACT_RET_PRS_ERR;
}
status = atol(args[cur_arg]);
if (status < 200 || status > 599) {
memprintf(err, "Unexpected status code '%d'", status);
goto error;
}
cur_arg++;
}
else if (strcmp(args[cur_arg], "content-type") == 0) {
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <ctype> as argument", args[cur_arg-1]);
goto error;
}
free(ctype);
ctype = strdup(args[cur_arg]);
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfiles") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <name> as argument", args[cur_arg-1]);
goto error;
}
/* Must be resolved during the config validity check */
name = strdup(args[cur_arg]);
action = -1;
cur_arg++;
}
else if (strcmp(args[cur_arg], "default-errorfiles") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
action = 1;
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfile") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <fmt> as argument", args[cur_arg-1]);
goto error;
}
file = args[cur_arg];
rule->arg.http_return.body.errmsg = http_load_errorfile(args[cur_arg], err);
if (!rule->arg.http_return.body.errmsg) {
goto error;
}
action = 1;
cur_arg++;
}
else if (strcmp(args[cur_arg], "file") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
goto error;
}
file = args[cur_arg];
fd = open(args[cur_arg], O_RDONLY);
if ((fd < 0) || (fstat(fd, &stat) < 0)) {
memprintf(err, "error opening file '%s'", args[cur_arg]);
goto error;
}
if (stat.st_size > global.tune.bufsize) {
memprintf(err, "file '%s' exceeds the buffer size (%ld > %d)",
args[cur_arg], stat.st_size, global.tune.bufsize);
goto error;
}
objlen = stat.st_size;
obj = malloc(objlen);
if (!obj || read(fd, obj, objlen) != objlen) {
memprintf(err, "error reading file '%s'", args[cur_arg]);
goto error;
}
close(fd);
fd = -1;
action = 2;
cur_arg++;
}
else if (strcmp(args[cur_arg], "string") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <str> as argument", args[cur_arg-1]);
goto error;
}
obj = strdup(args[cur_arg]);
objlen = strlen(args[cur_arg]);
action = 2;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-file") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
goto error;
}
file = args[cur_arg];
fd = open(args[cur_arg], O_RDONLY);
if ((fd < 0) || (fstat(fd, &stat) < 0)) {
memprintf(err, "error opening file '%s'", args[cur_arg]);
goto error;
}
if (stat.st_size > global.tune.bufsize) {
memprintf(err, "file '%s' exceeds the buffer size (%ld > %d)",
args[cur_arg], stat.st_size, global.tune.bufsize);
goto error;
}
objlen = stat.st_size;
obj = malloc(objlen + 1);
if (!obj || read(fd, obj, objlen) != objlen) {
memprintf(err, "error reading file '%s'", args[cur_arg]);
goto error;
}
close(fd);
fd = -1;
obj[objlen] = '\0';
action = 3;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-string") == 0) {
if (action != 0) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
cur_arg++;
if (!*args[cur_arg]) {
memprintf(err, "'%s' expects <fmt> as argument", args[cur_arg-1]);
goto error;
}
obj = strdup(args[cur_arg]);
objlen = strlen(args[cur_arg]);
action = 3;
cur_arg++;
}
else
break;
}
if (action == -1) { /* errorfiles */
int rc;
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
if (http_err_codes[rc] == status)
break;
}
if (rc >= HTTP_ERR_SIZE) {
memprintf(err, "status code '%d' not handled by default with '%s' argument.",
status, act_arg);
goto error;
}
if (ctype) {
ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored when the "
"returned response is an erorrfile.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
ctype);
free(ctype);
ctype = NULL;
}
rule->arg.act.p[0] = (void *)((intptr_t)status);
rule->arg.act.p[1] = name;
rule->check_ptr = check_http_return_action;
goto out;
}
else if (action == 0) { /* no payload */
if (ctype) {
ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored because"
" neither errorfile nor payload defined.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
ctype);
free(ctype);
ctype = NULL;
}
}
else if (action == 1) { /* errorfile */
if (!rule->arg.http_return.body.errmsg) { /* default errorfile */
int rc;
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
if (http_err_codes[rc] == status)
break;
}
if (rc >= HTTP_ERR_SIZE) {
memprintf(err, "status code '%d' not handled by default with '%s' argument",
status, act_arg);
goto error;
}
if (ctype) {
ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored when the "
"returned response is an erorrfile.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
ctype);
free(ctype);
ctype = NULL;
}
}
else { /* explicit payload using 'errorfile' parameter */
if (ctype) {
ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored because"
" the errorfile '%s' is used.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
ctype, file);
free(ctype);
ctype = NULL;
}
}
}
else if (action == 2) { /* explicit parameter using 'file' parameter*/
if (!ctype && objlen) {
memprintf(err, "a content type must be defined when non-empty payload is configured");
goto error;
}
if (ctype && !objlen) {
ha_warning("parsing [%s:%d] : 'http-%s return' : content-type '%s' ignored when the "
"configured payload is empty.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
ctype);
free(ctype);
ctype = NULL;
}
if (global.tune.bufsize - objlen < global.tune.maxrewrite) {
ha_warning("parsing [%s:%d] : 'http-%s return' : the payload runs ober the buffer space reserved to headers rewritting."
" It may lead to internal errors if strict rewritting mode is enabled.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"));
}
chunk_initlen(&rule->arg.http_return.body.obj, obj, global.tune.bufsize, objlen);
}
else if (action == 3) { /* log-format payload using 'lf-file' of 'lf-string' parameter */
LIST_INIT(&rule->arg.http_return.body.fmt);
if (!ctype) {
memprintf(err, "a content type must be defined with a log-format payload");
goto error;
}
if (rule->from == ACT_F_HTTP_REQ) {
px->conf.args.ctx = ARGC_HRQ;
cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
}
else {
px->conf.args.ctx = ARGC_HRS;
cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
}
if (!parse_logformat_string(obj, px, &rule->arg.http_return.body.fmt, LOG_OPT_HTTP, cap, err))
goto error;
free(px->conf.lfs_file);
px->conf.lfs_file = strdup(px->conf.args.file);
px->conf.lfs_line = px->conf.args.line;
free(obj);
}
rule->arg.http_return.status = status;
rule->arg.http_return.ctype = ctype;
rule->action = action;
rule->action_ptr = http_action_return;
rule->release_ptr = release_http_return;
out:
*orig_arg = cur_arg;
return ACT_RET_PRS_OK;
error:
free(obj);
free(ctype);
free(name);
if (fd >= 0)
close(fd);
if (action == 3) {
list_for_each_entry_safe(lf, lfb, &rule->arg.http_return.body.fmt, list) {
LIST_DEL(&lf->list);
release_sample_expr(lf->expr);
free(lf->arg);
free(lf);
}
}
free(rule->arg.http_return.ctype);
return ACT_RET_PRS_ERR;
}
/************************************************************************/ /************************************************************************/
/* All supported http-request action keywords must be declared here. */ /* All supported http-request action keywords must be declared here. */
/************************************************************************/ /************************************************************************/
@ -1828,6 +2340,7 @@ static struct action_kw_list http_req_actions = {
{ "replace-path", parse_replace_uri, 0 }, { "replace-path", parse_replace_uri, 0 },
{ "replace-uri", parse_replace_uri, 0 }, { "replace-uri", parse_replace_uri, 0 },
{ "replace-value", parse_http_replace_header, 0 }, { "replace-value", parse_http_replace_header, 0 },
{ "return", parse_http_return, 0 },
{ "set-header", parse_http_set_header, 0 }, { "set-header", parse_http_set_header, 0 },
{ "set-log-level", parse_http_set_log_level, 0 }, { "set-log-level", parse_http_set_log_level, 0 },
{ "set-map", parse_http_set_map, 1 }, { "set-map", parse_http_set_map, 1 },
@ -1860,6 +2373,7 @@ static struct action_kw_list http_res_actions = {
{ "redirect", parse_http_redirect, 0 }, { "redirect", parse_http_redirect, 0 },
{ "replace-header", parse_http_replace_header, 0 }, { "replace-header", parse_http_replace_header, 0 },
{ "replace-value", parse_http_replace_header, 0 }, { "replace-value", parse_http_replace_header, 0 },
{ "return", parse_http_return, 0 },
{ "set-header", parse_http_set_header, 0 }, { "set-header", parse_http_set_header, 0 },
{ "set-log-level", parse_http_set_log_level, 0 }, { "set-log-level", parse_http_set_log_level, 0 },
{ "set-map", parse_http_set_map, 1 }, { "set-map", parse_http_set_map, 1 },