blob: 471f1ab8114b368a150d58838e984ef6f89841a0 [file] [log] [blame]
Christopher Faulet47596d32018-10-22 09:17:28 +02001/*
2 * Functions to manipulate HTTP messages using the internal representation.
3 *
4 * Copyright (C) 2018 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <common/config.h>
Christopher Fauleta7b677c2018-11-29 16:48:49 +010014#include <common/cfgparse.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010015#include <common/h1.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020016#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010017#include <common/htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020018
19#include <proto/http_htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020020
Christopher Fauleta7b677c2018-11-29 16:48:49 +010021struct buffer htx_err_chunks[HTTP_ERR_SIZE];
22
Christopher Faulet47596d32018-10-22 09:17:28 +020023/* Finds the start line in the HTX message stopping at the first
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010024 * end-of-message. It returns NULL when not found, otherwise, it returns the
25 * pointer on the htx_sl structure. The HTX message may be updated if the
26 * start-line is returned following a lookup.
Christopher Faulet47596d32018-10-22 09:17:28 +020027 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010028struct htx_sl *http_find_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020029{
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010030 struct htx_sl *sl = NULL;
Christopher Faulet47596d32018-10-22 09:17:28 +020031 int32_t pos;
32
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010033 sl = htx_get_stline(htx);
34 if (sl)
35 return sl;
36
Christopher Fauletaa75b3d2018-12-05 16:20:40 +010037 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
38 struct htx_blk *blk = htx_get_blk(htx, pos);
39 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020040
Christopher Fauletf1ba18d2018-11-26 21:37:08 +010041 if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL) {
42 sl = htx_get_blk_ptr(htx, blk);
43 htx->sl_off = blk->addr;
44 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020045 }
Christopher Faulet573fe732018-11-28 16:55:12 +010046
47 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
Christopher Faulet47596d32018-10-22 09:17:28 +020048 break;
49 }
50
Christopher Faulet47596d32018-10-22 09:17:28 +020051 return sl;
52}
53
54/* Finds the first or next occurrence of header <name> in the HTX message <htx>
55 * using the context <ctx>. This structure holds everything necessary to use the
56 * header and find next occurrence. If its <blk> member is NULL, the header is
57 * searched from the beginning. Otherwise, the next occurrence is returned. The
58 * function returns 1 when it finds a value, and 0 when there is no more. It is
59 * designed to work with headers defined as comma-separated lists. If <full> is
60 * set, it works on full-line headers in whose comma is not a delimiter but is
61 * part of the syntax. A special case, if ctx->value is NULL when searching for
62 * a new values of a header, the current header is rescanned. This allows
63 * rescanning after a header deletion.
64 */
65int http_find_header(const struct htx *htx, const struct ist name,
66 struct http_hdr_ctx *ctx, int full)
67{
68 struct htx_blk *blk = ctx->blk;
69 struct ist n, v;
70 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020071
72 if (blk) {
73 char *p;
74
Christopher Faulet47596d32018-10-22 09:17:28 +020075 if (!ctx->value.ptr)
76 goto rescan_hdr;
77 if (full)
78 goto next_blk;
79 v = htx_get_blk_value(htx, blk);
80 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
81 v.len -= (p - v.ptr);
82 v.ptr = p;
83 if (!v.len)
84 goto next_blk;
85 /* Skip comma */
86 if (*(v.ptr) == ',') {
87 v.ptr++;
88 v.len--;
89 }
90
91 goto return_hdr;
92 }
93
94 if (!htx->used)
95 return 0;
96
Christopher Faulet28f29c72019-04-30 17:55:45 +020097 for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020098 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020099 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +0100100 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
101 break;
Christopher Faulet47596d32018-10-22 09:17:28 +0200102 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200103 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +0200104 if (name.len) {
105 /* If no name was passed, we want any header. So skip the comparison */
106 n = htx_get_blk_name(htx, blk);
107 if (!isteqi(n, name))
108 goto next_blk;
109 }
110 v = htx_get_blk_value(htx, blk);
111
112 return_hdr:
113 ctx->lws_before = 0;
114 ctx->lws_after = 0;
115 while (v.len && HTTP_IS_LWS(*v.ptr)) {
116 v.ptr++;
117 v.len--;
118 ctx->lws_before++;
119 }
120 if (!full)
121 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
122 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
123 v.len--;
124 ctx->lws_after++;
125 }
126 if (!v.len)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200127 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +0200128 ctx->blk = blk;
129 ctx->value = v;
130 return 1;
131
132 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200133 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200134 }
135
136 ctx->blk = NULL;
137 ctx->value = ist("");
138 ctx->lws_before = ctx->lws_after = 0;
139 return 0;
140}
141
142/* Adds a header block int the HTX message <htx>, just before the EOH block. It
143 * returns 1 on success, otherwise it returns 0.
144 */
145int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
146{
147 struct htx_blk *blk;
148 enum htx_blk_type type = htx_get_tail_type(htx);
149 int32_t prev;
150
151 blk = htx_add_header(htx, n, v);
152 if (!blk)
153 return 0;
154
155 if (unlikely(type < HTX_BLK_EOH))
156 return 1;
157
158 /* <blk> is the head, swap it iteratively with its predecessor to place
159 * it just before the end-of-header block. So blocks remains ordered. */
160 for (prev = htx_get_prev(htx, htx->tail); prev != -1; prev = htx_get_prev(htx, prev)) {
161 struct htx_blk *pblk = htx_get_blk(htx, prev);
162 enum htx_blk_type type = htx_get_blk_type(pblk);
163
164 /* Swap .addr and .info fields */
165 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
166 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
167
168 if (blk->addr == pblk->addr)
169 blk->addr += htx_get_blksz(pblk);
170 htx->front = prev;
171
172 /* Stop when end-of-header is reached */
173 if (type == HTX_BLK_EOH)
174 break;
175
176 blk = pblk;
177 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200178
179 if (htx_get_blk_pos(htx, blk) != htx->front)
180 htx_defrag(htx, NULL);
181
Christopher Faulet47596d32018-10-22 09:17:28 +0200182 return 1;
183}
184
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100185/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
186 * success, otherwise it returns 0. The right block is search in the HTX
Christopher Faulet47596d32018-10-22 09:17:28 +0200187 * message.
188 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100189int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3)
Christopher Faulet47596d32018-10-22 09:17:28 +0200190{
191 int32_t pos;
192
Christopher Fauletaa75b3d2018-12-05 16:20:40 +0100193 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100194 struct htx_blk *blk = htx_get_blk(htx, pos);
Christopher Fauletaa75b3d2018-12-05 16:20:40 +0100195 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200196
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100197 if (htx->sl_off == blk->addr) {
198 if (!htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet47596d32018-10-22 09:17:28 +0200199 return 0;
200 return 1;
201 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200202
Christopher Faulet47596d32018-10-22 09:17:28 +0200203 if (type == HTX_BLK_EOM)
204 break;
205 }
206
207 return 0;
208}
209
Christopher Faulete010c802018-10-24 10:36:45 +0200210/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
211 * on success, otherwise 0.
212 */
213int http_replace_req_meth(struct htx *htx, const struct ist meth)
214{
215 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100216 struct htx_sl *sl = http_find_stline(htx);
217 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200218
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100219 if (!sl)
220 return 0;
221
Christopher Faulete010c802018-10-24 10:36:45 +0200222 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100223 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
224 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200225
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100226 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
227 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200228
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100229 /* create the new start line */
230 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
231 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200232}
233
234/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
235 * success, otherwise 0.
236 */
237int http_replace_req_uri(struct htx *htx, const struct ist uri)
238{
239 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100240 struct htx_sl *sl = http_find_stline(htx);
241 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200242
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100243 if (!sl)
244 return 0;
245
Christopher Faulete010c802018-10-24 10:36:45 +0200246 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100247 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
248 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200249
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100250 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
251 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200252
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100253 /* create the new start line */
254 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200255}
256
257/* Replace the request path in the HTX message <htx> by <path>. The host part
258 * and the query string are preserved. It returns 1 on success, otherwise 0.
259 */
260int http_replace_req_path(struct htx *htx, const struct ist path)
261{
262 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100263 struct htx_sl *sl = http_find_stline(htx);
264 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200265 size_t plen = 0;
266
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100267 if (!sl)
268 return 0;
269
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100270 uri = htx_sl_req_uri(sl);
271 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200272 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100273 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200274 while (plen < p.len && *(p.ptr + plen) != '?')
275 plen++;
276
277 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100278 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
279 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200280
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100281 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
282 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
283
284 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200285 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
286 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100287 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200288
289 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100290 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200291}
292
293/* Replace the request query-string in the HTX message <htx> by <query>. The
294 * host part and the path are preserved. It returns 1 on success, otherwise
295 * 0.
296 */
297int http_replace_req_query(struct htx *htx, const struct ist query)
298{
299 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100300 struct htx_sl *sl = http_find_stline(htx);
301 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200302 int offset = 1;
303
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100304 if (!sl)
305 return 0;
306
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100307 uri = htx_sl_req_uri(sl);
308 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200309 while (q.len > 0 && *(q.ptr) != '?') {
310 q.ptr++;
311 q.len--;
312 }
313
314 /* skip the question mark or indicate that we must insert it
315 * (but only if the format string is not empty then).
316 */
317 if (q.len) {
318 q.ptr++;
319 q.len--;
320 }
321 else if (query.len > 1)
322 offset = 0;
323
324 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100325 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
326 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200327
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100328 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
329 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200330
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100331 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
332 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
333 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200334
335 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100336 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200337}
338
339/* Replace the response status in the HTX message <htx> by <status>. It returns
340 * 1 on success, otherwise 0.
341*/
342int http_replace_res_status(struct htx *htx, const struct ist status)
343{
344 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100345 struct htx_sl *sl = http_find_stline(htx);
346 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200347
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100348 if (!sl)
349 return 0;
350
Christopher Faulete010c802018-10-24 10:36:45 +0200351 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100352 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
353 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200354
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100355 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
356 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200357
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100358 /* create the new start line */
359 sl->info.res.status = strl2ui(status.ptr, status.len);
360 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200361}
362
363/* Replace the response reason in the HTX message <htx> by <reason>. It returns
364 * 1 on success, otherwise 0.
365*/
366int http_replace_res_reason(struct htx *htx, const struct ist reason)
367{
368 struct buffer *temp = get_trash_chunk();
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100369 struct htx_sl *sl = http_find_stline(htx);
370 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200371
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100372 if (!sl)
373 return 0;
374
Christopher Faulete010c802018-10-24 10:36:45 +0200375 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100376 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
377 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200378
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100379 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
380 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200381
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100382 /* create the new start line */
383 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200384}
385
Christopher Faulet47596d32018-10-22 09:17:28 +0200386/* Replaces a part of a header value referenced in the context <ctx> by
387 * <data>. It returns 1 on success, otherwise it returns 0. The context is
388 * updated if necessary.
389 */
390int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
391{
392 struct htx_blk *blk = ctx->blk;
393 char *start;
394 struct ist v;
395 uint32_t len, off;
396
397 if (!blk)
398 return 0;
399
400 v = htx_get_blk_value(htx, blk);
401 start = ctx->value.ptr - ctx->lws_before;
402 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
403 off = start - v.ptr;
404
405 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
406 if (!blk)
407 return 0;
408
409 v = htx_get_blk_value(htx, blk);
410 ctx->blk = blk;
411 ctx->value.ptr = v.ptr + off;
412 ctx->value.len = data.len;
413 ctx->lws_before = ctx->lws_after = 0;
414
415 return 1;
416}
417
418/* Fully replaces a header referenced in the context <ctx> by the name <name>
419 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
420 * context is updated if necessary.
421 */
422int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
423 const struct ist name, const struct ist value)
424{
425 struct htx_blk *blk = ctx->blk;
426
427 if (!blk)
428 return 0;
429
430 blk = htx_replace_header(htx, blk, name, value);
431 if (!blk)
432 return 0;
433
434 ctx->blk = blk;
435 ctx->value = ist(NULL);
436 ctx->lws_before = ctx->lws_after = 0;
437
438 return 1;
439}
440
441/* Remove one value of a header. This only works on a <ctx> returned by
442 * http_find_header function. The value is removed, as well as surrounding commas
443 * if any. If the removed value was alone, the whole header is removed. The
444 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
445 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
446 * form that can be handled by http_find_header() to find next occurrence.
447 */
448int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
449{
450 struct htx_blk *blk = ctx->blk;
451 char *start;
452 struct ist v;
453 uint32_t len;
454
455 if (!blk)
456 return 0;
457
458 start = ctx->value.ptr - ctx->lws_before;
459 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
460
461 v = htx_get_blk_value(htx, blk);
462 if (len == v.len) {
463 blk = htx_remove_blk(htx, blk);
464 if (blk || !htx->used) {
465 ctx->blk = blk;
466 ctx->value = ist2(NULL, 0);
467 ctx->lws_before = ctx->lws_after = 0;
468 }
469 else {
470 ctx->blk = htx_get_blk(htx, htx->tail);
471 ctx->value = htx_get_blk_value(htx, ctx->blk);
472 ctx->lws_before = ctx->lws_after = 0;
473 }
474 return 1;
475 }
476
477 /* This was not the only value of this header. We have to remove the
478 * part pointed by ctx->value. If it is the last entry of the list, we
479 * remove the last separator.
480 */
481 if (start == v.ptr) {
482 /* It's the first header part but not the only one. So remove
483 * the comma after it. */
484 len++;
485 }
486 else {
487 /* There is at least one header part before the removed one. So
488 * remove the comma between them. */
489 start--;
490 len++;
491 }
492 /* Update the block content and its len */
493 memmove(start, start+len, v.len-len);
494 htx_set_blk_value_len(blk, v.len-len);
495
496 /* Update HTX msg */
497 htx->data -= len;
498
499 /* Finally update the ctx */
500 ctx->value.ptr = start;
501 ctx->value.len = 0;
502 ctx->lws_before = ctx->lws_after = 0;
503
504 return 1;
505}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200506
507
508/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
509 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
510 * performed over the whole headers. Otherwise it must contain a valid header
511 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
512 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
513 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
514 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
515 * -1. The value fetch stops at commas, so this function is suited for use with
516 * list headers.
517 * The return value is 0 if nothing was found, or non-zero otherwise.
518 */
519unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
520 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
521{
522 struct http_hdr_ctx local_ctx;
523 struct ist val_hist[MAX_HDR_HISTORY];
524 unsigned int hist_idx;
525 int found;
526
527 if (!ctx) {
528 local_ctx.blk = NULL;
529 ctx = &local_ctx;
530 }
531
532 if (occ >= 0) {
533 /* search from the beginning */
534 while (http_find_header(htx, hdr, ctx, 0)) {
535 occ--;
536 if (occ <= 0) {
537 *vptr = ctx->value.ptr;
538 *vlen = ctx->value.len;
539 return 1;
540 }
541 }
542 return 0;
543 }
544
545 /* negative occurrence, we scan all the list then walk back */
546 if (-occ > MAX_HDR_HISTORY)
547 return 0;
548
549 found = hist_idx = 0;
550 while (http_find_header(htx, hdr, ctx, 0)) {
551 val_hist[hist_idx] = ctx->value;
552 if (++hist_idx >= MAX_HDR_HISTORY)
553 hist_idx = 0;
554 found++;
555 }
556 if (-occ > found)
557 return 0;
558
559 /* OK now we have the last occurrence in [hist_idx-1], and we need to
560 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
561 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
562 * to remain in the 0..9 range.
563 */
564 hist_idx += occ + MAX_HDR_HISTORY;
565 if (hist_idx >= MAX_HDR_HISTORY)
566 hist_idx -= MAX_HDR_HISTORY;
567 *vptr = val_hist[hist_idx].ptr;
568 *vlen = val_hist[hist_idx].len;
569 return 1;
570}
571
572/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
573 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
574 * performed over the whole headers. Otherwise it must contain a valid header
575 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
576 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
577 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
578 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
579 * -1. This function differs from http_get_hdr() in that it only returns full
580 * line header values and does not stop at commas.
581 * The return value is 0 if nothing was found, or non-zero otherwise.
582 */
583unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
584 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
585{
586 struct http_hdr_ctx local_ctx;
587 struct ist val_hist[MAX_HDR_HISTORY];
588 unsigned int hist_idx;
589 int found;
590
591 if (!ctx) {
592 local_ctx.blk = NULL;
593 ctx = &local_ctx;
594 }
595
596 if (occ >= 0) {
597 /* search from the beginning */
598 while (http_find_header(htx, hdr, ctx, 1)) {
599 occ--;
600 if (occ <= 0) {
601 *vptr = ctx->value.ptr;
602 *vlen = ctx->value.len;
603 return 1;
604 }
605 }
606 return 0;
607 }
608
609 /* negative occurrence, we scan all the list then walk back */
610 if (-occ > MAX_HDR_HISTORY)
611 return 0;
612
613 found = hist_idx = 0;
614 while (http_find_header(htx, hdr, ctx, 1)) {
615 val_hist[hist_idx] = ctx->value;
616 if (++hist_idx >= MAX_HDR_HISTORY)
617 hist_idx = 0;
618 found++;
619 }
620 if (-occ > found)
621 return 0;
622
623 /* OK now we have the last occurrence in [hist_idx-1], and we need to
624 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
625 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
626 * to remain in the 0..9 range.
627 */
628 hist_idx += occ + MAX_HDR_HISTORY;
629 if (hist_idx >= MAX_HDR_HISTORY)
630 hist_idx -= MAX_HDR_HISTORY;
631 *vptr = val_hist[hist_idx].ptr;
632 *vlen = val_hist[hist_idx].len;
633 return 1;
634}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100635
636static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw)
637{
638 struct htx *htx;
639 struct htx_sl *sl;
640 struct h1m h1m;
641 struct http_hdr hdrs[MAX_HTTP_HDR];
642 union h1_sl h1sl;
643 unsigned int flags = HTX_SL_F_IS_RESP;
644 int ret = 0;
645
646 buf->size = global.tune.bufsize;
647 buf->area = (char *)malloc(buf->size);
648 if (!buf->area)
649 goto error;
650 b_reset(buf);
651
652 h1m_init_res(&h1m);
653 h1m.flags |= H1_MF_NO_PHDR;
654 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
655 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
656 if (ret <= 0)
657 goto error;
658
659 if (unlikely(h1sl.st.v.len != 8))
660 goto error;
661 if ((*(h1sl.st.v.ptr + 5) > '1') ||
662 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
663 h1m.flags |= H1_MF_VER_11;
664
665 if (h1m.flags & H1_MF_VER_11)
666 flags |= HTX_SL_F_VER_11;
667 if (h1m.flags & H1_MF_XFER_ENC)
668 flags |= HTX_SL_F_XFER_ENC;
669 if (h1m.flags & H1_MF_XFER_LEN) {
670 flags |= HTX_SL_F_XFER_LEN;
671 if (h1m.flags & H1_MF_CHNK)
672 goto error; /* Unsupported because there is no body parsing */
673 else if (h1m.flags & H1_MF_CLEN) {
674 flags |= HTX_SL_F_CLEN;
675 if (h1m.body_len == 0)
676 flags |= HTX_SL_F_BODYLESS;
677 }
678 }
679
680 htx = htx_from_buf(buf);
681 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
682 if (!sl || !htx_add_all_headers(htx, hdrs))
683 goto error;
684 sl->info.res.status = h1sl.st.status;
685
686 if (raw.len > ret) {
687 if (!htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret)))
688 goto error;
689 }
690 if (!htx_add_endof(htx, HTX_BLK_EOM))
691 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100692 return htx;
693
694error:
695 if (buf->size)
696 free(buf->area);
697 return NULL;
698}
699
700static int http_htx_init(void)
701{
702 struct proxy *px;
703 struct buffer chk;
704 struct ist raw;
705 int rc;
706 int err_code = 0;
707
708 for (px = proxies_list; px; px = px->next) {
Christopher Faulet49040582019-04-24 15:25:00 +0200709 if (px->mode != PR_MODE_HTTP || !(px->options2 & PR_O2_USE_HTX))
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100710 continue;
711
712 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
713 if (!b_data(&px->errmsg[rc]))
714 continue;
715
716 raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc]));
717 if (!http_str_to_htx(&chk, raw)) {
718 ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n",
719 proxy_type_str(px), px->id, http_err_codes[rc]);
720 err_code |= ERR_ALERT | ERR_FATAL;
721 }
722 chunk_destroy(&px->errmsg[rc]);
723 px->errmsg[rc] = chk;
724 }
725 }
726
727 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
728 if (!http_err_msgs[rc]) {
729 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
730 err_code |= ERR_ALERT | ERR_FATAL;
731 continue;
732 }
733
734 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
735 if (!http_str_to_htx(&chk, raw)) {
736 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
737 http_err_codes[rc]);
738 err_code |= ERR_ALERT | ERR_FATAL;
739 }
740 htx_err_chunks[rc] = chk;
741 }
742end:
743 return err_code;
744}
745
746REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);