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

Synthesize a 503 when we fail to fetch, and pass it on.

parent cfbc1691
...@@ -486,7 +486,8 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -486,7 +486,8 @@ vbf_fetch_body(struct worker *wrk, void *priv)
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CAST_OBJ_NOTNULL(bo, priv, BUSYOBJ_MAGIC); CAST_OBJ_NOTNULL(bo, priv, BUSYOBJ_MAGIC);
CHECK_OBJ_NOTNULL(bo->vbc, VBC_MAGIC); htc = &bo->htc;
CHECK_OBJ_ORNULL(bo->vbc, VBC_MAGIC);
obj = bo->fetch_obj; obj = bo->fetch_obj;
CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC); CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC);
...@@ -500,7 +501,6 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -500,7 +501,6 @@ vbf_fetch_body(struct worker *wrk, void *priv)
AZ(bo->stats); AZ(bo->stats);
bo->stats = &wrk->stats; bo->stats = &wrk->stats;
htc = &bo->htc;
if (bo->vfp == NULL) if (bo->vfp == NULL)
bo->vfp = &vfp_nop; bo->vfp = &vfp_nop;
...@@ -513,7 +513,7 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -513,7 +513,7 @@ vbf_fetch_body(struct worker *wrk, void *priv)
/* XXX: pick up estimate from objdr ? */ /* XXX: pick up estimate from objdr ? */
cl = 0; cl = 0;
cls = 0; cls = bo->should_close;
switch (htc->body_status) { switch (htc->body_status) {
case BS_NONE: case BS_NONE:
mklen = 0; mklen = 0;
...@@ -526,7 +526,7 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -526,7 +526,7 @@ vbf_fetch_body(struct worker *wrk, void *priv)
bo->vfp->begin(bo, cl); bo->vfp->begin(bo, cl);
if (bo->state == BOS_FETCHING && cl > 0) if (bo->state == BOS_FETCHING && cl > 0)
cls = vbf_fetch_straight(bo, htc, cl); cls |= vbf_fetch_straight(bo, htc, cl);
mklen = 1; mklen = 1;
if (bo->vfp->end(bo)) if (bo->vfp->end(bo))
assert(bo->state == BOS_FAILED); assert(bo->state == BOS_FAILED);
...@@ -534,7 +534,7 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -534,7 +534,7 @@ vbf_fetch_body(struct worker *wrk, void *priv)
case BS_CHUNKED: case BS_CHUNKED:
bo->vfp->begin(bo, cl > 0 ? cl : 0); bo->vfp->begin(bo, cl > 0 ? cl : 0);
if (bo->state == BOS_FETCHING) if (bo->state == BOS_FETCHING)
cls = vbf_fetch_chunked(bo, htc); cls |= vbf_fetch_chunked(bo, htc);
mklen = 1; mklen = 1;
if (bo->vfp->end(bo)) if (bo->vfp->end(bo))
assert(bo->state == BOS_FAILED); assert(bo->state == BOS_FAILED);
...@@ -549,11 +549,10 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -549,11 +549,10 @@ vbf_fetch_body(struct worker *wrk, void *priv)
assert(bo->state == BOS_FAILED); assert(bo->state == BOS_FAILED);
break; break;
case BS_ERROR: case BS_ERROR:
cls = VBF_Error(bo, "error incompatible Transfer-Encoding"); cls |= VBF_Error(bo, "error incompatible Transfer-Encoding");
mklen = 0; mklen = 0;
break; break;
default: default:
cls = 0;
mklen = 0; mklen = 0;
INCOMPL(); INCOMPL();
} }
...@@ -574,18 +573,21 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -574,18 +573,21 @@ vbf_fetch_body(struct worker *wrk, void *priv)
http_Teardown(bo->bereq); http_Teardown(bo->bereq);
http_Teardown(bo->beresp); http_Teardown(bo->beresp);
if (bo->vbc != NULL) {
if (cls)
VDI_CloseFd(&bo->vbc);
else
VDI_RecycleFd(&bo->vbc);
}
if (bo->state == BOS_FAILED) { if (bo->state == BOS_FAILED) {
wrk->stats.fetch_failed++; wrk->stats.fetch_failed++;
VDI_CloseFd(&bo->vbc);
obj->len = 0; obj->len = 0;
EXP_Clr(&obj->exp); EXP_Clr(&obj->exp);
EXP_Rearm(obj); EXP_Rearm(obj);
} else { } else {
assert(bo->state == BOS_FETCHING); assert(bo->state == BOS_FETCHING);
if (cls == 0 && bo->should_close)
cls = 1;
VSLb(bo->vsl, SLT_Length, "%zd", obj->len); VSLb(bo->vsl, SLT_Length, "%zd", obj->len);
{ {
...@@ -609,12 +611,6 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -609,12 +611,6 @@ vbf_fetch_body(struct worker *wrk, void *priv)
"Content-Length: %zd", obj->len); "Content-Length: %zd", obj->len);
} }
if (cls)
VDI_CloseFd(&bo->vbc);
else
VDI_RecycleFd(&bo->vbc);
/* XXX: Atomic assignment, needs volatile/membar ? */ /* XXX: Atomic assignment, needs volatile/membar ? */
bo->state = BOS_FINISHED; bo->state = BOS_FINISHED;
} }
...@@ -627,15 +623,14 @@ vbf_fetch_body(struct worker *wrk, void *priv) ...@@ -627,15 +623,14 @@ vbf_fetch_body(struct worker *wrk, void *priv)
* Copy req->bereq and run it by VCL::vcl_backend_fetch{} * Copy req->bereq and run it by VCL::vcl_backend_fetch{}
*/ */
static void static enum fetch_step
vbf_make_bereq(struct worker *wrk, const struct req *req, struct busyobj *bo) vbf_stp_mkbereq(struct worker *wrk, struct busyobj *bo, const struct req *req)
{ {
int i; int i;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC);
AN(bo->director); AN(bo->director);
AZ(bo->vbc); AZ(bo->vbc);
...@@ -666,18 +661,54 @@ vbf_make_bereq(struct worker *wrk, const struct req *req, struct busyobj *bo) ...@@ -666,18 +661,54 @@ vbf_make_bereq(struct worker *wrk, const struct req *req, struct busyobj *bo)
http_PrintfHeader(bo->bereq, http_PrintfHeader(bo->bereq,
"X-Varnish: %u", bo->vsl->wid & VSL_IDENTMASK); "X-Varnish: %u", bo->vsl->wid & VSL_IDENTMASK);
return (F_STP_FETCHHDR);
} }
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
*/ */
static void static enum fetch_step
vbf_proc_resp(struct worker *wrk, struct busyobj *bo) vbf_stp_fetchhdr(struct worker *wrk, struct busyobj *bo, struct req **reqp)
{ {
int i; int i;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
AN(reqp);
CHECK_OBJ_NOTNULL((*reqp), REQ_MAGIC);
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
xxxassert (wrk->handling == VCL_RET_FETCH);
HTTP_Setup(bo->beresp, bo->ws, bo->vsl, HTTP_Beresp);
if (!bo->do_pass)
*reqp = NULL;
i = vbf_fetch_hdr(wrk, bo, *reqp);
/*
* If we recycle a backend connection, there is a finite chance
* that the backend closed it before we get a request to it.
* Do a single retry in that case.
*/
if (i == 1) {
VSC_C_main->backend_retry++;
i = vbf_fetch_hdr(wrk, bo, *reqp);
}
if (bo->do_pass)
*reqp = NULL;
if (i) {
AZ(bo->vbc);
bo->err_code = 503;
http_SetH(bo->beresp, HTTP_HDR_PROTO, "HTTP/1.1");
http_SetResp(bo->beresp,
"HTTP/1.1", 503, "Backend fetch failed");
http_SetHeader(bo->beresp, "Content-Length: 0");
http_SetHeader(bo->beresp, "Connection: close");
} else {
AN(bo->vbc);
}
/* /*
* These two headers can be spread over multiple actual headers * These two headers can be spread over multiple actual headers
* and we rely on their content outside of VCL, so collect them * and we rely on their content outside of VCL, so collect them
...@@ -727,6 +758,7 @@ vbf_proc_resp(struct worker *wrk, struct busyobj *bo) ...@@ -727,6 +758,7 @@ vbf_proc_resp(struct worker *wrk, struct busyobj *bo)
* no Content-Encoding --> object is not gzip'ed. * no Content-Encoding --> object is not gzip'ed.
* anything else --> do nothing wrt gzip * anything else --> do nothing wrt gzip
* *
* XXX: BS_NONE/cl==0 should avoid gzip/gunzip
*/ */
/* We do nothing unless the param is set */ /* We do nothing unless the param is set */
...@@ -769,17 +801,18 @@ vbf_proc_resp(struct worker *wrk, struct busyobj *bo) ...@@ -769,17 +801,18 @@ vbf_proc_resp(struct worker *wrk, struct busyobj *bo)
else if (bo->is_gzip) else if (bo->is_gzip)
bo->vfp = &vfp_testgzip; bo->vfp = &vfp_testgzip;
if (wrk->handling != VCL_RET_DELIVER)
return (F_STP_NOTYET);
return (F_STP_FETCH);
} }
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
*/ */
static enum fetch_step static enum fetch_step
vbf_stp_fetch(struct worker *wrk, struct busyobj *bo, struct req **reqp) vbf_stp_fetch(struct worker *wrk, struct busyobj *bo)
{ {
int i;
struct http *hp, *hp2; struct http *hp, *hp2;
struct req *req;
char *b; char *b;
uint16_t nhttp; uint16_t nhttp;
unsigned l; unsigned l;
...@@ -789,48 +822,10 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo, struct req **reqp) ...@@ -789,48 +822,10 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo, struct req **reqp)
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
AN(reqp);
req = *reqp;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
vbf_make_bereq(wrk, req, bo); if (wrk->handling != VCL_RET_DELIVER)
xxxassert (wrk->handling == VCL_RET_FETCH); VDI_CloseFd(&bo->vbc);
HTTP_Setup(bo->beresp, bo->ws, bo->vsl, HTTP_Beresp);
if (!bo->do_pass) {
AN(req);
req = NULL;
*reqp = NULL;
}
i = vbf_fetch_hdr(wrk, bo, req);
/*
* If we recycle a backend connection, there is a finite chance
* that the backend closed it before we get a request to it.
* Do a single retry in that case.
*/
if (i == 1) {
VSC_C_main->backend_retry++;
i = vbf_fetch_hdr(wrk, bo, req);
}
if (bo->do_pass) {
AN(req);
req = NULL;
*reqp = NULL;
}
AZ(req);
if (i) {
wrk->handling = VCL_RET_ERROR;
bo->err_code = 503;
} else {
vbf_proc_resp(wrk, bo);
if (wrk->handling != VCL_RET_DELIVER)
VDI_CloseFd(&bo->vbc);
}
if (wrk->handling != VCL_RET_DELIVER) { if (wrk->handling != VCL_RET_DELIVER) {
/* Clean up partial fetch */ /* Clean up partial fetch */
...@@ -1002,6 +997,12 @@ vbf_stp_abandon(struct worker *wrk, struct busyobj *bo) ...@@ -1002,6 +997,12 @@ vbf_stp_abandon(struct worker *wrk, struct busyobj *bo)
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
*/ */
static enum fetch_step
vbf_stp_notyet(void)
{
WRONG("Patience, grashopper, patience...");
}
static enum fetch_step static enum fetch_step
vbf_stp_done(void) vbf_stp_done(void)
{ {
...@@ -1033,7 +1034,7 @@ vbf_fetch_thread(struct worker *wrk, void *priv) ...@@ -1033,7 +1034,7 @@ vbf_fetch_thread(struct worker *wrk, void *priv)
bo = vsh->bo; bo = vsh->bo;
THR_SetBusyobj(bo); THR_SetBusyobj(bo);
bo->step = F_STP_FETCH; bo->step = F_STP_MKBEREQ;
while (bo->step != F_STP_DONE) { while (bo->step != F_STP_DONE) {
switch(bo->step) { switch(bo->step) {
......
...@@ -15,36 +15,51 @@ client c1 { ...@@ -15,36 +15,51 @@ client c1 {
expect resp.http.X-varnish == "1001" expect resp.http.X-varnish == "1001"
} -run } -run
delay .1
client c1 { client c1 {
txreq -url "/" txreq -url "/"
rxresp rxresp
expect resp.status == 503 expect resp.status == 503
expect resp.http.X-varnish == "1005" expect resp.http.X-varnish == "1004"
} -run } -run
# Then check that an cacheable error from the backend is delay .1
# Then check that a cacheable error from the backend is
varnish v1 -cliok "ban req.url ~ .*"
server s1 { server s1 {
rxreq rxreq
txresp -status 302 txresp -status 302
} -start } -start
varnish v1 -vcl+backend { } varnish v1 -vcl+backend {
sub vcl_backend_response {
set beresp.http.ttl = beresp.ttl;
set beresp.http.uncacheable = beresp.uncacheable;
}
}
client c1 { client c1 {
txreq -url "/" txreq -url "/"
rxresp rxresp
expect resp.status == 302 expect resp.status == 302
expect resp.http.X-varnish == "1009" expect resp.http.X-varnish == "1007"
} -run } -run
delay .1
client c1 { client c1 {
txreq -url "/" txreq -url "/"
rxresp rxresp
expect resp.status == 302 expect resp.status == 302
expect resp.http.X-varnish == "1012 1010" expect resp.http.X-varnish == "1010 1008"
} -run } -run
delay .1
# Then check that a non-cacheable error from the backend can be # Then check that a non-cacheable error from the backend can be
server s1 { server s1 {
...@@ -64,12 +79,16 @@ client c1 { ...@@ -64,12 +79,16 @@ client c1 {
txreq -url "/2" txreq -url "/2"
rxresp rxresp
expect resp.status == 502 expect resp.status == 502
expect resp.http.X-varnish == "1014" expect resp.http.X-varnish == "1012"
} -run } -run
delay .1
client c1 { client c1 {
txreq -url "/2" txreq -url "/2"
rxresp rxresp
expect resp.status == 502 expect resp.status == 502
expect resp.http.X-varnish == "1017 1015" expect resp.http.X-varnish == "1015 1013"
} -run } -run
delay .1
...@@ -5,21 +5,10 @@ server s1 { ...@@ -5,21 +5,10 @@ server s1 {
txresp txresp
} -start } -start
server s2 { varnish v1 -vcl+backend {
} -start
varnish v1 -vcl {
backend bad {
.host = "${s2_addr}";
.port = "${s2_port}";
}
backend good {
.host = "${s1_addr}";
.port = "${s1_port}";
}
sub vcl_recv { sub vcl_recv {
if (req.restarts > 0) { if (req.restarts == 0) {
set req.backend = good; return (error(701, "FOO"));
} }
} }
sub vcl_error { sub vcl_error {
......
...@@ -50,8 +50,11 @@ REQ_STEP(error, ERROR, (wrk, req)) ...@@ -50,8 +50,11 @@ REQ_STEP(error, ERROR, (wrk, req))
#endif #endif
#ifdef FETCH_STEP #ifdef FETCH_STEP
FETCH_STEP(fetch, FETCH, (wrk, bo, reqp)) FETCH_STEP(mkbereq, MKBEREQ, (wrk, bo, *reqp))
FETCH_STEP(fetchhdr, FETCHHDR, (wrk, bo, reqp))
FETCH_STEP(fetch, FETCH, (wrk, bo))
FETCH_STEP(abandon, ABANDON, (wrk, bo)) FETCH_STEP(abandon, ABANDON, (wrk, bo))
FETCH_STEP(notyet, NOTYET, ())
FETCH_STEP(done, DONE, ()) FETCH_STEP(done, DONE, ())
#endif #endif
......
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