BUG/MEDIUM: spoe: Create a SPOE applet if necessary when the last one is released

On a thread, when the last SPOE applet is released, if there are still
pending streams, a new one is created. Of course, HAproxy must not be
stopping. It is important to start a new applet in this case to not abort
in-progress jobs, especially when a maxconn is set. Because applets may be
closed to be fair with connections waiting for a free slot.

This patch should partely fix the issue #1340. It depends on the commit
"MINOR: spoe: Create a SPOE applet if necessary when the last one on a
thread is closed". Both must be backported as far as 2.0.

(cherry picked from commit 6f1296b5c70d2f513836b61cfb9a06aed2db10eb)
[wt: previous patch to be picked is in fact "MINOR: spoe: Add a pointer
     on the filter config in the spoe_agent structure"]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 4d350af9da9a417717468d49c45d5c59ff740c80)
[wt: context updates (LIST_DEL vs LISTçDELETE): reindent displaced block
     instead]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit fe7f615a27fc2995a492ecd77d81b1f34aa408f6)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit a2f0d9973f92182f27d95c414db90e6708fccc69)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 9ddffa8..6840ec8 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -109,6 +109,7 @@
 static int  spoe_queue_context(struct spoe_context *ctx);
 static int  spoe_acquire_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
 static void spoe_release_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
+static struct appctx *spoe_create_appctx(struct spoe_config *conf);
 
 /********************************************************************
  * helper functions/globals
@@ -1318,27 +1319,37 @@
 		}
 		goto end;
 	}
+	else {
+		/* It is the last running applet and the sending and waiting
+		 * queues are not empty. Try to start a new one if HAproxy is
+		 * not stopping.
+		 */
+		if (!stopping &&
+		    (!LIST_ISEMPTY(&agent->rt[tid].sending_queue) || !LIST_ISEMPTY(&agent->rt[tid].waiting_queue)) &&
+		    spoe_create_appctx(agent->spoe_conf))
+			goto end;
 
-	/* If this was the last running applet, notify all waiting streams */
-	list_for_each_entry_safe(ctx, back, &agent->rt[tid].sending_queue, list) {
-		LIST_DEL(&ctx->list);
-		LIST_INIT(&ctx->list);
-		_HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
-		spoe_update_stat_time(&ctx->stats.tv_queue, &ctx->stats.t_queue);
-		ctx->spoe_appctx = NULL;
-		ctx->state = SPOE_CTX_ST_ERROR;
-		ctx->status_code = (spoe_appctx->status_code + 0x100);
-		task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
-	}
-	list_for_each_entry_safe(ctx, back, &agent->rt[tid].waiting_queue, list) {
-		LIST_DEL(&ctx->list);
-		LIST_INIT(&ctx->list);
-		_HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
-		spoe_update_stat_time(&ctx->stats.tv_wait, &ctx->stats.t_waiting);
-		ctx->spoe_appctx = NULL;
-		ctx->state = SPOE_CTX_ST_ERROR;
-		ctx->status_code = (spoe_appctx->status_code + 0x100);
-		task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
+		/* otherwise, notify all waiting streams */
+		list_for_each_entry_safe(ctx, back, &agent->rt[tid].sending_queue, list) {
+			LIST_DEL(&ctx->list);
+			LIST_INIT(&ctx->list);
+			_HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
+			spoe_update_stat_time(&ctx->stats.tv_queue, &ctx->stats.t_queue);
+			ctx->spoe_appctx = NULL;
+			ctx->state = SPOE_CTX_ST_ERROR;
+			ctx->status_code = (spoe_appctx->status_code + 0x100);
+			task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
+		}
+		list_for_each_entry_safe(ctx, back, &agent->rt[tid].waiting_queue, list) {
+			LIST_DEL(&ctx->list);
+			LIST_INIT(&ctx->list);
+			_HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
+			spoe_update_stat_time(&ctx->stats.tv_wait, &ctx->stats.t_waiting);
+			ctx->spoe_appctx = NULL;
+			ctx->state = SPOE_CTX_ST_ERROR;
+			ctx->status_code = (spoe_appctx->status_code + 0x100);
+			task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
+		}
 	}
 
   end: