blob: 0cb76766e009528b64b1cc51cc11d8d1755413bf [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
957static int http_htx_init(void)
958{
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100959 struct buffer chk;
960 struct ist raw;
961 int rc;
962 int err_code = 0;
963
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100964 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
965 if (!http_err_msgs[rc]) {
966 ha_alert("Internal error: no message defined for HTTP return code %d", rc);
967 err_code |= ERR_ALERT | ERR_FATAL;
968 continue;
969 }
970
971 raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc]));
972 if (!http_str_to_htx(&chk, raw)) {
973 ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n",
974 http_err_codes[rc]);
975 err_code |= ERR_ALERT | ERR_FATAL;
976 }
Christopher Fauletf7346382019-07-17 22:02:08 +0200977 http_err_chunks[rc] = chk;
Christopher Fauleta7b677c2018-11-29 16:48:49 +0100978 }
979end:
980 return err_code;
981}
982
Christopher Faulet58857752020-01-15 15:19:50 +0100983static void http_htx_deinit(void)
984{
Christopher Faulet35cd81d2020-01-15 11:22:56 +0100985 struct http_errors *http_errs, *http_errsb;
Christopher Faulet58857752020-01-15 15:19:50 +0100986 struct ebpt_node *node, *next;
987 struct http_error *http_err;
988
989 node = ebpt_first(&http_error_messages);
990 while (node) {
991 next = ebpt_next(node);
992 ebpt_delete(node);
993 http_err = container_of(node, typeof(*http_err), node);
994 chunk_destroy(&http_err->msg);
995 free(node->key);
996 free(http_err);
997 node = next;
998 }
Christopher Faulet35cd81d2020-01-15 11:22:56 +0100999
1000 list_for_each_entry_safe(http_errs, http_errsb, &http_errors_list, list) {
1001 free(http_errs->conf.file);
1002 free(http_errs->id);
1003 LIST_DEL(&http_errs->list);
1004 free(http_errs);
1005 }
Christopher Faulet58857752020-01-15 15:19:50 +01001006}
1007
Christopher Fauleta7b677c2018-11-29 16:48:49 +01001008REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init);
Christopher Faulet58857752020-01-15 15:19:50 +01001009REGISTER_POST_DEINIT(http_htx_deinit);
Christopher Faulet29f72842019-12-11 15:52:32 +01001010
Christopher Faulet58857752020-01-15 15:19:50 +01001011/* Reads content of the error file <file> and convert it into an HTX message. On
1012 * success, the HTX message is returned. On error, NULL is returned and an error
1013 * message is written into the <errmsg> buffer.
Christopher Faulet5031ef52020-01-15 11:22:07 +01001014 */
Christopher Faulet58857752020-01-15 15:19:50 +01001015struct buffer *http_load_errorfile(const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +01001016{
Christopher Faulet58857752020-01-15 15:19:50 +01001017 struct buffer *buf = NULL;
1018 struct buffer chk;
1019 struct ebpt_node *node;
1020 struct http_error *http_err;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001021 struct stat stat;
1022 char *err = NULL;
1023 int errnum, errlen;
1024 int fd = -1;
Christopher Faulet58857752020-01-15 15:19:50 +01001025
1026 /* already loaded */
1027 node = ebis_lookup_len(&http_error_messages, file, strlen(file));
1028 if (node) {
1029 http_err = container_of(node, typeof(*http_err), node);
1030 buf = &http_err->msg;
1031 goto out;
1032 }
Christopher Faulet5031ef52020-01-15 11:22:07 +01001033
Christopher Faulet58857752020-01-15 15:19:50 +01001034 /* Read the error file content */
Christopher Faulet5031ef52020-01-15 11:22:07 +01001035 fd = open(file, O_RDONLY);
1036 if ((fd < 0) || (fstat(fd, &stat) < 0)) {
1037 memprintf(errmsg, "error opening file '%s'.", file);
1038 goto out;
1039 }
1040
1041 if (stat.st_size <= global.tune.bufsize)
1042 errlen = stat.st_size;
1043 else {
1044 ha_warning("custom error message file '%s' larger than %d bytes. Truncating.\n",
1045 file, global.tune.bufsize);
1046 errlen = global.tune.bufsize;
1047 }
1048
1049 err = malloc(errlen);
1050 if (!err) {
1051 memprintf(errmsg, "out of memory.");
1052 goto out;
1053 }
1054
1055 errnum = read(fd, err, errlen);
1056 if (errnum != errlen) {
1057 memprintf(errmsg, "error reading file '%s'.", file);
1058 goto out;
1059 }
1060
Christopher Faulet58857752020-01-15 15:19:50 +01001061 /* Create the node corresponding to the error file */
1062 http_err = calloc(1, sizeof(*http_err));
1063 if (!http_err) {
1064 memprintf(errmsg, "out of memory.");
1065 goto out;
1066 }
1067 http_err->node.key = strdup(file);
1068 if (!http_err->node.key) {
1069 memprintf(errmsg, "out of memory.");
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001070 free(http_err);
Christopher Faulet58857752020-01-15 15:19:50 +01001071 goto out;
1072 }
1073
1074 /* Convert the error file into an HTX message */
1075 if (!http_str_to_htx(&chk, ist2(err, errlen))) {
Christopher Faulet5031ef52020-01-15 11:22:07 +01001076 memprintf(errmsg, "unable to convert custom error message file '%s' in HTX.", file);
Christopher Faulet58857752020-01-15 15:19:50 +01001077 free(http_err->node.key);
1078 free(http_err);
Christopher Faulet5031ef52020-01-15 11:22:07 +01001079 goto out;
1080 }
1081
Christopher Faulet58857752020-01-15 15:19:50 +01001082 /* Insert the node in the tree and return the HTX message */
1083 http_err->msg = chk;
1084 ebis_insert(&http_error_messages, &http_err->node);
1085 buf = &http_err->msg;
1086
Christopher Faulet5031ef52020-01-15 11:22:07 +01001087 out:
1088 if (fd >= 0)
1089 close(fd);
1090 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +01001091 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001092}
1093
Ilya Shipitsind4259502020-04-08 01:07:56 +05001094/* Convert the raw http message <msg> into an HTX message. On success, the HTX
Christopher Faulet58857752020-01-15 15:19:50 +01001095 * message is returned. On error, NULL is returned and an error message is
1096 * written into the <errmsg> buffer.
Christopher Fauletbdf65262020-01-16 15:51:59 +01001097 */
Christopher Faulet58857752020-01-15 15:19:50 +01001098struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +01001099{
Christopher Faulet58857752020-01-15 15:19:50 +01001100 struct buffer *buf = NULL;
1101 struct buffer chk;
1102 struct ebpt_node *node;
1103 struct http_error *http_err;
1104
1105 /* already loaded */
1106 node = ebis_lookup_len(&http_error_messages, key, strlen(key));
1107 if (node) {
1108 http_err = container_of(node, typeof(*http_err), node);
1109 buf = &http_err->msg;
1110 goto out;
1111 }
1112 /* Create the node corresponding to the error file */
1113 http_err = calloc(1, sizeof(*http_err));
1114 if (!http_err) {
1115 memprintf(errmsg, "out of memory.");
1116 goto out;
1117 }
1118 http_err->node.key = strdup(key);
1119 if (!http_err->node.key) {
1120 memprintf(errmsg, "out of memory.");
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001121 free(http_err);
Christopher Faulet58857752020-01-15 15:19:50 +01001122 goto out;
1123 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001124
1125 /* Convert the error file into an HTX message */
Christopher Faulet58857752020-01-15 15:19:50 +01001126 if (!http_str_to_htx(&chk, msg)) {
Christopher Fauletbdf65262020-01-16 15:51:59 +01001127 memprintf(errmsg, "unable to convert message in HTX.");
Christopher Faulet58857752020-01-15 15:19:50 +01001128 free(http_err->node.key);
1129 free(http_err);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001130 goto out;
1131 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001132
Christopher Faulet58857752020-01-15 15:19:50 +01001133 /* Insert the node in the tree and return the HTX message */
1134 http_err->msg = chk;
1135 ebis_insert(&http_error_messages, &http_err->node);
1136 buf = &http_err->msg;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001137 out:
Christopher Faulet58857752020-01-15 15:19:50 +01001138 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001139}
1140
Christopher Faulet5031ef52020-01-15 11:22:07 +01001141/* This function parses the raw HTTP error file <file> for the status code
Christopher Faulet58857752020-01-15 15:19:50 +01001142 * <status>. It returns NULL if there is any error, otherwise it return the
1143 * corresponding HTX message.
Christopher Faulet5031ef52020-01-15 11:22:07 +01001144 */
Christopher Faulet58857752020-01-15 15:19:50 +01001145struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg)
Christopher Faulet5031ef52020-01-15 11:22:07 +01001146{
Christopher Faulet58857752020-01-15 15:19:50 +01001147 struct buffer *buf = NULL;
1148 int rc;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001149
1150 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1151 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +01001152 buf = http_load_errorfile(file, errmsg);
Christopher Faulet5031ef52020-01-15 11:22:07 +01001153 break;
1154 }
1155 }
1156
1157 if (rc >= HTTP_ERR_SIZE)
1158 memprintf(errmsg, "status code '%d' not handled.", status);
Christopher Faulet58857752020-01-15 15:19:50 +01001159 return buf;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001160}
1161
1162/* This function creates HTX error message corresponding to a redirect message
1163 * for the status code <status>. <url> is used as location url for the
Christopher Faulet58857752020-01-15 15:19:50 +01001164 * redirect. <errloc> is used to know if it is a 302 or a 303 redirect. It
1165 * returns NULL if there is any error, otherwise it return the corresponding HTX
1166 * message.
Christopher Fauletbdf65262020-01-16 15:51:59 +01001167 */
Christopher Faulet58857752020-01-15 15:19:50 +01001168struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg)
Christopher Fauletbdf65262020-01-16 15:51:59 +01001169{
Christopher Faulet58857752020-01-15 15:19:50 +01001170 struct buffer *buf = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001171 const char *msg;
Christopher Faulet58857752020-01-15 15:19:50 +01001172 char *key = NULL, *err = NULL;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001173 int rc, errlen;
Christopher Fauletbdf65262020-01-16 15:51:59 +01001174
1175 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1176 if (http_err_codes[rc] == status) {
Christopher Faulet58857752020-01-15 15:19:50 +01001177 /* Create the error key */
1178 if (!memprintf(&key, "errorloc%d %s", errloc, url)) {
1179 memprintf(errmsg, "out of memory.");
1180 goto out;
1181 }
Christopher Fauletbdf65262020-01-16 15:51:59 +01001182 /* Create the error message */
1183 msg = (errloc == 302 ? HTTP_302 : HTTP_303);
1184 errlen = strlen(msg) + strlen(url) + 5;
1185 err = malloc(errlen);
1186 if (!err) {
1187 memprintf(errmsg, "out of memory.");
1188 goto out;
1189 }
1190 errlen = snprintf(err, errlen, "%s%s\r\n\r\n", msg, url);
1191
1192 /* Load it */
Christopher Faulet58857752020-01-15 15:19:50 +01001193 buf = http_load_errormsg(key, ist2(err, errlen), errmsg);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001194 break;
1195 }
1196 }
1197
1198 if (rc >= HTTP_ERR_SIZE)
1199 memprintf(errmsg, "status code '%d' not handled.", status);
1200out:
Christopher Faulet58857752020-01-15 15:19:50 +01001201 free(key);
Christopher Fauletbdf65262020-01-16 15:51:59 +01001202 free(err);
Christopher Faulet58857752020-01-15 15:19:50 +01001203 return buf;
Christopher Faulet5031ef52020-01-15 11:22:07 +01001204}
1205
Christopher Faulet07f41f72020-01-16 16:16:06 +01001206/* Parses the "errorloc[302|303]" proxy keyword */
1207static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx,
1208 struct proxy *defpx, const char *file, int line,
1209 char **errmsg)
1210{
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001211 struct conf_errors *conf_err;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001212 struct buffer *msg;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001213 int errloc, status;
1214 int ret = 0;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001215
1216 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1217 ret = 1;
1218 goto out;
1219 }
1220
1221 if (*(args[1]) == 0 || *(args[2]) == 0) {
1222 memprintf(errmsg, "%s : expects <status_code> and <url> as arguments.\n", args[0]);
1223 ret = -1;
1224 goto out;
1225 }
1226
1227 status = atol(args[1]);
1228 errloc = (!strcmp(args[0], "errorloc303") ? 303 : 302);
1229 msg = http_parse_errorloc(errloc, status, args[2], errmsg);
1230 if (!msg) {
1231 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1232 ret = -1;
1233 goto out;
1234 }
1235
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001236 conf_err = calloc(1, sizeof(*conf_err));
1237 if (!conf_err) {
1238 memprintf(errmsg, "%s : out of memory.", args[0]);
1239 ret = -1;
1240 goto out;
1241 }
1242 conf_err->type = 1;
1243 conf_err->info.errorfile.status = status;
1244 conf_err->info.errorfile.msg = msg;
1245 conf_err->file = strdup(file);
1246 conf_err->line = line;
1247 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001248
1249 out:
1250 return ret;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001251
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001252}
Christopher Faulet07f41f72020-01-16 16:16:06 +01001253
1254/* Parses the "errorfile" proxy keyword */
1255static int proxy_parse_errorfile(char **args, int section, struct proxy *curpx,
1256 struct proxy *defpx, const char *file, int line,
1257 char **errmsg)
1258{
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001259 struct conf_errors *conf_err;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001260 struct buffer *msg;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001261 int status;
1262 int ret = 0;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001263
1264 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1265 ret = 1;
1266 goto out;
1267 }
1268
1269 if (*(args[1]) == 0 || *(args[2]) == 0) {
1270 memprintf(errmsg, "%s : expects <status_code> and <file> as arguments.\n", args[0]);
1271 ret = -1;
1272 goto out;
1273 }
1274
1275 status = atol(args[1]);
1276 msg = http_parse_errorfile(status, args[2], errmsg);
1277 if (!msg) {
1278 memprintf(errmsg, "%s : %s", args[0], *errmsg);
1279 ret = -1;
1280 goto out;
1281 }
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001282
1283 conf_err = calloc(1, sizeof(*conf_err));
1284 if (!conf_err) {
1285 memprintf(errmsg, "%s : out of memory.", args[0]);
1286 ret = -1;
1287 goto out;
1288 }
1289 conf_err->type = 1;
1290 conf_err->info.errorfile.status = status;
1291 conf_err->info.errorfile.msg = msg;
1292 conf_err->file = strdup(file);
1293 conf_err->line = line;
1294 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
1295
1296 out:
1297 return ret;
1298
1299}
1300
1301/* Parses the "errorfiles" proxy keyword */
1302static int proxy_parse_errorfiles(char **args, int section, struct proxy *curpx,
1303 struct proxy *defpx, const char *file, int line,
1304 char **err)
1305{
1306 struct conf_errors *conf_err = NULL;
1307 char *name = NULL;
1308 int rc, ret = 0;
1309
1310 if (warnifnotcap(curpx, PR_CAP_FE | PR_CAP_BE, file, line, args[0], NULL)) {
1311 ret = 1;
1312 goto out;
1313 }
1314
1315 if (!*(args[1])) {
1316 memprintf(err, "%s : expects <name> as argument.", args[0]);
1317 ret = -1;
1318 goto out;
1319 }
1320
1321 name = strdup(args[1]);
1322 conf_err = calloc(1, sizeof(*conf_err));
1323 if (!name || !conf_err) {
1324 memprintf(err, "%s : out of memory.", args[0]);
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001325 goto error;
1326 }
1327 conf_err->type = 0;
1328
1329 conf_err->info.errorfiles.name = name;
1330 if (!*(args[2])) {
1331 for (rc = 0; rc < HTTP_ERR_SIZE; rc++)
1332 conf_err->info.errorfiles.status[rc] = 1;
1333 }
1334 else {
1335 int cur_arg, status;
1336 for (cur_arg = 2; *(args[cur_arg]); cur_arg++) {
1337 status = atol(args[cur_arg]);
1338
1339 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1340 if (http_err_codes[rc] == status) {
1341 conf_err->info.errorfiles.status[rc] = 2;
1342 break;
1343 }
1344 }
1345 if (rc >= HTTP_ERR_SIZE) {
1346 memprintf(err, "%s : status code '%d' not handled.", args[0], status);
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001347 goto error;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001348 }
1349 }
1350 }
1351 conf_err->file = strdup(file);
1352 conf_err->line = line;
1353 LIST_ADDQ(&curpx->conf.errors, &conf_err->list);
1354 out:
1355 return ret;
1356
1357 error:
1358 free(name);
1359 free(conf_err);
Christopher Faulet7cde96c2020-01-21 10:10:11 +01001360 ret = -1;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001361 goto out;
1362}
1363
1364/* Check "errorfiles" proxy keyword */
1365static int proxy_check_errors(struct proxy *px)
1366{
1367 struct conf_errors *conf_err, *conf_err_back;
1368 struct http_errors *http_errs;
1369 int rc, err = 0;
1370
1371 list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
1372 if (conf_err->type == 1) {
1373 /* errorfile */
1374 rc = http_get_status_idx(conf_err->info.errorfile.status);
1375 px->errmsg[rc] = conf_err->info.errorfile.msg;
1376 }
1377 else {
1378 /* errorfiles */
1379 list_for_each_entry(http_errs, &http_errors_list, list) {
1380 if (strcmp(http_errs->id, conf_err->info.errorfiles.name) == 0)
1381 break;
1382 }
Christopher Faulet07f41f72020-01-16 16:16:06 +01001383
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001384 /* unknown http-errors section */
1385 if (&http_errs->list == &http_errors_list) {
1386 ha_alert("config : proxy '%s': unknown http-errors section '%s' (at %s:%d).\n",
1387 px->id, conf_err->info.errorfiles.name, conf_err->file, conf_err->line);
1388 err |= ERR_ALERT | ERR_FATAL;
1389 free(conf_err->info.errorfiles.name);
1390 goto next;
1391 }
1392
1393 free(conf_err->info.errorfiles.name);
1394 for (rc = 0; rc < HTTP_ERR_SIZE; rc++) {
1395 if (conf_err->info.errorfiles.status[rc] > 0) {
1396 if (http_errs->errmsg[rc])
1397 px->errmsg[rc] = http_errs->errmsg[rc];
1398 else if (conf_err->info.errorfiles.status[rc] == 2)
1399 ha_warning("config: proxy '%s' : status '%d' not declared in"
1400 " http-errors section '%s' (at %s:%d).\n",
1401 px->id, http_err_codes[rc], http_errs->id,
1402 conf_err->file, conf_err->line);
1403 }
1404 }
1405 }
1406 next:
1407 LIST_DEL(&conf_err->list);
1408 free(conf_err->file);
1409 free(conf_err);
1410 }
Christopher Faulet07f41f72020-01-16 16:16:06 +01001411
1412 out:
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001413 return err;
1414}
1415
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001416static int post_check_errors()
1417{
1418 struct ebpt_node *node;
1419 struct http_error *http_err;
1420 struct htx *htx;
1421 int err_code = 0;
1422
1423 node = ebpt_first(&http_error_messages);
1424 while (node) {
1425 http_err = container_of(node, typeof(*http_err), node);
1426 if (b_is_null(&http_err->msg))
1427 goto next;
1428 htx = htxbuf(&http_err->msg);
1429 if (htx_free_data_space(htx) < global.tune.maxrewrite) {
1430 ha_warning("config: errorfile '%s' runs over the buffer space"
1431 " reserved to headers rewritting. It may lead to internal errors if "
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001432 " http-after-response rules are evaluated on this message.\n",
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001433 (char *)node->key);
1434 err_code |= ERR_WARN;
1435 }
1436 next:
1437 node = ebpt_next(node);
1438 }
1439
1440 return err_code;
1441}
1442
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001443int proxy_dup_default_conf_errors(struct proxy *curpx, struct proxy *defpx, char **errmsg)
1444{
1445 struct conf_errors *conf_err, *new_conf_err = NULL;
1446 int ret = 0;
1447
1448 list_for_each_entry(conf_err, &defpx->conf.errors, list) {
1449 new_conf_err = calloc(1, sizeof(*new_conf_err));
1450 if (!new_conf_err) {
1451 memprintf(errmsg, "unable to duplicate default errors (out of memory).");
1452 goto out;
1453 }
1454 new_conf_err->type = conf_err->type;
1455 if (conf_err->type == 1) {
1456 new_conf_err->info.errorfile.status = conf_err->info.errorfile.status;
1457 new_conf_err->info.errorfile.msg = conf_err->info.errorfile.msg;
1458 }
1459 else {
1460 new_conf_err->info.errorfiles.name = strdup(conf_err->info.errorfiles.name);
1461 if (!new_conf_err->info.errorfiles.name) {
1462 memprintf(errmsg, "unable to duplicate default errors (out of memory).");
1463 goto out;
1464 }
1465 memcpy(&new_conf_err->info.errorfiles.status, &conf_err->info.errorfiles.status,
1466 sizeof(conf_err->info.errorfiles.status));
1467 }
1468 new_conf_err->file = strdup(conf_err->file);
1469 new_conf_err->line = conf_err->line;
1470 LIST_ADDQ(&curpx->conf.errors, &new_conf_err->list);
1471 new_conf_err = NULL;
1472 }
1473 ret = 1;
1474
1475 out:
1476 free(new_conf_err);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001477 return ret;
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001478}
1479
1480void proxy_release_conf_errors(struct proxy *px)
1481{
1482 struct conf_errors *conf_err, *conf_err_back;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001483
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001484 list_for_each_entry_safe(conf_err, conf_err_back, &px->conf.errors, list) {
1485 if (conf_err->type == 0)
1486 free(conf_err->info.errorfiles.name);
1487 LIST_DEL(&conf_err->list);
1488 free(conf_err->file);
1489 free(conf_err);
1490 }
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001491}
1492
1493/*
1494 * Parse an <http-errors> section.
1495 * Returns the error code, 0 if OK, or any combination of :
1496 * - ERR_ABORT: must abort ASAP
1497 * - ERR_FATAL: we can continue parsing but not start the service
1498 * - ERR_WARN: a warning has been emitted
1499 * - ERR_ALERT: an alert has been emitted
1500 * Only the two first ones can stop processing, the two others are just
1501 * indicators.
1502 */
1503static int cfg_parse_http_errors(const char *file, int linenum, char **args, int kwm)
1504{
1505 static struct http_errors *curr_errs = NULL;
1506 int err_code = 0;
1507 const char *err;
1508 char *errmsg = NULL;
1509
1510 if (strcmp(args[0], "http-errors") == 0) { /* new errors section */
1511 if (!*args[1]) {
1512 ha_alert("parsing [%s:%d] : missing name for http-errors section.\n", file, linenum);
1513 err_code |= ERR_ALERT | ERR_ABORT;
1514 goto out;
1515 }
1516
1517 err = invalid_char(args[1]);
1518 if (err) {
1519 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
1520 file, linenum, *err, args[0], args[1]);
1521 err_code |= ERR_ALERT | ERR_FATAL;
1522 }
1523
1524 list_for_each_entry(curr_errs, &http_errors_list, list) {
1525 /* Error if two errors section owns the same name */
1526 if (strcmp(curr_errs->id, args[1]) == 0) {
1527 ha_alert("parsing [%s:%d]: http-errors section '%s' already exists (declared at %s:%d).\n",
1528 file, linenum, args[1], curr_errs->conf.file, curr_errs->conf.line);
1529 err_code |= ERR_ALERT | ERR_FATAL;
1530 }
1531 }
1532
1533 if ((curr_errs = calloc(1, sizeof(*curr_errs))) == NULL) {
1534 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
1535 err_code |= ERR_ALERT | ERR_ABORT;
1536 goto out;
1537 }
1538
1539 LIST_ADDQ(&http_errors_list, &curr_errs->list);
1540 curr_errs->id = strdup(args[1]);
1541 curr_errs->conf.file = strdup(file);
1542 curr_errs->conf.line = linenum;
1543 }
1544 else if (!strcmp(args[0], "errorfile")) { /* error message from a file */
1545 struct buffer *msg;
1546 int status, rc;
1547
1548 if (*(args[1]) == 0 || *(args[2]) == 0) {
1549 ha_alert("parsing [%s:%d] : %s: expects <status_code> and <file> as arguments.\n",
1550 file, linenum, args[0]);
1551 err_code |= ERR_ALERT | ERR_FATAL;
1552 goto out;
1553 }
1554
1555 status = atol(args[1]);
1556 msg = http_parse_errorfile(status, args[2], &errmsg);
1557 if (!msg) {
1558 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
1559 err_code |= ERR_ALERT | ERR_FATAL;
1560 goto out;
1561 }
1562 rc = http_get_status_idx(status);
1563 curr_errs->errmsg[rc] = msg;
1564 }
1565 else if (*args[0] != 0) {
1566 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
1567 err_code |= ERR_ALERT | ERR_FATAL;
1568 goto out;
1569 }
1570
1571out:
1572 free(errmsg);
1573 return err_code;
Christopher Faulet07f41f72020-01-16 16:16:06 +01001574}
1575
1576static struct cfg_kw_list cfg_kws = {ILH, {
1577 { CFG_LISTEN, "errorloc", proxy_parse_errorloc },
1578 { CFG_LISTEN, "errorloc302", proxy_parse_errorloc },
1579 { CFG_LISTEN, "errorloc303", proxy_parse_errorloc },
1580 { CFG_LISTEN, "errorfile", proxy_parse_errorfile },
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001581 { CFG_LISTEN, "errorfiles", proxy_parse_errorfiles },
Christopher Faulet07f41f72020-01-16 16:16:06 +01001582 { 0, NULL, NULL },
1583}};
1584
1585INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Faulet76edc0f2020-01-13 15:52:01 +01001586REGISTER_POST_PROXY_CHECK(proxy_check_errors);
Christopher Faulet0a589fd2020-01-22 14:47:04 +01001587REGISTER_POST_CHECK(post_check_errors);
Christopher Faulet07f41f72020-01-16 16:16:06 +01001588
Christopher Faulet35cd81d2020-01-15 11:22:56 +01001589REGISTER_CONFIG_SECTION("http-errors", cfg_parse_http_errors, NULL);
1590
Christopher Faulet29f72842019-12-11 15:52:32 +01001591/************************************************************************/
1592/* HTX sample fetches */
1593/************************************************************************/
1594
1595/* Returns 1 if a stream is an HTX stream. Otherwise, it returns 0. */
1596static int
1597smp_fetch_is_htx(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1598{
1599 if (!smp->strm)
1600 return 0;
1601
1602 smp->data.u.sint = !!IS_HTX_STRM(smp->strm);
1603 smp->data.type = SMP_T_BOOL;
1604 return 1;
1605}
1606
1607/* Returns the number of blocks in an HTX message. The channel is chosen
1608 * depending on the sample direction. */
1609static int
1610smp_fetch_htx_nbblks(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1611{
1612 struct channel *chn;
1613 struct htx *htx;
1614
1615 if (!smp->strm)
1616 return 0;
1617
1618 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001619 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001620 if (!htx)
1621 return 0;
1622
1623 smp->data.u.sint = htx_nbblks(htx);
1624 smp->data.type = SMP_T_SINT;
1625 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1626 return 1;
1627}
1628
1629/* Returns the size of an HTX message. The channel is chosen depending on the
1630 * sample direction. */
1631static int
1632smp_fetch_htx_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1633{
1634 struct channel *chn;
1635 struct htx *htx;
1636
1637 if (!smp->strm)
1638 return 0;
1639
1640 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001641 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001642 if (!htx)
1643 return 0;
1644
1645 smp->data.u.sint = htx->size;
1646 smp->data.type = SMP_T_SINT;
1647 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1648 return 1;
1649}
1650
1651/* Returns the data size of an HTX message. The channel is chosen depending on the
1652 * sample direction. */
1653static int
1654smp_fetch_htx_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1655{
1656 struct channel *chn;
1657 struct htx *htx;
1658
1659 if (!smp->strm)
1660 return 0;
1661
1662 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001663 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001664 if (!htx)
1665 return 0;
1666
1667 smp->data.u.sint = htx->data;
1668 smp->data.type = SMP_T_SINT;
1669 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1670 return 1;
1671}
1672
1673/* Returns the used space (data+meta) of an HTX message. The channel is chosen
1674 * depending on the sample direction. */
1675static int
1676smp_fetch_htx_used(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1677{
1678 struct channel *chn;
1679 struct htx *htx;
1680
1681 if (!smp->strm)
1682 return 0;
1683
1684 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001685 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001686 if (!htx)
1687 return 0;
1688
1689 smp->data.u.sint = htx_used_space(htx);
1690 smp->data.type = SMP_T_SINT;
1691 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1692 return 1;
1693}
1694
1695/* Returns the free space (size-used) of an HTX message. The channel is chosen
1696 * depending on the sample direction. */
1697static int
1698smp_fetch_htx_free(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1699{
1700 struct channel *chn;
1701 struct htx *htx;
1702
1703 if (!smp->strm)
1704 return 0;
1705
1706 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001707 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001708 if (!htx)
1709 return 0;
1710
1711 smp->data.u.sint = htx_free_space(htx);
1712 smp->data.type = SMP_T_SINT;
1713 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1714 return 1;
1715}
1716
1717/* Returns the free space for data (free-sizeof(blk)) of an HTX message. The
1718 * channel is chosen depending on the sample direction. */
1719static int
1720smp_fetch_htx_free_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1721{
1722 struct channel *chn;
1723 struct htx *htx;
1724
1725 if (!smp->strm)
1726 return 0;
1727
1728 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001729 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001730 if (!htx)
1731 return 0;
1732
1733 smp->data.u.sint = htx_free_data_space(htx);
1734 smp->data.type = SMP_T_SINT;
1735 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1736 return 1;
1737}
1738
1739/* Returns 1 if the HTX message contains an EOM block. Otherwise it returns
1740 * 0. Concretely, it only checks the tail. The channel is chosen depending on
1741 * the sample direction. */
1742static int
1743smp_fetch_htx_has_eom(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1744{
1745 struct channel *chn;
1746 struct htx *htx;
1747
1748 if (!smp->strm)
1749 return 0;
1750
1751 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001752 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001753 if (!htx)
1754 return 0;
1755
1756 smp->data.u.sint = (htx_get_tail_type(htx) == HTX_BLK_EOM);
1757 smp->data.type = SMP_T_BOOL;
1758 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1759 return 1;
1760}
1761
1762/* Returns the type of a specific HTX block, if found in the message. Otherwise
1763 * HTX_BLK_UNUSED is returned. Any positive integer (>= 0) is supported or
1764 * "head", "tail" or "first". The channel is chosen depending on the sample
1765 * direction. */
1766static int
1767smp_fetch_htx_blk_type(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1768{
1769 struct channel *chn;
1770 struct htx *htx;
1771 enum htx_blk_type type;
1772 int32_t pos;
1773
1774 if (!smp->strm || !arg_p)
1775 return 0;
1776
1777 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001778 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001779 if (!htx)
1780 return 0;
1781
1782 pos = arg_p[0].data.sint;
1783 if (pos == -1)
1784 type = htx_get_head_type(htx);
1785 else if (pos == -2)
1786 type = htx_get_tail_type(htx);
1787 else if (pos == -3)
1788 type = htx_get_first_type(htx);
1789 else
1790 type = ((pos >= htx->head && pos <= htx->tail)
1791 ? htx_get_blk_type(htx_get_blk(htx, pos))
1792 : HTX_BLK_UNUSED);
1793
1794 chunk_initstr(&smp->data.u.str, htx_blk_type_str(type));
1795 smp->data.type = SMP_T_STR;
1796 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1797 return 1;
1798}
1799
1800/* Returns the size of a specific HTX block, if found in the message. Otherwise
1801 * 0 is returned. Any positive integer (>= 0) is supported or "head", "tail" or
1802 * "first". The channel is chosen depending on the sample direction. */
1803static int
1804smp_fetch_htx_blk_size(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1805{
1806 struct channel *chn;
1807 struct htx *htx;
1808 struct htx_blk *blk;
1809 int32_t pos;
1810
1811 if (!smp->strm || !arg_p)
1812 return 0;
1813
1814 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001815 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001816 if (!htx)
1817 return 0;
1818
1819 pos = arg_p[0].data.sint;
1820 if (pos == -1)
1821 blk = htx_get_head_blk(htx);
1822 else if (pos == -2)
1823 blk = htx_get_tail_blk(htx);
1824 else if (pos == -3)
1825 blk = htx_get_first_blk(htx);
1826 else
1827 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1828
1829 smp->data.u.sint = (blk ? htx_get_blksz(blk) : 0);
1830 smp->data.type = SMP_T_SINT;
1831 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1832 return 1;
1833}
1834
1835/* Returns the start-line if the selected HTX block exists and is a
1836 * start-line. Otherwise 0 an empty string. Any positive integer (>= 0) is
1837 * supported or "head", "tail" or "first". The channel is chosen depending on
1838 * the sample direction. */
1839static int
1840smp_fetch_htx_blk_stline(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1841{
1842 struct buffer *temp;
1843 struct channel *chn;
1844 struct htx *htx;
1845 struct htx_blk *blk;
1846 struct htx_sl *sl;
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 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL)) {
1868 smp->data.u.str.size = 0;
1869 smp->data.u.str.area = "";
1870 smp->data.u.str.data = 0;
1871 }
1872 else {
1873 sl = htx_get_blk_ptr(htx, blk);
1874
1875 temp = get_trash_chunk();
1876 chunk_istcat(temp, htx_sl_p1(sl));
1877 temp->area[temp->data++] = ' ';
1878 chunk_istcat(temp, htx_sl_p2(sl));
1879 temp->area[temp->data++] = ' ';
1880 chunk_istcat(temp, htx_sl_p3(sl));
1881
1882 smp->data.u.str = *temp;
1883 }
1884
1885 smp->data.type = SMP_T_STR;
1886 smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1887 return 1;
1888}
1889
1890/* Returns the header name if the selected HTX block exists and is a header or a
1891 * trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1892 * supported or "head", "tail" or "first". The channel is chosen depending on
1893 * the sample direction. */
1894static int
1895smp_fetch_htx_blk_hdrname(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1896{
1897 struct channel *chn;
1898 struct htx *htx;
1899 struct htx_blk *blk;
1900 int32_t pos;
1901
1902 if (!smp->strm || !arg_p)
1903 return 0;
1904
1905 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001906 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001907 if (!htx)
1908 return 0;
1909
1910 pos = arg_p[0].data.sint;
1911 if (pos == -1)
1912 blk = htx_get_head_blk(htx);
1913 else if (pos == -2)
1914 blk = htx_get_tail_blk(htx);
1915 else if (pos == -3)
1916 blk = htx_get_first_blk(htx);
1917 else
1918 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1919
1920 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1921 smp->data.u.str.size = 0;
1922 smp->data.u.str.area = "";
1923 smp->data.u.str.data = 0;
1924 }
1925 else {
1926 struct ist name = htx_get_blk_name(htx, blk);
1927
1928 chunk_initlen(&smp->data.u.str, name.ptr, name.len, name.len);
1929 }
1930 smp->data.type = SMP_T_STR;
1931 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1932 return 1;
1933}
1934
1935/* Returns the header value if the selected HTX block exists and is a header or
1936 * a trailer. Otherwise 0 an empty string. Any positive integer (>= 0) is
1937 * supported or "head", "tail" or "first". The channel is chosen depending on
1938 * the sample direction. */
1939static int
1940smp_fetch_htx_blk_hdrval(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
1941{
1942 struct channel *chn;
1943 struct htx *htx;
1944 struct htx_blk *blk;
1945 int32_t pos;
1946
1947 if (!smp->strm || !arg_p)
1948 return 0;
1949
1950 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001951 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001952 if (!htx)
1953 return 0;
1954
1955 pos = arg_p[0].data.sint;
1956 if (pos == -1)
1957 blk = htx_get_head_blk(htx);
1958 else if (pos == -2)
1959 blk = htx_get_tail_blk(htx);
1960 else if (pos == -3)
1961 blk = htx_get_first_blk(htx);
1962 else
1963 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
1964
1965 if (!blk || (htx_get_blk_type(blk) != HTX_BLK_HDR && htx_get_blk_type(blk) != HTX_BLK_TLR)) {
1966 smp->data.u.str.size = 0;
1967 smp->data.u.str.area = "";
1968 smp->data.u.str.data = 0;
1969 }
1970 else {
1971 struct ist val = htx_get_blk_value(htx, blk);
1972
1973 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
1974 }
1975 smp->data.type = SMP_T_STR;
1976 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
1977 return 1;
1978}
1979
1980/* Returns the value if the selected HTX block exists and is a data
1981 * block. Otherwise 0 an empty string. Any positive integer (>= 0) is supported
1982 * or "head", "tail" or "first". The channel is chosen depending on the sample
1983 * direction. */
1984static int
Christopher Fauletc5db14c2020-01-08 14:51:03 +01001985smp_fetch_htx_blk_data(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
Christopher Faulet29f72842019-12-11 15:52:32 +01001986{
1987 struct channel *chn;
1988 struct htx *htx;
1989 struct htx_blk *blk;
1990 int32_t pos;
1991
1992 if (!smp->strm || !arg_p)
1993 return 0;
1994
1995 chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001996 htx = smp_prefetch_htx(smp, chn, NULL, 0);
Christopher Faulet29f72842019-12-11 15:52:32 +01001997 if (!htx)
1998 return 0;
1999
2000 pos = arg_p[0].data.sint;
2001 if (pos == -1)
2002 blk = htx_get_head_blk(htx);
2003 else if (pos == -2)
2004 blk = htx_get_tail_blk(htx);
2005 else if (pos == -3)
2006 blk = htx_get_first_blk(htx);
2007 else
2008 blk = ((pos >= htx->head && pos <= htx->tail) ? htx_get_blk(htx, pos) : NULL);
2009
2010 if (!blk || htx_get_blk_type(blk) != HTX_BLK_DATA) {
2011 smp->data.u.str.size = 0;
2012 smp->data.u.str.area = "";
2013 smp->data.u.str.data = 0;
2014 }
2015 else {
2016 struct ist val = htx_get_blk_value(htx, blk);
2017
2018 chunk_initlen(&smp->data.u.str, val.ptr, val.len, val.len);
2019 }
Christopher Faulet8178e402020-01-08 14:38:58 +01002020 smp->data.type = SMP_T_BIN;
Christopher Faulet29f72842019-12-11 15:52:32 +01002021 smp->flags = SMP_F_CONST | SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
2022 return 1;
2023}
2024
2025/* This function is used to validate the arguments passed to any "htx_blk" fetch
2026 * keywords. An argument is expected by these keywords. It must be a positive
2027 * integer or on of the following strings: "head", "tail" or "first". It returns
2028 * 0 on error, and a non-zero value if OK.
2029 */
2030int val_blk_arg(struct arg *arg, char **err_msg)
2031{
2032 if (arg[0].type != ARGT_STR || !arg[0].data.str.data) {
2033 memprintf(err_msg, "a block position is expected (> 0) or a special block name (head, tail, first)");
2034 return 0;
2035 }
2036 if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "head", 4)) {
2037 free(arg[0].data.str.area);
2038 arg[0].type = ARGT_SINT;
2039 arg[0].data.sint = -1;
2040 }
2041 else if (arg[0].data.str.data == 4 && !strncmp(arg[0].data.str.area, "tail", 4)) {
2042 free(arg[0].data.str.area);
2043 arg[0].type = ARGT_SINT;
2044 arg[0].data.sint = -2;
2045 }
2046 else if (arg[0].data.str.data == 5 && !strncmp(arg[0].data.str.area, "first", 5)) {
2047 free(arg[0].data.str.area);
2048 arg[0].type = ARGT_SINT;
2049 arg[0].data.sint = -3;
2050 }
2051 else {
2052 int pos;
2053
2054 for (pos = 0; pos < arg[0].data.str.data; pos++) {
Willy Tarreau90807112020-02-25 08:16:33 +01002055 if (!isdigit((unsigned char)arg[0].data.str.area[pos])) {
Christopher Faulet29f72842019-12-11 15:52:32 +01002056 memprintf(err_msg, "invalid block position");
2057 return 0;
2058 }
2059 }
2060
2061 pos = strl2uic(arg[0].data.str.area, arg[0].data.str.data);
2062 if (pos < 0) {
2063 memprintf(err_msg, "block position must not be negative");
2064 return 0;
2065 }
2066 free(arg[0].data.str.area);
2067 arg[0].type = ARGT_SINT;
2068 arg[0].data.sint = pos;
2069 }
2070
2071 return 1;
2072}
2073
2074
2075/* Note: must not be declared <const> as its list will be overwritten.
Ilya Shipitsind4259502020-04-08 01:07:56 +05002076 * Note: htx sample fetches should only used for development purpose.
Christopher Faulet29f72842019-12-11 15:52:32 +01002077 */
2078static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
Christopher Faulet01f44452020-01-08 14:23:40 +01002079 { "internal.strm.is_htx", smp_fetch_is_htx, 0, NULL, SMP_T_BOOL, SMP_USE_L6REQ },
Christopher Faulet29f72842019-12-11 15:52:32 +01002080
Christopher Faulet01f44452020-01-08 14:23:40 +01002081 { "internal.htx.nbblks", smp_fetch_htx_nbblks, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2082 { "internal.htx.size", smp_fetch_htx_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2083 { "internal.htx.data", smp_fetch_htx_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2084 { "internal.htx.used", smp_fetch_htx_used, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2085 { "internal.htx.free", smp_fetch_htx_free, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2086 { "internal.htx.free_data", smp_fetch_htx_free_data, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2087 { "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 +01002088
Christopher Faulet01f44452020-01-08 14:23:40 +01002089 { "internal.htx_blk.type", smp_fetch_htx_blk_type, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
2090 { "internal.htx_blk.size", smp_fetch_htx_blk_size, ARG1(1,STR), val_blk_arg, SMP_T_SINT, SMP_USE_HRQHV|SMP_USE_HRSHV},
2091 { "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},
2092 { "internal.htx_blk.hdrname", smp_fetch_htx_blk_hdrname, ARG1(1,STR), val_blk_arg, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV},
2093 { "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 +01002094 { "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 +01002095
2096 { /* END */ },
2097}};
2098
2099INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);