--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}