static void cnt_vdp(struct req *req, struct busyobj *bo) { const char *r; uint16_t status; int wantbody; CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); req->res_mode = 0; wantbody = 1; status = http_GetStatus(req->resp); if (!strcmp(req->http0->hd[HTTP_HDR_METHOD].b, "HEAD")) { wantbody = 0; } else if (status < 200 || status == 204) { req->resp_len = 0; http_Unset(req->resp, H_Content_Length); wantbody = 0; } else if (status == 304) { http_Unset(req->resp, H_Content_Length); wantbody = 0; } else if (bo != NULL) req->resp_len = http_GetContentLength(req->resp); else req->resp_len = ObjGetLen(req->wrk, req->objcore); /* * Determine ESI status first. Not dependent on wantbody, because * we want ESI to supress C-L in HEAD too. */ if (!req->disable_esi && req->resp_len != 0 && wantbody && ObjGetattr(req->wrk, req->objcore, OA_ESIDATA, NULL) != NULL) { req->res_mode |= RES_ESI; RFC2616_Weaken_Etag(req->resp); req->resp_len = -1; VDP_push(req, VDP_ESI, NULL, 0); } if (cache_param->http_gzip_support && ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && !RFC2616_Req_Gzip(req->http)) { req->res_mode |= RES_GUNZIP; VDP_push(req, VDP_gunzip, NULL, 1); } /* * Range comes after the others and pushes on bottom because * it can (maybe) generate a correct C-L header. */ if (cache_param->http_range_support && http_IsStatus(req->resp, 200)) { http_SetHeader(req->resp, "Accept-Ranges: bytes"); if (wantbody && http_GetHdr(req->http, H_Range, &r)) VRG_dorange(req, r); } req->transport->deliver(req, bo, wantbody); }
static enum fetch_step vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) { const char *p; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC); assert(wrk->handling == VCL_RET_DELIVER); if (vbf_figure_out_vfp(bo)) { (bo)->htc->doclose = SC_OVERLOAD; VDI_Finish((bo)->wrk, bo); return (F_STP_ERROR); } if (bo->fetch_objcore->flags & OC_F_PRIVATE) AN(bo->uncacheable); bo->fetch_objcore->boc->len_so_far = 0; if (VFP_Open(bo->vfc)) { (void)VFP_Error(bo->vfc, "Fetch pipeline failed to open"); bo->htc->doclose = SC_RX_BODY; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); bo->htc->doclose = SC_RX_BODY; VFP_Close(bo->vfc); VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } if (bo->do_esi) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_ESIPROC, 1); if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_GZIPED, 1); if (bo->do_gzip || bo->do_gunzip) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_CHGGZIP, 1); if (!(bo->fetch_objcore->flags & OC_F_PASS) && http_IsStatus(bo->beresp, 200) && ( http_GetHdr(bo->beresp, H_Last_Modified, &p) || http_GetHdr(bo->beresp, H_ETag, &p))) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_IMSCAND, 1); if (bo->htc->body_status != BS_NONE && VDI_GetBody(bo->wrk, bo) != 0) { (void)VFP_Error(bo->vfc, "GetBody failed - workspace_backend overflow?"); VFP_Close(bo->vfc); bo->htc->doclose = SC_OVERLOAD; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } assert(bo->fetch_objcore->boc->refcount >= 1); assert(bo->fetch_objcore->boc->state == BOS_REQ_DONE); if (bo->do_stream) { ObjSetState(wrk, bo->fetch_objcore, BOS_PREP_STREAM); HSH_Unbusy(wrk, bo->fetch_objcore); ObjSetState(wrk, bo->fetch_objcore, BOS_STREAM); } VSLb(bo->vsl, SLT_Fetch_Body, "%u %s %s", bo->htc->body_status, body_status_2str(bo->htc->body_status), bo->do_stream ? "stream" : "-"); if (bo->htc->body_status != BS_NONE) { assert(bo->htc->body_status != BS_ERROR); return (F_STP_FETCHBODY); } AZ(bo->vfc->failed); return (F_STP_FETCHEND); }
static enum fetch_step vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) { int i; double now; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(bo->storage); bo->storage = bo->do_pass ? stv_transient : STV_next(); if (bo->retries > 0) http_Unset(bo->bereq, "\012X-Varnish:"); http_PrintfHeader(bo->bereq, "X-Varnish: %u", VXID(bo->vsl->wid)); VCL_backend_fetch_method(bo->vcl, wrk, NULL, bo, NULL); bo->uncacheable = bo->do_pass; if (wrk->handling == VCL_RET_ABANDON || wrk->handling == VCL_RET_FAIL) return (F_STP_FAIL); assert (wrk->handling == VCL_RET_FETCH); HTTP_Setup(bo->beresp, bo->ws, bo->vsl, SLT_BerespMethod); assert(bo->fetch_objcore->boc->state <= BOS_REQ_DONE); AZ(bo->htc); VFP_Setup(bo->vfc, wrk); bo->vfc->oc = bo->fetch_objcore; bo->vfc->resp = bo->beresp; bo->vfc->req = bo->bereq; i = VDI_GetHdr(wrk, bo); now = W_TIM_real(wrk); VSLb_ts_busyobj(bo, "Beresp", now); if (i) { assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } http_VSL_log(bo->beresp); if (bo->htc->body_status == BS_ERROR) { bo->htc->doclose = SC_RX_BODY; VDI_Finish(bo->wrk, bo); VSLb(bo->vsl, SLT_Error, "Body cannot be fetched"); assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } if (!http_GetHdr(bo->beresp, H_Date, NULL)) { /* * RFC 2616 14.18 Date: The Date general-header field * represents the date and time at which the message was * originated, having the same semantics as orig-date in * RFC 822. ... A received message that does not have a * Date header field MUST be assigned one by the recipient * if the message will be cached by that recipient or * gatewayed via a protocol which requires a Date. * * If we didn't get a Date header, we assign one here. */ http_TimeHeader(bo->beresp, "Date: ", now); } /* * These two headers can be spread over multiple actual headers * and we rely on their content outside of VCL, so collect them * into one line here. */ http_CollectHdr(bo->beresp, H_Cache_Control); http_CollectHdr(bo->beresp, H_Vary); if (bo->fetch_objcore->flags & OC_F_PRIVATE) { /* private objects have negative TTL */ bo->fetch_objcore->t_origin = now; bo->fetch_objcore->ttl = -1.; bo->fetch_objcore->grace = 0; bo->fetch_objcore->keep = 0; } else { /* What does RFC2616 think about TTL ? */ RFC2616_Ttl(bo, now, &bo->fetch_objcore->t_origin, &bo->fetch_objcore->ttl, &bo->fetch_objcore->grace, &bo->fetch_objcore->keep ); } AZ(bo->do_esi); AZ(bo->was_304); if (http_IsStatus(bo->beresp, 304)) { if (bo->stale_oc != NULL && ObjCheckFlag(bo->wrk, bo->stale_oc, OF_IMSCAND)) { if (ObjCheckFlag(bo->wrk, bo->stale_oc, OF_CHGGZIP)) { /* * If we changed the gzip status of the object * the stored Content_Encoding controls we * must weaken any new ETag we get. */ http_Unset(bo->beresp, H_Content_Encoding); RFC2616_Weaken_Etag(bo->beresp); } http_Unset(bo->beresp, H_Content_Length); HTTP_Merge(bo->wrk, bo->stale_oc, bo->beresp); assert(http_IsStatus(bo->beresp, 200)); bo->was_304 = 1; } else if (!bo->do_pass) { /* * Backend sent unallowed 304 */ VSLb(bo->vsl, SLT_Error, "304 response but not conditional fetch"); bo->htc->doclose = SC_RX_BAD; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } } VCL_backend_response_method(bo->vcl, wrk, NULL, bo, NULL); if (wrk->handling == VCL_RET_ABANDON || wrk->handling == VCL_RET_FAIL) { bo->htc->doclose = SC_RESP_CLOSE; VDI_Finish(bo->wrk, bo); return (F_STP_FAIL); } if (wrk->handling == VCL_RET_RETRY) { if (bo->htc->body_status != BS_NONE) bo->htc->doclose = SC_RESP_CLOSE; if (bo->director_state != DIR_S_NULL) VDI_Finish(bo->wrk, bo); if (bo->retries++ < cache_param->max_retries) return (F_STP_RETRY); VSLb(bo->vsl, SLT_VCL_Error, "Too many retries, delivering 503"); assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } assert(bo->fetch_objcore->boc->state <= BOS_REQ_DONE); if (bo->fetch_objcore->boc->state != BOS_REQ_DONE) { bo->req = NULL; ObjSetState(wrk, bo->fetch_objcore, BOS_REQ_DONE); } if (bo->do_esi) bo->do_stream = 0; if (wrk->handling == VCL_RET_PASS) { bo->fetch_objcore->flags |= OC_F_HFP; bo->uncacheable = 1; wrk->handling = VCL_RET_DELIVER; } if (bo->do_pass || bo->uncacheable) bo->fetch_objcore->flags |= OC_F_PASS; assert(wrk->handling == VCL_RET_DELIVER); return (bo->was_304 ? F_STP_CONDFETCH : F_STP_FETCH); }
static enum req_fsm_nxt cnt_deliver(struct worker *wrk, struct req *req) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(req->objcore->objhead, OBJHEAD_MAGIC); AN(req->vcl); assert(req->objcore->refcnt > 0); ObjTouch(req->wrk, req->objcore, req->t_prev); HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); if (HTTP_Decode(req->resp, ObjGetAttr(req->wrk, req->objcore, OA_HEADERS, NULL))) { req->err_code = 500; req->req_step = R_STP_SYNTH; return (REQ_FSM_MORE); } http_ForceField(req->resp, HTTP_HDR_PROTO, "HTTP/1.1"); if (req->is_hit) http_PrintfHeader(req->resp, "X-Varnish: %u %u", VXID(req->vsl->wid), ObjGetXID(wrk, req->objcore)); else http_PrintfHeader(req->resp, "X-Varnish: %u", VXID(req->vsl->wid)); /* * We base Age calculation upon the last timestamp taken during * client request processing. This gives some inaccuracy, but * since Age is only full second resolution that shouldn't * matter. (Last request timestamp could be a Start timestamp * taken before the object entered into cache leading to negative * age. Truncate to zero in that case). */ http_PrintfHeader(req->resp, "Age: %.0f", fmax(0., req->t_prev - req->objcore->t_origin)); http_SetHeader(req->resp, "Via: 1.1 varnish-v4"); if (cache_param->http_gzip_support && ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && !RFC2616_Req_Gzip(req->http)) RFC2616_Weaken_Etag(req->resp); VCL_deliver_method(req->vcl, wrk, req, NULL, NULL); VSLb_ts_req(req, "Process", W_TIM_real(wrk)); /* Stop the insanity before it turns "Hotel California" on us */ if (req->restarts >= cache_param->max_restarts) wrk->handling = VCL_RET_DELIVER; if (wrk->handling != VCL_RET_DELIVER) { (void)HSH_DerefObjCore(wrk, &req->objcore); http_Teardown(req->resp); switch (wrk->handling) { case VCL_RET_RESTART: req->req_step = R_STP_RESTART; break; case VCL_RET_SYNTH: req->req_step = R_STP_SYNTH; break; default: WRONG("Illegal return from vcl_deliver{}"); } return (REQ_FSM_MORE); } assert(wrk->handling == VCL_RET_DELIVER); if (!(req->objcore->flags & OC_F_PASS) && req->esi_level == 0 && http_IsStatus(req->resp, 200) && req->http->conds && RFC2616_Do_Cond(req)) http_PutResponse(req->resp, "HTTP/1.1", 304, NULL); req->req_step = R_STP_TRANSMIT; return (REQ_FSM_MORE); }
static enum req_fsm_nxt cnt_transmit(struct worker *wrk, struct req *req) { struct boc *boc; const char *r; uint16_t status; int sendbody; intmax_t clval; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->transport, TRANSPORT_MAGIC); /* Grab a ref to the bo if there is one */ boc = HSH_RefBoc(req->objcore); clval = http_GetContentLength(req->resp); if (boc != NULL) req->resp_len = clval; else req->resp_len = ObjGetLen(req->wrk, req->objcore); req->res_mode = 0; /* RFC 7230, 3.3.3 */ status = http_GetStatus(req->resp); if (!strcmp(req->http0->hd[HTTP_HDR_METHOD].b, "HEAD")) { if (req->objcore->flags & OC_F_PASS) sendbody = -1; else sendbody = 0; } else if (status < 200 || status == 204 || status == 304) { req->resp_len = -1; sendbody = 0; } else sendbody = 1; if (sendbody >= 0) { if (!req->disable_esi && req->resp_len != 0 && ObjHasAttr(wrk, req->objcore, OA_ESIDATA)) VDP_push(req, VDP_ESI, NULL, 0, "ESI"); if (cache_param->http_gzip_support && ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && !RFC2616_Req_Gzip(req->http)) VDP_push(req, VDP_gunzip, NULL, 1, "GUZ"); if (cache_param->http_range_support && http_IsStatus(req->resp, 200)) { http_SetHeader(req->resp, "Accept-Ranges: bytes"); if (sendbody && http_GetHdr(req->http, H_Range, &r)) VRG_dorange(req, r); } } if (sendbody < 0) { /* Don't touch pass+HEAD C-L */ sendbody = 0; } else if (clval >= 0 && clval == req->resp_len) { /* Reuse C-L header */ } else { http_Unset(req->resp, H_Content_Length); if (req->resp_len >= 0 && sendbody) http_PrintfHeader(req->resp, "Content-Length: %jd", req->resp_len); } req->transport->deliver(req, boc, sendbody); VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); if (req->objcore->flags & (OC_F_PRIVATE | OC_F_PASS)) { if (boc != NULL) { HSH_Abandon(req->objcore); ObjWaitState(req->objcore, BOS_FINISHED); } ObjSlim(wrk, req->objcore); } if (boc != NULL) HSH_DerefBoc(wrk, req->objcore); (void)HSH_DerefObjCore(wrk, &req->objcore); http_Teardown(req->resp); return (REQ_FSM_DONE); }
static enum fetch_step vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) { const char *p; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC); assert(wrk->handling == VCL_RET_DELIVER); /* * The VCL variables beresp.do_g[un]zip tells us how we want the * object processed before it is stored. * * The backend Content-Encoding header tells us what we are going * to receive, which we classify in the following three classes: * * "Content-Encoding: gzip" --> object is gzip'ed. * no Content-Encoding --> object is not gzip'ed. * anything else --> do nothing wrt gzip * */ /* We do nothing unless the param is set */ if (!cache_param->http_gzip_support) bo->do_gzip = bo->do_gunzip = 0; if (bo->htc->content_length == 0) http_Unset(bo->beresp, H_Content_Encoding); if (bo->htc->body_status != BS_NONE) { bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip"); bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL); assert(bo->is_gzip == 0 || bo->is_gunzip == 0); } /* We won't gunzip unless it is non-empty and gzip'ed */ if (bo->htc->body_status == BS_NONE || bo->htc->content_length == 0 || (bo->do_gunzip && !bo->is_gzip)) bo->do_gunzip = 0; /* We wont gzip unless it is non-empty and ungziped */ if (bo->htc->body_status == BS_NONE || bo->htc->content_length == 0 || (bo->do_gzip && !bo->is_gunzip)) bo->do_gzip = 0; /* But we can't do both at the same time */ assert(bo->do_gzip == 0 || bo->do_gunzip == 0); if (bo->do_gunzip || (bo->is_gzip && bo->do_esi)) (void)VFP_Push(bo->vfc, &vfp_gunzip, 1); if (bo->htc->content_length != 0) { if (bo->do_esi && bo->do_gzip) { (void)VFP_Push(bo->vfc, &vfp_esi_gzip, 1); } else if (bo->do_esi && bo->is_gzip && !bo->do_gunzip) { (void)VFP_Push(bo->vfc, &vfp_esi_gzip, 1); } else if (bo->do_esi) { (void)VFP_Push(bo->vfc, &vfp_esi, 1); } else if (bo->do_gzip) { (void)VFP_Push(bo->vfc, &vfp_gzip, 1); } else if (bo->is_gzip && !bo->do_gunzip) { (void)VFP_Push(bo->vfc, &vfp_testgunzip, 1); } } if (bo->fetch_objcore->flags & OC_F_PRIVATE) AN(bo->uncacheable); /* No reason to try streaming a non-existing body */ if (bo->htc->body_status == BS_NONE) bo->do_stream = 0; if (VFP_Open(bo->vfc)) { (void)VFP_Error(bo->vfc, "Fetch pipeline failed to open"); bo->htc->doclose = SC_RX_BODY; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); bo->htc->doclose = SC_RX_BODY; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } if (bo->do_esi) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_ESIPROC, 1); if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_GZIPED, 1); if (bo->do_gzip || bo->do_gunzip) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_CHGGZIP, 1); if (http_IsStatus(bo->beresp, 200) && ( http_GetHdr(bo->beresp, H_Last_Modified, &p) || http_GetHdr(bo->beresp, H_ETag, &p))) ObjSetFlag(bo->wrk, bo->fetch_objcore, OF_IMSCAND, 1); if (bo->htc->body_status != BS_NONE) AZ(VDI_GetBody(bo->wrk, bo)); assert(bo->refcount >= 1); assert(bo->state == BOS_REQ_DONE); if (bo->do_stream) { HSH_Unbusy(wrk, bo->fetch_objcore); VBO_setstate(bo, BOS_STREAM); } VSLb(bo->vsl, SLT_Fetch_Body, "%u %s %s", bo->htc->body_status, body_status_2str(bo->htc->body_status), bo->do_stream ? "stream" : "-"); if (bo->htc->body_status != BS_NONE) { assert(bo->htc->body_status != BS_ERROR); vbf_fetch_body_helper(bo); } if (bo->vfc->failed) { VDI_Finish(bo->wrk, bo); if (!bo->do_stream) { assert(bo->state < BOS_STREAM); // XXX: doclose = ? return (F_STP_ERROR); } else { return (F_STP_FAIL); } } if (bo->do_stream) assert(bo->state == BOS_STREAM); else { assert(bo->state == BOS_REQ_DONE); HSH_Unbusy(wrk, bo->fetch_objcore); } /* Recycle the backend connection before setting BOS_FINISHED to give predictable backend reuse behavior for varnishtest */ VDI_Finish(bo->wrk, bo); VBO_setstate(bo, BOS_FINISHED); VSLb_ts_busyobj(bo, "BerespBody", W_TIM_real(wrk)); if (bo->stale_oc != NULL) EXP_Rearm(bo->stale_oc, bo->stale_oc->exp.t_origin, 0, 0, 0); return (F_STP_DONE); }
static enum fetch_step vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) { int i; double now; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(bo->storage_hint); if (bo->do_pass) AN(bo->req); else AZ(bo->req); if (bo->retries > 0) http_Unset(bo->bereq, "\012X-Varnish:"); http_PrintfHeader(bo->bereq, "X-Varnish: %u", VXID(bo->vsl->wid)); VCL_backend_fetch_method(bo->vcl, wrk, NULL, bo, NULL); bo->uncacheable = bo->do_pass; if (wrk->handling == VCL_RET_ABANDON) return (F_STP_FAIL); assert (wrk->handling == VCL_RET_FETCH); HTTP_Setup(bo->beresp, bo->ws, bo->vsl, SLT_BerespMethod); assert(bo->state <= BOS_REQ_DONE); AZ(bo->htc); i = VDI_GetHdr(wrk, bo); now = W_TIM_real(wrk); VSLb_ts_busyobj(bo, "Beresp", now); if (i) { assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } http_VSL_log(bo->beresp); if (!http_GetHdr(bo->beresp, H_Date, NULL)) { /* * RFC 2616 14.18 Date: The Date general-header field * represents the date and time at which the message was * originated, having the same semantics as orig-date in * RFC 822. ... A received message that does not have a * Date header field MUST be assigned one by the recipient * if the message will be cached by that recipient or * gatewayed via a protocol which requires a Date. * * If we didn't get a Date header, we assign one here. */ http_TimeHeader(bo->beresp, "Date: ", now); } /* * These two headers can be spread over multiple actual headers * and we rely on their content outside of VCL, so collect them * into one line here. */ http_CollectHdr(bo->beresp, H_Cache_Control); http_CollectHdr(bo->beresp, H_Vary); /* * Figure out how the fetch is supposed to happen, before the * headers are adultered by VCL */ if (!strcasecmp(http_GetMethod(bo->bereq), "head")) { /* * A HEAD request can never have a body in the reply, * no matter what the headers might say. * [RFC2516 4.3 p33] */ wrk->stats->fetch_head++; bo->htc->body_status = BS_NONE; } else if (http_GetStatus(bo->beresp) <= 199) { /* * 1xx responses never have a body. * [RFC2616 4.3 p33] * ... but we should never see them. */ wrk->stats->fetch_1xx++; bo->htc->body_status = BS_ERROR; } else if (http_IsStatus(bo->beresp, 204)) { /* * 204 is "No Content", obviously don't expect a body. * [RFC7230 3.3.1 p28 and 3.3.2 p30] */ wrk->stats->fetch_204++; if (http_GetHdr(bo->beresp, H_Content_Length, NULL) || http_GetHdr(bo->beresp, H_Transfer_Encoding, NULL)) bo->htc->body_status = BS_ERROR; else bo->htc->body_status = BS_NONE; } else if (http_IsStatus(bo->beresp, 304)) { /* * 304 is "Not Modified" it has no body. * [RFC2616 10.3.5 p63] */ wrk->stats->fetch_304++; bo->htc->body_status = BS_NONE; } else if (bo->htc->body_status == BS_CHUNKED) { wrk->stats->fetch_chunked++; } else if (bo->htc->body_status == BS_LENGTH) { assert(bo->htc->content_length > 0); wrk->stats->fetch_length++; } else if (bo->htc->body_status == BS_EOF) { wrk->stats->fetch_eof++; } else if (bo->htc->body_status == BS_ERROR) { wrk->stats->fetch_bad++; } else if (bo->htc->body_status == BS_NONE) { wrk->stats->fetch_none++; } else { WRONG("wrong bodystatus"); } if (bo->htc->body_status == BS_ERROR) { bo->htc->doclose = SC_RX_BODY; VDI_Finish(bo->wrk, bo); VSLb(bo->vsl, SLT_Error, "Body cannot be fetched"); assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } /* * What does RFC2616 think about TTL ? */ EXP_Clr(&bo->fetch_objcore->exp); RFC2616_Ttl(bo, now); /* private objects have negative TTL */ if (bo->fetch_objcore->flags & OC_F_PRIVATE) bo->fetch_objcore->exp.ttl = -1.; AZ(bo->do_esi); AZ(bo->was_304); if (http_IsStatus(bo->beresp, 304)) { if (bo->stale_oc != NULL && ObjCheckFlag(bo->wrk, bo->stale_oc, OF_IMSCAND)) { if (ObjCheckFlag(bo->wrk, bo->stale_oc, OF_CHGGZIP)) { /* * If we changed the gzip status of the object * the stored Content_Encoding controls we * must weaken any new ETag we get. */ http_Unset(bo->beresp, H_Content_Encoding); RFC2616_Weaken_Etag(bo->beresp); } http_Unset(bo->beresp, H_Content_Length); HTTP_Merge(bo->wrk, bo->stale_oc, bo->beresp); assert(http_IsStatus(bo->beresp, 200)); bo->was_304 = 1; } else if (!bo->do_pass) { /* * Backend sent unallowed 304 */ VSLb(bo->vsl, SLT_Error, "304 response but not conditional fetch"); bo->htc->doclose = SC_RX_BAD; VDI_Finish(bo->wrk, bo); return (F_STP_FAIL); } } bo->vfc->bo = bo; bo->vfc->oc = bo->fetch_objcore; bo->vfc->wrk = bo->wrk; bo->vfc->http = bo->beresp; bo->vfc->esi_req = bo->bereq; VCL_backend_response_method(bo->vcl, wrk, NULL, bo, NULL); if (wrk->handling == VCL_RET_ABANDON) { bo->htc->doclose = SC_RESP_CLOSE; VDI_Finish(bo->wrk, bo); return (F_STP_FAIL); } if (wrk->handling == VCL_RET_RETRY) { if (bo->htc->body_status != BS_NONE) bo->htc->doclose = SC_RESP_CLOSE; if (bo->director_state != DIR_S_NULL) VDI_Finish(bo->wrk, bo); if (bo->retries++ < cache_param->max_retries) return (F_STP_RETRY); VSLb(bo->vsl, SLT_VCL_Error, "Too many retries, delivering 503"); assert(bo->director_state == DIR_S_NULL); return (F_STP_ERROR); } assert(bo->state == BOS_REQ_DONE); if (bo->do_esi) bo->do_stream = 0; if (bo->do_pass || bo->uncacheable) bo->fetch_objcore->flags |= OC_F_PASS; assert(wrk->handling == VCL_RET_DELIVER); return (bo->was_304 ? F_STP_CONDFETCH : F_STP_FETCH); }
static enum req_fsm_nxt cnt_deliver(struct worker *wrk, struct req *req) { struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(req->objcore->objhead, OBJHEAD_MAGIC); AN(req->vcl); assert(req->objcore->refcnt > 0); if (req->objcore->exp_flags & OC_EF_EXP) EXP_Touch(req->objcore, req->t_prev); HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); AZ(HTTP_Decode(req->resp, ObjGetattr(req->wrk, req->objcore, OA_HEADERS, NULL))); http_ForceField(req->resp, HTTP_HDR_PROTO, "HTTP/1.1"); if (req->is_hit) http_PrintfHeader(req->resp, "X-Varnish: %u %u", VXID(req->vsl->wid), ObjGetXID(wrk, req->objcore)); else http_PrintfHeader(req->resp, "X-Varnish: %u", VXID(req->vsl->wid)); /* We base Age calculation upon the last timestamp taken during client request processing. This gives some inaccuracy, but since Age is only full second resolution that shouldn't matter. (Last request timestamp could be a Start timestamp taken before the object entered into cache leading to negative age. Truncate to zero in that case). */ http_PrintfHeader(req->resp, "Age: %.0f", fmax(0., req->t_prev - req->objcore->exp.t_origin)); http_SetHeader(req->resp, "Via: 1.1 varnish-v4"); if (cache_param->http_gzip_support && ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && !RFC2616_Req_Gzip(req->http)) RFC2616_Weaken_Etag(req->resp); VCL_deliver_method(req->vcl, wrk, req, NULL, NULL); VSLb_ts_req(req, "Process", W_TIM_real(wrk)); /* Stop the insanity before it turns "Hotel California" on us */ if (req->restarts >= cache_param->max_restarts) wrk->handling = VCL_RET_DELIVER; if (wrk->handling != VCL_RET_DELIVER) { (void)HSH_DerefObjCore(wrk, &req->objcore); http_Teardown(req->resp); switch (wrk->handling) { case VCL_RET_RESTART: req->req_step = R_STP_RESTART; break; case VCL_RET_SYNTH: req->req_step = R_STP_SYNTH; break; default: INCOMPL(); } return (REQ_FSM_MORE); } assert(wrk->handling == VCL_RET_DELIVER); if (!(req->objcore->flags & OC_F_PASS) && req->esi_level == 0 && http_IsStatus(req->resp, 200) && req->http->conds && RFC2616_Do_Cond(req)) http_PutResponse(req->resp, "HTTP/1.1", 304, NULL); /* Grab a ref to the bo if there is one, and hand it down */ bo = HSH_RefBusy(req->objcore); if (bo != NULL) { if (req->esi_level == 0 && bo->state == BOS_FINISHED) { VBO_DerefBusyObj(wrk, &bo); } else if (!bo->do_stream) { VBO_waitstate(bo, BOS_FINISHED); VBO_DerefBusyObj(wrk, &bo); } } cnt_vdp(req, bo); if (bo != NULL) VBO_DerefBusyObj(wrk, &bo); VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); if (http_HdrIs(req->resp, H_Connection, "close")) req->doclose = SC_RESP_CLOSE; if ((req->objcore->flags & OC_F_PASS) && bo != NULL) { VBO_waitstate(bo, BOS_FINISHED); ObjSlim(wrk, req->objcore); } (void)HSH_DerefObjCore(wrk, &req->objcore); http_Teardown(req->resp); return (REQ_FSM_DONE); }
void V1D_Deliver(struct req *req, struct busyobj *bo) { const char *r; enum objiter_status ois; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); req->res_mode = 0; /* * Determine ESI status first. Not dependent on wantbody, because * we want ESI to supress C-L in HEAD too. */ if (!req->disable_esi && ObjGetattr(req->wrk, req->objcore, OA_ESIDATA, NULL) != NULL) req->res_mode |= RES_ESI; /* * ESI-childen don't care about headers -> early escape */ if (req->esi_level > 0) { ESI_DeliverChild(req, bo); return; } if (req->res_mode & RES_ESI) { RFC2616_Weaken_Etag(req->resp); } else if (http_IsStatus(req->resp, 304)) { http_Unset(req->resp, H_Content_Length); req->wantbody = 0; } else if (bo == NULL && !http_GetHdr(req->resp, H_Content_Length, NULL)) { http_PrintfHeader(req->resp, "Content-Length: %ju", (uintmax_t)ObjGetLen( req->wrk, req->objcore)); } if (cache_param->http_gzip_support && ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) && !RFC2616_Req_Gzip(req->http)) { /* * We don't know what it uncompresses to * XXX: we could cache that, but would still deliver * XXX: with multiple writes because of the gunzip buffer */ req->res_mode |= RES_GUNZIP; VDP_push(req, VDP_gunzip, NULL, 0); } if (req->res_mode & RES_ESI) { /* Gunzip could have added back a C-L */ http_Unset(req->resp, H_Content_Length); } /* * Range comes after the others and pushes on bottom because it * can generate a correct C-L header. */ if (cache_param->http_range_support && http_IsStatus(req->resp, 200)) { http_SetHeader(req->resp, "Accept-Ranges: bytes"); if (req->wantbody && http_GetHdr(req->http, H_Range, &r)) VRG_dorange(req, bo, r); } if (http_GetHdr(req->resp, H_Content_Length, NULL)) req->res_mode |= RES_LEN; if (req->wantbody && !(req->res_mode & RES_LEN)) { if (req->http->protover >= 11) { req->res_mode |= RES_CHUNKED; http_SetHeader(req->resp, "Transfer-Encoding: chunked"); } else { req->res_mode |= RES_EOF; req->doclose = SC_TX_EOF; } } VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode); if (req->doclose) { if (!http_HdrIs(req->resp, H_Connection, "close")) { http_Unset(req->resp, H_Connection); http_SetHeader(req->resp, "Connection: close"); } } else if (!http_GetHdr(req->resp, H_Connection, NULL)) http_SetHeader(req->resp, "Connection: keep-alive"); VDP_push(req, v1d_bytes, NULL, 1); V1L_Reserve(req->wrk, req->ws, &req->sp->fd, req->vsl, req->t_prev); req->acct.resp_hdrbytes += HTTP1_Write(req->wrk, req->resp, HTTP1_Resp); if (DO_DEBUG(DBG_FLUSH_HEAD)) (void)V1L_Flush(req->wrk); ois = OIS_DONE; if (req->wantbody) { if (req->res_mode & RES_CHUNKED) V1L_Chunked(req->wrk); ois = VDP_DeliverObj(req); (void)VDP_bytes(req, VDP_FLUSH, NULL, 0); if (ois == OIS_DONE && (req->res_mode & RES_CHUNKED)) V1L_EndChunk(req->wrk); } if ((V1L_FlushRelease(req->wrk) || ois != OIS_DONE) && req->sp->fd >= 0) SES_Close(req->sp, SC_REM_CLOSE); VDP_close(req); }