blob: 98a62bdc93bec98e591bfce104f2e5e9ce06aefe [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 */
Christopher Faulet5031ef52020-01-15 11:22:07 +010012#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <unistd.h>
16
17#include <types/global.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020018
19#include <common/config.h>
Christopher Faulet29f17582019-05-23 11:03:26 +020020#include <common/debug.h>
Christopher Fauleta7b677c2018-11-29 16:48:49 +010021#include <common/cfgparse.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010022#include <common/h1.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020023#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010024#include <common/htx.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020025
Christopher Faulet29f72842019-12-11 15:52:32 +010026#include <proto/arg.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020027#include <proto/http_htx.h>
Christopher Faulet29f72842019-12-11 15:52:32 +010028#include <proto/http_fetch.h>
29#include <proto/sample.h>
Christopher Faulet47596d32018-10-22 09:17:28 +020030
Christopher Fauletf7346382019-07-17 22:02:08 +020031struct buffer http_err_chunks[HTTP_ERR_SIZE];
Christopher Faulet58857752020-01-15 15:19:50 +010032struct eb_root http_error_messages = EB_ROOT;
Christopher Fauleta7b677c2018-11-29 16:48:49 +010033
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +020034static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host);
35static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri);
36
Christopher Faulet297fbb42019-05-13 14:41:27 +020037/* Returns the next unporocessed start line in the HTX message. It returns NULL
Christopher Faulet29f17582019-05-23 11:03:26 +020038 * if the start-line is undefined (first == -1). Otherwise, it returns the
Christopher Faulet297fbb42019-05-13 14:41:27 +020039 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020040 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020041struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020042{
Christopher Faulet297fbb42019-05-13 14:41:27 +020043 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010044
Christopher Faulet29f17582019-05-23 11:03:26 +020045 BUG_ON(htx->first == -1);
46 blk = htx_get_first_blk(htx);
Christopher Faulet297fbb42019-05-13 14:41:27 +020047 if (!blk)
48 return NULL;
Christopher Faulet29f17582019-05-23 11:03:26 +020049 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 +020050 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020051}
52
53/* Finds the first or next occurrence of header <name> in the HTX message <htx>
54 * using the context <ctx>. This structure holds everything necessary to use the
55 * header and find next occurrence. If its <blk> member is NULL, the header is
56 * searched from the beginning. Otherwise, the next occurrence is returned. The
57 * function returns 1 when it finds a value, and 0 when there is no more. It is
58 * designed to work with headers defined as comma-separated lists. If <full> is
59 * set, it works on full-line headers in whose comma is not a delimiter but is
60 * part of the syntax. A special case, if ctx->value is NULL when searching for
61 * a new values of a header, the current header is rescanned. This allows
62 * rescanning after a header deletion.
63 */
64int http_find_header(const struct htx *htx, const struct ist name,
65 struct http_hdr_ctx *ctx, int full)
66{
67 struct htx_blk *blk = ctx->blk;
68 struct ist n, v;
69 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +020070
71 if (blk) {
72 char *p;
73
Christopher Faulet47596d32018-10-22 09:17:28 +020074 if (!ctx->value.ptr)
75 goto rescan_hdr;
76 if (full)
77 goto next_blk;
78 v = htx_get_blk_value(htx, blk);
79 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
80 v.len -= (p - v.ptr);
81 v.ptr = p;
82 if (!v.len)
83 goto next_blk;
84 /* Skip comma */
85 if (*(v.ptr) == ',') {
86 v.ptr++;
87 v.len--;
88 }
89
90 goto return_hdr;
91 }
92
Christopher Faulet192c6a22019-06-11 16:32:24 +020093 if (htx_is_empty(htx))
Christopher Faulet47596d32018-10-22 09:17:28 +020094 return 0;
95
Christopher Fauleta3f15502019-05-13 15:27:23 +020096 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +020097 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +020098 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +010099 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
100 break;
Christopher Faulet47596d32018-10-22 09:17:28 +0200101 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200102 continue;
Christopher Faulet47596d32018-10-22 09:17:28 +0200103 if (name.len) {
104 /* If no name was passed, we want any header. So skip the comparison */
105 n = htx_get_blk_name(htx, blk);
106 if (!isteqi(n, name))
107 goto next_blk;
108 }
109 v = htx_get_blk_value(htx, blk);
110
111 return_hdr:
112 ctx->lws_before = 0;
113 ctx->lws_after = 0;
114 while (v.len && HTTP_IS_LWS(*v.ptr)) {
115 v.ptr++;
116 v.len--;
117 ctx->lws_before++;
118 }
119 if (!full)
120 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
121 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
122 v.len--;
123 ctx->lws_after++;
124 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200125 ctx->blk = blk;
126 ctx->value = v;
127 return 1;
128
129 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200130 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200131 }
132
133 ctx->blk = NULL;
134 ctx->value = ist("");
135 ctx->lws_before = ctx->lws_after = 0;
136 return 0;
137}
138
139/* Adds a header block int the HTX message <htx>, just before the EOH block. It
140 * returns 1 on success, otherwise it returns 0.
141 */
142int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
143{
144 struct htx_blk *blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200145 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200146 enum htx_blk_type type = htx_get_tail_type(htx);
147 int32_t prev;
148
149 blk = htx_add_header(htx, n, v);
150 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200151 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200152
153 if (unlikely(type < HTX_BLK_EOH))
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200154 goto end;
Christopher Faulet47596d32018-10-22 09:17:28 +0200155
156 /* <blk> is the head, swap it iteratively with its predecessor to place
157 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200158 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200159 struct htx_blk *pblk = htx_get_blk(htx, prev);
160 enum htx_blk_type type = htx_get_blk_type(pblk);
161
162 /* Swap .addr and .info fields */
163 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
164 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
165
166 if (blk->addr == pblk->addr)
167 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200168
169 /* Stop when end-of-header is reached */
170 if (type == HTX_BLK_EOH)
171 break;
172
173 blk = pblk;
174 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200175
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200176 end:
177 sl = http_get_stline(htx);
178 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(n, ist("host"))) {
179 if (!http_update_authority(htx, sl, v))
180 goto fail;
181 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200182 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200183
184 fail:
185 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200186}
187
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100188/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200189 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200190 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100191int 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 +0200192{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200193 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200194
Christopher Faulet29f17582019-05-23 11:03:26 +0200195 blk = htx_get_first_blk(htx);
196 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200197 return 0;
198 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200199}
200
Christopher Faulete010c802018-10-24 10:36:45 +0200201/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
202 * on success, otherwise 0.
203 */
204int http_replace_req_meth(struct htx *htx, const struct ist meth)
205{
206 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200207 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100208 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200209
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100210 if (!sl)
211 return 0;
212
Christopher Faulete010c802018-10-24 10:36:45 +0200213 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100214 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
215 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200216
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100217 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
218 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200219
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100220 /* create the new start line */
221 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
222 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200223}
224
225/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
226 * success, otherwise 0.
227 */
228int http_replace_req_uri(struct htx *htx, const struct ist uri)
229{
230 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200231 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100232 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200233
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100234 if (!sl)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200235 goto fail;
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100236
Christopher Faulete010c802018-10-24 10:36:45 +0200237 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100238 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
239 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200240
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100241 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
242 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200243
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100244 /* create the new start line */
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200245 if (!http_replace_stline(htx, meth, uri, vsn))
246 goto fail;
247
248 sl = http_get_stline(htx);
249 if (!http_update_host(htx, sl, uri))
250 goto fail;
251
252 return 1;
253 fail:
254 return 0;
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 Faulet297fbb42019-05-13 14:41:27 +0200263 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100264 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 Faulet297fbb42019-05-13 14:41:27 +0200300 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100301 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 Faulet297fbb42019-05-13 14:41:27 +0200345 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100346 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 Faulet297fbb42019-05-13 14:41:27 +0200369 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100370 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;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200393 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200394 char *start;
395 struct ist v;
396 uint32_t len, off;
397
398 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200399 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200400
401 v = htx_get_blk_value(htx, blk);
402 start = ctx->value.ptr - ctx->lws_before;
403 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
404 off = start - v.ptr;
405
406 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
407 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200408 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200409
410 v = htx_get_blk_value(htx, blk);
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200411
412 sl = http_get_stline(htx);
413 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
414 struct ist n = htx_get_blk_name(htx, blk);
415
416 if (isteq(n, ist("host"))) {
417 if (!http_update_authority(htx, sl, v))
418 goto fail;
419 ctx->blk = NULL;
420 http_find_header(htx, ist("host"), ctx, 1);
421 blk = ctx->blk;
422 v = htx_get_blk_value(htx, blk);
423 }
424 }
425
Christopher Faulet47596d32018-10-22 09:17:28 +0200426 ctx->blk = blk;
427 ctx->value.ptr = v.ptr + off;
428 ctx->value.len = data.len;
429 ctx->lws_before = ctx->lws_after = 0;
430
431 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200432 fail:
433 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200434}
435
436/* Fully replaces a header referenced in the context <ctx> by the name <name>
437 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
438 * context is updated if necessary.
439 */
440int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
441 const struct ist name, const struct ist value)
442{
443 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200444 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200445
446 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200447 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200448
449 blk = htx_replace_header(htx, blk, name, value);
450 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200451 goto fail;
452
453 sl = http_get_stline(htx);
454 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteq(name, ist("host"))) {
455 if (!http_update_authority(htx, sl, value))
456 goto fail;
457 ctx->blk = NULL;
458 http_find_header(htx, ist("host"), ctx, 1);
459 blk = ctx->blk;
460 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200461
462 ctx->blk = blk;
463 ctx->value = ist(NULL);
464 ctx->lws_before = ctx->lws_after = 0;
465
466 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200467 fail:
468 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200469}
470
471/* Remove one value of a header. This only works on a <ctx> returned by
472 * http_find_header function. The value is removed, as well as surrounding commas
473 * if any. If the removed value was alone, the whole header is removed. The
474 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
475 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
476 * form that can be handled by http_find_header() to find next occurrence.
477 */
478int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
479{
480 struct htx_blk *blk = ctx->blk;
481 char *start;
482 struct ist v;
483 uint32_t len;
484
485 if (!blk)
486 return 0;
487
488 start = ctx->value.ptr - ctx->lws_before;
489 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
490
491 v = htx_get_blk_value(htx, blk);
492 if (len == v.len) {
493 blk = htx_remove_blk(htx, blk);
Christopher Faulet192c6a22019-06-11 16:32:24 +0200494 if (blk || htx_is_empty(htx)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200495 ctx->blk = blk;
496 ctx->value = ist2(NULL, 0);
497 ctx->lws_before = ctx->lws_after = 0;
498 }
499 else {
500 ctx->blk = htx_get_blk(htx, htx->tail);
501 ctx->value = htx_get_blk_value(htx, ctx->blk);
502 ctx->lws_before = ctx->lws_after = 0;
503 }
504 return 1;
505 }
506
507 /* This was not the only value of this header. We have to remove the
508 * part pointed by ctx->value. If it is the last entry of the list, we
509 * remove the last separator.
510 */
511 if (start == v.ptr) {
512 /* It's the first header part but not the only one. So remove
513 * the comma after it. */
514 len++;
515 }
516 else {
517 /* There is at least one header part before the removed one. So
518 * remove the comma between them. */
519 start--;
520 len++;
521 }
522 /* Update the block content and its len */
523 memmove(start, start+len, v.len-len);
Christopher Faulet3e2638e2019-06-18 09:49:16 +0200524 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200525
526 /* Finally update the ctx */
527 ctx->value.ptr = start;
528 ctx->value.len = 0;
529 ctx->lws_before = ctx->lws_after = 0;
530
531 return 1;
532}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200533
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200534/* Updates the authority part of the uri with the value <host>. It happens when
535 * the header host is modified. It returns 0 on failure and 1 on success. It is
536 * the caller responsibility to provide the start-line and to be sure the uri
537 * contains an authority. Thus, if no authority is found in the uri, an error is
538 * returned.
539 */
540static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host)
541{
542 struct buffer *temp = get_trash_chunk();
543 struct ist meth, vsn, uri, authority;
544
545 uri = htx_sl_req_uri(sl);
546 authority = http_get_authority(uri, 1);
547 if (!authority.len || isteq(host, authority))
548 return 0;
549
550 /* Start by copying old method and version */
551 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
552 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
553
554 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
555 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
556
557 chunk_memcat(temp, uri.ptr, authority.ptr - uri.ptr);
558 chunk_memcat(temp, host.ptr, host.len);
559 chunk_memcat(temp, authority.ptr + authority.len, uri.ptr + uri.len - (authority.ptr + authority.len));
560 uri = ist2(temp->area + meth.len + vsn.len, host.len + uri.len - authority.len); /* uri */
561
562 return http_replace_stline(htx, meth, uri, vsn);
563
564}
565
566/* Update the header host by extracting the authority of the uri <uri>. flags of
567 * the start-line are also updated accordingly. For orgin-form and asterisk-form
568 * uri, the header host is not changed and the flag HTX_SL_F_HAS_AUTHORITY is
569 * removed from the flags of the start-line. Otherwise, this flag is set and the
570 * authority is used to set the value of the header host. This function returns
571 * 0 on failure and 1 on success.
572*/
573static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri)
574{
575 struct ist authority;
576 struct http_hdr_ctx ctx;
577
578 if (!uri.len || uri.ptr[0] == '/' || uri.ptr[0] == '*') {
579 // origin-form or a asterisk-form (RFC7320 #5.3.1 and #5.3.4)
580 sl->flags &= ~HTX_SL_F_HAS_AUTHORITY;
581 }
582 else {
583 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
584 if (sl->info.req.meth != HTTP_METH_CONNECT) {
585 // absolute-form (RFC7320 #5.3.2)
586 sl->flags |= HTX_SL_F_HAS_SCHM;
587 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
588 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
589
590 authority = http_get_authority(uri, 1);
591 if (!authority.len)
592 goto fail;
593 }
594 else {
595 // authority-form (RFC7320 #5.3.3)
596 authority = uri;
597 }
598
599 /* Replace header host value */
600 ctx.blk = NULL;
601 while (http_find_header(htx, ist("host"), &ctx, 1)) {
602 if (!http_replace_header_value(htx, &ctx, authority))
603 goto fail;
604 }
605
606 }
607 return 1;
608 fail:
609 return 0;
610}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200611
612/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
613 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
614 * performed over the whole headers. Otherwise it must contain a valid header
615 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
616 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
617 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
618 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
619 * -1. The value fetch stops at commas, so this function is suited for use with
620 * list headers.
621 * The return value is 0 if nothing was found, or non-zero otherwise.
622 */
623unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
624 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
625{
626 struct http_hdr_ctx local_ctx;
627 struct ist val_hist[MAX_HDR_HISTORY];
628 unsigned int hist_idx;
629 int found;
630
631 if (!ctx) {
632 local_ctx.blk = NULL;
633 ctx = &local_ctx;
634 }
635
636 if (occ >= 0) {
637 /* search from the beginning */
638 while (http_find_header(htx, hdr, ctx, 0)) {
639 occ--;
640 if (occ <= 0) {
641 *vptr = ctx->value.ptr;
642 *vlen = ctx->value.len;
643 return 1;
644 }
645 }
646 return 0;
647 }
648
649 /* negative occurrence, we scan all the list then walk back */
650 if (-occ > MAX_HDR_HISTORY)
651 return 0;
652
653 found = hist_idx = 0;
654 while (http_find_header(htx, hdr, ctx, 0)) {
655 val_hist[hist_idx] = ctx->value;
656 if (++hist_idx >= MAX_HDR_HISTORY)
657 hist_idx = 0;
658 found++;
659 }
660 if (-occ > found)
661 return 0;
662
663 /* OK now we have the last occurrence in [hist_idx-1], and we need to
664 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
665 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
666 * to remain in the 0..9 range.
667 */
668 hist_idx += occ + MAX_HDR_HISTORY;
669 if (hist_idx >= MAX_HDR_HISTORY)
670 hist_idx -= MAX_HDR_HISTORY;
671 *vptr = val_hist[hist_idx].ptr;
672 *vlen = val_hist[hist_idx].len;
673 return 1;
674}
675
676/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
677 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
678 * performed over the whole headers. Otherwise it must contain a valid header
679 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
680 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
681 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
682 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
683 * -1. This function differs from http_get_hdr() in that it only returns full
684 * line header values and does not stop at commas.
685 * The return value is 0 if nothing was found, or non-zero otherwise.
686 */
687unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
688 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
689{
690 struct http_hdr_ctx local_ctx;
691 struct ist val_hist[MAX_HDR_HISTORY];
692 unsigned int hist_idx;
693 int found;
694
695 if (!ctx) {
696 local_ctx.blk = NULL;
697 ctx = &local_ctx;
698 }
699
700 if (occ >= 0) {
701 /* search from the beginning */
702 while (http_find_header(htx, hdr, ctx, 1)) {
703 occ--;
704 if (occ <= 0) {
705 *vptr = ctx->value.ptr;
706 *vlen = ctx->value.len;
707 return 1;
708 }
709 }
710 return 0;
711 }
712
713 /* negative occurrence, we scan all the list then walk back */
714 if (-occ > MAX_HDR_HISTORY)
715 return 0;
716
717 found = hist_idx = 0;
718 while (http_find_header(htx, hdr, ctx, 1)) {
719 val_hist[hist_idx] = ctx->value;
720 if (++hist_idx >= MAX_HDR_HISTORY)
721 hist_idx = 0;
722 found++;
723 }
724 if (-occ > found)
725 return 0;
726
727 /* OK now we have the last occurrence in [hist_idx-1], and we need to
728 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
729 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
730 * to remain in the 0..9 range.
731 */
732 hist_idx += occ + MAX_HDR_HISTORY;
733 if (hist_idx >= MAX_HDR_HISTORY)
734 hist_idx -= MAX_HDR_HISTORY;
735 *vptr = val_hist[hist_idx].ptr;
736 *vlen = val_hist[hist_idx].len;
737 return 1;
738}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100739
Christopher Faulet90cc4812019-07-22 16:49:30 +0200740int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100741{
742 struct htx *htx;
743 struct htx_sl *sl;
744 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200745 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100746 union h1_sl h1sl;
747 unsigned int flags = HTX_SL_F_IS_RESP;
748 int ret = 0;
749
Christopher Faulet90cc4812019-07-22 16:49:30 +0200750 b_reset(buf);
751 if (!raw.len) {
752 buf->size = 0;
753 buf->area = malloc(raw.len);
754 return 1;
755 }
756
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100757 buf->size = global.tune.bufsize;
758 buf->area = (char *)malloc(buf->size);
759 if (!buf->area)
760 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100761
762 h1m_init_res(&h1m);
763 h1m.flags |= H1_MF_NO_PHDR;
764 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
765 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
766 if (ret <= 0)
767 goto error;
768
769 if (unlikely(h1sl.st.v.len != 8))
770 goto error;
771 if ((*(h1sl.st.v.ptr + 5) > '1') ||
772 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
773 h1m.flags |= H1_MF_VER_11;
774
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200775 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
776 goto error;
777
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100778 if (h1m.flags & H1_MF_VER_11)
779 flags |= HTX_SL_F_VER_11;
780 if (h1m.flags & H1_MF_XFER_ENC)
781 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200782 if (h1m.flags & H1_MF_CLEN) {
783 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
784 if (h1m.body_len == 0)
785 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100786 }
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200787 if (h1m.flags & H1_MF_CHNK)
788 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100789
790 htx = htx_from_buf(buf);
791 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
792 if (!sl || !htx_add_all_headers(htx, hdrs))
793 goto error;
794 sl->info.res.status = h1sl.st.status;
795
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200796 while (raw.len > ret) {
797 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
798 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100799 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200800 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100801 }
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200802
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100803 if (!htx_add_endof(htx, HTX_BLK_EOM))
804 goto error;
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200805
Christopher Faulet90cc4812019-07-22 16:49:30 +0200806 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100807
808error:
809 if (buf->size)
810 free(buf->area);
Christopher Faulet90cc4812019-07-22 16:49:30 +0200811 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100812}
813
814static int http_htx_init(void)
815{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100816 struct buffer chk;
817 struct ist raw;
818 int rc;
819 int err_code = 0;
820
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100821 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
822 if (!http_err_msgs[rc]) {
823 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
824 err_code |= ERR_ALERT | ERR_FATAL;
825 continue;
826 }
827
828 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
829 if (!http_str_to_htx(&chk, raw)) {
830 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
831 http_err_codes[rc]);
832 err_code |= ERR_ALERT | ERR_FATAL;
833 }
Christopher Fauletf7346382019-07-17 22:02:08 +0200834 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100835 }
836end:
837 return err_code;
838}
839
Christopher Faulet58857752020-01-15 15:19:50 +0100840static void http_htx_deinit(void)
841{
842 struct ebpt_node *node, *next;
843 struct http_error *http_err;
844
845 node = ebpt_first(&http_error_messages);
846 while (node) {
847 next = ebpt_next(node);
848 ebpt_delete(node);
849 http_err = container_of(node, typeof(*http_err), node);
850 chunk_destroy(&http_err->msg);
851 free(node->key);
852 free(http_err);
853 node = next;
854 }
855}
856
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100857REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);
Christopher Faulet58857752020-01-15 15:19:50 +0100858REGISTER_POST_DEINIT(http_htx_deinit);
Christopher Faulet29f72842019-12-11 15:52:32 +0100859
Christopher Faulet58857752020-01-15 15:19:50 +0100860/* Reads content of the error file <file> and convert it into an HTX message. On
861 * success, the HTX message is returned. On error, NULL is returned and an error
862 * message is written into the <errmsg> buffer.
Christopher Faulet5031ef52020-01-15 11:22:07 +0100863 */
Christopher Faulet58857752020-01-15 15:19:50 +0100864struct buffer *http_load_errorfile(const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +0100865{
Christopher Faulet58857752020-01-15 15:19:50 +0100866 struct buffer *buf = NULL;
867 struct buffer chk;
868 struct ebpt_node *node;
869 struct http_error *http_err;
Christopher Faulet5031ef52020-01-15 11:22:07 +0100870 struct stat stat;
871 char *err = NULL;
872 int errnum, errlen;
873 int fd = -1;
Christopher Faulet58857752020-01-15 15:19:50 +0100874
875 /* already loaded */
876 node = ebis_lookup_len(&http_error_messages, file, strlen(file));
877 if (node) {
878 http_err = container_of(node, typeof(*http_err), node);
879 buf = &http_err->msg;
880 goto out;
881 }
Christopher Faulet5031ef52020-01-15 11:22:07 +0100882
Christopher Faulet58857752020-01-15 15:19:50 +0100883 /* Read the error file content */
Christopher Faulet5031ef52020-01-15 11:22:07 +0100884 fd = open(file, O_RDONLY);
885 if ((fd < 0) || (fstat(fd, &stat) < 0)) {
886 memprintf(errmsg, "error opening file '%s'.", file);
887 goto out;
888 }
889
890 if (stat.st_size <= global.tune.bufsize)
891 errlen = stat.st_size;
892 else {
893 ha_warning("custom error message file '%s' larger than %d bytes. Truncating.\n",
894 file, global.tune.bufsize);
895 errlen = global.tune.bufsize;
896 }
897
898 err = malloc(errlen);
899 if (!err) {
900 memprintf(errmsg, "out of memory.");
901 goto out;
902 }
903
904 errnum = read(fd, err, errlen);
905 if (errnum != errlen) {
906 memprintf(errmsg, "error reading file '%s'.", file);
907 goto out;
908 }
909
Christopher Faulet58857752020-01-15 15:19:50 +0100910 /* Create the node corresponding to the error file */
911 http_err = calloc(1, sizeof(*http_err));
912 if (!http_err) {
913 memprintf(errmsg, "out of memory.");
914 goto out;
915 }
916 http_err->node.key = strdup(file);
917 if (!http_err->node.key) {
918 memprintf(errmsg, "out of memory.");
919 goto out;
920 }
921
922 /* Convert the error file into an HTX message */
923 if (!http_str_to_htx(&chk, ist2(err, errlen))) {
Christopher Faulet5031ef52020-01-15 11:22:07 +0100924 memprintf(errmsg, "unable to convert custom error message file '%s' in HTX.", file);
Christopher Faulet58857752020-01-15 15:19:50 +0100925 free(http_err->node.key);
926 free(http_err);
Christopher Faulet5031ef52020-01-15 11:22:07 +0100927 goto out;
928 }
929
Christopher Faulet58857752020-01-15 15:19:50 +0100930 /* Insert the node in the tree and return the HTX message */
931 http_err->msg = chk;
932 ebis_insert(&http_error_messages, &http_err->node);
933 buf = &http_err->msg;
934
Christopher Faulet5031ef52020-01-15 11:22:07 +0100935 out:
936 if (fd >= 0)
937 close(fd);
938 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +0100939 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +0100940}
941
Christopher Faulet58857752020-01-15 15:19:50 +0100942/* Convert the raw http message <msg> into an HTX message. On sucess, the HTX
943 * message is returned. On error, NULL is returned and an error message is
944 * written into the <errmsg> buffer.
Christopher Fauletbdf65262020-01-16 15:51:59 +0100945 */
Christopher Faulet58857752020-01-15 15:19:50 +0100946struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +0100947{
Christopher Faulet58857752020-01-15 15:19:50 +0100948 struct buffer *buf = NULL;
949 struct buffer chk;
950 struct ebpt_node *node;
951 struct http_error *http_err;
952
953 /* already loaded */
954 node = ebis_lookup_len(&http_error_messages, key, strlen(key));
955 if (node) {
956 http_err = container_of(node, typeof(*http_err), node);
957 buf = &http_err->msg;
958 goto out;
959 }
960 /* Create the node corresponding to the error file */
961 http_err = calloc(1, sizeof(*http_err));
962 if (!http_err) {
963 memprintf(errmsg, "out of memory.");
964 goto out;
965 }
966 http_err->node.key = strdup(key);
967 if (!http_err->node.key) {
968 memprintf(errmsg, "out of memory.");
969 goto out;
970 }
Christopher Fauletbdf65262020-01-16 15:51:59 +0100971
972 /* Convert the error file into an HTX message */
Christopher Faulet58857752020-01-15 15:19:50 +0100973 if (!http_str_to_htx(&chk, msg)) {
Christopher Fauletbdf65262020-01-16 15:51:59 +0100974 memprintf(errmsg, "unable to convert message in HTX.");
Christopher Faulet58857752020-01-15 15:19:50 +0100975 free(http_err->node.key);
976 free(http_err);
Christopher Fauletbdf65262020-01-16 15:51:59 +0100977 goto out;
978 }
Christopher Fauletbdf65262020-01-16 15:51:59 +0100979
Christopher Faulet58857752020-01-15 15:19:50 +0100980 /* Insert the node in the tree and return the HTX message */
981 http_err->msg = chk;
982 ebis_insert(&http_error_messages, &http_err->node);
983 buf = &http_err->msg;
Christopher Fauletbdf65262020-01-16 15:51:59 +0100984 out:
Christopher Faulet58857752020-01-15 15:19:50 +0100985 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +0100986}
987
Christopher Faulet5031ef52020-01-15 11:22:07 +0100988/* This function parses the raw HTTP error file <file> for the status code
Christopher Faulet58857752020-01-15 15:19:50 +0100989 * <status>. It returns NULL if there is any error, otherwise it return the
990 * corresponding HTX message.
Christopher Faulet5031ef52020-01-15 11:22:07 +0100991 */
Christopher Faulet58857752020-01-15 15:19:50 +0100992struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +0100993{
Christopher Faulet58857752020-01-15 15:19:50 +0100994 struct buffer *buf = NULL;
995 int rc;
Christopher Faulet5031ef52020-01-15 11:22:07 +0100996
997 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
998 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +0100999 buf = http_load_errorfile(file, errmsg);
Christopher Faulet5031ef52020-01-15 11:22:07 +01001000 break;
1001 }
1002 }
1003
1004 if (rc >= HTTP_ERR_SIZE)
1005 memprintf(errmsg, "status code '%d' not handled.", status);
Christopher Faulet58857752020-01-15 15:19:50 +01001006 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001007}
1008
1009/* This function creates HTX error message corresponding to a redirect message
1010 * for the status code <status>. <url> is used as location url for the
Christopher Faulet58857752020-01-15 15:19:50 +01001011 * redirect. <errloc> is used to know if it is a 302 or a 303 redirect. It
1012 * returns NULL if there is any error, otherwise it return the corresponding HTX
1013 * message.
Christopher Fauletbdf65262020-01-16 15:51:59 +01001014 */
Christopher Faulet58857752020-01-15 15:19:50 +01001015struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +01001016{
Christopher Faulet58857752020-01-15 15:19:50 +01001017 struct buffer *buf = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001018 const char *msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001019 char *key = NULL, *err = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001020 int rc, errlen;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001021
1022 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1023 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +01001024 /* Create the error key */
1025 if (!memprintf(&key, "errorloc%d %s", errloc, url)) {
1026 memprintf(errmsg, "out of memory.");
1027 goto out;
1028 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001029 /* Create the error message */
1030 msg = (errloc == 302 ? HTTP_302 : HTTP_303);
1031 errlen = strlen(msg) + strlen(url) + 5;
1032 err = malloc(errlen);
1033 if (!err) {
1034 memprintf(errmsg, "out of memory.");
1035 goto out;
1036 }
1037 errlen = snprintf(err, errlen, "%s%s\r\n\r\n", msg, url);
1038
1039 /* Load it */
Christopher Faulet58857752020-01-15 15:19:50 +01001040 buf = http_load_errormsg(key, ist2(err, errlen), errmsg);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001041 break;
1042 }
1043 }
1044
1045 if (rc >= HTTP_ERR_SIZE)
1046 memprintf(errmsg, "status code '%d' not handled.", status);
1047out:
Christopher Faulet58857752020-01-15 15:19:50 +01001048 free(key);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001049 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +01001050 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001051}
1052
Christopher Faulet07f41f72020-01-16 16:16:06 +01001053/* Parses the "errorloc[302|303]" proxy keyword */
1054static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
1055 struct proxy *defpx, const char *file, int line,
1056 char **errmsg)
1057{
1058 struct buffer *msg;
1059 int errloc, status, rc, ret = 0;
1060
1061 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1062 ret = 1;
1063 goto out;
1064 }
1065
1066 if (*(args[1]) == 0 || *(args[2]) == 0) {
1067 memprintf(errmsg, "%s : expects <status_code> and <url> as arguments.\n", args[0]);
1068 ret = -1;
1069 goto out;
1070 }
1071
1072 status = atol(args[1]);
1073 errloc = (!strcmp(args[0], "errorloc303") ? 303 : 302);
1074 msg = http_parse_errorloc(errloc, status, args[2], errmsg);
1075 if (!msg) {
1076 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1077 ret = -1;
1078 goto out;
1079 }
1080
1081 rc = http_get_status_idx(status);
1082 curpx->errmsg[rc] = msg;
1083
1084 out:
1085 return ret;
1086}
1087
1088
1089/* Parses the "errorfile" proxy keyword */
1090static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
1091 struct proxy *defpx, const char *file, int line,
1092 char **errmsg)
1093{
1094 struct buffer *msg;
1095 int status, rc, ret = 0;
1096
1097 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1098 ret = 1;
1099 goto out;
1100 }
1101
1102 if (*(args[1]) == 0 || *(args[2]) == 0) {
1103 memprintf(errmsg, "%s : expects <status_code> and <file> as arguments.\n", args[0]);
1104 ret = -1;
1105 goto out;
1106 }
1107
1108 status = atol(args[1]);
1109 msg = http_parse_errorfile(status, args[2], errmsg);
1110 if (!msg) {
1111 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1112 ret = -1;
1113 goto out;
1114 }
1115
1116 rc = http_get_status_idx(status);
1117 curpx->errmsg[rc] = msg;
1118
1119 out:
1120 return ret;
1121
1122}
1123
1124static struct cfg_kw_list cfg_kws = {ILH, {
1125 { CFG_LISTEN, "errorloc", proxy_parse_errorloc },
1126 { CFG_LISTEN, "errorloc302", proxy_parse_errorloc },
1127 { CFG_LISTEN, "errorloc303", proxy_parse_errorloc },
1128 { CFG_LISTEN, "errorfile", proxy_parse_errorfile },
1129 { 0, NULL, NULL },
1130}};
1131
1132INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
1133
Christopher Faulet29f72842019-12-11 15:52:32 +01001134/************************************************************************/
1135/* HTX sample fetches */
1136/************************************************************************/
1137
1138/* Returns 1 if a stream is an HTX stream. Otherwise, it returns 0. */
1139static int
1140smp_fetch_is_htx(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1141{
1142 if (!smp->strm)
1143 return 0;
1144
1145 smp->data.u.sint = !!IS_HTX_STRM(smp->strm);
1146 smp->data.type = SMP_T_BOOL;
1147 return 1;
1148}
1149
1150/* Returns the number of blocks in an HTX message. The channel is chosen
1151 * depending on the sample direction. */
1152static int
1153smp_fetch_htx_nbblks(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1154{
1155 struct channel *chn;
1156 struct htx *htx;
1157
1158 if (!smp->strm)
1159 return 0;
1160
1161 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1162 htx = smp_prefetch_htx(smp, chn, 0);
1163 if (!htx)
1164 return 0;
1165
1166 smp->data.u.sint = htx_nbblks(htx);
1167 smp->data.type = SMP_T_SINT;
1168 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1169 return 1;
1170}
1171
1172/* Returns the size of an HTX message. The channel is chosen depending on the
1173 * sample direction. */
1174static int
1175smp_fetch_htx_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1176{
1177 struct channel *chn;
1178 struct htx *htx;
1179
1180 if (!smp->strm)
1181 return 0;
1182
1183 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1184 htx = smp_prefetch_htx(smp, chn, 0);
1185 if (!htx)
1186 return 0;
1187
1188 smp->data.u.sint = htx->size;
1189 smp->data.type = SMP_T_SINT;
1190 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1191 return 1;
1192}
1193
1194/* Returns the data size of an HTX message. The channel is chosen depending on the
1195 * sample direction. */
1196static int
1197smp_fetch_htx_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1198{
1199 struct channel *chn;
1200 struct htx *htx;
1201
1202 if (!smp->strm)
1203 return 0;
1204
1205 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1206 htx = smp_prefetch_htx(smp, chn, 0);
1207 if (!htx)
1208 return 0;
1209
1210 smp->data.u.sint = htx->data;
1211 smp->data.type = SMP_T_SINT;
1212 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1213 return 1;
1214}
1215
1216/* Returns the used space (data+meta) of an HTX message. The channel is chosen
1217 * depending on the sample direction. */
1218static int
1219smp_fetch_htx_used(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1220{
1221 struct channel *chn;
1222 struct htx *htx;
1223
1224 if (!smp->strm)
1225 return 0;
1226
1227 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1228 htx = smp_prefetch_htx(smp, chn, 0);
1229 if (!htx)
1230 return 0;
1231
1232 smp->data.u.sint = htx_used_space(htx);
1233 smp->data.type = SMP_T_SINT;
1234 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1235 return 1;
1236}
1237
1238/* Returns the free space (size-used) of an HTX message. The channel is chosen
1239 * depending on the sample direction. */
1240static int
1241smp_fetch_htx_free(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1242{
1243 struct channel *chn;
1244 struct htx *htx;
1245
1246 if (!smp->strm)
1247 return 0;
1248
1249 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1250 htx = smp_prefetch_htx(smp, chn, 0);
1251 if (!htx)
1252 return 0;
1253
1254 smp->data.u.sint = htx_free_space(htx);
1255 smp->data.type = SMP_T_SINT;
1256 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1257 return 1;
1258}
1259
1260/* Returns the free space for data (free-sizeof(blk)) of an HTX message. The
1261 * channel is chosen depending on the sample direction. */
1262static int
1263smp_fetch_htx_free_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1264{
1265 struct channel *chn;
1266 struct htx *htx;
1267
1268 if (!smp->strm)
1269 return 0;
1270
1271 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1272 htx = smp_prefetch_htx(smp, chn, 0);
1273 if (!htx)
1274 return 0;
1275
1276 smp->data.u.sint = htx_free_data_space(htx);
1277 smp->data.type = SMP_T_SINT;
1278 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1279 return 1;
1280}
1281
1282/* Returns 1 if the HTX message contains an EOM block. Otherwise it returns
1283 * 0. Concretely, it only checks the tail. The channel is chosen depending on
1284 * the sample direction. */
1285static int
1286smp_fetch_htx_has_eom(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1287{
1288 struct channel *chn;
1289 struct htx *htx;
1290
1291 if (!smp->strm)
1292 return 0;
1293
1294 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1295 htx = smp_prefetch_htx(smp, chn, 0);
1296 if (!htx)
1297 return 0;
1298
1299 smp->data.u.sint = (htx_get_tail_type(htx) == HTX_BLK_EOM);
1300 smp->data.type = SMP_T_BOOL;
1301 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1302 return 1;
1303}
1304
1305/* Returns the type of a specific HTX block, if found in the message. Otherwise
1306 * HTX_BLK_UNUSED is returned. Any positive integer (>= 0) is supported or
1307 * "head", "tail" or "first". The channel is chosen depending on the sample
1308 * direction. */
1309static int
1310smp_fetch_htx_blk_type(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1311{
1312 struct channel *chn;
1313 struct htx *htx;
1314 enum htx_blk_type type;
1315 int32_t pos;
1316
1317 if (!smp->strm || !arg_p)
1318 return 0;
1319
1320 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1321 htx = smp_prefetch_htx(smp, chn, 0);
1322 if (!htx)
1323 return 0;
1324
1325 pos = arg_p[0].data.sint;
1326 if (pos == -1)
1327 type = htx_get_head_type(htx);
1328 else if (pos == -2)
1329 type = htx_get_tail_type(htx);
1330 else if (pos == -3)
1331 type = htx_get_first_type(htx);
1332 else
1333 type = ((pos >= htx->head && pos <= htx->tail)
1334 ? htx_get_blk_type(htx_get_blk(htx, pos))
1335 : HTX_BLK_UNUSED);
1336
1337 chunk_initstr(&smp->data.u.str, htx_blk_type_str(type));
1338 smp->data.type = SMP_T_STR;
1339 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1340 return 1;
1341}
1342
1343/* Returns the size of a specific HTX block, if found in the message. Otherwise
1344 * 0 is returned. Any positive integer (>= 0) is supported or "head", "tail" or
1345 * "first". The channel is chosen depending on the sample direction. */
1346static int
1347smp_fetch_htx_blk_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1348{
1349 struct channel *chn;
1350 struct htx *htx;
1351 struct htx_blk *blk;
1352 int32_t pos;
1353
1354 if (!smp->strm || !arg_p)
1355 return 0;
1356
1357 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1358 htx = smp_prefetch_htx(smp, chn, 0);
1359 if (!htx)
1360 return 0;
1361
1362 pos = arg_p[0].data.sint;
1363 if (pos == -1)
1364 blk = htx_get_head_blk(htx);
1365 else if (pos == -2)
1366 blk = htx_get_tail_blk(htx);
1367 else if (pos == -3)
1368 blk = htx_get_first_blk(htx);
1369 else
1370 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1371
1372 smp->data.u.sint = (blk ? htx_get_blksz(blk) : 0);
1373 smp->data.type = SMP_T_SINT;
1374 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1375 return 1;
1376}
1377
1378/* Returns the start-line if the selected HTX block exists and is a
1379 * start-line. Otherwise 0 an empty string. Any positive integer (>= 0) is
1380 * supported or "head", "tail" or "first". The channel is chosen depending on
1381 * the sample direction. */
1382static int
1383smp_fetch_htx_blk_stline(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1384{
1385 struct buffer *temp;
1386 struct channel *chn;
1387 struct htx *htx;
1388 struct htx_blk *blk;
1389 struct htx_sl *sl;
1390 int32_t pos;
1391
1392 if (!smp->strm || !arg_p)
1393 return 0;
1394
1395 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1396 htx = smp_prefetch_htx(smp, chn, 0);
1397 if (!htx)
1398 return 0;
1399
1400 pos = arg_p[0].data.sint;
1401 if (pos == -1)
1402 blk = htx_get_head_blk(htx);
1403 else if (pos == -2)
1404 blk = htx_get_tail_blk(htx);
1405 else if (pos == -3)
1406 blk = htx_get_first_blk(htx);
1407 else
1408 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1409
1410 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL)) {
1411 smp->data.u.str.size = 0;
1412 smp->data.u.str.area = "";
1413 smp->data.u.str.data = 0;
1414 }
1415 else {
1416 sl = htx_get_blk_ptr(htx, blk);
1417
1418 temp = get_trash_chunk();
1419 chunk_istcat(temp, htx_sl_p1(sl));
1420 temp->area[temp->data++] = ' ';
1421 chunk_istcat(temp, htx_sl_p2(sl));
1422 temp->area[temp->data++] = ' ';
1423 chunk_istcat(temp, htx_sl_p3(sl));
1424
1425 smp->data.u.str = *temp;
1426 }
1427
1428 smp->data.type = SMP_T_STR;
1429 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1430 return 1;
1431}
1432
1433/* Returns the header name if the selected HTX block exists and is a header or a
1434 * trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1435 * supported or "head", "tail" or "first". The channel is chosen depending on
1436 * the sample direction. */
1437static int
1438smp_fetch_htx_blk_hdrname(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1439{
1440 struct channel *chn;
1441 struct htx *htx;
1442 struct htx_blk *blk;
1443 int32_t pos;
1444
1445 if (!smp->strm || !arg_p)
1446 return 0;
1447
1448 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1449 htx = smp_prefetch_htx(smp, chn, 0);
1450 if (!htx)
1451 return 0;
1452
1453 pos = arg_p[0].data.sint;
1454 if (pos == -1)
1455 blk = htx_get_head_blk(htx);
1456 else if (pos == -2)
1457 blk = htx_get_tail_blk(htx);
1458 else if (pos == -3)
1459 blk = htx_get_first_blk(htx);
1460 else
1461 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1462
1463 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1464 smp->data.u.str.size = 0;
1465 smp->data.u.str.area = "";
1466 smp->data.u.str.data = 0;
1467 }
1468 else {
1469 struct ist name = htx_get_blk_name(htx, blk);
1470
1471 chunk_initlen(&smp->data.u.str, name.ptr, name.len, name.len);
1472 }
1473 smp->data.type = SMP_T_STR;
1474 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1475 return 1;
1476}
1477
1478/* Returns the header value if the selected HTX block exists and is a header or
1479 * a trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1480 * supported or "head", "tail" or "first". The channel is chosen depending on
1481 * the sample direction. */
1482static int
1483smp_fetch_htx_blk_hdrval(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1484{
1485 struct channel *chn;
1486 struct htx *htx;
1487 struct htx_blk *blk;
1488 int32_t pos;
1489
1490 if (!smp->strm || !arg_p)
1491 return 0;
1492
1493 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1494 htx = smp_prefetch_htx(smp, chn, 0);
1495 if (!htx)
1496 return 0;
1497
1498 pos = arg_p[0].data.sint;
1499 if (pos == -1)
1500 blk = htx_get_head_blk(htx);
1501 else if (pos == -2)
1502 blk = htx_get_tail_blk(htx);
1503 else if (pos == -3)
1504 blk = htx_get_first_blk(htx);
1505 else
1506 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1507
1508 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1509 smp->data.u.str.size = 0;
1510 smp->data.u.str.area = "";
1511 smp->data.u.str.data = 0;
1512 }
1513 else {
1514 struct ist val = htx_get_blk_value(htx, blk);
1515
1516 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
1517 }
1518 smp->data.type = SMP_T_STR;
1519 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1520 return 1;
1521}
1522
1523/* Returns the value if the selected HTX block exists and is a data
1524 * block. Otherwise 0 an empty string. Any positive integer (>= 0) is supported
1525 * or "head", "tail" or "first". The channel is chosen depending on the sample
1526 * direction. */
1527static int
Christopher Fauletc5db14c2020-01-08 14:51:03 +01001528smp_fetch_htx_blk_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
Christopher Faulet29f72842019-12-11 15:52:32 +01001529{
1530 struct channel *chn;
1531 struct htx *htx;
1532 struct htx_blk *blk;
1533 int32_t pos;
1534
1535 if (!smp->strm || !arg_p)
1536 return 0;
1537
1538 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
1539 htx = smp_prefetch_htx(smp, chn, 0);
1540 if (!htx)
1541 return 0;
1542
1543 pos = arg_p[0].data.sint;
1544 if (pos == -1)
1545 blk = htx_get_head_blk(htx);
1546 else if (pos == -2)
1547 blk = htx_get_tail_blk(htx);
1548 else if (pos == -3)
1549 blk = htx_get_first_blk(htx);
1550 else
1551 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1552
1553 if (!blk || htx_get_blk_type(blk) != HTX_BLK_DATA) {
1554 smp->data.u.str.size = 0;
1555 smp->data.u.str.area = "";
1556 smp->data.u.str.data = 0;
1557 }
1558 else {
1559 struct ist val = htx_get_blk_value(htx, blk);
1560
1561 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
1562 }
Christopher Faulet8178e402020-01-08 14:38:58 +01001563 smp->data.type = SMP_T_BIN;
Christopher Faulet29f72842019-12-11 15:52:32 +01001564 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1565 return 1;
1566}
1567
1568/* This function is used to validate the arguments passed to any "htx_blk" fetch
1569 * keywords. An argument is expected by these keywords. It must be a positive
1570 * integer or on of the following strings: "head", "tail" or "first". It returns
1571 * 0 on error, and a non-zero value if OK.
1572 */
1573int val_blk_arg(struct arg *arg, char **err_msg)
1574{
1575 if (arg[0].type != ARGT_STR || !arg[0].data.str.data) {
1576 memprintf(err_msg, "a block position is expected (> 0) or a special block name (head, tail, first)");
1577 return 0;
1578 }
1579 if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "head", 4)) {
1580 free(arg[0].data.str.area);
1581 arg[0].type = ARGT_SINT;
1582 arg[0].data.sint = -1;
1583 }
1584 else if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "tail", 4)) {
1585 free(arg[0].data.str.area);
1586 arg[0].type = ARGT_SINT;
1587 arg[0].data.sint = -2;
1588 }
1589 else if (arg[0].data.str.data == 5 && !strncmp(arg[0].data.str.area, "first", 5)) {
1590 free(arg[0].data.str.area);
1591 arg[0].type = ARGT_SINT;
1592 arg[0].data.sint = -3;
1593 }
1594 else {
1595 int pos;
1596
1597 for (pos = 0; pos < arg[0].data.str.data; pos++) {
1598 if (!isdigit(arg[0].data.str.area[pos])) {
1599 memprintf(err_msg, "invalid block position");
1600 return 0;
1601 }
1602 }
1603
1604 pos = strl2uic(arg[0].data.str.area, arg[0].data.str.data);
1605 if (pos < 0) {
1606 memprintf(err_msg, "block position must not be negative");
1607 return 0;
1608 }
1609 free(arg[0].data.str.area);
1610 arg[0].type = ARGT_SINT;
1611 arg[0].data.sint = pos;
1612 }
1613
1614 return 1;
1615}
1616
1617
1618/* Note: must not be declared <const> as its list will be overwritten.
1619 * Note: htx sample fetches should only used for developpement purpose.
1620 */
1621static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet01f44452020-01-08 14:23:40 +01001622 { "internal.strm.is_htx", smp_fetch_is_htx, 0, NULL, SMP_T_BOOL, SMP_USE_L6REQ },
Christopher Faulet29f72842019-12-11 15:52:32 +01001623
Christopher Faulet01f44452020-01-08 14:23:40 +01001624 { "internal.htx.nbblks", smp_fetch_htx_nbblks, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1625 { "internal.htx.size", smp_fetch_htx_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1626 { "internal.htx.data", smp_fetch_htx_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1627 { "internal.htx.used", smp_fetch_htx_used, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1628 { "internal.htx.free", smp_fetch_htx_free, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1629 { "internal.htx.free_data", smp_fetch_htx_free_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1630 { "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 +01001631
Christopher Faulet01f44452020-01-08 14:23:40 +01001632 { "internal.htx_blk.type", smp_fetch_htx_blk_type, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
1633 { "internal.htx_blk.size", smp_fetch_htx_blk_size, ARG1(1,STR), val_blk_arg, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
1634 { "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},
1635 { "internal.htx_blk.hdrname", smp_fetch_htx_blk_hdrname, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
1636 { "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 +01001637 { "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 +01001638
1639 { /* END */ },
1640}};
1641
1642INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);