blob: 6b1d11f2873ddcf0236e1afcd0e182c17fc9851d [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 Faulet29f17582019-05-23 11:03:26 +020014#include <common/debug.h>
Christopher Fauleta7b677c2018-11-29 16:48:49 +010015#include <common/cfgparse.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010016#include <common/h1.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020017#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010018#include <common/htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020019
Christopher Faulet29f72842019-12-11 15:52:32 +010020#include <proto/arg.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020021#include <proto/http_htx.h>
Christopher Faulet29f72842019-12-11 15:52:32 +010022#include <proto/http_fetch.h>
23#include <proto/sample.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020024
Christopher Fauletf7346382019-07-17 22:02:08 +020025struct buffer http_err_chunks[HTTP_ERR_SIZE];
Christopher Fauleta7b677c2018-11-29 16:48:49 +010026
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +020027static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host);
28static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri);
29
Christopher Faulet297fbb42019-05-13 14:41:27 +020030/* Returns the next unporocessed start line in the HTX message. It returns NULL
Christopher Faulet29f17582019-05-23 11:03:26 +020031 * if the start-line is undefined (first == -1). Otherwise, it returns the
Christopher Faulet297fbb42019-05-13 14:41:27 +020032 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020033 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020034struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020035{
Christopher Faulet297fbb42019-05-13 14:41:27 +020036 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010037
Christopher Faulet29f17582019-05-23 11:03:26 +020038 BUG_ON(htx->first == -1);
39 blk = htx_get_first_blk(htx);
Christopher Faulet297fbb42019-05-13 14:41:27 +020040 if (!blk)
41 return NULL;
Christopher Faulet29f17582019-05-23 11:03:26 +020042 BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL);
Christopher Faulet297fbb42019-05-13 14:41:27 +020043 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020044}
45
46/* Finds the first or next occurrence of header <name> in the HTX message <htx>
47 * using the context <ctx>. This structure holds everything necessary to use the
48 * header and find next occurrence. If its <blk> member is NULL, the header is
49 * searched from the beginning. Otherwise, the next occurrence is returned. The
50 * function returns 1 when it finds a value, and 0 when there is no more. It is
51 * designed to work with headers defined as comma-separated lists. If <full> is
52 * set, it works on full-line headers in whose comma is not a delimiter but is
53 * part of the syntax. A special case, if ctx->value is NULL when searching for
54 * a new values of a header, the current header is rescanned. This allows
55 * rescanning after a header deletion.
56 */
57int http_find_header(const struct htx *htx, const struct ist name,
58 struct http_hdr_ctx *ctx, int full)
59{
60 struct htx_blk *blk = ctx->blk;
61 struct ist n, v;
62 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020063
64 if (blk) {
65 char *p;
66
Christopher Faulet47596d32018-10-22 09:17:28 +020067 if (!ctx->value.ptr)
68 goto rescan_hdr;
69 if (full)
70 goto next_blk;
71 v = htx_get_blk_value(htx, blk);
72 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
73 v.len -= (p - v.ptr);
74 v.ptr = p;
75 if (!v.len)
76 goto next_blk;
77 /* Skip comma */
78 if (*(v.ptr) == ',') {
79 v.ptr++;
80 v.len--;
81 }
82
83 goto return_hdr;
84 }
85
Christopher Faulet192c6a22019-06-11 16:32:24 +020086 if (htx_is_empty(htx))
Christopher Faulet47596d32018-10-22 09:17:28 +020087 return 0;
88
Christopher Fauleta3f15502019-05-13 15:27:23 +020089 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020090 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020091 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +010092 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
93 break;
Christopher Faulet47596d32018-10-22 09:17:28 +020094 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +020095 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +020096 if (name.len) {
97 /* If no name was passed, we want any header. So skip the comparison */
98 n = htx_get_blk_name(htx, blk);
99 if (!isteqi(n, name))
100 goto next_blk;
101 }
102 v = htx_get_blk_value(htx, blk);
103
104 return_hdr:
105 ctx->lws_before = 0;
106 ctx->lws_after = 0;
107 while (v.len && HTTP_IS_LWS(*v.ptr)) {
108 v.ptr++;
109 v.len--;
110 ctx->lws_before++;
111 }
112 if (!full)
113 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
114 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
115 v.len--;
116 ctx->lws_after++;
117 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200118 ctx->blk = blk;
119 ctx->value = v;
120 return 1;
121
122 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200123 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200124 }
125
126 ctx->blk = NULL;
127 ctx->value = ist("");
128 ctx->lws_before = ctx->lws_after = 0;
129 return 0;
130}
131
132/* Adds a header block int the HTX message <htx>, just before the EOH block. It
133 * returns 1 on success, otherwise it returns 0.
134 */
135int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
136{
137 struct htx_blk *blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200138 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200139 enum htx_blk_type type = htx_get_tail_type(htx);
140 int32_t prev;
141
142 blk = htx_add_header(htx, n, v);
143 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200144 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200145
146 if (unlikely(type < HTX_BLK_EOH))
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200147 goto end;
Christopher Faulet47596d32018-10-22 09:17:28 +0200148
149 /* <blk> is the head, swap it iteratively with its predecessor to place
150 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200151 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200152 struct htx_blk *pblk = htx_get_blk(htx, prev);
153 enum htx_blk_type type = htx_get_blk_type(pblk);
154
155 /* Swap .addr and .info fields */
156 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
157 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
158
159 if (blk->addr == pblk->addr)
160 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200161
162 /* Stop when end-of-header is reached */
163 if (type == HTX_BLK_EOH)
164 break;
165
166 blk = pblk;
167 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200168
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200169 end:
170 sl = http_get_stline(htx);
171 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(n, ist("host"))) {
172 if (!http_update_authority(htx, sl, v))
173 goto fail;
174 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200175 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200176
177 fail:
178 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200179}
180
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100181/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200182 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200183 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100184int 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 +0200185{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200186 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200187
Christopher Faulet29f17582019-05-23 11:03:26 +0200188 blk = htx_get_first_blk(htx);
189 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200190 return 0;
191 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200192}
193
Christopher Faulete010c802018-10-24 10:36:45 +0200194/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
195 * on success, otherwise 0.
196 */
197int http_replace_req_meth(struct htx *htx, const struct ist meth)
198{
199 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200200 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100201 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200202
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100203 if (!sl)
204 return 0;
205
Christopher Faulete010c802018-10-24 10:36:45 +0200206 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100207 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
208 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200209
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100210 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
211 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200212
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100213 /* create the new start line */
214 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
215 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200216}
217
218/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
219 * success, otherwise 0.
220 */
221int http_replace_req_uri(struct htx *htx, const struct ist uri)
222{
223 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200224 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100225 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200226
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100227 if (!sl)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200228 goto fail;
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100229
Christopher Faulete010c802018-10-24 10:36:45 +0200230 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100231 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
232 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200233
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100234 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
235 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200236
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100237 /* create the new start line */
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200238 if (!http_replace_stline(htx, meth, uri, vsn))
239 goto fail;
240
241 sl = http_get_stline(htx);
242 if (!http_update_host(htx, sl, uri))
243 goto fail;
244
245 return 1;
246 fail:
247 return 0;
Christopher Faulete010c802018-10-24 10:36:45 +0200248}
249
250/* Replace the request path in the HTX message <htx> by <path>. The host part
251 * and the query string are preserved. It returns 1 on success, otherwise 0.
252 */
253int http_replace_req_path(struct htx *htx, const struct ist path)
254{
255 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200256 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100257 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200258 size_t plen = 0;
259
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100260 if (!sl)
261 return 0;
262
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100263 uri = htx_sl_req_uri(sl);
264 p = http_get_path(uri);
Christopher Faulete010c802018-10-24 10:36:45 +0200265 if (!p.ptr)
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100266 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200267 while (plen < p.len && *(p.ptr + plen) != '?')
268 plen++;
269
270 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100271 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
272 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200273
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100274 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
275 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
276
277 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200278 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
279 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100280 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200281
282 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100283 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200284}
285
286/* Replace the request query-string in the HTX message <htx> by <query>. The
287 * host part and the path are preserved. It returns 1 on success, otherwise
288 * 0.
289 */
290int http_replace_req_query(struct htx *htx, const struct ist query)
291{
292 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200293 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100294 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200295 int offset = 1;
296
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100297 if (!sl)
298 return 0;
299
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100300 uri = htx_sl_req_uri(sl);
301 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200302 while (q.len > 0 && *(q.ptr) != '?') {
303 q.ptr++;
304 q.len--;
305 }
306
307 /* skip the question mark or indicate that we must insert it
308 * (but only if the format string is not empty then).
309 */
310 if (q.len) {
311 q.ptr++;
312 q.len--;
313 }
314 else if (query.len > 1)
315 offset = 0;
316
317 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100318 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
319 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200320
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100321 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
322 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200323
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100324 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
325 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
326 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200327
328 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100329 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200330}
331
332/* Replace the response status in the HTX message <htx> by <status>. It returns
333 * 1 on success, otherwise 0.
334*/
335int http_replace_res_status(struct htx *htx, const struct ist status)
336{
337 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200338 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100339 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200340
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100341 if (!sl)
342 return 0;
343
Christopher Faulete010c802018-10-24 10:36:45 +0200344 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100345 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
346 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200347
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100348 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
349 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200350
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100351 /* create the new start line */
352 sl->info.res.status = strl2ui(status.ptr, status.len);
353 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200354}
355
356/* Replace the response reason in the HTX message <htx> by <reason>. It returns
357 * 1 on success, otherwise 0.
358*/
359int http_replace_res_reason(struct htx *htx, const struct ist reason)
360{
361 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200362 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100363 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200364
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100365 if (!sl)
366 return 0;
367
Christopher Faulete010c802018-10-24 10:36:45 +0200368 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100369 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
370 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200371
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100372 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
373 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200374
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100375 /* create the new start line */
376 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200377}
378
Christopher Faulet47596d32018-10-22 09:17:28 +0200379/* Replaces a part of a header value referenced in the context <ctx> by
380 * <data>. It returns 1 on success, otherwise it returns 0. The context is
381 * updated if necessary.
382 */
383int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
384{
385 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200386 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200387 char *start;
388 struct ist v;
389 uint32_t len, off;
390
391 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200392 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200393
394 v = htx_get_blk_value(htx, blk);
395 start = ctx->value.ptr - ctx->lws_before;
396 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
397 off = start - v.ptr;
398
399 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
400 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200401 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200402
403 v = htx_get_blk_value(htx, blk);
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200404
405 sl = http_get_stline(htx);
406 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
407 struct ist n = htx_get_blk_name(htx, blk);
408
409 if (isteq(n, ist("host"))) {
410 if (!http_update_authority(htx, sl, v))
411 goto fail;
412 ctx->blk = NULL;
413 http_find_header(htx, ist("host"), ctx, 1);
414 blk = ctx->blk;
415 v = htx_get_blk_value(htx, blk);
416 }
417 }
418
Christopher Faulet47596d32018-10-22 09:17:28 +0200419 ctx->blk = blk;
420 ctx->value.ptr = v.ptr + off;
421 ctx->value.len = data.len;
422 ctx->lws_before = ctx->lws_after = 0;
423
424 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200425 fail:
426 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200427}
428
429/* Fully replaces a header referenced in the context <ctx> by the name <name>
430 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
431 * context is updated if necessary.
432 */
433int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
434 const struct ist name, const struct ist value)
435{
436 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200437 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200438
439 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200440 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200441
442 blk = htx_replace_header(htx, blk, name, value);
443 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200444 goto fail;
445
446 sl = http_get_stline(htx);
447 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(name, ist("host"))) {
448 if (!http_update_authority(htx, sl, value))
449 goto fail;
450 ctx->blk = NULL;
451 http_find_header(htx, ist("host"), ctx, 1);
452 blk = ctx->blk;
453 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200454
455 ctx->blk = blk;
456 ctx->value = ist(NULL);
457 ctx->lws_before = ctx->lws_after = 0;
458
459 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200460 fail:
461 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200462}
463
464/* Remove one value of a header. This only works on a <ctx> returned by
465 * http_find_header function. The value is removed, as well as surrounding commas
466 * if any. If the removed value was alone, the whole header is removed. The
467 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
468 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
469 * form that can be handled by http_find_header() to find next occurrence.
470 */
471int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
472{
473 struct htx_blk *blk = ctx->blk;
474 char *start;
475 struct ist v;
476 uint32_t len;
477
478 if (!blk)
479 return 0;
480
481 start = ctx->value.ptr - ctx->lws_before;
482 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
483
484 v = htx_get_blk_value(htx, blk);
485 if (len == v.len) {
486 blk = htx_remove_blk(htx, blk);
Christopher Faulet192c6a22019-06-11 16:32:24 +0200487 if (blk || htx_is_empty(htx)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200488 ctx->blk = blk;
489 ctx->value = ist2(NULL, 0);
490 ctx->lws_before = ctx->lws_after = 0;
491 }
492 else {
493 ctx->blk = htx_get_blk(htx, htx->tail);
494 ctx->value = htx_get_blk_value(htx, ctx->blk);
495 ctx->lws_before = ctx->lws_after = 0;
496 }
497 return 1;
498 }
499
500 /* This was not the only value of this header. We have to remove the
501 * part pointed by ctx->value. If it is the last entry of the list, we
502 * remove the last separator.
503 */
504 if (start == v.ptr) {
505 /* It's the first header part but not the only one. So remove
506 * the comma after it. */
507 len++;
508 }
509 else {
510 /* There is at least one header part before the removed one. So
511 * remove the comma between them. */
512 start--;
513 len++;
514 }
515 /* Update the block content and its len */
516 memmove(start, start+len, v.len-len);
Christopher Faulet3e2638e2019-06-18 09:49:16 +0200517 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200518
519 /* Finally update the ctx */
520 ctx->value.ptr = start;
521 ctx->value.len = 0;
522 ctx->lws_before = ctx->lws_after = 0;
523
524 return 1;
525}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200526
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200527/* Updates the authority part of the uri with the value <host>. It happens when
528 * the header host is modified. It returns 0 on failure and 1 on success. It is
529 * the caller responsibility to provide the start-line and to be sure the uri
530 * contains an authority. Thus, if no authority is found in the uri, an error is
531 * returned.
532 */
533static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host)
534{
535 struct buffer *temp = get_trash_chunk();
536 struct ist meth, vsn, uri, authority;
537
538 uri = htx_sl_req_uri(sl);
539 authority = http_get_authority(uri, 1);
540 if (!authority.len || isteq(host, authority))
541 return 0;
542
543 /* Start by copying old method and version */
544 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
545 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
546
547 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
548 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
549
550 chunk_memcat(temp, uri.ptr, authority.ptr - uri.ptr);
551 chunk_memcat(temp, host.ptr, host.len);
552 chunk_memcat(temp, authority.ptr + authority.len, uri.ptr + uri.len - (authority.ptr + authority.len));
553 uri = ist2(temp->area + meth.len + vsn.len, host.len + uri.len - authority.len); /* uri */
554
555 return http_replace_stline(htx, meth, uri, vsn);
556
557}
558
559/* Update the header host by extracting the authority of the uri <uri>. flags of
560 * the start-line are also updated accordingly. For orgin-form and asterisk-form
561 * uri, the header host is not changed and the flag HTX_SL_F_HAS_AUTHORITY is
562 * removed from the flags of the start-line. Otherwise, this flag is set and the
563 * authority is used to set the value of the header host. This function returns
564 * 0 on failure and 1 on success.
565*/
566static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri)
567{
568 struct ist authority;
569 struct http_hdr_ctx ctx;
570
571 if (!uri.len || uri.ptr[0] == '/' || uri.ptr[0] == '*') {
572 // origin-form or a asterisk-form (RFC7320 #5.3.1 and #5.3.4)
573 sl->flags &= ~HTX_SL_F_HAS_AUTHORITY;
574 }
575 else {
576 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
577 if (sl->info.req.meth != HTTP_METH_CONNECT) {
578 // absolute-form (RFC7320 #5.3.2)
579 sl->flags |= HTX_SL_F_HAS_SCHM;
580 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
581 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
582
583 authority = http_get_authority(uri, 1);
584 if (!authority.len)
585 goto fail;
586 }
587 else {
588 // authority-form (RFC7320 #5.3.3)
589 authority = uri;
590 }
591
592 /* Replace header host value */
593 ctx.blk = NULL;
594 while (http_find_header(htx, ist("host"), &ctx, 1)) {
595 if (!http_replace_header_value(htx, &ctx, authority))
596 goto fail;
597 }
598
599 }
600 return 1;
601 fail:
602 return 0;
603}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200604
605/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
606 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
607 * performed over the whole headers. Otherwise it must contain a valid header
608 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
609 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
610 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
611 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
612 * -1. The value fetch stops at commas, so this function is suited for use with
613 * list headers.
614 * The return value is 0 if nothing was found, or non-zero otherwise.
615 */
616unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
617 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
618{
619 struct http_hdr_ctx local_ctx;
620 struct ist val_hist[MAX_HDR_HISTORY];
621 unsigned int hist_idx;
622 int found;
623
624 if (!ctx) {
625 local_ctx.blk = NULL;
626 ctx = &local_ctx;
627 }
628
629 if (occ >= 0) {
630 /* search from the beginning */
631 while (http_find_header(htx, hdr, ctx, 0)) {
632 occ--;
633 if (occ <= 0) {
634 *vptr = ctx->value.ptr;
635 *vlen = ctx->value.len;
636 return 1;
637 }
638 }
639 return 0;
640 }
641
642 /* negative occurrence, we scan all the list then walk back */
643 if (-occ > MAX_HDR_HISTORY)
644 return 0;
645
646 found = hist_idx = 0;
647 while (http_find_header(htx, hdr, ctx, 0)) {
648 val_hist[hist_idx] = ctx->value;
649 if (++hist_idx >= MAX_HDR_HISTORY)
650 hist_idx = 0;
651 found++;
652 }
653 if (-occ > found)
654 return 0;
655
656 /* OK now we have the last occurrence in [hist_idx-1], and we need to
657 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
658 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
659 * to remain in the 0..9 range.
660 */
661 hist_idx += occ + MAX_HDR_HISTORY;
662 if (hist_idx >= MAX_HDR_HISTORY)
663 hist_idx -= MAX_HDR_HISTORY;
664 *vptr = val_hist[hist_idx].ptr;
665 *vlen = val_hist[hist_idx].len;
666 return 1;
667}
668
669/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
670 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
671 * performed over the whole headers. Otherwise it must contain a valid header
672 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
673 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
674 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
675 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
676 * -1. This function differs from http_get_hdr() in that it only returns full
677 * line header values and does not stop at commas.
678 * The return value is 0 if nothing was found, or non-zero otherwise.
679 */
680unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
681 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
682{
683 struct http_hdr_ctx local_ctx;
684 struct ist val_hist[MAX_HDR_HISTORY];
685 unsigned int hist_idx;
686 int found;
687
688 if (!ctx) {
689 local_ctx.blk = NULL;
690 ctx = &local_ctx;
691 }
692
693 if (occ >= 0) {
694 /* search from the beginning */
695 while (http_find_header(htx, hdr, ctx, 1)) {
696 occ--;
697 if (occ <= 0) {
698 *vptr = ctx->value.ptr;
699 *vlen = ctx->value.len;
700 return 1;
701 }
702 }
703 return 0;
704 }
705
706 /* negative occurrence, we scan all the list then walk back */
707 if (-occ > MAX_HDR_HISTORY)
708 return 0;
709
710 found = hist_idx = 0;
711 while (http_find_header(htx, hdr, ctx, 1)) {
712 val_hist[hist_idx] = ctx->value;
713 if (++hist_idx >= MAX_HDR_HISTORY)
714 hist_idx = 0;
715 found++;
716 }
717 if (-occ > found)
718 return 0;
719
720 /* OK now we have the last occurrence in [hist_idx-1], and we need to
721 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
722 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
723 * to remain in the 0..9 range.
724 */
725 hist_idx += occ + MAX_HDR_HISTORY;
726 if (hist_idx >= MAX_HDR_HISTORY)
727 hist_idx -= MAX_HDR_HISTORY;
728 *vptr = val_hist[hist_idx].ptr;
729 *vlen = val_hist[hist_idx].len;
730 return 1;
731}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100732
Christopher Faulet90cc4812019-07-22 16:49:30 +0200733int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100734{
735 struct htx *htx;
736 struct htx_sl *sl;
737 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200738 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100739 union h1_sl h1sl;
740 unsigned int flags = HTX_SL_F_IS_RESP;
741 int ret = 0;
742
Christopher Faulet90cc4812019-07-22 16:49:30 +0200743 b_reset(buf);
744 if (!raw.len) {
745 buf->size = 0;
746 buf->area = malloc(raw.len);
747 return 1;
748 }
749
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100750 buf->size = global.tune.bufsize;
751 buf->area = (char *)malloc(buf->size);
752 if (!buf->area)
753 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100754
755 h1m_init_res(&h1m);
756 h1m.flags |= H1_MF_NO_PHDR;
757 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
758 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
759 if (ret <= 0)
760 goto error;
761
762 if (unlikely(h1sl.st.v.len != 8))
763 goto error;
764 if ((*(h1sl.st.v.ptr + 5) > '1') ||
765 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
766 h1m.flags |= H1_MF_VER_11;
767
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200768 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
769 goto error;
770
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100771 if (h1m.flags & H1_MF_VER_11)
772 flags |= HTX_SL_F_VER_11;
773 if (h1m.flags & H1_MF_XFER_ENC)
774 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200775 if (h1m.flags & H1_MF_CLEN) {
776 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
777 if (h1m.body_len == 0)
778 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100779 }
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200780 if (h1m.flags & H1_MF_CHNK)
781 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100782
783 htx = htx_from_buf(buf);
784 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
785 if (!sl || !htx_add_all_headers(htx, hdrs))
786 goto error;
787 sl->info.res.status = h1sl.st.status;
788
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200789 while (raw.len > ret) {
790 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
791 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100792 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200793 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100794 }
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200795
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100796 if (!htx_add_endof(htx, HTX_BLK_EOM))
797 goto error;
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200798
Christopher Faulet90cc4812019-07-22 16:49:30 +0200799 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100800
801error:
802 if (buf->size)
803 free(buf->area);
Christopher Faulet90cc4812019-07-22 16:49:30 +0200804 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100805}
806
807static int http_htx_init(void)
808{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100809 struct buffer chk;
810 struct ist raw;
811 int rc;
812 int err_code = 0;
813
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100814 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
815 if (!http_err_msgs[rc]) {
816 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
817 err_code |= ERR_ALERT | ERR_FATAL;
818 continue;
819 }
820
821 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
822 if (!http_str_to_htx(&chk, raw)) {
823 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
824 http_err_codes[rc]);
825 err_code |= ERR_ALERT | ERR_FATAL;
826 }
Christopher Fauletf7346382019-07-17 22:02:08 +0200827 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100828 }
829end:
830 return err_code;
831}
832
833REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);
Christopher Faulet29f72842019-12-11 15:52:32 +0100834
835/************************************************************************/
836/* HTX sample fetches */
837/************************************************************************/
838
839/* Returns 1 if a stream is an HTX stream. Otherwise, it returns 0. */
840static int
841smp_fetch_is_htx(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
842{
843 if (!smp->strm)
844 return 0;
845
846 smp->data.u.sint = !!IS_HTX_STRM(smp->strm);
847 smp->data.type = SMP_T_BOOL;
848 return 1;
849}
850
851/* Returns the number of blocks in an HTX message. The channel is chosen
852 * depending on the sample direction. */
853static int
854smp_fetch_htx_nbblks(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
855{
856 struct channel *chn;
857 struct htx *htx;
858
859 if (!smp->strm)
860 return 0;
861
862 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
863 htx = smp_prefetch_htx(smp, chn, 0);
864 if (!htx)
865 return 0;
866
867 smp->data.u.sint = htx_nbblks(htx);
868 smp->data.type = SMP_T_SINT;
869 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
870 return 1;
871}
872
873/* Returns the size of an HTX message. The channel is chosen depending on the
874 * sample direction. */
875static int
876smp_fetch_htx_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
877{
878 struct channel *chn;
879 struct htx *htx;
880
881 if (!smp->strm)
882 return 0;
883
884 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
885 htx = smp_prefetch_htx(smp, chn, 0);
886 if (!htx)
887 return 0;
888
889 smp->data.u.sint = htx->size;
890 smp->data.type = SMP_T_SINT;
891 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
892 return 1;
893}
894
895/* Returns the data size of an HTX message. The channel is chosen depending on the
896 * sample direction. */
897static int
898smp_fetch_htx_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
899{
900 struct channel *chn;
901 struct htx *htx;
902
903 if (!smp->strm)
904 return 0;
905
906 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
907 htx = smp_prefetch_htx(smp, chn, 0);
908 if (!htx)
909 return 0;
910
911 smp->data.u.sint = htx->data;
912 smp->data.type = SMP_T_SINT;
913 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
914 return 1;
915}
916
917/* Returns the used space (data+meta) of an HTX message. The channel is chosen
918 * depending on the sample direction. */
919static int
920smp_fetch_htx_used(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
921{
922 struct channel *chn;
923 struct htx *htx;
924
925 if (!smp->strm)
926 return 0;
927
928 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
929 htx = smp_prefetch_htx(smp, chn, 0);
930 if (!htx)
931 return 0;
932
933 smp->data.u.sint = htx_used_space(htx);
934 smp->data.type = SMP_T_SINT;
935 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
936 return 1;
937}
938
939/* Returns the free space (size-used) of an HTX message. The channel is chosen
940 * depending on the sample direction. */
941static int
942smp_fetch_htx_free(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
943{
944 struct channel *chn;
945 struct htx *htx;
946
947 if (!smp->strm)
948 return 0;
949
950 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
951 htx = smp_prefetch_htx(smp, chn, 0);
952 if (!htx)
953 return 0;
954
955 smp->data.u.sint = htx_free_space(htx);
956 smp->data.type = SMP_T_SINT;
957 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
958 return 1;
959}
960
961/* Returns the free space for data (free-sizeof(blk)) of an HTX message. The
962 * channel is chosen depending on the sample direction. */
963static int
964smp_fetch_htx_free_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
965{
966 struct channel *chn;
967 struct htx *htx;
968
969 if (!smp->strm)
970 return 0;
971
972 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
973 htx = smp_prefetch_htx(smp, chn, 0);
974 if (!htx)
975 return 0;
976
977 smp->data.u.sint = htx_free_data_space(htx);
978 smp->data.type = SMP_T_SINT;
979 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
980 return 1;
981}
982
983/* Returns 1 if the HTX message contains an EOM block. Otherwise it returns
984 * 0. Concretely, it only checks the tail. The channel is chosen depending on
985 * the sample direction. */
986static int
987smp_fetch_htx_has_eom(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
988{
989 struct channel *chn;
990 struct htx *htx;
991
992 if (!smp->strm)
993 return 0;
994
995 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
996 htx = smp_prefetch_htx(smp, chn, 0);
997 if (!htx)
998 return 0;
999
1000 smp->data.u.sint = (htx_get_tail_type(htx) == HTX_BLK_EOM);
1001 smp->data.type = SMP_T_BOOL;
1002 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1003 return 1;
1004}
1005
1006/* Returns the type of a specific HTX block, if found in the message. Otherwise
1007 * HTX_BLK_UNUSED is returned. Any positive integer (>= 0) is supported or
1008 * "head", "tail" or "first". The channel is chosen depending on the sample
1009 * direction. */
1010static int
1011smp_fetch_htx_blk_type(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1012{
1013 struct channel *chn;
1014 struct htx *htx;
1015 enum htx_blk_type type;
1016 int32_t pos;
1017
1018 if (!smp->strm || !arg_p)
1019 return 0;
1020
1021 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1022 htx = smp_prefetch_htx(smp, chn, 0);
1023 if (!htx)
1024 return 0;
1025
1026 pos = arg_p[0].data.sint;
1027 if (pos == -1)
1028 type = htx_get_head_type(htx);
1029 else if (pos == -2)
1030 type = htx_get_tail_type(htx);
1031 else if (pos == -3)
1032 type = htx_get_first_type(htx);
1033 else
1034 type = ((pos >= htx->head && pos <= htx->tail)
1035 ? htx_get_blk_type(htx_get_blk(htx, pos))
1036 : HTX_BLK_UNUSED);
1037
1038 chunk_initstr(&smp->data.u.str, htx_blk_type_str(type));
1039 smp->data.type = SMP_T_STR;
1040 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1041 return 1;
1042}
1043
1044/* Returns the size of a specific HTX block, if found in the message. Otherwise
1045 * 0 is returned. Any positive integer (>= 0) is supported or "head", "tail" or
1046 * "first". The channel is chosen depending on the sample direction. */
1047static int
1048smp_fetch_htx_blk_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1049{
1050 struct channel *chn;
1051 struct htx *htx;
1052 struct htx_blk *blk;
1053 int32_t pos;
1054
1055 if (!smp->strm || !arg_p)
1056 return 0;
1057
1058 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1059 htx = smp_prefetch_htx(smp, chn, 0);
1060 if (!htx)
1061 return 0;
1062
1063 pos = arg_p[0].data.sint;
1064 if (pos == -1)
1065 blk = htx_get_head_blk(htx);
1066 else if (pos == -2)
1067 blk = htx_get_tail_blk(htx);
1068 else if (pos == -3)
1069 blk = htx_get_first_blk(htx);
1070 else
1071 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1072
1073 smp->data.u.sint = (blk ? htx_get_blksz(blk) : 0);
1074 smp->data.type = SMP_T_SINT;
1075 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1076 return 1;
1077}
1078
1079/* Returns the start-line if the selected HTX block exists and is a
1080 * start-line. Otherwise 0 an empty string. Any positive integer (>= 0) is
1081 * supported or "head", "tail" or "first". The channel is chosen depending on
1082 * the sample direction. */
1083static int
1084smp_fetch_htx_blk_stline(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1085{
1086 struct buffer *temp;
1087 struct channel *chn;
1088 struct htx *htx;
1089 struct htx_blk *blk;
1090 struct htx_sl *sl;
1091 int32_t pos;
1092
1093 if (!smp->strm || !arg_p)
1094 return 0;
1095
1096 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1097 htx = smp_prefetch_htx(smp, chn, 0);
1098 if (!htx)
1099 return 0;
1100
1101 pos = arg_p[0].data.sint;
1102 if (pos == -1)
1103 blk = htx_get_head_blk(htx);
1104 else if (pos == -2)
1105 blk = htx_get_tail_blk(htx);
1106 else if (pos == -3)
1107 blk = htx_get_first_blk(htx);
1108 else
1109 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1110
1111 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL)) {
1112 smp->data.u.str.size = 0;
1113 smp->data.u.str.area = "";
1114 smp->data.u.str.data = 0;
1115 }
1116 else {
1117 sl = htx_get_blk_ptr(htx, blk);
1118
1119 temp = get_trash_chunk();
1120 chunk_istcat(temp, htx_sl_p1(sl));
1121 temp->area[temp->data++] = ' ';
1122 chunk_istcat(temp, htx_sl_p2(sl));
1123 temp->area[temp->data++] = ' ';
1124 chunk_istcat(temp, htx_sl_p3(sl));
1125
1126 smp->data.u.str = *temp;
1127 }
1128
1129 smp->data.type = SMP_T_STR;
1130 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1131 return 1;
1132}
1133
1134/* Returns the header name if the selected HTX block exists and is a header or a
1135 * trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1136 * supported or "head", "tail" or "first". The channel is chosen depending on
1137 * the sample direction. */
1138static int
1139smp_fetch_htx_blk_hdrname(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1140{
1141 struct channel *chn;
1142 struct htx *htx;
1143 struct htx_blk *blk;
1144 int32_t pos;
1145
1146 if (!smp->strm || !arg_p)
1147 return 0;
1148
1149 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1150 htx = smp_prefetch_htx(smp, chn, 0);
1151 if (!htx)
1152 return 0;
1153
1154 pos = arg_p[0].data.sint;
1155 if (pos == -1)
1156 blk = htx_get_head_blk(htx);
1157 else if (pos == -2)
1158 blk = htx_get_tail_blk(htx);
1159 else if (pos == -3)
1160 blk = htx_get_first_blk(htx);
1161 else
1162 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1163
1164 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1165 smp->data.u.str.size = 0;
1166 smp->data.u.str.area = "";
1167 smp->data.u.str.data = 0;
1168 }
1169 else {
1170 struct ist name = htx_get_blk_name(htx, blk);
1171
1172 chunk_initlen(&smp->data.u.str, name.ptr, name.len, name.len);
1173 }
1174 smp->data.type = SMP_T_STR;
1175 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1176 return 1;
1177}
1178
1179/* Returns the header value if the selected HTX block exists and is a header or
1180 * a trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1181 * supported or "head", "tail" or "first". The channel is chosen depending on
1182 * the sample direction. */
1183static int
1184smp_fetch_htx_blk_hdrval(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1185{
1186 struct channel *chn;
1187 struct htx *htx;
1188 struct htx_blk *blk;
1189 int32_t pos;
1190
1191 if (!smp->strm || !arg_p)
1192 return 0;
1193
1194 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1195 htx = smp_prefetch_htx(smp, chn, 0);
1196 if (!htx)
1197 return 0;
1198
1199 pos = arg_p[0].data.sint;
1200 if (pos == -1)
1201 blk = htx_get_head_blk(htx);
1202 else if (pos == -2)
1203 blk = htx_get_tail_blk(htx);
1204 else if (pos == -3)
1205 blk = htx_get_first_blk(htx);
1206 else
1207 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1208
1209 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1210 smp->data.u.str.size = 0;
1211 smp->data.u.str.area = "";
1212 smp->data.u.str.data = 0;
1213 }
1214 else {
1215 struct ist val = htx_get_blk_value(htx, blk);
1216
1217 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
1218 }
1219 smp->data.type = SMP_T_STR;
1220 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1221 return 1;
1222}
1223
1224/* Returns the value if the selected HTX block exists and is a data
1225 * block. Otherwise 0 an empty string. Any positive integer (>= 0) is supported
1226 * or "head", "tail" or "first". The channel is chosen depending on the sample
1227 * direction. */
1228static int
Christopher Fauletc5db14c2020-01-08 14:51:03 +01001229smp_fetch_htx_blk_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
Christopher Faulet29f72842019-12-11 15:52:32 +01001230{
1231 struct channel *chn;
1232 struct htx *htx;
1233 struct htx_blk *blk;
1234 int32_t pos;
1235
1236 if (!smp->strm || !arg_p)
1237 return 0;
1238
1239 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1240 htx = smp_prefetch_htx(smp, chn, 0);
1241 if (!htx)
1242 return 0;
1243
1244 pos = arg_p[0].data.sint;
1245 if (pos == -1)
1246 blk = htx_get_head_blk(htx);
1247 else if (pos == -2)
1248 blk = htx_get_tail_blk(htx);
1249 else if (pos == -3)
1250 blk = htx_get_first_blk(htx);
1251 else
1252 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1253
1254 if (!blk || htx_get_blk_type(blk) != HTX_BLK_DATA) {
1255 smp->data.u.str.size = 0;
1256 smp->data.u.str.area = "";
1257 smp->data.u.str.data = 0;
1258 }
1259 else {
1260 struct ist val = htx_get_blk_value(htx, blk);
1261
1262 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
1263 }
Christopher Faulet8178e402020-01-08 14:38:58 +01001264 smp->data.type = SMP_T_BIN;
Christopher Faulet29f72842019-12-11 15:52:32 +01001265 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1266 return 1;
1267}
1268
1269/* This function is used to validate the arguments passed to any "htx_blk" fetch
1270 * keywords. An argument is expected by these keywords. It must be a positive
1271 * integer or on of the following strings: "head", "tail" or "first". It returns
1272 * 0 on error, and a non-zero value if OK.
1273 */
1274int val_blk_arg(struct arg *arg, char **err_msg)
1275{
1276 if (arg[0].type != ARGT_STR || !arg[0].data.str.data) {
1277 memprintf(err_msg, "a block position is expected (> 0) or a special block name (head, tail, first)");
1278 return 0;
1279 }
1280 if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "head", 4)) {
1281 free(arg[0].data.str.area);
1282 arg[0].type = ARGT_SINT;
1283 arg[0].data.sint = -1;
1284 }
1285 else if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "tail", 4)) {
1286 free(arg[0].data.str.area);
1287 arg[0].type = ARGT_SINT;
1288 arg[0].data.sint = -2;
1289 }
1290 else if (arg[0].data.str.data == 5 && !strncmp(arg[0].data.str.area, "first", 5)) {
1291 free(arg[0].data.str.area);
1292 arg[0].type = ARGT_SINT;
1293 arg[0].data.sint = -3;
1294 }
1295 else {
1296 int pos;
1297
1298 for (pos = 0; pos < arg[0].data.str.data; pos++) {
1299 if (!isdigit(arg[0].data.str.area[pos])) {
1300 memprintf(err_msg, "invalid block position");
1301 return 0;
1302 }
1303 }
1304
1305 pos = strl2uic(arg[0].data.str.area, arg[0].data.str.data);
1306 if (pos < 0) {
1307 memprintf(err_msg, "block position must not be negative");
1308 return 0;
1309 }
1310 free(arg[0].data.str.area);
1311 arg[0].type = ARGT_SINT;
1312 arg[0].data.sint = pos;
1313 }
1314
1315 return 1;
1316}
1317
1318
1319/* Note: must not be declared <const> as its list will be overwritten.
1320 * Note: htx sample fetches should only used for developpement purpose.
1321 */
1322static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet01f44452020-01-08 14:23:40 +01001323 { "internal.strm.is_htx", smp_fetch_is_htx, 0, NULL, SMP_T_BOOL, SMP_USE_L6REQ },
Christopher Faulet29f72842019-12-11 15:52:32 +01001324
Christopher Faulet01f44452020-01-08 14:23:40 +01001325 { "internal.htx.nbblks", smp_fetch_htx_nbblks, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1326 { "internal.htx.size", smp_fetch_htx_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1327 { "internal.htx.data", smp_fetch_htx_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1328 { "internal.htx.used", smp_fetch_htx_used, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1329 { "internal.htx.free", smp_fetch_htx_free, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1330 { "internal.htx.free_data", smp_fetch_htx_free_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1331 { "internal.htx.has_eom", smp_fetch_htx_has_eom, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHV|SMP_USE_HRSHV},
Christopher Faulet29f72842019-12-11 15:52:32 +01001332
Christopher Faulet01f44452020-01-08 14:23:40 +01001333 { "internal.htx_blk.type", smp_fetch_htx_blk_type, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
1334 { "internal.htx_blk.size", smp_fetch_htx_blk_size, ARG1(1,STR), val_blk_arg, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1335 { "internal.htx_blk.start_line", smp_fetch_htx_blk_stline, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
1336 { "internal.htx_blk.hdrname", smp_fetch_htx_blk_hdrname, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
1337 { "internal.htx_blk.hdrval", smp_fetch_htx_blk_hdrval, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
Christopher Fauletc5db14c2020-01-08 14:51:03 +01001338 { "internal.htx_blk.data", smp_fetch_htx_blk_data, ARG1(1,STR), val_blk_arg, SMP_T_BIN, SMP_USE_HRQHV|SMP_USE_HRSHV},
Christopher Faulet29f72842019-12-11 15:52:32 +01001339
1340 { /* END */ },
1341}};
1342
1343INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);