blob: d720b374d8f3720ec60acfad5de13ac52c99c7cd [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP samples fetching
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
19#include <common/base64.h>
20#include <common/chunk.h>
21#include <common/compat.h>
22#include <common/config.h>
23#include <common/debug.h>
Willy Tarreauafba57a2018-12-11 13:44:24 +010024#include <common/h1.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020025#include <common/http.h>
Willy Tarreaub96b77e2018-12-11 10:22:41 +010026#include <common/htx.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010027#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020028#include <common/memory.h>
29#include <common/standard.h>
30#include <common/version.h>
31
32#include <types/global.h>
33
34#include <proto/arg.h>
35#include <proto/auth.h>
Willy Tarreau538746a2018-12-11 10:59:20 +010036#include <proto/hdr_idx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020037#include <proto/http_fetch.h>
Christopher Fauletef453ed2018-10-24 21:39:27 +020038#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020039#include <proto/log.h>
40#include <proto/obj_type.h>
41#include <proto/proto_http.h>
42#include <proto/sample.h>
43#include <proto/stream.h>
44
45
46/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
47static THREAD_LOCAL struct hdr_ctx static_hdr_ctx;
Christopher Fauletef453ed2018-10-24 21:39:27 +020048static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
49
Willy Tarreau79e57332018-10-02 16:01:16 +020050
51/*
52 * Returns the data from Authorization header. Function may be called more
53 * than once so data is stored in txn->auth_data. When no header is found
54 * or auth method is unknown auth_method is set to HTTP_AUTH_WRONG to avoid
55 * searching again for something we are unable to find anyway. However, if
56 * the result if valid, the cache is not reused because we would risk to
57 * have the credentials overwritten by another stream in parallel.
58 */
59
Christopher Faulet311c7ea2018-10-24 21:41:55 +020060static int get_http_auth(struct sample *smp)
Willy Tarreau79e57332018-10-02 16:01:16 +020061{
Christopher Faulet311c7ea2018-10-24 21:41:55 +020062 struct stream *s = smp->strm;
Willy Tarreau79e57332018-10-02 16:01:16 +020063 struct http_txn *txn = s->txn;
64 struct buffer auth_method;
Willy Tarreau79e57332018-10-02 16:01:16 +020065 char *h, *p;
66 int len;
67
68#ifdef DEBUG_AUTH
69 printf("Auth for stream %p: %d\n", s, txn->auth.method);
70#endif
Willy Tarreau79e57332018-10-02 16:01:16 +020071 if (txn->auth.method == HTTP_AUTH_WRONG)
72 return 0;
73
74 txn->auth.method = HTTP_AUTH_WRONG;
75
Christopher Faulet311c7ea2018-10-24 21:41:55 +020076 if (IS_HTX_STRM(s) || (smp->px->mode == PR_MODE_TCP)) {
77 /* HTX version */
Christopher Faulet27ba2dc2018-12-05 11:53:24 +010078 struct htx *htx = htxbuf(&s->req.buf);
Christopher Faulet311c7ea2018-10-24 21:41:55 +020079 struct http_hdr_ctx ctx = { .blk = NULL };
80 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +020081
Christopher Faulet311c7ea2018-10-24 21:41:55 +020082 if (txn->flags & TX_USE_PX_CONN)
83 hdr = ist("Proxy-Authorization");
84 else
85 hdr = ist("Authorization");
86
Christopher Faulet311c7ea2018-10-24 21:41:55 +020087 ctx.blk = NULL;
88 if (!http_find_header(htx, hdr, &ctx, 0))
89 return 0;
90
91 p = memchr(ctx.value.ptr, ' ', ctx.value.len);
92 len = p - ctx.value.ptr;
93 if (!p || len <= 0)
94 return 0;
95
96 if (chunk_initlen(&auth_method, ctx.value.ptr, 0, len) != 1)
97 return 0;
98
99 chunk_initlen(&txn->auth.method_data, p + 1, 0, ctx.value.len - len - 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200100 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200101 else {
102 /* LEGACY version */
103 struct hdr_ctx ctx = { .idx = 0 };
Willy Tarreau79e57332018-10-02 16:01:16 +0200104
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200105 if (txn->flags & TX_USE_PX_CONN) {
106 h = "Proxy-Authorization";
107 len = strlen(h);
108 } else {
109 h = "Authorization";
110 len = strlen(h);
111 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200112
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200113 if (!http_find_header2(h, len, ci_head(&s->req), &txn->hdr_idx, &ctx))
114 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200115
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200116 h = ctx.line + ctx.val;
Willy Tarreau79e57332018-10-02 16:01:16 +0200117
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200118 p = memchr(h, ' ', ctx.vlen);
119 len = p - h;
120 if (!p || len <= 0)
121 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200122
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200123 if (chunk_initlen(&auth_method, h, 0, len) != 1)
124 return 0;
125
126 chunk_initlen(&txn->auth.method_data, p + 1, 0, ctx.vlen - len - 1);
127 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200128
129 if (!strncasecmp("Basic", auth_method.area, auth_method.data)) {
130 struct buffer *http_auth = get_trash_chunk();
131
132 len = base64dec(txn->auth.method_data.area,
133 txn->auth.method_data.data,
134 http_auth->area, global.tune.bufsize - 1);
135
136 if (len < 0)
137 return 0;
138
139
140 http_auth->area[len] = '\0';
141
142 p = strchr(http_auth->area, ':');
143
144 if (!p)
145 return 0;
146
147 txn->auth.user = http_auth->area;
148 *p = '\0';
149 txn->auth.pass = p+1;
150
151 txn->auth.method = HTTP_AUTH_BASIC;
152 return 1;
153 }
154
155 return 0;
156}
157
158/* This function ensures that the prerequisites for an L7 fetch are ready,
159 * which means that a request or response is ready. If some data is missing,
160 * a parsing attempt is made. This is useful in TCP-based ACLs which are able
Christopher Fauletef453ed2018-10-24 21:39:27 +0200161 * to extract data from L7.
162 *
163 * The function returns :
164 * NULL with SMP_F_MAY_CHANGE in the sample flags if some data is missing to
165 * decide whether or not an HTTP message is present ;
166 * NULL if the requested data cannot be fetched or if it is certain that
167 * we'll never have any HTTP message there ;
168 * The HTX message if ready
169 */
170struct htx *smp_prefetch_htx(struct sample *smp, const struct arg *args)
171{
Christopher Fauletef453ed2018-10-24 21:39:27 +0200172 struct stream *s = smp->strm;
173 unsigned int opt = smp->opt;
174 struct http_txn *txn = NULL;
175 struct htx *htx = NULL;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100176 struct htx_sl *sl;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200177
178 /* Note: it is possible that <s> is NULL when called before stream
179 * initialization (eg: tcp-request connection), so this function is the
180 * one responsible for guarding against this case for all HTTP users.
181 */
182 if (!s)
183 return NULL;
184
185 if (!s->txn) {
186 if (unlikely(!http_alloc_txn(s)))
187 return NULL; /* not enough memory */
188 http_init_txn(s);
189 txn = s->txn;
190 }
191
Christopher Fauleteca88542019-04-03 10:12:42 +0200192 if (IS_HTX_STRM(s)) {
Christopher Fauletef453ed2018-10-24 21:39:27 +0200193 if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100194 htx = htxbuf(&s->req.buf);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200195 if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) {
196 /* Parsing is done by the mux, just wait */
197 smp->flags |= SMP_F_MAY_CHANGE;
198 return NULL;
199 }
200
201 /* OK we just got a valid HTTP request. We have some
202 * minor preparation to perform so that further checks
203 * can rely on HTTP tests.
204 */
205 if (txn) {
206 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100207 txn->meth = sl->info.req.meth;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200208 if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
209 s->flags |= SF_REDIRECTABLE;
210 }
211
212 /* otherwise everything's ready for the request */
213 }
214 else {
Christopher Faulet27ba2dc2018-12-05 11:53:24 +0100215 htx = htxbuf(&s->res.buf);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200216 if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) {
217 /* Parsing is done by the mux, just wait */
218 smp->flags |= SMP_F_MAY_CHANGE;
219 return NULL;
220 }
221 }
222 }
Christopher Fauleteca88542019-04-03 10:12:42 +0200223 else { /* RAW mode */
Christopher Fauletef453ed2018-10-24 21:39:27 +0200224 if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
225 struct buffer *buf;
226 struct h1m h1m;
227 struct http_hdr hdrs[MAX_HTTP_HDR];
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100228 union h1_sl h1sl;
229 unsigned int flags = HTX_FL_NONE;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200230 int ret;
231
232 buf = &s->req.buf;
233 if (b_head(buf) + b_data(buf) > b_wrap(buf))
234 b_slow_realign(buf, trash.area, 0);
235
236 h1m_init_req(&h1m);
237 ret = h1_headers_to_hdr_list(b_head(buf), b_stop(buf),
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100238 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200239 if (ret <= 0) {
240 /* Invalid or too big*/
241 if (ret < 0 || channel_full(&s->req, global.tune.maxrewrite))
242 return NULL;
243
244 /* wait for a full request */
245 smp->flags |= SMP_F_MAY_CHANGE;
246 return NULL;
247 }
248
249 /* OK we just got a valid HTTP request. We have to
250 * convert it into an HTX message.
251 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100252 if (unlikely(h1sl.rq.v.len == 0)) {
Christopher Fauletef453ed2018-10-24 21:39:27 +0200253 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100254 if (h1sl.rq.meth != HTTP_METH_GET || !h1sl.rq.u.len)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200255 return NULL;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100256 h1sl.rq.v = ist("HTTP/1.0");
Christopher Fauletef453ed2018-10-24 21:39:27 +0200257 }
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100258 else if ((h1sl.rq.v.len == 8) &&
259 ((*(h1sl.rq.v.ptr + 5) > '1') ||
260 ((*(h1sl.rq.v.ptr + 5) == '1') && (*(h1sl.rq.v.ptr + 7) >= '1'))))
261 h1m.flags |= H1_MF_VER_11;
262
263
264 /* Set HTX start-line flags */
265 if (h1m.flags & H1_MF_VER_11)
266 flags |= HTX_SL_F_VER_11;
267 if (h1m.flags & H1_MF_XFER_ENC)
268 flags |= HTX_SL_F_XFER_ENC;
269 if (h1m.flags & H1_MF_XFER_LEN) {
270 flags |= HTX_SL_F_XFER_LEN;
271 if (h1m.flags & H1_MF_CHNK)
272 flags |= HTX_SL_F_CHNK;
273 else if (h1m.flags & H1_MF_CLEN)
274 flags |= HTX_SL_F_CLEN;
275 }
276
Christopher Fauletef453ed2018-10-24 21:39:27 +0200277 htx = htx_from_buf(get_trash_chunk());
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100278 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
279 if (!sl || !htx_add_all_headers(htx, hdrs))
Christopher Fauletef453ed2018-10-24 21:39:27 +0200280 return NULL;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100281 sl->info.req.meth = h1sl.rq.meth;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200282
283 if (txn) {
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100284 txn->meth = h1sl.rq.meth;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200285 if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
286 s->flags |= SF_REDIRECTABLE;
287 }
288 /* Ok, now everything's ready for the request */
289 }
290 else {
291 /* Impossible, no HTTP fetch on tcp-response */
292 return NULL;
293 }
294 }
295
296 /* everything's OK */
297 smp->data.u.sint = 1;
298 return htx;
299}
300
301/* This function ensures that the prerequisites for an L7 fetch are ready,
302 * which means that a request or response is ready. If some data is missing,
303 * a parsing attempt is made. This is useful in TCP-based ACLs which are able
Willy Tarreau79e57332018-10-02 16:01:16 +0200304 * to extract data from L7. If <req_vol> is non-null during a request prefetch,
305 * another test is made to ensure the required information is not gone.
306 *
307 * The function returns :
308 * 0 with SMP_F_MAY_CHANGE in the sample flags if some data is missing to
309 * decide whether or not an HTTP message is present ;
310 * 0 if the requested data cannot be fetched or if it is certain that
311 * we'll never have any HTTP message there ;
312 * 1 if an HTTP message is ready
313 */
314int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
315 const struct arg *args, struct sample *smp, int req_vol)
316{
317 struct http_txn *txn;
318 struct http_msg *msg;
319
320 /* Note: it is possible that <s> is NULL when called before stream
321 * initialization (eg: tcp-request connection), so this function is the
322 * one responsible for guarding against this case for all HTTP users.
323 */
324 if (!s)
325 return 0;
326
327 if (!s->txn) {
328 if (unlikely(!http_alloc_txn(s)))
329 return 0; /* not enough memory */
330 http_init_txn(s);
331 }
332 txn = s->txn;
333 msg = &txn->req;
334
335 /* Check for a dependency on a request */
336 smp->data.type = SMP_T_BOOL;
337
338 if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
339 /* If the buffer does not leave enough free space at the end,
340 * we must first realign it.
341 */
342 if (ci_head(&s->req) > b_orig(&s->req.buf) &&
343 ci_head(&s->req) + ci_data(&s->req) > b_wrap(&s->req.buf) - global.tune.maxrewrite)
344 channel_slow_realign(&s->req, trash.area);
345
346 if (unlikely(txn->req.msg_state < HTTP_MSG_BODY)) {
347 if (msg->msg_state == HTTP_MSG_ERROR)
348 return 0;
349
350 /* Try to decode HTTP request */
351 if (likely(msg->next < ci_data(&s->req)))
352 http_msg_analyzer(msg, &txn->hdr_idx);
353
354 /* Still no valid request ? */
355 if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
356 if ((msg->msg_state == HTTP_MSG_ERROR) ||
357 channel_full(&s->req, global.tune.maxrewrite)) {
358 return 0;
359 }
360 /* wait for final state */
361 smp->flags |= SMP_F_MAY_CHANGE;
362 return 0;
363 }
364
365 /* OK we just got a valid HTTP request. We have some minor
366 * preparation to perform so that further checks can rely
367 * on HTTP tests.
368 */
369
370 /* If the request was parsed but was too large, we must absolutely
371 * return an error so that it is not processed. At the moment this
372 * cannot happen, but if the parsers are to change in the future,
373 * we want this check to be maintained.
374 */
375 if (unlikely(ci_head(&s->req) + ci_data(&s->req) >
376 b_wrap(&s->req.buf) - global.tune.maxrewrite)) {
377 msg->err_state = msg->msg_state;
378 msg->msg_state = HTTP_MSG_ERROR;
379 smp->data.u.sint = 1;
380 return 1;
381 }
382
383 txn->meth = find_http_meth(ci_head(msg->chn), msg->sl.rq.m_l);
384 if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
385 s->flags |= SF_REDIRECTABLE;
386
387 if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn))
388 return 0;
389 }
390
391 if (req_vol && txn->rsp.msg_state != HTTP_MSG_RPBEFORE) {
392 return 0; /* data might have moved and indexes changed */
393 }
394
395 /* otherwise everything's ready for the request */
396 }
397 else {
398 /* Check for a dependency on a response */
399 if (txn->rsp.msg_state < HTTP_MSG_BODY) {
400 smp->flags |= SMP_F_MAY_CHANGE;
401 return 0;
402 }
403 }
404
405 /* everything's OK */
406 smp->data.u.sint = 1;
407 return 1;
408}
409
410/* This function fetches the method of current HTTP request and stores
411 * it in the global pattern struct as a chunk. There are two possibilities :
412 * - if the method is known (not HTTP_METH_OTHER), its identifier is stored
413 * in <len> and <ptr> is NULL ;
414 * - if the method is unknown (HTTP_METH_OTHER), <ptr> points to the text and
415 * <len> to its length.
416 * This is intended to be used with pat_match_meth() only.
417 */
418static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
419{
420 int meth;
421 struct http_txn *txn;
422
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200423 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
424 /* HTX version */
425 struct htx *htx = smp_prefetch_htx(smp, args);
Willy Tarreau79e57332018-10-02 16:01:16 +0200426
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200427 if (!htx)
Willy Tarreau79e57332018-10-02 16:01:16 +0200428 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200429
430 txn = smp->strm->txn;
431 meth = txn->meth;
432 smp->data.type = SMP_T_METH;
433 smp->data.u.meth.meth = meth;
434 if (meth == HTTP_METH_OTHER) {
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100435 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200436
437 if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
438 /* ensure the indexes are not affected */
439 return 0;
440
441 sl = http_find_stline(htx);
442 smp->flags |= SMP_F_CONST;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100443 smp->data.u.meth.str.area = HTX_SL_REQ_MPTR(sl);
444 smp->data.u.meth.str.data = HTX_SL_REQ_MLEN(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200445 }
446 smp->flags |= SMP_F_VOL_1ST;
447 }
448 else {
449 /* LEGACY version */
450 CHECK_HTTP_MESSAGE_FIRST_PERM();
451
452 txn = smp->strm->txn;
453 meth = txn->meth;
454 smp->data.type = SMP_T_METH;
455 smp->data.u.meth.meth = meth;
456 if (meth == HTTP_METH_OTHER) {
457 if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE)
458 /* ensure the indexes are not affected */
459 return 0;
460 smp->flags |= SMP_F_CONST;
461 smp->data.u.meth.str.data = txn->req.sl.rq.m_l;
462 smp->data.u.meth.str.area = ci_head(txn->req.chn);
463 }
464 smp->flags |= SMP_F_VOL_1ST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200465 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200466 return 1;
467}
468
469static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private)
470{
471 struct http_txn *txn;
472 char *ptr;
473 int len;
474
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200475 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
476 /* HTX version */
477 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100478 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200479
480 if (!htx)
481 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200482
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200483 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100484 len = HTX_SL_REQ_VLEN(sl);
485 ptr = HTX_SL_REQ_VPTR(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200486 }
487 else {
488 /* LEGACY version */
489 CHECK_HTTP_MESSAGE_FIRST();
490
491 txn = smp->strm->txn;
492 len = txn->req.sl.rq.v_l;
493 ptr = ci_head(txn->req.chn) + txn->req.sl.rq.v;
494 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200495
496 while ((len-- > 0) && (*ptr++ != '/'));
497 if (len <= 0)
498 return 0;
499
500 smp->data.type = SMP_T_STR;
501 smp->data.u.str.area = ptr;
502 smp->data.u.str.data = len;
503
504 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
505 return 1;
506}
507
508static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private)
509{
510 struct http_txn *txn;
511 char *ptr;
512 int len;
513
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200514 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
515 /* HTX version */
516 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100517 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200518
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200519 if (!htx)
520 return 0;
521
522 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100523 len = HTX_SL_RES_VLEN(sl);
524 ptr = HTX_SL_RES_VPTR(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200525 }
526 else {
527 /* LEGACY version */
528 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +0200529
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200530 txn = smp->strm->txn;
531 if (txn->rsp.msg_state < HTTP_MSG_BODY)
532 return 0;
533
534 len = txn->rsp.sl.st.v_l;
535 ptr = ci_head(txn->rsp.chn);
536 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200537
538 while ((len-- > 0) && (*ptr++ != '/'));
539 if (len <= 0)
540 return 0;
541
542 smp->data.type = SMP_T_STR;
543 smp->data.u.str.area = ptr;
544 smp->data.u.str.data = len;
545
546 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
547 return 1;
548}
549
550/* 3. Check on Status Code. We manipulate integers here. */
551static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private)
552{
553 struct http_txn *txn;
554 char *ptr;
555 int len;
556
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200557 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
558 /* HTX version */
559 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100560 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200561
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200562 if (!htx)
563 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200564
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200565 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100566 len = HTX_SL_RES_CLEN(sl);
567 ptr = HTX_SL_RES_CPTR(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200568 }
569 else {
570 /* LEGACY version */
571 CHECK_HTTP_MESSAGE_FIRST();
572
573 txn = smp->strm->txn;
574 if (txn->rsp.msg_state < HTTP_MSG_BODY)
575 return 0;
576
577 len = txn->rsp.sl.st.c_l;
578 ptr = ci_head(txn->rsp.chn) + txn->rsp.sl.st.c;
579 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200580
581 smp->data.type = SMP_T_SINT;
582 smp->data.u.sint = __strl2ui(ptr, len);
583 smp->flags = SMP_F_VOL_1ST;
584 return 1;
585}
586
587static int smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const char *kw, void *private)
588{
589 if (LIST_ISEMPTY(&smp->sess->fe->format_unique_id))
590 return 0;
591
592 if (!smp->strm->unique_id) {
593 if ((smp->strm->unique_id = pool_alloc(pool_head_uniqueid)) == NULL)
594 return 0;
595 smp->strm->unique_id[0] = '\0';
596 }
597 smp->data.u.str.data = build_logline(smp->strm, smp->strm->unique_id,
598 UNIQUEID_LEN, &smp->sess->fe->format_unique_id);
599
600 smp->data.type = SMP_T_STR;
601 smp->data.u.str.area = smp->strm->unique_id;
602 smp->flags = SMP_F_CONST;
603 return 1;
604}
605
606/* Returns a string block containing all headers including the
Joseph Herlant942eea32018-11-15 13:57:22 -0800607 * empty line which separes headers from the body. This is useful
608 * for some headers analysis.
Willy Tarreau79e57332018-10-02 16:01:16 +0200609 */
610static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private)
611{
Willy Tarreau79e57332018-10-02 16:01:16 +0200612 struct http_txn *txn;
613
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200614 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
615 /* HTX version */
616 struct htx *htx = smp_prefetch_htx(smp, args);
617 struct buffer *temp;
618 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +0200619
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200620 if (!htx)
621 return 0;
622 temp = get_trash_chunk();
623 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
624 struct htx_blk *blk = htx_get_blk(htx, pos);
625 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200626
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200627 if (type == HTX_BLK_HDR) {
628 struct ist n = htx_get_blk_name(htx, blk);
629 struct ist v = htx_get_blk_value(htx, blk);
630
Christopher Fauletc59ff232018-12-03 13:58:44 +0100631 if (!htx_hdr_to_h1(n, v, temp))
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200632 return 0;
633 }
634 else if (type == HTX_BLK_EOH) {
635 if (!chunk_memcat(temp, "\r\n", 2))
636 return 0;
637 break;
638 }
639 }
640 smp->data.type = SMP_T_STR;
641 smp->data.u.str = *temp;
642
643 }
644 else {
645 /* LEGACY version */
646 struct http_msg *msg;
647 struct hdr_idx *idx;
648
649 CHECK_HTTP_MESSAGE_FIRST();
650
651 txn = smp->strm->txn;
652 idx = &txn->hdr_idx;
653 msg = &txn->req;
Willy Tarreau79e57332018-10-02 16:01:16 +0200654
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200655 smp->data.type = SMP_T_STR;
656 smp->data.u.str.area = ci_head(msg->chn) + hdr_idx_first_pos(idx);
657 smp->data.u.str.data = msg->eoh - hdr_idx_first_pos(idx) + 1 +
658 (ci_head(msg->chn)[msg->eoh] == '\r');
659 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200660 return 1;
661}
662
663/* Returns the header request in a length/value encoded format.
664 * This is useful for exchanges with the SPOE.
665 *
666 * A "length value" is a multibyte code encoding numbers. It uses the
667 * SPOE format. The encoding is the following:
668 *
669 * Each couple "header name" / "header value" is composed
670 * like this:
671 * "length value" "header name bytes"
672 * "length value" "header value bytes"
673 * When the last header is reached, the header name and the header
674 * value are empty. Their length are 0
675 */
676static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
677{
Willy Tarreau79e57332018-10-02 16:01:16 +0200678 struct http_txn *txn;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200679 struct buffer *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200680
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200681 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
682 /* HTX version */
683 struct htx *htx = smp_prefetch_htx(smp, args);
684 struct buffer *temp;
685 char *p, *end;
686 int32_t pos;
687 int ret;
Willy Tarreau79e57332018-10-02 16:01:16 +0200688
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200689 if (!htx)
690 return 0;
691 temp = get_trash_chunk();
692 p = temp->area;
693 end = temp->area + temp->size;
694 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
695 struct htx_blk *blk = htx_get_blk(htx, pos);
696 enum htx_blk_type type = htx_get_blk_type(blk);
697 struct ist n, v;
Willy Tarreau79e57332018-10-02 16:01:16 +0200698
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200699 if (type == HTX_BLK_HDR) {
700 n = htx_get_blk_name(htx,blk);
701 v = htx_get_blk_value(htx, blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200702
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200703 /* encode the header name. */
704 ret = encode_varint(n.len, &p, end);
705 if (ret == -1)
706 return 0;
707 if (p + n.len > end)
708 return 0;
709 memcpy(p, n.ptr, n.len);
710 p += n.len;
Willy Tarreau79e57332018-10-02 16:01:16 +0200711
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200712 /* encode the header value. */
713 ret = encode_varint(v.len, &p, end);
714 if (ret == -1)
715 return 0;
716 if (p + v.len > end)
717 return 0;
718 memcpy(p, v.ptr, v.len);
719 p += v.len;
Willy Tarreau79e57332018-10-02 16:01:16 +0200720
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200721 }
722 else if (type == HTX_BLK_EOH) {
723 /* encode the end of the header list with empty
724 * header name and header value.
725 */
726 ret = encode_varint(0, &p, end);
727 if (ret == -1)
728 return 0;
729 ret = encode_varint(0, &p, end);
730 if (ret == -1)
731 return 0;
732 break;
733 }
734 }
735
736 /* Initialise sample data which will be filled. */
737 smp->data.type = SMP_T_BIN;
738 smp->data.u.str.area = temp->area;
739 smp->data.u.str.data = p - temp->area;
740 smp->data.u.str.size = temp->size;
741 }
742 else {
743 /* LEGACY version */
744 struct http_msg *msg;
745 struct hdr_idx *idx;
746 const char *cur_ptr, *cur_next, *p;
747 int old_idx, cur_idx;
748 struct hdr_idx_elem *cur_hdr;
749 const char *hn, *hv;
750 int hnl, hvl;
751 int ret;
752 char *buf;
753 char *end;
754
755 CHECK_HTTP_MESSAGE_FIRST();
756
757 temp = get_trash_chunk();
758 buf = temp->area;
759 end = temp->area + temp->size;
760
761 txn = smp->strm->txn;
762 idx = &txn->hdr_idx;
763 msg = &txn->req;
764
765 /* Build array of headers. */
766 old_idx = 0;
767 cur_next = ci_head(msg->chn) + hdr_idx_first_pos(idx);
768 while (1) {
769 cur_idx = idx->v[old_idx].next;
770 if (!cur_idx)
771 break;
772 old_idx = cur_idx;
Willy Tarreau79e57332018-10-02 16:01:16 +0200773
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200774 cur_hdr = &idx->v[cur_idx];
775 cur_ptr = cur_next;
776 cur_next = cur_ptr + cur_hdr->len + cur_hdr->cr + 1;
777
778 /* Now we have one full header at cur_ptr of len cur_hdr->len,
779 * and the next header starts at cur_next. We'll check
780 * this header in the list as well as against the default
781 * rule.
782 */
783
784 /* look for ': *'. */
785 hn = cur_ptr;
786 for (p = cur_ptr; p < cur_ptr + cur_hdr->len && *p != ':'; p++);
787 if (p >= cur_ptr+cur_hdr->len)
788 continue;
789 hnl = p - hn;
Willy Tarreau79e57332018-10-02 16:01:16 +0200790 p++;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200791 while (p < cur_ptr + cur_hdr->len && (*p == ' ' || *p == '\t'))
792 p++;
793 if (p >= cur_ptr + cur_hdr->len)
794 continue;
795 hv = p;
796 hvl = cur_ptr + cur_hdr->len-p;
Willy Tarreau79e57332018-10-02 16:01:16 +0200797
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200798 /* encode the header name. */
799 ret = encode_varint(hnl, &buf, end);
800 if (ret == -1)
801 return 0;
802 if (buf + hnl > end)
803 return 0;
804 memcpy(buf, hn, hnl);
805 buf += hnl;
806
807 /* encode and copy the value. */
808 ret = encode_varint(hvl, &buf, end);
809 if (ret == -1)
810 return 0;
811 if (buf + hvl > end)
812 return 0;
813 memcpy(buf, hv, hvl);
814 buf += hvl;
815 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200816
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200817 /* encode the end of the header list with empty
818 * header name and header value.
819 */
820 ret = encode_varint(0, &buf, end);
Willy Tarreau79e57332018-10-02 16:01:16 +0200821 if (ret == -1)
822 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200823 ret = encode_varint(0, &buf, end);
824 if (ret == -1)
Willy Tarreau79e57332018-10-02 16:01:16 +0200825 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200826
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200827 /* Initialise sample data which will be filled. */
828 smp->data.type = SMP_T_BIN;
829 smp->data.u.str.area = temp->area;
830 smp->data.u.str.data = buf - temp->area;
831 smp->data.u.str.size = temp->size;
832 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200833 return 1;
834}
835
836/* returns the longest available part of the body. This requires that the body
837 * has been waited for using http-buffer-request.
838 */
839static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private)
840{
Willy Tarreau79e57332018-10-02 16:01:16 +0200841 struct buffer *temp;
842
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200843 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
844 /* HTX version */
845 struct htx *htx = smp_prefetch_htx(smp, args);
846 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +0200847
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200848 if (!htx)
849 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200850
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200851 temp = get_trash_chunk();
852 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
853 struct htx_blk *blk = htx_get_blk(htx, pos);
854 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200855
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200856 if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
857 break;
858 if (type == HTX_BLK_DATA) {
Christopher Fauletc59ff232018-12-03 13:58:44 +0100859 if (!htx_data_to_h1(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200860 return 0;
861 }
862 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200863
Willy Tarreau79e57332018-10-02 16:01:16 +0200864 smp->data.type = SMP_T_BIN;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200865 smp->data.u.str = *temp;
866 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200867 }
868 else {
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200869 /* LEGACY version */
870 struct http_msg *msg;
871 unsigned long len;
872 unsigned long block1;
873 char *body;
874
875 CHECK_HTTP_MESSAGE_FIRST();
876
877 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
878 msg = &smp->strm->txn->req;
879 else
880 msg = &smp->strm->txn->rsp;
881
882 len = http_body_bytes(msg);
883 body = c_ptr(msg->chn, -http_data_rewind(msg));
884
885 block1 = len;
886 if (block1 > b_wrap(&msg->chn->buf) - body)
887 block1 = b_wrap(&msg->chn->buf) - body;
888
889 if (block1 == len) {
890 /* buffer is not wrapped (or empty) */
891 smp->data.type = SMP_T_BIN;
892 smp->data.u.str.area = body;
893 smp->data.u.str.data = len;
894 smp->flags = SMP_F_VOL_TEST | SMP_F_CONST;
895 }
896 else {
897 /* buffer is wrapped, we need to defragment it */
898 temp = get_trash_chunk();
899 memcpy(temp->area, body, block1);
900 memcpy(temp->area + block1, b_orig(&msg->chn->buf),
901 len - block1);
902 smp->data.type = SMP_T_BIN;
903 smp->data.u.str.area = temp->area;
904 smp->data.u.str.data = len;
905 smp->flags = SMP_F_VOL_TEST;
906 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200907 }
908 return 1;
909}
910
911
912/* returns the available length of the body. This requires that the body
913 * has been waited for using http-buffer-request.
914 */
915static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
916{
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200917 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
918 /* HTX version */
Christopher Fauletc16317d2018-12-12 14:11:22 +0100919 struct htx *htx = smp_prefetch_htx(smp, args);
Dragan Dosen5a606682019-02-14 12:30:53 +0100920 int32_t pos;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100921 unsigned long long len = 0;
922
923 if (!htx)
924 return 0;
925
Dragan Dosen5a606682019-02-14 12:30:53 +0100926 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
927 struct htx_blk *blk = htx_get_blk(htx, pos);
928 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100929
Dragan Dosen5a606682019-02-14 12:30:53 +0100930 if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
Christopher Fauletc16317d2018-12-12 14:11:22 +0100931 break;
Dragan Dosen5a606682019-02-14 12:30:53 +0100932 if (type == HTX_BLK_DATA)
933 len += htx_get_blksz(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100934 }
935
936 smp->data.type = SMP_T_SINT;
937 smp->data.u.sint = len;
938
939 smp->flags = SMP_F_VOL_TEST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200940 }
941 else {
942 /* LEGACY version */
943 struct http_msg *msg;
Willy Tarreau79e57332018-10-02 16:01:16 +0200944
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200945 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +0200946
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200947 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
948 msg = &smp->strm->txn->req;
949 else
950 msg = &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200951
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200952 smp->data.type = SMP_T_SINT;
953 smp->data.u.sint = http_body_bytes(msg);
Willy Tarreau79e57332018-10-02 16:01:16 +0200954
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200955 smp->flags = SMP_F_VOL_TEST;
956 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200957 return 1;
958}
959
960
961/* returns the advertised length of the body, or the advertised size of the
962 * chunks available in the buffer. This requires that the body has been waited
963 * for using http-buffer-request.
964 */
965static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
966{
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200967 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
968 /* HTX version */
Christopher Fauletc16317d2018-12-12 14:11:22 +0100969 struct htx *htx = smp_prefetch_htx(smp, args);
Dragan Dosen5a606682019-02-14 12:30:53 +0100970 int32_t pos;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100971 unsigned long long len = 0;
972
973 if (!htx)
974 return 0;
975
Dragan Dosen5a606682019-02-14 12:30:53 +0100976 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
977 struct htx_blk *blk = htx_get_blk(htx, pos);
978 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100979
Dragan Dosen5a606682019-02-14 12:30:53 +0100980 if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
Christopher Fauletc16317d2018-12-12 14:11:22 +0100981 break;
Dragan Dosen5a606682019-02-14 12:30:53 +0100982 if (type == HTX_BLK_DATA)
983 len += htx_get_blksz(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100984 }
985 if (htx->extra != ULLONG_MAX)
986 len += htx->extra;
987
988 smp->data.type = SMP_T_SINT;
989 smp->data.u.sint = len;
990
991 smp->flags = SMP_F_VOL_TEST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200992 }
993 else {
994 /* LEGACY version */
995 struct http_msg *msg;
Willy Tarreau79e57332018-10-02 16:01:16 +0200996
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200997 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +0200998
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200999 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
1000 msg = &smp->strm->txn->req;
1001 else
1002 msg = &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001003
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001004 smp->data.type = SMP_T_SINT;
1005 smp->data.u.sint = msg->body_len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001006
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001007 smp->flags = SMP_F_VOL_TEST;
1008 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001009 return 1;
1010}
1011
1012
1013/* 4. Check on URL/URI. A pointer to the URI is stored. */
1014static int smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private)
1015{
1016 struct http_txn *txn;
1017
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001018 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1019 /* HTX version */
1020 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001021 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001022
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001023 if (!htx)
1024 return 0;
1025 sl = http_find_stline(htx);
1026 smp->data.type = SMP_T_STR;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001027 smp->data.u.str.area = HTX_SL_REQ_UPTR(sl);
1028 smp->data.u.str.data = HTX_SL_REQ_ULEN(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001029 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1030 }
1031 else {
1032 /* LEGACY version */
1033 CHECK_HTTP_MESSAGE_FIRST();
1034
1035 txn = smp->strm->txn;
1036 smp->data.type = SMP_T_STR;
1037 smp->data.u.str.data = txn->req.sl.rq.u_l;
1038 smp->data.u.str.area = ci_head(txn->req.chn) + txn->req.sl.rq.u;
1039 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1040 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001041 return 1;
1042}
1043
1044static int smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
1045{
1046 struct http_txn *txn;
1047 struct sockaddr_storage addr;
1048
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001049 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1050 /* HTX version */
1051 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001052 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001053
1054 if (!htx)
1055 return 0;
1056 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001057 url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001058 }
1059 else {
1060 /* LEGACY version */
1061 CHECK_HTTP_MESSAGE_FIRST();
1062
1063 txn = smp->strm->txn;
1064 url2sa(ci_head(txn->req.chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
1065 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001066
Willy Tarreau79e57332018-10-02 16:01:16 +02001067 if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
1068 return 0;
1069
1070 smp->data.type = SMP_T_IPV4;
1071 smp->data.u.ipv4 = ((struct sockaddr_in *)&addr)->sin_addr;
1072 smp->flags = 0;
1073 return 1;
1074}
1075
1076static int smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private)
1077{
1078 struct http_txn *txn;
1079 struct sockaddr_storage addr;
1080
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001081 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1082 /* HTX version */
1083 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001084 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001085
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001086 if (!htx)
1087 return 0;
1088 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001089 url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001090 }
1091 else {
1092 /* LEGACY version */
1093 CHECK_HTTP_MESSAGE_FIRST();
1094
1095 txn = smp->strm->txn;
1096 url2sa(ci_head(txn->req.chn) + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL);
1097 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001098 if (((struct sockaddr_in *)&addr)->sin_family != AF_INET)
1099 return 0;
1100
1101 smp->data.type = SMP_T_SINT;
1102 smp->data.u.sint = ntohs(((struct sockaddr_in *)&addr)->sin_port);
1103 smp->flags = 0;
1104 return 1;
1105}
1106
1107/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
1108 * Accepts an optional argument of type string containing the header field name,
1109 * and an optional argument of type signed or unsigned integer to request an
1110 * explicit occurrence of the header. Note that in the event of a missing name,
1111 * headers are considered from the first one. It does not stop on commas and
1112 * returns full lines instead (useful for User-Agent or Date for example).
1113 */
1114static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1115{
Willy Tarreau79e57332018-10-02 16:01:16 +02001116 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001117
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001118 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1119 /* HTX version */
1120 struct htx *htx = smp_prefetch_htx(smp, args);
1121 struct http_hdr_ctx *ctx = smp->ctx.a[0];
1122 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +02001123
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001124 if (!ctx) {
1125 /* first call */
1126 ctx = &static_http_hdr_ctx;
1127 ctx->blk = NULL;
1128 smp->ctx.a[0] = ctx;
1129 }
1130
1131 if (args) {
1132 if (args[0].type != ARGT_STR)
1133 return 0;
1134 name.ptr = args[0].data.str.area;
1135 name.len = args[0].data.str.data;
1136
1137 if (args[1].type == ARGT_SINT)
1138 occ = args[1].data.sint;
1139 }
1140
1141 if (!htx)
Willy Tarreau79e57332018-10-02 16:01:16 +02001142 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001143
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001144 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
1145 /* search for header from the beginning */
1146 ctx->blk = NULL;
1147
1148 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
1149 /* no explicit occurrence and single fetch => last header by default */
1150 occ = -1;
1151
1152 if (!occ)
1153 /* prepare to report multiple occurrences for ACL fetches */
1154 smp->flags |= SMP_F_NOT_LAST;
1155
1156 smp->data.type = SMP_T_STR;
1157 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
1158 if (http_get_htx_fhdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
1159 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +02001160 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001161 else {
1162 /* LEGACY version */
1163 struct hdr_idx *idx;
1164 struct hdr_ctx *ctx = smp->ctx.a[0];
1165 const struct http_msg *msg;
1166 const char *name_str = NULL;
1167 int name_len = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001168
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001169 if (!ctx) {
1170 /* first call */
1171 ctx = &static_hdr_ctx;
1172 ctx->idx = 0;
1173 smp->ctx.a[0] = ctx;
1174 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001175
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001176 if (args) {
1177 if (args[0].type != ARGT_STR)
1178 return 0;
1179 name_str = args[0].data.str.area;
1180 name_len = args[0].data.str.data;
Willy Tarreau79e57332018-10-02 16:01:16 +02001181
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001182 if (args[1].type == ARGT_SINT)
1183 occ = args[1].data.sint;
1184 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001185
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001186 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02001187
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001188 idx = &smp->strm->txn->hdr_idx;
1189 msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001190
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001191 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
1192 /* search for header from the beginning */
1193 ctx->idx = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001194
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001195 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
1196 /* no explicit occurrence and single fetch => last header by default */
1197 occ = -1;
1198
1199 if (!occ)
1200 /* prepare to report multiple occurrences for ACL fetches */
1201 smp->flags |= SMP_F_NOT_LAST;
1202
1203 smp->data.type = SMP_T_STR;
1204 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
1205 if (http_get_fhdr(msg, name_str, name_len, idx, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
1206 return 1;
1207 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001208 smp->flags &= ~SMP_F_NOT_LAST;
1209 return 0;
1210}
1211
1212/* 6. Check on HTTP header count. The number of occurrences is returned.
1213 * Accepts exactly 1 argument of type string. It does not stop on commas and
1214 * returns full lines instead (useful for User-Agent or Date for example).
1215 */
1216static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
1217{
Willy Tarreau79e57332018-10-02 16:01:16 +02001218 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001219
1220 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1221 /* HTX version */
1222 struct htx *htx = smp_prefetch_htx(smp, args);
1223 struct http_hdr_ctx ctx;
1224 struct ist name;
1225
1226 if (!htx)
1227 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001228
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001229 if (args && args->type == ARGT_STR) {
1230 name.ptr = args->data.str.area;
1231 name.len = args->data.str.data;
Olivier Houcharde2c78cd2018-11-21 13:49:48 +01001232 } else {
1233 name.ptr = NULL;
1234 name.len = 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001235 }
1236
1237 ctx.blk = NULL;
1238 cnt = 0;
1239 while (http_find_header(htx, name, &ctx, 1))
1240 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +02001241 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001242 else {
1243 /* LEGACY version */
1244 struct hdr_idx *idx;
1245 struct hdr_ctx ctx;
1246 const struct http_msg *msg;
1247 const char *name = NULL;
1248 int len = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001249
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001250 if (args && args->type == ARGT_STR) {
1251 name = args->data.str.area;
1252 len = args->data.str.data;
1253 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001254
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001255 CHECK_HTTP_MESSAGE_FIRST();
1256
1257 idx = &smp->strm->txn->hdr_idx;
1258 msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001259
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001260 ctx.idx = 0;
1261 cnt = 0;
1262 while (http_find_full_header2(name, len, ci_head(msg->chn), idx, &ctx))
1263 cnt++;
1264 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001265
1266 smp->data.type = SMP_T_SINT;
1267 smp->data.u.sint = cnt;
1268 smp->flags = SMP_F_VOL_HDR;
1269 return 1;
1270}
1271
1272static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private)
1273{
Willy Tarreau79e57332018-10-02 16:01:16 +02001274 struct buffer *temp;
1275 char del = ',';
1276
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001277 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1278 /* HTX version */
1279 struct htx *htx = smp_prefetch_htx(smp, args);
1280 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +02001281
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001282 if (!htx)
1283 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001284
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001285 if (args && args->type == ARGT_STR)
1286 del = *args[0].data.str.area;
Willy Tarreau79e57332018-10-02 16:01:16 +02001287
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001288 temp = get_trash_chunk();
1289 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
1290 struct htx_blk *blk = htx_get_blk(htx, pos);
1291 enum htx_blk_type type = htx_get_blk_type(blk);
1292 struct ist n;
Willy Tarreau79e57332018-10-02 16:01:16 +02001293
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001294 if (type == HTX_BLK_EOH)
1295 break;
1296 if (type != HTX_BLK_HDR)
1297 continue;
1298 n = htx_get_blk_name(htx, blk);
1299
1300 if (temp->data)
1301 temp->area[temp->data++] = del;
1302 chunk_memcat(temp, n.ptr, n.len);
1303 }
1304 }
1305 else {
1306 /* LEGACY version */
1307 struct hdr_idx *idx;
1308 struct hdr_ctx ctx;
1309 const struct http_msg *msg;
1310
1311 if (args && args->type == ARGT_STR)
1312 del = *args[0].data.str.area;
1313
1314 CHECK_HTTP_MESSAGE_FIRST();
1315
1316 idx = &smp->strm->txn->hdr_idx;
1317 msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
1318
1319 temp = get_trash_chunk();
1320
1321 ctx.idx = 0;
1322 while (http_find_next_header(ci_head(msg->chn), idx, &ctx)) {
1323 if (temp->data)
1324 temp->area[temp->data++] = del;
1325 memcpy(temp->area + temp->data, ctx.line, ctx.del);
1326 temp->data += ctx.del;
1327 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001328 }
1329
1330 smp->data.type = SMP_T_STR;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001331 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001332 smp->flags = SMP_F_VOL_HDR;
1333 return 1;
1334}
1335
1336/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
1337 * Accepts an optional argument of type string containing the header field name,
1338 * and an optional argument of type signed or unsigned integer to request an
1339 * explicit occurrence of the header. Note that in the event of a missing name,
1340 * headers are considered from the first one.
1341 */
1342static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1343{
Willy Tarreau79e57332018-10-02 16:01:16 +02001344 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001345
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001346 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1347 /* HTX version */
1348 struct htx *htx = smp_prefetch_htx(smp, args);
1349 struct http_hdr_ctx *ctx = smp->ctx.a[0];
1350 struct ist name;
1351
1352 if (!ctx) {
1353 /* first call */
1354 ctx = &static_http_hdr_ctx;
1355 ctx->blk = NULL;
1356 smp->ctx.a[0] = ctx;
1357 }
1358
1359 if (args) {
1360 if (args[0].type != ARGT_STR)
1361 return 0;
1362 name.ptr = args[0].data.str.area;
1363 name.len = args[0].data.str.data;
1364
1365 if (args[1].type == ARGT_SINT)
1366 occ = args[1].data.sint;
1367 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001368
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001369 if (!htx)
Willy Tarreau79e57332018-10-02 16:01:16 +02001370 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001371
1372 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
1373 /* search for header from the beginning */
1374 ctx->blk = NULL;
1375
1376 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
1377 /* no explicit occurrence and single fetch => last header by default */
1378 occ = -1;
1379
1380 if (!occ)
1381 /* prepare to report multiple occurrences for ACL fetches */
1382 smp->flags |= SMP_F_NOT_LAST;
Willy Tarreau79e57332018-10-02 16:01:16 +02001383
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001384 smp->data.type = SMP_T_STR;
1385 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
1386 if (http_get_htx_hdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
1387 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +02001388 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001389 else {
1390 /* LEGACY version */
1391 struct hdr_idx *idx;
1392 struct hdr_ctx *ctx = smp->ctx.a[0];
1393 const struct http_msg *msg;
1394 const char *name_str = NULL;
1395 int name_len = 0;
1396
1397 if (!ctx) {
1398 /* first call */
1399 ctx = &static_hdr_ctx;
1400 ctx->idx = 0;
1401 smp->ctx.a[0] = ctx;
1402 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001403
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001404 if (args) {
1405 if (args[0].type != ARGT_STR)
1406 return 0;
1407 name_str = args[0].data.str.area;
1408 name_len = args[0].data.str.data;
Willy Tarreau79e57332018-10-02 16:01:16 +02001409
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001410 if (args[1].type == ARGT_SINT)
1411 occ = args[1].data.sint;
1412 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001413
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001414 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02001415
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001416 idx = &smp->strm->txn->hdr_idx;
1417 msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001418
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001419 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
1420 /* search for header from the beginning */
1421 ctx->idx = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001422
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001423 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
1424 /* no explicit occurrence and single fetch => last header by default */
1425 occ = -1;
1426
1427 if (!occ)
1428 /* prepare to report multiple occurrences for ACL fetches */
1429 smp->flags |= SMP_F_NOT_LAST;
1430
1431 smp->data.type = SMP_T_STR;
1432 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
1433 if (http_get_hdr(msg, name_str, name_len, idx, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
1434 return 1;
1435 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001436
1437 smp->flags &= ~SMP_F_NOT_LAST;
1438 return 0;
1439}
1440
1441/* 6. Check on HTTP header count. The number of occurrences is returned.
1442 * Accepts exactly 1 argument of type string.
1443 */
1444static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
1445{
Willy Tarreau79e57332018-10-02 16:01:16 +02001446 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001447
1448 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1449 /* HTX version */
1450 struct htx *htx = smp_prefetch_htx(smp, args);
1451 struct http_hdr_ctx ctx;
1452 struct ist name;
1453
1454 if (!htx)
1455 return 0;
1456
1457 if (args && args->type == ARGT_STR) {
1458 name.ptr = args->data.str.area;
1459 name.len = args->data.str.data;
Olivier Houcharde2c78cd2018-11-21 13:49:48 +01001460 } else {
1461 name.ptr = NULL;
1462 name.len = 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001463 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001464
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001465 ctx.blk = NULL;
1466 cnt = 0;
1467 while (http_find_header(htx, name, &ctx, 0))
1468 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +02001469 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001470 else {
1471 /* LEGACY version */
1472 struct hdr_idx *idx;
1473 struct hdr_ctx ctx;
1474 const struct http_msg *msg;
1475 const char *name = NULL;
1476 int len = 0;
1477
1478 if (args && args->type == ARGT_STR) {
1479 name = args->data.str.area;
1480 len = args->data.str.data;
1481 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001482
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001483 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02001484
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001485 idx = &smp->strm->txn->hdr_idx;
1486 msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001487
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001488 ctx.idx = 0;
1489 cnt = 0;
1490 while (http_find_header2(name, len, ci_head(msg->chn), idx, &ctx))
1491 cnt++;
1492 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001493
1494 smp->data.type = SMP_T_SINT;
1495 smp->data.u.sint = cnt;
1496 smp->flags = SMP_F_VOL_HDR;
1497 return 1;
1498}
1499
1500/* Fetch an HTTP header's integer value. The integer value is returned. It
1501 * takes a mandatory argument of type string and an optional one of type int
1502 * to designate a specific occurrence. It returns an unsigned integer, which
1503 * may or may not be appropriate for everything.
1504 */
1505static int smp_fetch_hdr_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
1506{
1507 int ret = smp_fetch_hdr(args, smp, kw, private);
1508
1509 if (ret > 0) {
1510 smp->data.type = SMP_T_SINT;
1511 smp->data.u.sint = strl2ic(smp->data.u.str.area,
1512 smp->data.u.str.data);
1513 }
1514
1515 return ret;
1516}
1517
1518/* Fetch an HTTP header's IP value. takes a mandatory argument of type string
1519 * and an optional one of type int to designate a specific occurrence.
1520 * It returns an IPv4 or IPv6 address.
1521 */
1522static int smp_fetch_hdr_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
1523{
1524 int ret;
1525
1526 while ((ret = smp_fetch_hdr(args, smp, kw, private)) > 0) {
1527 if (url2ipv4((char *) smp->data.u.str.area, &smp->data.u.ipv4)) {
1528 smp->data.type = SMP_T_IPV4;
1529 break;
1530 } else {
1531 struct buffer *temp = get_trash_chunk();
1532 if (smp->data.u.str.data < temp->size - 1) {
1533 memcpy(temp->area, smp->data.u.str.area,
1534 smp->data.u.str.data);
1535 temp->area[smp->data.u.str.data] = '\0';
1536 if (inet_pton(AF_INET6, temp->area, &smp->data.u.ipv6)) {
1537 smp->data.type = SMP_T_IPV6;
1538 break;
1539 }
1540 }
1541 }
1542
1543 /* if the header doesn't match an IP address, fetch next one */
1544 if (!(smp->flags & SMP_F_NOT_LAST))
1545 return 0;
1546 }
1547 return ret;
1548}
1549
1550/* 8. Check on URI PATH. A pointer to the PATH is stored. The path starts at
1551 * the first '/' after the possible hostname, and ends before the possible '?'.
1552 */
1553static int smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private)
1554{
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001555 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1556 /* HTX version */
1557 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001558 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001559 struct ist path;
1560 size_t len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001561
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001562 if (!htx)
1563 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001564
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001565 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001566 path = http_get_path(htx_sl_req_uri(sl));
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001567 if (!path.ptr)
1568 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001569
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001570 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001571 ;
Willy Tarreau79e57332018-10-02 16:01:16 +02001572
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001573 /* OK, we got the '/' ! */
1574 smp->data.type = SMP_T_STR;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001575 smp->data.u.str.area = path.ptr;
1576 smp->data.u.str.data = len;
1577 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1578 }
1579 else {
1580 struct http_txn *txn;
1581 char *ptr, *end;
Willy Tarreau79e57332018-10-02 16:01:16 +02001582
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001583 CHECK_HTTP_MESSAGE_FIRST();
1584
1585 txn = smp->strm->txn;
1586 end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
1587 ptr = http_txn_get_path(txn);
1588 if (!ptr)
1589 return 0;
1590
1591 /* OK, we got the '/' ! */
1592 smp->data.type = SMP_T_STR;
1593 smp->data.u.str.area = ptr;
1594
1595 while (ptr < end && *ptr != '?')
1596 ptr++;
1597
1598 smp->data.u.str.data = ptr - smp->data.u.str.area;
1599 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1600 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001601 return 1;
1602}
1603
1604/* This produces a concatenation of the first occurrence of the Host header
1605 * followed by the path component if it begins with a slash ('/'). This means
1606 * that '*' will not be added, resulting in exactly the first Host entry.
1607 * If no Host header is found, then the path is returned as-is. The returned
1608 * value is stored in the trash so it does not need to be marked constant.
1609 * The returned sample is of type string.
1610 */
1611static int smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private)
1612{
Willy Tarreau79e57332018-10-02 16:01:16 +02001613 struct buffer *temp;
1614
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001615 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1616 /* HTX version */
1617 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001618 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001619 struct http_hdr_ctx ctx;
1620 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02001621
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001622 if (!htx)
1623 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001624
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001625 ctx.blk = NULL;
1626 if (!http_find_header(htx, ist("Host"), &ctx, 0) || !ctx.value.len)
1627 return smp_fetch_path(args, smp, kw, private);
Willy Tarreau79e57332018-10-02 16:01:16 +02001628
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001629 /* OK we have the header value in ctx.value */
1630 temp = get_trash_chunk();
1631 chunk_memcat(temp, ctx.value.ptr, ctx.value.len);
1632
1633 /* now retrieve the path */
1634 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001635 path = http_get_path(htx_sl_req_uri(sl));
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001636 if (path.ptr) {
1637 size_t len;
1638
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001639 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1640 ;
1641
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001642 if (len && *(path.ptr) == '/')
1643 chunk_memcat(temp, path.ptr, len);
1644 }
1645
1646 smp->data.type = SMP_T_STR;
1647 smp->data.u.str = *temp;
1648 }
1649 else {
1650 /* LEGACY version */
1651 struct http_txn *txn;
1652 char *ptr, *end, *beg;
1653 struct hdr_ctx ctx;
1654
1655 CHECK_HTTP_MESSAGE_FIRST();
1656
1657 txn = smp->strm->txn;
1658 ctx.idx = 0;
1659 if (!http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx) || !ctx.vlen)
1660 return smp_fetch_path(args, smp, kw, private);
1661
1662 /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
1663 temp = get_trash_chunk();
1664 memcpy(temp->area, ctx.line + ctx.val, ctx.vlen);
1665 smp->data.type = SMP_T_STR;
1666 smp->data.u.str.area = temp->area;
1667 smp->data.u.str.data = ctx.vlen;
Willy Tarreau79e57332018-10-02 16:01:16 +02001668
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001669 /* now retrieve the path */
1670 end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
1671 beg = http_txn_get_path(txn);
1672 if (!beg)
1673 beg = end;
Willy Tarreau79e57332018-10-02 16:01:16 +02001674
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001675 for (ptr = beg; ptr < end && *ptr != '?'; ptr++);
1676
1677 if (beg < ptr && *beg == '/') {
1678 memcpy(smp->data.u.str.area + smp->data.u.str.data, beg,
1679 ptr - beg);
1680 smp->data.u.str.data += ptr - beg;
1681 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001682 }
1683
1684 smp->flags = SMP_F_VOL_1ST;
1685 return 1;
1686}
1687
1688/* This produces a 32-bit hash of the concatenation of the first occurrence of
1689 * the Host header followed by the path component if it begins with a slash ('/').
1690 * This means that '*' will not be added, resulting in exactly the first Host
1691 * entry. If no Host header is found, then the path is used. The resulting value
1692 * is hashed using the path hash followed by a full avalanche hash and provides a
1693 * 32-bit integer value. This fetch is useful for tracking per-path activity on
1694 * high-traffic sites without having to store whole paths.
1695 */
1696static int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private)
1697{
Willy Tarreau79e57332018-10-02 16:01:16 +02001698 unsigned int hash = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001699
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001700 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1701 /* HTX version */
1702 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001703 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001704 struct http_hdr_ctx ctx;
1705 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02001706
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001707 if (!htx)
1708 return 0;
1709
1710 ctx.blk = NULL;
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001711 if (http_find_header(htx, ist("Host"), &ctx, 0)) {
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001712 /* OK we have the header value in ctx.value */
1713 while (ctx.value.len--)
1714 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
1715 }
1716
1717 /* now retrieve the path */
1718 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001719 path = http_get_path(htx_sl_req_uri(sl));
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001720 if (path.ptr) {
1721 size_t len;
1722
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001723 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1724 ;
1725
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001726 if (len && *(path.ptr) == '/') {
1727 while (len--)
1728 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
1729 }
1730 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001731 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001732 else {
1733 /* LEGACY version */
1734 struct http_txn *txn;
1735 struct hdr_ctx ctx;
1736 char *ptr, *beg, *end;
1737 int len;
1738
1739 CHECK_HTTP_MESSAGE_FIRST();
1740
1741 txn = smp->strm->txn;
1742 ctx.idx = 0;
1743 if (http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx)) {
1744 /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
1745 ptr = ctx.line + ctx.val;
1746 len = ctx.vlen;
1747 while (len--)
1748 hash = *(ptr++) + (hash << 6) + (hash << 16) - hash;
1749 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001750
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001751 /* now retrieve the path */
1752 end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
1753 beg = http_txn_get_path(txn);
1754 if (!beg)
1755 beg = end;
Willy Tarreau79e57332018-10-02 16:01:16 +02001756
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001757 for (ptr = beg; ptr < end && *ptr != '?'; ptr++);
Willy Tarreau79e57332018-10-02 16:01:16 +02001758
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001759 if (beg < ptr && *beg == '/') {
1760 while (beg < ptr)
1761 hash = *(beg++) + (hash << 6) + (hash << 16) - hash;
1762 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001763 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001764
Willy Tarreau79e57332018-10-02 16:01:16 +02001765 hash = full_hash(hash);
1766
1767 smp->data.type = SMP_T_SINT;
1768 smp->data.u.sint = hash;
1769 smp->flags = SMP_F_VOL_1ST;
1770 return 1;
1771}
1772
1773/* This concatenates the source address with the 32-bit hash of the Host and
1774 * path as returned by smp_fetch_base32(). The idea is to have per-source and
1775 * per-path counters. The result is a binary block from 8 to 20 bytes depending
1776 * on the source address length. The path hash is stored before the address so
1777 * that in environments where IPv6 is insignificant, truncating the output to
1778 * 8 bytes would still work.
1779 */
1780static int smp_fetch_base32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
1781{
1782 struct buffer *temp;
1783 struct connection *cli_conn = objt_conn(smp->sess->origin);
1784
1785 if (!cli_conn)
1786 return 0;
1787
1788 if (!smp_fetch_base32(args, smp, kw, private))
1789 return 0;
1790
1791 temp = get_trash_chunk();
1792 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
1793 temp->data += sizeof(unsigned int);
1794
1795 switch (cli_conn->addr.from.ss_family) {
1796 case AF_INET:
1797 memcpy(temp->area + temp->data,
1798 &((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr,
1799 4);
1800 temp->data += 4;
1801 break;
1802 case AF_INET6:
1803 memcpy(temp->area + temp->data,
1804 &((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr,
1805 16);
1806 temp->data += 16;
1807 break;
1808 default:
1809 return 0;
1810 }
1811
1812 smp->data.u.str = *temp;
1813 smp->data.type = SMP_T_BIN;
1814 return 1;
1815}
1816
1817/* Extracts the query string, which comes after the question mark '?'. If no
1818 * question mark is found, nothing is returned. Otherwise it returns a sample
1819 * of type string carrying the whole query string.
1820 */
1821static int smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private)
1822{
Willy Tarreau79e57332018-10-02 16:01:16 +02001823 char *ptr, *end;
1824
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001825 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1826 /* HTX version */
1827 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001828 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001829
1830 if (!htx)
1831 return 0;
1832
1833 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01001834 ptr = HTX_SL_REQ_UPTR(sl);
1835 end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001836 }
1837 else {
1838 /* LEGACY version */
1839 struct http_txn *txn;
1840
1841 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02001842
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001843 txn = smp->strm->txn;
1844 ptr = ci_head(txn->req.chn) + txn->req.sl.rq.u;
1845 end = ptr + txn->req.sl.rq.u_l;
1846 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001847
1848 /* look up the '?' */
1849 do {
1850 if (ptr == end)
1851 return 0;
1852 } while (*ptr++ != '?');
1853
1854 smp->data.type = SMP_T_STR;
1855 smp->data.u.str.area = ptr;
1856 smp->data.u.str.data = end - ptr;
1857 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1858 return 1;
1859}
1860
1861static int smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private)
1862{
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001863 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1864 /* HTX version */
1865 struct htx *htx = smp_prefetch_htx(smp, args);
Willy Tarreau79e57332018-10-02 16:01:16 +02001866
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001867 if (!htx)
1868 return 0;
1869 }
1870 else {
1871 /* LEGACY version */
Willy Tarreau79e57332018-10-02 16:01:16 +02001872
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001873 /* Note: hdr_idx.v cannot be NULL in this ACL because the ACL is tagged
1874 * as a layer7 ACL, which involves automatic allocation of hdr_idx.
1875 */
1876 CHECK_HTTP_MESSAGE_FIRST_PERM();
1877 }
1878 smp->data.type = SMP_T_BOOL;
Willy Tarreau79e57332018-10-02 16:01:16 +02001879 smp->data.u.sint = 1;
1880 return 1;
1881}
1882
1883/* return a valid test if the current request is the first one on the connection */
1884static int smp_fetch_http_first_req(const struct arg *args, struct sample *smp, const char *kw, void *private)
1885{
1886 smp->data.type = SMP_T_BOOL;
1887 smp->data.u.sint = !(smp->strm->txn->flags & TX_NOT_FIRST);
1888 return 1;
1889}
1890
1891/* Accepts exactly 1 argument of type userlist */
1892static int smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private)
1893{
1894
1895 if (!args || args->type != ARGT_USR)
1896 return 0;
1897
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001898 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1899 /* HTX version */
1900 struct htx *htx = smp_prefetch_htx(smp, args);
Willy Tarreau79e57332018-10-02 16:01:16 +02001901
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001902 if (!htx)
1903 return 0;
1904 }
1905 else {
1906 /* LEGACY version */
1907 CHECK_HTTP_MESSAGE_FIRST();
1908 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001909
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001910 if (!get_http_auth(smp))
1911 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001912 smp->data.type = SMP_T_BOOL;
1913 smp->data.u.sint = check_user(args->data.usr, smp->strm->txn->auth.user,
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001914 smp->strm->txn->auth.pass);
Willy Tarreau79e57332018-10-02 16:01:16 +02001915 return 1;
1916}
1917
1918/* Accepts exactly 1 argument of type userlist */
1919static int smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private)
1920{
1921 if (!args || args->type != ARGT_USR)
1922 return 0;
1923
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001924 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
1925 /* HTX version */
1926 struct htx *htx = smp_prefetch_htx(smp, args);
1927
1928 if (!htx)
1929 return 0;
1930 }
1931 else {
1932 /* LEGACY version */
1933 CHECK_HTTP_MESSAGE_FIRST();
1934 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001935
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001936 if (!get_http_auth(smp))
Willy Tarreau79e57332018-10-02 16:01:16 +02001937 return 0;
1938
1939 /* if the user does not belong to the userlist or has a wrong password,
1940 * report that it unconditionally does not match. Otherwise we return
1941 * a string containing the username.
1942 */
1943 if (!check_user(args->data.usr, smp->strm->txn->auth.user,
1944 smp->strm->txn->auth.pass))
1945 return 0;
1946
1947 /* pat_match_auth() will need the user list */
1948 smp->ctx.a[0] = args->data.usr;
1949
1950 smp->data.type = SMP_T_STR;
1951 smp->flags = SMP_F_CONST;
1952 smp->data.u.str.area = smp->strm->txn->auth.user;
1953 smp->data.u.str.data = strlen(smp->strm->txn->auth.user);
1954
1955 return 1;
1956}
1957
1958/* Fetch a captured HTTP request header. The index is the position of
1959 * the "capture" option in the configuration file
1960 */
1961static int smp_fetch_capture_req_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1962{
1963 struct proxy *fe = strm_fe(smp->strm);
1964 int idx;
1965
1966 if (!args || args->type != ARGT_SINT)
1967 return 0;
1968
1969 idx = args->data.sint;
1970
1971 if (idx > (fe->nb_req_cap - 1) || smp->strm->req_cap == NULL || smp->strm->req_cap[idx] == NULL)
1972 return 0;
1973
1974 smp->data.type = SMP_T_STR;
1975 smp->flags |= SMP_F_CONST;
1976 smp->data.u.str.area = smp->strm->req_cap[idx];
1977 smp->data.u.str.data = strlen(smp->strm->req_cap[idx]);
1978
1979 return 1;
1980}
1981
1982/* Fetch a captured HTTP response header. The index is the position of
1983 * the "capture" option in the configuration file
1984 */
1985static int smp_fetch_capture_res_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1986{
1987 struct proxy *fe = strm_fe(smp->strm);
1988 int idx;
1989
1990 if (!args || args->type != ARGT_SINT)
1991 return 0;
1992
1993 idx = args->data.sint;
1994
1995 if (idx > (fe->nb_rsp_cap - 1) || smp->strm->res_cap == NULL || smp->strm->res_cap[idx] == NULL)
1996 return 0;
1997
1998 smp->data.type = SMP_T_STR;
1999 smp->flags |= SMP_F_CONST;
2000 smp->data.u.str.area = smp->strm->res_cap[idx];
2001 smp->data.u.str.data = strlen(smp->strm->res_cap[idx]);
2002
2003 return 1;
2004}
2005
2006/* Extracts the METHOD in the HTTP request, the txn->uri should be filled before the call */
2007static int smp_fetch_capture_req_method(const struct arg *args, struct sample *smp, const char *kw, void *private)
2008{
2009 struct buffer *temp;
2010 struct http_txn *txn = smp->strm->txn;
2011 char *ptr;
2012
2013 if (!txn || !txn->uri)
2014 return 0;
2015
2016 ptr = txn->uri;
2017
2018 while (*ptr != ' ' && *ptr != '\0') /* find first space */
2019 ptr++;
2020
2021 temp = get_trash_chunk();
2022 temp->area = txn->uri;
2023 temp->data = ptr - txn->uri;
2024 smp->data.u.str = *temp;
2025 smp->data.type = SMP_T_STR;
2026 smp->flags = SMP_F_CONST;
2027
2028 return 1;
2029
2030}
2031
2032/* Extracts the path in the HTTP request, the txn->uri should be filled before the call */
2033static int smp_fetch_capture_req_uri(const struct arg *args, struct sample *smp, const char *kw, void *private)
2034{
2035 struct http_txn *txn = smp->strm->txn;
2036 struct ist path;
2037 const char *ptr;
2038
2039 if (!txn || !txn->uri)
2040 return 0;
2041
2042 ptr = txn->uri;
2043
2044 while (*ptr != ' ' && *ptr != '\0') /* find first space */
2045 ptr++;
2046
2047 if (!*ptr)
2048 return 0;
2049
Christopher Faulet78337bb2018-11-15 14:35:18 +01002050 /* skip the first space and find space after URI */
2051 path = ist2(++ptr, 0);
2052 while (*ptr != ' ' && *ptr != '\0')
2053 ptr++;
2054 path.len = ptr - path.ptr;
Willy Tarreau79e57332018-10-02 16:01:16 +02002055
Christopher Faulet78337bb2018-11-15 14:35:18 +01002056 path = http_get_path(path);
Willy Tarreau79e57332018-10-02 16:01:16 +02002057 if (!path.ptr)
2058 return 0;
2059
2060 smp->data.u.str.area = path.ptr;
2061 smp->data.u.str.data = path.len;
2062 smp->data.type = SMP_T_STR;
2063 smp->flags = SMP_F_CONST;
2064
2065 return 1;
2066}
2067
2068/* Retrieves the HTTP version from the request (either 1.0 or 1.1) and emits it
2069 * as a string (either "HTTP/1.0" or "HTTP/1.1").
2070 */
2071static int smp_fetch_capture_req_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
2072{
2073 struct http_txn *txn = smp->strm->txn;
2074
2075 if (!txn || txn->req.msg_state < HTTP_MSG_HDR_FIRST)
2076 return 0;
2077
2078 if (txn->req.flags & HTTP_MSGF_VER_11)
2079 smp->data.u.str.area = "HTTP/1.1";
2080 else
2081 smp->data.u.str.area = "HTTP/1.0";
2082
2083 smp->data.u.str.data = 8;
2084 smp->data.type = SMP_T_STR;
2085 smp->flags = SMP_F_CONST;
2086 return 1;
2087
2088}
2089
2090/* Retrieves the HTTP version from the response (either 1.0 or 1.1) and emits it
2091 * as a string (either "HTTP/1.0" or "HTTP/1.1").
2092 */
2093static int smp_fetch_capture_res_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
2094{
2095 struct http_txn *txn = smp->strm->txn;
2096
2097 if (!txn || txn->rsp.msg_state < HTTP_MSG_HDR_FIRST)
2098 return 0;
2099
2100 if (txn->rsp.flags & HTTP_MSGF_VER_11)
2101 smp->data.u.str.area = "HTTP/1.1";
2102 else
2103 smp->data.u.str.area = "HTTP/1.0";
2104
2105 smp->data.u.str.data = 8;
2106 smp->data.type = SMP_T_STR;
2107 smp->flags = SMP_F_CONST;
2108 return 1;
2109
2110}
2111
2112/* Iterate over all cookies present in a message. The context is stored in
2113 * smp->ctx.a[0] for the in-header position, smp->ctx.a[1] for the
2114 * end-of-header-value, and smp->ctx.a[2] for the hdr_ctx. Depending on
2115 * the direction, multiple cookies may be parsed on the same line or not.
2116 * The cookie name is in args and the name length in args->data.str.len.
2117 * Accepts exactly 1 argument of type string. If the input options indicate
2118 * that no iterating is desired, then only last value is fetched if any.
2119 * The returned sample is of type CSTR. Can be used to parse cookies in other
2120 * files.
2121 */
2122static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
2123{
Willy Tarreau79e57332018-10-02 16:01:16 +02002124 int occ = 0;
2125 int found = 0;
2126
2127 if (!args || args->type != ARGT_STR)
2128 return 0;
2129
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002130 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
2131 /* HTX version */
2132 struct htx *htx = smp_prefetch_htx(smp, args);
2133 struct http_hdr_ctx *ctx = smp->ctx.a[2];
2134 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +02002135
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002136 if (!ctx) {
2137 /* first call */
2138 ctx = &static_http_hdr_ctx;
2139 ctx->blk = NULL;
2140 smp->ctx.a[2] = ctx;
2141 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002142
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002143 if (!htx)
2144 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002145
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002146 hdr = (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
2147 ? ist("Cookie")
2148 : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02002149
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002150 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
2151 /* no explicit occurrence and single fetch => last cookie by default */
2152 occ = -1;
Willy Tarreau79e57332018-10-02 16:01:16 +02002153
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002154 /* OK so basically here, either we want only one value and it's the
2155 * last one, or we want to iterate over all of them and we fetch the
2156 * next one.
Willy Tarreau79e57332018-10-02 16:01:16 +02002157 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002158
2159 if (!(smp->flags & SMP_F_NOT_LAST)) {
2160 /* search for the header from the beginning, we must first initialize
2161 * the search parameters.
2162 */
2163 smp->ctx.a[0] = NULL;
2164 ctx->blk = NULL;
2165 }
2166
2167 smp->flags |= SMP_F_VOL_HDR;
2168 while (1) {
2169 /* Note: smp->ctx.a[0] == NULL every time we need to fetch a new header */
2170 if (!smp->ctx.a[0]) {
2171 if (!http_find_header(htx, hdr, ctx, 0))
2172 goto out;
2173
2174 if (ctx->value.len < args->data.str.data + 1)
2175 continue;
2176
2177 smp->ctx.a[0] = ctx->value.ptr;
2178 smp->ctx.a[1] = smp->ctx.a[0] + ctx->value.len;
2179 }
2180
2181 smp->data.type = SMP_T_STR;
2182 smp->flags |= SMP_F_CONST;
2183 smp->ctx.a[0] = http_extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1],
2184 args->data.str.area, args->data.str.data,
2185 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
2186 &smp->data.u.str.area,
2187 &smp->data.u.str.data);
2188 if (smp->ctx.a[0]) {
2189 found = 1;
2190 if (occ >= 0) {
2191 /* one value was returned into smp->data.u.str.{str,len} */
2192 smp->flags |= SMP_F_NOT_LAST;
2193 return 1;
2194 }
2195 }
2196 /* if we're looking for last occurrence, let's loop */
2197 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002198 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002199 else {
2200 /* LEGACY version */
2201 struct http_txn *txn;
2202 struct hdr_idx *idx;
2203 struct hdr_ctx *ctx = smp->ctx.a[2];
2204 const struct http_msg *msg;
2205 const char *hdr_name;
2206 int hdr_name_len;
2207 char *sol;
Willy Tarreau79e57332018-10-02 16:01:16 +02002208
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002209 if (!ctx) {
2210 /* first call */
2211 ctx = &static_hdr_ctx;
2212 ctx->idx = 0;
2213 smp->ctx.a[2] = ctx;
2214 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002215
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002216 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02002217
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002218 txn = smp->strm->txn;
2219 idx = &smp->strm->txn->hdr_idx;
Willy Tarreau79e57332018-10-02 16:01:16 +02002220
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002221 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
2222 msg = &txn->req;
2223 hdr_name = "Cookie";
2224 hdr_name_len = 6;
2225 } else {
2226 msg = &txn->rsp;
2227 hdr_name = "Set-Cookie";
2228 hdr_name_len = 10;
Willy Tarreau79e57332018-10-02 16:01:16 +02002229 }
2230
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002231 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
2232 /* no explicit occurrence and single fetch => last cookie by default */
2233 occ = -1;
2234
2235 /* OK so basically here, either we want only one value and it's the
2236 * last one, or we want to iterate over all of them and we fetch the
2237 * next one.
2238 */
2239
2240 sol = ci_head(msg->chn);
2241 if (!(smp->flags & SMP_F_NOT_LAST)) {
2242 /* search for the header from the beginning, we must first initialize
2243 * the search parameters.
2244 */
2245 smp->ctx.a[0] = NULL;
2246 ctx->idx = 0;
2247 }
2248
2249 smp->flags |= SMP_F_VOL_HDR;
2250
2251 while (1) {
2252 /* Note: smp->ctx.a[0] == NULL every time we need to fetch a new header */
2253 if (!smp->ctx.a[0]) {
2254 if (!http_find_header2(hdr_name, hdr_name_len, sol, idx, ctx))
2255 goto out;
2256
2257 if (ctx->vlen < args->data.str.data + 1)
2258 continue;
2259
2260 smp->ctx.a[0] = ctx->line + ctx->val;
2261 smp->ctx.a[1] = smp->ctx.a[0] + ctx->vlen;
2262 }
2263
2264 smp->data.type = SMP_T_STR;
2265 smp->flags |= SMP_F_CONST;
2266 smp->ctx.a[0] = http_extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1],
2267 args->data.str.area, args->data.str.data,
2268 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
2269 &smp->data.u.str.area, &smp->data.u.str.data);
2270 if (smp->ctx.a[0]) {
2271 found = 1;
2272 if (occ >= 0) {
2273 /* one value was returned into smp->data.u.str.{str,len} */
2274 smp->flags |= SMP_F_NOT_LAST;
2275 return 1;
2276 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002277 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002278 /* if we're looking for last occurrence, let's loop */
Willy Tarreau79e57332018-10-02 16:01:16 +02002279 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002280 }
2281 /* all cookie headers and values were scanned. If we're looking for the
2282 * last occurrence, we may return it now.
2283 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002284 out:
Willy Tarreau79e57332018-10-02 16:01:16 +02002285 smp->flags &= ~SMP_F_NOT_LAST;
2286 return found;
2287}
2288
2289/* Iterate over all cookies present in a request to count how many occurrences
2290 * match the name in args and args->data.str.len. If <multi> is non-null, then
2291 * multiple cookies may be parsed on the same line. The returned sample is of
2292 * type UINT. Accepts exactly 1 argument of type string.
2293 */
2294static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
2295{
Willy Tarreau79e57332018-10-02 16:01:16 +02002296 char *val_beg, *val_end;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002297 int cnt;
Willy Tarreau79e57332018-10-02 16:01:16 +02002298
2299 if (!args || args->type != ARGT_STR)
2300 return 0;
2301
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002302 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
2303 /* HTX version */
2304 struct htx *htx = smp_prefetch_htx(smp, args);
2305 struct http_hdr_ctx ctx;
2306 struct ist hdr;
2307
2308 if (!htx)
2309 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002310
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002311 hdr = (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
2312 ? ist("Cookie")
2313 : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02002314
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002315 val_end = val_beg = NULL;
2316 ctx.blk = NULL;
2317 cnt = 0;
2318 while (1) {
2319 /* Note: val_beg == NULL every time we need to fetch a new header */
2320 if (!val_beg) {
2321 if (!http_find_header(htx, hdr, &ctx, 0))
2322 break;
Willy Tarreau79e57332018-10-02 16:01:16 +02002323
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002324 if (ctx.value.len < args->data.str.data + 1)
2325 continue;
Willy Tarreau79e57332018-10-02 16:01:16 +02002326
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002327 val_beg = ctx.value.ptr;
2328 val_end = val_beg + ctx.value.len;
2329 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002330
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002331 smp->data.type = SMP_T_STR;
2332 smp->flags |= SMP_F_CONST;
2333 while ((val_beg = http_extract_cookie_value(val_beg, val_end,
2334 args->data.str.area, args->data.str.data,
2335 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
2336 &smp->data.u.str.area,
2337 &smp->data.u.str.data))) {
2338 cnt++;
2339 }
2340 }
2341 }
2342 else {
2343 /* LEGACY version */
2344 struct http_txn *txn;
2345 struct hdr_idx *idx;
2346 struct hdr_ctx ctx;
2347 const struct http_msg *msg;
2348 const char *hdr_name;
2349 int hdr_name_len;
2350 char *sol;
Willy Tarreau79e57332018-10-02 16:01:16 +02002351
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002352 CHECK_HTTP_MESSAGE_FIRST();
2353
2354 txn = smp->strm->txn;
2355 idx = &smp->strm->txn->hdr_idx;
2356
2357 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) {
2358 msg = &txn->req;
2359 hdr_name = "Cookie";
2360 hdr_name_len = 6;
2361 } else {
2362 msg = &txn->rsp;
2363 hdr_name = "Set-Cookie";
2364 hdr_name_len = 10;
Willy Tarreau79e57332018-10-02 16:01:16 +02002365 }
2366
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002367 sol = ci_head(msg->chn);
2368 val_end = val_beg = NULL;
2369 ctx.idx = 0;
2370 cnt = 0;
2371
2372 while (1) {
2373 /* Note: val_beg == NULL every time we need to fetch a new header */
2374 if (!val_beg) {
2375 if (!http_find_header2(hdr_name, hdr_name_len, sol, idx, &ctx))
2376 break;
2377
2378 if (ctx.vlen < args->data.str.data + 1)
2379 continue;
2380
2381 val_beg = ctx.line + ctx.val;
2382 val_end = val_beg + ctx.vlen;
2383 }
2384
2385 smp->data.type = SMP_T_STR;
2386 smp->flags |= SMP_F_CONST;
2387 while ((val_beg = http_extract_cookie_value(val_beg, val_end,
2388 args->data.str.area, args->data.str.data,
2389 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
2390 &smp->data.u.str.area, &smp->data.u.str.data))) {
2391 cnt++;
2392 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002393 }
2394 }
2395
2396 smp->data.type = SMP_T_SINT;
2397 smp->data.u.sint = cnt;
2398 smp->flags |= SMP_F_VOL_HDR;
2399 return 1;
2400}
2401
2402/* Fetch an cookie's integer value. The integer value is returned. It
2403 * takes a mandatory argument of type string. It relies on smp_fetch_cookie().
2404 */
2405static int smp_fetch_cookie_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
2406{
2407 int ret = smp_fetch_cookie(args, smp, kw, private);
2408
2409 if (ret > 0) {
2410 smp->data.type = SMP_T_SINT;
2411 smp->data.u.sint = strl2ic(smp->data.u.str.area,
2412 smp->data.u.str.data);
2413 }
2414
2415 return ret;
2416}
2417
2418/************************************************************************/
2419/* The code below is dedicated to sample fetches */
2420/************************************************************************/
2421
2422/* This scans a URL-encoded query string. It takes an optionally wrapping
2423 * string whose first contigous chunk has its beginning in ctx->a[0] and end
2424 * in ctx->a[1], and the optional second part in (ctx->a[2]..ctx->a[3]). The
2425 * pointers are updated for next iteration before leaving.
2426 */
2427static int smp_fetch_param(char delim, const char *name, int name_len, const struct arg *args, struct sample *smp, const char *kw, void *private)
2428{
2429 const char *vstart, *vend;
2430 struct buffer *temp;
2431 const char **chunks = (const char **)smp->ctx.a;
2432
2433 if (!http_find_next_url_param(chunks, name, name_len,
2434 &vstart, &vend, delim))
2435 return 0;
2436
2437 /* Create sample. If the value is contiguous, return the pointer as CONST,
2438 * if the value is wrapped, copy-it in a buffer.
2439 */
2440 smp->data.type = SMP_T_STR;
2441 if (chunks[2] &&
2442 vstart >= chunks[0] && vstart <= chunks[1] &&
2443 vend >= chunks[2] && vend <= chunks[3]) {
2444 /* Wrapped case. */
2445 temp = get_trash_chunk();
2446 memcpy(temp->area, vstart, chunks[1] - vstart);
2447 memcpy(temp->area + ( chunks[1] - vstart ), chunks[2],
2448 vend - chunks[2]);
2449 smp->data.u.str.area = temp->area;
2450 smp->data.u.str.data = ( chunks[1] - vstart ) + ( vend - chunks[2] );
2451 } else {
2452 /* Contiguous case. */
2453 smp->data.u.str.area = (char *)vstart;
2454 smp->data.u.str.data = vend - vstart;
2455 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
2456 }
2457
2458 /* Update context, check wrapping. */
2459 chunks[0] = vend;
2460 if (chunks[2] && vend >= chunks[2] && vend <= chunks[3]) {
2461 chunks[1] = chunks[3];
2462 chunks[2] = NULL;
2463 }
2464
2465 if (chunks[0] < chunks[1])
2466 smp->flags |= SMP_F_NOT_LAST;
2467
2468 return 1;
2469}
2470
2471/* This function iterates over each parameter of the query string. It uses
2472 * ctx->a[0] and ctx->a[1] to store the beginning and end of the current
2473 * parameter. Since it uses smp_fetch_param(), ctx->a[2..3] are both NULL.
2474 * An optional parameter name is passed in args[0], otherwise any parameter is
2475 * considered. It supports an optional delimiter argument for the beginning of
2476 * the string in args[1], which defaults to "?".
2477 */
2478static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
2479{
Willy Tarreau79e57332018-10-02 16:01:16 +02002480 char delim = '?';
2481 const char *name;
2482 int name_len;
2483
2484 if (!args ||
2485 (args[0].type && args[0].type != ARGT_STR) ||
2486 (args[1].type && args[1].type != ARGT_STR))
2487 return 0;
2488
2489 name = "";
2490 name_len = 0;
2491 if (args->type == ARGT_STR) {
2492 name = args->data.str.area;
2493 name_len = args->data.str.data;
2494 }
2495
2496 if (args[1].type)
2497 delim = *args[1].data.str.area;
2498
2499 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002500 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
2501 /* HTX version */
2502 struct htx *htx = smp_prefetch_htx(smp, args);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01002503 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02002504
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002505 if (!htx)
2506 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002507
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002508 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01002509 smp->ctx.a[0] = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), delim);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002510 if (!smp->ctx.a[0])
2511 return 0;
2512
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01002513 smp->ctx.a[1] = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002514 }
2515 else {
2516 /* LEGACY version */
2517 struct http_msg *msg;
2518
2519 CHECK_HTTP_MESSAGE_FIRST();
2520
2521 msg = &smp->strm->txn->req;
2522
2523 smp->ctx.a[0] = http_find_param_list(ci_head(msg->chn) + msg->sl.rq.u,
2524 msg->sl.rq.u_l, delim);
2525 if (!smp->ctx.a[0])
2526 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002527
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002528 smp->ctx.a[1] = ci_head(msg->chn) + msg->sl.rq.u + msg->sl.rq.u_l;
2529 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002530
2531 /* Assume that the context is filled with NULL pointer
2532 * before the first call.
2533 * smp->ctx.a[2] = NULL;
2534 * smp->ctx.a[3] = NULL;
2535 */
2536 }
2537
2538 return smp_fetch_param(delim, name, name_len, args, smp, kw, private);
2539}
2540
2541/* This function iterates over each parameter of the body. This requires
2542 * that the body has been waited for using http-buffer-request. It uses
2543 * ctx->a[0] and ctx->a[1] to store the beginning and end of the first
2544 * contigous part of the body, and optionally ctx->a[2..3] to reference the
2545 * optional second part if the body wraps at the end of the buffer. An optional
2546 * parameter name is passed in args[0], otherwise any parameter is considered.
2547 */
2548static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
2549{
Willy Tarreau79e57332018-10-02 16:01:16 +02002550 const char *name;
2551 int name_len;
2552
2553 if (!args || (args[0].type && args[0].type != ARGT_STR))
2554 return 0;
2555
2556 name = "";
2557 name_len = 0;
2558 if (args[0].type == ARGT_STR) {
2559 name = args[0].data.str.area;
2560 name_len = args[0].data.str.data;
2561 }
2562
2563 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002564 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
2565 /* HTX version */
2566 struct htx *htx = smp_prefetch_htx(smp, args);
2567 struct buffer *temp;
2568 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +02002569
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002570 if (!htx)
2571 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002572
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002573 temp = get_trash_chunk();
2574 for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
2575 struct htx_blk *blk = htx_get_blk(htx, pos);
2576 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +02002577
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002578 if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
2579 break;
2580 if (type == HTX_BLK_DATA) {
Christopher Fauletc59ff232018-12-03 13:58:44 +01002581 if (!htx_data_to_h1(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002582 return 0;
2583 }
2584 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002585
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002586 smp->ctx.a[0] = temp->area;
2587 smp->ctx.a[1] = temp->area + temp->data;
Willy Tarreau79e57332018-10-02 16:01:16 +02002588
2589 /* Assume that the context is filled with NULL pointer
2590 * before the first call.
2591 * smp->ctx.a[2] = NULL;
2592 * smp->ctx.a[3] = NULL;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002593 */
Willy Tarreau79e57332018-10-02 16:01:16 +02002594 }
2595 else {
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002596 /* LEGACY version */
2597 struct http_msg *msg;
2598 unsigned long len;
2599 unsigned long block1;
2600 char *body;
2601
2602 CHECK_HTTP_MESSAGE_FIRST();
2603
2604 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ)
2605 msg = &smp->strm->txn->req;
2606 else
2607 msg = &smp->strm->txn->rsp;
2608
2609 len = http_body_bytes(msg);
2610 body = c_ptr(msg->chn, -http_data_rewind(msg));
2611
2612 block1 = len;
2613 if (block1 > b_wrap(&msg->chn->buf) - body)
2614 block1 = b_wrap(&msg->chn->buf) - body;
2615
2616 if (block1 == len) {
2617 /* buffer is not wrapped (or empty) */
2618 smp->ctx.a[0] = body;
2619 smp->ctx.a[1] = body + len;
2620
2621 /* Assume that the context is filled with NULL pointer
2622 * before the first call.
2623 * smp->ctx.a[2] = NULL;
2624 * smp->ctx.a[3] = NULL;
2625 */
2626 }
2627 else {
2628 /* buffer is wrapped, we need to defragment it */
2629 smp->ctx.a[0] = body;
2630 smp->ctx.a[1] = body + block1;
2631 smp->ctx.a[2] = b_orig(&msg->chn->buf);
2632 smp->ctx.a[3] = b_orig(&msg->chn->buf) + ( len - block1 );
2633 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002634 }
2635 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002636
Willy Tarreau79e57332018-10-02 16:01:16 +02002637 return smp_fetch_param('&', name, name_len, args, smp, kw, private);
2638}
2639
2640/* Return the signed integer value for the specified url parameter (see url_param
2641 * above).
2642 */
2643static int smp_fetch_url_param_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
2644{
2645 int ret = smp_fetch_url_param(args, smp, kw, private);
2646
2647 if (ret > 0) {
2648 smp->data.type = SMP_T_SINT;
2649 smp->data.u.sint = strl2ic(smp->data.u.str.area,
2650 smp->data.u.str.data);
2651 }
2652
2653 return ret;
2654}
2655
2656/* This produces a 32-bit hash of the concatenation of the first occurrence of
2657 * the Host header followed by the path component if it begins with a slash ('/').
2658 * This means that '*' will not be added, resulting in exactly the first Host
2659 * entry. If no Host header is found, then the path is used. The resulting value
2660 * is hashed using the url hash followed by a full avalanche hash and provides a
2661 * 32-bit integer value. This fetch is useful for tracking per-URL activity on
2662 * high-traffic sites without having to store whole paths.
2663 * this differs from the base32 functions in that it includes the url parameters
2664 * as well as the path
2665 */
2666static int smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private)
2667{
Willy Tarreau79e57332018-10-02 16:01:16 +02002668 unsigned int hash = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02002669
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002670 if (IS_HTX_SMP(smp) || (smp->px->mode == PR_MODE_TCP)) {
2671 /* HTX version */
2672 struct htx *htx = smp_prefetch_htx(smp, args);
2673 struct http_hdr_ctx ctx;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01002674 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002675 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02002676
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002677 if (!htx)
2678 return 0;
2679
2680 ctx.blk = NULL;
2681 if (http_find_header(htx, ist("Host"), &ctx, 1)) {
2682 /* OK we have the header value in ctx.value */
2683 while (ctx.value.len--)
2684 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
2685 }
2686
2687 /* now retrieve the path */
2688 sl = http_find_stline(htx);
Christopher Fauletf1ba18d2018-11-26 21:37:08 +01002689 path = http_get_path(htx_sl_req_uri(sl));
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002690 while (path.len > 0 && *(path.ptr) != '?') {
2691 path.ptr++;
2692 path.len--;
2693 }
2694 if (path.len && *(path.ptr) == '/') {
2695 while (path.len--)
2696 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
2697 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002698 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002699 else {
2700 /* LEGACY version */
2701 struct http_txn *txn;
2702 struct hdr_ctx ctx;
2703 char *ptr, *beg, *end;
2704 int len;
2705
2706 CHECK_HTTP_MESSAGE_FIRST();
Willy Tarreau79e57332018-10-02 16:01:16 +02002707
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002708 txn = smp->strm->txn;
2709 ctx.idx = 0;
2710 if (http_find_header2("Host", 4, ci_head(txn->req.chn), &txn->hdr_idx, &ctx)) {
2711 /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */
2712 ptr = ctx.line + ctx.val;
2713 len = ctx.vlen;
2714 while (len--)
2715 hash = *(ptr++) + (hash << 6) + (hash << 16) - hash;
2716 }
2717
2718 /* now retrieve the path */
2719 end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
2720 beg = http_txn_get_path(txn);
2721 if (!beg)
2722 beg = end;
Willy Tarreau79e57332018-10-02 16:01:16 +02002723
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002724 for (ptr = beg; ptr < end ; ptr++);
Willy Tarreau79e57332018-10-02 16:01:16 +02002725
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002726 if (beg < ptr && *beg == '/') {
2727 while (beg < ptr)
2728 hash = *(beg++) + (hash << 6) + (hash << 16) - hash;
2729 }
Willy Tarreau79e57332018-10-02 16:01:16 +02002730 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002731
Willy Tarreau79e57332018-10-02 16:01:16 +02002732 hash = full_hash(hash);
2733
2734 smp->data.type = SMP_T_SINT;
2735 smp->data.u.sint = hash;
2736 smp->flags = SMP_F_VOL_1ST;
2737 return 1;
2738}
2739
2740/* This concatenates the source address with the 32-bit hash of the Host and
2741 * URL as returned by smp_fetch_base32(). The idea is to have per-source and
2742 * per-url counters. The result is a binary block from 8 to 20 bytes depending
2743 * on the source address length. The URL hash is stored before the address so
2744 * that in environments where IPv6 is insignificant, truncating the output to
2745 * 8 bytes would still work.
2746 */
2747static int smp_fetch_url32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
2748{
2749 struct buffer *temp;
2750 struct connection *cli_conn = objt_conn(smp->sess->origin);
2751
2752 if (!cli_conn)
2753 return 0;
2754
2755 if (!smp_fetch_url32(args, smp, kw, private))
2756 return 0;
2757
2758 temp = get_trash_chunk();
2759 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
2760 temp->data += sizeof(unsigned int);
2761
2762 switch (cli_conn->addr.from.ss_family) {
2763 case AF_INET:
2764 memcpy(temp->area + temp->data,
2765 &((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr,
2766 4);
2767 temp->data += 4;
2768 break;
2769 case AF_INET6:
2770 memcpy(temp->area + temp->data,
2771 &((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr,
2772 16);
2773 temp->data += 16;
2774 break;
2775 default:
2776 return 0;
2777 }
2778
2779 smp->data.u.str = *temp;
2780 smp->data.type = SMP_T_BIN;
2781 return 1;
2782}
2783
2784/************************************************************************/
2785/* Other utility functions */
2786/************************************************************************/
2787
2788/* This function is used to validate the arguments passed to any "hdr" fetch
2789 * keyword. These keywords support an optional positive or negative occurrence
2790 * number. We must ensure that the number is greater than -MAX_HDR_HISTORY. It
2791 * is assumed that the types are already the correct ones. Returns 0 on error,
2792 * non-zero if OK. If <err> is not NULL, it will be filled with a pointer to an
2793 * error message in case of error, that the caller is responsible for freeing.
2794 * The initial location must either be freeable or NULL.
2795 * Note: this function's pointer is checked from Lua.
2796 */
2797int val_hdr(struct arg *arg, char **err_msg)
2798{
2799 if (arg && arg[1].type == ARGT_SINT && arg[1].data.sint < -MAX_HDR_HISTORY) {
2800 memprintf(err_msg, "header occurrence must be >= %d", -MAX_HDR_HISTORY);
2801 return 0;
2802 }
2803 return 1;
2804}
2805
2806/************************************************************************/
2807/* All supported sample fetch keywords must be declared here. */
2808/************************************************************************/
2809
2810/* Note: must not be declared <const> as its list will be overwritten */
2811static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
2812 { "base", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2813 { "base32", smp_fetch_base32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2814 { "base32+src", smp_fetch_base32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2815
2816 /* capture are allocated and are permanent in the stream */
2817 { "capture.req.hdr", smp_fetch_capture_req_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRQHP },
2818
2819 /* retrieve these captures from the HTTP logs */
2820 { "capture.req.method", smp_fetch_capture_req_method, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2821 { "capture.req.uri", smp_fetch_capture_req_uri, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2822 { "capture.req.ver", smp_fetch_capture_req_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2823
2824 { "capture.res.hdr", smp_fetch_capture_res_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRSHP },
2825 { "capture.res.ver", smp_fetch_capture_res_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2826
2827 /* cookie is valid in both directions (eg: for "stick ...") but cook*
2828 * are only here to match the ACL's name, are request-only and are used
2829 * for ACL compatibility only.
2830 */
2831 { "cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2832 { "cookie", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
2833 { "cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2834 { "cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2835
2836 /* hdr is valid in both directions (eg: for "stick ...") but hdr_* are
2837 * only here to match the ACL's name, are request-only and are used for
2838 * ACL compatibility only.
2839 */
2840 { "hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
2841 { "hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2842 { "hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2843 { "hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2844
2845 { "http_auth", smp_fetch_http_auth, ARG1(1,USR), NULL, SMP_T_BOOL, SMP_USE_HRQHV },
2846 { "http_auth_group", smp_fetch_http_auth_grp, ARG1(1,USR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2847 { "http_first_req", smp_fetch_http_first_req, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2848 { "method", smp_fetch_meth, 0, NULL, SMP_T_METH, SMP_USE_HRQHP },
2849 { "path", smp_fetch_path, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2850 { "query", smp_fetch_query, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2851
2852 /* HTTP protocol on the request path */
2853 { "req.proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2854 { "req_proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2855
2856 /* HTTP version on the request path */
2857 { "req.ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2858 { "req_ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2859
2860 { "req.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2861 { "req.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2862 { "req.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2863 { "req.body_param", smp_fetch_body_param, ARG1(0,STR), NULL, SMP_T_BIN, SMP_USE_HRQHV },
2864
2865 { "req.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2866 { "req.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2867
2868 /* HTTP version on the response path */
2869 { "res.ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2870 { "resp_ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2871
2872 /* explicit req.{cook,hdr} are used to force the fetch direction to be request-only */
2873 { "req.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2874 { "req.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2875 { "req.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2876
2877 { "req.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2878 { "req.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2879 { "req.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2880 { "req.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2881 { "req.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2882 { "req.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2883 { "req.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2884
2885 /* explicit req.{cook,hdr} are used to force the fetch direction to be response-only */
2886 { "res.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2887 { "res.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2888 { "res.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2889
2890 { "res.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2891 { "res.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2892 { "res.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2893 { "res.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2894 { "res.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2895 { "res.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2896 { "res.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2897
2898 /* scook is valid only on the response and is used for ACL compatibility */
2899 { "scook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2900 { "scook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2901 { "scook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2902 { "set-cookie", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV }, /* deprecated */
2903
2904 /* shdr is valid only on the response and is used for ACL compatibility */
2905 { "shdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2906 { "shdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2907 { "shdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2908 { "shdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2909
2910 { "status", smp_fetch_stcode, 0, NULL, SMP_T_SINT, SMP_USE_HRSHP },
2911 { "unique-id", smp_fetch_uniqueid, 0, NULL, SMP_T_STR, SMP_SRC_L4SRV },
2912 { "url", smp_fetch_url, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2913 { "url32", smp_fetch_url32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2914 { "url32+src", smp_fetch_url32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2915 { "url_ip", smp_fetch_url_ip, 0, NULL, SMP_T_IPV4, SMP_USE_HRQHV },
2916 { "url_port", smp_fetch_url_port, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2917 { "url_param", smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2918 { "urlp" , smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2919 { "urlp_val", smp_fetch_url_param_val, ARG2(0,STR,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2920 { /* END */ },
2921}};
2922
Willy Tarreau0108d902018-11-25 19:14:37 +01002923INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
Willy Tarreau79e57332018-10-02 16:01:16 +02002924
2925/*
2926 * Local variables:
2927 * c-indent-level: 8
2928 * c-basic-offset: 8
2929 * End:
2930 */