MINOR: http_htx: Add functions to manipulate HTX messages in http_htx.c

This file will host all functions to manipulate HTTP messages using the HTX
representation. Functions in this file will be able to be called from anywhere
and are mainly related to the HTTP semantics.
diff --git a/Makefile b/Makefile
index 33f70e9..b4125e1 100644
--- a/Makefile
+++ b/Makefile
@@ -924,7 +924,7 @@
        src/mailers.o src/h2.o src/base64.o src/hash.o src/http.o	\
        src/http_acl.o src/http_fetch.o src/http_conv.o src/http_act.o   \
        src/http_rules.o src/proto_sockpair.o src/proto_htx.o            \
-       src/mux_h1.o src/htx.o
+       src/mux_h1.o src/htx.o src/http_htx.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/include/proto/http_htx.h b/include/proto/http_htx.h
new file mode 100644
index 0000000..1ca7902
--- /dev/null
+++ b/include/proto/http_htx.h
@@ -0,0 +1,40 @@
+/*
+ * include/types/http_htx.h
+ * This file defines function prototypes for HTTP manipulation using the
+ * internal representation.
+ *
+ * Copyright (C) 2018 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _PROTO_HTTP_HTX_H
+#define _PROTO_HTTP_HTX_H
+
+#include <common/buf.h>
+
+#include <types/h1.h>
+#include <types/http_htx.h>
+
+union h1_sl http_find_stline(const struct htx *htx);
+int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full);
+int http_add_header(struct htx *htx, const struct ist n, const struct ist v);
+int http_replace_reqline(struct htx *htx, const union h1_sl sl);
+int http_replace_resline(struct htx *htx, const union h1_sl sl);
+int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data);
+int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist name, const struct ist value);
+int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx);
+
+#endif /* _PROTO_HTTP_HTX_H */
diff --git a/include/types/http_htx.h b/include/types/http_htx.h
new file mode 100644
index 0000000..c8f4197
--- /dev/null
+++ b/include/types/http_htx.h
@@ -0,0 +1,37 @@
+/*
+ * include/types/http_htx.h
+ * This file defines everything related to HTTP manipulation using the internal
+ * representation.
+ *
+ * Copyright (C) 2018 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _TYPES_HTTP_HTX_H
+#define _TYPES_HTTP_HTX_H
+
+#include <common/ist.h>
+#include <types/htx.h>
+
+/* Context used to find/remove an HTTP header. */
+struct http_hdr_ctx {
+	struct htx_blk *blk;
+	struct ist     value;
+	uint16_t       lws_before;
+	uint16_t       lws_after;
+};
+
+#endif /* _TYPES_HTTP_HTX_H */
diff --git a/src/http_htx.c b/src/http_htx.c
new file mode 100644
index 0000000..47e36c8
--- /dev/null
+++ b/src/http_htx.c
@@ -0,0 +1,363 @@
+/*
+ * Functions to manipulate HTTP messages using the internal representation.
+ *
+ * Copyright (C) 2018 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
+ *
+ * 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 <common/config.h>
+#include <common/http.h>
+
+#include <proto/http_htx.h>
+#include <proto/htx.h>
+
+/* Finds the start line in the HTX message stopping at the first
+ * end-of-message. It returns an empty start line when not found, otherwise, it
+ * returns the corresponding <struct h1_sl>.
+ */
+union h1_sl http_find_stline(const struct htx *htx)
+{
+	union htx_sl *htx_sl;
+	union h1_sl sl;
+	int32_t pos;
+
+        for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
+                struct htx_blk    *blk  = htx_get_blk(htx, pos);
+                enum htx_blk_type  type = htx_get_blk_type(blk);
+
+		if (type == HTX_BLK_REQ_SL) {
+			htx_sl = htx_get_blk_ptr(htx, blk);
+			sl.rq.meth = htx_sl->rq.meth;
+			sl.rq.m = ist2(htx_sl->rq.l, htx_sl->rq.m_len);
+			sl.rq.u = ist2(htx_sl->rq.l + htx_sl->rq.m_len, htx_sl->rq.u_len);
+			sl.rq.v = ist2(htx_sl->rq.l + htx_sl->rq.m_len + htx_sl->rq.u_len, htx_sl->rq.v_len);
+			return sl;
+		}
+
+		if (type == HTX_BLK_RES_SL) {
+			htx_sl = htx_get_blk_ptr(htx, blk);
+			sl.st.status = htx_sl->st.status;
+			sl.st.v = ist2(htx_sl->st.l, htx_sl->st.v_len);
+			sl.st.c = ist2(htx_sl->st.l + htx_sl->st.v_len, htx_sl->st.c_len);
+			sl.st.r = ist2(htx_sl->st.l + htx_sl->st.v_len + htx_sl->st.c_len, htx_sl->st.r_len);
+			return sl;
+		}
+		if (type == HTX_BLK_EOM)
+			break;
+	}
+
+	sl.rq.m = ist("");
+	sl.rq.u = ist("");
+	sl.rq.v = ist("");
+	return sl;
+}
+
+/* Finds the first or next occurrence of header <name> in the HTX message <htx>
+ * using the context <ctx>. This structure holds everything necessary to use the
+ * header and find next occurrence. If its <blk> member is NULL, 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. If <full> is
+ * set, it works on full-line headers in whose comma is not a delimiter but is
+ * part of the syntax. A special case, if ctx->value 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_header(const struct htx *htx, const struct ist name,
+		    struct http_hdr_ctx *ctx, int full)
+{
+	struct htx_blk *blk = ctx->blk;
+	struct ist n, v;
+	enum htx_blk_type type;
+	uint32_t pos;
+
+	if (blk) {
+		char *p;
+
+		pos = htx_get_blk_pos(htx, blk);
+		if (!ctx->value.ptr)
+			goto rescan_hdr;
+		if (full)
+			goto next_blk;
+		v = htx_get_blk_value(htx, blk);
+		p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
+		v.len -= (p - v.ptr);
+		v.ptr  = p;
+		if (!v.len)
+			goto next_blk;
+		/* Skip comma */
+		if (*(v.ptr) == ',') {
+			v.ptr++;
+			v.len--;
+		}
+
+		goto return_hdr;
+	}
+
+	if (!htx->used)
+		return 0;
+
+	pos = htx_get_head(htx);
+	while (1) {
+	  rescan_hdr:
+		blk  = htx_get_blk(htx, pos);
+		type = htx_get_blk_type(blk);
+		if (type != HTX_BLK_HDR)
+			goto next_blk;
+		if (name.len) {
+			/* If no name was passed, we want any header. So skip the comparison */
+			n = htx_get_blk_name(htx, blk);
+			if (!isteqi(n, name))
+				goto next_blk;
+		}
+		v = htx_get_blk_value(htx, blk);
+
+	  return_hdr:
+		ctx->lws_before = 0;
+		ctx->lws_after = 0;
+		while (v.len && HTTP_IS_LWS(*v.ptr)) {
+			v.ptr++;
+			v.len--;
+			ctx->lws_before++;
+		}
+		if (!full)
+			v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
+		while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
+			v.len--;
+			ctx->lws_after++;
+		}
+		if (!v.len)
+			goto next_blk;
+		ctx->blk   = blk;
+		ctx->value = v;
+		return 1;
+
+	  next_blk:
+		if (pos == htx->tail)
+			break;
+		pos++;
+		if (pos >= htx->wrap)
+			pos = 0;
+	}
+
+	ctx->blk   = NULL;
+	ctx->value = ist("");
+	ctx->lws_before = ctx->lws_after = 0;
+	return 0;
+}
+
+/* Adds a header block int the HTX message <htx>, just before the EOH block. It
+ * returns 1 on success, otherwise it returns 0.
+ */
+int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
+{
+	struct htx_blk *blk;
+	enum htx_blk_type type = htx_get_tail_type(htx);
+	int32_t prev;
+
+	blk = htx_add_header(htx, n, v);
+	if (!blk)
+		return 0;
+
+	if (unlikely(type < HTX_BLK_EOH))
+		return 1;
+
+	/* <blk> is the head, swap it iteratively with its predecessor to place
+	 * it just before the end-of-header block. So blocks remains ordered. */
+	for (prev = htx_get_prev(htx, htx->tail); prev != -1; prev = htx_get_prev(htx, prev)) {
+		struct htx_blk   *pblk = htx_get_blk(htx, prev);
+		enum htx_blk_type type = htx_get_blk_type(pblk);
+
+		/* Swap .addr and .info fields */
+		blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
+		blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
+
+		if (blk->addr == pblk->addr)
+			blk->addr += htx_get_blksz(pblk);
+		htx->front = prev;
+
+		/* Stop when end-of-header is reached */
+		if (type == HTX_BLK_EOH)
+			break;
+
+		blk = pblk;
+	}
+	return 1;
+}
+
+/* Replaces the request start line of the HTX message <htx> by <sl>. It returns
+ * 1 on success, otherwise it returns 0. The start line must be found in the
+ * message.
+ */
+int http_replace_reqline(struct htx *htx, const union h1_sl sl)
+{
+	int32_t pos;
+
+        for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
+                struct htx_blk    *blk  = htx_get_blk(htx, pos);
+                enum htx_blk_type  type = htx_get_blk_type(blk);
+
+		if (type == HTX_BLK_REQ_SL) {
+			blk = htx_replace_reqline(htx, blk, sl);
+			if (!blk)
+				return 0;
+			return 1;
+		}
+		if (type == HTX_BLK_EOM)
+			break;
+	}
+
+	return 0;
+}
+
+
+/* Replaces the response start line of the HTX message <htx> by <sl>. It returns
+ * 1 on success, otherwise it returns 0. The start line must be found in the
+ * message.
+ */
+int http_replace_resline(struct htx *htx, const union h1_sl sl)
+{
+	int32_t pos;
+
+        for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
+                struct htx_blk    *blk  = htx_get_blk(htx, pos);
+                enum htx_blk_type  type = htx_get_blk_type(blk);
+
+		if (type == HTX_BLK_RES_SL) {
+			blk = htx_replace_resline(htx, blk, sl);
+			if (!blk)
+				return 0;
+			return 1;
+		}
+		if (type == HTX_BLK_EOM)
+			break;
+	}
+
+	return 0;
+}
+
+/* Replaces a part of a header value referenced in the context <ctx> by
+ * <data>. It returns 1 on success, otherwise it returns 0. The context is
+ * updated if necessary.
+ */
+int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
+{
+	struct htx_blk *blk = ctx->blk;
+	char *start;
+	struct ist v;
+	uint32_t len, off;
+
+	if (!blk)
+		return 0;
+
+	v     = htx_get_blk_value(htx, blk);
+	start = ctx->value.ptr - ctx->lws_before;
+	len   = ctx->lws_before + ctx->value.len + ctx->lws_after;
+	off   = start - v.ptr;
+
+	blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
+	if (!blk)
+		return 0;
+
+	v = htx_get_blk_value(htx, blk);
+	ctx->blk = blk;
+	ctx->value.ptr = v.ptr + off;
+	ctx->value.len = data.len;
+	ctx->lws_before = ctx->lws_after = 0;
+
+	return 1;
+}
+
+/* Fully replaces a header referenced in the context <ctx> by the name <name>
+ * with the value <value>. It returns 1 on success, otherwise it returns 0. The
+ * context is updated if necessary.
+ */
+int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
+			const struct ist name, const struct ist value)
+{
+	struct htx_blk *blk = ctx->blk;
+
+	if (!blk)
+		return 0;
+
+	blk = htx_replace_header(htx, blk, name, value);
+	if (!blk)
+		return 0;
+
+	ctx->blk = blk;
+	ctx->value = ist(NULL);
+	ctx->lws_before = ctx->lws_after = 0;
+
+	return 1;
+}
+
+/* Remove one value of a header. This only works on a <ctx> returned by
+ * http_find_header function. 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 HTX message <htx>. It
+ * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
+ * form that can be handled by http_find_header() to find next occurrence.
+ */
+int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
+{
+	struct htx_blk *blk = ctx->blk;
+	char *start;
+	struct ist v;
+	uint32_t len;
+
+	if (!blk)
+		return 0;
+
+	start = ctx->value.ptr - ctx->lws_before;
+	len   = ctx->lws_before + ctx->value.len + ctx->lws_after;
+
+	v = htx_get_blk_value(htx, blk);
+	if (len == v.len) {
+		blk = htx_remove_blk(htx, blk);
+		if (blk || !htx->used) {
+			ctx->blk = blk;
+			ctx->value = ist2(NULL, 0);
+			ctx->lws_before = ctx->lws_after = 0;
+		}
+		else {
+			ctx->blk = htx_get_blk(htx, htx->tail);
+			ctx->value = htx_get_blk_value(htx, ctx->blk);
+			ctx->lws_before = ctx->lws_after = 0;
+		}
+		return 1;
+	}
+
+	/* This was not the only value of this header. We have to remove the
+	 * part pointed by ctx->value. If it is the last entry of the list, we
+	 * remove the last separator.
+	 */
+	if (start == v.ptr) {
+		/* It's the first header part but not the only one. So remove
+		 * the comma after it. */
+		len++;
+	}
+	else {
+		/* There is at least one header part before the removed one. So
+		 * remove the comma between them. */
+		start--;
+		len++;
+	}
+	/* Update the block content and its len */
+	memmove(start, start+len, v.len-len);
+	htx_set_blk_value_len(blk, v.len-len);
+
+	/* Update HTX msg */
+	htx->data -= len;
+
+	/* Finally update the ctx */
+	ctx->value.ptr = start;
+	ctx->value.len = 0;
+	ctx->lws_before = ctx->lws_after = 0;
+
+	return 1;
+}