MINOR: http-htx: Use a dedicated function to parse http reply arguments

A dedicated function to parse arguments and create an http_reply object is
added. It is used to parse http return rule. Thus, following arguments are
parsed by this function :

  ... [status <code>] [content-type <type>]
      [ { default-errorfiles | errorfile <file> | errorfiles <name> |
          file <file> | lf-file <file> | string <str> | lf-string <fmt> } ]
      [ hdr <name> <fmt> ]*

Because the status code argument is optional, a default status code must be
defined when this function is called.
This commit is contained in:
Christopher Faulet 2020-05-13 14:36:55 +02:00
parent 18630643a9
commit 47e791e220
3 changed files with 359 additions and 366 deletions

View File

@ -61,6 +61,8 @@ unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
int http_str_to_htx(struct buffer *buf, struct ist raw);
void release_http_reply(struct http_reply *http_reply);
struct http_reply *http_parse_http_reply(const char **args, int *orig_arg, struct proxy *px,
int default_status, char **errmsg);
struct buffer *http_load_errorfile(const char *file, char **errmsg);
struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg);

View File

@ -11,8 +11,6 @@
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
@ -1980,381 +1978,24 @@ static int check_http_return_action(struct act_rule *rule, struct proxy *px, cha
}
/* Parse a "return" action. It returns ACT_RET_PRS_OK on success,
* ACT_RET_PRS_ERR on error. This function creates one of the following http
* replies :
*
* - HTTP_REPLY_EMPTY : dummy response, no payload
* - HTTP_REPLY_ERRMSG : implicit error message depending on the status code or explicit one
* - HTTP_REPLY_ERRFILES : points on an http-errors section (resolved during post-parsing)
* - HTTP_REPLY_RAW : explicit file object ('file' argument)
* - HTTP_REPLY_LOGFMT : 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.
* ACT_RET_PRS_ERR on error. It relies on http_parse_http_reply() to set
* <.arg.http_reply>.
*/
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 http_reply *reply = NULL;
struct http_reply_hdr *hdr, *hdrb;
struct stat stat;
const char *act_arg = NULL;
char *obj = NULL;
int cur_arg, cap, objlen = 0, fd = -1;
/* Prepare parsing of log-format strings */
px->conf.args.ctx = ((rule->from == ACT_F_HTTP_REQ) ? ARGC_HRQ : ARGC_HRS);
rule->arg.http_reply = http_parse_http_reply(args, orig_arg, px, 200, err);
if (!rule->arg.http_reply)
return ACT_RET_PRS_ERR;
reply = calloc(1, sizeof(*reply));
if (!reply) {
memprintf(err, "out of memory");
goto error;
}
LIST_INIT(&reply->hdrs);
reply->type = HTTP_REPLY_EMPTY;
reply->status = 200;
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]);
goto error;
}
reply->status = atol(args[cur_arg]);
if (reply->status < 200 || reply->status > 599) {
memprintf(err, "Unexpected status code '%d'", reply->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(reply->ctype);
reply->ctype = strdup(args[cur_arg]);
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfiles") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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;
}
reply->body.http_errors = strdup(args[cur_arg]);
if (!reply->body.http_errors) {
memprintf(err, "out of memory");
goto error;
}
reply->type = HTTP_REPLY_ERRFILES;
cur_arg++;
}
else if (strcmp(args[cur_arg], "default-errorfiles") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(err, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
reply->type = HTTP_REPLY_ERRMSG;
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfile") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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;
}
reply->body.errmsg = http_load_errorfile(args[cur_arg], err);
if (!reply->body.errmsg) {
goto error;
}
reply->type = HTTP_REPLY_ERRMSG;
cur_arg++;
}
else if (strcmp(args[cur_arg], "file") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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;
}
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 (%lld > %d)",
args[cur_arg], (long long)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;
reply->type = HTTP_REPLY_RAW;
chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
obj = NULL;
cur_arg++;
}
else if (strcmp(args[cur_arg], "string") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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]);
if (!obj) {
memprintf(err, "out of memory");
goto error;
}
reply->type = HTTP_REPLY_RAW;
chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
obj = NULL;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-file") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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;
}
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 (%lld > %d)",
args[cur_arg], (long long)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';
reply->type = HTTP_REPLY_LOGFMT;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-string") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
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]);
reply->type = HTTP_REPLY_LOGFMT;
cur_arg++;
}
else if (strcmp(args[cur_arg], "hdr") == 0) {
cur_arg++;
if (!*args[cur_arg] || !*args[cur_arg+1]) {
memprintf(err, "'%s' expects <name> and <value> as arguments", args[cur_arg-1]);
goto error;
}
if (strcasecmp(args[cur_arg], "content-length") == 0 ||
strcasecmp(args[cur_arg], "transfer-encoding") == 0 ||
strcasecmp(args[cur_arg], "content-type") == 0) {
ha_warning("parsing [%s:%d] : 'http-%s return' : header '%s' ignored.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"),
args[cur_arg]);
cur_arg += 2;
continue;
}
hdr = calloc(1, sizeof(*hdr));
if (!hdr) {
memprintf(err, "'%s' : out of memory", args[cur_arg-1]);
goto error;
}
LIST_INIT(&hdr->value);
hdr->name = ist(strdup(args[cur_arg]));
if (!isttest(hdr->name)) {
memprintf(err, "out of memory");
goto error;
}
LIST_ADDQ(&reply->hdrs, &hdr->list);
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(args[cur_arg+1], px, &hdr->value, 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;
cur_arg += 2;
}
else
break;
}
if (reply->type == HTTP_REPLY_EMPTY) { /* no payload */
if (reply->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"),
reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
}
else if (reply->type == HTTP_REPLY_ERRFILES || reply->type == HTTP_REPLY_ERRMSG) { /* errorfiles or errorfile */
if (reply->type != HTTP_REPLY_ERRMSG || !reply->body.errmsg) {
/* default errorfile or errorfiles: check the status */
int rc;
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
if (http_err_codes[rc] == reply->status)
break;
}
if (rc >= HTTP_ERR_SIZE) {
memprintf(err, "status code '%d' not handled by default with '%s' argument.",
reply->status, act_arg);
goto error;
}
}
if (reply->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"),
reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
if (!LIST_ISEMPTY(&reply->hdrs)) {
ha_warning("parsing [%s:%d] : 'http-%s return' : hdr parameters 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"));
list_for_each_entry_safe(hdr, hdrb, &reply->hdrs, list) {
LIST_DEL(&hdr->list);
list_for_each_entry_safe(lf, lfb, &hdr->value, list) {
LIST_DEL(&lf->list);
release_sample_expr(lf->expr);
free(lf->arg);
free(lf);
}
istfree(&hdr->name);
free(hdr);
}
}
}
else if (reply->type == HTTP_REPLY_RAW) { /* explicit parameter using 'file' parameter*/
if (!reply->ctype && objlen) {
memprintf(err, "a content type must be defined when non-empty payload is configured");
goto error;
}
if (reply->ctype && !b_data(&reply->body.obj)) {
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"),
reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
if (b_room(&reply->body.obj) < global.tune.maxrewrite) {
ha_warning("parsing [%s:%d] : 'http-%s return' : the payload runs over the buffer space reserved to headers rewriting."
" It may lead to internal errors if strict rewriting mode is enabled.\n",
px->conf.args.file, px->conf.args.line,
(rule->from == ACT_F_HTTP_REQ ? "request" : "response"));
}
}
else if (reply->type == HTTP_REPLY_LOGFMT) { /* log-format payload using 'lf-file' of 'lf-string' parameter */
LIST_INIT(&reply->body.fmt);
if (!reply->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, &reply->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;
}
rule->arg.http_reply = reply;
rule->flags |= ACT_FLAG_FINAL;
rule->action = ACT_CUSTOM;
rule->check_ptr = check_http_return_action;
rule->action_ptr = http_action_return;
rule->release_ptr = release_http_return;
free(obj);
*orig_arg = cur_arg;
return ACT_RET_PRS_OK;
error:
free(obj);
if (fd >= 0)
close(fd);
release_http_reply(reply);
return ACT_RET_PRS_ERR;
}
/************************************************************************/

View File

@ -1241,6 +1241,356 @@ out:
return buf;
}
/* Parse an "http reply". It returns the reply on success or NULL on error. This
* function creates one of the following http replies :
*
* - HTTP_REPLY_EMPTY : dummy response, no payload
* - HTTP_REPLY_ERRMSG : implicit error message depending on the status code or explicit one
* - HTTP_REPLY_ERRFILES : points on an http-errors section (resolved during post-parsing)
* - HTTP_REPLY_RAW : explicit file object ('file' argument)
* - HTTP_REPLY_LOGFMT : 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 (HTTP_REPLY_ERRFILES), the real error message should be resolved
* during the configuration validity check or dynamically. It is the caller
* responsibility to choose. If no status code is configured, <default_status>
* is set.
*/
struct http_reply *http_parse_http_reply(const char **args, int *orig_arg, struct proxy *px,
int default_status, char **errmsg)
{
struct logformat_node *lf, *lfb;
struct http_reply *reply = NULL;
struct http_reply_hdr *hdr, *hdrb;
struct stat stat;
const char *act_arg = NULL;
char *obj = NULL;
int cur_arg, cap, objlen = 0, fd = -1;
reply = calloc(1, sizeof(*reply));
if (!reply) {
memprintf(errmsg, "out of memory");
goto error;
}
LIST_INIT(&reply->hdrs);
reply->type = HTTP_REPLY_EMPTY;
reply->status = default_status;
cap = ((px->conf.args.ctx == ARGC_HRQ)
? ((px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR)
: ((px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR));
cur_arg = *orig_arg;
while (*args[cur_arg]) {
if (strcmp(args[cur_arg], "status") == 0) {
cur_arg++;
if (!*args[cur_arg]) {
memprintf(errmsg, "'%s' expects <status_code> as argument", args[cur_arg-1]);
goto error;
}
reply->status = atol(args[cur_arg]);
if (reply->status < 200 || reply->status > 599) {
memprintf(errmsg, "Unexpected status code '%d'", reply->status);
goto error;
}
cur_arg++;
}
else if (strcmp(args[cur_arg], "content-type") == 0) {
cur_arg++;
if (!*args[cur_arg]) {
memprintf(errmsg, "'%s' expects <ctype> as argument", args[cur_arg-1]);
goto error;
}
free(reply->ctype);
reply->ctype = strdup(args[cur_arg]);
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfiles") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <name> as argument", args[cur_arg-1]);
goto error;
}
reply->body.http_errors = strdup(args[cur_arg]);
if (!reply->body.http_errors) {
memprintf(errmsg, "out of memory");
goto error;
}
reply->type = HTTP_REPLY_ERRFILES;
cur_arg++;
}
else if (strcmp(args[cur_arg], "default-errorfiles") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "unexpected '%s' argument, '%s' already defined", args[cur_arg], act_arg);
goto error;
}
act_arg = args[cur_arg];
reply->type = HTTP_REPLY_ERRMSG;
cur_arg++;
}
else if (strcmp(args[cur_arg], "errorfile") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <fmt> as argument", args[cur_arg-1]);
goto error;
}
reply->body.errmsg = http_load_errorfile(args[cur_arg], errmsg);
if (!reply->body.errmsg) {
goto error;
}
reply->type = HTTP_REPLY_ERRMSG;
cur_arg++;
}
else if (strcmp(args[cur_arg], "file") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <file> as argument", args[cur_arg-1]);
goto error;
}
fd = open(args[cur_arg], O_RDONLY);
if ((fd < 0) || (fstat(fd, &stat) < 0)) {
memprintf(errmsg, "error opening file '%s'", args[cur_arg]);
goto error;
}
if (stat.st_size > global.tune.bufsize) {
memprintf(errmsg, "file '%s' exceeds the buffer size (%lld > %d)",
args[cur_arg], (long long)stat.st_size, global.tune.bufsize);
goto error;
}
objlen = stat.st_size;
obj = malloc(objlen);
if (!obj || read(fd, obj, objlen) != objlen) {
memprintf(errmsg, "error reading file '%s'", args[cur_arg]);
goto error;
}
close(fd);
fd = -1;
reply->type = HTTP_REPLY_RAW;
chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
obj = NULL;
cur_arg++;
}
else if (strcmp(args[cur_arg], "string") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <str> as argument", args[cur_arg-1]);
goto error;
}
obj = strdup(args[cur_arg]);
objlen = strlen(args[cur_arg]);
if (!obj) {
memprintf(errmsg, "out of memory");
goto error;
}
reply->type = HTTP_REPLY_RAW;
chunk_initlen(&reply->body.obj, obj, global.tune.bufsize, objlen);
obj = NULL;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-file") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <file> as argument", args[cur_arg-1]);
goto error;
}
fd = open(args[cur_arg], O_RDONLY);
if ((fd < 0) || (fstat(fd, &stat) < 0)) {
memprintf(errmsg, "error opening file '%s'", args[cur_arg]);
goto error;
}
if (stat.st_size > global.tune.bufsize) {
memprintf(errmsg, "file '%s' exceeds the buffer size (%lld > %d)",
args[cur_arg], (long long)stat.st_size, global.tune.bufsize);
goto error;
}
objlen = stat.st_size;
obj = malloc(objlen + 1);
if (!obj || read(fd, obj, objlen) != objlen) {
memprintf(errmsg, "error reading file '%s'", args[cur_arg]);
goto error;
}
close(fd);
fd = -1;
obj[objlen] = '\0';
reply->type = HTTP_REPLY_LOGFMT;
cur_arg++;
}
else if (strcmp(args[cur_arg], "lf-string") == 0) {
if (reply->type != HTTP_REPLY_EMPTY) {
memprintf(errmsg, "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(errmsg, "'%s' expects <fmt> as argument", args[cur_arg-1]);
goto error;
}
obj = strdup(args[cur_arg]);
objlen = strlen(args[cur_arg]);
reply->type = HTTP_REPLY_LOGFMT;
cur_arg++;
}
else if (strcmp(args[cur_arg], "hdr") == 0) {
cur_arg++;
if (!*args[cur_arg] || !*args[cur_arg+1]) {
memprintf(errmsg, "'%s' expects <name> and <value> as arguments", args[cur_arg-1]);
goto error;
}
if (strcasecmp(args[cur_arg], "content-length") == 0 ||
strcasecmp(args[cur_arg], "transfer-encoding") == 0 ||
strcasecmp(args[cur_arg], "content-type") == 0) {
ha_warning("parsing [%s:%d] : header '%s' always ignored by the http reply.\n",
px->conf.args.file, px->conf.args.line, args[cur_arg]);
cur_arg += 2;
continue;
}
hdr = calloc(1, sizeof(*hdr));
if (!hdr) {
memprintf(errmsg, "'%s' : out of memory", args[cur_arg-1]);
goto error;
}
LIST_INIT(&hdr->value);
hdr->name = ist(strdup(args[cur_arg]));
if (!isttest(hdr->name)) {
memprintf(errmsg, "out of memory");
goto error;
}
LIST_ADDQ(&reply->hdrs, &hdr->list);
if (!parse_logformat_string(args[cur_arg+1], px, &hdr->value, LOG_OPT_HTTP, cap, errmsg))
goto error;
free(px->conf.lfs_file);
px->conf.lfs_file = strdup(px->conf.args.file);
px->conf.lfs_line = px->conf.args.line;
cur_arg += 2;
}
else
break;
}
if (reply->type == HTTP_REPLY_EMPTY) { /* no payload */
if (reply->ctype) {
ha_warning("parsing [%s:%d] : content-type '%s' ignored by the http reply because"
" neither errorfile nor payload defined.\n",
px->conf.args.file, px->conf.args.line, reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
}
else if (reply->type == HTTP_REPLY_ERRFILES || reply->type == HTTP_REPLY_ERRMSG) { /* errorfiles or errorfile */
if (reply->type != HTTP_REPLY_ERRMSG || !reply->body.errmsg) {
/* default errorfile or errorfiles: check the status */
int rc;
for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
if (http_err_codes[rc] == reply->status)
break;
}
if (rc >= HTTP_ERR_SIZE) {
memprintf(errmsg, "status code '%d' not handled by default with '%s' argument.",
reply->status, act_arg);
goto error;
}
}
if (reply->ctype) {
ha_warning("parsing [%s:%d] : content-type '%s' ignored by the http reply when used "
"with an erorrfile.\n",
px->conf.args.file, px->conf.args.line, reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
if (!LIST_ISEMPTY(&reply->hdrs)) {
ha_warning("parsing [%s:%d] : hdr parameters ignored by the http reply when used "
"with an erorrfile.\n",
px->conf.args.file, px->conf.args.line);
list_for_each_entry_safe(hdr, hdrb, &reply->hdrs, list) {
LIST_DEL(&hdr->list);
list_for_each_entry_safe(lf, lfb, &hdr->value, list) {
LIST_DEL(&lf->list);
release_sample_expr(lf->expr);
free(lf->arg);
free(lf);
}
istfree(&hdr->name);
free(hdr);
}
}
}
else if (reply->type == HTTP_REPLY_RAW) { /* explicit parameter using 'file' parameter*/
if (!reply->ctype && objlen) {
memprintf(errmsg, "a content type must be defined when non-empty payload is configured");
goto error;
}
if (reply->ctype && !b_data(&reply->body.obj)) {
ha_warning("parsing [%s:%d] : content-type '%s' ignored by the http reply when used "
"with an emtpy payload.\n",
px->conf.args.file, px->conf.args.line, reply->ctype);
free(reply->ctype);
reply->ctype = NULL;
}
if (b_room(&reply->body.obj) < global.tune.maxrewrite) {
ha_warning("parsing [%s:%d] : http reply payload runs over the buffer space reserved to headers rewriting."
" It may lead to internal errors if strict rewriting mode is enabled.\n",
px->conf.args.file, px->conf.args.line);
}
}
else if (reply->type == HTTP_REPLY_LOGFMT) { /* log-format payload using 'lf-file' of 'lf-string' parameter */
LIST_INIT(&reply->body.fmt);
if (!reply->ctype) {
memprintf(errmsg, "a content type must be defined with a log-format payload");
goto error;
}
if (!parse_logformat_string(obj, px, &reply->body.fmt, LOG_OPT_HTTP, cap, errmsg))
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);
*orig_arg = cur_arg;
return reply;
error:
free(obj);
if (fd >= 0)
close(fd);
release_http_reply(reply);
return NULL;
}
/* Parses the "errorloc[302|303]" proxy keyword */
static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,