Commit b253bf9c authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

When a streaming delivery of a pass object is terminated prematurely,

we cannot just throw the storage away, we have to wait for the
fetch-thread to go away, possibly in response to a new "abandon"
signal.

Spotted first by:	scoof
Fixes	#1391
parent 781fb81d
...@@ -573,6 +573,7 @@ struct busyobj { ...@@ -573,6 +573,7 @@ struct busyobj {
/* do_pass is our intent, uncacheable is the result */ /* do_pass is our intent, uncacheable is the result */
unsigned do_pass; unsigned do_pass;
unsigned uncacheable; unsigned uncacheable;
unsigned abandon;
/* Timeouts */ /* Timeouts */
double connect_timeout; double connect_timeout;
......
...@@ -201,6 +201,19 @@ VFP_Fetch_Body(struct busyobj *bo, ssize_t est) ...@@ -201,6 +201,19 @@ VFP_Fetch_Body(struct busyobj *bo, ssize_t est)
} }
do { do {
if (bo->abandon) {
/*
* A pass object and delivery was terminted
* We don't fail the fetch, in order for hit-for-pass
* objects to be created.
*/
AN(bo->fetch_objcore->flags & OC_F_PASS);
VSLb(bo->vsl, SLT_FetchError,
"Pass delivery abandonned");
vfps = VFP_END;
bo->should_close = 1;
break;
}
assert(bo->state != BOS_FAILED); assert(bo->state != BOS_FAILED);
if (st == NULL) { if (st == NULL) {
st = VFP_GetStorage(bo, est); st = VFP_GetStorage(bo, est);
......
...@@ -170,11 +170,13 @@ v1d_WriteDirObj(struct req *req) ...@@ -170,11 +170,13 @@ v1d_WriteDirObj(struct req *req)
while (1) { while (1) {
ois = ObjIter(oi, &ptr, &len); ois = ObjIter(oi, &ptr, &len);
if (ois == OIS_DATA && !VDP_bytes(req, VDP_NULL, ptr, len)) if (ois == OIS_DONE) {
continue; AZ(len);
if (ois == OIS_STREAM && !VDP_bytes(req, VDP_FLUSH, ptr, len)) break;
continue; }
break; if (VDP_bytes(req,
ois == OIS_DATA ? VDP_NULL : VDP_FLUSH, ptr, len))
break;
} }
(void)VDP_bytes(req, VDP_FINISH, NULL, 0); (void)VDP_bytes(req, VDP_FINISH, NULL, 0);
ObjIterEnd(&oi); ObjIterEnd(&oi);
......
...@@ -126,8 +126,11 @@ ObjIterEnd(struct objiter **oi) ...@@ -126,8 +126,11 @@ ObjIterEnd(struct objiter **oi)
AN(oi); AN(oi);
CHECK_OBJ_NOTNULL((*oi), OBJITER_MAGIC); CHECK_OBJ_NOTNULL((*oi), OBJITER_MAGIC);
CHECK_OBJ_NOTNULL((*oi)->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL((*oi)->obj, OBJECT_MAGIC);
if ((*oi)->bo != NULL) if ((*oi)->bo != NULL) {
if ((*oi)->obj->objcore->flags & OC_F_PASS)
(*oi)->bo->abandon = 1;
VBO_DerefBusyObj((*oi)->wrk, &(*oi)->bo); VBO_DerefBusyObj((*oi)->wrk, &(*oi)->bo);
}
FREE_OBJ((*oi)); FREE_OBJ((*oi));
*oi = NULL; *oi = NULL;
} }
...@@ -159,9 +159,16 @@ cnt_deliver(struct worker *wrk, struct req *req) ...@@ -159,9 +159,16 @@ cnt_deliver(struct worker *wrk, struct req *req)
V1D_Deliver(req); V1D_Deliver(req);
/* No point in saving the body if it is hit-for-pass */ if (req->obj->objcore->flags & OC_F_PASS) {
if (req->obj->objcore->flags & OC_F_PASS) /*
* No point in saving the body if it is hit-for-pass,
* but we can't yank it until the fetching thread has
* finished/abandonned also.
*/
while (req->obj->objcore->busyobj != NULL)
(void)usleep(100000);
STV_Freestore(req->obj); STV_Freestore(req->obj);
}
assert(WRW_IsReleased(wrk)); assert(WRW_IsReleased(wrk));
VSLb(req->vsl, SLT_Debug, "XXX REF %d", req->obj->objcore->refcnt); VSLb(req->vsl, SLT_Debug, "XXX REF %d", req->obj->objcore->refcnt);
......
varnishtest "client abandoning hit-for-pass"
server s1 {
rxreq
txresp -nolen -hdr "Transfer-Encoding: chunked" -hdr "Set-Cookie: foo=bar"
chunked "foo"
sema r1 sync 2
chunked "bar"
delay .1
chunkedlen 64
delay .1
chunkedlen 64
chunkedlen 0
} -start
varnish v1 -vcl+backend {
} -start
client c1 {
txreq
rxhdrs
rxchunk
sema r1 sync 2
} -run
delay 2
server s1 {
rxreq
txresp
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
} -run
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment