Hi,

Here is an updated relayd socket splicing diff that uses the new
EFBIG feature of maximum splicing lenght.  This way "relayctl show
sessions" displays an updated idle counter immediately after the
whole http content has been transferred.

bluhm

Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.160
diff -u -p -r1.160 relay.c
--- usr.sbin/relayd/relay.c     18 Dec 2012 15:58:25 -0000      1.160
+++ usr.sbin/relayd/relay.c     14 Jan 2013 21:36:44 -0000
@@ -70,9 +70,6 @@ void           relay_input(struct rsession *);
 
 u_int32_t       relay_hash_addr(struct sockaddr_storage *, u_int32_t);
 
-int             relay_splice(struct ctl_relay_event *);
-int             relay_splicelen(struct ctl_relay_event *);
-
 SSL_CTX                *relay_ssl_ctx_create(struct relay *);
 void            relay_ssl_transaction(struct rsession *,
                    struct ctl_relay_event *);
@@ -643,6 +640,7 @@ relay_connected(int fd, short sig, void 
        case RELAY_PROTO_HTTP:
                /* Check the servers's HTTP response */
                if (!RB_EMPTY(&rlay->rl_proto->response_tree)) {
+                       con->se_out.toread = TOREAD_HTTP_HEADER;
                        outrd = relay_read_http;
                        if ((con->se_out.nodes = calloc(proto->response_nodes,
                            sizeof(u_int8_t))) == NULL) {
@@ -699,6 +697,7 @@ relay_input(struct rsession *con)
                /* Check the client's HTTP request */
                if (!RB_EMPTY(&rlay->rl_proto->request_tree) ||
                    proto->lateconnect) {
+                       con->se_in.toread = TOREAD_HTTP_HEADER;
                        inrd = relay_read_http;
                        if ((con->se_in.nodes = calloc(proto->request_nodes,
                            sizeof(u_int8_t))) == NULL) {
@@ -741,10 +740,19 @@ relay_write(struct bufferevent *bev, voi
 {
        struct ctl_relay_event  *cre = (struct ctl_relay_event *)arg;
        struct rsession         *con = cre->con;
+
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
-               con->se_done = 1;
+               goto fail;
        if (con->se_done)
-               relay_close(con, "last write (done)");
+               goto done;
+       if (relay_splice(cre->dst) == -1)
+               goto fail;
+       return;
+ done:
+       relay_close(con, "last write (done)");
+       return;
+ fail:
+       relay_close(con, strerror(errno));
 }
 
 void
@@ -822,11 +830,31 @@ relay_splice(struct ctl_relay_event *cre
            (proto->tcpflags & TCPFLAG_NSPLICE))
                return (0);
 
-       if (cre->bev->readcb != relay_read)
+       if (cre->splicelen >= 0)
                return (0);
 
+       /* still not connected */
+       if (cre->bev == NULL || cre->dst->bev == NULL)
+               return (0);
+
+       if (! (cre->toread == TOREAD_UNLIMITED || cre->toread > 0)) {
+               DPRINTF("%s: session %d: splice dir %d, nothing to read %lld",
+                   __func__, con->se_id, cre->dir, cre->toread);
+               return (0);
+       }
+
+       /* do not splice before buffers have not been completely fushed */
+       if (EVBUFFER_LENGTH(cre->bev->input) ||
+           EVBUFFER_LENGTH(cre->dst->bev->output)) {
+               DPRINTF("%s: session %d: splice dir %d, dirty buffer",
+                   __func__, con->se_id, cre->dir);
+               bufferevent_disable(cre->bev, EV_READ);
+               return (2);
+       }
+
        bzero(&sp, sizeof(sp));
        sp.sp_fd = cre->dst->s;
+       sp.sp_max = cre->toread > 0 ? cre->toread : 0;
        sp.sp_idle = rlay->rl_conf.timeout;
        if (setsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)) == -1) {
                log_debug("%s: session %d: splice dir %d failed: %s",
@@ -834,8 +862,11 @@ relay_splice(struct ctl_relay_event *cre
                return (-1);
        }
        cre->splicelen = 0;
-       DPRINTF("%s: session %d: splice dir %d successful",
-           __func__, con->se_id, cre->dir);
+       bufferevent_enable(cre->bev, EV_READ);
+
+       DPRINTF("%s: session %d: splice dir %d, maximum %lld, successful",
+           __func__, con->se_id, cre->dir, cre->toread);
+
        return (1);
 }
 
@@ -846,19 +877,42 @@ relay_splicelen(struct ctl_relay_event *
        off_t                    len;
        socklen_t                optlen;
 
+       if (cre->splicelen < 0)
+               return (0);
+
        optlen = sizeof(len);
        if (getsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &len, &optlen) == -1) {
                log_debug("%s: session %d: splice dir %d get length failed: %s",
                    __func__, con->se_id, cre->dir, strerror(errno));
                return (-1);
        }
+
+       DPRINTF("%s: session %d: splice dir %d, length %lld",
+           __func__, con->se_id, cre->dir, len);
+
        if (len > cre->splicelen) {
+               if (gettimeofday(&con->se_tv_last, NULL) == -1)
+                       return (-1);
                cre->splicelen = len;
                return (1);
        }
        return (0);
 }
 
+int
+relay_spliceadjust(struct ctl_relay_event *cre)
+{
+       if (cre->splicelen < 0)
+               return (0);
+       if (relay_splicelen(cre) == -1)
+               return (-1);
+       if (cre->splicelen > 0 && cre->toread > 0)
+               cre->toread -= cre->splicelen;
+       cre->splicelen = -1;
+
+       return (1);
+}
+
 void
 relay_error(struct bufferevent *bev, short error, void *arg)
 {
@@ -898,10 +952,18 @@ relay_error(struct bufferevent *bev, sho
                                break;
                        }
                }
+               if (relay_spliceadjust(cre) == -1)
+                       goto fail;
                if (relay_splice(cre) == -1)
                        goto fail;
                return;
        }
+       if (error & EVBUFFER_ERROR && errno == EFBIG) {
+               if (relay_spliceadjust(cre) == -1)
+                       goto fail;
+               bufferevent_enable(cre->bev, EV_READ);
+               return;
+       }
        if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
                bufferevent_disable(bev, EV_READ|EV_WRITE);
 
@@ -974,6 +1036,8 @@ relay_accept(int fd, short event, void *
        con->se_out.con = con;
        con->se_in.splicelen = -1;
        con->se_out.splicelen = -1;
+       con->se_in.toread = TOREAD_UNLIMITED;
+       con->se_out.toread = TOREAD_UNLIMITED;
        con->se_relay = rlay;
        con->se_id = ++relay_conid;
        con->se_relayid = rlay->rl_conf.id;
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.5
diff -u -p -r1.5 relay_http.c
--- usr.sbin/relayd/relay_http.c        27 Nov 2012 05:00:28 -0000      1.5
+++ usr.sbin/relayd/relay_http.c        14 Jan 2013 17:30:42 -0000
@@ -85,7 +85,7 @@ relay_read_http(struct bufferevent *bev,
        if (!size) {
                if (cre->dir == RELAY_DIR_RESPONSE)
                        return;
-               cre->toread = 0;
+               cre->toread = TOREAD_HTTP_HEADER;
                goto done;
        }
 
@@ -317,6 +317,7 @@ relay_read_http(struct bufferevent *bev,
                        return;
                case HTTP_METHOD_CONNECT:
                        /* Data stream */
+                       cre->toread = TOREAD_UNLIMITED;
                        bev->readcb = relay_read;
                        break;
                case HTTP_METHOD_DELETE:
@@ -327,22 +328,25 @@ relay_read_http(struct bufferevent *bev,
                case HTTP_METHOD_PUT:
                case HTTP_METHOD_RESPONSE:
                        /* HTTP request payload */
-                       if (cre->toread) {
+                       if (cre->toread > 0)
                                bev->readcb = relay_read_httpcontent;
-                               break;
-                       }
 
                        /* Single-pass HTTP response */
-                       bev->readcb = relay_read;
+                       if (cre->toread < 0) {
+                               cre->toread = TOREAD_UNLIMITED;
+                               bev->readcb = relay_read;
+                       }
+
                        break;
                default:
                        /* HTTP handler */
+                       cre->toread = TOREAD_HTTP_HEADER;
                        bev->readcb = relay_read_http;
                        break;
                }
                if (cre->chunked) {
                        /* Chunked transfer encoding */
-                       cre->toread = 0;
+                       cre->toread = TOREAD_HTTP_CHUNK_LENGHT;
                        bev->readcb = relay_read_httpchunks;
                }
 
@@ -353,7 +357,7 @@ relay_read_http(struct bufferevent *bev,
                relay_http_request_close(cre);
 
  done:
-               if (cre->dir == RELAY_DIR_REQUEST && !cre->toread &&
+               if (cre->dir == RELAY_DIR_REQUEST && cre->toread < 0 &&
                    proto->lateconnect && cre->dst->bev == NULL) {
                        if (rlay->rl_conf.fwdmode == FWD_TRANS) {
                                relay_bindanyreq(con, 0, IPPROTO_TCP);
@@ -371,6 +375,8 @@ relay_read_http(struct bufferevent *bev,
        if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http)
                bev->readcb(bev, arg);
        bufferevent_enable(bev, EV_READ);
+       if (relay_splice(cre) == -1)
+               relay_close(con, strerror(errno));
        return;
  fail:
        relay_abort_http(con, 500, strerror(errno), 0);
@@ -390,17 +396,33 @@ relay_read_httpcontent(struct buffereven
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
                goto fail;
        size = EVBUFFER_LENGTH(src);
-       DPRINTF("%s: size %lu, to read %lld", __func__,
-           size, cre->toread);
+       DPRINTF("%s: dir %d, size %lu, to read %lld",
+           __func__, cre->dir, size, cre->toread);
        if (!size)
                return;
-       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+       if (relay_spliceadjust(cre) == -1)
                goto fail;
-       if ((off_t)size >= cre->toread)
+
+       if (cre->toread > 0) {
+               /* Read content data */
+               if ((off_t)size > cre->toread) {
+                       size = cre->toread;
+                       if (relay_bufferevent_write_chunk(cre->dst, src, size)
+                           == -1)
+                               goto fail;
+                       cre->toread = 0;
+               } else {
+                       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+                               goto fail;
+                       cre->toread -= size;
+               }
+               DPRINTF("%s: done, size %lu, to read %lld", __func__,
+                   size, cre->toread);
+       }
+       if (cre->toread == 0) {
+               cre->toread = TOREAD_HTTP_HEADER;
                bev->readcb = relay_read_http;
-       cre->toread -= size;
-       DPRINTF("%s: done, size %lu, to read %lld", __func__,
-           size, cre->toread);
+       }
        if (con->se_done)
                goto done;
        if (bev->readcb != relay_read_httpcontent)
@@ -427,19 +449,37 @@ relay_read_httpchunks(struct bufferevent
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
                goto fail;
        size = EVBUFFER_LENGTH(src);
-       DPRINTF("%s: size %lu, to read %lld", __func__,
-           size, cre->toread);
+       DPRINTF("%s: dir %d, size %lu, to read %lld",
+           __func__, cre->dir, size, cre->toread);
        if (!size)
                return;
+       if (relay_spliceadjust(cre) == -1)
+               goto fail;
 
-       if (!cre->toread) {
+       if (cre->toread > 0) {
+               /* Read chunk data */
+               if ((off_t)size > cre->toread) {
+                       size = cre->toread;
+                       if (relay_bufferevent_write_chunk(cre->dst, src, size)
+                           == -1)
+                               goto fail;
+                       cre->toread = 0;
+               } else {
+                       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+                               goto fail;
+                       cre->toread -= size;
+               }
+               DPRINTF("%s: done, size %lu, to read %lld", __func__,
+                   size, cre->toread);
+       }
+       if (cre->toread == TOREAD_HTTP_CHUNK_LENGHT) {
                line = evbuffer_readline(src);
                if (line == NULL) {
                        /* Ignore empty line, continue */
                        bufferevent_enable(bev, EV_READ);
                        return;
                }
-               if (!strlen(line)) {
+               if (strlen(line) == 0) {
                        free(line);
                        goto next;
                }
@@ -458,40 +498,38 @@ relay_read_httpchunks(struct bufferevent
                }
                free(line);
 
-               /* Last chunk is 0 bytes followed by an empty newline */
+               /* Last chunk is 0 bytes followed by optional trailer */
                if ((cre->toread = lval) == 0) {
                        DPRINTF("%s: last chunk", __func__);
-
-                       line = evbuffer_readline(src);
-                       if (line == NULL) {
-                               relay_close(con, "invalid last chunk");
-                               return;
-                       }
+                       cre->toread = TOREAD_HTTP_CHUNK_TRAILER;
+               }
+       } else if (cre->toread == TOREAD_HTTP_CHUNK_TRAILER) {
+               /* Last chunk is 0 bytes followed by trailer and empty line */
+               line = evbuffer_readline(src);
+               if (line == NULL) {
+                       /* Ignore empty line, continue */
+                       bufferevent_enable(bev, EV_READ);
+                       return;
+               }
+               if (relay_bufferevent_print(cre->dst, line) == -1 ||
+                   relay_bufferevent_print(cre->dst, "\r\n") == -1) {
                        free(line);
-                       if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
-                               goto fail;
-
+                       goto fail;
+               }
+               if (strlen(line) == 0) {
                        /* Switch to HTTP header mode */
+                       cre->toread = TOREAD_HTTP_HEADER;
                        bev->readcb = relay_read_http;
                }
-       } else {
-               /* Read chunk data */
-               if ((off_t)size > cre->toread)
-                       size = cre->toread;
-               if (relay_bufferevent_write_chunk(cre->dst, src, size) == -1)
+               free(line);
+       } else if (cre->toread == 0) {
+               /* Chunk is terminated by an empty newline */
+               line = evbuffer_readline(src);
+               if (line != NULL)
+                       free(line);
+               if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
                        goto fail;
-               cre->toread -= size;
-               DPRINTF("%s: done, size %lu, to read %lld", __func__,
-                   size, cre->toread);
-
-               if (cre->toread == 0) {
-                       /* Chunk is terminated by an empty (empty) newline */
-                       line = evbuffer_readline(src);
-                       if (line != NULL)
-                               free(line);
-                       if (relay_bufferevent_print(cre->dst, "\r\n\r\n") == -1)
-                               goto fail;
-               }
+               cre->toread = TOREAD_HTTP_CHUNK_LENGHT;
        }
 
  next:
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.163
diff -u -p -r1.163 relayd.h
--- usr.sbin/relayd/relayd.h    27 Nov 2012 05:00:28 -0000      1.163
+++ usr.sbin/relayd/relayd.h    14 Jan 2013 17:30:42 -0000
@@ -196,6 +196,11 @@ struct ctl_relay_event {
        int                      buflen;
 };
 
+#define        TOREAD_UNLIMITED                -1
+#define        TOREAD_HTTP_HEADER              -2
+#define        TOREAD_HTTP_CHUNK_LENGHT        -3
+#define        TOREAD_HTTP_CHUNK_TRAILER       -4
+
 struct ctl_natlook {
        objid_t                  id;
        int                      proc;
@@ -988,6 +993,9 @@ int  relay_cmp_af(struct sockaddr_storag
            struct sockaddr_storage *);
 void    relay_write(struct bufferevent *, void *);
 void    relay_read(struct bufferevent *, void *);
+int     relay_splice(struct ctl_relay_event *);
+int     relay_splicelen(struct ctl_relay_event *);
+int     relay_spliceadjust(struct ctl_relay_event *);
 void    relay_error(struct bufferevent *, short, void *);
 int     relay_lognode(struct rsession *,
            struct protonode *, struct protonode *, char *, size_t);

Reply via email to