]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: http-htx: Use a dedicated function to parse http reply arguments
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 13 May 2020 12:36:55 +0000 (14:36 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 20 May 2020 16:27:13 +0000 (18:27 +0200)
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.

include/proto/http_htx.h
src/http_act.c
src/http_htx.c

index 2307838e5912524a094dd30d935ecdd842274cc2..40e608a93b5bdbea366ab4697032ce9eb864e53e 100644 (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);
index 04c27eaee684dada3027916109a609d4af357c50..d15030378491133de298e7d655f971153c7234bd 100644 (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;
-
-       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;
-       }
+       /* 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;
 
-       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;
 }
 
 /************************************************************************/
index 4e8ca0b9604852ed55a9502c3db8660130ae852c..3015320ec46093e18ff36da2015cb35ab7f91e4b 100644 (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,