MAJOR: stream_int: use a common stream_int_shut*() functions regardless of the data layer

Up to now, we had to use a shutr/shutw interface per data layer, which
basically means 3 distinct functions when we include SSL :
  - generic stream_interface
  - sock_raw
  - sock_ssl

With this change, the code located in the stream_interface manages all the
stream_interface and buffer updates, and calls the data layer hooks when
needed.

At the moment, the socket layer hook had been implicitly considered as
being a regular socket, so the si_shut*() functions call the normal
shutdown() and EV_FD_CLR() functions on the fd if a socket layer is
defined. This may change in the future. The stream_int_shut*()
functions don't call EV_FD_CLR() so that they can later be embedded
in lower layers.

Thus, the si->data->shutr() is not called anymore and si->data->shutw()
is called to close the data layer only (eg: only for SSL).

Proceeding like this is very important because it's the only way to be
able not to rely on these functions when called from the connection
handlers, and call the data layers' instead.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index 84f2f0e..9ee9e9a 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -33,7 +33,7 @@
 /* Calls the close() function of the data layer if any */
 static inline void conn_data_close(struct connection *conn)
 {
-	if (conn->data->close)
+	if (conn->data && conn->data->close)
 		conn->data->close(conn);
 }
 
diff --git a/include/proto/stream_interface.h b/include/proto/stream_interface.h
index 91691c0..724c27f 100644
--- a/include/proto/stream_interface.h
+++ b/include/proto/stream_interface.h
@@ -35,6 +35,8 @@
 void stream_int_retnclose(struct stream_interface *si, const struct chunk *msg);
 int conn_si_send_proxy(struct connection *conn, unsigned int flag);
 void stream_sock_update_conn(struct connection *conn);
+int stream_int_shutr(struct stream_interface *si);
+int stream_int_shutw(struct stream_interface *si);
 
 extern struct sock_ops stream_int_embedded;
 extern struct sock_ops stream_int_task;
@@ -164,13 +166,15 @@
 /* Sends a shutr to the connection using the data layer */
 static inline void si_shutr(struct stream_interface *si)
 {
-	si_data(si)->shutr(si);
+	if (stream_int_shutr(si))
+		EV_FD_CLR(si_fd(si), DIR_RD);
 }
 
 /* Sends a shutw to the connection using the data layer */
 static inline void si_shutw(struct stream_interface *si)
 {
-	si_data(si)->shutw(si);
+	if (stream_int_shutw(si))
+		EV_FD_CLR(si_fd(si), DIR_WR);
 }
 
 /* Calls the data state update on the stream interfaace */
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 851870f..20787e8 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -110,8 +110,8 @@
 
 struct sock_ops {
 	void (*update)(struct stream_interface *);  /* I/O update function */
-	void (*shutr)(struct stream_interface *);   /* shutr function */
-	void (*shutw)(struct stream_interface *);   /* shutw function */
+	void (*shutr)(struct connection *, int);    /* shutr function */
+	void (*shutw)(struct connection *, int);    /* shutw function */
 	void (*chk_rcv)(struct stream_interface *); /* chk_rcv function */
 	void (*chk_snd)(struct stream_interface *); /* chk_snd function */
 	int (*read)(struct connection *conn);       /* read callback after poll() */
diff --git a/src/sock_raw.c b/src/sock_raw.c
index 08e0b31..4b81ae0 100644
--- a/src/sock_raw.c
+++ b/src/sock_raw.c
@@ -46,8 +46,6 @@
 static int sock_raw_read(struct connection *conn);
 static int sock_raw_write(struct connection *conn);
 static void sock_raw_data_finish(struct stream_interface *si);
-static void sock_raw_shutr(struct stream_interface *si);
-static void sock_raw_shutw(struct stream_interface *si);
 static void sock_raw_read0(struct stream_interface *si);
 static void sock_raw_chk_rcv(struct stream_interface *si);
 static void sock_raw_chk_snd(struct stream_interface *si);
@@ -667,107 +665,6 @@
 }
 
 /*
- * This function performs a shutdown-write on a stream interface in a connected or
- * init state (it does nothing for other states). It either shuts the write side
- * or closes the file descriptor and marks itself as closed. The buffer flags are
- * updated to reflect the new state. It does also close everything is the SI was
- * marked as being in error state.
- */
-static void sock_raw_shutw(struct stream_interface *si)
-{
-	si->ob->flags &= ~BF_SHUTW_NOW;
-	if (si->ob->flags & BF_SHUTW)
-		return;
-	si->ob->flags |= BF_SHUTW;
-	si->ob->wex = TICK_ETERNITY;
-	si->flags &= ~SI_FL_WAIT_DATA;
-
-	switch (si->state) {
-	case SI_ST_EST:
-		/* we have to shut before closing, otherwise some short messages
-		 * may never leave the system, especially when there are remaining
-		 * unread data in the socket input buffer, or when nolinger is set.
-		 * However, if SI_FL_NOLINGER is explicitly set, we know there is
-		 * no risk so we close both sides immediately.
-		 */
-		if (si->flags & SI_FL_ERR) {
-			/* quick close, the socket is already shut. Remove pending flags. */
-			si->flags &= ~SI_FL_NOLINGER;
-		}
-		else if (si->flags & SI_FL_NOLINGER) {
-			si->flags &= ~SI_FL_NOLINGER;
-			setsockopt(si_fd(si), SOL_SOCKET, SO_LINGER,
-				   (struct linger *) &nolinger, sizeof(struct linger));
-		}
-		else if (!(si->flags & SI_FL_NOHALF)) {
-			EV_FD_CLR(si_fd(si), DIR_WR);
-			shutdown(si_fd(si), SHUT_WR);
-
-			if (!(si->ib->flags & (BF_SHUTR|BF_DONT_READ)))
-				return;
-		}
-
-		/* fall through */
-	case SI_ST_CON:
-		/* we may have to close a pending connection, and mark the
-		 * response buffer as shutr
-		 */
-		conn_data_close(&si->conn);
-		fd_delete(si_fd(si));
-		/* fall through */
-	case SI_ST_CER:
-	case SI_ST_QUE:
-	case SI_ST_TAR:
-		si->state = SI_ST_DIS;
-
-		if (si->release)
-			si->release(si);
-	default:
-		si->flags &= ~SI_FL_WAIT_ROOM;
-		si->ib->flags |= BF_SHUTR;
-		si->ib->rex = TICK_ETERNITY;
-		si->exp = TICK_ETERNITY;
-	}
-}
-
-/*
- * This function performs a shutdown-read on a stream interface in a connected or
- * init state (it does nothing for other states). It either shuts the read side
- * or closes the file descriptor and marks itself as closed. The buffer flags are
- * updated to reflect the new state. If the stream interface has SI_FL_NOHALF,
- * we also forward the close to the write side.
- */
-static void sock_raw_shutr(struct stream_interface *si)
-{
-	si->ib->flags &= ~BF_SHUTR_NOW;
-	if (si->ib->flags & BF_SHUTR)
-		return;
-	si->ib->flags |= BF_SHUTR;
-	si->ib->rex = TICK_ETERNITY;
-	si->flags &= ~SI_FL_WAIT_ROOM;
-
-	if (si->state != SI_ST_EST && si->state != SI_ST_CON)
-		return;
-
-	if (si->ob->flags & BF_SHUTW) {
-		conn_data_close(&si->conn);
-		fd_delete(si_fd(si));
-		si->state = SI_ST_DIS;
-		si->exp = TICK_ETERNITY;
-
-		if (si->release)
-			si->release(si);
-		return;
-	}
-	else if (si->flags & SI_FL_NOHALF) {
-		/* we want to immediately forward this close to the write side */
-		return sock_raw_shutw(si);
-	}
-	EV_FD_CLR(si_fd(si), DIR_RD);
-	return;
-}
-
-/*
  * This function propagates a null read received on a connection. It updates
  * the stream interface. If the stream interface has SI_FL_NOHALF, we also
  * forward the close to the write side.
@@ -990,7 +887,7 @@
 		if (((ob->flags & (BF_SHUTW|BF_HIJACK|BF_AUTO_CLOSE|BF_SHUTW_NOW)) ==
 		     (BF_AUTO_CLOSE|BF_SHUTW_NOW)) &&
 		    (si->state == SI_ST_EST)) {
-			sock_raw_shutw(si);
+			si_shutw(si);
 			goto out_wakeup;
 		}
 
@@ -1041,8 +938,8 @@
 /* stream sock operations */
 struct sock_ops sock_raw = {
 	.update  = sock_raw_data_finish,
-	.shutr   = sock_raw_shutr,
-	.shutw   = sock_raw_shutw,
+	.shutr   = NULL,
+	.shutw   = NULL,
 	.chk_rcv = sock_raw_chk_rcv,
 	.chk_snd = sock_raw_chk_snd,
 	.read    = sock_raw_read,
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 030c7c1..3ee0c4e 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -39,16 +39,14 @@
 /* socket functions used when running a stream interface as a task */
 static void stream_int_update(struct stream_interface *si);
 static void stream_int_update_embedded(struct stream_interface *si);
-static void stream_int_shutr(struct stream_interface *si);
-static void stream_int_shutw(struct stream_interface *si);
 static void stream_int_chk_rcv(struct stream_interface *si);
 static void stream_int_chk_snd(struct stream_interface *si);
 
 /* socket operations for embedded tasks */
 struct sock_ops stream_int_embedded = {
 	.update  = stream_int_update_embedded,
-	.shutr   = stream_int_shutr,
-	.shutw   = stream_int_shutw,
+	.shutr   = NULL,
+	.shutw   = NULL,
 	.chk_rcv = stream_int_chk_rcv,
 	.chk_snd = stream_int_chk_snd,
 	.read    = NULL,
@@ -59,8 +57,8 @@
 /* socket operations for external tasks */
 struct sock_ops stream_int_task = {
 	.update  = stream_int_update,
-	.shutr   = stream_int_shutr,
-	.shutw   = stream_int_shutw,
+	.shutr   = NULL,
+	.shutw   = NULL,
 	.chk_rcv = stream_int_chk_rcv,
 	.chk_snd = stream_int_chk_snd,
 	.read    = NULL,
@@ -210,65 +208,134 @@
 		si->ib->flags &= ~BF_READ_DONTWAIT;
 }
 
-/* default shutr function for scheduled tasks */
-static void stream_int_shutr(struct stream_interface *si)
+/*
+ * This function performs a shutdown-read on a stream interface in a connected
+ * or init state (it does nothing for other states). It either shuts the read
+ * side or marks itself as closed. The buffer flags are updated to reflect the
+ * new state. If the stream interface has SI_FL_NOHALF, we also forward the
+ * close to the write side. If a control layer is defined, then it is supposed
+ * to be a socket layer and file descriptors are then shutdown or closed
+ * accordingly. If no control layer is defined, then the SI is supposed to be
+ * an embedded one and the owner task is woken up if it exists. The function
+ * does not disable polling on the FD by itself, it returns non-zero instead
+ * if the caller needs to do so (except when the FD is deleted where this is
+ * implicit).
+ */
+int stream_int_shutr(struct stream_interface *si)
 {
-	DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
-		__FUNCTION__,
-		si, si->state, si->ib->flags, si->ob->flags);
+	struct connection *conn = &si->conn;
 
 	si->ib->flags &= ~BF_SHUTR_NOW;
 	if (si->ib->flags & BF_SHUTR)
-		return;
+		return 0;
 	si->ib->flags |= BF_SHUTR;
 	si->ib->rex = TICK_ETERNITY;
 	si->flags &= ~SI_FL_WAIT_ROOM;
 
 	if (si->state != SI_ST_EST && si->state != SI_ST_CON)
-		return;
+		return 0;
 
 	if (si->ob->flags & BF_SHUTW) {
+		conn_data_close(&si->conn);
+		if (conn->ctrl)
+			fd_delete(si_fd(si));
 		si->state = SI_ST_DIS;
 		si->exp = TICK_ETERNITY;
 
-		conn_data_close(&si->conn);
 		if (si->release)
 			si->release(si);
 	}
+	else if (si->flags & SI_FL_NOHALF) {
+		/* we want to immediately forward this close to the write side */
+		return stream_int_shutw(si);
+	}
+	else if (conn->ctrl) {
+		/* we want the caller to disable polling on this FD */
+		return 1;
+	}
 
-	/* note that if the task exist, it must unregister itself once it runs */
-	if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
+	/* note that if the task exists, it must unregister itself once it runs */
+	if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
 		task_wakeup(si->owner, TASK_WOKEN_IO);
+	return 0;
 }
 
-/* default shutw function for scheduled tasks */
-static void stream_int_shutw(struct stream_interface *si)
+/*
+ * This function performs a shutdown-write on a stream interface in a connected or
+ * init state (it does nothing for other states). It either shuts the write side
+ * or marks itself as closed. The buffer flags are updated to reflect the new state.
+ * It does also close everything if the SI was marked as being in error state. If
+ * there is a data-layer shutdown, it is called. If a control layer is defined, then
+ * it is supposed to be a socket layer and file descriptors are then shutdown or
+ * closed accordingly. If no control layer is defined, then the SI is supposed to
+ * be an embedded one and the owner task is woken up if it exists. The function
+ * does not disable polling on the FD by itself, it returns non-zero instead if
+ * the caller needs to do so (except when the FD is deleted where this is implicit).
+ */
+int stream_int_shutw(struct stream_interface *si)
 {
-	DPRINTF(stderr, "%s: si=%p, si->state=%d ib->flags=%08x ob->flags=%08x\n",
-		__FUNCTION__,
-		si, si->state, si->ib->flags, si->ob->flags);
+	struct connection *conn = &si->conn;
 
 	si->ob->flags &= ~BF_SHUTW_NOW;
 	if (si->ob->flags & BF_SHUTW)
-		return;
+		return 0;
 	si->ob->flags |= BF_SHUTW;
 	si->ob->wex = TICK_ETERNITY;
 	si->flags &= ~SI_FL_WAIT_DATA;
 
 	switch (si->state) {
 	case SI_ST_EST:
-		if (!(si->ib->flags & (BF_SHUTR|BF_DONT_READ)))
-			break;
+		/* we have to shut before closing, otherwise some short messages
+		 * may never leave the system, especially when there are remaining
+		 * unread data in the socket input buffer, or when nolinger is set.
+		 * However, if SI_FL_NOLINGER is explicitly set, we know there is
+		 * no risk so we close both sides immediately.
+		 */
+		if (si->flags & SI_FL_ERR) {
+			/* quick close, the socket is already shut. Remove pending flags. */
+			si->flags &= ~SI_FL_NOLINGER;
+		} else if (si->flags & SI_FL_NOLINGER) {
+			si->flags &= ~SI_FL_NOLINGER;
+			if (conn->ctrl) {
+				setsockopt(si_fd(si), SOL_SOCKET, SO_LINGER,
+					   (struct linger *) &nolinger, sizeof(struct linger));
+			}
+			/* unclean data-layer shutdown */
+			if (conn->data && conn->data->shutw)
+				conn->data->shutw(conn, 0);
+		} else {
+			/* clean data-layer shutdown */
+			if (conn->data && conn->data->shutw)
+				conn->data->shutw(conn, 1);
+
+			if (!(si->flags & SI_FL_NOHALF)) {
+				/* We shutdown transport layer */
+				if (conn->ctrl)
+					shutdown(si_fd(si), SHUT_WR);
+
+				if (!(si->ib->flags & (BF_SHUTR|BF_DONT_READ))) {
+					/* OK just a shutw, but we want the caller
+					 * to disable polling on this FD if exists.
+					 */
+					return !!conn->ctrl;
+				}
+			}
+		}
 
 		/* fall through */
 	case SI_ST_CON:
+		/* we may have to close a pending connection, and mark the
+		 * response buffer as shutr
+		 */
+		conn_data_close(&si->conn);
+		if (conn->ctrl)
+			fd_delete(si_fd(si));
+		/* fall through */
 	case SI_ST_CER:
 	case SI_ST_QUE:
 	case SI_ST_TAR:
 		si->state = SI_ST_DIS;
-		/* fall through */
 
-		conn_data_close(&si->conn);
 		if (si->release)
 			si->release(si);
 	default:
@@ -278,9 +345,10 @@
 		si->exp = TICK_ETERNITY;
 	}
 
-	/* note that if the task exist, it must unregister itself once it runs */
-	if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
+	/* note that if the task exists, it must unregister itself once it runs */
+	if (!conn->ctrl && !(si->flags & SI_FL_DONT_WAKE) && si->owner)
 		task_wakeup(si->owner, TASK_WOKEN_IO);
+	return 0;
 }
 
 /* default chk_rcv function for scheduled tasks */
@@ -513,7 +581,7 @@
 		if (si->ob->flags & BF_OUT_EMPTY) {
 			if (((si->ob->flags & (BF_SHUTW|BF_HIJACK|BF_SHUTW_NOW)) == BF_SHUTW_NOW) &&
 			    (si->state == SI_ST_EST))
-				si_shutw(si);
+				stream_int_shutw(si);
 			EV_FD_CLR(fd, DIR_WR);
 			si->ob->wex = TICK_ETERNITY;
 		}