]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: http: create http_msg.c to place there some legacy HTTP parts
authorWilly Tarreau <w@1wt.eu>
Tue, 11 Dec 2018 10:42:27 +0000 (11:42 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 11 Dec 2018 16:15:13 +0000 (17:15 +0100)
Lots of HTTP code still uses struct http_msg. Not only this code is
still huge, but it's part of the legacy interface. Let's move most
of these functions to a separate file http_msg.c to make it more
visible which file relies on what. It's mostly symmetrical with
what is present in http_htx.c.

The function http_transform_header_str() which used to rely on two
function pointers to look up a header was simplified to rely on
two variants http_legacy_replace_{,full_}header(), making both
sides of the function much simpler.

No code was changed beyond these moves.

Makefile
include/proto/proto_http.h
src/http_msg.c [new file with mode: 0644]
src/proto_http.c

index 141a6e6d36d00c8ed9a502a7459e140afe5ef35c..587bc04867720ad0e1b5030a0d55e61a1dcb92ae 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -947,7 +947,7 @@ OBJS = src/proto_http.o src/cfgparse-listen.o src/proto_htx.o src/stream.o    \
        src/http.o src/hpack-dec.o src/action.o src/proto_udp.o src/http_acl.o \
        src/xxhash.o src/hpack-enc.o src/h2.o src/freq_ctr.o src/lru.o         \
        src/protocol.o src/arg.o src/hpack-huff.o src/hdr_idx.o src/base64.o   \
-       src/hash.o src/mailers.o src/activity.o
+       src/hash.o src/mailers.o src/activity.o src/http_msg.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
index 3e7701cf215312cb6fbc3e36431fcfa1d09bc09c..c32a555c2315b58e2db5c0691c1e2f4610f36dea 100644 (file)
@@ -51,6 +51,14 @@ int http_upgrade_v09_to_v10(struct http_txn *txn);
 void http_msg_analyzer(struct http_msg *msg, struct hdr_idx *idx);
 void http_txn_reset_req(struct http_txn *txn);
 void http_txn_reset_res(struct http_txn *txn);
+int http_legacy_replace_header(struct hdr_idx *idx, struct http_msg *msg,
+                               const char *name, unsigned int name_len,
+                               const char *str, struct my_regex *re,
+                               struct buffer *output);
+int http_legacy_replace_full_header(struct hdr_idx *idx, struct http_msg *msg,
+                                    const char *name, unsigned int name_len,
+                                    const char *str, struct my_regex *re,
+                                    struct buffer *output);
 
 /* Export HTX analyzers and helpers */
 // FIXME: Rename all these functions http_* once legacy code will be removed
diff --git a/src/http_msg.c b/src/http_msg.c
new file mode 100644 (file)
index 0000000..fa5606d
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Legacy HTTP protocol manipulation
+ * If you think you need something from this file, you're mistaken as it will
+ * soon be removed. Please check http_htx.c instead!
+ *
+ * Copyright 2000-2011 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+#include <proto/channel.h>
+#include <proto/hdr_idx.h>
+#include <proto/proto_http.h>
+
+/*
+ * Adds a header and its CRLF at the tail of the message's buffer, just before
+ * the last CRLF. <len> bytes are copied, not counting the CRLF.
+ * The header is also automatically added to the index <hdr_idx>, and the end
+ * of headers is automatically adjusted. The number of bytes added is returned
+ * on success, otherwise <0 is returned indicating an error.
+ */
+int http_header_add_tail2(struct http_msg *msg,
+                          struct hdr_idx *hdr_idx, const char *text, int len)
+{
+       int bytes;
+
+       bytes = ci_insert_line2(msg->chn, msg->eoh, text, len);
+       if (!bytes)
+               return -1;
+       http_msg_move_end(msg, bytes);
+       return hdr_idx_add(len, 1, hdr_idx, hdr_idx->tail);
+}
+
+/* Find the first or next occurrence of header <name> in message buffer <sol>
+ * using headers index <idx>, and return it in the <ctx> structure. This
+ * structure holds everything necessary to use the header and find next
+ * occurrence. If its <idx> member is 0, the header is searched from the
+ * beginning. Otherwise, the next occurrence is returned. The function returns
+ * 1 when it finds a value, and 0 when there is no more. It is very similar to
+ * http_find_header2() except that it is designed to work with full-line headers
+ * whose comma is not a delimiter but is part of the syntax. As a special case,
+ * if ctx->val is NULL when searching for a new values of a header, the current
+ * header is rescanned. This allows rescanning after a header deletion.
+ */
+int http_find_full_header2(const char *name, int len,
+                           char *sol, struct hdr_idx *idx,
+                           struct hdr_ctx *ctx)
+{
+       char *eol, *sov;
+       int cur_idx, old_idx;
+
+       cur_idx = ctx->idx;
+       if (cur_idx) {
+               /* We have previously returned a header, let's search another one */
+               sol = ctx->line;
+               eol = sol + idx->v[cur_idx].len;
+               goto next_hdr;
+       }
+
+       /* first request for this header */
+       sol += hdr_idx_first_pos(idx);
+       old_idx = 0;
+       cur_idx = hdr_idx_first_idx(idx);
+       while (cur_idx) {
+               eol = sol + idx->v[cur_idx].len;
+
+               if (len == 0) {
+                       /* No argument was passed, we want any header.
+                        * To achieve this, we simply build a fake request. */
+                       while (sol + len < eol && sol[len] != ':')
+                               len++;
+                       name = sol;
+               }
+
+               if ((len < eol - sol) &&
+                   (sol[len] == ':') &&
+                   (strncasecmp(sol, name, len) == 0)) {
+                       ctx->del = len;
+                       sov = sol + len + 1;
+                       while (sov < eol && HTTP_IS_LWS(*sov))
+                               sov++;
+
+                       ctx->line = sol;
+                       ctx->prev = old_idx;
+                       ctx->idx  = cur_idx;
+                       ctx->val  = sov - sol;
+                       ctx->tws = 0;
+                       while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
+                               eol--;
+                               ctx->tws++;
+                       }
+                       ctx->vlen = eol - sov;
+                       return 1;
+               }
+       next_hdr:
+               sol = eol + idx->v[cur_idx].cr + 1;
+               old_idx = cur_idx;
+               cur_idx = idx->v[cur_idx].next;
+       }
+       return 0;
+}
+
+/* Find the first or next header field in message buffer <sol> using headers
+ * index <idx>, and return it in the <ctx> structure. This structure holds
+ * everything necessary to use the header and find next occurrence. If its
+ * <idx> member is 0, the first header is retrieved. Otherwise, the next
+ * occurrence is returned. The function returns 1 when it finds a value, and
+ * 0 when there is no more. It is equivalent to http_find_full_header2() with
+ * no header name.
+ */
+int http_find_next_header(char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx)
+{
+       char *eol, *sov;
+       int cur_idx, old_idx;
+       int len;
+
+       cur_idx = ctx->idx;
+       if (cur_idx) {
+               /* We have previously returned a header, let's search another one */
+               sol = ctx->line;
+               eol = sol + idx->v[cur_idx].len;
+               goto next_hdr;
+       }
+
+       /* first request for this header */
+       sol += hdr_idx_first_pos(idx);
+       old_idx = 0;
+       cur_idx = hdr_idx_first_idx(idx);
+       while (cur_idx) {
+               eol = sol + idx->v[cur_idx].len;
+
+               len = 0;
+               while (1) {
+                       if (len >= eol - sol)
+                               goto next_hdr;
+                       if (sol[len] == ':')
+                               break;
+                       len++;
+               }
+
+               ctx->del = len;
+               sov = sol + len + 1;
+               while (sov < eol && HTTP_IS_LWS(*sov))
+                       sov++;
+
+               ctx->line = sol;
+               ctx->prev = old_idx;
+               ctx->idx  = cur_idx;
+               ctx->val  = sov - sol;
+               ctx->tws = 0;
+
+               while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
+                       eol--;
+                       ctx->tws++;
+               }
+               ctx->vlen = eol - sov;
+               return 1;
+
+       next_hdr:
+               sol = eol + idx->v[cur_idx].cr + 1;
+               old_idx = cur_idx;
+               cur_idx = idx->v[cur_idx].next;
+       }
+       return 0;
+}
+
+/* Find the first or next occurrence of header <name> in message buffer <sol>
+ * using headers index <idx>, and return it in the <ctx> structure. This
+ * structure holds everything necessary to use the header and find next
+ * occurrence. If its <idx> member is 0, the header is searched from the
+ * beginning. Otherwise, the next occurrence is returned. The function returns
+ * 1 when it finds a value, and 0 when there is no more. It is designed to work
+ * with headers defined as comma-separated lists. As a special case, if ctx->val
+ * is NULL when searching for a new values of a header, the current header is
+ * rescanned. This allows rescanning after a header deletion.
+ */
+int http_find_header2(const char *name, int len,
+                     char *sol, struct hdr_idx *idx,
+                     struct hdr_ctx *ctx)
+{
+       char *eol, *sov;
+       int cur_idx, old_idx;
+
+       cur_idx = ctx->idx;
+       if (cur_idx) {
+               /* We have previously returned a value, let's search
+                * another one on the same line.
+                */
+               sol = ctx->line;
+               ctx->del = ctx->val + ctx->vlen + ctx->tws;
+               sov = sol + ctx->del;
+               eol = sol + idx->v[cur_idx].len;
+
+               if (sov >= eol)
+                       /* no more values in this header */
+                       goto next_hdr;
+
+               /* values remaining for this header, skip the comma but save it
+                * for later use (eg: for header deletion).
+                */
+               sov++;
+               while (sov < eol && HTTP_IS_LWS((*sov)))
+                       sov++;
+
+               goto return_hdr;
+       }
+
+       /* first request for this header */
+       sol += hdr_idx_first_pos(idx);
+       old_idx = 0;
+       cur_idx = hdr_idx_first_idx(idx);
+       while (cur_idx) {
+               eol = sol + idx->v[cur_idx].len;
+
+               if (len == 0) {
+                       /* No argument was passed, we want any header.
+                        * To achieve this, we simply build a fake request. */
+                       while (sol + len < eol && sol[len] != ':')
+                               len++;
+                       name = sol;
+               }
+
+               if ((len < eol - sol) &&
+                   (sol[len] == ':') &&
+                   (strncasecmp(sol, name, len) == 0)) {
+                       ctx->del = len;
+                       sov = sol + len + 1;
+                       while (sov < eol && HTTP_IS_LWS(*sov))
+                               sov++;
+
+                       ctx->line = sol;
+                       ctx->prev = old_idx;
+               return_hdr:
+                       ctx->idx  = cur_idx;
+                       ctx->val  = sov - sol;
+
+                       eol = http_find_hdr_value_end(sov, eol);
+                       ctx->tws = 0;
+                       while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
+                               eol--;
+                               ctx->tws++;
+                       }
+                       ctx->vlen = eol - sov;
+                       return 1;
+               }
+       next_hdr:
+               sol = eol + idx->v[cur_idx].cr + 1;
+               old_idx = cur_idx;
+               cur_idx = idx->v[cur_idx].next;
+       }
+       return 0;
+}
+
+/* Remove one value of a header. This only works on a <ctx> returned by one of
+ * the http_find_header functions. The value is removed, as well as surrounding
+ * commas if any. If the removed value was alone, the whole header is removed.
+ * The ctx is always updated accordingly, as well as the buffer and HTTP
+ * message <msg>. The new index is returned. If it is zero, it means there is
+ * no more header, so any processing may stop. The ctx is always left in a form
+ * that can be handled by http_find_header2() to find next occurrence.
+ */
+int http_remove_header2(struct http_msg *msg, struct hdr_idx *idx, struct hdr_ctx *ctx)
+{
+       int cur_idx = ctx->idx;
+       char *sol = ctx->line;
+       struct hdr_idx_elem *hdr;
+       int delta, skip_comma;
+
+       if (!cur_idx)
+               return 0;
+
+       hdr = &idx->v[cur_idx];
+       if (sol[ctx->del] == ':' && ctx->val + ctx->vlen + ctx->tws == hdr->len) {
+               /* This was the only value of the header, we must now remove it entirely. */
+               delta = b_rep_blk(&msg->chn->buf, sol, sol + hdr->len + hdr->cr + 1, NULL, 0);
+               http_msg_move_end(msg, delta);
+               idx->used--;
+               hdr->len = 0;   /* unused entry */
+               idx->v[ctx->prev].next = idx->v[ctx->idx].next;
+               if (idx->tail == ctx->idx)
+                       idx->tail = ctx->prev;
+               ctx->idx = ctx->prev;    /* walk back to the end of previous header */
+               ctx->line -= idx->v[ctx->idx].len + idx->v[ctx->idx].cr + 1;
+               ctx->val = idx->v[ctx->idx].len; /* point to end of previous header */
+               ctx->tws = ctx->vlen = 0;
+               return ctx->idx;
+       }
+
+       /* This was not the only value of this header. We have to remove between
+        * ctx->del+1 and ctx->val+ctx->vlen+ctx->tws+1 included. If it is the
+        * last entry of the list, we remove the last separator.
+        */
+
+       skip_comma = (ctx->val + ctx->vlen + ctx->tws == hdr->len) ? 0 : 1;
+       delta = b_rep_blk(&msg->chn->buf, sol + ctx->del + skip_comma,
+                               sol + ctx->val + ctx->vlen + ctx->tws + skip_comma,
+                               NULL, 0);
+       hdr->len += delta;
+       http_msg_move_end(msg, delta);
+       ctx->val = ctx->del;
+       ctx->tws = ctx->vlen = 0;
+       return ctx->idx;
+}
+
+int http_legacy_replace_header(struct hdr_idx *idx, struct http_msg *msg,
+                               const char *name, unsigned int name_len,
+                               const char *str, struct my_regex *re,
+                               struct buffer *output)
+{
+       struct hdr_ctx ctx;
+       char *buf = ci_head(msg->chn);
+
+       ctx.idx = 0;
+       while (http_find_header2(name, name_len, buf, idx, &ctx)) {
+               struct hdr_idx_elem *hdr = idx->v + ctx.idx;
+               int delta, len;
+               char *val = ctx.line + ctx.val;
+               char* val_end = val + ctx.vlen;
+
+               if (!regex_exec_match2(re, val, val_end-val, MAX_MATCH, pmatch, 0))
+                       continue;
+
+               len = exp_replace(output->area, output->size, val, str, pmatch);
+               if (len == -1)
+                       return -1;
+
+               delta = b_rep_blk(&msg->chn->buf, val, val_end, output->area, len);
+
+               hdr->len += delta;
+               http_msg_move_end(msg, delta);
+
+               /* Adjust the length of the current value of the index. */
+               ctx.vlen += delta;
+       }
+       return 0;
+}
+
+int http_legacy_replace_full_header(struct hdr_idx *idx, struct http_msg *msg,
+                                    const char *name, unsigned int name_len,
+                                    const char *str, struct my_regex *re,
+                                    struct buffer *output)
+{
+       struct hdr_ctx ctx;
+       char *buf = ci_head(msg->chn);
+
+       ctx.idx = 0;
+       while (http_find_full_header2(name, name_len, buf, idx, &ctx)) {
+               struct hdr_idx_elem *hdr = idx->v + ctx.idx;
+               int delta, len;
+               char *val = ctx.line + ctx.val;
+               char* val_end = val + ctx.vlen;
+
+               if (!regex_exec_match2(re, val, val_end-val, MAX_MATCH, pmatch, 0))
+                       continue;
+
+               len = exp_replace(output->area, output->size, val, str, pmatch);
+               if (len == -1)
+                       return -1;
+
+               delta = b_rep_blk(&msg->chn->buf, val, val_end, output->area, len);
+
+               hdr->len += delta;
+               http_msg_move_end(msg, delta);
+
+               /* Adjust the length of the current value of the index. */
+               ctx.vlen += delta;
+       }
+       return 0;
+}
+
+/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
+ * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
+ * performed over the whole headers. Otherwise it must contain a valid header
+ * context, initialised with ctx->idx=0 for the first lookup in a series. If
+ * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
+ * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
+ * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
+ * -1. The value fetch stops at commas, so this function is suited for use with
+ * list headers.
+ * The return value is 0 if nothing was found, or non-zero otherwise.
+ */
+unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen,
+                         struct hdr_idx *idx, int occ,
+                         struct hdr_ctx *ctx, char **vptr, size_t *vlen)
+{
+       struct hdr_ctx local_ctx;
+       char *ptr_hist[MAX_HDR_HISTORY];
+       unsigned int len_hist[MAX_HDR_HISTORY];
+       unsigned int hist_ptr;
+       int found;
+
+       if (!ctx) {
+               local_ctx.idx = 0;
+               ctx = &local_ctx;
+       }
+
+       if (occ >= 0) {
+               /* search from the beginning */
+               while (http_find_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
+                       occ--;
+                       if (occ <= 0) {
+                               *vptr = ctx->line + ctx->val;
+                               *vlen = ctx->vlen;
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+
+       /* negative occurrence, we scan all the list then walk back */
+       if (-occ > MAX_HDR_HISTORY)
+               return 0;
+
+       found = hist_ptr = 0;
+       while (http_find_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
+               ptr_hist[hist_ptr] = ctx->line + ctx->val;
+               len_hist[hist_ptr] = ctx->vlen;
+               if (++hist_ptr >= MAX_HDR_HISTORY)
+                       hist_ptr = 0;
+               found++;
+       }
+       if (-occ > found)
+               return 0;
+       /* OK now we have the last occurrence in [hist_ptr-1], and we need to
+        * find occurrence -occ. 0 <= hist_ptr < MAX_HDR_HISTORY, and we have
+        * -10 <= occ <= -1. So we have to check [hist_ptr%MAX_HDR_HISTORY+occ]
+        * to remain in the 0..9 range.
+        */
+       hist_ptr += occ + MAX_HDR_HISTORY;
+       if (hist_ptr >= MAX_HDR_HISTORY)
+               hist_ptr -= MAX_HDR_HISTORY;
+       *vptr = ptr_hist[hist_ptr];
+       *vlen = len_hist[hist_ptr];
+       return 1;
+}
+
+/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
+ * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
+ * performed over the whole headers. Otherwise it must contain a valid header
+ * context, initialised with ctx->idx=0 for the first lookup in a series. If
+ * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
+ * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
+ * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
+ * -1. This function differs from http_get_hdr() in that it only returns full
+ * line header values and does not stop at commas.
+ * The return value is 0 if nothing was found, or non-zero otherwise.
+ */
+unsigned int http_get_fhdr(const struct http_msg *msg, const char *hname, int hlen,
+                          struct hdr_idx *idx, int occ,
+                          struct hdr_ctx *ctx, char **vptr, size_t *vlen)
+{
+       struct hdr_ctx local_ctx;
+       char *ptr_hist[MAX_HDR_HISTORY];
+       unsigned int len_hist[MAX_HDR_HISTORY];
+       unsigned int hist_ptr;
+       int found;
+
+       if (!ctx) {
+               local_ctx.idx = 0;
+               ctx = &local_ctx;
+       }
+
+       if (occ >= 0) {
+               /* search from the beginning */
+               while (http_find_full_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
+                       occ--;
+                       if (occ <= 0) {
+                               *vptr = ctx->line + ctx->val;
+                               *vlen = ctx->vlen;
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+
+       /* negative occurrence, we scan all the list then walk back */
+       if (-occ > MAX_HDR_HISTORY)
+               return 0;
+
+       found = hist_ptr = 0;
+       while (http_find_full_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
+               ptr_hist[hist_ptr] = ctx->line + ctx->val;
+               len_hist[hist_ptr] = ctx->vlen;
+               if (++hist_ptr >= MAX_HDR_HISTORY)
+                       hist_ptr = 0;
+               found++;
+       }
+       if (-occ > found)
+               return 0;
+
+       /* OK now we have the last occurrence in [hist_ptr-1], and we need to
+        * find occurrence -occ. 0 <= hist_ptr < MAX_HDR_HISTORY, and we have
+        * -10 <= occ <= -1. So we have to check [hist_ptr%MAX_HDR_HISTORY+occ]
+        * to remain in the 0..9 range.
+        */
+       hist_ptr += occ + MAX_HDR_HISTORY;
+       if (hist_ptr >= MAX_HDR_HISTORY)
+               hist_ptr -= MAX_HDR_HISTORY;
+       *vptr = ptr_hist[hist_ptr];
+       *vlen = len_hist[hist_ptr];
+       return 1;
+}
+
index 6c7cfcbc075ce8f12210f72144bb23bd2b0faa42..a6492b667be29e1bfe2aef5bf7f9ea4f6a1047b5 100644 (file)
@@ -86,296 +86,6 @@ const char *stat_status_codes[STAT_STATUS_SIZE] = {
        [STAT_STATUS_UNKN] = "UNKN",
 };
 
-/*
- * Adds a header and its CRLF at the tail of the message's buffer, just before
- * the last CRLF. <len> bytes are copied, not counting the CRLF.
- * The header is also automatically added to the index <hdr_idx>, and the end
- * of headers is automatically adjusted. The number of bytes added is returned
- * on success, otherwise <0 is returned indicating an error.
- */
-int http_header_add_tail2(struct http_msg *msg,
-                          struct hdr_idx *hdr_idx, const char *text, int len)
-{
-       int bytes;
-
-       bytes = ci_insert_line2(msg->chn, msg->eoh, text, len);
-       if (!bytes)
-               return -1;
-       http_msg_move_end(msg, bytes);
-       return hdr_idx_add(len, 1, hdr_idx, hdr_idx->tail);
-}
-
-/* Find the first or next occurrence of header <name> in message buffer <sol>
- * using headers index <idx>, and return it in the <ctx> structure. This
- * structure holds everything necessary to use the header and find next
- * occurrence. If its <idx> member is 0, the header is searched from the
- * beginning. Otherwise, the next occurrence is returned. The function returns
- * 1 when it finds a value, and 0 when there is no more. It is very similar to
- * http_find_header2() except that it is designed to work with full-line headers
- * whose comma is not a delimiter but is part of the syntax. As a special case,
- * if ctx->val is NULL when searching for a new values of a header, the current
- * header is rescanned. This allows rescanning after a header deletion.
- */
-int http_find_full_header2(const char *name, int len,
-                           char *sol, struct hdr_idx *idx,
-                           struct hdr_ctx *ctx)
-{
-       char *eol, *sov;
-       int cur_idx, old_idx;
-
-       cur_idx = ctx->idx;
-       if (cur_idx) {
-               /* We have previously returned a header, let's search another one */
-               sol = ctx->line;
-               eol = sol + idx->v[cur_idx].len;
-               goto next_hdr;
-       }
-
-       /* first request for this header */
-       sol += hdr_idx_first_pos(idx);
-       old_idx = 0;
-       cur_idx = hdr_idx_first_idx(idx);
-       while (cur_idx) {
-               eol = sol + idx->v[cur_idx].len;
-
-               if (len == 0) {
-                       /* No argument was passed, we want any header.
-                        * To achieve this, we simply build a fake request. */
-                       while (sol + len < eol && sol[len] != ':')
-                               len++;
-                       name = sol;
-               }
-
-               if ((len < eol - sol) &&
-                   (sol[len] == ':') &&
-                   (strncasecmp(sol, name, len) == 0)) {
-                       ctx->del = len;
-                       sov = sol + len + 1;
-                       while (sov < eol && HTTP_IS_LWS(*sov))
-                               sov++;
-
-                       ctx->line = sol;
-                       ctx->prev = old_idx;
-                       ctx->idx  = cur_idx;
-                       ctx->val  = sov - sol;
-                       ctx->tws = 0;
-                       while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
-                               eol--;
-                               ctx->tws++;
-                       }
-                       ctx->vlen = eol - sov;
-                       return 1;
-               }
-       next_hdr:
-               sol = eol + idx->v[cur_idx].cr + 1;
-               old_idx = cur_idx;
-               cur_idx = idx->v[cur_idx].next;
-       }
-       return 0;
-}
-
-/* Find the first or next header field in message buffer <sol> using headers
- * index <idx>, and return it in the <ctx> structure. This structure holds
- * everything necessary to use the header and find next occurrence. If its
- * <idx> member is 0, the first header is retrieved. Otherwise, the next
- * occurrence is returned. The function returns 1 when it finds a value, and
- * 0 when there is no more. It is equivalent to http_find_full_header2() with
- * no header name.
- */
-int http_find_next_header(char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx)
-{
-       char *eol, *sov;
-       int cur_idx, old_idx;
-       int len;
-
-       cur_idx = ctx->idx;
-       if (cur_idx) {
-               /* We have previously returned a header, let's search another one */
-               sol = ctx->line;
-               eol = sol + idx->v[cur_idx].len;
-               goto next_hdr;
-       }
-
-       /* first request for this header */
-       sol += hdr_idx_first_pos(idx);
-       old_idx = 0;
-       cur_idx = hdr_idx_first_idx(idx);
-       while (cur_idx) {
-               eol = sol + idx->v[cur_idx].len;
-
-               len = 0;
-               while (1) {
-                       if (len >= eol - sol)
-                               goto next_hdr;
-                       if (sol[len] == ':')
-                               break;
-                       len++;
-               }
-
-               ctx->del = len;
-               sov = sol + len + 1;
-               while (sov < eol && HTTP_IS_LWS(*sov))
-                       sov++;
-
-               ctx->line = sol;
-               ctx->prev = old_idx;
-               ctx->idx  = cur_idx;
-               ctx->val  = sov - sol;
-               ctx->tws = 0;
-
-               while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
-                       eol--;
-                       ctx->tws++;
-               }
-               ctx->vlen = eol - sov;
-               return 1;
-
-       next_hdr:
-               sol = eol + idx->v[cur_idx].cr + 1;
-               old_idx = cur_idx;
-               cur_idx = idx->v[cur_idx].next;
-       }
-       return 0;
-}
-
-/* Find the first or next occurrence of header <name> in message buffer <sol>
- * using headers index <idx>, and return it in the <ctx> structure. This
- * structure holds everything necessary to use the header and find next
- * occurrence. If its <idx> member is 0, the header is searched from the
- * beginning. Otherwise, the next occurrence is returned. The function returns
- * 1 when it finds a value, and 0 when there is no more. It is designed to work
- * with headers defined as comma-separated lists. As a special case, if ctx->val
- * is NULL when searching for a new values of a header, the current header is
- * rescanned. This allows rescanning after a header deletion.
- */
-int http_find_header2(const char *name, int len,
-                     char *sol, struct hdr_idx *idx,
-                     struct hdr_ctx *ctx)
-{
-       char *eol, *sov;
-       int cur_idx, old_idx;
-
-       cur_idx = ctx->idx;
-       if (cur_idx) {
-               /* We have previously returned a value, let's search
-                * another one on the same line.
-                */
-               sol = ctx->line;
-               ctx->del = ctx->val + ctx->vlen + ctx->tws;
-               sov = sol + ctx->del;
-               eol = sol + idx->v[cur_idx].len;
-
-               if (sov >= eol)
-                       /* no more values in this header */
-                       goto next_hdr;
-
-               /* values remaining for this header, skip the comma but save it
-                * for later use (eg: for header deletion).
-                */
-               sov++;
-               while (sov < eol && HTTP_IS_LWS((*sov)))
-                       sov++;
-
-               goto return_hdr;
-       }
-
-       /* first request for this header */
-       sol += hdr_idx_first_pos(idx);
-       old_idx = 0;
-       cur_idx = hdr_idx_first_idx(idx);
-       while (cur_idx) {
-               eol = sol + idx->v[cur_idx].len;
-
-               if (len == 0) {
-                       /* No argument was passed, we want any header.
-                        * To achieve this, we simply build a fake request. */
-                       while (sol + len < eol && sol[len] != ':')
-                               len++;
-                       name = sol;
-               }
-
-               if ((len < eol - sol) &&
-                   (sol[len] == ':') &&
-                   (strncasecmp(sol, name, len) == 0)) {
-                       ctx->del = len;
-                       sov = sol + len + 1;
-                       while (sov < eol && HTTP_IS_LWS(*sov))
-                               sov++;
-
-                       ctx->line = sol;
-                       ctx->prev = old_idx;
-               return_hdr:
-                       ctx->idx  = cur_idx;
-                       ctx->val  = sov - sol;
-
-                       eol = http_find_hdr_value_end(sov, eol);
-                       ctx->tws = 0;
-                       while (eol > sov && HTTP_IS_LWS(*(eol - 1))) {
-                               eol--;
-                               ctx->tws++;
-                       }
-                       ctx->vlen = eol - sov;
-                       return 1;
-               }
-       next_hdr:
-               sol = eol + idx->v[cur_idx].cr + 1;
-               old_idx = cur_idx;
-               cur_idx = idx->v[cur_idx].next;
-       }
-       return 0;
-}
-
-/* Remove one value of a header. This only works on a <ctx> returned by one of
- * the http_find_header functions. The value is removed, as well as surrounding
- * commas if any. If the removed value was alone, the whole header is removed.
- * The ctx is always updated accordingly, as well as the buffer and HTTP
- * message <msg>. The new index is returned. If it is zero, it means there is
- * no more header, so any processing may stop. The ctx is always left in a form
- * that can be handled by http_find_header2() to find next occurrence.
- */
-int http_remove_header2(struct http_msg *msg, struct hdr_idx *idx, struct hdr_ctx *ctx)
-{
-       int cur_idx = ctx->idx;
-       char *sol = ctx->line;
-       struct hdr_idx_elem *hdr;
-       int delta, skip_comma;
-
-       if (!cur_idx)
-               return 0;
-
-       hdr = &idx->v[cur_idx];
-       if (sol[ctx->del] == ':' && ctx->val + ctx->vlen + ctx->tws == hdr->len) {
-               /* This was the only value of the header, we must now remove it entirely. */
-               delta = b_rep_blk(&msg->chn->buf, sol, sol + hdr->len + hdr->cr + 1, NULL, 0);
-               http_msg_move_end(msg, delta);
-               idx->used--;
-               hdr->len = 0;   /* unused entry */
-               idx->v[ctx->prev].next = idx->v[ctx->idx].next;
-               if (idx->tail == ctx->idx)
-                       idx->tail = ctx->prev;
-               ctx->idx = ctx->prev;    /* walk back to the end of previous header */
-               ctx->line -= idx->v[ctx->idx].len + idx->v[ctx->idx].cr + 1;
-               ctx->val = idx->v[ctx->idx].len; /* point to end of previous header */
-               ctx->tws = ctx->vlen = 0;
-               return ctx->idx;
-       }
-
-       /* This was not the only value of this header. We have to remove between
-        * ctx->del+1 and ctx->val+ctx->vlen+ctx->tws+1 included. If it is the
-        * last entry of the list, we remove the last separator.
-        */
-
-       skip_comma = (ctx->val + ctx->vlen + ctx->tws == hdr->len) ? 0 : 1;
-       delta = b_rep_blk(&msg->chn->buf, sol + ctx->del + skip_comma,
-                               sol + ctx->val + ctx->vlen + ctx->tws + skip_comma,
-                               NULL, 0);
-       hdr->len += delta;
-       http_msg_move_end(msg, delta);
-       ctx->val = ctx->del;
-       ctx->tws = ctx->vlen = 0;
-       return ctx->idx;
-}
-
 /* This function handles a server error at the stream interface level. The
  * stream interface is assumed to be already in a closed state. An optional
  * message is copied into the input buffer.
@@ -1639,50 +1349,18 @@ int http_transform_header_str(struct stream* s, struct http_msg *msg,
                               const char *str, struct my_regex *re,
                               int action)
 {
-       struct hdr_ctx ctx;
-       char *buf = ci_head(msg->chn);
        struct hdr_idx *idx = &s->txn->hdr_idx;
-       int (*http_find_hdr_func)(const char *name, int len, char *sol,
-                                 struct hdr_idx *idx, struct hdr_ctx *ctx);
        struct buffer *output = get_trash_chunk();
 
-       ctx.idx = 0;
-
        /* Choose the header browsing function. */
        switch (action) {
        case ACT_HTTP_REPLACE_VAL:
-               http_find_hdr_func = http_find_header2;
-               break;
+               return http_legacy_replace_header(idx, msg, name, name_len, str, re, output);
        case ACT_HTTP_REPLACE_HDR:
-               http_find_hdr_func = http_find_full_header2;
-               break;
+               return http_legacy_replace_full_header(idx, msg, name, name_len, str, re, output);
        default: /* impossible */
                return -1;
        }
-
-       while (http_find_hdr_func(name, name_len, buf, idx, &ctx)) {
-               struct hdr_idx_elem *hdr = idx->v + ctx.idx;
-               int delta, len;
-               char *val = ctx.line + ctx.val;
-               char* val_end = val + ctx.vlen;
-
-               if (!regex_exec_match2(re, val, val_end-val, MAX_MATCH, pmatch, 0))
-                       continue;
-
-               len = exp_replace(output->area, output->size, val, str, pmatch);
-               if (len == -1)
-                       return -1;
-
-               delta = b_rep_blk(&msg->chn->buf, val, val_end, output->area, len);
-
-               hdr->len += delta;
-               http_msg_move_end(msg, delta);
-
-               /* Adjust the length of the current value of the index. */
-               ctx.vlen += delta;
-       }
-
-       return 0;
 }
 
 static int http_transform_header(struct stream* s, struct http_msg *msg,
@@ -6164,7 +5842,7 @@ int apply_filters_to_request(struct stream *s, struct channel *req, struct proxy
  *   - there are non-space chars before <from> ;
  *   - there is a CR/LF at or after <next>.
  */
-int del_hdr_value(struct buffer *buf, char **from, char *next)
+static int del_hdr_value(struct buffer *buf, char **from, char *next)
 {
        char *prev = *from;
 
@@ -7435,139 +7113,6 @@ void http_capture_bad_message(struct proxy *proxy, struct stream *s,
                            &ctx, http_show_error_snapshot);
 }
 
-/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
- * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
- * performed over the whole headers. Otherwise it must contain a valid header
- * context, initialised with ctx->idx=0 for the first lookup in a series. If
- * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
- * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
- * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
- * -1. The value fetch stops at commas, so this function is suited for use with
- * list headers.
- * The return value is 0 if nothing was found, or non-zero otherwise.
- */
-unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen,
-                         struct hdr_idx *idx, int occ,
-                         struct hdr_ctx *ctx, char **vptr, size_t *vlen)
-{
-       struct hdr_ctx local_ctx;
-       char *ptr_hist[MAX_HDR_HISTORY];
-       unsigned int len_hist[MAX_HDR_HISTORY];
-       unsigned int hist_ptr;
-       int found;
-
-       if (!ctx) {
-               local_ctx.idx = 0;
-               ctx = &local_ctx;
-       }
-
-       if (occ >= 0) {
-               /* search from the beginning */
-               while (http_find_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
-                       occ--;
-                       if (occ <= 0) {
-                               *vptr = ctx->line + ctx->val;
-                               *vlen = ctx->vlen;
-                               return 1;
-                       }
-               }
-               return 0;
-       }
-
-       /* negative occurrence, we scan all the list then walk back */
-       if (-occ > MAX_HDR_HISTORY)
-               return 0;
-
-       found = hist_ptr = 0;
-       while (http_find_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
-               ptr_hist[hist_ptr] = ctx->line + ctx->val;
-               len_hist[hist_ptr] = ctx->vlen;
-               if (++hist_ptr >= MAX_HDR_HISTORY)
-                       hist_ptr = 0;
-               found++;
-       }
-       if (-occ > found)
-               return 0;
-       /* OK now we have the last occurrence in [hist_ptr-1], and we need to
-        * find occurrence -occ. 0 <= hist_ptr < MAX_HDR_HISTORY, and we have
-        * -10 <= occ <= -1. So we have to check [hist_ptr%MAX_HDR_HISTORY+occ]
-        * to remain in the 0..9 range.
-        */
-       hist_ptr += occ + MAX_HDR_HISTORY;
-       if (hist_ptr >= MAX_HDR_HISTORY)
-               hist_ptr -= MAX_HDR_HISTORY;
-       *vptr = ptr_hist[hist_ptr];
-       *vlen = len_hist[hist_ptr];
-       return 1;
-}
-
-/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
- * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
- * performed over the whole headers. Otherwise it must contain a valid header
- * context, initialised with ctx->idx=0 for the first lookup in a series. If
- * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
- * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
- * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
- * -1. This function differs from http_get_hdr() in that it only returns full
- * line header values and does not stop at commas.
- * The return value is 0 if nothing was found, or non-zero otherwise.
- */
-unsigned int http_get_fhdr(const struct http_msg *msg, const char *hname, int hlen,
-                          struct hdr_idx *idx, int occ,
-                          struct hdr_ctx *ctx, char **vptr, size_t *vlen)
-{
-       struct hdr_ctx local_ctx;
-       char *ptr_hist[MAX_HDR_HISTORY];
-       unsigned int len_hist[MAX_HDR_HISTORY];
-       unsigned int hist_ptr;
-       int found;
-
-       if (!ctx) {
-               local_ctx.idx = 0;
-               ctx = &local_ctx;
-       }
-
-       if (occ >= 0) {
-               /* search from the beginning */
-               while (http_find_full_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
-                       occ--;
-                       if (occ <= 0) {
-                               *vptr = ctx->line + ctx->val;
-                               *vlen = ctx->vlen;
-                               return 1;
-                       }
-               }
-               return 0;
-       }
-
-       /* negative occurrence, we scan all the list then walk back */
-       if (-occ > MAX_HDR_HISTORY)
-               return 0;
-
-       found = hist_ptr = 0;
-       while (http_find_full_header2(hname, hlen, ci_head(msg->chn), idx, ctx)) {
-               ptr_hist[hist_ptr] = ctx->line + ctx->val;
-               len_hist[hist_ptr] = ctx->vlen;
-               if (++hist_ptr >= MAX_HDR_HISTORY)
-                       hist_ptr = 0;
-               found++;
-       }
-       if (-occ > found)
-               return 0;
-
-       /* OK now we have the last occurrence in [hist_ptr-1], and we need to
-        * find occurrence -occ. 0 <= hist_ptr < MAX_HDR_HISTORY, and we have
-        * -10 <= occ <= -1. So we have to check [hist_ptr%MAX_HDR_HISTORY+occ]
-        * to remain in the 0..9 range.
-        */
-       hist_ptr += occ + MAX_HDR_HISTORY;
-       if (hist_ptr >= MAX_HDR_HISTORY)
-               hist_ptr -= MAX_HDR_HISTORY;
-       *vptr = ptr_hist[hist_ptr];
-       *vlen = len_hist[hist_ptr];
-       return 1;
-}
-
 /*
  * Print a debug line with a header. Always stop at the first CR or LF char,
  * so it is safe to pass it a full buffer if needed. If <err> is not NULL, an