[MEDIUM] stream-interface: add a ->release callback

When a connection is closed on a stream interface, some iohandlers
will need to be informed in order to release some resources. This
normally happens upon a shutr+shutw. It is the equivalent of the
fd_delete() call which is done for real sockets, except that this
time we release internal resources.

It can also be used with real sockets because it does not cost
anything else and might one day be useful.
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index db166c9..cfba687 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -100,6 +100,7 @@
 	void (*chk_snd)(struct stream_interface *);/* chk_snd function */
 	int (*connect)(struct stream_interface *, struct proxy *, struct server *,
 		       struct sockaddr *, struct sockaddr *); /* connect function if any */
+	void (*release)(struct stream_interface *); /* handler to call after the last close() */
 	void (*iohandler)(struct stream_interface *);  /* internal I/O handler when embedded */
 	struct buffer *ib, *ob; /* input and output buffers */
 	int conn_retries;	/* number of connect retries left */
diff --git a/src/session.c b/src/session.c
index 556fffe..10c7f13 100644
--- a/src/session.c
+++ b/src/session.c
@@ -139,6 +139,7 @@
 	s->si[0].err_loc   = NULL;
 	s->si[0].connect   = NULL;
 	s->si[0].iohandler = NULL;
+	s->si[0].release   = NULL;
 	s->si[0].exp       = TICK_ETERNITY;
 	s->si[0].flags     = SI_FL_NONE;
 
@@ -161,6 +162,7 @@
 	s->si[1].err_loc   = NULL;
 	s->si[1].connect   = NULL;
 	s->si[1].iohandler = NULL;
+	s->si[1].release   = NULL;
 	s->si[1].shutr     = stream_int_shutr;
 	s->si[1].shutw     = stream_int_shutw;
 	s->si[1].exp       = TICK_ETERNITY;
@@ -434,6 +436,9 @@
 		si->flags &= ~SI_FL_CAP_SPLICE;
 		fd_delete(si->fd);
 
+		if (si->release)
+			si->release(si);
+
 		if (si->err_type)
 			return 0;
 
diff --git a/src/stream_interface.c b/src/stream_interface.c
index c45abdb..113f01c 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -178,6 +178,9 @@
 		si->exp = TICK_ETERNITY;
 	}
 
+	if (si->release)
+		si->release(si);
+
 	/* note that if the task exist, it must unregister itself once it runs */
 	if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
 		task_wakeup(si->owner, TASK_WOKEN_IO);
@@ -214,6 +217,9 @@
 		si->exp = TICK_ETERNITY;
 	}
 
+	if (si->release)
+		si->release(si);
+
 	/* note that if the task exist, it must unregister itself once it runs */
 	if (!(si->flags & SI_FL_DONT_WAKE) && si->owner)
 		task_wakeup(si->owner, TASK_WOKEN_IO);
@@ -289,6 +295,7 @@
 	si->chk_snd = stream_int_chk_snd;
 	si->connect = NULL;
 	si->iohandler = fct;
+	si->release   = NULL;
 	si->flags |= SI_FL_WAIT_DATA;
 	return si->owner;
 }
@@ -312,6 +319,7 @@
 	si->chk_snd = stream_int_chk_snd;
 	si->connect = NULL;
 	si->iohandler = NULL; /* not used when running as an external task */
+	si->release   = NULL;
 	si->flags |= SI_FL_WAIT_DATA;
 
 	t = task_new();
@@ -337,6 +345,7 @@
 		task_free(si->owner);
 	}
 	si->iohandler = NULL;
+	si->release   = NULL;
 	si->owner = NULL;
 }
 
diff --git a/src/stream_sock.c b/src/stream_sock.c
index 2d5ef6d..8cd721e 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -875,6 +875,9 @@
 		si->exp = TICK_ETERNITY;
 		return;
 	}
+
+	if (si->release)
+		si->release(si);
 }
 
 /*
@@ -899,6 +902,9 @@
 		fd_delete(si->fd);
 		si->state = SI_ST_DIS;
 		si->exp = TICK_ETERNITY;
+
+		if (si->release)
+			si->release(si);
 		return;
 	}
 	EV_FD_CLR(si->fd, DIR_RD);