MINOR: spoe: Add status code in error variable instead of hardcoded value

Now, when option "set-on-error" is enabled, we set a status code representing
the error occurred instead of "true". For values under 256, it represents an
error coming from the engine. Below 256, it reports a SPOP error. In this case,
to retrieve the right SPOP status code, you must remove 256 to this value. Here
are possible values:

  * 1:     a timeout occurred during the event processing.
  * 2:     an error was triggered during the ressources allocation.
  * 255:   an unknown error occurred during the event processing.
  * 256+N: a SPOP error occurred during the event processing.
diff --git a/doc/SPOE.txt b/doc/SPOE.txt
index d716c6b..81376e0 100644
--- a/doc/SPOE.txt
+++ b/doc/SPOE.txt
@@ -217,9 +217,22 @@
   prefixed. So, if your variable name is "error" and your prefix is
   "my_spoe_pfx", the variable will be "txn.my_spoe_pfx.error".
 
-  When set, the variable is the boolean "true". Note that if "option
-  continue-on-error" is set, the variable is not automatically removed between
-  events processing.
+  When set, the variable is an integer representing the error reason. For values
+  under 256, it represents an error coming from the engine. Below 256, it
+  reports a SPOP error. In this case, to retrieve the right SPOP status code,
+  you must remove 256 to this value. Here are possible values:
+
+    * 1       a timeout occurred during the event processing.
+
+    * 2       an error was triggered during the ressources allocation.
+
+    * 255     an unknown error occurred during the event processing.
+
+    * 256+N   a SPOP error occurred during the event processing (see section
+              "Errors & timeouts").
+
+  Note that if "option continue-on-error" is set, the variable is not
+  automatically removed between events processing.
 
   See also: "option continue-on-error", "option var-prefix".
 
@@ -341,7 +354,7 @@
     - on-backend-http-request
     - on-http-response
 
-  See section 3.5 about Events.
+  See section "Events & Messages".
 
 2.4. Example
 -------------
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 6b8c799..1b04598 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -119,6 +119,15 @@
 	SPOE_EV_EVENTS
 };
 
+/* Errors triggered by streams */
+enum spoe_context_error {
+	SPOE_CTX_ERR_NONE = 0,
+	SPOE_CTX_ERR_TOUT,
+	SPOE_CTX_ERR_RES,
+	SPOE_CTX_ERR_UNKNOWN = 255,
+	SPOE_CTX_ERRS,
+};
+
 /* Errors triggerd by SPOE applet */
 enum spoe_frame_error {
 	SPOE_FRM_ERR_NONE = 0,
@@ -243,6 +252,7 @@
 
 	enum spoe_ctx_state state;        /* SPOE_CTX_ST_* */
 	unsigned int        flags;        /* SPOE_CTX_FL_* */
+	unsigned int        status_code;  /* SPOE_CTX_ERR_* */
 
 	unsigned int        stream_id;    /* stream_id and frame_id are used */
 	unsigned int        frame_id;     /* to map NOTIFY and ACK frames */
@@ -259,6 +269,7 @@
 	unsigned int        max_frame_size; /* the negotiated max-frame-size value */
 	unsigned int        flags;          /* SPOE_APPCTX_FL_* */
 
+	unsigned int        status_code;    /* SPOE_FRM_ERR_* */
 	struct list         waiting_queue;  /* list of streams waiting for a ACK frame, in sync and pipelining mode */
 	struct list         list;           /* next spoe appctx for the same agent */
 };
@@ -793,8 +804,10 @@
 			+ 1 + SLEN(CAPABILITIES_KEY) + 1 + 1 + SLEN(CAPABILITIES_VAL)
 			+ 1 + SLEN(ENGINE_ID_KEY) + 1 + 1 + 36);
 
-	if (size < max)
+	if (size < max) {
+		spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
 		return -1;
+	}
 
 	/* Frame type */
 	frame[idx++] = SPOE_FRM_T_HAPROXY_HELLO;
@@ -890,9 +903,6 @@
 {
 	int idx = 0;
 
-	if (size < SPOE_APPCTX(appctx)->max_frame_size)
-		return -1;
-
 	frame[idx++] = SPOE_FRM_T_HAPROXY_NOTIFY;
 
 	/* No flags for now */
@@ -904,8 +914,10 @@
 	idx += encode_spoe_varint(ctx->frame_id, frame+idx);
 
 	/* Copy encoded messages */
-	if (idx + ctx->buffer->i > size)
+	if (idx + ctx->buffer->i > size) {
+		spoe_status_code = SPOE_FRM_ERR_TOO_BIG;
 		return 0;
+	}
 
 	/* Copy encoded messages */
 	memcpy(frame+idx, ctx->buffer->p, ctx->buffer->i);
@@ -1218,7 +1230,7 @@
 	return 0;
 
   found:
-	if (acquire_spoe_buffer(ctx) <= 0)
+	if (!acquire_spoe_buffer(ctx))
 		return 1; /* Retry later */
 
 	/* Copy encoded actions */
@@ -1327,6 +1339,7 @@
 	if (ret <= 0) {
 		if (ret == -1)
 			return 1; /* retry */
+		spoe_status_code = SPOE_FRM_ERR_IO;
 		return -1; /* error */
 	}
 	return framesz;
@@ -1411,6 +1424,8 @@
 		si_shutr(si);
 		si_ic(si)->flags |= CF_READ_NULL;
 		appctx->st0 = SPOE_APPCTX_ST_END;
+		if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
+			SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
 	}
 
 	if (SPOE_APPCTX(appctx)->task) {
@@ -1422,6 +1437,7 @@
 		LIST_DEL(&ctx->list);
 		LIST_INIT(&ctx->list);
 		ctx->state = SPOE_CTX_ST_ERROR;
+		ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
 		task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
 	}
 
@@ -1434,6 +1450,7 @@
 		LIST_DEL(&ctx->list);
 		LIST_INIT(&ctx->list);
 		ctx->state = SPOE_CTX_ST_ERROR;
+		ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
 		task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
 	}
 
@@ -1441,6 +1458,7 @@
 		LIST_DEL(&ctx->list);
 		LIST_INIT(&ctx->list);
 		ctx->state = SPOE_CTX_ST_ERROR;
+		ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
 		task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
 	}
 }
@@ -1458,12 +1476,15 @@
 		task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
 		goto stop;
 	}
-	if (si->state != SI_ST_EST)
+	if (si->state != SI_ST_EST) {
+		spoe_status_code = SPOE_FRM_ERR_IO;
 		goto exit;
+	}
 
 	if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
 		SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
 			    (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
+		spoe_status_code = SPOE_FRM_ERR_TOUT;
 		goto exit;
 	}
 
@@ -1495,6 +1516,7 @@
   stop:
 	return 1;
   exit:
+	SPOE_APPCTX(appctx)->status_code = spoe_status_code;
 	appctx->st0 = SPOE_APPCTX_ST_EXIT;
 	return 0;
 }
@@ -1508,12 +1530,15 @@
 	int   ret, framesz = 0;
 
 
-	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
+	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
+		spoe_status_code = SPOE_FRM_ERR_IO;
 		goto exit;
+	}
 
 	if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
 		SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p - Connection timed out\n",
 			    (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__, appctx);
+		spoe_status_code = SPOE_FRM_ERR_TOUT;
 		goto exit;
 	}
 
@@ -1562,6 +1587,7 @@
   stop:
 	return 1;
   exit:
+	SPOE_APPCTX(appctx)->status_code = spoe_status_code;
 	appctx->st0 = SPOE_APPCTX_ST_EXIT;
 	return 0;
 }
@@ -1576,8 +1602,10 @@
 	unsigned int  fpa = 0;
 	int           ret, framesz = 0, skip_sending = 0, skip_receiving = 0;
 
-	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
+	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
+		spoe_status_code = SPOE_FRM_ERR_IO;
 		goto exit;
+	}
 
 	if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
 		spoe_status_code = SPOE_FRM_ERR_TOUT;
@@ -1617,6 +1645,7 @@
 		case 0: /* ignore */
 			agent->sending_rate++;
 			ctx->state = SPOE_CTX_ST_ERROR;
+			ctx->status_code = (spoe_status_code + 0x100);
 			release_spoe_buffer(ctx);
 			task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
 			LIST_DEL(&ctx->list);
@@ -1702,6 +1731,7 @@
 	return 1;
 
   exit:
+	SPOE_APPCTX(appctx)->status_code = spoe_status_code;
 	appctx->st0 = SPOE_APPCTX_ST_EXIT;
 	return 0;
 }
@@ -1714,6 +1744,8 @@
 	char *frame = trash.str;
 	int ret;
 
+	SPOE_APPCTX(appctx)->status_code = spoe_status_code;
+
 	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
 		goto exit;
 
@@ -1763,11 +1795,15 @@
 	char *frame = trash.str;
 	int   ret, framesz = 0;
 
-	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
+	if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
+		spoe_status_code = SPOE_FRM_ERR_IO;
 		goto exit;
+	}
 
-	if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
+	if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
+		spoe_status_code = SPOE_FRM_ERR_TOUT;
 		goto exit;
+	}
 
 	framesz = 0;
 	ret = recv_spoe_frame(appctx, frame, SPOE_APPCTX(appctx)->max_frame_size);
@@ -1813,6 +1849,8 @@
   stop:
 	return 1;
   exit:
+	if (SPOE_APPCTX(appctx)->status_code == SPOE_FRM_ERR_NONE)
+		SPOE_APPCTX(appctx)->status_code = spoe_status_code;
 	appctx->st0 = SPOE_APPCTX_ST_EXIT;
 	return 0;
 }
@@ -1824,6 +1862,8 @@
 	struct stream_interface *si    = appctx->owner;
 	struct spoe_agent       *agent = SPOE_APPCTX(appctx)->agent;
 
+	spoe_status_code = SPOE_FRM_ERR_NONE;
+
   switchstate:
 	SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
 		    " - appctx-state=%s\n",
@@ -1832,7 +1872,6 @@
 
 	switch (appctx->st0) {
 		case SPOE_APPCTX_ST_CONNECT:
-			spoe_status_code = SPOE_FRM_ERR_NONE;
 			if (handle_connect_spoe_applet(appctx))
 				goto out;
 			goto switchstate;
@@ -1923,6 +1962,7 @@
 	SPOE_APPCTX(appctx)->version         = 0;
 	SPOE_APPCTX(appctx)->max_frame_size  = conf->agent->max_frame_size;
 	SPOE_APPCTX(appctx)->flags           = 0;
+	SPOE_APPCTX(appctx)->status_code     = SPOE_FRM_ERR_NONE;
 
 	LIST_INIT(&SPOE_APPCTX(appctx)->list);
 	LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
@@ -2330,15 +2370,13 @@
 static int
 start_event_processing(struct spoe_context *ctx, int dir)
 {
-	int ret;
 	/* If a process is already started for this SPOE context, retry
 	 * later. */
 	if (ctx->flags & SPOE_CTX_FL_PROCESS)
 		goto wait;
 
-	ret = acquire_spoe_buffer(ctx);
-	if (ret <= 0)
-		return ret;
+	if (!acquire_spoe_buffer(ctx))
+		goto wait;
 
 	/* Set the right flag to prevent request and response processing
 	 * in same time. */
@@ -2358,6 +2396,8 @@
 	/* Reset the flag to allow next processing */
 	ctx->flags &= ~SPOE_CTX_FL_PROCESS;
 
+	ctx->status_code = 0;
+
 	/* Reset processing timer */
 	ctx->process_exp = TICK_ETERNITY;
 
@@ -2405,6 +2445,7 @@
 		send_log(ctx->strm->be, LOG_WARNING,
 			 "failed to process event '%s': timeout.\n",
 			 spoe_event_str[ev]);
+		ctx->status_code = SPOE_CTX_ERR_TOUT;
 		goto error;
 	}
 
@@ -2425,20 +2466,16 @@
 						      ctx->process_exp);
 		}
 		ret = start_event_processing(ctx, dir);
-		if (ret <= 0) {
-			if (!ret)
-				goto out;
-			goto error;
-		}
+		if (!ret)
+			goto out;
 		ret = process_spoe_messages(s, ctx, &(ctx->messages[ev]), dir);
-		if (ret <= 0) {
-			if (!ret)
-				goto skip;
-			goto error;
-		}
+		if (!ret)
+			goto skip;
 
-		if (!queue_spoe_context(ctx))
+		if (!queue_spoe_context(ctx)) {
+			ctx->status_code = SPOE_CTX_ERR_RES;
 			goto error;
+		}
 
 		ctx->state = SPOE_CTX_ST_SENDING_MSGS;
 		/* fall through */
@@ -2452,11 +2489,8 @@
 
 	if (ctx->state == SPOE_CTX_ST_DONE) {
 		ret = process_spoe_actions(s, ctx, ev, dir);
-		if (ret <= 0) {
-			if (!ret)
-				goto skip;
-			goto error;
-		}
+		if (!ret)
+			goto skip;
 		ctx->frame_id++;
 		ctx->state = SPOE_CTX_ST_READY;
 		goto end;
@@ -2475,7 +2509,7 @@
 		// FIXME: Get the error code here
 		memset(&smp, 0, sizeof(smp));
 		smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
-		smp.data.u.sint = 1;
+		smp.data.u.sint = ctx->status_code;
 		smp.data.type   = SMP_T_BOOL;
 
 		set_spoe_var(ctx, "txn", agent->var_on_error,
@@ -2484,7 +2518,7 @@
 
 	ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
 		      ? SPOE_CTX_ST_READY
-		      : SPOE_CTX_ST_ERROR);
+		      : SPOE_CTX_ST_NONE);
 	ret = 1;
 	goto end;
 
@@ -2550,11 +2584,12 @@
 		return NULL;
 	}
 	memset(ctx, 0, sizeof(*ctx));
-	ctx->filter   = filter;
-	ctx->state    = SPOE_CTX_ST_NONE;
-	ctx->flags    = 0;
-	ctx->messages = conf->agent->messages;
-	ctx->buffer   = &buf_empty;
+	ctx->filter      = filter;
+	ctx->state       = SPOE_CTX_ST_NONE;
+	ctx->status_code = SPOE_CTX_ERR_NONE;
+	ctx->flags       = 0;
+	ctx->messages    = conf->agent->messages;
+	ctx->buffer      = &buf_empty;
 	LIST_INIT(&ctx->buffer_wait.list);
 	ctx->buffer_wait.target = ctx;
 	ctx->buffer_wait.wakeup_cb = (int (*)(void *))wakeup_spoe_context;
@@ -2791,6 +2826,9 @@
 		    ((struct spoe_config *)FLT_CONF(filter))->agent->id,
 		    __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
 
+	if (ctx->state == SPOE_CTX_ST_NONE)
+		goto out;
+
 	if (!(chn->flags & CF_ISRESP)) {
 		if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
 			chn->analysers |= AN_REQ_INSPECT_FE;
@@ -2801,11 +2839,9 @@
 			goto out;
 
 		ctx->stream_id = s->uniq_id;
-		if (ctx->state != SPOE_CTX_ST_NONE && ctx->state != SPOE_CTX_ST_ERROR) {
-			ret = process_spoe_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
-			if (ret != 1)
-				goto out;
-		}
+		ret = process_spoe_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
+		if (!ret)
+			goto out;
 		ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
 	}
 	else {
@@ -2815,16 +2851,13 @@
 		if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
 			goto out;
 
-		if (ctx->state != SPOE_CTX_ST_NONE && ctx->state != SPOE_CTX_ST_ERROR) {
-			ret = process_spoe_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
-			if (ret != 1)
-				goto out;
-		}
-		ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
+		ret = process_spoe_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
 		if (!ret) {
 			channel_dont_read(chn);
 			channel_dont_close(chn);
+			goto out;
 		}
+		ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
 	}
 
   out:
@@ -2846,7 +2879,7 @@
 		    __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
 		    ctx->flags, an_bit);
 
-	if (ctx->state == SPOE_CTX_ST_NONE || ctx->state == SPOE_CTX_ST_ERROR)
+	if (ctx->state == SPOE_CTX_ST_NONE)
 		goto out;
 
 	switch (an_bit) {