blob: d6015d3624683dff6362e310a2f77761a23cfebf [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP actions
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/chunk.h>
20#include <common/compat.h>
21#include <common/config.h>
22#include <common/debug.h>
23#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010024#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020025#include <common/memory.h>
26#include <common/standard.h>
27#include <common/version.h>
28
29#include <types/capture.h>
30#include <types/global.h>
31
32#include <proto/acl.h>
33#include <proto/arg.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020034#include <proto/http_rules.h>
Willy Tarreau33810222019-06-12 17:44:02 +020035#include <proto/http_htx.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020036#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020037#include <proto/http_ana.h>
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +010038#include <proto/stream_interface.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020039
40
41/* This function executes one of the set-{method,path,query,uri} actions. It
42 * builds a string in the trash from the specified format string. It finds
43 * the action to be performed in <http.action>, previously filled by function
44 * parse_set_req_line(). The replacement action is excuted by the function
45 * http_action_set_req_line(). It always returns ACT_RET_CONT. If an error
46 * occurs the action is canceled, but the rule processing continue.
47 */
48static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
49 struct session *sess, struct stream *s, int flags)
50{
51 struct buffer *replace;
52 enum act_return ret = ACT_RET_ERR;
53
54 replace = alloc_trash_chunk();
55 if (!replace)
56 goto leave;
57
58 /* If we have to create a query string, prepare a '?'. */
59 if (rule->arg.http.action == 2)
60 replace->area[replace->data++] = '?';
61 replace->data += build_logline(s, replace->area + replace->data,
62 replace->size - replace->data,
63 &rule->arg.http.logfmt);
64
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020065 http_req_replace_stline(rule->arg.http.action, replace->area,
66 replace->data, px, s);
Willy Tarreau79e57332018-10-02 16:01:16 +020067
68 ret = ACT_RET_CONT;
69
70leave:
71 free_trash_chunk(replace);
72 return ret;
73}
74
75/* parse an http-request action among :
76 * set-method
77 * set-path
78 * set-query
79 * set-uri
80 *
81 * All of them accept a single argument of type string representing a log-format.
82 * The resulting rule makes use of arg->act.p[0..1] to store the log-format list
83 * head, and p[2] to store the action as an int (0=method, 1=path, 2=query, 3=uri).
84 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
85 */
86static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
87 struct act_rule *rule, char **err)
88{
89 int cur_arg = *orig_arg;
90
91 rule->action = ACT_CUSTOM;
92
93 switch (args[0][4]) {
94 case 'm' :
95 rule->arg.http.action = 0;
96 rule->action_ptr = http_action_set_req_line;
97 break;
98 case 'p' :
99 rule->arg.http.action = 1;
100 rule->action_ptr = http_action_set_req_line;
101 break;
102 case 'q' :
103 rule->arg.http.action = 2;
104 rule->action_ptr = http_action_set_req_line;
105 break;
106 case 'u' :
107 rule->arg.http.action = 3;
108 rule->action_ptr = http_action_set_req_line;
109 break;
110 default:
111 memprintf(err, "internal error: unhandled action '%s'", args[0]);
112 return ACT_RET_PRS_ERR;
113 }
114
115 if (!*args[cur_arg] ||
116 (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
117 memprintf(err, "expects exactly 1 argument <format>");
118 return ACT_RET_PRS_ERR;
119 }
120
121 LIST_INIT(&rule->arg.http.logfmt);
122 px->conf.args.ctx = ARGC_HRQ;
123 if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.logfmt, LOG_OPT_HTTP,
124 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
125 return ACT_RET_PRS_ERR;
126 }
127
128 (*orig_arg)++;
129 return ACT_RET_PRS_OK;
130}
131
Willy Tarreau33810222019-06-12 17:44:02 +0200132/* This function executes a replace-uri action. It finds its arguments in
133 * <rule>.arg.act.p[]. It builds a string in the trash from the format string
134 * previously filled by function parse_replace_uri() and will execute the regex
135 * in p[1] to replace the URI. It uses the format string present in act.p[2..3].
136 * It always returns ACT_RET_CONT. If an error occurs, the action is canceled,
137 * but the rule processing continues.
138 */
139static enum act_return http_action_replace_uri(struct act_rule *rule, struct proxy *px,
140 struct session *sess, struct stream *s, int flags)
141{
142 enum act_return ret = ACT_RET_ERR;
143 struct buffer *replace, *output;
144 struct ist uri;
145 int len;
146
147 replace = alloc_trash_chunk();
148 output = alloc_trash_chunk();
149 if (!replace || !output)
150 goto leave;
Christopher Faulet12c28b62019-07-15 16:30:24 +0200151 uri = htx_sl_req_uri(http_get_stline(htxbuf(&s->req.buf)));
Willy Tarreau33810222019-06-12 17:44:02 +0200152 if (!regex_exec_match2(rule->arg.act.p[1], uri.ptr, uri.len, MAX_MATCH, pmatch, 0))
153 goto leave;
154
155 replace->data = build_logline(s, replace->area, replace->size, (struct list *)&rule->arg.act.p[2]);
156
157 /* note: uri.ptr doesn't need to be zero-terminated because it will
158 * only be used to pick pmatch references.
159 */
160 len = exp_replace(output->area, output->size, uri.ptr, replace->area, pmatch);
161 if (len == -1)
162 goto leave;
163
164 /* 3 is the set-uri action */
Christopher Fauletfc9cfe42019-07-16 14:54:53 +0200165 http_req_replace_stline(3, output->area, len, px, s);
Willy Tarreau33810222019-06-12 17:44:02 +0200166
167 ret = ACT_RET_CONT;
168
169leave:
170 free_trash_chunk(output);
171 free_trash_chunk(replace);
172 return ret;
173}
174
175/* parse a "replace-uri" http-request action.
176 * This action takes 2 arguments (a regex and a replacement format string).
177 * The resulting rule makes use of arg->act.p[0] to store the action (0 for now),
178 * p[1] to store the compiled regex, and arg->act.p[2..3] to store the log-format
179 * list head. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
180 */
181static enum act_parse_ret parse_replace_uri(const char **args, int *orig_arg, struct proxy *px,
182 struct act_rule *rule, char **err)
183{
184 int cur_arg = *orig_arg;
185 char *error = NULL;
186
187 rule->action = ACT_CUSTOM;
188 rule->arg.act.p[0] = (void *)0; // replace-uri
189 rule->action_ptr = http_action_replace_uri;
190
191 if (!*args[cur_arg] || !*args[cur_arg+1] ||
192 (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
193 memprintf(err, "expects exactly 2 arguments <match-regex> and <replace-format>");
194 return ACT_RET_PRS_ERR;
195 }
196
197 if (!(rule->arg.act.p[1] = regex_comp(args[cur_arg], 1, 1, &error))) {
198 memprintf(err, "failed to parse the regex : %s", error);
199 free(error);
200 return ACT_RET_PRS_ERR;
201 }
202
203 LIST_INIT((struct list *)&rule->arg.act.p[2]);
204 px->conf.args.ctx = ARGC_HRQ;
205 if (!parse_logformat_string(args[cur_arg + 1], px, (struct list *)&rule->arg.act.p[2], LOG_OPT_HTTP,
206 (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
207 return ACT_RET_PRS_ERR;
208 }
209
210 (*orig_arg) += 2;
211 return ACT_RET_PRS_OK;
212}
213
Willy Tarreau79e57332018-10-02 16:01:16 +0200214/* This function is just a compliant action wrapper for "set-status". */
215static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
216 struct session *sess, struct stream *s, int flags)
217{
Christopher Fauletfc9cfe42019-07-16 14:54:53 +0200218 http_res_set_status(rule->arg.status.code, rule->arg.status.reason, s);
Willy Tarreau79e57332018-10-02 16:01:16 +0200219 return ACT_RET_CONT;
220}
221
222/* parse set-status action:
223 * This action accepts a single argument of type int representing
224 * an http status code. It returns ACT_RET_PRS_OK on success,
225 * ACT_RET_PRS_ERR on error.
226 */
227static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
228 struct act_rule *rule, char **err)
229{
230 char *error;
231
232 rule->action = ACT_CUSTOM;
233 rule->action_ptr = action_http_set_status;
234
235 /* Check if an argument is available */
236 if (!*args[*orig_arg]) {
237 memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
238 return ACT_RET_PRS_ERR;
239 }
240
241 /* convert status code as integer */
242 rule->arg.status.code = strtol(args[*orig_arg], &error, 10);
243 if (*error != '\0' || rule->arg.status.code < 100 || rule->arg.status.code > 999) {
244 memprintf(err, "expects an integer status code between 100 and 999");
245 return ACT_RET_PRS_ERR;
246 }
247
248 (*orig_arg)++;
249
250 /* set custom reason string */
251 rule->arg.status.reason = NULL; // If null, we use the default reason for the status code.
252 if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
253 (*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
254 (*orig_arg)++;
255 rule->arg.status.reason = strdup(args[*orig_arg]);
256 (*orig_arg)++;
257 }
258
259 return ACT_RET_PRS_OK;
260}
261
262/* This function executes the "reject" HTTP action. It clears the request and
263 * response buffer without sending any response. It can be useful as an HTTP
264 * alternative to the silent-drop action to defend against DoS attacks, and may
265 * also be used with HTTP/2 to close a connection instead of just a stream.
266 * The txn status is unchanged, indicating no response was sent. The termination
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200267 * flags will indicate "PR". It always returns ACT_RET_DONE.
Willy Tarreau79e57332018-10-02 16:01:16 +0200268 */
269static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
270 struct session *sess, struct stream *s, int flags)
271{
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100272 si_must_kill_conn(chn_prod(&s->req));
Willy Tarreau79e57332018-10-02 16:01:16 +0200273 channel_abort(&s->req);
274 channel_abort(&s->res);
275 s->req.analysers = 0;
276 s->res.analysers = 0;
277
Olivier Houcharda798bf52019-03-08 18:52:00 +0100278 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
279 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200280 if (sess->listener && sess->listener->counters)
Olivier Houcharda798bf52019-03-08 18:52:00 +0100281 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200282
283 if (!(s->flags & SF_ERR_MASK))
284 s->flags |= SF_ERR_PRXCOND;
285 if (!(s->flags & SF_FINST_MASK))
286 s->flags |= SF_FINST_R;
287
Christopher Faulet8f1aa772019-07-04 11:27:15 +0200288 return ACT_RET_DONE;
Willy Tarreau79e57332018-10-02 16:01:16 +0200289}
290
291/* parse the "reject" action:
292 * This action takes no argument and returns ACT_RET_PRS_OK on success,
293 * ACT_RET_PRS_ERR on error.
294 */
295static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
296 struct act_rule *rule, char **err)
297{
298 rule->action = ACT_CUSTOM;
299 rule->action_ptr = http_action_reject;
300 return ACT_RET_PRS_OK;
301}
302
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200303/* This function executes the "disable-l7-retry" HTTP action.
304 * It disables L7 retries (all retry except for a connection failure). This
305 * can be useful for example to avoid retrying on POST requests.
306 * It just removes the L7 retry flag on the stream_interface, and always
307 * return ACT_RET_CONT;
308 */
309static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
310 struct session *sess, struct stream *s, int flags)
311{
312 struct stream_interface *si = &s->si[1];
313
314 /* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
315 * let's be future-proof and remove it anyway.
316 */
317 si->flags &= ~SI_FL_L7_RETRY;
318 si->flags |= SI_FL_D_L7_RETRY;
319 return ACT_RET_CONT;
320}
321
322/* parse the "disable-l7-retry" action:
323 * This action takes no argument and returns ACT_RET_PRS_OK on success,
324 * ACT_RET_PRS_ERR on error.
325 */
326static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
327 int *orig_args, struct proxy *px,
328 struct act_rule *rule, char **err)
329{
330 rule->action = ACT_CUSTOM;
331 rule->action_ptr = http_req_disable_l7_retry;
332 return ACT_RET_PRS_OK;
333}
334
Willy Tarreau79e57332018-10-02 16:01:16 +0200335/* This function executes the "capture" action. It executes a fetch expression,
336 * turns the result into a string and puts it in a capture slot. It always
337 * returns 1. If an error occurs the action is cancelled, but the rule
338 * processing continues.
339 */
340static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
341 struct session *sess, struct stream *s, int flags)
342{
343 struct sample *key;
344 struct cap_hdr *h = rule->arg.cap.hdr;
345 char **cap = s->req_cap;
346 int len;
347
348 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
349 if (!key)
350 return ACT_RET_CONT;
351
352 if (cap[h->index] == NULL)
353 cap[h->index] = pool_alloc(h->pool);
354
355 if (cap[h->index] == NULL) /* no more capture memory */
356 return ACT_RET_CONT;
357
358 len = key->data.u.str.data;
359 if (len > h->len)
360 len = h->len;
361
362 memcpy(cap[h->index], key->data.u.str.area, len);
363 cap[h->index][len] = 0;
364 return ACT_RET_CONT;
365}
366
367/* This function executes the "capture" action and store the result in a
368 * capture slot if exists. It executes a fetch expression, turns the result
369 * into a string and puts it in a capture slot. It always returns 1. If an
370 * error occurs the action is cancelled, but the rule processing continues.
371 */
372static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
373 struct session *sess, struct stream *s, int flags)
374{
375 struct sample *key;
376 struct cap_hdr *h;
377 char **cap = s->req_cap;
378 struct proxy *fe = strm_fe(s);
379 int len;
380 int i;
381
382 /* Look for the original configuration. */
383 for (h = fe->req_cap, i = fe->nb_req_cap - 1;
384 h != NULL && i != rule->arg.capid.idx ;
385 i--, h = h->next);
386 if (!h)
387 return ACT_RET_CONT;
388
389 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
390 if (!key)
391 return ACT_RET_CONT;
392
393 if (cap[h->index] == NULL)
394 cap[h->index] = pool_alloc(h->pool);
395
396 if (cap[h->index] == NULL) /* no more capture memory */
397 return ACT_RET_CONT;
398
399 len = key->data.u.str.data;
400 if (len > h->len)
401 len = h->len;
402
403 memcpy(cap[h->index], key->data.u.str.area, len);
404 cap[h->index][len] = 0;
405 return ACT_RET_CONT;
406}
407
408/* Check an "http-request capture" action.
409 *
410 * The function returns 1 in success case, otherwise, it returns 0 and err is
411 * filled.
412 */
413static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
414{
415 if (rule->action_ptr != http_action_req_capture_by_id)
416 return 1;
417
418 if (rule->arg.capid.idx >= px->nb_req_cap) {
419 memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
420 rule->arg.capid.idx);
421 return 0;
422 }
423
424 return 1;
425}
426
427/* parse an "http-request capture" action. It takes a single argument which is
428 * a sample fetch expression. It stores the expression into arg->act.p[0] and
429 * the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
430 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
431 */
432static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
433 struct act_rule *rule, char **err)
434{
435 struct sample_expr *expr;
436 struct cap_hdr *hdr;
437 int cur_arg;
438 int len = 0;
439
440 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
441 if (strcmp(args[cur_arg], "if") == 0 ||
442 strcmp(args[cur_arg], "unless") == 0)
443 break;
444
445 if (cur_arg < *orig_arg + 3) {
446 memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
447 return ACT_RET_PRS_ERR;
448 }
449
450 cur_arg = *orig_arg;
451 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
452 if (!expr)
453 return ACT_RET_PRS_ERR;
454
455 if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
456 memprintf(err,
457 "fetch method '%s' extracts information from '%s', none of which is available here",
458 args[cur_arg-1], sample_src_names(expr->fetch->use));
459 free(expr);
460 return ACT_RET_PRS_ERR;
461 }
462
463 if (!args[cur_arg] || !*args[cur_arg]) {
464 memprintf(err, "expects 'len or 'id'");
465 free(expr);
466 return ACT_RET_PRS_ERR;
467 }
468
469 if (strcmp(args[cur_arg], "len") == 0) {
470 cur_arg++;
471
472 if (!(px->cap & PR_CAP_FE)) {
473 memprintf(err, "proxy '%s' has no frontend capability", px->id);
474 return ACT_RET_PRS_ERR;
475 }
476
477 px->conf.args.ctx = ARGC_CAP;
478
479 if (!args[cur_arg]) {
480 memprintf(err, "missing length value");
481 free(expr);
482 return ACT_RET_PRS_ERR;
483 }
484 /* we copy the table name for now, it will be resolved later */
485 len = atoi(args[cur_arg]);
486 if (len <= 0) {
487 memprintf(err, "length must be > 0");
488 free(expr);
489 return ACT_RET_PRS_ERR;
490 }
491 cur_arg++;
492
Willy Tarreau79e57332018-10-02 16:01:16 +0200493 hdr = calloc(1, sizeof(*hdr));
494 hdr->next = px->req_cap;
495 hdr->name = NULL; /* not a header capture */
496 hdr->namelen = 0;
497 hdr->len = len;
498 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
499 hdr->index = px->nb_req_cap++;
500
501 px->req_cap = hdr;
502 px->to_log |= LW_REQHDR;
503
504 rule->action = ACT_CUSTOM;
505 rule->action_ptr = http_action_req_capture;
506 rule->arg.cap.expr = expr;
507 rule->arg.cap.hdr = hdr;
508 }
509
510 else if (strcmp(args[cur_arg], "id") == 0) {
511 int id;
512 char *error;
513
514 cur_arg++;
515
516 if (!args[cur_arg]) {
517 memprintf(err, "missing id value");
518 free(expr);
519 return ACT_RET_PRS_ERR;
520 }
521
522 id = strtol(args[cur_arg], &error, 10);
523 if (*error != '\0') {
524 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
525 free(expr);
526 return ACT_RET_PRS_ERR;
527 }
528 cur_arg++;
529
530 px->conf.args.ctx = ARGC_CAP;
531
532 rule->action = ACT_CUSTOM;
533 rule->action_ptr = http_action_req_capture_by_id;
534 rule->check_ptr = check_http_req_capture;
535 rule->arg.capid.expr = expr;
536 rule->arg.capid.idx = id;
537 }
538
539 else {
540 memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
541 free(expr);
542 return ACT_RET_PRS_ERR;
543 }
544
545 *orig_arg = cur_arg;
546 return ACT_RET_PRS_OK;
547}
548
549/* This function executes the "capture" action and store the result in a
550 * capture slot if exists. It executes a fetch expression, turns the result
551 * into a string and puts it in a capture slot. It always returns 1. If an
552 * error occurs the action is cancelled, but the rule processing continues.
553 */
554static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
555 struct session *sess, struct stream *s, int flags)
556{
557 struct sample *key;
558 struct cap_hdr *h;
559 char **cap = s->res_cap;
560 struct proxy *fe = strm_fe(s);
561 int len;
562 int i;
563
564 /* Look for the original configuration. */
565 for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
566 h != NULL && i != rule->arg.capid.idx ;
567 i--, h = h->next);
568 if (!h)
569 return ACT_RET_CONT;
570
571 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
572 if (!key)
573 return ACT_RET_CONT;
574
575 if (cap[h->index] == NULL)
576 cap[h->index] = pool_alloc(h->pool);
577
578 if (cap[h->index] == NULL) /* no more capture memory */
579 return ACT_RET_CONT;
580
581 len = key->data.u.str.data;
582 if (len > h->len)
583 len = h->len;
584
585 memcpy(cap[h->index], key->data.u.str.area, len);
586 cap[h->index][len] = 0;
587 return ACT_RET_CONT;
588}
589
590/* Check an "http-response capture" action.
591 *
592 * The function returns 1 in success case, otherwise, it returns 0 and err is
593 * filled.
594 */
595static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
596{
597 if (rule->action_ptr != http_action_res_capture_by_id)
598 return 1;
599
600 if (rule->arg.capid.idx >= px->nb_rsp_cap) {
601 memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
602 rule->arg.capid.idx);
603 return 0;
604 }
605
606 return 1;
607}
608
609/* parse an "http-response capture" action. It takes a single argument which is
610 * a sample fetch expression. It stores the expression into arg->act.p[0] and
611 * the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
612 * It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
613 */
614static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
615 struct act_rule *rule, char **err)
616{
617 struct sample_expr *expr;
618 int cur_arg;
619 int id;
620 char *error;
621
622 for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
623 if (strcmp(args[cur_arg], "if") == 0 ||
624 strcmp(args[cur_arg], "unless") == 0)
625 break;
626
627 if (cur_arg < *orig_arg + 3) {
628 memprintf(err, "expects <expression> id <idx>");
629 return ACT_RET_PRS_ERR;
630 }
631
632 cur_arg = *orig_arg;
633 expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
634 if (!expr)
635 return ACT_RET_PRS_ERR;
636
637 if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
638 memprintf(err,
639 "fetch method '%s' extracts information from '%s', none of which is available here",
640 args[cur_arg-1], sample_src_names(expr->fetch->use));
641 free(expr);
642 return ACT_RET_PRS_ERR;
643 }
644
645 if (!args[cur_arg] || !*args[cur_arg]) {
646 memprintf(err, "expects 'id'");
647 free(expr);
648 return ACT_RET_PRS_ERR;
649 }
650
651 if (strcmp(args[cur_arg], "id") != 0) {
652 memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
653 free(expr);
654 return ACT_RET_PRS_ERR;
655 }
656
657 cur_arg++;
658
659 if (!args[cur_arg]) {
660 memprintf(err, "missing id value");
661 free(expr);
662 return ACT_RET_PRS_ERR;
663 }
664
665 id = strtol(args[cur_arg], &error, 10);
666 if (*error != '\0') {
667 memprintf(err, "cannot parse id '%s'", args[cur_arg]);
668 free(expr);
669 return ACT_RET_PRS_ERR;
670 }
671 cur_arg++;
672
673 px->conf.args.ctx = ARGC_CAP;
674
675 rule->action = ACT_CUSTOM;
676 rule->action_ptr = http_action_res_capture_by_id;
677 rule->check_ptr = check_http_res_capture;
678 rule->arg.capid.expr = expr;
679 rule->arg.capid.idx = id;
680
681 *orig_arg = cur_arg;
682 return ACT_RET_PRS_OK;
683}
684
685/************************************************************************/
686/* All supported http-request action keywords must be declared here. */
687/************************************************************************/
688
689static struct action_kw_list http_req_actions = {
690 .kw = {
691 { "capture", parse_http_req_capture },
692 { "reject", parse_http_action_reject },
Olivier Houchard602bf7d2019-05-10 13:59:15 +0200693 { "disable-l7-retry", parse_http_req_disable_l7_retry },
Willy Tarreau33810222019-06-12 17:44:02 +0200694 { "replace-uri", parse_replace_uri },
Willy Tarreau79e57332018-10-02 16:01:16 +0200695 { "set-method", parse_set_req_line },
696 { "set-path", parse_set_req_line },
697 { "set-query", parse_set_req_line },
698 { "set-uri", parse_set_req_line },
699 { NULL, NULL }
700 }
701};
702
Willy Tarreau0108d902018-11-25 19:14:37 +0100703INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
704
Willy Tarreau79e57332018-10-02 16:01:16 +0200705static struct action_kw_list http_res_actions = {
706 .kw = {
707 { "capture", parse_http_res_capture },
708 { "set-status", parse_http_set_status },
709 { NULL, NULL }
710 }
711};
712
Willy Tarreau0108d902018-11-25 19:14:37 +0100713INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
Willy Tarreau79e57332018-10-02 16:01:16 +0200714
715/*
716 * Local variables:
717 * c-indent-level: 8
718 * c-basic-offset: 8
719 * End:
720 */