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);