[PATCH 2/2] On return from waitinglist, do a poll() on the socket to see if the client has closed the connection and gone away. If so, release the session early.
Martin Blix Grydeland
martin at varnish-software.com
Thu Feb 14 22:45:49 CET 2013
Fixes: #1252
---
bin/varnishd/cache/cache_hash.c | 25 +++++++++++++---
bin/varnishd/cache/cache_http1_fsm.c | 14 +++++++++
bin/varnishd/hash/hash_slinger.h | 1 +
bin/varnishtest/tests.disabled/r01252.vtc | 44 +++++++++++++++++++++++++++++
include/vtcp.h | 1 +
lib/libvarnish/vtcp.c | 19 +++++++++++++
6 files changed, 100 insertions(+), 4 deletions(-)
create mode 100644 bin/varnishtest/tests.disabled/r01252.vtc
diff --git a/bin/varnishd/cache/cache_hash.c b/bin/varnishd/cache/cache_hash.c
index d0d987e..db906ec 100644
--- a/bin/varnishd/cache/cache_hash.c
+++ b/bin/varnishd/cache/cache_hash.c
@@ -399,7 +399,7 @@ HSH_Lookup(struct req *req)
assert(oc->objhead == oh);
oc->refcnt++;
Lck_Unlock(&oh->mtx);
- assert(hash->deref(oh));
+ assert(HSH_DerefObjHead(&wrk->stats, &oh));
o = oc_getobj(&wrk->stats, oc);
CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
if (!cache_param->obj_readonly && o->hits < INT_MAX)
@@ -694,13 +694,30 @@ HSH_Deref(struct dstat *ds, struct objcore *oc, struct object **oo)
if (oh != NULL) {
/* Drop our ref on the objhead */
assert(oh->refcnt > 0);
- if (hash->deref(oh))
- return (0);
- HSH_DeleteObjHead(ds, oh);
+ (void)HSH_DerefObjHead(ds, &oh);
}
return (0);
}
+int
+HSH_DerefObjHead(struct dstat *ds, struct objhead **poh)
+{
+ struct objhead *oh;
+ int r;
+
+ AN(ds);
+ AN(poh);
+ oh = *poh;
+ *poh = NULL;
+ CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+
+ assert(oh->refcnt > 0);
+ r = hash->deref(oh);
+ if (!r)
+ HSH_DeleteObjHead(ds, oh);
+ return (r);
+}
+
void
HSH_Init(const struct hash_slinger *slinger)
{
diff --git a/bin/varnishd/cache/cache_http1_fsm.c b/bin/varnishd/cache/cache_http1_fsm.c
index 2f99c6e..ecc3583 100644
--- a/bin/varnishd/cache/cache_http1_fsm.c
+++ b/bin/varnishd/cache/cache_http1_fsm.c
@@ -74,6 +74,7 @@
#include <stdlib.h>
#include "cache.h"
+#include "hash/hash_slinger.h"
#include "vcl.h"
#include "vct.h"
@@ -329,6 +330,19 @@ HTTP1_Session(struct worker *wrk, struct req *req)
return;
}
+ /*
+ * Return from waitinglist. Check to see if the remote has left.
+ */
+ if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) {
+ AN(req->hash_objhead);
+ (void)HSH_DerefObjHead(&wrk->stats, &req->hash_objhead);
+ AZ(req->hash_objhead);
+ SES_Close(sp, SC_REM_CLOSE);
+ sdr = http1_cleanup(sp, wrk, req);
+ assert(sdr == SESS_DONE_RET_GONE);
+ return;
+ }
+
if (sp->sess_step == S_STP_NEWREQ) {
HTTP1_Init(req->htc, req->ws, sp->fd, req->vsl,
cache_param->http_req_size,
diff --git a/bin/varnishd/hash/hash_slinger.h b/bin/varnishd/hash/hash_slinger.h
index c385ea6..f90f592 100644
--- a/bin/varnishd/hash/hash_slinger.h
+++ b/bin/varnishd/hash/hash_slinger.h
@@ -98,6 +98,7 @@ struct objhead {
void HSH_Unbusy(struct dstat *, struct objcore *);
void HSH_Complete(struct objcore *oc);
void HSH_DeleteObjHead(struct dstat *, struct objhead *oh);
+int HSH_DerefObjHead(struct dstat *, struct objhead **poh);
int HSH_Deref(struct dstat *, struct objcore *oc, struct object **o);
#endif /* VARNISH_CACHE_CHILD */
diff --git a/bin/varnishtest/tests.disabled/r01252.vtc b/bin/varnishtest/tests.disabled/r01252.vtc
new file mode 100644
index 0000000..c988da6
--- /dev/null
+++ b/bin/varnishtest/tests.disabled/r01252.vtc
@@ -0,0 +1,44 @@
+varnishtest "#1252 - Drop remote closed connections returning from waitinglists"
+
+# This test case is disabled because it will only pass on platforms
+# where the tcp_keepalive_* runtime arguments are available, and also
+# because it requires "-t 75" argument to varnishtest (remote closed
+# state will only be detected after FIN timeout has passed (60s))
+
+server s1 {
+ rxreq
+ expect req.http.X-Client == "1"
+ sema r1 sync 2
+ delay 75
+ close
+} -start
+
+server s2 {
+ rxreq
+ expect req.url == "/should/not/happen"
+ txresp
+} -start
+
+varnish v1 -arg "-p debug=+waitinglist -p tcp_keepalive_time=1s -p tcp_keepalive_probes=1 -p tcp_keepalive_intvl=1s -p first_byte_timeout=70" -vcl+backend {
+ sub vcl_recv {
+ if (req.http.x-client == "2") {
+ set req.backend = s2;
+ }
+ }
+} -start
+
+client c1 {
+ timeout 70
+ txreq -hdr "X-Client: 1"
+ rxresp
+ expect resp.status == 503
+} -start
+
+client c2 {
+ sema r1 sync 2
+ txreq -hdr "X-Client: 2"
+ delay 1
+} -start
+
+client c1 -wait
+client c2 -wait
diff --git a/include/vtcp.h b/include/vtcp.h
index 77f86ed..1594a4d 100644
--- a/include/vtcp.h
+++ b/include/vtcp.h
@@ -62,6 +62,7 @@ int VTCP_filter_http(int sock);
int VTCP_blocking(int sock);
int VTCP_nonblocking(int sock);
int VTCP_linger(int sock, int linger);
+int VTCP_check_hup(int sock);
#ifdef SOL_SOCKET
int VTCP_port(const struct sockaddr_storage *addr);
diff --git a/lib/libvarnish/vtcp.c b/lib/libvarnish/vtcp.c
index 2c6dd3f..7227fb3 100644
--- a/lib/libvarnish/vtcp.c
+++ b/lib/libvarnish/vtcp.c
@@ -308,3 +308,22 @@ VTCP_linger(int sock, int linger)
VTCP_Assert(i);
return (i);
}
+
+/*--------------------------------------------------------------------
+ * Do a poll to check for remote HUP
+ */
+
+int
+VTCP_check_hup(int sock)
+{
+ struct pollfd pfd;
+
+ assert(sock > 0);
+ pfd.fd = sock;
+ pfd.events = POLLOUT;
+ pfd.revents = 0;
+
+ if (poll(&pfd, 1, 0) == 1 && pfd.revents & POLLHUP)
+ return (1);
+ return (0);
+}
--
1.7.10.4
More information about the varnish-dev
mailing list