static enum fetch_step vbf_stp_condfetch(struct worker *wrk, struct busyobj *bo) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(vbf_beresp2obj(bo)); if (ObjHasAttr(bo->wrk, bo->stale_oc, OA_ESIDATA)) AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_ESIDATA)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_FLAGS)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_GZIPBITS)); 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); } if (ObjIterate(wrk, bo->stale_oc, bo, vbf_objiterator, 0)) (void)VFP_Error(bo->vfc, "Template object failed"); if (bo->stale_oc->flags & OC_F_FAILED) (void)VFP_Error(bo->vfc, "Template object failed"); if (bo->vfc->failed) { VDI_Finish(bo->wrk, bo); wrk->stats->fetch_failed++; return (F_STP_FAIL); } return (F_STP_FETCHEND); }
vfp_esi_end(struct busyobj *bo) { struct vsb *vsb; struct vef_priv *vef; ssize_t l; int retval = 0; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AN(bo->vep); if (bo->state == BOS_FAILED) retval = -1; if (bo->vgz_rx != NULL && VGZ_Destroy(&bo->vgz_rx) != VGZ_END) retval = VFP_Error(bo, "Gunzip+ESI Failed at the very end"); vsb = VEP_Finish(bo); if (vsb != NULL) { if (!retval) { l = VSB_len(vsb); assert(l > 0); /* XXX: This is a huge waste of storage... */ bo->fetch_obj->esidata = STV_alloc(bo, l); if (bo->fetch_obj->esidata != NULL) { memcpy(bo->fetch_obj->esidata->ptr, VSB_data(vsb), l); bo->fetch_obj->esidata->len = l; } else { retval = VFP_Error(bo, "Could not allocate storage for esidata"); } } VSB_delete(vsb); } vef = bo->vef_priv; bo->vef_priv = NULL; CHECK_OBJ_NOTNULL(vef, VEF_MAGIC); if (vef->vgz != NULL) { VGZ_UpdateObj(vef->vgz, bo->fetch_obj); if (VGZ_Destroy(&vef->vgz) != VGZ_END) retval = VFP_Error(bo, "ESI+Gzip Failed at the very end"); } if (vef->ibuf != NULL) free(vef->ibuf); if (vef->ibuf2 != NULL) free(vef->ibuf2); FREE_OBJ(vef); return (retval); }
static enum fetch_step vbf_stp_condfetch(struct worker *wrk, struct busyobj *bo) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(vbf_beresp2obj(bo)); if (ObjHasAttr(bo->wrk, bo->stale_oc, OA_ESIDATA)) AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_ESIDATA)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_FLAGS)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_GZIPBITS)); 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); } if (ObjIterate(wrk, bo->stale_oc, bo, vbf_objiterator, 0)) (void)VFP_Error(bo->vfc, "Template object failed"); if (bo->stale_oc->flags & OC_F_FAILED) (void)VFP_Error(bo->vfc, "Template object failed"); if (bo->vfc->failed) { VDI_Finish(bo->wrk, bo); return (F_STP_FAIL); } AZ(ObjSetU64(wrk, bo->fetch_objcore, OA_LEN, bo->fetch_objcore->boc->len_so_far)); if (!bo->do_stream) HSH_Unbusy(wrk, bo->fetch_objcore); HSH_Kill(bo->stale_oc); /* Recycle the backend connection before setting BOS_FINISHED to give predictable backend reuse behavior for varnishtest */ VDI_Finish(bo->wrk, bo); ObjSetState(wrk, bo->fetch_objcore, BOS_FINISHED); VSLb_ts_busyobj(bo, "BerespBody", W_TIM_real(wrk)); return (F_STP_DONE); }
static enum fetch_step vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AN(bo->director); AZ(bo->vbc); AZ(bo->should_close); AZ(bo->storage_hint); HTTP_Setup(bo->bereq, bo->ws, bo->vsl, HTTP_Bereq); HTTP_Copy(bo->bereq, bo->bereq0); VCL_backend_fetch_method(bo->vcl, wrk, NULL, bo, bo->bereq->ws); bo->uncacheable = bo->do_pass; http_PrintfHeader(bo->bereq, "X-Varnish: %u", bo->vsl->wid & VSL_IDENTMASK); if (wrk->handling == VCL_RET_ABANDON) { if (bo->req != NULL) vbf_release_req(bo); (void)VFP_Error(bo, "Abandoned in vcl_backend_fetch"); return (F_STP_DONE); } assert (wrk->handling == VCL_RET_FETCH); return (F_STP_FETCHHDR); }
vfp_gzip_end(struct busyobj *bo) { struct vgz *vg; size_t dl; const void *dp; int i; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vg = bo->vgz_rx; CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC); bo->vgz_rx = NULL; if (bo->state == BOS_FAILED) { (void)VGZ_Destroy(&vg); return(0); } do { VGZ_Ibuf(vg, "", 0); if (VGZ_ObufStorage(bo, vg)) return(-1); i = VGZ_Gzip(vg, &dp, &dl, VGZ_FINISH); VBO_extend(bo, dl); } while (i != Z_STREAM_END); VGZ_UpdateObj(vg, bo->fetch_obj); if (VGZ_Destroy(&vg) != VGZ_END) return(VFP_Error(bo, "Gzip error at the very end")); return (0); }
struct storage * VFP_GetStorage(struct busyobj *bo, ssize_t sz) { ssize_t l; struct storage *st; struct object *obj; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); obj = bo->fetch_obj; CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); st = VTAILQ_LAST(&obj->store, storagehead); if (st != NULL && st->len < st->space) return (st); AN(bo->stats); l = fetchfrag; if (l == 0) l = sz; if (l == 0) l = cache_param->fetch_chunksize; st = STV_alloc(bo, l); if (st == NULL) { (void)VFP_Error(bo, "Could not get storage"); } else { AZ(st->len); Lck_Lock(&bo->mtx); VTAILQ_INSERT_TAIL(&obj->store, st, list); Lck_Unlock(&bo->mtx); } return (st); }
v1f_pull_straight(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv) { ssize_t l, lr; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); if (p == vfp_init) return (VFP_OK); if (p == vfp_fini) return (VFP_ERROR); AN(p); AN(lp); AN(priv); l = *lp; *lp = 0; if (!*priv) // XXX: Optimize Content-Len: 0 out earlier return (VFP_END); if (*priv < l) l = *priv; lr = HTTP1_Read(&bo->htc, p, l); if (lr <= 0) return (VFP_Error(bo, "straight insufficient bytes")); *lp = lr; *priv -= lr; if (*priv == 0) return (VFP_END); return (VFP_OK); }
vfp_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp) { ssize_t l; struct vgz *vg; enum vgzret_e vr = VGZ_ERROR; const void *dp; ssize_t dl; enum vfp_status vp = VFP_ERROR; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); AN(p); AN(lp); l = *lp; *lp = 0; VGZ_Obuf(vg, p, l); do { if (VGZ_IbufEmpty(vg)) { l = vg->m_sz; vp = VFP_Suck(vc, vg->m_buf, &l); if (vp == VFP_ERROR) break; if (vp == VFP_END) vg->flag = VGZ_FINISH; VGZ_Ibuf(vg, vg->m_buf, l); } if (!VGZ_IbufEmpty(vg) || vg->flag == VGZ_FINISH) { vr = VGZ_Gzip(vg, &dp, &dl, vg->flag); if (vr < VGZ_OK) return (VFP_Error(vc, "Gzip failed")); if (dl > 0) { VGZ_UpdateObj(vc, vg, VUA_UPDATE); *lp = dl; assert(dp == p); return (VFP_OK); } } AN(VGZ_IbufEmpty(vg)); } while (vg->flag != VGZ_FINISH); if (vr != VGZ_END) return (VFP_Error(vc, "Gzip failed")); VGZ_UpdateObj(vc, vg, VUA_END_GZIP); return (VFP_END); }
static int vbf_fetch_straight(struct busyobj *bo, struct http_conn *htc, ssize_t cl) { int i; assert(htc->body_status == BS_LENGTH); if (cl < 0) { return (VFP_Error(bo, "straight length field bogus")); } else if (cl == 0) return (0); i = bo->vfp->bytes(bo, htc, cl); if (i <= 0) return (VFP_Error(bo, "straight insufficient bytes")); return (0); }
static void vbf_fetch_eof(struct busyobj *bo, struct http_conn *htc) { assert(htc->body_status == BS_EOF); if (bo->vfp->bytes(bo, htc, SSIZE_MAX) < 0) (void)VFP_Error(bo,"eof socket fail"); }
vfp_gunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp) { ssize_t l; struct vgz *vg; enum vgzret_e vr = VGZ_ERROR; const void *dp; ssize_t dl; enum vfp_status vp = VFP_OK; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); AN(p); AN(lp); l = *lp; *lp = 0; VGZ_Obuf(vg, p, l); do { if (VGZ_IbufEmpty(vg)) { l = vg->m_sz; vp = VFP_Suck(vc, vg->m_buf, &l); if (vp == VFP_ERROR) return (vp); VGZ_Ibuf(vg, vg->m_buf, l); } if (!VGZ_IbufEmpty(vg) || vp == VFP_END) { vr = VGZ_Gunzip(vg, &dp, &dl); if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) return(VFP_Error(vc, "Junk after gzip data")); if (vr < VGZ_OK) return (VFP_Error(vc, "Invalid Gzip data: %s", vgz_msg(vg))); if (dl > 0) { *lp = dl; assert(dp == p); return (VFP_OK); } } AN(VGZ_IbufEmpty(vg)); } while (vp == VFP_OK); if (vr != VGZ_END) return(VFP_Error(vc, "Gunzip error at the very end")); return (vp); }
static enum vfp_status vfp_esi_end(struct busyobj *bo, struct vef_priv *vef, enum vfp_status retval) { struct vsb *vsb; ssize_t l; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(vef, VEF_MAGIC); vsb = VEP_Finish(vef->vep, bo); if (vsb != NULL) { if (retval == VFP_END) { l = VSB_len(vsb); assert(l > 0); /* XXX: This is a huge waste of storage... */ bo->fetch_obj->esidata = STV_alloc(bo, l); if (bo->fetch_obj->esidata != NULL) { memcpy(bo->fetch_obj->esidata->ptr, VSB_data(vsb), l); bo->fetch_obj->esidata->len = l; } else { retval = VFP_Error(bo, "Could not allocate storage for esidata"); } } VSB_delete(vsb); } if (vef->vgz != NULL) { VGZ_UpdateObj(vef->vgz, bo->fetch_obj); if (VGZ_Destroy(&vef->vgz) != VGZ_END) retval = VFP_Error(bo, "ESI+Gzip Failed at the very end"); } if (vef->ibuf != NULL) free(vef->ibuf); FREE_OBJ(vef); return (retval); }
static enum vfp_status vfp_esi_end(struct vfp_ctx *vc, struct vef_priv *vef, enum vfp_status retval) { struct vsb *vsb; ssize_t l; void *p; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); CHECK_OBJ_NOTNULL(vef, VEF_MAGIC); vsb = VEP_Finish(vef->vep); if (vsb != NULL) { if (retval == VFP_END) { l = VSB_len(vsb); assert(l > 0); p = ObjSetattr(vc->wrk, vc->oc, OA_ESIDATA, l, VSB_data(vsb)); if (p == NULL) { retval = VFP_Error(vc, "Could not allocate storage for esidata"); } } VSB_delete(vsb); } if (vef->vgz != NULL) { VGZ_UpdateObj(vc, vef->vgz, VUA_END_GZIP); if (VGZ_Destroy(&vef->vgz) != VGZ_END) retval = VFP_Error(vc, "ESI+Gzip Failed at the very end"); } if (vef->ibuf != NULL) free(vef->ibuf); FREE_OBJ(vef); return (retval); }
vfp_testgunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp) { struct vgz *vg; enum vgzret_e vr = VGZ_ERROR; const void *dp; ssize_t dl; enum vfp_status vp; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); AN(p); AN(lp); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); vp = VFP_Suck(vc, p, lp); if (vp == VFP_ERROR) return (vp); if (*lp > 0 || vp == VFP_END) { VGZ_Ibuf(vg, p, *lp); do { VGZ_Obuf(vg, vg->m_buf, vg->m_sz); vr = VGZ_Gunzip(vg, &dp, &dl); if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) return(VFP_Error(vc, "Junk after gzip data")); if (vr < VGZ_OK) return (VFP_Error(vc, "Invalid Gzip data: %s", vgz_msg(vg))); } while (!VGZ_IbufEmpty(vg)); } VGZ_UpdateObj(vc, vg, VUA_UPDATE); if (vp == VFP_END) { if (vr != VGZ_END) return (VFP_Error(vc, "tGunzip failed")); VGZ_UpdateObj(vc, vg, VUA_END_GUNZIP); } return (vp); }
vfp_testgunzip_pull(struct busyobj *bo, struct vfp_entry *vfe, void *p, ssize_t *lp) { struct vgz *vg; enum vgzret_e vr = VGZ_ERROR; const void *dp; size_t dl; enum vfp_status vp; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); AN(p); AN(lp); CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC); vp = VFP_Suck(bo, p, lp); if (vp == VFP_ERROR) return (vp); if (*lp > 0 || vp == VFP_END) { VGZ_Ibuf(vg, p, *lp); do { VGZ_Obuf(vg, vg->m_buf, vg->m_sz); vr = VGZ_Gunzip(vg, &dp, &dl); if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) return(VFP_Error(bo, "Junk after gzip data")); if (vr < VGZ_OK) return (VFP_Error(bo, "Invalid Gzip data: %s", vg->vz.msg)); } while (!VGZ_IbufEmpty(vg)); } if (vp == VFP_END) { if (vr != VGZ_END) return (VFP_Error(bo, "tGunzip failed")); VGZ_UpdateObj(vg, bo->fetch_obj); } return (vp); }
vfp_gunzip_bytes(struct busyobj *bo, struct http_conn *htc, ssize_t bytes) { struct vgz *vg; ssize_t l, wl; int i = -100; size_t dl; const void *dp; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vg = bo->vgz_rx; CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC); AZ(vg->vz.avail_in); while (bytes > 0 || vg->vz.avail_in > 0) { if (vg->vz.avail_in == 0 && bytes > 0) { l = vg->m_sz; if (l > bytes) l = bytes; wl = htc->read(htc, vg->m_buf, l); if (wl <= 0) return (wl); VGZ_Ibuf(vg, vg->m_buf, wl); bytes -= wl; } if (VGZ_ObufStorage(bo, vg)) return(-1); i = VGZ_Gunzip(vg, &dp, &dl); if (i != VGZ_OK && i != VGZ_END) return(VFP_Error(bo, "Gunzip data error")); if (i == VGZ_END && !VGZ_IbufEmpty(vg)) return(VFP_Error(bo, "Junk after gzip data")); VBO_extend(bo, dl); } assert(i == Z_OK || i == Z_STREAM_END); return (1); }
vfp_gunzip_end(struct busyobj *bo) { struct vgz *vg; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vg = bo->vgz_rx; bo->vgz_rx = NULL; CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC); if (bo->state == BOS_FAILED) { (void)VGZ_Destroy(&vg); return(0); } if (VGZ_Destroy(&vg) != VGZ_END) return(VFP_Error(bo, "Gunzip error at the very end")); return (0); }
vfp_testgzip_bytes(struct busyobj *bo, struct http_conn *htc, ssize_t bytes) { struct vgz *vg; ssize_t l, wl; int i = -100; size_t dl; const void *dp; struct storage *st; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vg = bo->vgz_rx; CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC); AZ(vg->vz.avail_in); while (bytes > 0) { st = VFP_GetStorage(bo, 0); if (st == NULL) return(-1); l = st->space - st->len; if (l > bytes) l = bytes; wl = htc->read(htc, st->ptr + st->len, l); if (wl <= 0) return (wl); bytes -= wl; VGZ_Ibuf(vg, st->ptr + st->len, wl); VBO_extend(bo, wl); while (!VGZ_IbufEmpty(vg)) { VGZ_Obuf(vg, vg->m_buf, vg->m_sz); i = VGZ_Gunzip(vg, &dp, &dl); if (i == VGZ_END && !VGZ_IbufEmpty(vg)) return(VFP_Error(bo, "Junk after gzip data")); if (i != VGZ_OK && i != VGZ_END) return(VFP_Error2(bo, "Invalid Gzip data", vg->vz.msg)); } } assert(i == VGZ_OK || i == VGZ_END); return (1); }
ssize_t VRB_Cache(struct req *req, ssize_t maxsize) { CHECK_OBJ_NOTNULL(req, REQ_MAGIC); assert(maxsize >= 0); /* * We only allow caching to happen the first time through vcl_recv{} * where we know we will have no competition or conflicts for the * updates to req.http.* etc. */ if (req->restarts > 0 && req->req_body_status != REQ_BODY_CACHED) return (-1); assert (req->req_step == R_STP_RECV); switch(req->req_body_status) { case REQ_BODY_CACHED: return (req->req_bodybytes); case REQ_BODY_FAIL: return (-1); case REQ_BODY_NONE: return (0); case REQ_BODY_WITHOUT_LEN: case REQ_BODY_WITH_LEN: break; default: WRONG("Wrong req_body_status in VRB_Cache()"); } if (req->htc->content_length > maxsize) { req->req_body_status = REQ_BODY_FAIL; (void)VFP_Error(req->htc->vfc, "Request body too big to cache"); return (-1); } return (vrb_pull(req, maxsize, NULL, NULL)); }
v1f_pull_eof(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv) { ssize_t l, lr; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); if (p == vfp_init) return (VFP_OK); if (p == vfp_fini) return (VFP_ERROR); AN(p); AN(lp); AN(priv); l = *lp; *lp = 0; lr = HTTP1_Read(&bo->htc, p, l); if (lr < 0) return (VFP_Error(bo,"eof socket fail")); if (lr == 0) return (VFP_END); *lp = lr; return (VFP_OK); }
static enum fetch_step vbf_stp_error(struct worker *wrk, struct busyobj *bo) { ssize_t l, ll, o; double now; uint8_t *ptr; struct vsb *synth_body; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(bo->fetch_objcore, OBJCORE_MAGIC); AN(bo->fetch_objcore->flags & OC_F_BUSY); assert(bo->director_state == DIR_S_NULL); wrk->stats->fetch_failed++; now = W_TIM_real(wrk); VSLb_ts_busyobj(bo, "Error", now); if (bo->fetch_objcore->stobj->stevedore != NULL) ObjFreeObj(bo->wrk, bo->fetch_objcore); if (bo->storage == NULL) bo->storage = STV_next(); // XXX: reset all beresp flags ? HTTP_Setup(bo->beresp, bo->ws, bo->vsl, SLT_BerespMethod); http_PutResponse(bo->beresp, "HTTP/1.1", 503, "Backend fetch failed"); http_TimeHeader(bo->beresp, "Date: ", now); http_SetHeader(bo->beresp, "Server: Varnish"); bo->fetch_objcore->t_origin = now; if (!VTAILQ_EMPTY(&bo->fetch_objcore->objhead->waitinglist)) { /* * If there is a waitinglist, it means that there is no * grace-able object, so cache the error return for a * short time, so the waiting list can drain, rather than * each objcore on the waiting list sequentially attempt * to fetch from the backend. */ bo->fetch_objcore->ttl = 1; bo->fetch_objcore->grace = 5; bo->fetch_objcore->keep = 5; } else { bo->fetch_objcore->ttl = 0; bo->fetch_objcore->grace = 0; bo->fetch_objcore->keep = 0; } synth_body = VSB_new_auto(); AN(synth_body); VCL_backend_error_method(bo->vcl, wrk, NULL, bo, synth_body); AZ(VSB_finish(synth_body)); if (wrk->handling == VCL_RET_ABANDON || wrk->handling == VCL_RET_FAIL) { VSB_destroy(&synth_body); return (F_STP_FAIL); } if (wrk->handling == VCL_RET_RETRY) { VSB_destroy(&synth_body); if (bo->retries++ < cache_param->max_retries) return (F_STP_RETRY); VSLb(bo->vsl, SLT_VCL_Error, "Too many retries, failing"); return (F_STP_FAIL); } assert(wrk->handling == VCL_RET_DELIVER); assert(bo->vfc->wrk == bo->wrk); assert(bo->vfc->oc == bo->fetch_objcore); assert(bo->vfc->resp == bo->beresp); assert(bo->vfc->req == bo->bereq); if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); VSB_destroy(&synth_body); return (F_STP_FAIL); } ll = VSB_len(synth_body); o = 0; while (ll > 0) { l = ll; if (VFP_GetStorage(bo->vfc, &l, &ptr) != VFP_OK) break; if (l > ll) l = ll; memcpy(ptr, VSB_data(synth_body) + o, l); VFP_Extend(bo->vfc, l); ll -= l; o += l; } AZ(ObjSetU64(wrk, bo->fetch_objcore, OA_LEN, o)); VSB_destroy(&synth_body); HSH_Unbusy(wrk, bo->fetch_objcore); ObjSetState(wrk, bo->fetch_objcore, BOS_FINISHED); return (F_STP_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); 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_fetch(struct worker *wrk, struct busyobj *bo) { struct object *obj; ssize_t est; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_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 * * XXX: BS_NONE/cl==0 should avoid gzip/gunzip */ /* We do nothing unless the param is set */ if (!cache_param->http_gzip_support) bo->do_gzip = bo->do_gunzip = 0; bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip"); bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL); /* It can't be both */ assert(bo->is_gzip == 0 || bo->is_gunzip == 0); /* We won't gunzip unless it is gzip'ed */ if (bo->do_gunzip && !bo->is_gzip) bo->do_gunzip = 0; /* We wont gzip unless it is ungziped */ if (bo->do_gzip && !bo->is_gunzip) bo->do_gzip = 0; AN(bo->vbc); est = V1F_Setup_Fetch(bo); if (est == 0) { /* * If the length is known to be zero, it's not gziped. * A similar issue exists for chunked encoding but we * don't handle that. See #1320. */ http_Unset(bo->beresp, H_Content_Encoding); bo->is_gzip = 0; bo->is_gunzip = 1; } /* But we can't do both at the same time */ assert(bo->do_gzip == 0 || bo->do_gunzip == 0); /* Fix Content-Encoding, as appropriate */ if (bo->do_gzip) http_SetHeader(bo->beresp, "Content-Encoding: gzip"); else if (bo->do_gunzip) http_Unset(bo->beresp, H_Content_Encoding); if (bo->do_gunzip || (bo->is_gzip && bo->do_esi)) { RFC2616_Weaken_Etag(bo->beresp); VFP_Push(bo, vfp_gunzip_pull, 0); } if (bo->do_esi && bo->do_gzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi && bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi) { VFP_Push(bo, vfp_esi_pull, 0); } else if (bo->do_gzip) { VFP_Push(bo, vfp_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_testgunzip_pull, 0); } 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 (vbf_beresp2obj(bo)) { (void)VFP_Error(bo, "Could not get storage"); VDI_CloseFd(&bo->vbc, &bo->acct); return (F_STP_ERROR); } assert(WRW_IsReleased(wrk)); obj = bo->fetch_obj; if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) obj->gziped = 1; if (bo->do_gzip || bo->do_gunzip) obj->changed_gzip = 1; /* * Ready to fetch the body */ assert(bo->refcount >= 1); AZ(WS_Overflowed(bo->ws_o)); assert (bo->state == BOS_REQ_DONE); if (bo->do_stream) { HSH_Unbusy(&wrk->stats, obj->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); VFP_Fetch_Body(bo, est); } if (bo->failed && !bo->do_stream) { assert(bo->state < BOS_STREAM); if (bo->fetch_obj != NULL) { oc_freeobj(bo->fetch_objcore); bo->fetch_obj = NULL; bo->stats->n_object--; } return (F_STP_ERROR); } if (bo->failed) return (F_STP_FAIL); if (bo->do_stream) assert(bo->state == BOS_STREAM); else { assert(bo->state == BOS_REQ_DONE); HSH_Unbusy(&wrk->stats, obj->objcore); } /* Recycle the backend connection before setting BOS_FINISHED to give predictable backend reuse behavior for varnishtest */ if (bo->vbc != NULL && !(bo->should_close)) { VDI_RecycleFd(&bo->vbc, &bo->acct); AZ(bo->vbc); } VBO_setstate(bo, BOS_FINISHED); VSLb_ts_busyobj(bo, "BerespBody", W_TIM_real(wrk)); return (F_STP_DONE); }
static enum fetch_step vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) { struct object *obj; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_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; bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip"); bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL); /* It can't be both */ assert(bo->is_gzip == 0 || bo->is_gunzip == 0); /* We won't gunzip unless it is gzip'ed */ if (bo->do_gunzip && !bo->is_gzip) bo->do_gunzip = 0; /* We wont gzip unless it is ungziped */ if (bo->do_gzip && !bo->is_gunzip) bo->do_gzip = 0; AN(bo->vbc); /* 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->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->doclose = SC_RX_BODY; return (F_STP_ERROR); } if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); bo->doclose = SC_RX_BODY; return (F_STP_ERROR); } assert(WRW_IsReleased(wrk)); obj = bo->fetch_obj; bo->vfc->body = obj->body; if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) obj->gziped = 1; if (bo->do_gzip || bo->do_gunzip) obj->changed_gzip = 1; if (bo->htc.body_status != BS_NONE) V1F_Setup_Fetch(bo); /* * Ready to fetch the body */ assert(bo->refcount >= 1); AZ(WS_Overflowed(bo->ws_o)); assert (bo->state == BOS_REQ_DONE); if (bo->do_stream) { HSH_Unbusy(&wrk->stats, obj->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); VFP_Fetch_Body(bo); bo->acct.beresp_bodybytes = bo->vfc->bodybytes; } if (bo->vfc->failed && !bo->do_stream) { assert(bo->state < BOS_STREAM); if (bo->fetch_obj != NULL) { ObjFreeObj(bo->fetch_objcore, bo->stats); bo->fetch_obj = NULL; } return (F_STP_ERROR); } if (bo->vfc->failed) return (F_STP_FAIL); if (bo->do_stream) assert(bo->state == BOS_STREAM); else { assert(bo->state == BOS_REQ_DONE); HSH_Unbusy(&wrk->stats, obj->objcore); } /* Recycle the backend connection before setting BOS_FINISHED to give predictable backend reuse behavior for varnishtest */ if (bo->vbc != NULL && bo->doclose == SC_NULL) { VDI_RecycleFd(&bo->vbc, &bo->acct); AZ(bo->vbc); } VBO_setstate(bo, BOS_FINISHED); VSLb_ts_busyobj(bo, "BerespBody", W_TIM_real(wrk)); if (bo->ims_obj != NULL) EXP_Rearm(bo->ims_obj->objcore, bo->ims_obj->objcore->exp.t_origin, 0, 0, 0); return (F_STP_DONE); }
static enum fetch_step vbf_stp_fetchbody(struct worker *wrk, struct busyobj *bo) { ssize_t l; uint8_t *ptr; enum vfp_status vfps = VFP_ERROR; ssize_t est; struct vfp_ctx *vfc; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vfc = bo->vfc; CHECK_OBJ_NOTNULL(vfc, VFP_CTX_MAGIC); AN(vfc->vfp_nxt); est = bo->htc->content_length; if (est < 0) est = 0; do { if (vfc->oc->flags & OC_F_ABANDON) { /* * A pass object and delivery was terminated * We don't fail the fetch, in order for hit-for-pass * objects to be created. */ AN(vfc->oc->flags & OC_F_PASS); VSLb(wrk->vsl, SLT_Debug, "Fetch: Pass delivery abandoned"); bo->htc->doclose = SC_RX_BODY; break; } AZ(vfc->failed); l = est; assert(l >= 0); if (VFP_GetStorage(vfc, &l, &ptr) != VFP_OK) { bo->htc->doclose = SC_RX_BODY; break; } AZ(vfc->failed); vfps = VFP_Suck(vfc, ptr, &l); if (l > 0 && vfps != VFP_ERROR) { bo->acct.beresp_bodybytes += l; VFP_Extend(vfc, l); if (est >= l) est -= l; else est = 0; } } while (vfps == VFP_OK); if (vfc->failed) { (void)VFP_Error(vfc, "Fetch pipeline failed to process"); bo->htc->doclose = SC_RX_BODY; VFP_Close(vfc); VDI_Finish(wrk, bo); if (!bo->do_stream) { assert(bo->fetch_objcore->boc->state < BOS_STREAM); // XXX: doclose = ? return (F_STP_ERROR); } else { wrk->stats->fetch_failed++; return (F_STP_FAIL); } } ObjTrimStore(wrk, vfc->oc); return (F_STP_FETCHEND); }
static enum fetch_step vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) { struct http *hp, *hp2; char *b; uint16_t nhttp; unsigned l; struct vsb *vary = NULL; int varyl = 0; struct object *obj; ssize_t est = -1; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_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 * * XXX: BS_NONE/cl==0 should avoid gzip/gunzip */ /* We do nothing unless the param is set */ if (!cache_param->http_gzip_support) bo->do_gzip = bo->do_gunzip = 0; bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip"); bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL); /* It can't be both */ assert(bo->is_gzip == 0 || bo->is_gunzip == 0); /* We won't gunzip unless it is gzip'ed */ if (bo->do_gunzip && !bo->is_gzip) bo->do_gunzip = 0; /* If we do gunzip, remove the C-E header */ if (bo->do_gunzip) http_Unset(bo->beresp, H_Content_Encoding); /* We wont gzip unless it is ungziped */ if (bo->do_gzip && !bo->is_gunzip) bo->do_gzip = 0; /* If we do gzip, add the C-E header */ if (bo->do_gzip) http_SetHeader(bo->beresp, "Content-Encoding: gzip"); /* But we can't do both at the same time */ assert(bo->do_gzip == 0 || bo->do_gunzip == 0); if (bo->vbc != NULL) est = V1F_Setup_Fetch(bo); if (bo->do_gunzip || (bo->is_gzip && bo->do_esi)) { RFC2616_Weaken_Etag(bo->beresp); VFP_Push(bo, vfp_gunzip_pull, 0); } if (bo->do_esi && bo->do_gzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi && bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi) { VFP_Push(bo, vfp_esi_pull, 0); } else if (bo->do_gzip) { VFP_Push(bo, vfp_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_testgunzip_pull, 0); } 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; l = 0; /* Create Vary instructions */ if (!(bo->fetch_objcore->flags & OC_F_PRIVATE)) { varyl = VRY_Create(bo, &vary); if (varyl > 0) { AN(vary); assert(varyl == VSB_len(vary)); l += varyl; } else if (varyl < 0) { /* * Vary parse error * Complain about it, and make this a pass. */ VSLb(bo->vsl, SLT_Error, "Illegal 'Vary' header from backend, " "making this a pass."); bo->uncacheable = 1; AZ(vary); } else /* No vary */ AZ(vary); } l += http_EstimateWS(bo->beresp, bo->uncacheable ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp); if (bo->uncacheable) bo->fetch_objcore->flags |= OC_F_PASS; if (bo->uncacheable || bo->exp.ttl+bo->exp.grace+bo->exp.keep < cache_param->shortlived) bo->storage_hint = TRANSIENT_STORAGE; AZ(bo->stats); bo->stats = &wrk->stats; AN(bo->fetch_objcore); obj = STV_NewObject(bo, bo->storage_hint, l, nhttp); if (obj == NULL) { /* * Try to salvage the transaction by allocating a * shortlived object on Transient storage. */ if (bo->exp.ttl > cache_param->shortlived) bo->exp.ttl = cache_param->shortlived; bo->exp.grace = 0.0; bo->exp.keep = 0.0; obj = STV_NewObject(bo, TRANSIENT_STORAGE, l, nhttp); } if (obj == NULL) { bo->stats = NULL; (void)VFP_Error(bo, "Could not get storage"); VDI_CloseFd(&bo->vbc); return (F_STP_DONE); } CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); bo->storage_hint = NULL; AZ(bo->fetch_obj); bo->fetch_obj = obj; if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) obj->gziped = 1; if (vary != NULL) { obj->vary = (void *)WS_Copy(obj->http->ws, VSB_data(vary), varyl); AN(obj->vary); (void)VRY_Validate(obj->vary); VSB_delete(vary); } obj->vxid = bo->vsl->wid; obj->response = bo->err_code; WS_Assert(bo->ws_o); /* Filter into object */ hp = bo->beresp; hp2 = obj->http; hp2->logtag = HTTP_Obj; http_FilterResp(hp, hp2, bo->uncacheable ? HTTPH_R_PASS : HTTPH_A_INS); http_CopyHome(hp2); if (http_GetHdr(hp, H_Last_Modified, &b)) obj->last_modified = VTIM_parse(b); else obj->last_modified = floor(bo->exp.t_origin); assert(WRW_IsReleased(wrk)); /* * Ready to fetch the body */ assert(bo->refcount >= 1); AZ(WS_Overflowed(bo->ws_o)); if (bo->do_stream) HSH_Unbusy(&wrk->stats, obj->objcore); assert(bo->state == BOS_REQ_DONE); VBO_setstate(bo, BOS_FETCHING); switch (bo->htc.body_status) { case BS_NONE: break; case BS_ERROR: /* XXX: Why not earlier ? */ bo->should_close |= VFP_Error(bo, "error incompatible Transfer-Encoding"); break; default: if (bo->vbc == NULL) (void)VFP_Error(bo, "Backend connection gone"); else VFP_Fetch_Body(bo, est); } bo->stats = NULL; bo->t_body = VTIM_mono(); if (bo->vbc != NULL) { if (bo->should_close) VDI_CloseFd(&bo->vbc); else VDI_RecycleFd(&bo->vbc); AZ(bo->vbc); } http_Teardown(bo->bereq); http_Teardown(bo->beresp); VSLb(bo->vsl, SLT_Fetch_Body, "%u(%s)", bo->htc.body_status, body_status_2str(bo->htc.body_status)); if (bo->state == BOS_FAILED) { wrk->stats.fetch_failed++; } else { assert(bo->state == BOS_FETCHING); VSLb(bo->vsl, SLT_Length, "%zd", obj->len); { /* Sanity check fetch methods accounting */ ssize_t uu; struct storage *st; uu = 0; VTAILQ_FOREACH(st, &obj->store, list) uu += st->len; if (bo->do_stream) /* Streaming might have started freeing stuff */ assert(uu <= obj->len); else assert(uu == obj->len); } } if (!bo->do_stream && bo->state != BOS_FAILED) HSH_Unbusy(&wrk->stats, obj->objcore); if (bo->state != BOS_FAILED && !(obj->objcore->flags & OC_F_PRIVATE)) { EXP_Insert(obj->objcore); AN(obj->objcore->ban); } HSH_Complete(obj->objcore); assert(bo->refcount >= 1); if (bo->state != BOS_FAILED) VBO_setstate(bo, BOS_FINISHED); VSLb(bo->vsl, SLT_Debug, "YYY REF %d %d", bo->refcount, bo->fetch_obj->objcore->refcnt); return (F_STP_DONE); }
static enum fetch_step vbf_stp_condfetch(struct worker *wrk, struct busyobj *bo) { unsigned l; uint16_t nhttp; struct object *obj; struct objiter *oi; void *sp; ssize_t sl, al, tl, vl; struct storage *st; enum objiter_status ois; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); l = 0; if (bo->ims_obj->vary != NULL) { vl = VRY_Validate(bo->ims_obj->vary); l += vl; } else vl = 0; l += http_EstimateWS(bo->ims_obj->http, 0, &nhttp); bo->stats = &wrk->stats; obj = STV_NewObject(bo, bo->storage_hint, l, nhttp); if (obj == NULL) { (void)VFP_Error(bo, "Could not get storage"); VDI_CloseFd(&bo->vbc); return (F_STP_DONE); } AZ(bo->fetch_obj); bo->fetch_obj = obj; obj->gziped = bo->ims_obj->gziped; obj->gzip_start = bo->ims_obj->gzip_start; obj->gzip_last = bo->ims_obj->gzip_last; obj->gzip_stop = bo->ims_obj->gzip_stop; /* XXX: ESI */ if (bo->ims_obj->vary != NULL) { obj->vary = (void *)WS_Copy(obj->http->ws, bo->ims_obj->vary, vl); assert(vl == VRY_Validate(obj->vary)); } obj->vxid = bo->vsl->wid; obj->http->logtag = HTTP_Obj; /* XXX: we should have our own HTTP_A_CONDFETCH */ http_FilterResp(bo->ims_obj->http, obj->http, HTTPH_A_INS); http_CopyHome(obj->http); AZ(WS_Overflowed(bo->ws_o)); VBO_setstate(bo, BOS_FETCHING); HSH_Unbusy(&wrk->stats, obj->objcore); if (!(obj->objcore->flags & OC_F_PRIVATE)) { EXP_Insert(obj->objcore); AN(obj->objcore->ban); } st = NULL; al = 0; oi = ObjIterBegin(wrk, bo->ims_obj); do { ois = ObjIter(oi, &sp, &sl); while (sl > 0) { if (st == NULL) { st = VFP_GetStorage(bo, bo->ims_obj->len - al); XXXAN(st); } tl = sl; if (tl > st->space - st->len) tl = st->space - st->len; memcpy(st->ptr + st->len, sp, tl); al += tl; sp = (char *)sp + tl; sl -= tl; VBO_extend(bo, al); if (st->len == st->space) st = NULL; } } while (ois == OIS_DATA || ois == OIS_STREAM); ObjIterEnd(&oi); bo->stats = NULL; assert(al == bo->ims_obj->len); assert(obj->len == al); if (bo->state != BOS_FAILED) VBO_setstate(bo, BOS_FINISHED); HSH_Complete(obj->objcore); return (F_STP_DONE); }
static enum fetch_step vbf_stp_condfetch(struct worker *wrk, struct busyobj *bo) { void *oi; void *sp; ssize_t sl, al, l; uint8_t *ptr; enum objiter_status ois; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(vbf_beresp2obj(bo)); if (ObjGetattr(bo->wrk, bo->stale_oc, OA_ESIDATA, NULL) != NULL) AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_ESIDATA)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_FLAGS)); AZ(ObjCopyAttr(bo->wrk, bo->fetch_objcore, bo->stale_oc, OA_GZIPBITS)); if (bo->do_stream) { HSH_Unbusy(wrk, bo->fetch_objcore); VBO_setstate(bo, BOS_STREAM); } al = 0; oi = ObjIterBegin(wrk, bo->stale_oc); do { ois = ObjIter(bo->stale_oc, oi, &sp, &sl); if (ois == OIS_ERROR) (void)VFP_Error(bo->vfc, "Template object failed"); while (sl > 0) { l = ObjGetLen(bo->wrk, bo->stale_oc) - al; assert(l > 0); if (VFP_GetStorage(bo->vfc, &l, &ptr) != VFP_OK) break; if (sl < l) l = sl; memcpy(ptr, sp, l); VBO_extend(bo, l); al += l; sp = (char *)sp + l; sl -= l; } } while (!bo->vfc->failed && (ois == OIS_DATA || ois == OIS_STREAM)); ObjIterEnd(bo->stale_oc, &oi); if (bo->stale_oc->flags & OC_F_FAILED) (void)VFP_Error(bo->vfc, "Template object failed"); if (bo->vfc->failed) { VDI_Finish(bo->wrk, bo); return (F_STP_FAIL); } if (!bo->do_stream) HSH_Unbusy(wrk, bo->fetch_objcore); assert(ObjGetLen(bo->wrk, bo->fetch_objcore) == al); EXP_Rearm(bo->stale_oc, bo->stale_oc->exp.t_origin, 0, 0, 0); /* 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)); return (F_STP_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 void vbf_fetch_body_helper(struct busyobj *bo) { ssize_t l; uint8_t *ptr; enum vfp_status vfps = VFP_ERROR; ssize_t est; struct vfp_ctx *vfc; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); vfc = bo->vfc; CHECK_OBJ_NOTNULL(vfc, VFP_CTX_MAGIC); AN(vfc->vfp_nxt); est = bo->htc->content_length; if (est < 0) est = 0; do { if (bo->abandon) { /* * A pass object and delivery was terminated * We don't fail the fetch, in order for hit-for-pass * objects to be created. */ AN(vfc->oc->flags & OC_F_PASS); VSLb(vfc->wrk->vsl, SLT_FetchError, "Pass delivery abandoned"); vfps = VFP_END; bo->htc->doclose = SC_RX_BODY; break; } AZ(vfc->failed); l = est; assert(l >= 0); if (VFP_GetStorage(vfc, &l, &ptr) != VFP_OK) { bo->htc->doclose = SC_RX_BODY; break; } AZ(vfc->failed); vfps = VFP_Suck(vfc, ptr, &l); if (l > 0 && vfps != VFP_ERROR) { bo->acct.beresp_bodybytes += l; VBO_extend(bo, l); if (est >= l) est -= l; else est = 0; } } while (vfps == VFP_OK); VFP_Close(vfc); if (vfps == VFP_ERROR) { AN(vfc->failed); (void)VFP_Error(vfc, "Fetch pipeline failed to process"); bo->htc->doclose = SC_RX_BODY; } if (!bo->do_stream) ObjTrimStore(bo->wrk, vfc->oc); }