src/session.o src/stream.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o src/proto_udp.o \
src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o \
- src/namespace.o src/mailers.o src/dns.o src/vars.o
+ src/namespace.o src/mailers.o src/dns.o src/vars.o src/filters.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
$(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
--- /dev/null
+/*
+ * include/proto/filters.h
+ * This file defines function prototypes for stream filters management.
+ *
+ * Copyright (C) 2015 Qualys Inc., Christopher Faulet <cfaulet@qualys.com>
+ *
+ * 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_FILTERS_H
+#define _PROTO_FILTERS_H
+
+#include <types/channel.h>
+#include <types/filters.h>
+#include <types/proto_http.h>
+#include <types/proxy.h>
+#include <types/stream.h>
+
+#include <proto/channel.h>
+
+/* Useful macros to access per-channel values. It can be safely used inside
+ * filters. */
+#define CHN_IDX(chn) (((chn)->flags & CF_ISRESP) == CF_ISRESP)
+#define FLT_NXT(flt, chn) ((flt)->next[CHN_IDX(chn)])
+#define FLT_FWD(flt, chn) ((flt)->fwd[CHN_IDX(chn)])
+
+extern struct pool_head *pool2_filter;
+
+int flt_init(struct proxy *p);
+void flt_deinit(struct proxy *p);
+int flt_check(struct proxy *p);
+
+int flt_stream_start(struct stream *s);
+void flt_stream_stop(struct stream *s);
+
+int flt_http_headers(struct stream *s, struct http_msg *msg);
+int flt_http_start_chunk(struct stream *s, struct http_msg *msg);
+int flt_http_data(struct stream *s, struct http_msg *msg);
+int flt_http_last_chunk(struct stream *s, struct http_msg *msg);
+int flt_http_end_chunk(struct stream *s, struct http_msg *msg);
+int flt_http_chunk_trailers(struct stream *s, struct http_msg *msg);
+int flt_http_end(struct stream *s, struct http_msg *msg);
+void flt_http_reset(struct stream *s, struct http_msg *msg);
+
+void flt_http_reply(struct stream *s, short status, const struct chunk *msg);
+int flt_http_forward_data(struct stream *s, struct http_msg *msg, unsigned int len);
+
+int flt_start_analyze(struct stream *s, struct channel *chn, unsigned int an_bit);
+int flt_analyze(struct stream *s, struct channel *chn, unsigned int an_bit);
+int flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit);
+
+int flt_xfer_data(struct stream *s, struct channel *chn, unsigned int an_bit);
+
+void flt_register_keywords(struct flt_kw_list *kwl);
+struct flt_kw *flt_find_kw(const char *kw);
+void flt_dump_kws(char **out);
+
+static inline void
+flt_set_forward_data(struct filter *filter, struct channel *chn)
+{
+ filter->flags[CHN_IDX(chn)] |= FILTER_FL_FORWARD_DATA;
+}
+
+static inline void
+flt_reset_forward_data(struct filter *filter, struct channel *chn)
+{
+ filter->flags[CHN_IDX(chn)] &= ~FILTER_FL_FORWARD_DATA;
+}
+
+static inline int
+flt_want_forward_data(struct filter *filter, const struct channel *chn)
+{
+ return filter->flags[CHN_IDX(chn)] & FILTER_FL_FORWARD_DATA;
+}
+
+
+/* This function must be called when a filter alter incoming data. It updates
+ * next offset value of all filter's predecessors. Do not call this function
+ * when a filter change the size of incomding data leads to an undefined
+ * behavior.
+ *
+ * This is the filter's responsiblitiy to update data itself. For now, it is
+ * unclear to know how to handle data updates, so we do the minimum here. For
+ * example, if you filter an HTTP message, we must update msg->next and
+ * msg->chunk_len values.
+ */
+static inline void
+flt_change_next_size(struct filter *filter, struct channel *chn, int len)
+{
+ struct stream *s = chn_strm(chn);
+ struct filter *f;
+
+ list_for_each_entry(f, &s->strm_flt.filters, list) {
+ if (f == filter)
+ break;
+ FLT_NXT(f, chn) += len;
+ }
+}
+
+/* This function must be called when a filter alter forwarded data. It updates
+ * offset values (next and forward) of all filters. Do not call this function
+ * when a filter change the size of forwarded data leads to an undefined
+ * behavior.
+ *
+ * This is the filter's responsiblitiy to update data itself. For now, it is
+ * unclear to know how to handle data updates, so we do the minimum here. For
+ * example, if you filter an HTTP message, we must update msg->next and
+ * msg->chunk_len values.
+ */
+static inline void
+flt_change_forward_size(struct filter *filter, struct channel *chn, int len)
+{
+ struct stream *s = chn_strm(chn);
+ struct filter *f;
+ int before = 1;
+
+ list_for_each_entry(f, &s->strm_flt.filters, list) {
+ if (f == filter)
+ before = 0;
+ if (before)
+ FLT_FWD(f, chn) += len;
+ FLT_NXT(f, chn) += len;
+ }
+}
+
+
+#endif /* _PROTO_FILTERS_H */
void http_init_txn(struct stream *s);
void http_end_txn(struct stream *s);
void http_reset_txn(struct stream *s);
+void http_end_txn_clean_session(struct stream *s);
void http_adjust_conn_mode(struct stream *s, struct http_txn *txn, struct http_msg *msg);
struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
case HTTP_MSG_DATA: return "MSG_DATA";
case HTTP_MSG_CHUNK_CRLF: return "MSG_CHUNK_CRLF";
case HTTP_MSG_TRAILERS: return "MSG_TRAILERS";
+ case HTTP_MSG_ENDING: return "MSG_ENDING";
case HTTP_MSG_DONE: return "MSG_DONE";
case HTTP_MSG_CLOSING: return "MSG_CLOSING";
case HTTP_MSG_CLOSED: return "MSG_CLOSED";
#define AN_RES_STORE_RULES 0x00080000 /* table persistence matching */
#define AN_RES_HTTP_XFER_BODY 0x00100000 /* forward response body */
+#define AN_FLT_START_FE 0x01000000
+#define AN_FLT_START_BE 0x02000000
+#define AN_FLT_END 0x04000000
+#define AN_FLT_XFER_DATA 0x08000000
+
+#define AN_FLT_ALL_FE 0x0d000000
+#define AN_FLT_ALL_BE 0x0e000000
/* Magic value to forward infinite size (TCP, ...), used with ->to_forward */
#define CHN_INFINITE_FORWARD MAX_RANGE(unsigned int)
--- /dev/null
+/*
+ * include/types/filteers.h
+ * This file defines everything related to stream filters.
+ *
+ * Copyright (C) 2015 Qualys Inc., Christopher Faulet <cfaulet@qualys.com>
+ *
+ * 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 _TYPES_FILTERS_H
+#define _TYPES_FILTERS_H
+
+#include <common/config.h>
+#include <common/mini-clist.h>
+
+struct http_msg;
+struct proxy;
+struct stream;
+struct channel;
+struct filter;
+
+/* Descriptor for a "filter" keyword. The ->parse() function returns 0 in case
+ * of success, or a combination of ERR_* flags if an error is encountered. The
+ * function pointer can be NULL if not implemented. The function also has an
+ * access to the current "server" config line. The ->skip value tells the parser
+ * how many words have to be skipped after the keyword. If the function needs to
+ * parse more keywords, it needs to update cur_arg.
+ */
+struct flt_kw {
+ const char *kw;
+ int (*parse)(char **args, int *cur_arg, struct proxy *px,
+ struct filter *filter, char **err);
+};
+
+/*
+ * A keyword list. It is a NULL-terminated array of keywords. It embeds a struct
+ * list in order to be linked to other lists, allowing it to easily be declared
+ * where it is needed, and linked without duplicating data nor allocating
+ * memory. It is also possible to indicate a scope for the keywords.
+ */
+struct flt_kw_list {
+ const char *scope;
+ struct list list;
+ struct flt_kw kw[VAR_ARRAY];
+};
+
+/*
+ * Filter flags set for a specific filter on channel
+ *
+ * - FILTER_FL_FORWARD_DATA : When this flag is set, the rest of the data is
+ * directly forwarded. For chunk-encoded HTTP
+ * messages, this flag is reseted between each
+ * chunks.
+ */
+#define FILTER_FL_FORWARD_DATA 0x00000001
+
+
+/*
+ * Callbacks available on a filter:
+ *
+ * - init : Initializes the filter for a proxy. Returns a
+ * negative value if an error occurs.
+ * - deinit : Cleans up what the init function has done.
+ * - check : Check the filter config for a proxy. Returns the
+ * number of errors encountered.
+ *
+ *
+ * - stream_start : Called when a stream is started. This callback will
+ * only be called for filters defined on a proxy with
+ * the frontend capability.
+ * Returns a negative value if an error occurs, any
+ * other value otherwise.
+ * - stream_stop : Called when a stream is stopped. This callback will
+ * only be called for filters defined on a proxy with
+ * the frontend capability.
+ *
+ *
+ * - channel_start_analyze: Called when a filter starts to analyze a channel.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to wait, any other value otherwise.
+ * - channel_analyze : Called before each analyzer attached to a channel,
+ * expects analyzers responsible for data sending.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to wait, any other value otherwise.
+ * - channel_end_analyze : Called when all other analyzers have finished their
+ * processing.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to wait, any other value otherwise.
+ *
+ *
+ * - http_headers : Called just before headers sending and parsing of
+ * the body. At this step, headers are fully parsed
+ * and the processing on it is finished.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to read more data (or to wait for some
+ * reason), any other value otherwise.
+ * - http_start_chunk : Called when we start to process a new chunk
+ * (for chunk-encoded request/response only). At this
+ * step, the chunk length is known and non-null.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to read more data (or to wait for some
+ * reason), any other value otherwise.
+ * - http_data : Called when unparsed body data are available.
+ * Returns a negative value if an error occurs, else
+ * the number of consumed bytes.
+ * - http_last_chunk : Called when the last chunk (with a zero length) is
+ * received.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to read more data (or to wait for some
+ * reason), any other value otherwise.
+ * - http_end_chunk : Called at the end of a chunk (expect for the last
+ * one).
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to read more data (or to wait for some
+ * reason), any other value otherwise.
+ * - http_chunk_trailers : Called when part of trailer headers of a
+ * chunk-encoded request/response are ready to be
+ * processed.
+ * Returns a negative value if an error occurs, any
+ * other value otherwise.
+ * - http_end : Called when all the request/response has been
+ * processed and all body data has been forwarded.
+ * Returns a negative value if an error occurs, 0 if
+ * it needs to wait for some reason, any other value
+ * otherwise.
+ * - http_reset : Called when the HTTP message is reseted. It happens
+ * when a 100-continue response is received.
+ * Returns nothing.
+ * - http_reply : Called when, at any time, HA proxy decides to stop
+ * the HTTP message's processing and to send a message
+ * to the client (mainly, when an error or a redirect
+ * occur).
+ * Returns nothing.
+ * - http_forward_data : Called when some data can be consumed.
+ * Returns a negative value if an error occurs, else
+ * the number of forwarded bytes.
+ * - tcp_data : Called when unparsed data are available.
+ * Returns a negative value if an error occurs, else
+ * the number of consumed bytes.
+ * - tcp_forward_data : Called when some data can be consumed.
+ * Returns a negative value if an error occurs, else
+ * or the number of forwarded bytes.
+ */
+struct flt_ops {
+ /*
+ * Callbacks to manage the filter lifecycle
+ */
+ int (*init) (struct proxy *p, struct filter *f);
+ void (*deinit)(struct proxy *p, struct filter *f);
+ int (*check) (struct proxy *p, struct filter *f);
+
+ /*
+ * Stream callbacks
+ */
+ int (*stream_start) (struct stream *s, struct filter *f);
+ void (*stream_stop) (struct stream *s, struct filter *f);
+
+ /*
+ * Channel callbacks
+ */
+ int (*channel_start_analyze)(struct stream *s, struct filter *f, struct channel *chn);
+ int (*channel_analyze) (struct stream *s, struct filter *f, struct channel *chn, unsigned int an_bit);
+ int (*channel_end_analyze) (struct stream *s, struct filter *f, struct channel *chn);
+
+ /*
+ * HTTP callbacks
+ */
+ int (*http_headers) (struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_start_chunk) (struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_data) (struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_last_chunk) (struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_end_chunk) (struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_chunk_trailers)(struct stream *s, struct filter *f, struct http_msg *msg);
+ int (*http_end) (struct stream *s, struct filter *f, struct http_msg *msg);
+ void (*http_reset) (struct stream *s, struct filter *f, struct http_msg *msg);
+
+ void (*http_reply) (struct stream *s, struct filter *f, short status,
+ const struct chunk *msg);
+ int (*http_forward_data) (struct stream *s, struct filter *f, struct http_msg *msg,
+ unsigned int len);
+
+ /*
+ * TCP callbacks
+ */
+ int (*tcp_data) (struct stream *s, struct filter *f, struct channel *chn);
+ int (*tcp_forward_data)(struct stream *s, struct filter *f, struct channel *chn,
+ unsigned int len);
+};
+
+/*
+ * Structure representing the state of a filter. When attached to a proxy, only
+ * <ops> and <conf> field (and optionnaly <id>) are set. All other fields are
+ * used when the filter is attached to a stream.
+ *
+ * 2D-Array fields are used to store info per channel. The first index stands
+ * for the request channel, and the second one for the response channel.
+ * Especially, <next> and <fwd> are offets representing amount of data that the
+ * filter are, respectively, parsed and forwarded on a channel. Filters can
+ * access these values using FLT_NXT and FLT_FWD macros.
+ */
+struct filter {
+ const char *id; /* The filter id */
+ struct flt_ops *ops; /* The filter callbacks */
+ void *conf; /* The filter configuration */
+ void *ctx; /* The filter context (opaque) */
+ int is_backend_filter; /* Flag to specify if the filter is a "backend" filter */
+ unsigned int flags[2]; /* 0: request, 1: response */
+ unsigned int next[2]; /* Offset, relative to buf->p, to the next byte to parse for a specific channel
+ * 0: request channel, 1: response channel */
+ unsigned int fwd[2]; /* Offset, relative to buf->p, to the next byte to forward for a specific channel
+ * 0: request channel, 1: response channel */
+ struct list list; /* Next filter for the same proxy/stream */
+};
+
+#endif /* _TYPES_FILTERS_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
#include <common/regex.h>
#include <types/hdr_idx.h>
-#include <types/sample.h>
+#include <types/filters.h>
/* These are the flags that are found in txn->flags */
HTTP_MSG_CHUNK_CRLF = 31, // skipping CRLF after data chunk
HTTP_MSG_TRAILERS = 32, // trailers (post-data entity headers)
/* we enter this state when we've received the end of the current message */
- HTTP_MSG_DONE = 33, // message end received, waiting for resync or close
- HTTP_MSG_CLOSING = 34, // shutdown_w done, not all bytes sent yet
- HTTP_MSG_CLOSED = 35, // shutdown_w done, all bytes sent
- HTTP_MSG_TUNNEL = 36, // tunneled data after DONE
+ HTTP_MSG_ENDING = 33, // message end received, wait that the filters end too
+ HTTP_MSG_DONE = 34, // message end received, waiting for resync or close
+ HTTP_MSG_CLOSING = 35, // shutdown_w done, not all bytes sent yet
+ HTTP_MSG_CLOSED = 36, // shutdown_w done, all bytes sent
+ HTTP_MSG_TUNNEL = 37, // tunneled data after DONE
} __attribute__((packed));
/*
* contents if something needs them during a redispatch.
*/
#define HTTP_MSGF_WAIT_CONN 0x00000010 /* Wait for connect() to be confirmed before processing body */
+#define HTTP_MSGF_COMPRESSING 0x00000020 /* data compression is in progress */
/* Redirect flags */
#include <types/acl.h>
#include <types/backend.h>
#include <types/counters.h>
+#include <types/filters.h>
#include <types/freq_ctr.h>
#include <types/listener.h>
#include <types/log.h>
* this backend. If not specified or void, then the backend
* name is used
*/
+
+ struct list filters;
};
struct switching_rule {
#include <types/channel.h>
#include <types/compression.h>
+#include <types/filters.h>
#include <types/hlua.h>
#include <types/obj_type.h>
#include <types/proto_http.h>
#include <types/stick_table.h>
#include <types/vars.h>
-
/* Various Stream Flags, bits values 0x01 to 0x100 (shift 0) */
#define SF_DIRECT 0x00000001 /* connection made on the server matching the client cookie */
#define SF_ASSIGNED 0x00000002 /* no need to assign a server to this stream */
struct http_txn *txn; /* current HTTP transaction being processed. Should become a list. */
+ struct {
+ struct list filters;
+ struct filter *current[2]; /* 0: request, 1: response */
+ } strm_flt;
+
struct task *task; /* the task associated with this stream */
struct list list; /* position in global streams list */
struct list by_srv; /* position in server stream list */
#include <types/capture.h>
#include <types/compression.h>
+#include <types/filters.h>
#include <types/global.h>
#include <types/obj_type.h>
#include <types/peers.h>
#include <proto/checks.h>
#include <proto/compression.h>
#include <proto/dumpstats.h>
+#include <proto/filters.h>
#include <proto/frontend.h>
#include <proto/hdr_idx.h>
#include <proto/lb_chash.h>
}
}
+ /* Check filter configuration, if any */
+ cfgerr += flt_check(curproxy);
+
if (curproxy->cap & PR_CAP_FE) {
if (!curproxy->accept)
curproxy->accept = frontend_accept;
/* both TCP and HTTP must check switching rules */
curproxy->fe_req_ana |= AN_REQ_SWITCHING_RULES;
+
+ /* Add filters analyzers if needed */
+ if (!LIST_ISEMPTY(&curproxy->filters)) {
+ curproxy->fe_req_ana |= AN_FLT_ALL_FE;
+ curproxy->fe_rsp_ana |= AN_FLT_ALL_FE;
+ }
}
if (curproxy->cap & PR_CAP_BE) {
*/
if (curproxy->options2 & PR_O2_RDPC_PRST)
curproxy->be_req_ana |= AN_REQ_PRST_RDP_COOKIE;
+
+ /* Add filters analyzers if needed */
+ if (!LIST_ISEMPTY(&curproxy->filters)) {
+ curproxy->be_req_ana |= AN_FLT_ALL_BE;
+ curproxy->be_rsp_ana |= AN_FLT_ALL_BE;
+ }
}
}
/* restore original buffer pointer */
b_rew(in, msg->next);
-
- if (consumed_data > 0) {
- msg->next += consumed_data;
- msg->chunk_len -= consumed_data;
- }
return consumed_data;
}
if (msg->msg_state >= HTTP_MSG_TRAILERS) {
memcpy(tail, "0\r\n", 3);
tail += 3;
- if (msg->msg_state >= HTTP_MSG_DONE) {
+ if (msg->msg_state >= HTTP_MSG_ENDING) {
memcpy(tail, "\r\n", 2);
tail += 2;
}
--- /dev/null
+/*
+ * Stream filters related variables and functions.
+ *
+ * Copyright (C) 2015 Qualys Inc., Christopher Faulet <cfaulet@qualys.com>
+ *
+ * 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 <common/buffer.h>
+#include <common/debug.h>
+#include <common/cfgparse.h>
+#include <common/compat.h>
+#include <common/config.h>
+#include <common/errors.h>
+#include <common/namespace.h>
+#include <common/standard.h>
+
+#include <types/filters.h>
+#include <types/proto_http.h>
+
+#include <proto/compression.h>
+#include <proto/filters.h>
+#include <proto/proto_http.h>
+#include <proto/stream.h>
+#include <proto/stream_interface.h>
+
+/* Pool used to allocate filters */
+struct pool_head *pool2_filter = NULL;
+
+static int handle_analyzer_result(struct stream *s, struct channel *chn, unsigned int an_bit, int ret);
+
+/* - RESUME_FILTER_LOOP and RESUME_FILTER_END must always be used together.
+ * The first one begins a loop and the seconds one ends it.
+ *
+ * - BREAK_EXECUTION must be used to break the loop and set the filter from
+ * which to resume the next time.
+ *
+ * Here is an exemple:
+ *
+ * RESUME_FILTER_LOOP(stream, channel) {
+ * ...
+ * if (cond)
+ * BREAK_EXECUTION(stream, channel, label);
+ * ...
+ * } RESUME_FILTER_END;
+ * ...
+ * label:
+ * ...
+ *
+ */
+#define RESUME_FILTER_LOOP(strm, chn) \
+ do { \
+ struct filter *filter; \
+ \
+ if ((strm)->strm_flt.current[CHN_IDX(chn)]) { \
+ filter = (strm)->strm_flt.current[CHN_IDX(chn)]; \
+ (strm)->strm_flt.current[CHN_IDX(chn)] = NULL; \
+ goto resume_execution; \
+ } \
+ \
+ list_for_each_entry(filter, &s->strm_flt.filters, list) { \
+ resume_execution:
+
+#define RESUME_FILTER_END \
+ } \
+ } while(0)
+
+#define BREAK_EXECUTION(strm, chn, label) \
+ do { \
+ (strm)->strm_flt.current[CHN_IDX(chn)] = filter; \
+ goto label; \
+ } while (0)
+
+
+/* List head of all known filter keywords */
+static struct flt_kw_list flt_keywords = {
+ .list = LIST_HEAD_INIT(flt_keywords.list)
+};
+
+/*
+ * Registers the filter keyword list <kwl> as a list of valid keywords for next
+ * parsing sessions.
+ */
+void
+flt_register_keywords(struct flt_kw_list *kwl)
+{
+ LIST_ADDQ(&flt_keywords.list, &kwl->list);
+}
+
+/*
+ * Returns a pointer to the filter keyword <kw>, or NULL if not found. If the
+ * keyword is found with a NULL ->parse() function, then an attempt is made to
+ * find one with a valid ->parse() function. This way it is possible to declare
+ * platform-dependant, known keywords as NULL, then only declare them as valid
+ * if some options are met. Note that if the requested keyword contains an
+ * opening parenthesis, everything from this point is ignored.
+ */
+struct flt_kw *
+flt_find_kw(const char *kw)
+{
+ int index;
+ const char *kwend;
+ struct flt_kw_list *kwl;
+ struct flt_kw *ret = NULL;
+
+ kwend = strchr(kw, '(');
+ if (!kwend)
+ kwend = kw + strlen(kw);
+
+ list_for_each_entry(kwl, &flt_keywords.list, list) {
+ for (index = 0; kwl->kw[index].kw != NULL; index++) {
+ if ((strncmp(kwl->kw[index].kw, kw, kwend - kw) == 0) &&
+ kwl->kw[index].kw[kwend-kw] == 0) {
+ if (kwl->kw[index].parse)
+ return &kwl->kw[index]; /* found it !*/
+ else
+ ret = &kwl->kw[index]; /* may be OK */
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Dumps all registered "filter" keywords to the <out> string pointer. The
+ * unsupported keywords are only dumped if their supported form was not found.
+ */
+void
+flt_dump_kws(char **out)
+{
+ struct flt_kw_list *kwl;
+ int index;
+
+ *out = NULL;
+ list_for_each_entry(kwl, &flt_keywords.list, list) {
+ for (index = 0; kwl->kw[index].kw != NULL; index++) {
+ if (kwl->kw[index].parse ||
+ flt_find_kw(kwl->kw[index].kw) == &kwl->kw[index]) {
+ memprintf(out, "%s[%4s] %s%s\n", *out ? *out : "",
+ kwl->scope,
+ kwl->kw[index].kw,
+ kwl->kw[index].parse ? "" : " (not supported)");
+ }
+ }
+ }
+}
+
+/*
+ * Parses the "filter" keyword. All keywords must be handled by filters
+ * themselves
+ */
+static int
+parse_filter(char **args, int section_type, struct proxy *curpx,
+ struct proxy *defpx, const char *file, int line, char **err)
+{
+ struct filter *filter = NULL;
+
+ /* Filter cannot be defined on a default proxy */
+ if (curpx == defpx) {
+ memprintf(err, "parsing [%s:%d] : %s is only allowed in a 'default' section.",
+ file, line, args[0]);
+ return -1;
+ }
+ if (!strcmp(args[0], "filter")) {
+ struct flt_kw *kw;
+ int cur_arg;
+
+ if (!*args[1]) {
+ memprintf(err,
+ "parsing [%s:%d] : missing argument for '%s' in %s '%s'.",
+ file, line, args[0], proxy_type_str(curpx), curpx->id);
+ goto error;
+ }
+ filter = pool_alloc2(pool2_filter);
+ if (!filter) {
+ memprintf(err, "'%s' : out of memory", args[0]);
+ goto error;
+ }
+ memset(filter, 0, sizeof(*filter));
+
+ cur_arg = 1;
+ kw = flt_find_kw(args[cur_arg]);
+ if (kw) {
+ if (!kw->parse) {
+ memprintf(err, "parsing [%s:%d] : '%s' : "
+ "'%s' option is not implemented in this version (check build options).",
+ file, line, args[0], args[cur_arg]);
+ goto error;
+ }
+ if (kw->parse(args, &cur_arg, curpx, filter, err) != 0) {
+ if (err && *err)
+ memprintf(err, "'%s' : '%s'",
+ args[0], *err);
+ else
+ memprintf(err, "'%s' : error encountered while processing '%s'",
+ args[0], args[cur_arg]);
+ goto error;
+ }
+ }
+ else {
+ flt_dump_kws(err);
+ indent_msg(err, 4);
+ memprintf(err, "'%s' : unknown keyword '%s'.%s%s",
+ args[0], args[cur_arg],
+ err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
+ goto error;
+ }
+ if (*args[cur_arg]) {
+ memprintf(err, "'%s %s' : unknown keyword '%s'.",
+ args[0], args[1], args[cur_arg]);
+ goto error;
+ }
+
+ LIST_ADDQ(&curpx->filters, &filter->list);
+ }
+ return 0;
+
+ error:
+ if (filter)
+ pool_free2(pool2_filter, filter);
+ return -1;
+
+
+}
+
+/*
+ * Calls 'init' callback for all filters attached to a proxy. This happens after
+ * the configuration parsing. Filters can finish to fill their config. Returns
+ * (ERR_ALERT|ERR_FATAL) if an error occurs, 0 otherwise.
+ */
+int
+flt_init(struct proxy *proxy)
+{
+ struct filter *filter;
+
+ list_for_each_entry(filter, &proxy->filters, list) {
+ if (filter->ops->init && filter->ops->init(proxy, filter) < 0)
+ return ERR_ALERT|ERR_FATAL;
+ }
+ return 0;
+}
+
+/*
+ * Calls 'check' callback for all filters attached to a proxy. This happens
+ * after the configuration parsing but before filters initialization. Returns
+ * the number of encountered errors.
+ */
+int
+flt_check(struct proxy *proxy)
+{
+ struct filter *filter;
+ int err = 0;
+
+ list_for_each_entry(filter, &proxy->filters, list) {
+ if (filter->ops->check)
+ err += filter->ops->check(proxy, filter);
+ }
+ return err;
+}
+
+/*
+ * Calls 'denit' callback for all filters attached to a proxy. This happens when
+ * HAProxy is stopped.
+ */
+void
+flt_deinit(struct proxy *proxy)
+{
+ struct filter *filter, *back;
+
+ list_for_each_entry_safe(filter, back, &proxy->filters, list) {
+ if (filter->ops->deinit)
+ filter->ops->deinit(proxy, filter);
+ LIST_DEL(&filter->list);
+ pool_free2(pool2_filter, filter);
+ }
+}
+
+/*
+ * Calls 'stream_start' for all filters attached to a stream. This happens when
+ * the stream is created, just after calling flt_stream_init
+ * function. Returns -1 if an error occurs, 0 otherwise.
+ */
+int
+flt_stream_start(struct stream *s)
+{
+ struct filter *filter;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->stream_start && filter->ops->stream_start(s, filter) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Calls 'stream_stop' for all filters attached to a stream. This happens when
+ * the stream is stopped, just before calling flt_stream_release function.
+ */
+void
+flt_stream_stop(struct stream *s)
+{
+ struct filter *filter;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->stream_stop)
+ filter->ops->stream_stop(s, filter);
+ }
+}
+
+int
+flt_http_headers(struct stream *s, struct http_msg *msg)
+{
+ struct filter *filter;
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops && filter->ops->http_headers) {
+ ret = filter->ops->http_headers(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ } RESUME_FILTER_END;
+
+ /* We increase FLT_NXT offset after all processing on headers because
+ * any filter can alter them. So the definitive size of headers
+ * (msg->sov) is only known when all filters have been called. */
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ FLT_NXT(filter, msg->chn) = msg->sov;
+ }
+ end:
+ return ret;
+}
+
+int
+flt_http_start_chunk(struct stream *s, struct http_msg *msg)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops->http_start_chunk) {
+ ret = filter->ops->http_start_chunk(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ FLT_NXT(filter, msg->chn) += msg->sol;
+ } RESUME_FILTER_END;
+ end:
+ return ret;
+}
+
+/*
+ * Calls 'http_data' callback for all "data" filters attached to a stream. This
+ * function is called when incoming data are available (excluding chunks
+ * envelope for chunked messages) in the AN_REQ_HTTP_XFER_BODY and
+ * AN_RES_HTTP_XFER_BODY analyzers. It takes care to update the next offset of
+ * filters and adjusts available data to be sure that a filter cannot parse more
+ * data than its predecessors. A filter can choose to not consume all available
+ * data. Returns -1 if an error occurs, the number of consumed bytes otherwise.
+ */
+int
+flt_http_data(struct stream *s, struct http_msg *msg)
+{
+ struct filter *filter = NULL;
+ unsigned int buf_i;
+ int ret = 0;
+
+ /* No filter, consume all available data */
+ if (LIST_ISEMPTY(&s->strm_flt.filters)) {
+ ret = MIN(msg->chunk_len, msg->chn->buf->i - msg->next);
+ goto end;
+ }
+
+ /* Save buffer state */
+ buf_i = msg->chn->buf->i;
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->http_data && !flt_want_forward_data(filter, msg->chn)) {
+ ret = filter->ops->http_data(s, filter, msg);
+ if (ret < 0)
+ break;
+ }
+ else {
+ /* msg->chunk_len is the remaining size of data to parse
+ * in the body (or in the current chunk for
+ * chunk-encoded messages) from the HTTP parser point of
+ * view (relatively to msg->next). To have it from the
+ * filter point of view, we need to be add (msg->next
+ * -FLT_NEXT) to it. */
+ ret = MIN(msg->chunk_len + msg->next, msg->chn->buf->i) - FLT_NXT(filter, msg->chn);
+ }
+
+ /* Increase FLT_NXT offset of the current filter */
+ FLT_NXT(filter, msg->chn) += ret;
+
+ /* And set this value as the bound for the next filter. It will
+ * not able to parse more data than the current one. */
+ msg->chn->buf->i = FLT_NXT(filter, msg->chn);
+ }
+ /* Restore the original buffer state */
+ msg->chn->buf->i = buf_i;
+ end:
+ return ret;
+}
+
+int
+flt_http_end_chunk(struct stream *s, struct http_msg *msg)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops->http_end_chunk) {
+ ret = filter->ops->http_end_chunk(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ flt_reset_forward_data(filter, msg->chn);
+ FLT_NXT(filter, msg->chn) += msg->sol;
+ } RESUME_FILTER_END;
+ end:
+ return ret;
+}
+
+int
+flt_http_last_chunk(struct stream *s, struct http_msg *msg)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops->http_last_chunk) {
+ ret = filter->ops->http_last_chunk(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ flt_reset_forward_data(filter, msg->chn);
+ FLT_NXT(filter, msg->chn) += msg->sol;
+ } RESUME_FILTER_END;
+ end:
+ return ret;
+}
+
+
+/*
+ * Calls 'http_chunk_trailers' callback for all "data" filters attached to a
+ * stream. This function is called for chunked messages only when a part of the
+ * trailers was parsed in the AN_REQ_HTTP_XFER_BODY and AN_RES_HTTP_XFER_BODY
+ * analyzers. Filters can know how much data were parsed by the HTTP parsing
+ * until the last call with the msg->sol value. Returns a negative value if an
+ * error occurs, any other value otherwise.
+ */
+int
+flt_http_chunk_trailers(struct stream *s, struct http_msg *msg)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops->http_chunk_trailers) {
+ ret = filter->ops->http_chunk_trailers(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ FLT_NXT(filter, msg->chn) += msg->sol;
+ } RESUME_FILTER_END;
+end:
+ return ret;
+}
+
+/*
+ * Calls 'http_end' callback for all filters attached to a stream. All filters
+ * are called here, but only if there is at least one "data" filter. This
+ * functions is called when all data were parsed and forwarded. 'http_end'
+ * callback is resumable, so this function returns a negative value if an error
+ * occurs, 0 if it needs to wait for some reason, any other value otherwise.
+ */
+int
+flt_http_end(struct stream *s, struct http_msg *msg)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, msg->chn) {
+ if (filter->ops->http_end) {
+ ret = filter->ops->http_end(s, filter, msg);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, msg->chn, end);
+ }
+ flt_reset_forward_data(filter, msg->chn);
+ } RESUME_FILTER_END;
+end:
+ return ret;
+}
+
+/*
+ * Calls 'http_reset' callback for all filters attached to a stream. This
+ * happens when a 100-continue response is received.
+ */
+void
+flt_http_reset(struct stream *s, struct http_msg *msg)
+{
+ struct filter *filter;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ return;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->http_reset)
+ filter->ops->http_reset(s, filter, msg);
+ }
+}
+
+/*
+ * Calls 'http_reply' callback for all filters attached to a stream when HA
+ * decides to stop the HTTP message processing.
+ */
+void
+flt_http_reply(struct stream *s, short status, const struct chunk *msg)
+{
+ struct filter *filter;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ return;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->http_reply)
+ filter->ops->http_reply(s, filter, status, msg);
+ }
+}
+
+/*
+ * Calls 'http_forward_data' callback for all "data" filters attached to a
+ * stream. This function is called when some data can be forwarded in the
+ * AN_REQ_HTTP_XFER_BODY and AN_RES_HTTP_XFER_BODY analyzers. It takes care to
+ * update the forward offset of filters and adjusts "forwardable" data to be
+ * sure that a filter cannot forward more data than its predecessors. A filter
+ * can choose to not forward all parsed data. Returns a negative value if an
+ * error occurs, else the number of forwarded bytes.
+ */
+int
+flt_http_forward_data(struct stream *s, struct http_msg *msg, unsigned int len)
+{
+ struct filter *filter = NULL;
+ int ret = len;
+
+ /* No filter, forward all data */
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->http_forward_data) {
+ /* Remove bytes that the current filter considered as
+ * forwarded */
+ ret = filter->ops->http_forward_data(s, filter, msg,
+ ret - FLT_FWD(filter, msg->chn));
+ if (ret < 0)
+ goto end;
+ }
+
+ /* Adjust bytes that the current filter considers as
+ * forwarded */
+ FLT_FWD(filter, msg->chn) += ret;
+
+ /* And set this value as the bound for the next filter. It will
+ * not able to forward more data than the current one. */
+ ret = FLT_FWD(filter, msg->chn);
+ }
+
+ if (!ret)
+ goto end;
+
+ /* Finally, adjust filters offsets by removing data that HAProxy will
+ * forward. */
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ FLT_NXT(filter, msg->chn) -= ret;
+ FLT_FWD(filter, msg->chn) -= ret;
+ }
+ end:
+ return ret;
+}
+
+/*
+ * Calls 'channel_start_analyze' callback for all filters attached to a
+ * stream. This function is called when we start to analyze a request or a
+ * response. For frontend filters, it is called before all other analyzers. For
+ * backend ones, it is called before all backend
+ * analyzers. 'channel_start_analyze' callback is resumable, so this function
+ * returns 0 if an error occurs or if it needs to wait, any other value
+ * otherwise.
+ */
+int
+flt_start_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
+{
+ int ret = 1;
+
+ /* If this function is called, this means there is at least one filter,
+ * so we do not need to check the filter list's emptiness. */
+
+ RESUME_FILTER_LOOP(s, chn) {
+ if (an_bit == AN_FLT_START_BE && !filter->is_backend_filter)
+ continue;
+
+ filter->next[CHN_IDX(chn)] = 0;
+ filter->fwd[CHN_IDX(chn)] = 0;
+
+ if (filter->ops->channel_start_analyze) {
+ ret = filter->ops->channel_start_analyze(s, filter, chn);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, chn, end);
+ }
+ } RESUME_FILTER_END;
+
+ end:
+ return handle_analyzer_result(s, chn, an_bit, ret);
+}
+
+/*
+ * Calls 'channel_analyze' callback for all filters attached to a stream. This
+ * function is called before each analyzer attached to a channel, expects
+ * analyzers responsible for data sending. 'channel_analyze' callback is
+ * resumable, so this function returns 0 if an error occurs or if it needs to
+ * wait, any other value otherwise.
+ */
+int
+flt_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
+{
+ int ret = 1;
+
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ goto end;
+
+ RESUME_FILTER_LOOP(s, chn) {
+ if (filter->ops->channel_analyze) {
+ ret = filter->ops->channel_analyze(s, filter, chn, an_bit);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, chn, check_result);
+ }
+ } RESUME_FILTER_END;
+
+ check_result:
+ ret = handle_analyzer_result(s, chn, 0, ret);
+ end:
+ return ret;
+}
+
+/*
+ * Calls 'channel_end_analyze' callback for all filters attached to a
+ * stream. This function is called when we stop to analyze a request or a
+ * response. It is called after all other analyzers. 'channel_end_analyze'
+ * callback is resumable, so this function returns 0 if an error occurs or if it
+ * needs to wait, any other value otherwise.
+ */
+int
+flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
+{
+ int ret = 1;
+
+ /* If this function is called, this means there is at least one filter,
+ * so we do not need to check the filter list's emptiness. */
+
+ RESUME_FILTER_LOOP(s, chn) {
+ filter->next[CHN_IDX(chn)] = 0;
+
+ if (filter->ops->channel_end_analyze) {
+ ret = filter->ops->channel_end_analyze(s, filter, chn);
+ if (ret <= 0)
+ BREAK_EXECUTION(s, chn, end);
+ }
+ } RESUME_FILTER_END;
+
+end:
+ ret = handle_analyzer_result(s, chn, an_bit, ret);
+ if (!(s->req.analysers & AN_FLT_END) &&
+ !(s->res.analysers & AN_FLT_END) &&
+ s->txn && (s->txn->flags & TX_WAIT_NEXT_RQ)) {
+ struct filter *filter, *back;
+
+ s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
+ s->res.analysers = 0;
+
+ list_for_each_entry_safe(filter, back, &s->strm_flt.filters, list) {
+ if (filter->is_backend_filter) {
+ LIST_DEL(&filter->list);
+ pool_free2(pool2_filter, filter);
+ }
+ }
+ }
+ else if (ret) {
+ /* Analyzer ends only for one channel. So wake up the stream to
+ * be sure to process it for the other side as soon as
+ * possible. */
+ task_wakeup(s->task, TASK_WOKEN_MSG);
+ }
+ return ret;
+}
+
+
+/*
+ * Calls 'tcp_data' callback for all "data" filters attached to a stream. This
+ * function is called when incoming data are available. It takes care to update
+ * the next offset of filters and adjusts available data to be sure that a
+ * filter cannot parse more data than its predecessors. A filter can choose to
+ * not consume all available data. Returns -1 if an error occurs, the number of
+ * consumed bytes otherwise.
+ */
+static int
+flt_data(struct stream *s, struct channel *chn)
+{
+ struct filter *filter = NULL;
+ unsigned int buf_i;
+ int ret = chn->buf->i;
+
+ /* Save buffer state */
+ buf_i = chn->buf->i;
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->tcp_data && !flt_want_forward_data(filter, chn)) {
+ ret = filter->ops->tcp_data(s, filter, chn);
+ if (ret < 0)
+ break;
+ }
+ else
+ ret = chn->buf->i - FLT_NXT(filter, chn);
+
+ /* Increase next offset of the current filter */
+ FLT_NXT(filter, chn) += ret;
+
+ /* Update <ret> value to be sure to have the last one when we
+ * exit from the loop. */
+ ret = FLT_NXT(filter, chn);
+
+ /* And set this value as the bound for the next filter. It will
+ * not able to parse more data than the current one. */
+ chn->buf->i = FLT_NXT(filter, chn);
+ }
+ // Restore the original buffer state
+ chn->buf->i = buf_i;
+ return ret;
+}
+
+/*
+ * Calls 'tcp_forward_data' callback for all "data" filters attached to a
+ * stream. This function is called when some data can be forwarded. It takes
+ * care to update the forward offset of filters and adjusts "forwardable" data
+ * to be sure that a filter cannot forward more data than its predecessors. A
+ * filter can choose to not forward all parsed data. Returns a negative value if
+ * an error occurs, else the number of forwarded bytes.
+ */
+static int
+flt_forward_data(struct stream *s, struct channel *chn, unsigned int len)
+{
+ struct filter *filter = NULL;
+ int ret = len;
+
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ if (filter->ops->tcp_forward_data) {
+ /* Remove bytes that the current filter considered as
+ * forwarded */
+ ret = filter->ops->tcp_forward_data(s, filter, chn, ret - FLT_FWD(filter, chn));
+ if (ret < 0)
+ goto end;
+ }
+
+ /* Adjust bytes taht the current filter considers as
+ * forwarded */
+ FLT_FWD(filter, chn) += ret;
+
+ /* And set this value as the bound for the next filter. It will
+ * not able to forward more data than the current one. */
+ ret = FLT_FWD(filter, chn);
+ }
+
+ if (!ret)
+ goto end;
+
+ /* Adjust forward counter and next offset of filters by removing data
+ * that HAProxy will consider as forwarded. */
+ list_for_each_entry(filter, &s->strm_flt.filters, list) {
+ FLT_NXT(filter, chn) -= ret;
+ FLT_FWD(filter, chn) -= ret;
+ }
+
+ /* Consume data that all filters consider as forwarded. */
+ b_adv(chn->buf, ret);
+ end:
+ return ret;
+}
+
+/*
+ * Called when TCP data must be filtered on a channel. This function is the
+ * AN_FLT_XFER_DATA analyzer. When called, it is responsible to forward data
+ * when the proxy is not in http mode. Behind the scene, it calls consecutively
+ * 'tcp_data' and 'tcp_forward_data' callbacks for all "data" filters attached
+ * to a stream. Returns 0 if an error occurs or if it needs to wait, any other
+ * value otherwise.
+ */
+int
+flt_xfer_data(struct stream *s, struct channel *chn, unsigned int an_bit)
+{
+ int ret = 1;
+
+ /* If this function is called, this means there is at least one filter,
+ * so we do not need to check the filter list's emptiness. */
+
+ /* Be sure that the output is still opened. Else we stop the data
+ * filtering. */
+ if ((chn->flags & (CF_READ_ERROR|CF_READ_TIMEOUT|CF_WRITE_ERROR|CF_WRITE_TIMEOUT)) ||
+ ((chn->flags & CF_SHUTW) && (chn->to_forward || chn->buf->o)))
+ goto end;
+
+ /* Let all "data" filters parsing incoming data */
+ ret = flt_data(s, chn);
+ if (ret < 0)
+ goto end;
+
+ /* And forward them */
+ ret = flt_forward_data(s, chn, ret);
+ if (ret < 0)
+ goto end;
+
+ /* Stop waiting data if the input in closed and no data is pending or if
+ * the output is closed. */
+ if ((chn->flags & CF_SHUTW) ||
+ ((chn->flags & CF_SHUTR) && !buffer_pending(chn->buf))) {
+ ret = 1;
+ goto end;
+ }
+
+ /* Wait for data */
+ return 0;
+ end:
+ /* Terminate the data filtering. If <ret> is negative, an error was
+ * encountered during the filtering. */
+ return handle_analyzer_result(s, chn, an_bit, ret);
+}
+
+/*
+ * Handles result of filter's analyzers. It returns 0 if an error occurs or if
+ * it needs to wait, any other value otherwise.
+ */
+static int
+handle_analyzer_result(struct stream *s, struct channel *chn,
+ unsigned int an_bit, int ret)
+{
+ int finst;
+
+ if (ret < 0)
+ goto return_bad_req;
+ else if (!ret)
+ goto wait;
+
+ /* End of job, return OK */
+ if (an_bit) {
+ chn->analysers &= ~an_bit;
+ chn->analyse_exp = TICK_ETERNITY;
+ }
+ return 1;
+
+ return_bad_req:
+ /* An error occurs */
+ channel_abort(&s->req);
+ channel_abort(&s->res);
+
+ if (!(chn->flags & CF_ISRESP)) {
+ s->req.analysers &= AN_FLT_END;
+ finst = SF_FINST_R;
+ /* FIXME: incr counters */
+ }
+ else {
+ s->res.analysers &= AN_FLT_END;
+ finst = SF_FINST_H;
+ /* FIXME: incr counters */
+ }
+
+ if (s->txn) {
+ /* Do not do that when we are waiting for the next request */
+ if (s->txn->status)
+ http_reply_and_close(s, s->txn->status, NULL);
+ else {
+ s->txn->status = 400;
+ http_reply_and_close(s, 400, http_error_message(s, HTTP_ERR_400));
+ }
+ }
+
+ if (!(s->flags & SF_ERR_MASK))
+ s->flags |= SF_ERR_PRXCOND;
+ if (!(s->flags & SF_FINST_MASK))
+ s->flags |= finst;
+ return 0;
+
+ wait:
+ if (!(chn->flags & CF_ISRESP))
+ channel_dont_connect(chn);
+ return 0;
+}
+
+
+/* Note: must not be declared <const> as its list will be overwritten.
+ * Please take care of keeping this list alphabetically sorted, doing so helps
+ * all code contributors.
+ * Optional keywords are also declared with a NULL ->parse() function so that
+ * the config parser can report an appropriate error when a known keyword was
+ * not enabled. */
+static struct cfg_kw_list cfg_kws = {ILH, {
+ { CFG_LISTEN, "filter", parse_filter },
+ { 0, NULL, NULL },
+ }
+};
+
+__attribute__((constructor))
+static void
+__filters_init(void)
+{
+ pool2_filter = create_pool("filter", sizeof(struct filter), MEM_F_SHARED);
+ cfg_register_keywords(&cfg_kws);
+}
+
+__attribute__((destructor))
+static void
+__filters_deinit(void)
+{
+ pool_destroy2(pool2_filter);
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
#include <common/version.h>
#include <types/capture.h>
+#include <types/filters.h>
#include <types/global.h>
#include <types/acl.h>
#include <types/peers.h>
#include <proto/checks.h>
#include <proto/connection.h>
#include <proto/fd.h>
+#include <proto/filters.h>
#include <proto/hdr_idx.h>
#include <proto/hlua.h>
#include <proto/listener.h>
char *progname;
char *change_dir = NULL;
struct tm curtime;
+ struct proxy *px;
chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
alloc_trash_buffers(global.tune.bufsize);
init_51degrees();
#endif
+ for (px = proxy; px; px = px->next) {
+ err_code |= flt_init(px);
+ if (err_code & (ERR_ABORT|ERR_FATAL)) {
+ Alert("Failed to initialize filters for proxy '%s'.\n",
+ px->id);
+ exit(1);
+ }
+ }
+
if (start_checks() < 0)
exit(1);
free(bind_conf);
}
+ flt_deinit(p);
+
free(p->desc);
free(p->fwdfor_hdr_name);
pool_destroy2(pool2_sig_handlers);
pool_destroy2(pool2_hdr_idx);
pool_destroy2(pool2_http_txn);
-
deinit_pollers();
} /* end deinit() */
#include <common/version.h>
#include <types/capture.h>
+#include <types/filters.h>
#include <types/global.h>
#include <proto/acl.h>
#include <proto/compression.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
+#include <proto/filters.h>
#include <proto/frontend.h>
#include <proto/log.h>
#include <proto/hdr_idx.h>
static void http_server_error(struct stream *s, struct stream_interface *si,
int err, int finst, int status, const struct chunk *msg)
{
+ flt_http_reply(s, status, msg);
channel_auto_read(si_oc(si));
channel_abort(si_oc(si));
channel_auto_close(si_oc(si));
void
http_reply_and_close(struct stream *s, short status, struct chunk *msg)
{
+ s->txn->flags &= ~TX_WAIT_NEXT_RQ;
+ flt_http_reply(s, status, msg);
stream_int_retnclose(&s->si[0], msg);
}
msg->sol = ptr - ptr_old;
if (unlikely(ptr < ptr_old))
msg->sol += buf->size;
- msg->next = buffer_count(buf, buf->p, ptr);
msg->chunk_len = chunk;
msg->body_len += chunk;
- msg->msg_state = chunk ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
return 1;
error:
msg->err_pos = buffer_count(buf, buf->p, ptr);
/* we have msg->next which points to next line. Look for CRLF. */
while (1) {
const char *p1 = NULL, *p2 = NULL;
- const char *ptr = b_ptr(buf, msg->next);
+ const char *ptr = b_ptr(buf, msg->next + msg->sol);
const char *stop = bi_end(buf);
int bytes;
if (p2 >= buf->data + buf->size)
p2 = buf->data;
- bytes = p2 - b_ptr(buf, msg->next);
+ bytes = p2 - b_ptr(buf, msg->next + msg->sol);
if (bytes < 0)
bytes += buf->size;
- if (p1 == b_ptr(buf, msg->next)) {
- /* LF/CRLF at beginning of line => end of trailers at p2.
- * Everything was scheduled for forwarding, there's nothing
- * left from this message.
- */
- msg->next = buffer_count(buf, buf->p, p2);
- msg->msg_state = HTTP_MSG_DONE;
+ /* LF/CRLF at beginning of line => end of trailers at p2.
+ * Everything was scheduled for forwarding, there's nothing left
+ * from this message.
+ */
+ if (p1 == b_ptr(buf, msg->next + msg->sol)) {
+ msg->sol += bytes;
return 1;
}
+ msg->sol += bytes;
/* OK, next line then */
- msg->next = buffer_count(buf, buf->p, p2);
}
}
msg->err_pos = buffer_count(buf, buf->p, ptr);
return -1;
}
-
- ptr++;
- if (unlikely(ptr >= buf->data + buf->size))
- ptr = buf->data;
- /* Advance ->next to allow the CRLF to be forwarded */
- msg->next += bytes;
- msg->msg_state = HTTP_MSG_CHUNK_SIZE;
+ msg->sol = bytes;
return 1;
}
/* compress 200,201,202,203 responses only */
if ((txn->status != 200) &&
- (txn->status != 201) &&
- (txn->status != 202) &&
- (txn->status != 203))
+ (txn->status != 201) &&
+ (txn->status != 202) &&
+ (txn->status != 203))
goto fail;
+
/* Content-Length is null */
if (!(msg->flags & HTTP_MSGF_TE_CHNK) && msg->body_len == 0)
goto fail;
txn->status = 400;
msg->msg_state = HTTP_MSG_ERROR;
http_reply_and_close(s, txn->status, NULL);
- req->analysers = 0;
-
+ req->analysers &= AN_FLT_END;
stream_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(sess->fe);
sess->fe->fe_counters.failed_req++;
txn->status = 408;
msg->msg_state = HTTP_MSG_ERROR;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_408));
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
stream_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(sess->fe);
txn->status = 400;
msg->msg_state = HTTP_MSG_ERROR;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400));
- req->analysers = 0;
-
+ req->analysers &= AN_FLT_END;
stream_inc_http_err_ctr(s);
stream_inc_http_req_ctr(s);
proxy_inc_fe_req_ctr(sess->fe);
*/
txn->status = 0;
msg->msg_state = HTTP_MSG_RQBEFORE;
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
s->logs.logwait = 0;
s->logs.level = 0;
s->res.flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */
if (!(s->flags & SF_FINST_MASK))
s->flags |= SF_FINST_R;
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
req->analyse_exp = TICK_ETERNITY;
return 0;
}
}
memcpy(trash.str + trash.len, "\r\n\r\n", 4);
trash.len += 4;
+ flt_http_reply(s, txn->status, &trash);
bo_inject(res->chn, trash.str, trash.len);
/* "eat" the request */
bi_fast_delete(req->chn->buf, req->sov);
req->next -= req->sov;
req->sov = 0;
- s->req.analysers = AN_REQ_HTTP_XFER_BODY;
- s->res.analysers = AN_RES_HTTP_XFER_BODY;
+ s->req.analysers = AN_REQ_HTTP_XFER_BODY | (s->req.analysers & AN_FLT_END);
+ s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->req.analysers & AN_FLT_END);
req->msg_state = HTTP_MSG_CLOSED;
res->msg_state = HTTP_MSG_DONE;
/* Trim any possible response */
trash.len += 23;
}
http_reply_and_close(s, txn->status, &trash);
- req->chn->analysers = 0;
+ req->chn->analysers &= AN_FLT_END;
}
if (!(s->flags & SF_ERR_MASK))
select_compression_request_header(s, req->buf);
/* enable the minimally required analyzers to handle keep-alive and compression on the HTTP response */
- req->analysers = (req->analysers & AN_REQ_HTTP_BODY) | AN_REQ_HTTP_XFER_BODY;
+ req->analysers &= (AN_REQ_HTTP_BODY | AN_FLT_END);
+ req->analysers &= ~AN_FLT_XFER_DATA;
+ req->analysers |= AN_REQ_HTTP_XFER_BODY;
goto done;
}
* if the client closes first.
*/
channel_dont_connect(req);
- req->analysers = 0; /* remove switching rules etc... */
+ req->analysers &= AN_FLT_END; /* remove switching rules etc... */
req->analysers |= AN_REQ_HTTP_TARPIT;
req->analyse_exp = tick_add_ifset(now_ms, s->be->timeout.tarpit);
if (!req->analyse_exp)
if (!(s->flags & SF_FINST_MASK))
s->flags |= SF_FINST_R;
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
req->analyse_exp = TICK_ETERNITY;
return 0;
if (unlikely((conn = si_alloc_conn(&s->si[1])) == NULL)) {
txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 500;
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_500));
if (!(s->flags & SF_ERR_MASK))
}
if (msg->flags & HTTP_MSGF_XFER_LEN) {
+ req->analysers &= ~AN_FLT_XFER_DATA;
req->analysers |= AN_REQ_HTTP_XFER_BODY;
#ifdef TCP_QUICKACK
/* We expect some data from the client. Unless we know for sure
txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 400;
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400));
sess->fe->fe_counters.failed_req++;
if (!(req->flags & CF_READ_ERROR))
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_500));
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
req->analyse_exp = TICK_ETERNITY;
if (!(s->flags & SF_ERR_MASK))
stream_inc_http_err_ctr(s);
goto return_bad_req;
}
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = msg->chunk_len ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
}
/* Now we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state.
s->flags |= SF_FINST_R;
return_err_msg:
- req->analysers = 0;
+ req->analysers &= AN_FLT_END;
sess->fe->fe_counters.failed_req++;
if (sess->listener->counters)
sess->listener->counters->failed_req++;
/* The request was already skipped, let's restore it */
b_rew(chn->buf, old_o);
txn->req.next += old_o;
- txn->req.sov += old_o;
+ txn->req.sov += old_o;
}
old_i = chn->buf->i;
si_idle_conn(&s->si[1], &srv->idle_conns);
}
- s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
- s->res.analysers = 0;
+ if (LIST_ISEMPTY(&s->strm_flt.filters)) {
+ s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
+ s->res.analysers = 0;
+ }
+ else {
+ s->req.analysers &= AN_FLT_END;
+ s->res.analysers &= AN_FLT_END;
+ }
}
txn->rsp.msg_state == HTTP_MSG_TUNNEL ||
(txn->req.msg_state == HTTP_MSG_CLOSED &&
txn->rsp.msg_state == HTTP_MSG_CLOSED)) {
- s->req.analysers = 0;
+ s->req.analysers &= AN_FLT_END;
channel_auto_close(&s->req);
channel_auto_read(&s->req);
- s->res.analysers = 0;
+ s->res.analysers &= AN_FLT_END;
channel_auto_close(&s->res);
channel_auto_read(&s->res);
}
(txn->rsp.msg_state == HTTP_MSG_CLOSED || (s->res.flags & CF_SHUTW))) ||
txn->rsp.msg_state == HTTP_MSG_ERROR ||
txn->req.msg_state == HTTP_MSG_ERROR) {
- s->res.analysers = 0;
+ s->res.analysers &= AN_FLT_END;
channel_auto_close(&s->res);
channel_auto_read(&s->res);
- s->req.analysers = 0;
+ s->req.analysers &= AN_FLT_END;
channel_abort(&s->req);
channel_auto_close(&s->req);
channel_auto_read(&s->req);
struct session *sess = s->sess;
struct http_txn *txn = s->txn;
struct http_msg *msg = &s->txn->req;
+ int ret, ret2;
if (unlikely(msg->msg_state < HTTP_MSG_BODY))
return 0;
* an "Expect: 100-continue" header.
*/
- if (msg->sov > 0) {
+ if (msg->msg_state == HTTP_MSG_BODY) {
/* we have msg->sov which points to the first byte of message
* body, and req->buf.p still points to the beginning of the
* message. We forward the headers now, as we don't need them
* anymore, and we want to flush them.
*/
- b_adv(req->buf, msg->sov);
- msg->next -= msg->sov;
- msg->sov = 0;
+ ret = flt_http_headers(s, msg);
+ if (ret < 0)
+ goto return_bad_req;
+ if (!ret)
+ return 0;
+
+ ret = flt_http_forward_data(s, msg, msg->sov);
+ if (ret < 0)
+ goto return_bad_req;
+ b_adv(req->buf, ret);
+ msg->next -= ret;
+ msg->sov -= ret;
/* The previous analysers guarantee that the state is somewhere
* between MSG_BODY and the first MSG_DATA. So msg->sol and
else
msg->msg_state = HTTP_MSG_DATA;
}
+
+ /* TODO/filters: when http-buffer-request option is set or if a
+ * rule on url_param exists, the first chunk size could be
+ * already parsed. In that case, msg->next is after the chunk
+ * size (including the CRLF after the size). So this case should
+ * be handled to */
}
/* Some post-connect processing might want us to refrain from starting to
if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */
/* we may have some pending data starting at req->buf->p */
- if (msg->chunk_len > req->buf->i - msg->next) {
- req->flags |= CF_WAKE_WRITE;
+ ret = flt_http_data(s, msg);
+ if (ret < 0)
+ goto aborted_xfer;
+ msg->next += ret;
+ msg->chunk_len -= ret;
+
+ if (msg->chunk_len) {
+ /* input empty or output full */
+ if (req->buf->i > msg->next)
+ req->flags |= CF_WAKE_WRITE;
goto missing_data;
}
- msg->next += msg->chunk_len;
- msg->chunk_len = 0;
/* nothing left to forward */
if (msg->flags & HTTP_MSGF_TE_CHNK)
msg->msg_state = HTTP_MSG_CHUNK_CRLF;
else
- msg->msg_state = HTTP_MSG_DONE;
+ msg->msg_state = HTTP_MSG_ENDING;
}
else if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->chunk_len, then
* set ->next to point to the body and switch to DATA or
* TRAILERS state.
*/
- int ret = http_parse_chunk_size(msg);
-
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_SIZE, s->be);
- goto return_bad_req;
+ if (!msg->sol) {
+ ret = http_parse_chunk_size(msg);
+ if (ret == 0)
+ goto missing_data;
+ else if (ret < 0) {
+ stream_inc_http_err_ctr(s);
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_SIZE, s->be);
+ goto return_bad_req;
+ }
}
+ ret = (msg->chunk_len
+ ? flt_http_start_chunk(s, msg)
+ : flt_http_last_chunk(s, msg));
+ if (ret < 0)
+ goto return_bad_req;
+ if (!ret)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = msg->chunk_len ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
}
else if (msg->msg_state == HTTP_MSG_CHUNK_CRLF) {
/* we want the CRLF after the data */
- int ret = http_skip_chunk_crlf(msg);
-
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_CRLF, s->be);
- goto return_bad_req;
+ if (!msg->sol) {
+ ret = http_skip_chunk_crlf(msg);
+ if (ret == 0)
+ goto missing_data;
+ else if (ret < 0) {
+ stream_inc_http_err_ctr(s);
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_CHUNK_CRLF, s->be);
+ goto return_bad_req;
+ }
}
+ ret = flt_http_end_chunk(s, msg);
+ if (ret < 0)
+ goto return_bad_req;
+ if (!ret)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = HTTP_MSG_CHUNK_SIZE;
/* we're in MSG_CHUNK_SIZE now */
}
else if (msg->msg_state == HTTP_MSG_TRAILERS) {
- int ret = http_forward_trailers(msg);
-
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- stream_inc_http_err_ctr(s);
- if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_TRAILERS, s->be);
- goto return_bad_req;
+ ret = 1;
+ if (!msg->sol) {
+ ret = http_forward_trailers(msg);
+ if (ret < 0) {
+ stream_inc_http_err_ctr(s);
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&sess->fe->invalid_req, s, msg, HTTP_MSG_TRAILERS, s->be);
+ goto return_bad_req;
+ }
}
- /* we're in HTTP_MSG_DONE now */
+ ret2 = flt_http_chunk_trailers(s, msg);
+ if (ret2 < 0)
+ goto return_bad_req;
+ if (!ret2)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ if (!ret)
+ goto missing_data;
+ msg->msg_state = HTTP_MSG_ENDING;
}
- else {
- int old_state = msg->msg_state;
-
- /* other states, DONE...TUNNEL */
-
+ else if (msg->msg_state == HTTP_MSG_ENDING) {
+ /* we don't want to forward closes on DONE except in
+ * tunnel mode.
+ */
+ if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
+ channel_dont_close(req);
/* we may have some pending data starting at req->buf->p
* such as last chunk of data or trailers.
*/
- b_adv(req->buf, msg->next);
- if (unlikely(!(s->req.flags & CF_WROTE_DATA)))
- msg->sov -= msg->next;
- msg->next = 0;
+ ret = flt_http_forward_data(s, msg, msg->next);
+ if (ret < 0)
+ goto return_bad_req;
+ b_adv(req->buf, ret);
+ msg->next -= ret;
+ if (unlikely(!(s->req.flags & CF_WROTE_DATA) || msg->sov > 0))
+ msg->sov -= ret;
+
+ if (msg->next)
+ goto skip_resync_states;
+ ret = flt_http_end(s, msg);
+ if (ret < 0)
+ goto return_bad_req;
+ if (!ret)
+ goto skip_resync_states;
+ msg->msg_state = HTTP_MSG_DONE;
+ }
+ else {
+ /* other states, DONE...TUNNEL */
/* we don't want to forward closes on DONE except in
* tunnel mode.
*/
if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
channel_dont_close(req);
+
+ ret = msg->msg_state;
if (http_resync_states(s)) {
/* some state changes occurred, maybe the analyser
* was disabled too.
goto aborted_xfer;
}
if (msg->err_pos >= 0)
- http_capture_bad_message(&sess->fe->invalid_req, s, msg, old_state, s->be);
+ http_capture_bad_message(&sess->fe->invalid_req, s, msg, ret, s->be);
goto return_bad_req;
}
return 1;
}
+ skip_resync_states:
/* If "option abortonclose" is set on the backend, we
* want to monitor the client's connection and forward
* any shutdown notification to the server, which will
missing_data:
/* we may have some pending data starting at req->buf->p */
- b_adv(req->buf, msg->next);
- if (unlikely(!(s->req.flags & CF_WROTE_DATA)))
- msg->sov -= msg->next + MIN(msg->chunk_len, req->buf->i);
-
- msg->next = 0;
- msg->chunk_len -= channel_forward(req, msg->chunk_len);
+ ret = flt_http_forward_data(s, msg, msg->next);
+ if (ret < 0)
+ goto return_bad_req;
+ b_adv(req->buf, ret);
+ msg->next -= ret;
+ if (unlikely(!(s->req.flags & CF_WROTE_DATA) || msg->sov > 0))
+ msg->sov -= ret;
+ if (LIST_ISEMPTY(&s->strm_flt.filters))
+ msg->chunk_len -= channel_forward(req, msg->chunk_len);
/* stop waiting for data if the input is closed before the end */
if (req->flags & CF_SHUTR) {
sess->listener->counters->failed_req++;
return_bad_req_stats_ok:
- /* we may have some pending data starting at req->buf->p */
- b_adv(req->buf, msg->next);
- msg->next = 0;
-
txn->req.msg_state = HTTP_MSG_ERROR;
if (txn->status) {
/* Note: we don't send any error if some data were already sent */
txn->status = 400;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_400));
}
- req->analysers = 0;
- s->res.analysers = 0; /* we're in data phase, we want to abort both directions */
+ req->analysers &= AN_FLT_END;
+ s->res.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND;
txn->status = 502;
http_reply_and_close(s, txn->status, http_error_message(s, HTTP_ERR_502));
}
- req->analysers = 0;
- s->res.analysers = 0; /* we're in data phase, we want to abort both directions */
+ req->analysers &= AN_FLT_END;
+ s->res.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */
sess->fe->fe_counters.srv_aborts++;
s->be->be_counters.srv_aborts++;
}
abort_response:
channel_auto_close(rep);
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
txn->status = 502;
s->si[1].flags |= SI_FL_NOLINGER;
channel_truncate(rep);
}
channel_auto_close(rep);
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
txn->status = 502;
s->si[1].flags |= SI_FL_NOLINGER;
channel_truncate(rep);
}
channel_auto_close(rep);
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
txn->status = 504;
s->si[1].flags |= SI_FL_NOLINGER;
channel_truncate(rep);
if (objt_server(s->target))
objt_server(s->target)->counters.cli_aborts++;
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
channel_auto_close(rep);
txn->status = 400;
}
channel_auto_close(rep);
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
txn->status = 502;
s->si[1].flags |= SI_FL_NOLINGER;
channel_truncate(rep);
goto abort_keep_alive;
s->be->be_counters.failed_resp++;
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
channel_auto_close(rep);
if (!(s->flags & SF_ERR_MASK))
msg->msg_state = HTTP_MSG_RPBEFORE;
txn->status = 0;
s->logs.t_data = -1; /* was not a response yet */
+ flt_http_reset(s, msg);
goto next_one;
case 200:
* any other information so that the client retries.
*/
txn->status = 0;
- rep->analysers = 0;
- s->req.analysers = 0;
+ rep->analysers &= AN_FLT_END;
+ s->req.analysers &= AN_FLT_END;
channel_auto_close(rep);
s->logs.logwait = 0;
s->logs.level = 0;
}
s->be->be_counters.failed_resp++;
return_srv_prx_502:
- rep->analysers = 0;
+ rep->analysers &= AN_FLT_END;
txn->status = 502;
s->logs.t_data = -1; /* was not a valid response */
s->si[1].flags |= SI_FL_NOLINGER;
skip_header_mangling:
if ((msg->flags & HTTP_MSGF_XFER_LEN) ||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN)
+ !LIST_ISEMPTY(&s->strm_flt.filters) ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN) {
+ rep->analysers &= ~AN_FLT_XFER_DATA;
rep->analysers |= AN_RES_HTTP_XFER_BODY;
+ }
/* if the user wants to log as soon as possible, without counting
* bytes from the server, then this is the right moment. We have
struct session *sess = s->sess;
struct http_txn *txn = s->txn;
struct http_msg *msg = &s->txn->rsp;
- static struct buffer *tmpbuf = &buf_empty;
- int compressing = 0;
- int ret;
+ int ret, ret2;
if (unlikely(msg->msg_state < HTTP_MSG_BODY))
return 0;
if ((res->flags & (CF_READ_ERROR|CF_READ_TIMEOUT|CF_WRITE_ERROR|CF_WRITE_TIMEOUT)) ||
((res->flags & CF_SHUTW) && (res->to_forward || res->buf->o)) ||
- !s->req.analysers) {
+ !s->req.analysers) {
/* Output closed while we were sending data. We must abort and
* wake the other side up.
*/
/* in most states, we should abort in case of early close */
channel_auto_close(res);
- if (msg->sov > 0) {
+ if (msg->msg_state == HTTP_MSG_BODY) {
/* we have msg->sov which points to the first byte of message
* body, and res->buf.p still points to the beginning of the
* message. We forward the headers now, as we don't need them
* anymore, and we want to flush them.
*/
- b_adv(res->buf, msg->sov);
- msg->next -= msg->sov;
- msg->sov = 0;
+ ret = flt_http_headers(s, msg);
+ if (ret < 0)
+ goto return_bad_res;
+ if (!ret)
+ return 0;
+
+ ret = flt_http_forward_data(s, msg, msg->sov);
+ if (ret < 0)
+ goto return_bad_res;
+ b_adv(res->buf, ret);
+ msg->next -= ret;
+ msg->sov -= ret;
/* The previous analysers guarantee that the state is somewhere
* between MSG_BODY and the first MSG_DATA. So msg->sol and
goto missing_data;
}
- if (unlikely(s->comp_algo != NULL) && msg->msg_state < HTTP_MSG_TRAILERS) {
- /* We need a compression buffer in the DATA state to put the
- * output of compressed data, and in CRLF state to let the
- * TRAILERS state finish the job of removing the trailing CRLF.
- */
- if (unlikely(!tmpbuf->size)) {
- /* this is the first time we need the compression buffer */
- if (b_alloc(&tmpbuf) == NULL)
- goto aborted_xfer; /* no memory */
- }
-
- ret = http_compression_buffer_init(s, res->buf, tmpbuf);
- if (ret < 0) {
- res->flags |= CF_WAKE_WRITE;
- goto missing_data; /* not enough spaces in buffers */
- }
- compressing = 1;
- }
-
while (1) {
switch (msg->msg_state - HTTP_MSG_DATA) {
case HTTP_MSG_DATA - HTTP_MSG_DATA: /* must still forward */
/* we may have some pending data starting at res->buf->p */
- if (unlikely(s->comp_algo)) {
- ret = http_compression_buffer_add_data(s, res->buf, tmpbuf);
- if (ret < 0)
- goto aborted_xfer;
-
- if (msg->chunk_len) {
- /* input empty or output full */
- if (res->buf->i > msg->next)
- res->flags |= CF_WAKE_WRITE;
- goto missing_data;
- }
- }
- else {
- if (msg->chunk_len > res->buf->i - msg->next) {
- /* output full */
+
+ /* Neither content-length, nor transfer-encoding was
+ * found, so we must read the body until the server
+ * connection is closed. In that case, we eat data as
+ * they come. */
+ if (!(msg->flags & HTTP_MSGF_XFER_LEN))
+ msg->chunk_len = (res->buf->i - msg->next);
+ ret = flt_http_data(s, msg);
+ if (ret < 0)
+ goto aborted_xfer;
+ msg->next += ret;
+ msg->chunk_len -= ret;
+ if (msg->chunk_len) {
+ /* input empty or output full */
+ if (res->buf->i > msg->next)
res->flags |= CF_WAKE_WRITE;
- goto missing_data;
- }
- msg->next += msg->chunk_len;
- msg->chunk_len = 0;
+ goto missing_data;
}
/* nothing left to forward */
if (msg->flags & HTTP_MSGF_TE_CHNK) {
msg->msg_state = HTTP_MSG_CHUNK_CRLF;
+ } else if (!(msg->flags & HTTP_MSGF_XFER_LEN) &&
+ !(res->flags & CF_SHUTR)) {
+ /* The server still sending data */
+ goto missing_data;
} else {
- msg->msg_state = HTTP_MSG_DONE;
+ msg->msg_state = HTTP_MSG_ENDING;
break;
}
/* fall through for HTTP_MSG_CHUNK_CRLF */
case HTTP_MSG_CHUNK_CRLF - HTTP_MSG_DATA:
/* we want the CRLF after the data */
-
- ret = http_skip_chunk_crlf(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_CRLF, sess->fe);
- goto return_bad_res;
+ if (!msg->sol) {
+ ret = http_skip_chunk_crlf(msg);
+ if (ret == 0)
+ goto missing_data;
+ else if (ret < 0) {
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_CRLF, sess->fe);
+ goto return_bad_res;
+ }
}
+ ret = flt_http_end_chunk(s, msg);
+ if (ret < 0)
+ goto return_bad_res;
+ if (!ret)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = HTTP_MSG_CHUNK_SIZE;
/* we're in MSG_CHUNK_SIZE now, fall through */
case HTTP_MSG_CHUNK_SIZE - HTTP_MSG_DATA:
* set ->next to point to the body and switch to DATA or
* TRAILERS state.
*/
-
- ret = http_parse_chunk_size(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_SIZE, sess->fe);
- goto return_bad_res;
+ if (!msg->sol) {
+ ret = http_parse_chunk_size(msg);
+ if (ret == 0)
+ goto missing_data;
+ else if (ret < 0) {
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_SIZE, sess->fe);
+ goto return_bad_res;
+ }
}
+ ret = (msg->chunk_len
+ ? flt_http_start_chunk(s, msg)
+ : flt_http_last_chunk(s, msg));
+ if (ret < 0)
+ goto return_bad_res;
+ if (!ret)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ msg->msg_state = msg->chunk_len ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
break;
case HTTP_MSG_TRAILERS - HTTP_MSG_DATA:
- if (unlikely(compressing)) {
- /* we need to flush output contents before syncing FSMs */
- http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
- compressing = 0;
+ ret = 1;
+ if (!msg->sol) {
+ ret = http_forward_trailers(msg);
+ if (ret < 0) {
+ if (msg->err_pos >= 0)
+ http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_TRAILERS, sess->fe);
+ goto return_bad_res;
+ }
}
-
- ret = http_forward_trailers(msg);
- if (ret == 0)
- goto missing_data;
- else if (ret < 0) {
- if (msg->err_pos >= 0)
- http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_TRAILERS, sess->fe);
+ ret2 = flt_http_chunk_trailers(s, msg);
+ if (ret2 < 0)
goto return_bad_res;
- }
- /* we're in HTTP_MSG_DONE now, fall through */
+ if (!ret2)
+ goto missing_data;
+ msg->next += msg->sol;
+ msg->sol = 0;
+ if (!ret)
+ goto missing_data;
+ msg->msg_state = HTTP_MSG_ENDING;
+ /* fall through */
- default:
- /* other states, DONE...TUNNEL */
- if (unlikely(compressing)) {
- /* we need to flush output contents before syncing FSMs */
- http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
- compressing = 0;
- }
+ case HTTP_MSG_ENDING - HTTP_MSG_DATA:
+ /* for keep-alive we don't want to forward closes on ENDING */
+ if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
+ (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
+ channel_dont_close(res);
/* we may have some pending data starting at res->buf->p
* such as a last chunk of data or trailers.
*/
- b_adv(res->buf, msg->next);
- msg->next = 0;
+ ret = flt_http_forward_data(s, msg, msg->next);
+ if (ret < 0)
+ goto return_bad_res;
+ b_adv(res->buf, ret);
+ msg->next -= ret;
+ if (msg->sov > 0)
+ msg->sov -= ret;
+
+ if (msg->next)
+ goto skip_resync_states;
+
+ ret = flt_http_end(s, msg);
+ if (ret < 0)
+ goto return_bad_res;
+ if (!ret)
+ goto skip_resync_states;
+ msg->msg_state = HTTP_MSG_DONE;
+ /* fall through */
+
+ default:
+ /* other states, DONE...TUNNEL */
- ret = msg->msg_state;
/* for keep-alive we don't want to forward closes on DONE */
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
channel_dont_close(res);
+ ret = msg->msg_state;
if (http_resync_states(s)) {
/* some state changes occurred, maybe the analyser
* was disabled too.
}
return 1;
}
+
+ skip_resync_states:
return 0;
}
}
missing_data:
/* we may have some pending data starting at res->buf->p */
- if (unlikely(compressing)) {
- http_compression_buffer_end(s, &res->buf, &tmpbuf, msg->msg_state >= HTTP_MSG_TRAILERS);
- compressing = 0;
- }
+ ret = flt_http_forward_data(s, msg, msg->next);
+ if (ret < 0)
+ goto return_bad_res;
+ b_adv(res->buf, ret);
+ msg->next -= ret;
+ if (msg->sov > 0)
+ msg->sov -= ret;
- if ((s->comp_algo == NULL || msg->msg_state >= HTTP_MSG_TRAILERS)) {
- b_adv(res->buf, msg->next);
- msg->next = 0;
+ if ((s->comp_algo == NULL || msg->msg_state >= HTTP_MSG_TRAILERS) &&
+ LIST_ISEMPTY(&s->strm_flt.filters))
msg->chunk_len -= channel_forward(res, msg->chunk_len);
- }
if (res->flags & CF_SHUTW)
goto aborted_xfer;
* Similarly, with keep-alive on the client side, we don't want to forward a
* close.
*/
- if ((msg->flags & HTTP_MSGF_TE_CHNK) || s->comp_algo ||
+ if ((msg->flags & HTTP_MSGF_TE_CHNK) || s->comp_algo || !msg->body_len ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
(txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
channel_dont_close(res);
objt_server(s->target)->counters.failed_resp++;
return_bad_res_stats_ok:
- if (unlikely(compressing)) {
- http_compression_buffer_end(s, &res->buf, &tmpbuf, msg->msg_state >= HTTP_MSG_TRAILERS);
- compressing = 0;
- }
-
- /* we may have some pending data starting at res->buf->p */
- if (s->comp_algo == NULL) {
- b_adv(res->buf, msg->next);
- msg->next = 0;
- }
-
txn->rsp.msg_state = HTTP_MSG_ERROR;
/* don't send any error message as we're in the body */
http_reply_and_close(s, txn->status, NULL);
- res->analysers = 0;
- s->req.analysers = 0; /* we're in data phase, we want to abort both directions */
+ res->analysers &= AN_FLT_END;
+ s->req.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */
if (objt_server(s->target))
health_adjust(objt_server(s->target), HANA_STATUS_HTTP_HDRRSP);
return 0;
aborted_xfer:
- if (unlikely(compressing)) {
- http_compression_buffer_end(s, &res->buf, &tmpbuf, msg->msg_state >= HTTP_MSG_TRAILERS);
- compressing = 0;
- }
-
txn->rsp.msg_state = HTTP_MSG_ERROR;
/* don't send any error message as we're in the body */
http_reply_and_close(s, txn->status, NULL);
- res->analysers = 0;
- s->req.analysers = 0; /* we're in data phase, we want to abort both directions */
+ res->analysers &= AN_FLT_END;
+ s->req.analysers &= AN_FLT_END; /* we're in data phase, we want to abort both directions */
sess->fe->fe_counters.cli_aborts++;
s->be->be_counters.cli_aborts++;
#include <proto/backend.h>
#include <proto/fd.h>
+#include <proto/filters.h>
#include <proto/hdr_idx.h>
#include <proto/listener.h>
#include <proto/log.h>
LIST_INIT(&p->conf.listeners);
LIST_INIT(&p->conf.args.list);
LIST_INIT(&p->tcpcheck_rules);
+ LIST_INIT(&p->filters);
/* Timeouts are defined as -1 */
proxy_reset_timeouts(p);
*/
int stream_set_backend(struct stream *s, struct proxy *be)
{
+ struct filter *filter;
+
if (s->flags & SF_BE_ASSIGNED)
return 1;
s->be = be;
be->be_counters.conn_max = be->beconn;
proxy_inc_be_ctr(be);
+ if (strm_fe(s) != be) {
+ list_for_each_entry(filter, &be->filters, list) {
+ struct filter *f = pool_alloc2(pool2_filter);
+ if (!f)
+ return 0; /* not enough memory */
+ memset(f, 0, sizeof(*f));
+ f->id = filter->id;
+ f->ops = filter->ops;
+ f->conf = filter->conf;
+ f->is_backend_filter = 1;
+ LIST_ADDQ(&s->strm_flt.filters, &f->list);
+ }
+ }
+
/* assign new parameters to the stream from the new backend */
s->si[1].flags &= ~SI_FL_INDEP_STR;
if (be->options2 & PR_O2_INDEPSTR)
* be more reliable to store the list of analysers that have been run,
* but what we do here is OK for now.
*/
- s->req.analysers |= be->be_req_ana;
- if (strm_li(s))
- s->req.analysers &= ~strm_li(s)->analysers;
+ s->req.analysers |= be->be_req_ana & (strm_li(s) ? ~strm_li(s)->analysers : 0);
/* If the target backend requires HTTP processing, we have to allocate
* the HTTP transaction and hdr_idx if we did not have one.
if (!strm)
goto out_free_task;
- strm->target = sess->listener->default_target;
- strm->req.analysers = sess->listener->analysers;
+ strm->target = sess->listener->default_target;
+ strm->req.analysers |= sess->listener->analysers;
+
return 1;
out_free_task:
if (!strm)
goto fail;
- strm->target = sess->listener->default_target;
- strm->req.analysers = sess->listener->analysers;
+ strm->target = sess->listener->default_target;
+ strm->req.analysers |= sess->listener->analysers;
conn->flags &= ~CO_FL_INIT_DATA;
return 0;
#include <types/applet.h>
#include <types/capture.h>
+#include <types/filters.h>
#include <types/global.h>
#include <proto/acl.h>
#include <proto/connection.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
+#include <proto/filters.h>
#include <proto/freq_ctr.h>
#include <proto/frontend.h>
#include <proto/hdr_idx.h>
struct stream *stream_new(struct session *sess, struct task *t, enum obj_type *origin)
{
struct stream *s;
+ struct filter *filter, *back;
struct connection *conn = objt_conn(origin);
struct appctx *appctx = objt_appctx(origin);
HLUA_INIT(&s->hlua);
+ LIST_INIT(&s->strm_flt.filters);
+ memset(s->strm_flt.current, 0, sizeof(s->strm_flt.current));
+ list_for_each_entry(filter, &sess->fe->filters, list) {
+ struct filter *f = pool_alloc2(pool2_filter);
+ if (!f)
+ goto out_fail_accept;
+ memset(f, 0, sizeof(*f));
+ f->id = filter->id;
+ f->ops = filter->ops;
+ f->conf = filter->conf;
+ LIST_ADDQ(&s->strm_flt.filters, &f->list);
+ }
+ if (flt_stream_start(s) < 0)
+ goto out_fail_accept;
+
/* finish initialization of the accepted file descriptor */
if (conn)
conn_data_want_recv(conn);
/* Error unrolling */
out_fail_accept:
+ list_for_each_entry_safe(filter, back, &s->strm_flt.filters, list) {
+ LIST_DEL(&filter->list);
+ pool_free2(pool2_filter, filter);
+ }
LIST_DEL(&s->list);
pool_free2(pool2_stream, s);
return NULL;
struct proxy *fe = sess->fe;
struct bref *bref, *back;
struct connection *cli_conn = objt_conn(sess->origin);
+ struct filter *filter, *fback;
int i;
if (s->pend_pos)
s->txn = NULL;
}
+ flt_stream_stop(s);
+ list_for_each_entry_safe(filter, fback, &s->strm_flt.filters, list) {
+ LIST_DEL(&filter->list);
+ pool_free2(pool2_filter, filter);
+ }
+
if (fe) {
pool_free2(fe->rsp_cap_pool, s->res_cap);
pool_free2(fe->req_cap_pool, s->req_cap);
if (fe == s->be) {
s->req.analysers &= ~AN_REQ_INSPECT_BE;
s->req.analysers &= ~AN_REQ_HTTP_PROCESS_BE;
+ s->req.analysers &= ~AN_FLT_START_BE;
}
/* as soon as we know the backend, we must check if we have a matching forced or ignored
if (s->txn)
s->txn->status = 500;
- s->req.analysers = 0;
+ s->req.analysers &= AN_FLT_END;
s->req.analyse_exp = TICK_ETERNITY;
return 0;
}
ana_list = ana_back = req->analysers;
while (ana_list && max_loops--) {
/* Warning! ensure that analysers are always placed in ascending order! */
+ if (ana_list & AN_FLT_START_FE) {
+ if (!flt_start_analyze(s, req, AN_FLT_START_FE))
+ break;
+ UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_FLT_START_FE);
+ }
if (ana_list & AN_REQ_INSPECT_FE) {
+ if (!flt_analyze(s, req, AN_REQ_INSPECT_FE))
+ break;
if (!tcp_inspect_request(s, req, AN_REQ_INSPECT_FE))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_INSPECT_FE);
}
if (ana_list & AN_REQ_WAIT_HTTP) {
+ if (!flt_analyze(s, req, AN_REQ_WAIT_HTTP))
+ break;
if (!http_wait_for_request(s, req, AN_REQ_WAIT_HTTP))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_WAIT_HTTP);
}
if (ana_list & AN_REQ_HTTP_BODY) {
+ if (!flt_analyze(s, req, AN_REQ_HTTP_BODY))
+ break;
if (!http_wait_for_request_body(s, req, AN_REQ_HTTP_BODY))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_BODY);
}
if (ana_list & AN_REQ_HTTP_PROCESS_FE) {
+ if (!flt_analyze(s, req, AN_REQ_HTTP_PROCESS_FE))
+ break;
if (!http_process_req_common(s, req, AN_REQ_HTTP_PROCESS_FE, sess->fe))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_PROCESS_FE);
}
if (ana_list & AN_REQ_SWITCHING_RULES) {
+ if (!flt_analyze(s, req, AN_REQ_SWITCHING_RULES))
+ break;
if (!process_switching_rules(s, req, AN_REQ_SWITCHING_RULES))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_SWITCHING_RULES);
}
if (ana_list & AN_REQ_INSPECT_BE) {
+ if (!flt_analyze(s, req, AN_REQ_INSPECT_BE))
+ break;
if (!tcp_inspect_request(s, req, AN_REQ_INSPECT_BE))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_INSPECT_BE);
}
if (ana_list & AN_REQ_HTTP_PROCESS_BE) {
+ if (!flt_analyze(s, req, AN_REQ_HTTP_PROCESS_BE))
+ break;
if (!http_process_req_common(s, req, AN_REQ_HTTP_PROCESS_BE, s->be))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_PROCESS_BE);
}
if (ana_list & AN_REQ_HTTP_TARPIT) {
+ if (!flt_analyze(s, req, AN_REQ_HTTP_TARPIT))
+ break;
if (!http_process_tarpit(s, req, AN_REQ_HTTP_TARPIT))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_TARPIT);
}
if (ana_list & AN_REQ_SRV_RULES) {
+ if (!flt_analyze(s, req, AN_REQ_SRV_RULES))
+ break;
if (!process_server_rules(s, req, AN_REQ_SRV_RULES))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_SRV_RULES);
}
if (ana_list & AN_REQ_HTTP_INNER) {
+ if (!flt_analyze(s, req, AN_REQ_HTTP_INNER))
+ break;
if (!http_process_request(s, req, AN_REQ_HTTP_INNER))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_INNER);
}
if (ana_list & AN_REQ_PRST_RDP_COOKIE) {
+ if (!flt_analyze(s, req, AN_REQ_PRST_RDP_COOKIE))
+ break;
if (!tcp_persist_rdp_cookie(s, req, AN_REQ_PRST_RDP_COOKIE))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE);
}
if (ana_list & AN_REQ_STICKING_RULES) {
+ if (!flt_analyze(s, req, AN_REQ_STICKING_RULES))
+ break;
if (!process_sticking_rules(s, req, AN_REQ_STICKING_RULES))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_STICKING_RULES);
}
+ if (ana_list & AN_FLT_START_BE) {
+ if (!flt_start_analyze(s, req, AN_FLT_START_BE))
+ break;
+ UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_FLT_START_BE);
+ }
+
+ if (ana_list & AN_FLT_XFER_DATA) {
+ if (!flt_xfer_data(s, req, AN_FLT_XFER_DATA))
+ break;
+ UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_FLT_XFER_DATA);
+ }
+
if (ana_list & AN_REQ_HTTP_XFER_BODY) {
if (!http_request_forward_body(s, req, AN_REQ_HTTP_XFER_BODY))
break;
UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_REQ_HTTP_XFER_BODY);
}
+
+ if (ana_list & AN_FLT_END) {
+ if (!flt_end_analyze(s, req, AN_FLT_END))
+ break;
+ UPDATE_ANALYSERS(req->analysers, ana_list, ana_back, AN_FLT_END);
+ }
break;
}
}
ana_list = ana_back = res->analysers;
while (ana_list && max_loops--) {
/* Warning! ensure that analysers are always placed in ascending order! */
+ if (ana_list & AN_FLT_START_FE) {
+ if (!flt_start_analyze(s, res, AN_FLT_START_FE))
+ break;
+ UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_FLT_START_FE);
+ }
if (ana_list & AN_RES_INSPECT) {
+ if (!flt_analyze(s, res, AN_RES_INSPECT))
+ break;
if (!tcp_inspect_response(s, res, AN_RES_INSPECT))
break;
UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_RES_INSPECT);
}
if (ana_list & AN_RES_WAIT_HTTP) {
+ if (!flt_analyze(s, res, AN_RES_WAIT_HTTP))
+ break;
if (!http_wait_for_response(s, res, AN_RES_WAIT_HTTP))
break;
UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_RES_WAIT_HTTP);
}
if (ana_list & AN_RES_STORE_RULES) {
+ if (!flt_analyze(s, res, AN_RES_STORE_RULES))
+ break;
if (!process_store_rules(s, res, AN_RES_STORE_RULES))
break;
UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_RES_STORE_RULES);
}
if (ana_list & AN_RES_HTTP_PROCESS_BE) {
+ if (!flt_analyze(s, res, AN_RES_HTTP_PROCESS_BE))
+ break;
if (!http_process_res_common(s, res, AN_RES_HTTP_PROCESS_BE, s->be))
break;
UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_RES_HTTP_PROCESS_BE);
}
+ if (ana_list & AN_FLT_START_BE) {
+ if (!flt_start_analyze(s, res, AN_FLT_START_BE))
+ break;
+ UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_FLT_START_BE);
+ }
+
+ if (ana_list & AN_FLT_XFER_DATA) {
+ if (!flt_xfer_data(s, res, AN_FLT_XFER_DATA))
+ break;
+ UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_FLT_XFER_DATA);
+ }
+
if (ana_list & AN_RES_HTTP_XFER_BODY) {
if (!http_response_forward_body(s, res, AN_RES_HTTP_XFER_BODY))
break;
UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_RES_HTTP_XFER_BODY);
}
+
+ if (ana_list & AN_FLT_END) {
+ if (!flt_end_analyze(s, res, AN_FLT_END))
+ break;
+ UPDATE_ANALYSERS(res->analysers, ana_list, ana_back, AN_FLT_END);
+ }
break;
}
}