blob: 4e8ca0b9604852ed55a9502c3db8660130ae852c [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 Faulet35cd81d2020-01-15 11:22:56 +010033struct list http_errors_list = LIST_HEAD_INIT(http_errors_list);
Christopher Fauleta7b677c2018-11-29 16:48:49 +010034
Christopher Faulet76edc0f2020-01-13 15:52:01 +010035/* The declaration of an errorfiles/errorfile directives. Used during config
36 * parsing only. */
37struct conf_errors {
38 char type; /* directive type (0: errorfiles, 1: errorfile) */
39 union {
40 struct {
41 int status; /* the status code associated to this error */
42 struct buffer *msg; /* the HTX error message */
43 } errorfile; /* describe an "errorfile" directive */
44 struct {
45 char *name; /* the http-errors section name */
46 char status[HTTP_ERR_SIZE]; /* list of status to import (0: ignore, 1: implicit import, 2: explicit import) */
47 } errorfiles; /* describe an "errorfiles" directive */
48 } info;
49
50 char *file; /* file where the directive appears */
51 int line; /* line where the directive appears */
52
53 struct list list; /* next conf_errors */
54};
55
Christopher Faulet297fbb42019-05-13 14:41:27 +020056/* Returns the next unporocessed start line in the HTX message. It returns NULL
Christopher Faulet29f17582019-05-23 11:03:26 +020057 * if the start-line is undefined (first == -1). Otherwise, it returns the
Christopher Faulet297fbb42019-05-13 14:41:27 +020058 * pointer on the htx_sl structure.
Christopher Faulet47596d32018-10-22 09:17:28 +020059 */
Christopher Faulet297fbb42019-05-13 14:41:27 +020060struct htx_sl *http_get_stline(struct htx *htx)
Christopher Faulet47596d32018-10-22 09:17:28 +020061{
Christopher Faulet297fbb42019-05-13 14:41:27 +020062 struct htx_blk *blk;
Christopher Faulet573fe732018-11-28 16:55:12 +010063
Christopher Faulet29f17582019-05-23 11:03:26 +020064 BUG_ON(htx->first == -1);
65 blk = htx_get_first_blk(htx);
Christopher Faulet297fbb42019-05-13 14:41:27 +020066 if (!blk)
67 return NULL;
Christopher Faulet29f17582019-05-23 11:03:26 +020068 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 +020069 return htx_get_blk_ptr(htx, blk);
Christopher Faulet47596d32018-10-22 09:17:28 +020070}
71
Christopher Faulet727a3f12020-02-07 16:39:41 +010072/* Returns the headers size in the HTX message */
73size_t http_get_hdrs_size(struct htx *htx)
74{
75 struct htx_blk *blk;
76 size_t sz = 0;
77
78 blk = htx_get_first_blk(htx);
79 if (!blk || htx_get_blk_type(blk) > HTX_BLK_EOH)
80 return sz;
81
82 for (; blk; blk = htx_get_next_blk(htx, blk)) {
83 sz += htx_get_blksz(blk);
84 if (htx_get_blk_type(blk) == HTX_BLK_EOH)
85 break;
86 }
87 return sz;
88}
89
Christopher Faulet8dd33e12020-05-05 07:42:42 +020090/* Finds the first or next occurrence of header matching <pattern> in the HTX
91 * message <htx> using the context <ctx>. This structure holds everything
92 * necessary to use the header and find next occurrence. If its <blk> member is
93 * NULL, the header is searched from the beginning. Otherwise, the next
94 * occurrence is returned. The function returns 1 when it finds a value, and 0
95 * when there is no more. It is designed to work with headers defined as
96 * comma-separated lists. If HTTP_FIND_FL_FULL flag is set, it works on
97 * full-line headers in whose comma is not a delimiter but is part of the
98 * syntax. A special case, if ctx->value is NULL when searching for a new values
99 * of a header, the current header is rescanned. This allows rescanning after a
100 * header deletion.
101 *
102 * The matching method is chosen by checking the flags :
103 *
104 * * HTTP_FIND_FL_MATCH_REG : <pattern> is a regex. header names matching
105 * the regex are evaluated.
106 * * HTTP_FIND_FL_MATCH_STR : <pattern> is a string. The header names equal
107 * to the string are evaluated.
108 * * HTTP_FIND_FL_MATCH_PFX : <pattern> is a string. The header names
109 * starting by the string are evaluated.
110 * * HTTP_FIND_FL_MATCH_SFX : <pattern> is a string. The header names
111 * ending by the string are evaluated.
112 * * HTTP_FIND_FL_MATCH_SUB : <pattern> is a string. The header names
113 * containing the string are evaluated.
Christopher Faulet47596d32018-10-22 09:17:28 +0200114 */
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200115
116#define HTTP_FIND_FL_MATCH_STR 0x0001
117#define HTTP_FIND_FL_MATCH_PFX 0x0002
118#define HTTP_FIND_FL_MATCH_SFX 0x0003
119#define HTTP_FIND_FL_MATCH_SUB 0x0004
120#define HTTP_FIND_FL_MATCH_REG 0x0005
121/* 0x0006..0x000f: for other matching methods */
122#define HTTP_FIND_FL_MATCH_TYPE 0x000F
123#define HTTP_FIND_FL_FULL 0x0010
124
125static int __http_find_header(const struct htx *htx, const void *pattern, struct http_hdr_ctx *ctx, int flags)
Christopher Faulet47596d32018-10-22 09:17:28 +0200126{
127 struct htx_blk *blk = ctx->blk;
128 struct ist n, v;
129 enum htx_blk_type type;
Christopher Faulet47596d32018-10-22 09:17:28 +0200130
131 if (blk) {
132 char *p;
133
Tim Duesterhused526372020-03-05 17:56:33 +0100134 if (!isttest(ctx->value))
Christopher Faulet47596d32018-10-22 09:17:28 +0200135 goto rescan_hdr;
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200136 if (flags & HTTP_FIND_FL_FULL)
Christopher Faulet47596d32018-10-22 09:17:28 +0200137 goto next_blk;
138 v = htx_get_blk_value(htx, blk);
139 p = ctx->value.ptr + ctx->value.len + ctx->lws_after;
140 v.len -= (p - v.ptr);
141 v.ptr = p;
142 if (!v.len)
143 goto next_blk;
144 /* Skip comma */
145 if (*(v.ptr) == ',') {
146 v.ptr++;
147 v.len--;
148 }
149
150 goto return_hdr;
151 }
152
Christopher Faulet192c6a22019-06-11 16:32:24 +0200153 if (htx_is_empty(htx))
Christopher Faulet47596d32018-10-22 09:17:28 +0200154 return 0;
155
Christopher Fauleta3f15502019-05-13 15:27:23 +0200156 for (blk = htx_get_first_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200157 rescan_hdr:
Christopher Faulet47596d32018-10-22 09:17:28 +0200158 type = htx_get_blk_type(blk);
Christopher Faulet573fe732018-11-28 16:55:12 +0100159 if (type == HTX_BLK_EOH || type == HTX_BLK_EOM)
160 break;
Christopher Faulet47596d32018-10-22 09:17:28 +0200161 if (type != HTX_BLK_HDR)
Christopher Faulet28f29c72019-04-30 17:55:45 +0200162 continue;
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200163
164 if ((flags & HTTP_FIND_FL_MATCH_TYPE) == HTTP_FIND_FL_MATCH_REG) {
165 const struct my_regex *re = pattern;
166
167 n = htx_get_blk_name(htx, blk);
168 if (!regex_exec2(re, n.ptr, n.len))
169 goto next_blk;
170 }
171 else {
172 const struct ist name = *(const struct ist *)(pattern);
173
Christopher Faulet47596d32018-10-22 09:17:28 +0200174 /* If no name was passed, we want any header. So skip the comparison */
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200175 if (!istlen(name))
176 goto match;
177
Christopher Faulet47596d32018-10-22 09:17:28 +0200178 n = htx_get_blk_name(htx, blk);
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200179 switch (flags & HTTP_FIND_FL_MATCH_TYPE) {
180 case HTTP_FIND_FL_MATCH_STR:
181 if (!isteqi(n, name))
182 goto next_blk;
183 break;
184 case HTTP_FIND_FL_MATCH_PFX:
185 if (istlen(n) < istlen(name))
186 goto next_blk;
187
188 n = ist2(istptr(n), istlen(name));
189 if (!isteqi(n, name))
190 goto next_blk;
191 break;
192 case HTTP_FIND_FL_MATCH_SFX:
193 if (istlen(n) < istlen(name))
194 goto next_blk;
195
196 n = ist2(istptr(n) + istlen(n) - istlen(name), istlen(name));
197 if (!isteqi(n, name))
198 goto next_blk;
199 break;
200 case HTTP_FIND_FL_MATCH_SUB:
201 if (strnistr(n.ptr, n.len, name.ptr, n.len) != NULL)
202 goto next_blk;
203 break;
204 default:
Christopher Faulet47596d32018-10-22 09:17:28 +0200205 goto next_blk;
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200206 break;
207 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200208 }
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200209 match:
Christopher Faulet47596d32018-10-22 09:17:28 +0200210 v = htx_get_blk_value(htx, blk);
211
212 return_hdr:
213 ctx->lws_before = 0;
214 ctx->lws_after = 0;
215 while (v.len && HTTP_IS_LWS(*v.ptr)) {
216 v.ptr++;
217 v.len--;
218 ctx->lws_before++;
219 }
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200220 if (!(flags & HTTP_FIND_FL_FULL))
Christopher Faulet47596d32018-10-22 09:17:28 +0200221 v.len = http_find_hdr_value_end(v.ptr, v.ptr + v.len) - v.ptr;
222 while (v.len && HTTP_IS_LWS(*(v.ptr + v.len - 1))) {
223 v.len--;
224 ctx->lws_after++;
225 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200226 ctx->blk = blk;
227 ctx->value = v;
228 return 1;
229
230 next_blk:
Christopher Faulet28f29c72019-04-30 17:55:45 +0200231 ;
Christopher Faulet47596d32018-10-22 09:17:28 +0200232 }
233
234 ctx->blk = NULL;
235 ctx->value = ist("");
236 ctx->lws_before = ctx->lws_after = 0;
237 return 0;
238}
239
Christopher Faulet8dd33e12020-05-05 07:42:42 +0200240
241/* Header names must match <name> */
242int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full)
243{
244 return __http_find_header(htx, &name, ctx, HTTP_FIND_FL_MATCH_STR | (full ? HTTP_FIND_FL_FULL : 0));
245}
246
247/* Header names must match <name>. Same than http_find_header */
248int http_find_str_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full)
249{
250 return __http_find_header(htx, &name, ctx, HTTP_FIND_FL_MATCH_STR | (full ? HTTP_FIND_FL_FULL : 0));
251}
252
253
254/* Header names must start with <prefix> */
255int http_find_pfx_header(const struct htx *htx, const struct ist prefix, struct http_hdr_ctx *ctx, int full)
256{
257 return __http_find_header(htx, &prefix, ctx, HTTP_FIND_FL_MATCH_PFX | (full ? HTTP_FIND_FL_FULL : 0));
258}
259
260/* Header names must end with <suffix> */
261int http_find_sfx_header(const struct htx *htx, const struct ist suffix, struct http_hdr_ctx *ctx, int full)
262{
263 return __http_find_header(htx, &suffix, ctx, HTTP_FIND_FL_MATCH_SFX | (full ? HTTP_FIND_FL_FULL : 0));
264}
265/* Header names must contain <sub> */
266int http_find_sub_header(const struct htx *htx, const struct ist sub, struct http_hdr_ctx *ctx, int full)
267{
268 return __http_find_header(htx, &sub, ctx, HTTP_FIND_FL_MATCH_SUB | (full ? HTTP_FIND_FL_FULL : 0));
269}
270
271/* Header names must match <re> regex*/
272int http_match_header(const struct htx *htx, const struct my_regex *re, struct http_hdr_ctx *ctx, int full)
273{
274 return __http_find_header(htx, re, ctx, HTTP_FIND_FL_MATCH_REG | (full ? HTTP_FIND_FL_FULL : 0));
275}
276
277
Christopher Faulet47596d32018-10-22 09:17:28 +0200278/* Adds a header block int the HTX message <htx>, just before the EOH block. It
279 * returns 1 on success, otherwise it returns 0.
280 */
281int http_add_header(struct htx *htx, const struct ist n, const struct ist v)
282{
283 struct htx_blk *blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200284 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200285 enum htx_blk_type type = htx_get_tail_type(htx);
286 int32_t prev;
287
288 blk = htx_add_header(htx, n, v);
289 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200290 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200291
292 if (unlikely(type < HTX_BLK_EOH))
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200293 goto end;
Christopher Faulet47596d32018-10-22 09:17:28 +0200294
295 /* <blk> is the head, swap it iteratively with its predecessor to place
296 * it just before the end-of-header block. So blocks remains ordered. */
Christopher Faulet29f17582019-05-23 11:03:26 +0200297 for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200298 struct htx_blk *pblk = htx_get_blk(htx, prev);
299 enum htx_blk_type type = htx_get_blk_type(pblk);
300
301 /* Swap .addr and .info fields */
302 blk->addr ^= pblk->addr; pblk->addr ^= blk->addr; blk->addr ^= pblk->addr;
303 blk->info ^= pblk->info; pblk->info ^= blk->info; blk->info ^= pblk->info;
304
305 if (blk->addr == pblk->addr)
306 blk->addr += htx_get_blksz(pblk);
Christopher Faulet47596d32018-10-22 09:17:28 +0200307
308 /* Stop when end-of-header is reached */
309 if (type == HTX_BLK_EOH)
310 break;
311
312 blk = pblk;
313 }
Christopher Faulet05aab642019-04-11 13:43:57 +0200314
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200315 end:
316 sl = http_get_stline(htx);
Christopher Faulet3e1f7f42020-02-28 09:47:07 +0100317 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(n, ist("host"))) {
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200318 if (!http_update_authority(htx, sl, v))
319 goto fail;
320 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200321 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200322
323 fail:
324 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200325}
326
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100327/* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
Christopher Faulet29f17582019-05-23 11:03:26 +0200328 * success, otherwise it returns 0.
Christopher Faulet47596d32018-10-22 09:17:28 +0200329 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100330int 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 +0200331{
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200332 struct htx_blk *blk;
Christopher Faulet47596d32018-10-22 09:17:28 +0200333
Christopher Faulet29f17582019-05-23 11:03:26 +0200334 blk = htx_get_first_blk(htx);
335 if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
Christopher Faulet7b7d5072019-05-13 15:22:59 +0200336 return 0;
337 return 1;
Christopher Faulet47596d32018-10-22 09:17:28 +0200338}
339
Christopher Faulete010c802018-10-24 10:36:45 +0200340/* Replace the request method in the HTX message <htx> by <meth>. It returns 1
341 * on success, otherwise 0.
342 */
343int http_replace_req_meth(struct htx *htx, const struct ist meth)
344{
345 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200346 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100347 struct ist uri, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200348
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100349 if (!sl)
350 return 0;
351
Christopher Faulete010c802018-10-24 10:36:45 +0200352 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100353 chunk_memcat(temp, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl)); /* uri */
354 uri = ist2(temp->area, HTX_SL_REQ_ULEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200355
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100356 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
357 vsn = ist2(temp->area + uri.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200358
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100359 /* create the new start line */
360 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
361 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200362}
363
364/* Replace the request uri in the HTX message <htx> by <uri>. It returns 1 on
365 * success, otherwise 0.
366 */
367int http_replace_req_uri(struct htx *htx, const struct ist uri)
368{
369 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200370 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100371 struct ist meth, vsn;
Christopher Faulete010c802018-10-24 10:36:45 +0200372
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100373 if (!sl)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200374 goto fail;
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100375
Christopher Faulete010c802018-10-24 10:36:45 +0200376 /* Start by copying old method and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100377 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
378 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200379
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100380 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
381 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200382
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100383 /* create the new start line */
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200384 if (!http_replace_stline(htx, meth, uri, vsn))
385 goto fail;
386
387 sl = http_get_stline(htx);
388 if (!http_update_host(htx, sl, uri))
389 goto fail;
390
391 return 1;
392 fail:
393 return 0;
Christopher Faulete010c802018-10-24 10:36:45 +0200394}
395
396/* Replace the request path in the HTX message <htx> by <path>. The host part
397 * and the query string are preserved. It returns 1 on success, otherwise 0.
398 */
399int http_replace_req_path(struct htx *htx, const struct ist path)
400{
401 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200402 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100403 struct ist meth, uri, vsn, p;
Christopher Faulete010c802018-10-24 10:36:45 +0200404 size_t plen = 0;
405
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100406 if (!sl)
407 return 0;
408
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100409 uri = htx_sl_req_uri(sl);
410 p = http_get_path(uri);
Tim Duesterhused526372020-03-05 17:56:33 +0100411 if (!isttest(p))
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100412 p = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200413 while (plen < p.len && *(p.ptr + plen) != '?')
414 plen++;
415
416 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100417 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
418 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200419
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100420 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
421 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
422
423 chunk_memcat(temp, uri.ptr, p.ptr - uri.ptr); /* uri: host part */
Christopher Faulete010c802018-10-24 10:36:45 +0200424 chunk_memcat(temp, path.ptr, path.len); /* uri: new path */
425 chunk_memcat(temp, p.ptr + plen, p.len - plen); /* uri: QS part */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100426 uri = ist2(temp->area + meth.len + vsn.len, uri.len - plen + path.len);
Christopher Faulete010c802018-10-24 10:36:45 +0200427
428 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100429 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200430}
431
432/* Replace the request query-string in the HTX message <htx> by <query>. The
433 * host part and the path are preserved. It returns 1 on success, otherwise
434 * 0.
435 */
436int http_replace_req_query(struct htx *htx, const struct ist query)
437{
438 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200439 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100440 struct ist meth, uri, vsn, q;
Christopher Faulete010c802018-10-24 10:36:45 +0200441 int offset = 1;
442
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100443 if (!sl)
444 return 0;
445
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100446 uri = htx_sl_req_uri(sl);
447 q = uri;
Christopher Faulete010c802018-10-24 10:36:45 +0200448 while (q.len > 0 && *(q.ptr) != '?') {
449 q.ptr++;
450 q.len--;
451 }
452
453 /* skip the question mark or indicate that we must insert it
454 * (but only if the format string is not empty then).
455 */
456 if (q.len) {
457 q.ptr++;
458 q.len--;
459 }
460 else if (query.len > 1)
461 offset = 0;
462
463 /* Start by copying old method and version and create the new uri */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100464 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
465 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200466
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100467 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
468 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200469
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100470 chunk_memcat(temp, uri.ptr, q.ptr - uri.ptr); /* uri: host + path part */
471 chunk_memcat(temp, query.ptr + offset, query.len - offset); /* uri: new QS */
472 uri = ist2(temp->area + meth.len + vsn.len, uri.len - q.len + query.len - offset);
Christopher Faulete010c802018-10-24 10:36:45 +0200473
474 /* create the new start line */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100475 return http_replace_stline(htx, meth, uri, vsn);
Christopher Faulete010c802018-10-24 10:36:45 +0200476}
477
478/* Replace the response status in the HTX message <htx> by <status>. It returns
479 * 1 on success, otherwise 0.
480*/
481int http_replace_res_status(struct htx *htx, const struct ist status)
482{
483 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200484 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100485 struct ist vsn, reason;
Christopher Faulete010c802018-10-24 10:36:45 +0200486
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100487 if (!sl)
488 return 0;
489
Christopher Faulete010c802018-10-24 10:36:45 +0200490 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100491 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
492 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200493
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100494 chunk_memcat(temp, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)); /* reason */
495 reason = ist2(temp->area + vsn.len, HTX_SL_RES_RLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200496
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100497 /* create the new start line */
498 sl->info.res.status = strl2ui(status.ptr, status.len);
499 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200500}
501
502/* Replace the response reason in the HTX message <htx> by <reason>. It returns
503 * 1 on success, otherwise 0.
504*/
505int http_replace_res_reason(struct htx *htx, const struct ist reason)
506{
507 struct buffer *temp = get_trash_chunk();
Christopher Faulet297fbb42019-05-13 14:41:27 +0200508 struct htx_sl *sl = http_get_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100509 struct ist vsn, status;
Christopher Faulete010c802018-10-24 10:36:45 +0200510
Willy Tarreaucdce54c2019-02-12 12:02:27 +0100511 if (!sl)
512 return 0;
513
Christopher Faulete010c802018-10-24 10:36:45 +0200514 /* Start by copying old uri and version */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100515 chunk_memcat(temp, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)); /* vsn */
516 vsn = ist2(temp->area, HTX_SL_RES_VLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200517
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100518 chunk_memcat(temp, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)); /* code */
519 status = ist2(temp->area + vsn.len, HTX_SL_RES_CLEN(sl));
Christopher Faulete010c802018-10-24 10:36:45 +0200520
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100521 /* create the new start line */
522 return http_replace_stline(htx, vsn, status, reason);
Christopher Faulete010c802018-10-24 10:36:45 +0200523}
524
Christopher Faulet47596d32018-10-22 09:17:28 +0200525/* Replaces a part of a header value referenced in the context <ctx> by
526 * <data>. It returns 1 on success, otherwise it returns 0. The context is
527 * updated if necessary.
528 */
529int http_replace_header_value(struct htx *htx, struct http_hdr_ctx *ctx, const struct ist data)
530{
531 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200532 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200533 char *start;
534 struct ist v;
535 uint32_t len, off;
536
537 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200538 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200539
540 v = htx_get_blk_value(htx, blk);
541 start = ctx->value.ptr - ctx->lws_before;
542 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
543 off = start - v.ptr;
544
545 blk = htx_replace_blk_value(htx, blk, ist2(start, len), data);
546 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200547 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200548
549 v = htx_get_blk_value(htx, blk);
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200550
551 sl = http_get_stline(htx);
552 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY)) {
553 struct ist n = htx_get_blk_name(htx, blk);
554
555 if (isteq(n, ist("host"))) {
556 if (!http_update_authority(htx, sl, v))
557 goto fail;
558 ctx->blk = NULL;
559 http_find_header(htx, ist("host"), ctx, 1);
560 blk = ctx->blk;
561 v = htx_get_blk_value(htx, blk);
562 }
563 }
564
Christopher Faulet47596d32018-10-22 09:17:28 +0200565 ctx->blk = blk;
566 ctx->value.ptr = v.ptr + off;
567 ctx->value.len = data.len;
568 ctx->lws_before = ctx->lws_after = 0;
569
570 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200571 fail:
572 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200573}
574
575/* Fully replaces a header referenced in the context <ctx> by the name <name>
576 * with the value <value>. It returns 1 on success, otherwise it returns 0. The
577 * context is updated if necessary.
578 */
579int http_replace_header(struct htx *htx, struct http_hdr_ctx *ctx,
580 const struct ist name, const struct ist value)
581{
582 struct htx_blk *blk = ctx->blk;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200583 struct htx_sl *sl;
Christopher Faulet47596d32018-10-22 09:17:28 +0200584
585 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200586 goto fail;
Christopher Faulet47596d32018-10-22 09:17:28 +0200587
588 blk = htx_replace_header(htx, blk, name, value);
589 if (!blk)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200590 goto fail;
591
592 sl = http_get_stline(htx);
Christopher Faulet3e1f7f42020-02-28 09:47:07 +0100593 if (sl && (sl->flags & HTX_SL_F_HAS_AUTHORITY) && isteqi(name, ist("host"))) {
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200594 if (!http_update_authority(htx, sl, value))
595 goto fail;
596 ctx->blk = NULL;
597 http_find_header(htx, ist("host"), ctx, 1);
598 blk = ctx->blk;
599 }
Christopher Faulet47596d32018-10-22 09:17:28 +0200600
601 ctx->blk = blk;
602 ctx->value = ist(NULL);
603 ctx->lws_before = ctx->lws_after = 0;
604
605 return 1;
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200606 fail:
607 return 0;
Christopher Faulet47596d32018-10-22 09:17:28 +0200608}
609
610/* Remove one value of a header. This only works on a <ctx> returned by
611 * http_find_header function. The value is removed, as well as surrounding commas
612 * if any. If the removed value was alone, the whole header is removed. The
613 * <ctx> is always updated accordingly, as well as the HTX message <htx>. It
614 * returns 1 on success. Otherwise, it returns 0. The <ctx> is always left in a
615 * form that can be handled by http_find_header() to find next occurrence.
616 */
617int http_remove_header(struct htx *htx, struct http_hdr_ctx *ctx)
618{
619 struct htx_blk *blk = ctx->blk;
620 char *start;
621 struct ist v;
622 uint32_t len;
623
624 if (!blk)
625 return 0;
626
627 start = ctx->value.ptr - ctx->lws_before;
628 len = ctx->lws_before + ctx->value.len + ctx->lws_after;
629
630 v = htx_get_blk_value(htx, blk);
631 if (len == v.len) {
632 blk = htx_remove_blk(htx, blk);
Christopher Faulet192c6a22019-06-11 16:32:24 +0200633 if (blk || htx_is_empty(htx)) {
Christopher Faulet47596d32018-10-22 09:17:28 +0200634 ctx->blk = blk;
Tim Duesterhus241e29e2020-03-05 17:56:30 +0100635 ctx->value = IST_NULL;
Christopher Faulet47596d32018-10-22 09:17:28 +0200636 ctx->lws_before = ctx->lws_after = 0;
637 }
638 else {
639 ctx->blk = htx_get_blk(htx, htx->tail);
640 ctx->value = htx_get_blk_value(htx, ctx->blk);
641 ctx->lws_before = ctx->lws_after = 0;
642 }
643 return 1;
644 }
645
646 /* This was not the only value of this header. We have to remove the
647 * part pointed by ctx->value. If it is the last entry of the list, we
648 * remove the last separator.
649 */
650 if (start == v.ptr) {
651 /* It's the first header part but not the only one. So remove
652 * the comma after it. */
653 len++;
654 }
655 else {
656 /* There is at least one header part before the removed one. So
657 * remove the comma between them. */
658 start--;
659 len++;
660 }
661 /* Update the block content and its len */
662 memmove(start, start+len, v.len-len);
Christopher Faulet3e2638e2019-06-18 09:49:16 +0200663 htx_change_blk_value_len(htx, blk, v.len-len);
Christopher Faulet47596d32018-10-22 09:17:28 +0200664
665 /* Finally update the ctx */
666 ctx->value.ptr = start;
667 ctx->value.len = 0;
668 ctx->lws_before = ctx->lws_after = 0;
669
670 return 1;
671}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200672
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200673/* Updates the authority part of the uri with the value <host>. It happens when
674 * the header host is modified. It returns 0 on failure and 1 on success. It is
675 * the caller responsibility to provide the start-line and to be sure the uri
676 * contains an authority. Thus, if no authority is found in the uri, an error is
677 * returned.
678 */
Christopher Faulet1543d442020-04-28 19:57:29 +0200679int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200680{
681 struct buffer *temp = get_trash_chunk();
682 struct ist meth, vsn, uri, authority;
683
684 uri = htx_sl_req_uri(sl);
685 authority = http_get_authority(uri, 1);
Christopher Faulet34b18e42020-02-18 11:02:21 +0100686 if (!authority.len)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200687 return 0;
688
Christopher Faulet34b18e42020-02-18 11:02:21 +0100689 /* Don't update the uri if there is no change */
690 if (isteq(host, authority))
691 return 1;
692
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200693 /* Start by copying old method and version */
694 chunk_memcat(temp, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)); /* meth */
695 meth = ist2(temp->area, HTX_SL_REQ_MLEN(sl));
696
697 chunk_memcat(temp, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)); /* vsn */
698 vsn = ist2(temp->area + meth.len, HTX_SL_REQ_VLEN(sl));
699
700 chunk_memcat(temp, uri.ptr, authority.ptr - uri.ptr);
701 chunk_memcat(temp, host.ptr, host.len);
702 chunk_memcat(temp, authority.ptr + authority.len, uri.ptr + uri.len - (authority.ptr + authority.len));
703 uri = ist2(temp->area + meth.len + vsn.len, host.len + uri.len - authority.len); /* uri */
704
705 return http_replace_stline(htx, meth, uri, vsn);
706
707}
708
709/* Update the header host by extracting the authority of the uri <uri>. flags of
710 * the start-line are also updated accordingly. For orgin-form and asterisk-form
711 * uri, the header host is not changed and the flag HTX_SL_F_HAS_AUTHORITY is
712 * removed from the flags of the start-line. Otherwise, this flag is set and the
713 * authority is used to set the value of the header host. This function returns
714 * 0 on failure and 1 on success.
715*/
Christopher Faulet1543d442020-04-28 19:57:29 +0200716int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri)
Christopher Fauletd7b7a1c2019-10-08 15:24:52 +0200717{
718 struct ist authority;
719 struct http_hdr_ctx ctx;
720
721 if (!uri.len || uri.ptr[0] == '/' || uri.ptr[0] == '*') {
722 // origin-form or a asterisk-form (RFC7320 #5.3.1 and #5.3.4)
723 sl->flags &= ~HTX_SL_F_HAS_AUTHORITY;
724 }
725 else {
726 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
727 if (sl->info.req.meth != HTTP_METH_CONNECT) {
728 // absolute-form (RFC7320 #5.3.2)
729 sl->flags |= HTX_SL_F_HAS_SCHM;
730 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
731 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
732
733 authority = http_get_authority(uri, 1);
734 if (!authority.len)
735 goto fail;
736 }
737 else {
738 // authority-form (RFC7320 #5.3.3)
739 authority = uri;
740 }
741
742 /* Replace header host value */
743 ctx.blk = NULL;
744 while (http_find_header(htx, ist("host"), &ctx, 1)) {
745 if (!http_replace_header_value(htx, &ctx, authority))
746 goto fail;
747 }
748
749 }
750 return 1;
751 fail:
752 return 0;
753}
Christopher Faulet7ff1cea2018-10-24 10:39:35 +0200754
755/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
756 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
757 * performed over the whole headers. Otherwise it must contain a valid header
758 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
759 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
760 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
761 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
762 * -1. The value fetch stops at commas, so this function is suited for use with
763 * list headers.
764 * The return value is 0 if nothing was found, or non-zero otherwise.
765 */
766unsigned int http_get_htx_hdr(const struct htx *htx, const struct ist hdr,
767 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
768{
769 struct http_hdr_ctx local_ctx;
770 struct ist val_hist[MAX_HDR_HISTORY];
771 unsigned int hist_idx;
772 int found;
773
774 if (!ctx) {
775 local_ctx.blk = NULL;
776 ctx = &local_ctx;
777 }
778
779 if (occ >= 0) {
780 /* search from the beginning */
781 while (http_find_header(htx, hdr, ctx, 0)) {
782 occ--;
783 if (occ <= 0) {
784 *vptr = ctx->value.ptr;
785 *vlen = ctx->value.len;
786 return 1;
787 }
788 }
789 return 0;
790 }
791
792 /* negative occurrence, we scan all the list then walk back */
793 if (-occ > MAX_HDR_HISTORY)
794 return 0;
795
796 found = hist_idx = 0;
797 while (http_find_header(htx, hdr, ctx, 0)) {
798 val_hist[hist_idx] = ctx->value;
799 if (++hist_idx >= MAX_HDR_HISTORY)
800 hist_idx = 0;
801 found++;
802 }
803 if (-occ > found)
804 return 0;
805
806 /* OK now we have the last occurrence in [hist_idx-1], and we need to
807 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
808 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
809 * to remain in the 0..9 range.
810 */
811 hist_idx += occ + MAX_HDR_HISTORY;
812 if (hist_idx >= MAX_HDR_HISTORY)
813 hist_idx -= MAX_HDR_HISTORY;
814 *vptr = val_hist[hist_idx].ptr;
815 *vlen = val_hist[hist_idx].len;
816 return 1;
817}
818
819/* Return in <vptr> and <vlen> the pointer and length of occurrence <occ> of
820 * header whose name is <hname> of length <hlen>. If <ctx> is null, lookup is
821 * performed over the whole headers. Otherwise it must contain a valid header
822 * context, initialised with ctx->blk=NULL for the first lookup in a series. If
823 * <occ> is positive or null, occurrence #occ from the beginning (or last ctx)
824 * is returned. Occ #0 and #1 are equivalent. If <occ> is negative (and no less
825 * than -MAX_HDR_HISTORY), the occurrence is counted from the last one which is
826 * -1. This function differs from http_get_hdr() in that it only returns full
827 * line header values and does not stop at commas.
828 * The return value is 0 if nothing was found, or non-zero otherwise.
829 */
830unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr,
831 int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen)
832{
833 struct http_hdr_ctx local_ctx;
834 struct ist val_hist[MAX_HDR_HISTORY];
835 unsigned int hist_idx;
836 int found;
837
838 if (!ctx) {
839 local_ctx.blk = NULL;
840 ctx = &local_ctx;
841 }
842
843 if (occ >= 0) {
844 /* search from the beginning */
845 while (http_find_header(htx, hdr, ctx, 1)) {
846 occ--;
847 if (occ <= 0) {
848 *vptr = ctx->value.ptr;
849 *vlen = ctx->value.len;
850 return 1;
851 }
852 }
853 return 0;
854 }
855
856 /* negative occurrence, we scan all the list then walk back */
857 if (-occ > MAX_HDR_HISTORY)
858 return 0;
859
860 found = hist_idx = 0;
861 while (http_find_header(htx, hdr, ctx, 1)) {
862 val_hist[hist_idx] = ctx->value;
863 if (++hist_idx >= MAX_HDR_HISTORY)
864 hist_idx = 0;
865 found++;
866 }
867 if (-occ > found)
868 return 0;
869
870 /* OK now we have the last occurrence in [hist_idx-1], and we need to
871 * find occurrence -occ. 0 <= hist_idx < MAX_HDR_HISTORY, and we have
872 * -10 <= occ <= -1. So we have to check [hist_idx%MAX_HDR_HISTORY+occ]
873 * to remain in the 0..9 range.
874 */
875 hist_idx += occ + MAX_HDR_HISTORY;
876 if (hist_idx >= MAX_HDR_HISTORY)
877 hist_idx -= MAX_HDR_HISTORY;
878 *vptr = val_hist[hist_idx].ptr;
879 *vlen = val_hist[hist_idx].len;
880 return 1;
881}
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100882
Christopher Faulet90cc4812019-07-22 16:49:30 +0200883int http_str_to_htx(struct buffer *buf, struct ist raw)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100884{
885 struct htx *htx;
886 struct htx_sl *sl;
887 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200888 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100889 union h1_sl h1sl;
890 unsigned int flags = HTX_SL_F_IS_RESP;
891 int ret = 0;
892
Christopher Faulet90cc4812019-07-22 16:49:30 +0200893 b_reset(buf);
894 if (!raw.len) {
895 buf->size = 0;
896 buf->area = malloc(raw.len);
897 return 1;
898 }
899
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100900 buf->size = global.tune.bufsize;
901 buf->area = (char *)malloc(buf->size);
902 if (!buf->area)
903 goto error;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100904
905 h1m_init_res(&h1m);
906 h1m.flags |= H1_MF_NO_PHDR;
907 ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len,
908 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
909 if (ret <= 0)
910 goto error;
911
912 if (unlikely(h1sl.st.v.len != 8))
913 goto error;
914 if ((*(h1sl.st.v.ptr + 5) > '1') ||
915 ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1')))
916 h1m.flags |= H1_MF_VER_11;
917
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200918 if (h1sl.st.status < 200 && (h1sl.st.status == 100 || h1sl.st.status >= 102))
919 goto error;
920
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100921 if (h1m.flags & H1_MF_VER_11)
922 flags |= HTX_SL_F_VER_11;
923 if (h1m.flags & H1_MF_XFER_ENC)
924 flags |= HTX_SL_F_XFER_ENC;
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200925 if (h1m.flags & H1_MF_CLEN) {
926 flags |= (HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);
927 if (h1m.body_len == 0)
928 flags |= HTX_SL_F_BODYLESS;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100929 }
Christopher Faulet0d4ce932019-10-16 09:09:04 +0200930 if (h1m.flags & H1_MF_CHNK)
931 goto error; /* Unsupported because there is no body parsing */
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100932
933 htx = htx_from_buf(buf);
934 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
935 if (!sl || !htx_add_all_headers(htx, hdrs))
936 goto error;
937 sl->info.res.status = h1sl.st.status;
938
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200939 while (raw.len > ret) {
940 int sent = htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret));
941 if (!sent)
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100942 goto error;
Willy Tarreau0a7ef022019-05-28 10:30:11 +0200943 ret += sent;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100944 }
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200945
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100946 if (!htx_add_endof(htx, HTX_BLK_EOM))
947 goto error;
Christopher Faulet1d5ec092019-06-26 14:23:54 +0200948
Christopher Faulet90cc4812019-07-22 16:49:30 +0200949 return 1;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100950
951error:
952 if (buf->size)
953 free(buf->area);
Christopher Faulet90cc4812019-07-22 16:49:30 +0200954 return 0;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100955}
956
Christopher Faulet18630642020-05-12 18:57:28 +0200957void release_http_reply(struct http_reply *http_reply)
958{
959 struct logformat_node *lf, *lfb;
960 struct http_reply_hdr *hdr, *hdrb;
961
962 if (!http_reply)
963 return;
964
965 free(http_reply->ctype);
966 http_reply->ctype = NULL;
967 list_for_each_entry_safe(hdr, hdrb, &http_reply->hdrs, list) {
968 LIST_DEL(&hdr->list);
969 list_for_each_entry_safe(lf, lfb, &hdr->value, list) {
970 LIST_DEL(&lf->list);
971 release_sample_expr(lf->expr);
972 free(lf->arg);
973 free(lf);
974 }
975 istfree(&hdr->name);
976 free(hdr);
977 }
978
979 if (http_reply->type == HTTP_REPLY_ERRFILES) {
980 free(http_reply->body.http_errors);
981 http_reply->body.http_errors = NULL;
982 }
983 else if (http_reply->type == HTTP_REPLY_RAW)
984 chunk_destroy(&http_reply->body.obj);
985 else if (http_reply->type == HTTP_REPLY_LOGFMT) {
986 list_for_each_entry_safe(lf, lfb, &http_reply->body.fmt, list) {
987 LIST_DEL(&lf->list);
988 release_sample_expr(lf->expr);
989 free(lf->arg);
990 free(lf);
991 }
992 }
993}
994
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100995static int http_htx_init(void)
996{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100997 struct buffer chk;
998 struct ist raw;
999 int rc;
1000 int err_code = 0;
1001
Christopher Fauleta7b677c2018-11-29 16:48:49 +01001002 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1003 if (!http_err_msgs[rc]) {
1004 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
1005 err_code |= ERR_ALERT | ERR_FATAL;
1006 continue;
1007 }
1008
1009 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
1010 if (!http_str_to_htx(&chk, raw)) {
1011 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
1012 http_err_codes[rc]);
1013 err_code |= ERR_ALERT | ERR_FATAL;
1014 }
Christopher Fauletf7346382019-07-17 22:02:08 +02001015 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +01001016 }
1017end:
1018 return err_code;
1019}
1020
Christopher Faulet58857752020-01-15 15:19:50 +01001021static void http_htx_deinit(void)
1022{
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001023 struct http_errors *http_errs, *http_errsb;
Christopher Faulet58857752020-01-15 15:19:50 +01001024 struct ebpt_node *node, *next;
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001025 struct http_error_msg *http_errmsg;
Christopher Faulet58857752020-01-15 15:19:50 +01001026
1027 node = ebpt_first(&http_error_messages);
1028 while (node) {
1029 next = ebpt_next(node);
1030 ebpt_delete(node);
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001031 http_errmsg = container_of(node, typeof(*http_errmsg), node);
1032 chunk_destroy(&http_errmsg->msg);
Christopher Faulet58857752020-01-15 15:19:50 +01001033 free(node->key);
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001034 free(http_errmsg);
Christopher Faulet58857752020-01-15 15:19:50 +01001035 node = next;
1036 }
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001037
1038 list_for_each_entry_safe(http_errs, http_errsb, &http_errors_list, list) {
1039 free(http_errs->conf.file);
1040 free(http_errs->id);
1041 LIST_DEL(&http_errs->list);
1042 free(http_errs);
1043 }
Christopher Faulet58857752020-01-15 15:19:50 +01001044}
1045
Christopher Fauleta7b677c2018-11-29 16:48:49 +01001046REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);
Christopher Faulet58857752020-01-15 15:19:50 +01001047REGISTER_POST_DEINIT(http_htx_deinit);
Christopher Faulet29f72842019-12-11 15:52:32 +01001048
Christopher Faulet58857752020-01-15 15:19:50 +01001049/* Reads content of the error file <file> and convert it into an HTX message. On
1050 * success, the HTX message is returned. On error, NULL is returned and an error
1051 * message is written into the <errmsg> buffer.
Christopher Faulet5031ef52020-01-15 11:22:07 +01001052 */
Christopher Faulet58857752020-01-15 15:19:50 +01001053struct buffer *http_load_errorfile(const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +01001054{
Christopher Faulet58857752020-01-15 15:19:50 +01001055 struct buffer *buf = NULL;
1056 struct buffer chk;
1057 struct ebpt_node *node;
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001058 struct http_error_msg *http_errmsg;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001059 struct stat stat;
1060 char *err = NULL;
1061 int errnum, errlen;
1062 int fd = -1;
Christopher Faulet58857752020-01-15 15:19:50 +01001063
1064 /* already loaded */
1065 node = ebis_lookup_len(&http_error_messages, file, strlen(file));
1066 if (node) {
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001067 http_errmsg = container_of(node, typeof(*http_errmsg), node);
1068 buf = &http_errmsg->msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001069 goto out;
1070 }
Christopher Faulet5031ef52020-01-15 11:22:07 +01001071
Christopher Faulet58857752020-01-15 15:19:50 +01001072 /* Read the error file content */
Christopher Faulet5031ef52020-01-15 11:22:07 +01001073 fd = open(file, O_RDONLY);
1074 if ((fd < 0) || (fstat(fd, &stat) < 0)) {
1075 memprintf(errmsg, "error opening file '%s'.", file);
1076 goto out;
1077 }
1078
1079 if (stat.st_size <= global.tune.bufsize)
1080 errlen = stat.st_size;
1081 else {
1082 ha_warning("custom error message file '%s' larger than %d bytes. Truncating.\n",
1083 file, global.tune.bufsize);
1084 errlen = global.tune.bufsize;
1085 }
1086
1087 err = malloc(errlen);
1088 if (!err) {
1089 memprintf(errmsg, "out of memory.");
1090 goto out;
1091 }
1092
1093 errnum = read(fd, err, errlen);
1094 if (errnum != errlen) {
1095 memprintf(errmsg, "error reading file '%s'.", file);
1096 goto out;
1097 }
1098
Christopher Faulet58857752020-01-15 15:19:50 +01001099 /* Create the node corresponding to the error file */
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001100 http_errmsg = calloc(1, sizeof(*http_errmsg));
1101 if (!http_errmsg) {
Christopher Faulet58857752020-01-15 15:19:50 +01001102 memprintf(errmsg, "out of memory.");
1103 goto out;
1104 }
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001105 http_errmsg->node.key = strdup(file);
1106 if (!http_errmsg->node.key) {
Christopher Faulet58857752020-01-15 15:19:50 +01001107 memprintf(errmsg, "out of memory.");
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001108 free(http_errmsg);
Christopher Faulet58857752020-01-15 15:19:50 +01001109 goto out;
1110 }
1111
1112 /* Convert the error file into an HTX message */
1113 if (!http_str_to_htx(&chk, ist2(err, errlen))) {
Christopher Faulet5031ef52020-01-15 11:22:07 +01001114 memprintf(errmsg, "unable to convert custom error message file '%s' in HTX.", file);
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001115 free(http_errmsg->node.key);
1116 free(http_errmsg);
Christopher Faulet5031ef52020-01-15 11:22:07 +01001117 goto out;
1118 }
1119
Christopher Faulet58857752020-01-15 15:19:50 +01001120 /* Insert the node in the tree and return the HTX message */
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001121 http_errmsg->msg = chk;
1122 ebis_insert(&http_error_messages, &http_errmsg->node);
1123 buf = &http_errmsg->msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001124
Christopher Faulet5031ef52020-01-15 11:22:07 +01001125 out:
1126 if (fd >= 0)
1127 close(fd);
1128 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +01001129 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001130}
1131
Ilya Shipitsind4259502020-04-08 01:07:56 +05001132/* Convert the raw http message <msg> into an HTX message. On success, the HTX
Christopher Faulet58857752020-01-15 15:19:50 +01001133 * message is returned. On error, NULL is returned and an error message is
1134 * written into the <errmsg> buffer.
Christopher Fauletbdf65262020-01-16 15:51:59 +01001135 */
Christopher Faulet58857752020-01-15 15:19:50 +01001136struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +01001137{
Christopher Faulet58857752020-01-15 15:19:50 +01001138 struct buffer *buf = NULL;
1139 struct buffer chk;
1140 struct ebpt_node *node;
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001141 struct http_error_msg *http_errmsg;
Christopher Faulet58857752020-01-15 15:19:50 +01001142
1143 /* already loaded */
1144 node = ebis_lookup_len(&http_error_messages, key, strlen(key));
1145 if (node) {
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001146 http_errmsg = container_of(node, typeof(*http_errmsg), node);
1147 buf = &http_errmsg->msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001148 goto out;
1149 }
1150 /* Create the node corresponding to the error file */
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001151 http_errmsg = calloc(1, sizeof(*http_errmsg));
1152 if (!http_errmsg) {
Christopher Faulet58857752020-01-15 15:19:50 +01001153 memprintf(errmsg, "out of memory.");
1154 goto out;
1155 }
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001156 http_errmsg->node.key = strdup(key);
1157 if (!http_errmsg->node.key) {
Christopher Faulet58857752020-01-15 15:19:50 +01001158 memprintf(errmsg, "out of memory.");
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001159 free(http_errmsg);
Christopher Faulet58857752020-01-15 15:19:50 +01001160 goto out;
1161 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001162
1163 /* Convert the error file into an HTX message */
Christopher Faulet58857752020-01-15 15:19:50 +01001164 if (!http_str_to_htx(&chk, msg)) {
Christopher Fauletbdf65262020-01-16 15:51:59 +01001165 memprintf(errmsg, "unable to convert message in HTX.");
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001166 free(http_errmsg->node.key);
1167 free(http_errmsg);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001168 goto out;
1169 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001170
Christopher Faulet58857752020-01-15 15:19:50 +01001171 /* Insert the node in the tree and return the HTX message */
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001172 http_errmsg->msg = chk;
1173 ebis_insert(&http_error_messages, &http_errmsg->node);
1174 buf = &http_errmsg->msg;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001175 out:
Christopher Faulet58857752020-01-15 15:19:50 +01001176 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001177}
1178
Christopher Faulet5031ef52020-01-15 11:22:07 +01001179/* This function parses the raw HTTP error file <file> for the status code
Christopher Faulet58857752020-01-15 15:19:50 +01001180 * <status>. It returns NULL if there is any error, otherwise it return the
1181 * corresponding HTX message.
Christopher Faulet5031ef52020-01-15 11:22:07 +01001182 */
Christopher Faulet58857752020-01-15 15:19:50 +01001183struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +01001184{
Christopher Faulet58857752020-01-15 15:19:50 +01001185 struct buffer *buf = NULL;
1186 int rc;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001187
1188 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1189 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +01001190 buf = http_load_errorfile(file, errmsg);
Christopher Faulet5031ef52020-01-15 11:22:07 +01001191 break;
1192 }
1193 }
1194
1195 if (rc >= HTTP_ERR_SIZE)
1196 memprintf(errmsg, "status code '%d' not handled.", status);
Christopher Faulet58857752020-01-15 15:19:50 +01001197 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001198}
1199
1200/* This function creates HTX error message corresponding to a redirect message
1201 * for the status code <status>. <url> is used as location url for the
Christopher Faulet58857752020-01-15 15:19:50 +01001202 * redirect. <errloc> is used to know if it is a 302 or a 303 redirect. It
1203 * returns NULL if there is any error, otherwise it return the corresponding HTX
1204 * message.
Christopher Fauletbdf65262020-01-16 15:51:59 +01001205 */
Christopher Faulet58857752020-01-15 15:19:50 +01001206struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +01001207{
Christopher Faulet58857752020-01-15 15:19:50 +01001208 struct buffer *buf = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001209 const char *msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001210 char *key = NULL, *err = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001211 int rc, errlen;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001212
1213 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1214 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +01001215 /* Create the error key */
1216 if (!memprintf(&key, "errorloc%d %s", errloc, url)) {
1217 memprintf(errmsg, "out of memory.");
1218 goto out;
1219 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001220 /* Create the error message */
1221 msg = (errloc == 302 ? HTTP_302 : HTTP_303);
1222 errlen = strlen(msg) + strlen(url) + 5;
1223 err = malloc(errlen);
1224 if (!err) {
1225 memprintf(errmsg, "out of memory.");
1226 goto out;
1227 }
1228 errlen = snprintf(err, errlen, "%s%s\r\n\r\n", msg, url);
1229
1230 /* Load it */
Christopher Faulet58857752020-01-15 15:19:50 +01001231 buf = http_load_errormsg(key, ist2(err, errlen), errmsg);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001232 break;
1233 }
1234 }
1235
1236 if (rc >= HTTP_ERR_SIZE)
1237 memprintf(errmsg, "status code '%d' not handled.", status);
1238out:
Christopher Faulet58857752020-01-15 15:19:50 +01001239 free(key);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001240 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +01001241 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001242}
1243
Christopher Faulet07f41f72020-01-16 16:16:06 +01001244/* Parses the "errorloc[302|303]" proxy keyword */
1245static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
1246 struct proxy *defpx, const char *file, int line,
1247 char **errmsg)
1248{
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001249 struct conf_errors *conf_err;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001250 struct buffer *msg;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001251 int errloc, status;
1252 int ret = 0;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001253
1254 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1255 ret = 1;
1256 goto out;
1257 }
1258
1259 if (*(args[1]) == 0 || *(args[2]) == 0) {
1260 memprintf(errmsg, "%s : expects <status_code> and <url> as arguments.\n", args[0]);
1261 ret = -1;
1262 goto out;
1263 }
1264
1265 status = atol(args[1]);
1266 errloc = (!strcmp(args[0], "errorloc303") ? 303 : 302);
1267 msg = http_parse_errorloc(errloc, status, args[2], errmsg);
1268 if (!msg) {
1269 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1270 ret = -1;
1271 goto out;
1272 }
1273
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001274 conf_err = calloc(1, sizeof(*conf_err));
1275 if (!conf_err) {
1276 memprintf(errmsg, "%s : out of memory.", args[0]);
1277 ret = -1;
1278 goto out;
1279 }
1280 conf_err->type = 1;
1281 conf_err->info.errorfile.status = status;
1282 conf_err->info.errorfile.msg = msg;
1283 conf_err->file = strdup(file);
1284 conf_err->line = line;
1285 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001286
1287 out:
1288 return ret;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001289
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001290}
Christopher Faulet07f41f72020-01-16 16:16:06 +01001291
1292/* Parses the "errorfile" proxy keyword */
1293static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
1294 struct proxy *defpx, const char *file, int line,
1295 char **errmsg)
1296{
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001297 struct conf_errors *conf_err;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001298 struct buffer *msg;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001299 int status;
1300 int ret = 0;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001301
1302 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1303 ret = 1;
1304 goto out;
1305 }
1306
1307 if (*(args[1]) == 0 || *(args[2]) == 0) {
1308 memprintf(errmsg, "%s : expects <status_code> and <file> as arguments.\n", args[0]);
1309 ret = -1;
1310 goto out;
1311 }
1312
1313 status = atol(args[1]);
1314 msg = http_parse_errorfile(status, args[2], errmsg);
1315 if (!msg) {
1316 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1317 ret = -1;
1318 goto out;
1319 }
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001320
1321 conf_err = calloc(1, sizeof(*conf_err));
1322 if (!conf_err) {
1323 memprintf(errmsg, "%s : out of memory.", args[0]);
1324 ret = -1;
1325 goto out;
1326 }
1327 conf_err->type = 1;
1328 conf_err->info.errorfile.status = status;
1329 conf_err->info.errorfile.msg = msg;
1330 conf_err->file = strdup(file);
1331 conf_err->line = line;
1332 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
1333
1334 out:
1335 return ret;
1336
1337}
1338
1339/* Parses the "errorfiles" proxy keyword */
1340static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
1341 struct proxy *defpx, const char *file, int line,
1342 char **err)
1343{
1344 struct conf_errors *conf_err = NULL;
1345 char *name = NULL;
1346 int rc, ret = 0;
1347
1348 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1349 ret = 1;
1350 goto out;
1351 }
1352
1353 if (!*(args[1])) {
1354 memprintf(err, "%s : expects <name> as argument.", args[0]);
1355 ret = -1;
1356 goto out;
1357 }
1358
1359 name = strdup(args[1]);
1360 conf_err = calloc(1, sizeof(*conf_err));
1361 if (!name || !conf_err) {
1362 memprintf(err, "%s : out of memory.", args[0]);
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001363 goto error;
1364 }
1365 conf_err->type = 0;
1366
1367 conf_err->info.errorfiles.name = name;
1368 if (!*(args[2])) {
1369 for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
1370 conf_err->info.errorfiles.status[rc] = 1;
1371 }
1372 else {
1373 int cur_arg, status;
1374 for (cur_arg = 2; *(args[cur_arg]); cur_arg++) {
1375 status = atol(args[cur_arg]);
1376
1377 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1378 if (http_err_codes[rc] == status) {
1379 conf_err->info.errorfiles.status[rc] = 2;
1380 break;
1381 }
1382 }
1383 if (rc >= HTTP_ERR_SIZE) {
1384 memprintf(err, "%s : status code '%d' not handled.", args[0], status);
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001385 goto error;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001386 }
1387 }
1388 }
1389 conf_err->file = strdup(file);
1390 conf_err->line = line;
1391 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
1392 out:
1393 return ret;
1394
1395 error:
1396 free(name);
1397 free(conf_err);
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001398 ret = -1;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001399 goto out;
1400}
1401
1402/* Check "errorfiles" proxy keyword */
1403static int proxy_check_errors(struct proxy *px)
1404{
1405 struct conf_errors *conf_err, *conf_err_back;
1406 struct http_errors *http_errs;
1407 int rc, err = 0;
1408
1409 list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
1410 if (conf_err->type == 1) {
1411 /* errorfile */
1412 rc = http_get_status_idx(conf_err->info.errorfile.status);
1413 px->errmsg[rc] = conf_err->info.errorfile.msg;
1414 }
1415 else {
1416 /* errorfiles */
1417 list_for_each_entry(http_errs, &http_errors_list, list) {
1418 if (strcmp(http_errs->id, conf_err->info.errorfiles.name) == 0)
1419 break;
1420 }
Christopher Faulet07f41f72020-01-16 16:16:06 +01001421
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001422 /* unknown http-errors section */
1423 if (&http_errs->list == &http_errors_list) {
1424 ha_alert("config : proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
1425 px->id, conf_err->info.errorfiles.name, conf_err->file, conf_err->line);
1426 err |= ERR_ALERT | ERR_FATAL;
1427 free(conf_err->info.errorfiles.name);
1428 goto next;
1429 }
1430
1431 free(conf_err->info.errorfiles.name);
1432 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1433 if (conf_err->info.errorfiles.status[rc] > 0) {
1434 if (http_errs->errmsg[rc])
1435 px->errmsg[rc] = http_errs->errmsg[rc];
1436 else if (conf_err->info.errorfiles.status[rc] == 2)
1437 ha_warning("config: proxy '%s' : status '%d' not declared in"
1438 " http-errors section '%s' (at %s:%d).\n",
1439 px->id, http_err_codes[rc], http_errs->id,
1440 conf_err->file, conf_err->line);
1441 }
1442 }
1443 }
1444 next:
1445 LIST_DEL(&conf_err->list);
1446 free(conf_err->file);
1447 free(conf_err);
1448 }
Christopher Faulet07f41f72020-01-16 16:16:06 +01001449
1450 out:
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001451 return err;
1452}
1453
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001454static int post_check_errors()
1455{
1456 struct ebpt_node *node;
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001457 struct http_error_msg *http_errmsg;
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001458 struct htx *htx;
1459 int err_code = 0;
1460
1461 node = ebpt_first(&http_error_messages);
1462 while (node) {
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001463 http_errmsg = container_of(node, typeof(*http_errmsg), node);
1464 if (b_is_null(&http_errmsg->msg))
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001465 goto next;
Christopher Fauletb6ea17c2020-05-13 21:45:22 +02001466 htx = htxbuf(&http_errmsg->msg);
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001467 if (htx_free_data_space(htx) < global.tune.maxrewrite) {
1468 ha_warning("config: errorfile '%s' runs over the buffer space"
1469 " reserved to headers rewritting. It may lead to internal errors if "
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001470 " http-after-response rules are evaluated on this message.\n",
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001471 (char *)node->key);
1472 err_code |= ERR_WARN;
1473 }
1474 next:
1475 node = ebpt_next(node);
1476 }
1477
1478 return err_code;
1479}
1480
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001481int proxy_dup_default_conf_errors(struct proxy *curpx, struct proxy *defpx, char **errmsg)
1482{
1483 struct conf_errors *conf_err, *new_conf_err = NULL;
1484 int ret = 0;
1485
1486 list_for_each_entry(conf_err, &defpx->conf.errors, list) {
1487 new_conf_err = calloc(1, sizeof(*new_conf_err));
1488 if (!new_conf_err) {
1489 memprintf(errmsg, "unable to duplicate default errors (out of memory).");
1490 goto out;
1491 }
1492 new_conf_err->type = conf_err->type;
1493 if (conf_err->type == 1) {
1494 new_conf_err->info.errorfile.status = conf_err->info.errorfile.status;
1495 new_conf_err->info.errorfile.msg = conf_err->info.errorfile.msg;
1496 }
1497 else {
1498 new_conf_err->info.errorfiles.name = strdup(conf_err->info.errorfiles.name);
1499 if (!new_conf_err->info.errorfiles.name) {
1500 memprintf(errmsg, "unable to duplicate default errors (out of memory).");
1501 goto out;
1502 }
1503 memcpy(&new_conf_err->info.errorfiles.status, &conf_err->info.errorfiles.status,
1504 sizeof(conf_err->info.errorfiles.status));
1505 }
1506 new_conf_err->file = strdup(conf_err->file);
1507 new_conf_err->line = conf_err->line;
1508 LIST_ADDQ(&curpx->conf.errors, &new_conf_err->list);
1509 new_conf_err = NULL;
1510 }
1511 ret = 1;
1512
1513 out:
1514 free(new_conf_err);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001515 return ret;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001516}
1517
1518void proxy_release_conf_errors(struct proxy *px)
1519{
1520 struct conf_errors *conf_err, *conf_err_back;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001521
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001522 list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
1523 if (conf_err->type == 0)
1524 free(conf_err->info.errorfiles.name);
1525 LIST_DEL(&conf_err->list);
1526 free(conf_err->file);
1527 free(conf_err);
1528 }
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001529}
1530
1531/*
1532 * Parse an <http-errors> section.
1533 * Returns the error code, 0 if OK, or any combination of :
1534 * - ERR_ABORT: must abort ASAP
1535 * - ERR_FATAL: we can continue parsing but not start the service
1536 * - ERR_WARN: a warning has been emitted
1537 * - ERR_ALERT: an alert has been emitted
1538 * Only the two first ones can stop processing, the two others are just
1539 * indicators.
1540 */
1541static int cfg_parse_http_errors(const char *file, int linenum, char **args, int kwm)
1542{
1543 static struct http_errors *curr_errs = NULL;
1544 int err_code = 0;
1545 const char *err;
1546 char *errmsg = NULL;
1547
1548 if (strcmp(args[0], "http-errors") == 0) { /* new errors section */
1549 if (!*args[1]) {
1550 ha_alert("parsing [%s:%d] : missing name for http-errors section.\n", file, linenum);
1551 err_code |= ERR_ALERT | ERR_ABORT;
1552 goto out;
1553 }
1554
1555 err = invalid_char(args[1]);
1556 if (err) {
1557 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
1558 file, linenum, *err, args[0], args[1]);
1559 err_code |= ERR_ALERT | ERR_FATAL;
1560 }
1561
1562 list_for_each_entry(curr_errs, &http_errors_list, list) {
1563 /* Error if two errors section owns the same name */
1564 if (strcmp(curr_errs->id, args[1]) == 0) {
1565 ha_alert("parsing [%s:%d]: http-errors section '%s' already exists (declared at %s:%d).\n",
1566 file, linenum, args[1], curr_errs->conf.file, curr_errs->conf.line);
1567 err_code |= ERR_ALERT | ERR_FATAL;
1568 }
1569 }
1570
1571 if ((curr_errs = calloc(1, sizeof(*curr_errs))) == NULL) {
1572 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1573 err_code |= ERR_ALERT | ERR_ABORT;
1574 goto out;
1575 }
1576
1577 LIST_ADDQ(&http_errors_list, &curr_errs->list);
1578 curr_errs->id = strdup(args[1]);
1579 curr_errs->conf.file = strdup(file);
1580 curr_errs->conf.line = linenum;
1581 }
1582 else if (!strcmp(args[0], "errorfile")) { /* error message from a file */
1583 struct buffer *msg;
1584 int status, rc;
1585
1586 if (*(args[1]) == 0 || *(args[2]) == 0) {
1587 ha_alert("parsing [%s:%d] : %s: expects <status_code> and <file> as arguments.\n",
1588 file, linenum, args[0]);
1589 err_code |= ERR_ALERT | ERR_FATAL;
1590 goto out;
1591 }
1592
1593 status = atol(args[1]);
1594 msg = http_parse_errorfile(status, args[2], &errmsg);
1595 if (!msg) {
1596 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
1597 err_code |= ERR_ALERT | ERR_FATAL;
1598 goto out;
1599 }
1600 rc = http_get_status_idx(status);
1601 curr_errs->errmsg[rc] = msg;
1602 }
1603 else if (*args[0] != 0) {
1604 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
1605 err_code |= ERR_ALERT | ERR_FATAL;
1606 goto out;
1607 }
1608
1609out:
1610 free(errmsg);
1611 return err_code;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001612}
1613
1614static struct cfg_kw_list cfg_kws = {ILH, {
1615 { CFG_LISTEN, "errorloc", proxy_parse_errorloc },
1616 { CFG_LISTEN, "errorloc302", proxy_parse_errorloc },
1617 { CFG_LISTEN, "errorloc303", proxy_parse_errorloc },
1618 { CFG_LISTEN, "errorfile", proxy_parse_errorfile },
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001619 { CFG_LISTEN, "errorfiles", proxy_parse_errorfiles },
Christopher Faulet07f41f72020-01-16 16:16:06 +01001620 { 0, NULL, NULL },
1621}};
1622
1623INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001624REGISTER_POST_PROXY_CHECK(proxy_check_errors);
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001625REGISTER_POST_CHECK(post_check_errors);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001626
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001627REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
1628
Christopher Faulet29f72842019-12-11 15:52:32 +01001629/************************************************************************/
1630/* HTX sample fetches */
1631/************************************************************************/
1632
1633/* Returns 1 if a stream is an HTX stream. Otherwise, it returns 0. */
1634static int
1635smp_fetch_is_htx(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1636{
1637 if (!smp->strm)
1638 return 0;
1639
1640 smp->data.u.sint = !!IS_HTX_STRM(smp->strm);
1641 smp->data.type = SMP_T_BOOL;
1642 return 1;
1643}
1644
1645/* Returns the number of blocks in an HTX message. The channel is chosen
1646 * depending on the sample direction. */
1647static int
1648smp_fetch_htx_nbblks(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1649{
1650 struct channel *chn;
1651 struct htx *htx;
1652
1653 if (!smp->strm)
1654 return 0;
1655
1656 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001657 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001658 if (!htx)
1659 return 0;
1660
1661 smp->data.u.sint = htx_nbblks(htx);
1662 smp->data.type = SMP_T_SINT;
1663 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1664 return 1;
1665}
1666
1667/* Returns the size of an HTX message. The channel is chosen depending on the
1668 * sample direction. */
1669static int
1670smp_fetch_htx_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1671{
1672 struct channel *chn;
1673 struct htx *htx;
1674
1675 if (!smp->strm)
1676 return 0;
1677
1678 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001679 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001680 if (!htx)
1681 return 0;
1682
1683 smp->data.u.sint = htx->size;
1684 smp->data.type = SMP_T_SINT;
1685 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1686 return 1;
1687}
1688
1689/* Returns the data size of an HTX message. The channel is chosen depending on the
1690 * sample direction. */
1691static int
1692smp_fetch_htx_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1693{
1694 struct channel *chn;
1695 struct htx *htx;
1696
1697 if (!smp->strm)
1698 return 0;
1699
1700 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001701 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001702 if (!htx)
1703 return 0;
1704
1705 smp->data.u.sint = htx->data;
1706 smp->data.type = SMP_T_SINT;
1707 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1708 return 1;
1709}
1710
1711/* Returns the used space (data+meta) of an HTX message. The channel is chosen
1712 * depending on the sample direction. */
1713static int
1714smp_fetch_htx_used(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1715{
1716 struct channel *chn;
1717 struct htx *htx;
1718
1719 if (!smp->strm)
1720 return 0;
1721
1722 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001723 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001724 if (!htx)
1725 return 0;
1726
1727 smp->data.u.sint = htx_used_space(htx);
1728 smp->data.type = SMP_T_SINT;
1729 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1730 return 1;
1731}
1732
1733/* Returns the free space (size-used) of an HTX message. The channel is chosen
1734 * depending on the sample direction. */
1735static int
1736smp_fetch_htx_free(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1737{
1738 struct channel *chn;
1739 struct htx *htx;
1740
1741 if (!smp->strm)
1742 return 0;
1743
1744 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001745 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001746 if (!htx)
1747 return 0;
1748
1749 smp->data.u.sint = htx_free_space(htx);
1750 smp->data.type = SMP_T_SINT;
1751 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1752 return 1;
1753}
1754
1755/* Returns the free space for data (free-sizeof(blk)) of an HTX message. The
1756 * channel is chosen depending on the sample direction. */
1757static int
1758smp_fetch_htx_free_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1759{
1760 struct channel *chn;
1761 struct htx *htx;
1762
1763 if (!smp->strm)
1764 return 0;
1765
1766 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001767 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001768 if (!htx)
1769 return 0;
1770
1771 smp->data.u.sint = htx_free_data_space(htx);
1772 smp->data.type = SMP_T_SINT;
1773 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1774 return 1;
1775}
1776
1777/* Returns 1 if the HTX message contains an EOM block. Otherwise it returns
1778 * 0. Concretely, it only checks the tail. The channel is chosen depending on
1779 * the sample direction. */
1780static int
1781smp_fetch_htx_has_eom(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1782{
1783 struct channel *chn;
1784 struct htx *htx;
1785
1786 if (!smp->strm)
1787 return 0;
1788
1789 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001790 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001791 if (!htx)
1792 return 0;
1793
1794 smp->data.u.sint = (htx_get_tail_type(htx) == HTX_BLK_EOM);
1795 smp->data.type = SMP_T_BOOL;
1796 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1797 return 1;
1798}
1799
1800/* Returns the type of a specific HTX block, if found in the message. Otherwise
1801 * HTX_BLK_UNUSED is returned. Any positive integer (>= 0) is supported or
1802 * "head", "tail" or "first". The channel is chosen depending on the sample
1803 * direction. */
1804static int
1805smp_fetch_htx_blk_type(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1806{
1807 struct channel *chn;
1808 struct htx *htx;
1809 enum htx_blk_type type;
1810 int32_t pos;
1811
1812 if (!smp->strm || !arg_p)
1813 return 0;
1814
1815 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001816 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001817 if (!htx)
1818 return 0;
1819
1820 pos = arg_p[0].data.sint;
1821 if (pos == -1)
1822 type = htx_get_head_type(htx);
1823 else if (pos == -2)
1824 type = htx_get_tail_type(htx);
1825 else if (pos == -3)
1826 type = htx_get_first_type(htx);
1827 else
1828 type = ((pos >= htx->head && pos <= htx->tail)
1829 ? htx_get_blk_type(htx_get_blk(htx, pos))
1830 : HTX_BLK_UNUSED);
1831
1832 chunk_initstr(&smp->data.u.str, htx_blk_type_str(type));
1833 smp->data.type = SMP_T_STR;
1834 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1835 return 1;
1836}
1837
1838/* Returns the size of a specific HTX block, if found in the message. Otherwise
1839 * 0 is returned. Any positive integer (>= 0) is supported or "head", "tail" or
1840 * "first". The channel is chosen depending on the sample direction. */
1841static int
1842smp_fetch_htx_blk_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1843{
1844 struct channel *chn;
1845 struct htx *htx;
1846 struct htx_blk *blk;
1847 int32_t pos;
1848
1849 if (!smp->strm || !arg_p)
1850 return 0;
1851
1852 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001853 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001854 if (!htx)
1855 return 0;
1856
1857 pos = arg_p[0].data.sint;
1858 if (pos == -1)
1859 blk = htx_get_head_blk(htx);
1860 else if (pos == -2)
1861 blk = htx_get_tail_blk(htx);
1862 else if (pos == -3)
1863 blk = htx_get_first_blk(htx);
1864 else
1865 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1866
1867 smp->data.u.sint = (blk ? htx_get_blksz(blk) : 0);
1868 smp->data.type = SMP_T_SINT;
1869 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1870 return 1;
1871}
1872
1873/* Returns the start-line if the selected HTX block exists and is a
1874 * start-line. Otherwise 0 an empty string. Any positive integer (>= 0) is
1875 * supported or "head", "tail" or "first". The channel is chosen depending on
1876 * the sample direction. */
1877static int
1878smp_fetch_htx_blk_stline(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1879{
1880 struct buffer *temp;
1881 struct channel *chn;
1882 struct htx *htx;
1883 struct htx_blk *blk;
1884 struct htx_sl *sl;
1885 int32_t pos;
1886
1887 if (!smp->strm || !arg_p)
1888 return 0;
1889
1890 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001891 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001892 if (!htx)
1893 return 0;
1894
1895 pos = arg_p[0].data.sint;
1896 if (pos == -1)
1897 blk = htx_get_head_blk(htx);
1898 else if (pos == -2)
1899 blk = htx_get_tail_blk(htx);
1900 else if (pos == -3)
1901 blk = htx_get_first_blk(htx);
1902 else
1903 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1904
1905 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL)) {
1906 smp->data.u.str.size = 0;
1907 smp->data.u.str.area = "";
1908 smp->data.u.str.data = 0;
1909 }
1910 else {
1911 sl = htx_get_blk_ptr(htx, blk);
1912
1913 temp = get_trash_chunk();
1914 chunk_istcat(temp, htx_sl_p1(sl));
1915 temp->area[temp->data++] = ' ';
1916 chunk_istcat(temp, htx_sl_p2(sl));
1917 temp->area[temp->data++] = ' ';
1918 chunk_istcat(temp, htx_sl_p3(sl));
1919
1920 smp->data.u.str = *temp;
1921 }
1922
1923 smp->data.type = SMP_T_STR;
1924 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1925 return 1;
1926}
1927
1928/* Returns the header name if the selected HTX block exists and is a header or a
1929 * trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1930 * supported or "head", "tail" or "first". The channel is chosen depending on
1931 * the sample direction. */
1932static int
1933smp_fetch_htx_blk_hdrname(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1934{
1935 struct channel *chn;
1936 struct htx *htx;
1937 struct htx_blk *blk;
1938 int32_t pos;
1939
1940 if (!smp->strm || !arg_p)
1941 return 0;
1942
1943 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001944 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001945 if (!htx)
1946 return 0;
1947
1948 pos = arg_p[0].data.sint;
1949 if (pos == -1)
1950 blk = htx_get_head_blk(htx);
1951 else if (pos == -2)
1952 blk = htx_get_tail_blk(htx);
1953 else if (pos == -3)
1954 blk = htx_get_first_blk(htx);
1955 else
1956 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1957
1958 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1959 smp->data.u.str.size = 0;
1960 smp->data.u.str.area = "";
1961 smp->data.u.str.data = 0;
1962 }
1963 else {
1964 struct ist name = htx_get_blk_name(htx, blk);
1965
1966 chunk_initlen(&smp->data.u.str, name.ptr, name.len, name.len);
1967 }
1968 smp->data.type = SMP_T_STR;
1969 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1970 return 1;
1971}
1972
1973/* Returns the header value if the selected HTX block exists and is a header or
1974 * a trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1975 * supported or "head", "tail" or "first". The channel is chosen depending on
1976 * the sample direction. */
1977static int
1978smp_fetch_htx_blk_hdrval(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1979{
1980 struct channel *chn;
1981 struct htx *htx;
1982 struct htx_blk *blk;
1983 int32_t pos;
1984
1985 if (!smp->strm || !arg_p)
1986 return 0;
1987
1988 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001989 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001990 if (!htx)
1991 return 0;
1992
1993 pos = arg_p[0].data.sint;
1994 if (pos == -1)
1995 blk = htx_get_head_blk(htx);
1996 else if (pos == -2)
1997 blk = htx_get_tail_blk(htx);
1998 else if (pos == -3)
1999 blk = htx_get_first_blk(htx);
2000 else
2001 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
2002
2003 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
2004 smp->data.u.str.size = 0;
2005 smp->data.u.str.area = "";
2006 smp->data.u.str.data = 0;
2007 }
2008 else {
2009 struct ist val = htx_get_blk_value(htx, blk);
2010
2011 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
2012 }
2013 smp->data.type = SMP_T_STR;
2014 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
2015 return 1;
2016}
2017
2018/* Returns the value if the selected HTX block exists and is a data
2019 * block. Otherwise 0 an empty string. Any positive integer (>= 0) is supported
2020 * or "head", "tail" or "first". The channel is chosen depending on the sample
2021 * direction. */
2022static int
Christopher Fauletc5db14c2020-01-08 14:51:03 +01002023smp_fetch_htx_blk_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
Christopher Faulet29f72842019-12-11 15:52:32 +01002024{
2025 struct channel *chn;
2026 struct htx *htx;
2027 struct htx_blk *blk;
2028 int32_t pos;
2029
2030 if (!smp->strm || !arg_p)
2031 return 0;
2032
2033 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02002034 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01002035 if (!htx)
2036 return 0;
2037
2038 pos = arg_p[0].data.sint;
2039 if (pos == -1)
2040 blk = htx_get_head_blk(htx);
2041 else if (pos == -2)
2042 blk = htx_get_tail_blk(htx);
2043 else if (pos == -3)
2044 blk = htx_get_first_blk(htx);
2045 else
2046 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
2047
2048 if (!blk || htx_get_blk_type(blk) != HTX_BLK_DATA) {
2049 smp->data.u.str.size = 0;
2050 smp->data.u.str.area = "";
2051 smp->data.u.str.data = 0;
2052 }
2053 else {
2054 struct ist val = htx_get_blk_value(htx, blk);
2055
2056 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
2057 }
Christopher Faulet8178e402020-01-08 14:38:58 +01002058 smp->data.type = SMP_T_BIN;
Christopher Faulet29f72842019-12-11 15:52:32 +01002059 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
2060 return 1;
2061}
2062
2063/* This function is used to validate the arguments passed to any "htx_blk" fetch
2064 * keywords. An argument is expected by these keywords. It must be a positive
2065 * integer or on of the following strings: "head", "tail" or "first". It returns
2066 * 0 on error, and a non-zero value if OK.
2067 */
2068int val_blk_arg(struct arg *arg, char **err_msg)
2069{
2070 if (arg[0].type != ARGT_STR || !arg[0].data.str.data) {
2071 memprintf(err_msg, "a block position is expected (> 0) or a special block name (head, tail, first)");
2072 return 0;
2073 }
2074 if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "head", 4)) {
2075 free(arg[0].data.str.area);
2076 arg[0].type = ARGT_SINT;
2077 arg[0].data.sint = -1;
2078 }
2079 else if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "tail", 4)) {
2080 free(arg[0].data.str.area);
2081 arg[0].type = ARGT_SINT;
2082 arg[0].data.sint = -2;
2083 }
2084 else if (arg[0].data.str.data == 5 && !strncmp(arg[0].data.str.area, "first", 5)) {
2085 free(arg[0].data.str.area);
2086 arg[0].type = ARGT_SINT;
2087 arg[0].data.sint = -3;
2088 }
2089 else {
2090 int pos;
2091
2092 for (pos = 0; pos < arg[0].data.str.data; pos++) {
Willy Tarreau90807112020-02-25 08:16:33 +01002093 if (!isdigit((unsigned char)arg[0].data.str.area[pos])) {
Christopher Faulet29f72842019-12-11 15:52:32 +01002094 memprintf(err_msg, "invalid block position");
2095 return 0;
2096 }
2097 }
2098
2099 pos = strl2uic(arg[0].data.str.area, arg[0].data.str.data);
2100 if (pos < 0) {
2101 memprintf(err_msg, "block position must not be negative");
2102 return 0;
2103 }
2104 free(arg[0].data.str.area);
2105 arg[0].type = ARGT_SINT;
2106 arg[0].data.sint = pos;
2107 }
2108
2109 return 1;
2110}
2111
2112
2113/* Note: must not be declared <const> as its list will be overwritten.
Ilya Shipitsind4259502020-04-08 01:07:56 +05002114 * Note: htx sample fetches should only used for development purpose.
Christopher Faulet29f72842019-12-11 15:52:32 +01002115 */
2116static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet01f44452020-01-08 14:23:40 +01002117 { "internal.strm.is_htx", smp_fetch_is_htx, 0, NULL, SMP_T_BOOL, SMP_USE_L6REQ },
Christopher Faulet29f72842019-12-11 15:52:32 +01002118
Christopher Faulet01f44452020-01-08 14:23:40 +01002119 { "internal.htx.nbblks", smp_fetch_htx_nbblks, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2120 { "internal.htx.size", smp_fetch_htx_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2121 { "internal.htx.data", smp_fetch_htx_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2122 { "internal.htx.used", smp_fetch_htx_used, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2123 { "internal.htx.free", smp_fetch_htx_free, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2124 { "internal.htx.free_data", smp_fetch_htx_free_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2125 { "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 +01002126
Christopher Faulet01f44452020-01-08 14:23:40 +01002127 { "internal.htx_blk.type", smp_fetch_htx_blk_type, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
2128 { "internal.htx_blk.size", smp_fetch_htx_blk_size, ARG1(1,STR), val_blk_arg, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2129 { "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},
2130 { "internal.htx_blk.hdrname", smp_fetch_htx_blk_hdrname, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
2131 { "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 +01002132 { "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 +01002133
2134 { /* END */ },
2135}};
2136
2137INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);