MINOR: ssl/proto_http: Add keywords to take care of early data.

Add a new sample fetch, "ssl_fc_has_early", a boolean that will be true
if early data were sent, and a new action, "wait-for-handshake", if used,
the request won't be forwarded until the SSL handshake is done.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 7ab0f3c..cf0d696 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4163,6 +4163,10 @@
       pass the first router, though it's still delivered to local networks. Do
       not use it unless you fully understand how it works.
 
+    - "wait-for-handshake" : this will delay the processing of the request
+      until the SSL handshake happened. This is mostly useful to delay
+      processing early data until we're sure they are valid.
+
   There is no limit to the number of http-request statements per instance.
 
   It is important to know that http-request rules are processed very early in
@@ -14252,6 +14256,11 @@
   from the cache or the ticket. So prefer "ssl_c_used" if you want to check if
   current SSL session uses a client certificate.
 
+ssl_fc_has_early : boolean
+  Returns true if early data were sent, and the handshake didn't happen yet. As
+  it has security implications, it is useful to be able to refuse those, or
+  wait until the handshake happened.
+
 ssl_fc_has_sni : boolean
   This checks for the presence of a Server Name Indication TLS extension (SNI)
   in an incoming connection was made over an SSL/TLS transport layer. Returns
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 579a25b..844be0a 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -95,6 +95,7 @@
 #include <proto/openssl-compat.h>
 #include <proto/pattern.h>
 #include <proto/proto_tcp.h>
+#include <proto/proto_http.h>
 #include <proto/server.h>
 #include <proto/stream_interface.h>
 #include <proto/log.h>
@@ -4893,13 +4894,6 @@
 	if (global_ssl.async)
 		SSL_clear_mode(conn->xprt_ctx, SSL_MODE_ASYNC);
 #endif
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L
-	/* Once the handshake succeeded, we can consider the early data
-	 * as valid.
-	 */
-	if (conn->flags & CO_FL_EARLY_DATA)
-		conn->flags &= ~CO_FL_EARLY_DATA;
-#endif
 	/* Handshake succeeded */
 	if (!SSL_session_reused(conn->xprt_ctx)) {
 		if (objt_server(conn->target)) {
@@ -5648,6 +5642,22 @@
 
 /***** Below are some sample fetching functions for ACL/patterns *****/
 
+static int
+smp_fetch_ssl_fc_has_early(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	struct connection *conn;
+
+	conn = objt_conn(smp->sess->origin);
+	if (!conn || conn->xprt != &ssl_sock)
+		return 0;
+
+	smp->flags = 0;
+	smp->data.type = SMP_T_BOOL;
+	smp->data.u.sint = (conn->flags & CO_FL_EARLY_DATA) ? 1 : 0;
+
+	return 1;
+}
+
 /* boolean, returns true if client cert was present */
 static int
 smp_fetch_ssl_fc_has_crt(const struct arg *args, struct sample *smp, const char *kw, void *private)
@@ -8139,6 +8149,7 @@
 	{ "ssl_fc_alg_keysize",     smp_fetch_ssl_fc_alg_keysize, 0,                   NULL,    SMP_T_SINT, SMP_USE_L5CLI },
 	{ "ssl_fc_cipher",          smp_fetch_ssl_fc_cipher,      0,                   NULL,    SMP_T_STR,  SMP_USE_L5CLI },
 	{ "ssl_fc_has_crt",         smp_fetch_ssl_fc_has_crt,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
+	{ "ssl_fc_has_early",       smp_fetch_ssl_fc_has_early,   0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
 	{ "ssl_fc_has_sni",         smp_fetch_ssl_fc_has_sni,     0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
 	{ "ssl_fc_is_resumed",      smp_fetch_ssl_fc_is_resumed,  0,                   NULL,    SMP_T_BOOL, SMP_USE_L5CLI },
 #ifdef OPENSSL_NPN_NEGOTIATED
@@ -8317,6 +8328,34 @@
 	.name     = "SSL",
 };
 
+enum act_return ssl_action_wait_for_hs(struct act_rule *rule, struct proxy *px,
+                                       struct session *sess, struct stream *s, int flags)
+{
+	struct connection *conn;
+
+	conn = objt_conn(sess->origin);
+
+	if (conn) {
+		if (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_SSL_WAIT_HS)) {
+			s->req.flags |= CF_READ_NULL;
+			return ACT_RET_YIELD;
+		}
+	}
+	return (ACT_RET_CONT);
+}
+
+static enum act_parse_ret ssl_parse_wait_for_hs(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err)
+{
+	rule->action_ptr = ssl_action_wait_for_hs;
+
+	return ACT_RET_PRS_OK;
+}
+
+static struct action_kw_list http_req_actions = {ILH, {
+	{ "wait-for-handshake", ssl_parse_wait_for_hs },
+	{ /* END */ }
+}};
+
 #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
 
 static void ssl_sock_sctl_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
@@ -8419,6 +8458,8 @@
 #endif
 	/* Load SSL string for the verbose & debug mode. */
 	ERR_load_SSL_strings();
+
+	http_req_keywords_register(&http_req_actions);
 }
 
 #ifndef OPENSSL_NO_ENGINE
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 53b201c..a5463b7 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -572,6 +572,16 @@
 	if (conn->flags & CO_FL_ERROR)
 		si->flags |= SI_FL_ERR;
 
+	/* If we had early data, and the handshake ended, then
+	 * we can remove the flag, and attempt to wake the task up,
+	 * in the event there's an analyser waiting for the end of
+	 * the handshake.
+	 */
+	if ((conn->flags & (CO_FL_EARLY_DATA | CO_FL_EARLY_SSL_HS)) == CO_FL_EARLY_DATA) {
+		conn->flags &= ~CO_FL_EARLY_DATA;
+		task_wakeup(si_task(si), TASK_WOKEN_MSG);
+	}
+
 	if ((si->state < SI_ST_EST) &&
 	    (conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) {
 		si->exp = TICK_ETERNITY;