VED_Deliver(struct req *req, struct busyobj *bo, int wantbody) { int i; struct ecx *ecx; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CAST_OBJ_NOTNULL(ecx, req->transport_priv, ECX_MAGIC); if (wantbody == 0) return; req->res_mode |= RES_ESI_CHILD; i = ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED); if (ecx->isgzip && i && !(req->res_mode & RES_ESI)) { ved_stripgzip(req, bo); } else { if (ecx->isgzip && !i) VDP_push(req, ved_pretend_gzip, ecx, 1); else VDP_push(req, ved_vdp_bytes, ecx->preq, 1); (void)VDP_DeliverObj(req); } VDP_close(req); }
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_mkbereq(struct worker *wrk, struct busyobj *bo) { const char *q; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->req, REQ_MAGIC); assert(bo->fetch_objcore->boc->state == BOS_INVALID); AZ(bo->storage); HTTP_Setup(bo->bereq0, bo->ws, bo->vsl, SLT_BereqMethod); http_FilterReq(bo->bereq0, bo->req->http, bo->do_pass ? HTTPH_R_PASS : HTTPH_R_FETCH); if (!bo->do_pass) { http_ForceField(bo->bereq0, HTTP_HDR_METHOD, "GET"); http_ForceField(bo->bereq0, HTTP_HDR_PROTO, "HTTP/1.1"); if (cache_param->http_gzip_support) http_ForceHeader(bo->bereq0, H_Accept_Encoding, "gzip"); } else { AZ(bo->stale_oc); if (bo->bereq0->protover > 11) http_ForceField(bo->bereq0, HTTP_HDR_PROTO, "HTTP/1.1"); } http_CopyHome(bo->bereq0); if (bo->stale_oc != NULL && ObjCheckFlag(bo->wrk, bo->stale_oc, OF_IMSCAND) && (bo->stale_oc->boc != NULL || ObjGetLen(wrk, bo->stale_oc) != 0)) { AZ(bo->stale_oc->flags & OC_F_PASS); q = HTTP_GetHdrPack(bo->wrk, bo->stale_oc, H_Last_Modified); if (q != NULL) http_PrintfHeader(bo->bereq0, "If-Modified-Since: %s", q); q = HTTP_GetHdrPack(bo->wrk, bo->stale_oc, H_ETag); if (q != NULL) http_PrintfHeader(bo->bereq0, "If-None-Match: %s", q); } HTTP_Setup(bo->bereq, bo->ws, bo->vsl, SLT_BereqMethod); bo->ws_bo = WS_Snapshot(bo->ws); HTTP_Copy(bo->bereq, bo->bereq0); if (bo->req->req_body_status == REQ_BODY_NONE) { bo->req = NULL; ObjSetState(bo->wrk, bo->fetch_objcore, BOS_REQ_DONE); } return (F_STP_STARTFETCH); }
static void ved_stripgzip(struct req *req, struct busyobj *bo) { ssize_t start, last, stop, lpad; ssize_t l; char *p; uint32_t icrc; uint32_t ilen; uint64_t olen; uint8_t *dbits; uint8_t *pp; uint8_t tailbuf[8]; enum objiter_status ois; void *oi; void *sp; ssize_t sl, ll, dl; struct ecx *ecx; struct req *preq; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CAST_OBJ_NOTNULL(ecx, req->transport_priv, ECX_MAGIC); preq = ecx->preq; if (bo != NULL) VBO_waitstate(bo, BOS_FINISHED); AN(ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED)); /* * This is the interesting case: Deliver all the deflate * blocks, stripping the "LAST" bit of the last one and * padding it, as necessary, to a byte boundary. */ p = ObjGetattr(req->wrk, req->objcore, OA_GZIPBITS, &l); AN(p); assert(l == 32); start = vbe64dec(p); last = vbe64dec(p + 8); stop = vbe64dec(p + 16); olen = ObjGetLen(req->wrk, req->objcore); assert(start > 0 && start < olen * 8); assert(last > 0 && last < olen * 8); assert(stop > 0 && stop < olen * 8); assert(last >= start); assert(last < stop); /* The start bit must be byte aligned. */ AZ(start & 7); /* * XXX: optimize for the case where the 'last' * XXX: bit is in a empty copy block */ memset(tailbuf, 0xdd, sizeof tailbuf); dbits = WS_Alloc(req->ws, 8); AN(dbits); ll = 0; oi = ObjIterBegin(req->wrk, req->objcore); do { ois = ObjIter(req->objcore, oi, &sp, &sl); pp = sp; if (sl > 0) { /* Skip over the GZIP header */ dl = start / 8 - ll; if (dl > 0) { /* Before start, skip */ if (dl > sl) dl = sl; ll += dl; sl -= dl; pp += dl; } } if (sl > 0) { /* The main body of the object */ dl = last / 8 - ll; if (dl > 0) { if (dl > sl) dl = sl; if (ved_bytes(req, preq, VDP_NULL, pp, dl)) break; ll += dl; sl -= dl; pp += dl; } } if (sl > 0 && ll == last / 8) { /* Remove the "LAST" bit */ dbits[0] = *pp; dbits[0] &= ~(1U << (last & 7)); if (ved_bytes(req, preq, VDP_NULL, dbits, 1)) break; ll++; sl--; pp++; } if (sl > 0) { /* Last block */ dl = stop / 8 - ll; if (dl > 0) { if (dl > sl) dl = sl; if (ved_bytes(req, preq, VDP_NULL, pp, dl)) break; ll += dl; sl -= dl; pp += dl; } } if (sl > 0 && (stop & 7) && ll == stop / 8) { /* Add alignment to byte boundary */ dbits[1] = *pp; ll++; sl--; pp++; switch((int)(stop & 7)) { case 1: /* * x000.... * 00000000 00000000 11111111 11111111 */ case 3: /* * xxx000.. * 00000000 00000000 11111111 11111111 */ case 5: /* * xxxxx000 * 00000000 00000000 11111111 11111111 */ dbits[2] = 0x00; dbits[3] = 0x00; dbits[4] = 0xff; dbits[5] = 0xff; lpad = 5; break; case 2: /* xx010000 00000100 00000001 00000000 */ dbits[1] |= 0x08; dbits[2] = 0x20; dbits[3] = 0x80; dbits[4] = 0x00; lpad = 4; break; case 4: /* xxxx0100 00000001 00000000 */ dbits[1] |= 0x20; dbits[2] = 0x80; dbits[3] = 0x00; lpad = 3; break; case 6: /* xxxxxx01 00000000 */ dbits[1] |= 0x80; dbits[2] = 0x00; lpad = 2; break; case 7: /* * xxxxxxx0 * 00...... * 00000000 00000000 11111111 11111111 */ dbits[2] = 0x00; dbits[3] = 0x00; dbits[4] = 0x00; dbits[5] = 0xff; dbits[6] = 0xff; lpad = 6; break; case 0: /* xxxxxxxx */ default: WRONG("compiler must be broken"); } if (ved_bytes(req, preq, VDP_NULL, dbits + 1, lpad)) break; } if (sl > 0) { /* Recover GZIP tail */ dl = olen - ll; assert(dl >= 0); if (dl > sl) dl = sl; if (dl > 0) { assert(dl <= 8); l = ll - (olen - 8); assert(l >= 0); assert(l <= 8); assert(l + dl <= 8); memcpy(tailbuf + l, pp, dl); ll += dl; sl -= dl; pp += dl; } } } while (ois == OIS_DATA || ois == OIS_STREAM); ObjIterEnd(req->objcore, &oi); (void)ved_bytes(req, preq, VDP_FLUSH, NULL, 0); icrc = vle32dec(tailbuf); ilen = vle32dec(tailbuf + 4); ecx->crc = crc32_combine(ecx->crc, icrc, ilen); ecx->l_crc += ilen; }
vbf_fetch_thread(struct worker *wrk, void *priv) { struct busyobj *bo; enum fetch_step stp; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CAST_OBJ_NOTNULL(bo, priv, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC); THR_SetBusyobj(bo); stp = F_STP_MKBEREQ; assert(isnan(bo->t_first)); assert(isnan(bo->t_prev)); VSLb_ts_busyobj(bo, "Start", W_TIM_real(wrk)); bo->wrk = wrk; wrk->vsl = bo->vsl; #if 0 if (bo->stale_oc != NULL) { CHECK_OBJ_NOTNULL(bo->stale_oc, OBJCORE_MAGIC); /* We don't want the oc/stevedore ops in fetching thread */ if (!ObjCheckFlag(wrk, bo->stale_oc, OF_IMSCAND)) (void)HSH_DerefObjCore(wrk, &bo->stale_oc, 0); } #endif while (stp != F_STP_DONE) { CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); assert(bo->fetch_objcore->boc->refcount >= 1); switch (stp) { #define FETCH_STEP(l, U, arg) \ case F_STP_##U: \ stp = vbf_stp_##l arg; \ break; #include "tbl/steps.h" default: WRONG("Illegal fetch_step"); } } assert(bo->director_state == DIR_S_NULL); http_Teardown(bo->bereq); http_Teardown(bo->beresp); if (bo->fetch_objcore->boc->state == BOS_FINISHED) { AZ(bo->fetch_objcore->flags & OC_F_FAILED); VSLb(bo->vsl, SLT_Length, "%ju", (uintmax_t)ObjGetLen(bo->wrk, bo->fetch_objcore)); } // AZ(bo->fetch_objcore->boc); // XXX if (bo->stale_oc != NULL) (void)HSH_DerefObjCore(wrk, &bo->stale_oc, 0); wrk->vsl = NULL; HSH_DerefBoc(wrk, bo->fetch_objcore); SES_Rel(bo->sp); VBO_ReleaseBusyObj(wrk, &bo); THR_SetBusyobj(NULL); }
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_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); }
static void ved_stripgzip(struct req *req, const struct boc *boc) { ssize_t l; char *p; uint32_t icrc; uint32_t ilen; uint8_t *dbits; struct ecx *ecx; struct ved_foo foo; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); CAST_OBJ_NOTNULL(ecx, req->transport_priv, ECX_MAGIC); INIT_OBJ(&foo, VED_FOO_MAGIC); foo.req = req; foo.preq = ecx->preq; memset(foo.tailbuf, 0xdd, sizeof foo.tailbuf); /* OA_GZIPBITS is not valid until BOS_FINISHED */ if (boc != NULL) ObjWaitState(req->objcore, BOS_FINISHED); AN(ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED)); /* * This is the interesting case: Deliver all the deflate * blocks, stripping the "LAST" bit of the last one and * padding it, as necessary, to a byte boundary. */ p = ObjGetAttr(req->wrk, req->objcore, OA_GZIPBITS, &l); AN(p); assert(l == 32); foo.start = vbe64dec(p); foo.last = vbe64dec(p + 8); foo.stop = vbe64dec(p + 16); foo.olen = ObjGetLen(req->wrk, req->objcore); assert(foo.start > 0 && foo.start < foo.olen * 8); assert(foo.last > 0 && foo.last < foo.olen * 8); assert(foo.stop > 0 && foo.stop < foo.olen * 8); assert(foo.last >= foo.start); assert(foo.last < foo.stop); /* The start bit must be byte aligned. */ AZ(foo.start & 7); dbits = WS_Alloc(req->ws, 8); AN(dbits); foo.dbits = dbits; (void)ObjIterate(req->wrk, req->objcore, &foo, ved_objiterate); /* XXX: error check ?? */ (void)ved_bytes(req, foo.preq, VDP_FLUSH, NULL, 0); icrc = vle32dec(foo.tailbuf); ilen = vle32dec(foo.tailbuf + 4); ecx->crc = crc32_combine(ecx->crc, icrc, ilen); ecx->l_crc += ilen; }
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); }
void VBF_Fetch(struct worker *wrk, struct req *req, struct objcore *oc, struct objcore *oldoc, enum vbf_fetch_mode_e mode) { struct busyobj *bo; const char *how; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); CHECK_OBJ_ORNULL(oldoc, OBJCORE_MAGIC); switch(mode) { case VBF_PASS: how = "pass"; break; case VBF_NORMAL: how = "fetch"; break; case VBF_BACKGROUND: how = "bgfetch"; break; default: WRONG("Wrong fetch mode"); } bo = VBO_GetBusyObj(wrk, req); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); VSLb(bo->vsl, SLT_Begin, "bereq %u %s", VXID(req->vsl->wid), how); VSLb(req->vsl, SLT_Link, "bereq %u %s", VXID(bo->vsl->wid), how); THR_SetBusyobj(bo); bo->refcount = 2; oc->busyobj = bo; CHECK_OBJ_NOTNULL(bo->vcl, VCL_CONF_MAGIC); if (mode == VBF_PASS) bo->do_pass = 1; bo->vary = req->vary_b; req->vary_b = NULL; if (mode != VBF_BACKGROUND) HSH_Ref(oc); bo->fetch_objcore = oc; AZ(bo->ims_oc); if (oldoc != NULL && ObjCheckFlag(req->wrk, oldoc, OF_IMSCAND)) { assert(oldoc->refcnt > 0); HSH_Ref(oldoc); bo->ims_oc = oldoc; } AZ(bo->req); bo->req = req; bo->fetch_task.priv = bo; bo->fetch_task.func = vbf_fetch_thread; if (Pool_Task(wrk->pool, &bo->fetch_task, POOL_QUEUE_FRONT)) vbf_fetch_thread(wrk, bo); if (mode == VBF_BACKGROUND) { VBO_waitstate(bo, BOS_REQ_DONE); } else { VBO_waitstate(bo, BOS_STREAM); if (bo->state == BOS_FAILED) { AN((oc->flags & OC_F_FAILED)); } else { AZ(bo->fetch_objcore->flags & OC_F_BUSY); } } VSLb_ts_req(req, "Fetch", W_TIM_real(wrk)); THR_SetBusyobj(NULL); VBO_DerefBusyObj(wrk, &bo); }