[MEDIUM] stream interface: add the ->shutw method as well as in and out buffers
Those entries were really needed for cleaner and better code. Using them
has permitted to automatically close a file descriptor during a shut write,
reducing by 20% the number of calls to process_session() and derived
functions.
Process_session() does not need to know the file descriptor anymore, though
it still remains very complicated due to the special case for the connect
mode.
diff --git a/include/proto/stream_sock.h b/include/proto/stream_sock.h
index e104054..45064be 100644
--- a/include/proto/stream_sock.h
+++ b/include/proto/stream_sock.h
@@ -36,6 +36,7 @@
int stream_sock_data_check_timeouts(int fd);
int stream_sock_data_update(int fd);
int stream_sock_data_finish(int fd);
+int stream_sock_shutw(struct stream_interface *si);
/* This either returns the sockname or the original destination address. Code
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 2525f12..f976565 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -24,6 +24,7 @@
#include <stdlib.h>
+#include <types/buffers.h>
#include <common/config.h>
/* A stream interface must have its own errors independantly of the buffer's,
@@ -60,6 +61,8 @@
unsigned int prev_state;/* SI_ST*, copy of previous state */
void *owner; /* generally a (struct task*) */
int fd; /* file descriptor for a stream driver when known */
+ int (*shutw)(struct stream_interface *); /* shutw function */
+ struct buffer *ib, *ob; /* input and output buffers */
unsigned int err_type; /* first error detected, one of SI_ET_* */
void *err_loc; /* commonly the server, NULL when SI_ET_NONE */
};
diff --git a/src/client.c b/src/client.c
index ef1ee09..aed9415 100644
--- a/src/client.c
+++ b/src/client.c
@@ -174,6 +174,7 @@
s->si[0].err_type = SI_ET_NONE;
s->si[0].err_loc = NULL;
s->si[0].owner = t;
+ s->si[0].shutw = stream_sock_shutw;
s->si[0].fd = cfd;
s->cli_fd = cfd;
@@ -181,6 +182,7 @@
s->si[1].err_type = SI_ET_NONE;
s->si[1].err_loc = NULL;
s->si[1].owner = t;
+ s->si[1].shutw = stream_sock_shutw;
s->si[1].fd = -1; /* just to help with debugging */
s->srv = s->prev_srv = s->srv_conn = NULL;
@@ -338,6 +340,7 @@
buffer_init(s->req);
s->req->prod = &s->si[0];
s->req->cons = &s->si[1];
+ s->si[0].ib = s->si[1].ob = s->req;
if (p->mode == PR_MODE_HTTP) /* reserve some space for header rewriting */
s->req->rlim -= MAXREWRITE;
@@ -361,6 +364,7 @@
buffer_init(s->rep);
s->rep->prod = &s->si[1];
s->rep->cons = &s->si[0];
+ s->si[0].ob = s->si[1].ib = s->rep;
s->rep->rto = s->be->timeout.server;
s->rep->wto = s->fe->timeout.client;
diff --git a/src/proto_http.c b/src/proto_http.c
index b3db20d..b32c042 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -669,6 +669,21 @@
stream_sock_data_check_timeouts(s->req->cons->fd);
}
+ /* Check if we need to close the write side. This can only happen
+ * when either SHUTR or EMPTY appears, because WRITE_ENA cannot appear
+ * from low level, and neither HIJACK nor SHUTW can disappear from low
+ * level.
+ */
+ if (unlikely((s->req->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR))) {
+ buffer_shutw(s->req);
+ s->req->cons->shutw(s->req->cons);
+ }
+
+ if (unlikely((s->rep->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR))) {
+ buffer_shutw(s->rep);
+ s->rep->cons->shutw(s->rep->cons);
+ }
+
/* When a server-side connection is released, we have to
* count it and check for pending connections on this server.
*/
@@ -698,6 +713,7 @@
}
}
+ /* This is needed when debugging is enabled, to indicate client-side close */
if (unlikely(s->rep->cons->state == SI_ST_CLO &&
s->rep->cons->prev_state == SI_ST_EST)) {
if (unlikely((s->rep->cons->state == SI_ST_CLO) &&
@@ -710,39 +726,6 @@
}
}
-
- /* Check if we need to close the write side. This can only happen
- * when either SHUTR or EMPTY appears, because WRITE_ENA cannot appear
- * from low level, and neither HIJACK nor SHUTW can disappear from low
- * level. Later, this should move to stream_sock_{read,write}.
- */
- if ((s->req->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)) {
- buffer_shutw(s->req);
- if (s->rep->flags & BF_SHUTR) {
- fd_delete(s->req->cons->fd);
- s->req->cons->state = SI_ST_CLO;
- }
- else {
- EV_FD_CLR(s->req->cons->fd, DIR_WR);
- shutdown(s->req->cons->fd, SHUT_WR);
- }
- }
-
- /* Check if we need to close the write side */
- if ((s->rep->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)) {
- buffer_shutw(s->rep);
- if (s->req->flags & BF_SHUTR) {
- fd_delete(s->rep->cons->fd);
- s->rep->cons->state = SI_ST_CLO;
- }
- else {
- EV_FD_CLR(s->rep->cons->fd, DIR_WR);
- shutdown(s->rep->cons->fd, SHUT_WR);
- }
- }
-
-
-
/* Dirty trick: force one first pass everywhere */
rqf_cli = rqf_srv = rqf_req = ~s->req->flags;
rpf_cli = rpf_srv = rpf_rep = ~s->rep->flags;
diff --git a/src/stream_sock.c b/src/stream_sock.c
index 14a8df2..3cdf3f2 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -41,8 +41,8 @@
*/
int stream_sock_read(int fd) {
__label__ out_wakeup, out_shutdown_r, out_error;
- struct buffer *b = fdtab[fd].cb[DIR_RD].b;
struct stream_interface *si = fdtab[fd].owner;
+ struct buffer *b = si->ib;
int ret, max, retval, cur_read;
int read_poll = MAX_READ_POLL_LOOPS;
@@ -251,8 +251,9 @@
fdtab[fd].ev &= ~FD_POLL_HUP;
b->flags |= BF_READ_NULL;
buffer_shutr(b);
- /* Maybe we have to completely close the socket */
- if (fdtab[fd].cb[DIR_WR].b->flags & BF_SHUTW)
+
+ /* Maybe we have to completely close the local socket */
+ if (si->ob->flags & BF_SHUTW)
goto do_close_and_return;
EV_FD_CLR(fd, DIR_RD);
goto out_wakeup;
@@ -275,10 +276,10 @@
if (!si->err_type)
si->err_type = SI_ET_DATA_ERR;
- buffer_shutr(fdtab[fd].cb[DIR_RD].b);
- fdtab[fd].cb[DIR_RD].b->flags |= BF_READ_ERROR;
- buffer_shutw(fdtab[fd].cb[DIR_WR].b);
- fdtab[fd].cb[DIR_WR].b->flags |= BF_WRITE_ERROR;
+ buffer_shutr(b);
+ b->flags |= BF_READ_ERROR;
+ buffer_shutw(si->ob);
+ si->ob->flags |= BF_WRITE_ERROR;
do_close_and_return:
fd_delete(fd);
@@ -296,8 +297,8 @@
*/
int stream_sock_write(int fd) {
__label__ out_wakeup, out_error;
- struct buffer *b = fdtab[fd].cb[DIR_WR].b;
struct stream_interface *si = fdtab[fd].owner;
+ struct buffer *b = si->ob;
int ret, max, retval;
int write_poll = MAX_WRITE_POLL_LOOPS;
@@ -390,6 +391,17 @@
if (!b->l) {
b->flags |= BF_EMPTY;
+
+ /* Maybe we just wrote the last chunk and need to close ? */
+ if ((b->flags & (BF_SHUTW|BF_EMPTY|BF_HIJACK|BF_WRITE_ENA|BF_SHUTR)) == (BF_EMPTY|BF_WRITE_ENA|BF_SHUTR)) {
+ if (si->state == SI_ST_EST) {
+ buffer_shutw(b);
+ if (si->ib->flags & BF_SHUTR)
+ goto do_close_and_return;
+ shutdown(fd, SHUT_WR);
+ }
+ }
+
EV_FD_CLR(fd, DIR_WR);
b->wex = TICK_ETERNITY;
goto out_wakeup;
@@ -461,17 +473,41 @@
if (!si->err_type)
si->err_type = SI_ET_DATA_ERR;
- buffer_shutr(fdtab[fd].cb[DIR_RD].b);
- fdtab[fd].cb[DIR_RD].b->flags |= BF_READ_ERROR;
- buffer_shutw(fdtab[fd].cb[DIR_WR].b);
- fdtab[fd].cb[DIR_WR].b->flags |= BF_WRITE_ERROR;
-
+ buffer_shutw(b);
+ b->flags |= BF_WRITE_ERROR;
+ buffer_shutr(si->ib);
+ si->ib->flags |= BF_READ_ERROR;
+ do_close_and_return:
fd_delete(fd);
si->state = SI_ST_CLO;
task_wakeup(si->owner, TASK_WOKEN_IO);
return 1;
}
+/*
+ * This function performs a shutdown-write on a stream interface in a connected
+ * state (it does nothing for other states). It either shuts the write side or
+ * closes the file descriptor and marks itself as closed. No buffer flags are
+ * changed, it's up to the caller to adjust them. The sole purpose of this
+ * function is to be called from the other stream interface to notify of a
+ * close_read, or by itself upon a full write leading to an empty buffer.
+ * It normally returns zero, unless it has completely closed the socket, in
+ * which case it returns 1.
+ */
+int stream_sock_shutw(struct stream_interface *si)
+{
+ if (si->state != SI_ST_EST)
+ return 0;
+
+ if (si->ib->flags & BF_SHUTR) {
+ fd_delete(si->fd);
+ si->state = SI_ST_CLO;
+ return 1;
+ }
+ EV_FD_CLR(si->fd, DIR_WR);
+ shutdown(si->fd, SHUT_WR);
+ return 0;
+}
/*
* This function only has to be called once after a wakeup event during a data