REORG: http: move the code to different files
The current proto_http.c file is huge and contains different processing domains making it very difficult to work on an alternative representation. This commit moves some parts to other files : - ACL registration code => http_acl.c This code only creates some ACL mappings and doesn't know anything about HTTP nor about the representation. This code could even have moved to acl.c but it was not worth polluting it again. - HTTP sample conversion => http_conv.c This code doesn't depend on the internal representation but definitely manipulates some HTTP elements, such as dates. It also has access to captures. - HTTP sample fetching => http_fetch.c This code does depend entirely on the internal representation but is totally independent on the analysers. Placing it into a different file will ease the transition to the new representation and the creation of a wrapper if required. An include file was created due to CHECK_HTTP_MESSAGE_FIRST() being used at various places. - HTTP action registration => http_act.c This code doesn't directly interact with the messages nor the transaction but it does so via some exported http functions like http_replace_req_line() or http_set_status() so it will be easier to change only this after the conversion. - a few very generic parts were found and moved to http.{c,h} as relevant. It is worth noting that the functions moved to these new files are not referenced anywhere outside of the files and are only called as registered callbacks, so these files do not even require associated include files.
This commit is contained in:
parent
ca56fce8bd
commit
79e57336b5
1
Makefile
1
Makefile
|
@ -894,6 +894,7 @@ OBJS = src/proto_http.o src/cfgparse.o src/server.o src/stream.o \
|
|||
src/time.o src/proto_udp.o src/arg.o src/signal.o \
|
||||
src/protocol.o src/lru.o src/hdr_idx.o src/hpack-huff.o \
|
||||
src/mailers.o src/h2.o src/base64.o src/hash.o src/http.o \
|
||||
src/http_acl.o src/http_fetch.o src/http_conv.o src/http_act.o \
|
||||
src/proto_sockpair.o
|
||||
|
||||
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \
|
||||
|
|
|
@ -161,6 +161,32 @@ static inline int http_is_param_delimiter(char c, char delim)
|
|||
return c == '&' || c == ';' || c == delim;
|
||||
}
|
||||
|
||||
/* Match language range with language tag. RFC2616 14.4:
|
||||
*
|
||||
* A language-range matches a language-tag if it exactly equals
|
||||
* the tag, or if it exactly equals a prefix of the tag such
|
||||
* that the first tag character following the prefix is "-".
|
||||
*
|
||||
* Return 1 if the strings match, else return 0.
|
||||
*/
|
||||
static inline int http_language_range_match(const char *range, int range_len,
|
||||
const char *tag, int tag_len)
|
||||
{
|
||||
const char *end = range + range_len;
|
||||
const char *tend = tag + tag_len;
|
||||
|
||||
while (range < end) {
|
||||
if (*range == '-' && tag == tend)
|
||||
return 1;
|
||||
if (*range != *tag || tag == tend)
|
||||
return 0;
|
||||
range++;
|
||||
tag++;
|
||||
}
|
||||
/* Return true only if the last char of the tag is matched. */
|
||||
return tag == tend;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _COMMON_HTTP_H */
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* include/proto/http_fetch.h
|
||||
* This file contains the minimally required http sample fetch declarations.
|
||||
*
|
||||
* Copyright (C) 2000-2018 Willy Tarreau - w@1wt.eu
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, version 2.1
|
||||
* exclusively.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _PROTO_HTTP_FETCH_H
|
||||
#define _PROTO_HTTP_FETCH_H
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <types/action.h>
|
||||
#include <types/proxy.h>
|
||||
|
||||
/* Note: these functions *do* modify the sample. Even in case of success, at
|
||||
* least the type and uint value are modified.
|
||||
*/
|
||||
#define CHECK_HTTP_MESSAGE_FIRST() \
|
||||
do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1); if (r <= 0) return r; } while (0)
|
||||
|
||||
#define CHECK_HTTP_MESSAGE_FIRST_PERM() \
|
||||
do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 0); if (r <= 0) return r; } while (0)
|
||||
|
||||
int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
|
||||
const struct arg *args, struct sample *smp, int req_vol);
|
||||
|
||||
int val_hdr(struct arg *arg, char **err_msg);
|
||||
|
||||
|
||||
#endif /* _PROTO_HTTP_FETCH_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
|
@ -44,6 +44,8 @@
|
|||
* ver_token = 'H', 'P', 'T', '/', '.', and digits.
|
||||
*/
|
||||
|
||||
extern struct pool_head *pool_head_uniqueid;
|
||||
|
||||
int process_cli(struct stream *s);
|
||||
int process_srv_data(struct stream *s);
|
||||
int process_srv_conn(struct stream *s);
|
||||
|
@ -57,6 +59,7 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit);
|
|||
int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, struct proxy *px);
|
||||
int http_request_forward_body(struct stream *s, struct channel *req, int an_bit);
|
||||
int http_response_forward_body(struct stream *s, struct channel *res, int an_bit);
|
||||
int http_upgrade_v09_to_v10(struct http_txn *txn);
|
||||
void http_msg_analyzer(struct http_msg *msg, struct hdr_idx *idx);
|
||||
void http_txn_reset_req(struct http_txn *txn);
|
||||
void http_txn_reset_res(struct http_txn *txn);
|
||||
|
@ -100,6 +103,9 @@ void http_capture_bad_message(struct proxy *proxy, struct stream *s,
|
|||
unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen,
|
||||
struct hdr_idx *idx, int occ,
|
||||
struct hdr_ctx *ctx, char **vptr, size_t *vlen);
|
||||
unsigned int http_get_fhdr(const struct http_msg *msg, const char *hname, int hlen,
|
||||
struct hdr_idx *idx, int occ,
|
||||
struct hdr_ctx *ctx, char **vptr, size_t *vlen);
|
||||
char *http_txn_get_path(const struct http_txn *txn);
|
||||
|
||||
struct http_txn *http_alloc_txn(struct stream *s);
|
||||
|
@ -117,31 +123,9 @@ void http_reply_and_close(struct stream *s, short status, struct buffer *msg);
|
|||
struct buffer *http_error_message(struct stream *s);
|
||||
struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
|
||||
const char **args, char **errmsg, int use_fmt, int dir);
|
||||
int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private);
|
||||
int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private);
|
||||
|
||||
struct action_kw *action_http_req_custom(const char *kw);
|
||||
struct action_kw *action_http_res_custom(const char *kw);
|
||||
int val_hdr(struct arg *arg, char **err_msg);
|
||||
|
||||
int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt,
|
||||
const struct arg *args, struct sample *smp, int req_vol);
|
||||
|
||||
enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags);
|
||||
enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags);
|
||||
|
||||
int parse_qvalue(const char *qvalue, const char **end);
|
||||
|
||||
/* Note: these functions *do* modify the sample. Even in case of success, at
|
||||
* least the type and uint value are modified.
|
||||
*/
|
||||
#define CHECK_HTTP_MESSAGE_FIRST() \
|
||||
do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1); if (r <= 0) return r; } while (0)
|
||||
|
||||
#define CHECK_HTTP_MESSAGE_FIRST_PERM() \
|
||||
do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 0); if (r <= 0) return r; } while (0)
|
||||
|
||||
static inline void http_req_keywords_register(struct action_kw_list *kw_list)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <common/buffer.h>
|
||||
#include <common/errors.h>
|
||||
#include <proto/arg.h>
|
||||
#include <proto/http_fetch.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/sample.h>
|
||||
|
|
1
src/da.c
1
src/da.c
|
@ -3,6 +3,7 @@
|
|||
#include <common/cfgparse.h>
|
||||
#include <common/errors.h>
|
||||
#include <proto/arg.h>
|
||||
#include <proto/http_fetch.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/sample.h>
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <proto/hdr_idx.h>
|
||||
#include <proto/hlua.h>
|
||||
#include <proto/hlua_fcn.h>
|
||||
#include <proto/http_fetch.h>
|
||||
#include <proto/map.h>
|
||||
#include <proto/obj_type.h>
|
||||
#include <proto/queue.h>
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* HTTP ACLs declaration
|
||||
*
|
||||
* Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <common/chunk.h>
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/http.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/version.h>
|
||||
|
||||
#include <types/global.h>
|
||||
|
||||
#include <proto/acl.h>
|
||||
#include <proto/arg.h>
|
||||
#include <proto/auth.h>
|
||||
#include <proto/pattern.h>
|
||||
|
||||
|
||||
/* We use the pre-parsed method if it is known, and store its number as an
|
||||
* integer. If it is unknown, we use the pointer and the length.
|
||||
*/
|
||||
static int pat_parse_meth(const char *text, struct pattern *pattern, int mflags, char **err)
|
||||
{
|
||||
int len, meth;
|
||||
|
||||
len = strlen(text);
|
||||
meth = find_http_meth(text, len);
|
||||
|
||||
pattern->val.i = meth;
|
||||
if (meth == HTTP_METH_OTHER) {
|
||||
pattern->ptr.str = (char *)text;
|
||||
pattern->len = len;
|
||||
}
|
||||
else {
|
||||
pattern->ptr.str = NULL;
|
||||
pattern->len = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See above how the method is stored in the global pattern */
|
||||
static struct pattern *pat_match_meth(struct sample *smp, struct pattern_expr *expr, int fill)
|
||||
{
|
||||
int icase;
|
||||
struct pattern_list *lst;
|
||||
struct pattern *pattern;
|
||||
|
||||
list_for_each_entry(lst, &expr->patterns, list) {
|
||||
pattern = &lst->pat;
|
||||
|
||||
/* well-known method */
|
||||
if (pattern->val.i != HTTP_METH_OTHER) {
|
||||
if (smp->data.u.meth.meth == pattern->val.i)
|
||||
return pattern;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Other method, we must compare the strings */
|
||||
if (pattern->len != smp->data.u.meth.str.data)
|
||||
continue;
|
||||
|
||||
icase = expr->mflags & PAT_MF_IGNORE_CASE;
|
||||
if ((icase && strncasecmp(pattern->ptr.str, smp->data.u.meth.str.area, smp->data.u.meth.str.data) == 0) ||
|
||||
(!icase && strncmp(pattern->ptr.str, smp->data.u.meth.str.area, smp->data.u.meth.str.data) == 0))
|
||||
return pattern;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* All supported ACL keywords must be declared here. */
|
||||
/************************************************************************/
|
||||
|
||||
/* Note: must not be declared <const> as its list will be overwritten.
|
||||
* Please take care of keeping this list alphabetically sorted.
|
||||
*/
|
||||
static struct acl_kw_list acl_kws = {ILH, {
|
||||
{ "base", "base", PAT_MATCH_STR },
|
||||
{ "base_beg", "base", PAT_MATCH_BEG },
|
||||
{ "base_dir", "base", PAT_MATCH_DIR },
|
||||
{ "base_dom", "base", PAT_MATCH_DOM },
|
||||
{ "base_end", "base", PAT_MATCH_END },
|
||||
{ "base_len", "base", PAT_MATCH_LEN },
|
||||
{ "base_reg", "base", PAT_MATCH_REG },
|
||||
{ "base_sub", "base", PAT_MATCH_SUB },
|
||||
|
||||
{ "cook", "req.cook", PAT_MATCH_STR },
|
||||
{ "cook_beg", "req.cook", PAT_MATCH_BEG },
|
||||
{ "cook_dir", "req.cook", PAT_MATCH_DIR },
|
||||
{ "cook_dom", "req.cook", PAT_MATCH_DOM },
|
||||
{ "cook_end", "req.cook", PAT_MATCH_END },
|
||||
{ "cook_len", "req.cook", PAT_MATCH_LEN },
|
||||
{ "cook_reg", "req.cook", PAT_MATCH_REG },
|
||||
{ "cook_sub", "req.cook", PAT_MATCH_SUB },
|
||||
|
||||
{ "hdr", "req.hdr", PAT_MATCH_STR },
|
||||
{ "hdr_beg", "req.hdr", PAT_MATCH_BEG },
|
||||
{ "hdr_dir", "req.hdr", PAT_MATCH_DIR },
|
||||
{ "hdr_dom", "req.hdr", PAT_MATCH_DOM },
|
||||
{ "hdr_end", "req.hdr", PAT_MATCH_END },
|
||||
{ "hdr_len", "req.hdr", PAT_MATCH_LEN },
|
||||
{ "hdr_reg", "req.hdr", PAT_MATCH_REG },
|
||||
{ "hdr_sub", "req.hdr", PAT_MATCH_SUB },
|
||||
|
||||
/* these two declarations uses strings with list storage (in place
|
||||
* of tree storage). The basic match is PAT_MATCH_STR, but the indexation
|
||||
* and delete functions are relative to the list management. The parse
|
||||
* and match method are related to the corresponding fetch methods. This
|
||||
* is very particular ACL declaration mode.
|
||||
*/
|
||||
{ "http_auth_group", NULL, PAT_MATCH_STR, NULL, pat_idx_list_str, pat_del_list_ptr, NULL, pat_match_auth },
|
||||
{ "method", NULL, PAT_MATCH_STR, pat_parse_meth, pat_idx_list_str, pat_del_list_ptr, NULL, pat_match_meth },
|
||||
|
||||
{ "path", "path", PAT_MATCH_STR },
|
||||
{ "path_beg", "path", PAT_MATCH_BEG },
|
||||
{ "path_dir", "path", PAT_MATCH_DIR },
|
||||
{ "path_dom", "path", PAT_MATCH_DOM },
|
||||
{ "path_end", "path", PAT_MATCH_END },
|
||||
{ "path_len", "path", PAT_MATCH_LEN },
|
||||
{ "path_reg", "path", PAT_MATCH_REG },
|
||||
{ "path_sub", "path", PAT_MATCH_SUB },
|
||||
|
||||
{ "req_ver", "req.ver", PAT_MATCH_STR },
|
||||
{ "resp_ver", "res.ver", PAT_MATCH_STR },
|
||||
|
||||
{ "scook", "res.cook", PAT_MATCH_STR },
|
||||
{ "scook_beg", "res.cook", PAT_MATCH_BEG },
|
||||
{ "scook_dir", "res.cook", PAT_MATCH_DIR },
|
||||
{ "scook_dom", "res.cook", PAT_MATCH_DOM },
|
||||
{ "scook_end", "res.cook", PAT_MATCH_END },
|
||||
{ "scook_len", "res.cook", PAT_MATCH_LEN },
|
||||
{ "scook_reg", "res.cook", PAT_MATCH_REG },
|
||||
{ "scook_sub", "res.cook", PAT_MATCH_SUB },
|
||||
|
||||
{ "shdr", "res.hdr", PAT_MATCH_STR },
|
||||
{ "shdr_beg", "res.hdr", PAT_MATCH_BEG },
|
||||
{ "shdr_dir", "res.hdr", PAT_MATCH_DIR },
|
||||
{ "shdr_dom", "res.hdr", PAT_MATCH_DOM },
|
||||
{ "shdr_end", "res.hdr", PAT_MATCH_END },
|
||||
{ "shdr_len", "res.hdr", PAT_MATCH_LEN },
|
||||
{ "shdr_reg", "res.hdr", PAT_MATCH_REG },
|
||||
{ "shdr_sub", "res.hdr", PAT_MATCH_SUB },
|
||||
|
||||
{ "url", "url", PAT_MATCH_STR },
|
||||
{ "url_beg", "url", PAT_MATCH_BEG },
|
||||
{ "url_dir", "url", PAT_MATCH_DIR },
|
||||
{ "url_dom", "url", PAT_MATCH_DOM },
|
||||
{ "url_end", "url", PAT_MATCH_END },
|
||||
{ "url_len", "url", PAT_MATCH_LEN },
|
||||
{ "url_reg", "url", PAT_MATCH_REG },
|
||||
{ "url_sub", "url", PAT_MATCH_SUB },
|
||||
|
||||
{ "urlp", "urlp", PAT_MATCH_STR },
|
||||
{ "urlp_beg", "urlp", PAT_MATCH_BEG },
|
||||
{ "urlp_dir", "urlp", PAT_MATCH_DIR },
|
||||
{ "urlp_dom", "urlp", PAT_MATCH_DOM },
|
||||
{ "urlp_end", "urlp", PAT_MATCH_END },
|
||||
{ "urlp_len", "urlp", PAT_MATCH_LEN },
|
||||
{ "urlp_reg", "urlp", PAT_MATCH_REG },
|
||||
{ "urlp_sub", "urlp", PAT_MATCH_SUB },
|
||||
|
||||
{ /* END */ },
|
||||
}};
|
||||
|
||||
__attribute__((constructor))
|
||||
static void __http_acl_init(void)
|
||||
{
|
||||
acl_register_keywords(&acl_kws);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
|
@ -0,0 +1,608 @@
|
|||
/*
|
||||
* HTTP actions
|
||||
*
|
||||
* Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <common/chunk.h>
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/http.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/version.h>
|
||||
|
||||
#include <types/capture.h>
|
||||
#include <types/global.h>
|
||||
|
||||
#include <proto/acl.h>
|
||||
#include <proto/arg.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/proto_http.h>
|
||||
|
||||
|
||||
/* This function executes one of the set-{method,path,query,uri} actions. It
|
||||
* builds a string in the trash from the specified format string. It finds
|
||||
* the action to be performed in <http.action>, previously filled by function
|
||||
* parse_set_req_line(). The replacement action is excuted by the function
|
||||
* http_action_set_req_line(). It always returns ACT_RET_CONT. If an error
|
||||
* occurs the action is canceled, but the rule processing continue.
|
||||
*/
|
||||
static enum act_return http_action_set_req_line(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
struct buffer *replace;
|
||||
enum act_return ret = ACT_RET_ERR;
|
||||
|
||||
replace = alloc_trash_chunk();
|
||||
if (!replace)
|
||||
goto leave;
|
||||
|
||||
/* If we have to create a query string, prepare a '?'. */
|
||||
if (rule->arg.http.action == 2)
|
||||
replace->area[replace->data++] = '?';
|
||||
replace->data += build_logline(s, replace->area + replace->data,
|
||||
replace->size - replace->data,
|
||||
&rule->arg.http.logfmt);
|
||||
|
||||
http_replace_req_line(rule->arg.http.action, replace->area,
|
||||
replace->data, px, s);
|
||||
|
||||
ret = ACT_RET_CONT;
|
||||
|
||||
leave:
|
||||
free_trash_chunk(replace);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* parse an http-request action among :
|
||||
* set-method
|
||||
* set-path
|
||||
* set-query
|
||||
* set-uri
|
||||
*
|
||||
* All of them accept a single argument of type string representing a log-format.
|
||||
* The resulting rule makes use of arg->act.p[0..1] to store the log-format list
|
||||
* head, and p[2] to store the action as an int (0=method, 1=path, 2=query, 3=uri).
|
||||
* It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_set_req_line(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
int cur_arg = *orig_arg;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
|
||||
switch (args[0][4]) {
|
||||
case 'm' :
|
||||
rule->arg.http.action = 0;
|
||||
rule->action_ptr = http_action_set_req_line;
|
||||
break;
|
||||
case 'p' :
|
||||
rule->arg.http.action = 1;
|
||||
rule->action_ptr = http_action_set_req_line;
|
||||
break;
|
||||
case 'q' :
|
||||
rule->arg.http.action = 2;
|
||||
rule->action_ptr = http_action_set_req_line;
|
||||
break;
|
||||
case 'u' :
|
||||
rule->arg.http.action = 3;
|
||||
rule->action_ptr = http_action_set_req_line;
|
||||
break;
|
||||
default:
|
||||
memprintf(err, "internal error: unhandled action '%s'", args[0]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (!*args[cur_arg] ||
|
||||
(*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
|
||||
memprintf(err, "expects exactly 1 argument <format>");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
LIST_INIT(&rule->arg.http.logfmt);
|
||||
px->conf.args.ctx = ARGC_HRQ;
|
||||
if (!parse_logformat_string(args[cur_arg], px, &rule->arg.http.logfmt, LOG_OPT_HTTP,
|
||||
(px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, err)) {
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
(*orig_arg)++;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* This function is just a compliant action wrapper for "set-status". */
|
||||
static enum act_return action_http_set_status(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
http_set_status(rule->arg.status.code, rule->arg.status.reason, s);
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* parse set-status action:
|
||||
* This action accepts a single argument of type int representing
|
||||
* an http status code. It returns ACT_RET_PRS_OK on success,
|
||||
* ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_http_set_status(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
char *error;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = action_http_set_status;
|
||||
|
||||
/* Check if an argument is available */
|
||||
if (!*args[*orig_arg]) {
|
||||
memprintf(err, "expects 1 argument: <status>; or 3 arguments: <status> reason <fmt>");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
/* convert status code as integer */
|
||||
rule->arg.status.code = strtol(args[*orig_arg], &error, 10);
|
||||
if (*error != '\0' || rule->arg.status.code < 100 || rule->arg.status.code > 999) {
|
||||
memprintf(err, "expects an integer status code between 100 and 999");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
(*orig_arg)++;
|
||||
|
||||
/* set custom reason string */
|
||||
rule->arg.status.reason = NULL; // If null, we use the default reason for the status code.
|
||||
if (*args[*orig_arg] && strcmp(args[*orig_arg], "reason") == 0 &&
|
||||
(*args[*orig_arg + 1] && strcmp(args[*orig_arg + 1], "if") != 0 && strcmp(args[*orig_arg + 1], "unless") != 0)) {
|
||||
(*orig_arg)++;
|
||||
rule->arg.status.reason = strdup(args[*orig_arg]);
|
||||
(*orig_arg)++;
|
||||
}
|
||||
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* This function executes the "reject" HTTP action. It clears the request and
|
||||
* response buffer without sending any response. It can be useful as an HTTP
|
||||
* alternative to the silent-drop action to defend against DoS attacks, and may
|
||||
* also be used with HTTP/2 to close a connection instead of just a stream.
|
||||
* The txn status is unchanged, indicating no response was sent. The termination
|
||||
* flags will indicate "PR". It always returns ACT_RET_STOP.
|
||||
*/
|
||||
static enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
channel_abort(&s->req);
|
||||
channel_abort(&s->res);
|
||||
s->req.analysers = 0;
|
||||
s->res.analysers = 0;
|
||||
|
||||
HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
|
||||
HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
|
||||
if (sess->listener && sess->listener->counters)
|
||||
HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
|
||||
|
||||
if (!(s->flags & SF_ERR_MASK))
|
||||
s->flags |= SF_ERR_PRXCOND;
|
||||
if (!(s->flags & SF_FINST_MASK))
|
||||
s->flags |= SF_FINST_R;
|
||||
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* parse the "reject" action:
|
||||
* This action takes no argument and returns ACT_RET_PRS_OK on success,
|
||||
* ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_http_action_reject(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = http_action_reject;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* This function executes the "capture" action. It executes a fetch expression,
|
||||
* turns the result into a string and puts it in a capture slot. It always
|
||||
* returns 1. If an error occurs the action is cancelled, but the rule
|
||||
* processing continues.
|
||||
*/
|
||||
static enum act_return http_action_req_capture(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
struct sample *key;
|
||||
struct cap_hdr *h = rule->arg.cap.hdr;
|
||||
char **cap = s->req_cap;
|
||||
int len;
|
||||
|
||||
key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.cap.expr, SMP_T_STR);
|
||||
if (!key)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
if (cap[h->index] == NULL)
|
||||
cap[h->index] = pool_alloc(h->pool);
|
||||
|
||||
if (cap[h->index] == NULL) /* no more capture memory */
|
||||
return ACT_RET_CONT;
|
||||
|
||||
len = key->data.u.str.data;
|
||||
if (len > h->len)
|
||||
len = h->len;
|
||||
|
||||
memcpy(cap[h->index], key->data.u.str.area, len);
|
||||
cap[h->index][len] = 0;
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* This function executes the "capture" action and store the result in a
|
||||
* capture slot if exists. It executes a fetch expression, turns the result
|
||||
* into a string and puts it in a capture slot. It always returns 1. If an
|
||||
* error occurs the action is cancelled, but the rule processing continues.
|
||||
*/
|
||||
static enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
struct sample *key;
|
||||
struct cap_hdr *h;
|
||||
char **cap = s->req_cap;
|
||||
struct proxy *fe = strm_fe(s);
|
||||
int len;
|
||||
int i;
|
||||
|
||||
/* Look for the original configuration. */
|
||||
for (h = fe->req_cap, i = fe->nb_req_cap - 1;
|
||||
h != NULL && i != rule->arg.capid.idx ;
|
||||
i--, h = h->next);
|
||||
if (!h)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
|
||||
if (!key)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
if (cap[h->index] == NULL)
|
||||
cap[h->index] = pool_alloc(h->pool);
|
||||
|
||||
if (cap[h->index] == NULL) /* no more capture memory */
|
||||
return ACT_RET_CONT;
|
||||
|
||||
len = key->data.u.str.data;
|
||||
if (len > h->len)
|
||||
len = h->len;
|
||||
|
||||
memcpy(cap[h->index], key->data.u.str.area, len);
|
||||
cap[h->index][len] = 0;
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* Check an "http-request capture" action.
|
||||
*
|
||||
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
||||
* filled.
|
||||
*/
|
||||
static int check_http_req_capture(struct act_rule *rule, struct proxy *px, char **err)
|
||||
{
|
||||
if (rule->action_ptr != http_action_req_capture_by_id)
|
||||
return 1;
|
||||
|
||||
if (rule->arg.capid.idx >= px->nb_req_cap) {
|
||||
memprintf(err, "unable to find capture id '%d' referenced by http-request capture rule",
|
||||
rule->arg.capid.idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* parse an "http-request capture" action. It takes a single argument which is
|
||||
* a sample fetch expression. It stores the expression into arg->act.p[0] and
|
||||
* the allocated hdr_cap struct or the preallocated "id" into arg->act.p[1].
|
||||
* It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_http_req_capture(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
struct sample_expr *expr;
|
||||
struct cap_hdr *hdr;
|
||||
int cur_arg;
|
||||
int len = 0;
|
||||
|
||||
for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
|
||||
if (strcmp(args[cur_arg], "if") == 0 ||
|
||||
strcmp(args[cur_arg], "unless") == 0)
|
||||
break;
|
||||
|
||||
if (cur_arg < *orig_arg + 3) {
|
||||
memprintf(err, "expects <expression> [ 'len' <length> | id <idx> ]");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
cur_arg = *orig_arg;
|
||||
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
|
||||
memprintf(err,
|
||||
"fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (!args[cur_arg] || !*args[cur_arg]) {
|
||||
memprintf(err, "expects 'len or 'id'");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (strcmp(args[cur_arg], "len") == 0) {
|
||||
cur_arg++;
|
||||
|
||||
if (!(px->cap & PR_CAP_FE)) {
|
||||
memprintf(err, "proxy '%s' has no frontend capability", px->id);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
px->conf.args.ctx = ARGC_CAP;
|
||||
|
||||
if (!args[cur_arg]) {
|
||||
memprintf(err, "missing length value");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
/* we copy the table name for now, it will be resolved later */
|
||||
len = atoi(args[cur_arg]);
|
||||
if (len <= 0) {
|
||||
memprintf(err, "length must be > 0");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
cur_arg++;
|
||||
|
||||
if (!len) {
|
||||
memprintf(err, "a positive 'len' argument is mandatory");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
hdr = calloc(1, sizeof(*hdr));
|
||||
hdr->next = px->req_cap;
|
||||
hdr->name = NULL; /* not a header capture */
|
||||
hdr->namelen = 0;
|
||||
hdr->len = len;
|
||||
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
|
||||
hdr->index = px->nb_req_cap++;
|
||||
|
||||
px->req_cap = hdr;
|
||||
px->to_log |= LW_REQHDR;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = http_action_req_capture;
|
||||
rule->arg.cap.expr = expr;
|
||||
rule->arg.cap.hdr = hdr;
|
||||
}
|
||||
|
||||
else if (strcmp(args[cur_arg], "id") == 0) {
|
||||
int id;
|
||||
char *error;
|
||||
|
||||
cur_arg++;
|
||||
|
||||
if (!args[cur_arg]) {
|
||||
memprintf(err, "missing id value");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
id = strtol(args[cur_arg], &error, 10);
|
||||
if (*error != '\0') {
|
||||
memprintf(err, "cannot parse id '%s'", args[cur_arg]);
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
cur_arg++;
|
||||
|
||||
px->conf.args.ctx = ARGC_CAP;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = http_action_req_capture_by_id;
|
||||
rule->check_ptr = check_http_req_capture;
|
||||
rule->arg.capid.expr = expr;
|
||||
rule->arg.capid.idx = id;
|
||||
}
|
||||
|
||||
else {
|
||||
memprintf(err, "expects 'len' or 'id', found '%s'", args[cur_arg]);
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
*orig_arg = cur_arg;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* This function executes the "capture" action and store the result in a
|
||||
* capture slot if exists. It executes a fetch expression, turns the result
|
||||
* into a string and puts it in a capture slot. It always returns 1. If an
|
||||
* error occurs the action is cancelled, but the rule processing continues.
|
||||
*/
|
||||
static enum act_return http_action_res_capture_by_id(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *s, int flags)
|
||||
{
|
||||
struct sample *key;
|
||||
struct cap_hdr *h;
|
||||
char **cap = s->res_cap;
|
||||
struct proxy *fe = strm_fe(s);
|
||||
int len;
|
||||
int i;
|
||||
|
||||
/* Look for the original configuration. */
|
||||
for (h = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
|
||||
h != NULL && i != rule->arg.capid.idx ;
|
||||
i--, h = h->next);
|
||||
if (!h)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_RES|SMP_OPT_FINAL, rule->arg.capid.expr, SMP_T_STR);
|
||||
if (!key)
|
||||
return ACT_RET_CONT;
|
||||
|
||||
if (cap[h->index] == NULL)
|
||||
cap[h->index] = pool_alloc(h->pool);
|
||||
|
||||
if (cap[h->index] == NULL) /* no more capture memory */
|
||||
return ACT_RET_CONT;
|
||||
|
||||
len = key->data.u.str.data;
|
||||
if (len > h->len)
|
||||
len = h->len;
|
||||
|
||||
memcpy(cap[h->index], key->data.u.str.area, len);
|
||||
cap[h->index][len] = 0;
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* Check an "http-response capture" action.
|
||||
*
|
||||
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
||||
* filled.
|
||||
*/
|
||||
static int check_http_res_capture(struct act_rule *rule, struct proxy *px, char **err)
|
||||
{
|
||||
if (rule->action_ptr != http_action_res_capture_by_id)
|
||||
return 1;
|
||||
|
||||
if (rule->arg.capid.idx >= px->nb_rsp_cap) {
|
||||
memprintf(err, "unable to find capture id '%d' referenced by http-response capture rule",
|
||||
rule->arg.capid.idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* parse an "http-response capture" action. It takes a single argument which is
|
||||
* a sample fetch expression. It stores the expression into arg->act.p[0] and
|
||||
* the allocated hdr_cap struct od the preallocated id into arg->act.p[1].
|
||||
* It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
struct sample_expr *expr;
|
||||
int cur_arg;
|
||||
int id;
|
||||
char *error;
|
||||
|
||||
for (cur_arg = *orig_arg; cur_arg < *orig_arg + 3 && *args[cur_arg]; cur_arg++)
|
||||
if (strcmp(args[cur_arg], "if") == 0 ||
|
||||
strcmp(args[cur_arg], "unless") == 0)
|
||||
break;
|
||||
|
||||
if (cur_arg < *orig_arg + 3) {
|
||||
memprintf(err, "expects <expression> id <idx>");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
cur_arg = *orig_arg;
|
||||
expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
if (!(expr->fetch->val & SMP_VAL_FE_HRS_HDR)) {
|
||||
memprintf(err,
|
||||
"fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (!args[cur_arg] || !*args[cur_arg]) {
|
||||
memprintf(err, "expects 'id'");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
if (strcmp(args[cur_arg], "id") != 0) {
|
||||
memprintf(err, "expects 'id', found '%s'", args[cur_arg]);
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
|
||||
if (!args[cur_arg]) {
|
||||
memprintf(err, "missing id value");
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
id = strtol(args[cur_arg], &error, 10);
|
||||
if (*error != '\0') {
|
||||
memprintf(err, "cannot parse id '%s'", args[cur_arg]);
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
cur_arg++;
|
||||
|
||||
px->conf.args.ctx = ARGC_CAP;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = http_action_res_capture_by_id;
|
||||
rule->check_ptr = check_http_res_capture;
|
||||
rule->arg.capid.expr = expr;
|
||||
rule->arg.capid.idx = id;
|
||||
|
||||
*orig_arg = cur_arg;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* All supported http-request action keywords must be declared here. */
|
||||
/************************************************************************/
|
||||
|
||||
static struct action_kw_list http_req_actions = {
|
||||
.kw = {
|
||||
{ "capture", parse_http_req_capture },
|
||||
{ "reject", parse_http_action_reject },
|
||||
{ "set-method", parse_set_req_line },
|
||||
{ "set-path", parse_set_req_line },
|
||||
{ "set-query", parse_set_req_line },
|
||||
{ "set-uri", parse_set_req_line },
|
||||
{ NULL, NULL }
|
||||
}
|
||||
};
|
||||
|
||||
static struct action_kw_list http_res_actions = {
|
||||
.kw = {
|
||||
{ "capture", parse_http_res_capture },
|
||||
{ "set-status", parse_http_set_status },
|
||||
{ NULL, NULL }
|
||||
}
|
||||
};
|
||||
|
||||
__attribute__((constructor))
|
||||
static void __http_act_init(void)
|
||||
{
|
||||
http_req_keywords_register(&http_req_actions);
|
||||
http_res_keywords_register(&http_res_actions);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* HTTP sample conversion
|
||||
*
|
||||
* Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <common/chunk.h>
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/http.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/version.h>
|
||||
|
||||
#include <types/capture.h>
|
||||
#include <types/global.h>
|
||||
|
||||
#include <proto/arg.h>
|
||||
#include <proto/sample.h>
|
||||
#include <proto/stream.h>
|
||||
|
||||
|
||||
/* takes an UINT value on input supposed to represent the time since EPOCH,
|
||||
* adds an optional offset found in args[0] and emits a string representing
|
||||
* the date in RFC-1123/5322 format.
|
||||
*/
|
||||
static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
struct buffer *temp;
|
||||
struct tm *tm;
|
||||
/* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
|
||||
time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
|
||||
|
||||
/* add offset */
|
||||
if (args && (args[0].type == ARGT_SINT))
|
||||
curr_date += args[0].data.sint;
|
||||
|
||||
tm = gmtime(&curr_date);
|
||||
if (!tm)
|
||||
return 0;
|
||||
|
||||
temp = get_trash_chunk();
|
||||
temp->data = snprintf(temp->area, temp->size - temp->data,
|
||||
"%s, %02d %s %04d %02d:%02d:%02d GMT",
|
||||
day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
|
||||
1900+tm->tm_year,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
smp->data.u.str = *temp;
|
||||
smp->data.type = SMP_T_STR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Arguments: The list of expected value, the number of parts returned and the separator */
|
||||
static int sample_conv_q_preferred(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
const char *al = smp->data.u.str.area;
|
||||
const char *end = al + smp->data.u.str.data;
|
||||
const char *token;
|
||||
int toklen;
|
||||
int qvalue;
|
||||
const char *str;
|
||||
const char *w;
|
||||
int best_q = 0;
|
||||
|
||||
/* Set the constant to the sample, because the output of the
|
||||
* function will be peek in the constant configuration string.
|
||||
*/
|
||||
smp->flags |= SMP_F_CONST;
|
||||
smp->data.u.str.size = 0;
|
||||
smp->data.u.str.area = "";
|
||||
smp->data.u.str.data = 0;
|
||||
|
||||
/* Parse the accept language */
|
||||
while (1) {
|
||||
|
||||
/* Jump spaces, quit if the end is detected. */
|
||||
while (al < end && isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al >= end)
|
||||
break;
|
||||
|
||||
/* Start of the fisrt word. */
|
||||
token = al;
|
||||
|
||||
/* Look for separator: isspace(), ',' or ';'. Next value if 0 length word. */
|
||||
while (al < end && *al != ';' && *al != ',' && !isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al == token)
|
||||
goto expect_comma;
|
||||
|
||||
/* Length of the token. */
|
||||
toklen = al - token;
|
||||
qvalue = 1000;
|
||||
|
||||
/* Check if the token exists in the list. If the token not exists,
|
||||
* jump to the next token.
|
||||
*/
|
||||
str = args[0].data.str.area;
|
||||
w = str;
|
||||
while (1) {
|
||||
if (*str == ';' || *str == '\0') {
|
||||
if (http_language_range_match(token, toklen, w, str - w))
|
||||
goto look_for_q;
|
||||
if (*str == '\0')
|
||||
goto expect_comma;
|
||||
w = str + 1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
goto expect_comma;
|
||||
|
||||
look_for_q:
|
||||
|
||||
/* Jump spaces, quit if the end is detected. */
|
||||
while (al < end && isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al >= end)
|
||||
goto process_value;
|
||||
|
||||
/* If ',' is found, process the result */
|
||||
if (*al == ',')
|
||||
goto process_value;
|
||||
|
||||
/* If the character is different from ';', look
|
||||
* for the end of the header part in best effort.
|
||||
*/
|
||||
if (*al != ';')
|
||||
goto expect_comma;
|
||||
|
||||
/* Assumes that the char is ';', now expect "q=". */
|
||||
al++;
|
||||
|
||||
/* Jump spaces, process value if the end is detected. */
|
||||
while (al < end && isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al >= end)
|
||||
goto process_value;
|
||||
|
||||
/* Expect 'q'. If no 'q', continue in best effort */
|
||||
if (*al != 'q')
|
||||
goto process_value;
|
||||
al++;
|
||||
|
||||
/* Jump spaces, process value if the end is detected. */
|
||||
while (al < end && isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al >= end)
|
||||
goto process_value;
|
||||
|
||||
/* Expect '='. If no '=', continue in best effort */
|
||||
if (*al != '=')
|
||||
goto process_value;
|
||||
al++;
|
||||
|
||||
/* Jump spaces, process value if the end is detected. */
|
||||
while (al < end && isspace((unsigned char)*al))
|
||||
al++;
|
||||
if (al >= end)
|
||||
goto process_value;
|
||||
|
||||
/* Parse the q value. */
|
||||
qvalue = http_parse_qvalue(al, &al);
|
||||
|
||||
process_value:
|
||||
|
||||
/* If the new q value is the best q value, then store the associated
|
||||
* language in the response. If qvalue is the biggest value (1000),
|
||||
* break the process.
|
||||
*/
|
||||
if (qvalue > best_q) {
|
||||
smp->data.u.str.area = (char *)w;
|
||||
smp->data.u.str.data = str - w;
|
||||
if (qvalue >= 1000)
|
||||
break;
|
||||
best_q = qvalue;
|
||||
}
|
||||
|
||||
expect_comma:
|
||||
|
||||
/* Expect comma or end. If the end is detected, quit the loop. */
|
||||
while (al < end && *al != ',')
|
||||
al++;
|
||||
if (al >= end)
|
||||
break;
|
||||
|
||||
/* Comma is found, jump it and restart the analyzer. */
|
||||
al++;
|
||||
}
|
||||
|
||||
/* Set default value if required. */
|
||||
if (smp->data.u.str.data == 0 && args[1].type == ARGT_STR) {
|
||||
smp->data.u.str.area = args[1].data.str.area;
|
||||
smp->data.u.str.data = args[1].data.str.data;
|
||||
}
|
||||
|
||||
/* Return true only if a matching language was found. */
|
||||
return smp->data.u.str.data != 0;
|
||||
}
|
||||
|
||||
/* This fetch url-decode any input string. */
|
||||
static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* If the constant flag is set or if not size is avalaible at
|
||||
* the end of the buffer, copy the string in other buffer
|
||||
* before decoding.
|
||||
*/
|
||||
if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
|
||||
struct buffer *str = get_trash_chunk();
|
||||
memcpy(str->area, smp->data.u.str.area, smp->data.u.str.data);
|
||||
smp->data.u.str.area = str->area;
|
||||
smp->data.u.str.size = str->size;
|
||||
smp->flags &= ~SMP_F_CONST;
|
||||
}
|
||||
|
||||
/* Add final \0 required by url_decode(), and convert the input string. */
|
||||
smp->data.u.str.area[smp->data.u.str.data] = '\0';
|
||||
len = url_decode(smp->data.u.str.area);
|
||||
if (len < 0)
|
||||
return 0;
|
||||
smp->data.u.str.data = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int smp_conv_req_capture(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
struct proxy *fe = strm_fe(smp->strm);
|
||||
int idx, i;
|
||||
struct cap_hdr *hdr;
|
||||
int len;
|
||||
|
||||
if (!args || args->type != ARGT_SINT)
|
||||
return 0;
|
||||
|
||||
idx = args->data.sint;
|
||||
|
||||
/* Check the availibity of the capture id. */
|
||||
if (idx > fe->nb_req_cap - 1)
|
||||
return 0;
|
||||
|
||||
/* Look for the original configuration. */
|
||||
for (hdr = fe->req_cap, i = fe->nb_req_cap - 1;
|
||||
hdr != NULL && i != idx ;
|
||||
i--, hdr = hdr->next);
|
||||
if (!hdr)
|
||||
return 0;
|
||||
|
||||
/* check for the memory allocation */
|
||||
if (smp->strm->req_cap[hdr->index] == NULL)
|
||||
smp->strm->req_cap[hdr->index] = pool_alloc(hdr->pool);
|
||||
if (smp->strm->req_cap[hdr->index] == NULL)
|
||||
return 0;
|
||||
|
||||
/* Check length. */
|
||||
len = smp->data.u.str.data;
|
||||
if (len > hdr->len)
|
||||
len = hdr->len;
|
||||
|
||||
/* Capture input data. */
|
||||
memcpy(smp->strm->req_cap[idx], smp->data.u.str.area, len);
|
||||
smp->strm->req_cap[idx][len] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void *private)
|
||||
{
|
||||
struct proxy *fe = strm_fe(smp->strm);
|
||||
int idx, i;
|
||||
struct cap_hdr *hdr;
|
||||
int len;
|
||||
|
||||
if (!args || args->type != ARGT_SINT)
|
||||
return 0;
|
||||
|
||||
idx = args->data.sint;
|
||||
|
||||
/* Check the availibity of the capture id. */
|
||||
if (idx > fe->nb_rsp_cap - 1)
|
||||
return 0;
|
||||
|
||||
/* Look for the original configuration. */
|
||||
for (hdr = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
|
||||
hdr != NULL && i != idx ;
|
||||
i--, hdr = hdr->next);
|
||||
if (!hdr)
|
||||
return 0;
|
||||
|
||||
/* check for the memory allocation */
|
||||
if (smp->strm->res_cap[hdr->index] == NULL)
|
||||
smp->strm->res_cap[hdr->index] = pool_alloc(hdr->pool);
|
||||
if (smp->strm->res_cap[hdr->index] == NULL)
|
||||
return 0;
|
||||
|
||||
/* Check length. */
|
||||
len = smp->data.u.str.data;
|
||||
if (len > hdr->len)
|
||||
len = hdr->len;
|
||||
|
||||
/* Capture input data. */
|
||||
memcpy(smp->strm->res_cap[idx], smp->data.u.str.area, len);
|
||||
smp->strm->res_cap[idx][len] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* All supported converter keywords must be declared here. */
|
||||
/************************************************************************/
|
||||
|
||||
/* Note: must not be declared <const> as its list will be overwritten */
|
||||
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||
{ "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_T_STR},
|
||||
{ "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
|
||||
{ "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
||||
{ "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
|
||||
{ "url_dec", sample_conv_url_dec, 0, NULL, SMP_T_STR, SMP_T_STR},
|
||||
{ NULL, NULL, 0, 0, 0 },
|
||||
}};
|
||||
|
||||
__attribute__((constructor))
|
||||
static void __http_conv_init(void)
|
||||
{
|
||||
sample_register_convs(&sample_conv_kws);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
2963
src/proto_http.c
2963
src/proto_http.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue