/***
 * Copyright 2020 HAProxy Technologies
 *
 * This file is part of the HAProxy OpenTracing filter.
 *
 * 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.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include "include.h"


/*
 * OpenTracing filter id, used to identify OpenTracing filters.
 * The name of this variable is consistent with the other filter names
 * declared in include/haproxy/filters.h .
 */
const char *ot_flt_id = "the OpenTracing filter";


/***
 * NAME
 *   flt_ot_is_disabled -
 *
 * ARGUMENTS
 *   f     -
 *   event -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   -
 */
bool flt_ot_is_disabled(const struct filter *f FLT_OT_DBG_ARGS(, int event))
{
#ifdef DEBUG_OT
	const struct flt_ot_conf *conf = FLT_OT_CONF(f);
	const char               *msg;
#endif
	bool                      retval;

	retval = FLT_OT_RT_CTX(f->ctx)->flag_disabled ? 1 : 0;

#ifdef DEBUG_OT
	msg    = retval ? " (disabled)" : "";

	if (FLT_OT_IN_RANGE(event, 0, FLT_OT_EVENT_MAX - 1))
		FLT_OT_DBG(2, "filter '%s', type: %s, event: '%s' %d%s", conf->id, flt_ot_type(f), flt_ot_event_data[event].name, event, msg);
	else
		FLT_OT_DBG(2, "filter '%s', type: %s%s", conf->id, flt_ot_type(f), msg);
#endif

	return retval;
}


/***
 * NAME
 *   flt_ot_return_int -
 *
 * ARGUMENTS
 *   f      -
 *   err    -
 *   retval -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   -
 */
static int flt_ot_return_int(const struct filter *f, char **err, int retval)
{
	struct flt_ot_runtime_context *rt_ctx = f->ctx;

	if ((retval == FLT_OT_RET_ERROR) || ((err != NULL) && (*err != NULL))) {
		if (rt_ctx->flag_harderr) {
			FLT_OT_DBG(1, "WARNING: filter hard-error (disabled)");

			rt_ctx->flag_disabled = 1;

			_HA_ATOMIC_ADD(FLT_OT_CONF(f)->cnt.disabled + 1, 1);
		} else {
			FLT_OT_DBG(1, "WARNING: filter soft-error");
		}

		retval = FLT_OT_RET_OK;
	}

	FLT_OT_ERR_FREE(*err);

	return retval;
}


/***
 * NAME
 *   flt_ot_return_void -
 *
 * ARGUMENTS
 *   f   -
 *   err -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_return_void(const struct filter *f, char **err)
{
	struct flt_ot_runtime_context *rt_ctx = f->ctx;

	if ((err != NULL) && (*err != NULL)) {
		if (rt_ctx->flag_harderr) {
			FLT_OT_DBG(1, "WARNING: filter hard-error (disabled)");

			rt_ctx->flag_disabled = 1;

			_HA_ATOMIC_ADD(FLT_OT_CONF(f)->cnt.disabled + 1, 1);
		} else {
			FLT_OT_DBG(1, "WARNING: filter soft-error");
		}
	}

	FLT_OT_ERR_FREE(*err);
}


/***
 * NAME
 *   flt_ot_init - Initialize the filter.
 *
 * ARGUMENTS
 *   p     -
 *   fconf -
 *
 * DESCRIPTION
 *   It initializes the filter for a proxy.  You may define this callback
 *   if you need to complete your filter configuration.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_init(struct proxy *p, struct flt_conf *fconf)
{
	struct flt_ot_conf *conf = FLT_OT_DEREF(fconf, conf, NULL);
	char               *err = NULL;
	int                 retval = FLT_OT_RET_ERROR;

	FLT_OT_FUNC("%p, %p", p, fconf);

	if (conf == NULL)
		FLT_OT_RETURN_INT(retval);

	flt_ot_cli_init();

	/*
	 * Initialize the OpenTracing library.
	 */
	retval = ot_init(&(conf->tracer->tracer), conf->tracer->plugin, &err);
	if (retval != FLT_OT_RET_ERROR)
		/* Do nothing. */;
	else if (err != NULL) {
		FLT_OT_ALERT("%s", err);

		FLT_OT_ERR_FREE(err);
	}

	FLT_OT_RETURN_INT(retval);
}


/***
 * NAME
 *   flt_ot_deinit - Free resources allocated by the filter.
 *
 * ARGUMENTS
 *   p     -
 *   fconf -
 *
 * DESCRIPTION
 *   It cleans up what the parsing function and the init callback have done.
 *   This callback is useful to release memory allocated for the filter
 *   configuration.
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_deinit(struct proxy *p, struct flt_conf *fconf)
{
	struct flt_ot_conf **conf = (fconf == NULL) ? NULL : (typeof(conf))&(fconf->conf);
#ifdef DEBUG_OT
	int                  i;
#endif

	FLT_OT_FUNC("%p, %p", p, fconf);

	if (conf == NULL)
		FLT_OT_RETURN();

	ot_debug();
	ot_close(&((*conf)->tracer->tracer));

#ifdef DEBUG_OT
	FLT_OT_DBG(0, "--- used events ----------");
	for (i = 0; i < FLT_OT_TABLESIZE((*conf)->cnt.event); i++)
		if ((*conf)->cnt.event[i].flag_used)
			FLT_OT_DBG(0, "  %02d: %" PRIu64 " / %" PRIu64, i, (*conf)->cnt.event[i].htx[0], (*conf)->cnt.event[i].htx[1]);
#endif

	flt_ot_conf_free(conf);

	FLT_OT_MEMINFO();

	FLT_OT_RETURN();
}


/***
 * NAME
 *   flt_ot_check - Check configuration of the filter for the specified proxy.
 *
 * ARGUMENTS
 *   p     -
 *   fconf -
 *
 * DESCRIPTION
 *   Optionally, by implementing the flt_ot_check() callback, you add a
 *   step to check the internal configuration of your filter after the
 *   parsing phase, when the HAProxy configuration is fully defined.
 *
 * RETURN VALUE
 *   Returns the number of encountered errors.
 */
static int flt_ot_check(struct proxy *p, struct flt_conf *fconf)
{
	struct proxy             *px;
	struct flt_ot_conf       *conf = FLT_OT_DEREF(fconf, conf, NULL);
	struct flt_ot_conf_group *conf_group;
	struct flt_ot_conf_scope *conf_scope;
	struct flt_ot_conf_ph    *ph_group, *ph_scope;
	int                       retval = 0, scope_unused_cnt = 0, span_root_cnt = 0;

	FLT_OT_FUNC("%p, %p", p, fconf);

	if (conf == NULL)
		FLT_OT_RETURN_INT(++retval);

	/*
	 * If only the proxy specified with the <p> parameter is checked, then
	 * no duplicate filters can be found that are not defined in the same
	 * configuration sections.
	 */
	for (px = proxies_list; px != NULL; px = px->next) {
		struct flt_conf *fconf_tmp;

		FLT_OT_DBG(2, "proxy '%s'", px->id);

		/*
		 * The names of all OT filters (filter ID) should be checked,
		 * they must be unique.
		 */
		list_for_each_entry(fconf_tmp, &(px->filter_configs), list)
			if ((fconf_tmp != fconf) && (fconf_tmp->id == ot_flt_id)) {
				struct flt_ot_conf *conf_tmp = fconf_tmp->conf;

				FLT_OT_DBG(2, "  OT filter '%s'", conf_tmp->id);

				if (strcmp(conf_tmp->id, conf->id) == 0) {
					FLT_OT_ALERT("''%s' : duplicated filter ID'", conf_tmp->id);

					retval++;
				}
			}
	}

	if (FLT_OT_DEREF(conf->tracer, id, NULL) == NULL) {
		FLT_OT_ALERT("''%s' : no tracer found'", conf->id);

		retval++;
	}

	/*
	 * Checking that all defined 'ot-group' sections have correctly declared
	 * 'ot-scope' sections (ie whether the declared 'ot-scope' sections have
	 * corresponding definitions).
	 */
	list_for_each_entry(conf_group, &(conf->groups), list)
		list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
			bool flag_found = 0;

			list_for_each_entry(conf_scope, &(conf->scopes), list)
				if (strcmp(ph_scope->id, conf_scope->id) == 0) {
					ph_scope->ptr         = conf_scope;
					conf_scope->flag_used = 1;
					flag_found            = 1;

					break;
				}

			if (!flag_found) {
				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_GROUP_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf_group->id, ph_scope->id);

				retval++;
			}
		}

	if (conf->tracer != NULL) {
		/*
		 * Checking that all declared 'groups' keywords have correctly
		 * defined 'ot-group' sections.
		 */
		list_for_each_entry(ph_group, &(conf->tracer->ph_groups), list) {
			bool flag_found = 0;

			list_for_each_entry(conf_group, &(conf->groups), list)
				if (strcmp(ph_group->id, conf_group->id) == 0) {
					ph_group->ptr         = conf_group;
					conf_group->flag_used = 1;
					flag_found            = 1;

					break;
				}

			if (!flag_found) {
				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_TRACER_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_GROUP_ID " '%s''", conf->tracer->id, ph_group->id);

				retval++;
			}
		}

		/*
		 * Checking that all declared 'scopes' keywords have correctly
		 * defined 'ot-scope' sections.
		 */
		list_for_each_entry(ph_scope, &(conf->tracer->ph_scopes), list) {
			bool flag_found = 0;

			list_for_each_entry(conf_scope, &(conf->scopes), list)
				if (strcmp(ph_scope->id, conf_scope->id) == 0) {
					ph_scope->ptr         = conf_scope;
					conf_scope->flag_used = 1;
					flag_found            = 1;

					break;
				}

			if (!flag_found) {
				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_TRACER_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf->tracer->id, ph_scope->id);

				retval++;
			}
		}
	}

	FLT_OT_DBG(3, "--- filter '%s' configuration ----------", conf->id);
	FLT_OT_DBG(3, "- defined spans ----------");

	list_for_each_entry(conf_scope, &(conf->scopes), list) {
		if (conf_scope->flag_used) {
			struct flt_ot_conf_span *conf_span;

			/*
			 * In principle, only one span should be labeled
			 * as a root span.
			 */
			list_for_each_entry(conf_span, &(conf_scope->spans), list) {
				FLT_OT_DBG_CONF_SPAN("   ", conf_span);

				span_root_cnt += conf_span->flag_root ? 1 : 0;
			}

#ifdef DEBUG_OT
			conf->cnt.event[conf_scope->event].flag_used = 1;
#endif

			/* Set the flags of the analyzers used. */
			conf->tracer->analyzers |= flt_ot_event_data[conf_scope->event].an_bit;
		} else {
			FLT_OT_ALERT("''%s' : unused " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);

			scope_unused_cnt++;
		}
	}

	/*
	 * Unused scopes or a number of root spans other than one do not
	 * necessarily have to be errors, but it is good to print it when
	 * starting HAProxy.
	 */
	if (scope_unused_cnt > 0)
		FLT_OT_ALERT("''%s' : %d scope(s) not in use'", conf->id, scope_unused_cnt);

	if (LIST_ISEMPTY(&(conf->scopes)))
		/* Do nothing. */;
	else if (span_root_cnt == 0)
		FLT_OT_ALERT("''%s' : no span is marked as the root span'", conf->id);
	else if (span_root_cnt > 1)
		FLT_OT_ALERT("''%s' : multiple spans are marked as the root span'", conf->id);

	FLT_OT_DBG_LIST(conf, group, "", "defined", _group,
	                FLT_OT_DBG_CONF_GROUP("   ", _group);
	                FLT_OT_DBG_LIST(_group, ph_scope, "   ", "used", _scope, FLT_OT_DBG_CONF_PH("      ", _scope)));
	FLT_OT_DBG_LIST(conf, scope, "", "defined", _scope, FLT_OT_DBG_CONF_SCOPE("   ", _scope));

	if (conf->tracer != NULL) {
		FLT_OT_DBG(3, "   --- tracer '%s' configuration ----------", conf->tracer->id);
		FLT_OT_DBG_LIST(conf->tracer, ph_group, "   ", "used", _group, FLT_OT_DBG_CONF_PH("      ", _group));
		FLT_OT_DBG_LIST(conf->tracer, ph_scope, "   ", "used", _scope, FLT_OT_DBG_CONF_PH("      ", _scope));
	}

	FLT_OT_RETURN_INT(retval);
}


/***
 * NAME
 *   flt_ot_init_per_thread -
 *
 * ARGUMENTS
 *   p     -
 *   fconf -
 *
 * DESCRIPTION
 *   It initializes the filter for each thread.  It works the same way than
 *   flt_ot_init() but in the context of a thread.  This callback is called
 *   after the thread creation.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_init_per_thread(struct proxy *p, struct flt_conf *fconf)
{
	struct flt_ot_conf *conf = FLT_OT_DEREF(fconf, conf, NULL);
	char               *err = NULL;
	int                 retval = FLT_OT_RET_ERROR;

	FLT_OT_FUNC("%p, %p", p, fconf);

	if (conf == NULL)
		FLT_OT_RETURN_INT(retval);

	/*
	 * Start the OpenTracing library tracer thread.
	 * Enable HTX streams filtering.
	 */
	if (!(fconf->flags & FLT_CFG_FL_HTX)) {
		retval = ot_start(conf->tracer->tracer, conf->tracer->cfgbuf, &err);
		if (retval != FLT_OT_RET_ERROR)
			fconf->flags |= FLT_CFG_FL_HTX;
		else if (err != NULL) {
			FLT_OT_ALERT("%s", err);

			FLT_OT_ERR_FREE(err);
		}
	} else {
		retval = FLT_OT_RET_OK;
	}

	FLT_OT_RETURN_INT(retval);
}


#ifdef DEBUG_OT

/***
 * NAME
 *   flt_ot_deinit_per_thread -
 *
 * ARGUMENTS
 *   p     -
 *   fconf -
 *
 * DESCRIPTION
 *   It cleans up what the init_per_thread callback have done.  It is called
 *   in the context of a thread, before exiting it.
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_deinit_per_thread(struct proxy *p, struct flt_conf *fconf)
{
	FLT_OT_FUNC("%p, %p", p, fconf);

	FLT_OT_RETURN();
}

#endif /* DEBUG_OT */


/***
 * NAME
 *   flt_ot_attach - Called when a filter instance is created and attach to a stream.
 *
 * ARGUMENTS
 *   s -
 *   f -
 *
 * DESCRIPTION
 *   It is called after a filter instance creation, when it is attached to a
 *   stream.  This happens when the stream is started for filters defined on
 *   the stream's frontend and when the backend is set for filters declared
 *   on the stream's backend.  It is possible to ignore the filter, if needed,
 *   by returning 0.  This could be useful to have conditional filtering.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 to ignore the filter,
 *   any other value otherwise.
 */
static int flt_ot_attach(struct stream *s, struct filter *f)
{
	const struct flt_ot_conf *conf = FLT_OT_CONF(f);
	char                     *err = NULL;

	FLT_OT_FUNC("%p, %p", s, f);

	if (conf->tracer->flag_disabled) {
		FLT_OT_DBG(2, "filter '%s', type: %s (disabled)", conf->id, flt_ot_type(f));

		FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
	}
	else if (conf->tracer->rate_limit < FLT_OT_FLOAT_U32(FLT_OT_RATE_LIMIT_MAX, FLT_OT_RATE_LIMIT_MAX)) {
		uint32_t rnd = ha_random32();

		if (conf->tracer->rate_limit <= rnd) {
			FLT_OT_DBG(2, "filter '%s', type: %s (ignored: %u <= %u)", conf->id, flt_ot_type(f), conf->tracer->rate_limit, rnd);

			FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
		}
	}

	FLT_OT_DBG(2, "filter '%s', type: %s (run)", conf->id, flt_ot_type(f));

	f->ctx = flt_ot_runtime_context_init(s, f, &err);
	FLT_OT_ERR_FREE(err);
	if (f->ctx == NULL) {
		FLT_OT_LOG(LOG_EMERG, "failed to create context");

		FLT_OT_RETURN_INT(FLT_OT_RET_IGNORE);
	}

	/*
	 * AN_REQ_WAIT_HTTP and AN_RES_WAIT_HTTP analyzers can only be used
	 * in the .channel_post_analyze callback function.
	 */
	f->pre_analyzers  |= conf->tracer->analyzers & ((AN_REQ_ALL & ~AN_REQ_WAIT_HTTP & ~AN_REQ_HTTP_TARPIT) | (AN_RES_ALL & ~AN_RES_WAIT_HTTP));
	f->post_analyzers |= conf->tracer->analyzers & (AN_REQ_WAIT_HTTP | AN_RES_WAIT_HTTP);

	FLT_OT_LOG(LOG_INFO, "%08x %08x", f->pre_analyzers, f->post_analyzers);

	flt_ot_vars_dump(s);
	flt_ot_http_headers_dump(&(s->req));

	FLT_OT_RETURN_INT(FLT_OT_RET_OK);
}


#ifdef DEBUG_OT

/***
 * NAME
 *   flt_ot_stream_start - Called when a stream is created.
 *
 * ARGUMENTS
 *   s -
 *   f -
 *
 * DESCRIPTION
 *   It is called when a stream is started.  This callback can fail by
 *   returning a negative value.  It will be considered as a critical error
 *   by HAProxy which disabled the listener for a short time.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_stream_start(struct stream *s, struct filter *f)
{
	char *err = NULL;
	int   retval = FLT_OT_RET_OK;

	FLT_OT_FUNC("%p, %p", s, f);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(retval);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_stream_set_backend - Called when a backend is set for a stream.
 *
 * ARGUMENTS
 *   s  -
 *   f  -
 *   be -
 *
 * DESCRIPTION
 *   It is called when a backend is set for a stream.  This callbacks will be
 *   called for all filters attached to a stream (frontend and backend).  Note
 *   this callback is not called if the frontend and the backend are the same.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be)
{
	char *err = NULL;
	int   retval = FLT_OT_RET_OK;

	FLT_OT_FUNC("%p, %p, %p", s, f, be);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(retval);

	FLT_OT_DBG(3, "backend: %s", be->id);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_stream_stop - Called when a stream is destroyed.
 *
 * ARGUMENTS
 *   s -
 *   f -
 *
 * DESCRIPTION
 *   It is called when a stream is stopped.  This callback always succeed.
 *   Anyway, it is too late to return an error.
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_stream_stop(struct stream *s, struct filter *f)
{
	char *err = NULL;

	FLT_OT_FUNC("%p, %p", s, f);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN();

	flt_ot_return_void(f, &err);

	FLT_OT_RETURN();
}

#endif /* DEBUG_OT */


/***
 * NAME
 *   flt_ot_detach - Called when a filter instance is detach from a stream, just before its destruction.
 *
 * ARGUMENTS
 *   s -
 *   f -
 *
 * DESCRIPTION
 *   It is called when a filter instance is detached from a stream, before its
 *   destruction.  This happens when the stream is stopped for filters defined
 *   on the stream's frontend and when the analyze ends for filters defined on
 *   the stream's backend.
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_detach(struct stream *s, struct filter *f)
{
	FLT_OT_FUNC("%p, %p", s, f);

	FLT_OT_DBG(2, "filter '%s', type: %s", FLT_OT_CONF(f)->id, flt_ot_type(f));

	flt_ot_runtime_context_free(f);

	FLT_OT_RETURN();
}


/***
 * NAME
 *   flt_ot_check_timeouts - Called when a stream is woken up because of an expired timer.
 *
 * ARGUMENTS
 *   s -
 *   f -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_check_timeouts(struct stream *s, struct filter *f)
{
	char *err = NULL;

	FLT_OT_FUNC("%p, %p", s, f);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN();

	s->pending_events |= TASK_WOKEN_MSG;

	flt_ot_return_void(f, &err);

	FLT_OT_RETURN();
}


/***
 * NAME
 *   flt_ot_channel_start_analyze - Called when analyze starts for a given channel.
 *
 * ARGUMENTS
 *   s   -
 *   f   -
 *   chn -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn)
{
	char *err = NULL;
	int   retval;

	FLT_OT_FUNC("%p, %p, %p", s, f, chn);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_START : FLT_OT_EVENT_REQ_CLIENT_SESS_START)))
		FLT_OT_RETURN_INT(FLT_OT_RET_OK);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));

	if (chn->flags & CF_ISRESP) {
		/* The response channel. */
		chn->analysers |= f->pre_analyzers & AN_RES_ALL;

		/* The event 'on-server-session-start'. */
		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_RES_SERVER_SESS_START, &err);
		if (retval == FLT_OT_RET_WAIT) {
			channel_dont_read(chn);
			channel_dont_close(chn);
		}
	} else {
		/* The request channel. */
		chn->analysers |= f->pre_analyzers & AN_REQ_ALL;

		/* The event 'on-client-session-start'. */
		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_CLIENT_SESS_START, &err);
	}

//	register_data_filter(s, chn, f);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_channel_pre_analyze - Called before a processing happens on a given channel.
 *
 * ARGUMENTS
 *   s      -
 *   f      -
 *   chn    - the channel on which the analyzing is done
 *   an_bit - the analyzer id
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
{
	char *err = NULL;
	int   i, event = -1, retval;

	FLT_OT_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);

	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
		if (flt_ot_event_data[i].an_bit == an_bit) {
			event = i;

			break;
		}

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
		FLT_OT_RETURN_INT(FLT_OT_RET_OK);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));

	retval = flt_ot_event_run(s, f, chn, event, &err);

	if ((retval == FLT_OT_RET_WAIT) && (chn->flags & CF_ISRESP)) {
		channel_dont_read(chn);
		channel_dont_close(chn);
	}

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_channel_post_analyze - Called after a processing happens on a given channel.
 *
 * ARGUMENTS
 *   s      -
 *   f      -
 *   chn    -
 *   an_bit -
 *
 * DESCRIPTION
 *   This function, for its part, is not resumable.  It is called when a
 *   filterable analyzer finishes its processing.  So it called once for
 *   the same analyzer.
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
{
	char *err = NULL;
	int   i, event = -1, retval;

	FLT_OT_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);

	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
		if (flt_ot_event_data[i].an_bit == an_bit) {
			event = i;

			break;
		}

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
		FLT_OT_RETURN_INT(FLT_OT_RET_OK);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));

	retval = flt_ot_event_run(s, f, chn, event, &err);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_channel_end_analyze - Called when analyze ends for a given channel.
 *
 * ARGUMENTS
 *   s   -
 *   f   -
 *   chn -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn)
{
	char *err = NULL;
	int   rc, retval;

	FLT_OT_FUNC("%p, %p, %p", s, f, chn);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_END : FLT_OT_EVENT_REQ_CLIENT_SESS_END)))
		FLT_OT_RETURN_INT(FLT_OT_RET_OK);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));

	if (chn->flags & CF_ISRESP) {
		/* The response channel, event 'on-server-session-end'. */
		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_RES_SERVER_SESS_END, &err);
	} else {
		/* The request channel, event 'on-client-session-end'. */
		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_CLIENT_SESS_END, &err);

		/*
		 * In case an event using server response is defined and not
		 * executed, event 'on-server-unavailable' is called here.
		 */
		if ((FLT_OT_CONF(f)->tracer->analyzers & AN_RES_ALL) && !(FLT_OT_RT_CTX(f->ctx)->analyzers & AN_RES_ALL)) {
			rc = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_SERVER_UNAVAILABLE, &err);
			if ((retval == FLT_OT_RET_OK) && (rc != FLT_OT_RET_OK))
				retval = rc;
		}
	}

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


#ifdef DEBUG_OT

/***
 * NAME
 *   flt_ot_http_headers -
 *
 * ARGUMENTS
 *   s   -
 *   f   -
 *   msg -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_http_headers(struct stream *s, struct filter *f, struct http_msg *msg)
{
	char          *err = NULL;
	struct htx    *htx = htxbuf(&(msg->chn->buf));
	struct htx_sl *sl = http_get_stline(htx);
	int            retval = FLT_OT_RET_OK;

	FLT_OT_FUNC("%p, %p, %p", s, f, msg);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(retval);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), %.*s %.*s %.*s", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), HTX_SL_P1_LEN(sl), HTX_SL_P1_PTR(sl), HTX_SL_P2_LEN(sl), HTX_SL_P2_PTR(sl), HTX_SL_P3_LEN(sl), HTX_SL_P3_PTR(sl));

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_http_payload -
 *
 * ARGUMENTS
 *   s      -
 *   f      -
 *   msg    -
 *   offset -
 *   len    -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len)
{
	char *err = NULL;
	int   retval = len;

	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, msg, offset, len);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(len);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);

	if (retval != len)
		task_wakeup(s->task, TASK_WOKEN_MSG);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_http_end -
 *
 * ARGUMENTS
 *   s   -
 *   f   -
 *   msg -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, 0 if it needs to wait,
 *   any other value otherwise.
 */
static int flt_ot_http_end(struct stream *s, struct filter *f, struct http_msg *msg)
{
	char *err = NULL;
	int   retval = FLT_OT_RET_OK;

	FLT_OT_FUNC("%p, %p, %p", s, f, msg);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(retval);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}


/***
 * NAME
 *   flt_ot_http_reset -
 *
 * ARGUMENTS
 *   s   -
 *   f   -
 *   msg -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_http_reset(struct stream *s, struct filter *f, struct http_msg *msg)
{
	char *err = NULL;

	FLT_OT_FUNC("%p, %p, %p", s, f, msg);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN();

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));

	flt_ot_return_void(f, &err);

	FLT_OT_RETURN();
}


/***
 * NAME
 *   flt_ot_http_reply -
 *
 * ARGUMENTS
 *   s      -
 *   f      -
 *   status -
 *   msg    -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   This function does not return a value.
 */
static void flt_ot_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg)
{
	char *err = NULL;

	FLT_OT_FUNC("%p, %p, %hd, %p", s, f, status, msg);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN();

	FLT_OT_DBG(3, "channel: -, mode: %s (%s)", flt_ot_pr_mode(s), flt_ot_stream_pos(s));

	flt_ot_return_void(f, &err);

	FLT_OT_RETURN();
}


/***
 * NAME
 *   flt_ot_tcp_payload -
 *
 * ARGUMENTS
 *   s      -
 *   f      -
 *   chn    -
 *   offset -
 *   len    -
 *
 * DESCRIPTION
 *   -
 *
 * RETURN VALUE
 *   Returns a negative value if an error occurs, any other value otherwise.
 */
static int flt_ot_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len)
{
	char *err = NULL;
	int   retval = len;

	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, chn, offset, len);

	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
		FLT_OT_RETURN_INT(len);

	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);

	if (s->flags & SF_HTX) {
	} else {
	}

	if (retval != len)
		task_wakeup(s->task, TASK_WOKEN_MSG);

	FLT_OT_RETURN_INT(flt_ot_return_int(f, &err, retval));
}

#endif /* DEBUG_OT */


struct flt_ops flt_ot_ops = {
	/* Callbacks to manage the filter lifecycle. */
	.init                  = flt_ot_init,
	.deinit                = flt_ot_deinit,
	.check                 = flt_ot_check,
	.init_per_thread       = flt_ot_init_per_thread,
	.deinit_per_thread     = FLT_OT_DBG_IFDEF(flt_ot_deinit_per_thread, NULL),

	/* Stream callbacks. */
	.attach                = flt_ot_attach,
	.stream_start          = FLT_OT_DBG_IFDEF(flt_ot_stream_start, NULL),
	.stream_set_backend    = FLT_OT_DBG_IFDEF(flt_ot_stream_set_backend, NULL),
	.stream_stop           = FLT_OT_DBG_IFDEF(flt_ot_stream_stop, NULL),
	.detach                = flt_ot_detach,
	.check_timeouts        = flt_ot_check_timeouts,

	/* Channel callbacks. */
	.channel_start_analyze = flt_ot_channel_start_analyze,
	.channel_pre_analyze   = flt_ot_channel_pre_analyze,
	.channel_post_analyze  = flt_ot_channel_post_analyze,
	.channel_end_analyze   = flt_ot_channel_end_analyze,

	/* HTTP callbacks. */
	.http_headers          = FLT_OT_DBG_IFDEF(flt_ot_http_headers, NULL),
	.http_payload          = FLT_OT_DBG_IFDEF(flt_ot_http_payload, NULL),
	.http_end              = FLT_OT_DBG_IFDEF(flt_ot_http_end, NULL),
	.http_reset            = FLT_OT_DBG_IFDEF(flt_ot_http_reset, NULL),
	.http_reply            = FLT_OT_DBG_IFDEF(flt_ot_http_reply, NULL),

	/* TCP callbacks. */
	.tcp_payload           = FLT_OT_DBG_IFDEF(flt_ot_tcp_payload, NULL)
};


REGISTER_BUILD_OPTS("Built with OpenTracing support.");

/*
 * Local variables:
 *  c-indent-level: 8
 *  c-basic-offset: 8
 * End:
 *
 * vi: noexpandtab shiftwidth=8 tabstop=8
 */
