From 68085d8cfb7e8d8a18aa99caf5446a91bbed4e3a Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 18 Jan 2010 14:54:04 +0100 Subject: [PATCH] [MINOR] http: add http_remove_header2() to remove a header value. Calling this function after http_find_header2() automatically deletes the current value of the header, and removes the header itself if the value is the only one. The context is automatically adjusted for a next call to http_find_header2() to return the next header. No other change nor test should be made on the transient context though. --- include/proto/proto_http.h | 2 +- include/types/proto_http.h | 8 +++- src/proto_http.c | 91 +++++++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index f4a57a5f7f..cfaf9859a3 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -84,7 +84,7 @@ void check_response_for_cacheability(struct session *t, struct buffer *rtr); int stats_check_uri_auth(struct session *t, struct proxy *backend); void init_proto_http(); int http_find_header2(const char *name, int len, - const char *sol, struct hdr_idx *idx, + char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx); void http_sess_log(struct session *s); void perform_http_redirect(struct session *s, struct stream_interface *si); diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 89c8f88de3..f068d7f6da 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -300,13 +300,17 @@ struct http_txn { }; /* This structure is used by http_find_header() to return values of headers. - * The header starts at , the value at + for bytes. + * The header starts at , the value at + for bytes, and + * sets + to point to the last delimitor (colon or comma) before + * this value. points to the index of the header whose next is this one. */ struct hdr_ctx { - const char *line; + char *line; int idx; int val; /* relative to line */ int vlen; /* relative to line+val */ + int del; /* relative to line */ + int prev; /* index of previous header */ }; #endif /* _TYPES_PROTO_HTTP_H */ diff --git a/src/proto_http.c b/src/proto_http.c index 980ef392f4..b9ef0204f4 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -452,11 +452,11 @@ int http_header_match2(const char *hdr, const char *end, return val - hdr; } -/* Find the end of the header value contained between and . - * See RFC2616, par 2.2 for more information. Note that it requires - * a valid header to return a valid result. +/* Find the end of the header value contained between and . See RFC2616, + * par 2.2 for more information. Note that it requires a valid header to return + * a valid result. This works for headers defined as comma-separated lists. */ -const char *find_hdr_value_end(const char *s, const char *e) +char *find_hdr_value_end(char *s, const char *e) { int quoted, qdpair; @@ -476,29 +476,35 @@ const char *find_hdr_value_end(const char *s, const char *e) * structure holds everything necessary to use the header and find next * occurrence. If its 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. + * 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, - const char *sol, struct hdr_idx *idx, + char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx) { - const char *eol, *sov; - int cur_idx; + char *eol, *sov; + int cur_idx, old_idx; - if (ctx->idx) { + cur_idx = ctx->idx; + if (cur_idx) { /* We have previously returned a value, let's search * another one on the same line. */ - cur_idx = ctx->idx; sol = ctx->line; - sov = sol + ctx->val + ctx->vlen; + ctx->del = ctx->val + ctx->vlen; + 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 */ + /* 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[(unsigned char)*sov]) sov++; @@ -508,8 +514,8 @@ int http_find_header2(const char *name, int len, /* 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; @@ -524,12 +530,14 @@ int http_find_header2(const char *name, int len, if ((len < eol - sol) && (sol[len] == ':') && (strncasecmp(sol, name, len) == 0)) { - + ctx->del = len; sov = sol + len + 1; while (sov < eol && http_is_lws[(unsigned char)*sov]) sov++; - return_hdr: + ctx->line = sol; + ctx->prev = old_idx; + return_hdr: ctx->idx = cur_idx; ctx->val = sov - sol; @@ -539,18 +547,69 @@ int http_find_header2(const char *name, int len, } next_hdr: sol = eol + idx->v[cur_idx].cr + 1; + old_idx = cur_idx; cur_idx = idx->v[cur_idx].next; } return 0; } int http_find_header(const char *name, - const char *sol, struct hdr_idx *idx, + char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx) { return http_find_header2(name, strlen(name), sol, idx, ctx); } +/* Remove one value of a header. This only works on a 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 buffer and HTTP + * message . 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 buffer *buf, + 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 == hdr->len) { + /* This was the only value of the header, we must now remove it entirely. */ + delta = buffer_replace2(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; + ctx->idx = ctx->prev; /* walk back to the end of previous header */ + ctx->line -= idx->v[ctx->idx].len + idx->v[cur_idx].cr + 1; + ctx->val = idx->v[ctx->idx].len; /* point to end of previous header */ + 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+1 included. If it is the last entry + * of the list, we remove the last separator. + */ + + skip_comma = (ctx->val + ctx->vlen == hdr->len) ? 0 : 1; + delta = buffer_replace2(buf, sol + ctx->del + skip_comma, + sol + ctx->val + ctx->vlen + skip_comma, + NULL, 0); + hdr->len += delta; + http_msg_move_end(msg, delta); + ctx->val = ctx->del; + 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, and an HTTP status code stored. -- 2.39.5