static int cnt_miss(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->vcl, VCL_CONF_MAGIC); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); bo = req->busyobj; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(req->obj); HTTP_Setup(bo->bereq, bo->ws, bo->vsl, HTTP_Bereq); http_FilterReq(req, HTTPH_R_FETCH); http_ForceGet(bo->bereq); if (cache_param->http_gzip_support) { /* * We always ask the backend for gzip, even if the * client doesn't grok it. We will uncompress for * the minority of clients which don't. */ http_Unset(bo->bereq, H_Accept_Encoding); http_SetHeader(bo->bereq, "Accept-Encoding: gzip"); } VCL_miss_method(req); if (req->handling == VCL_RET_FETCH) { CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); req->req_step = R_STP_FETCH; return (0); } AZ(HSH_Deref(&wrk->stats, req->objcore, NULL)); req->objcore = NULL; http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &req->busyobj); switch(req->handling) { case VCL_RET_ERROR: req->req_step = R_STP_ERROR; break; case VCL_RET_PASS: req->req_step = R_STP_PASS; break; case VCL_RET_RESTART: req->req_step = R_STP_RESTART; break; default: WRONG("Illegal action in vcl_miss{}"); } return (0); }
static int 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->obj, OBJECT_MAGIC); bo = req->busyobj; CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); if (bo != NULL) { while (bo->state < BOS_FAILED) (void)usleep(10000); assert(bo->state >= BOS_FAILED); if (bo->state == BOS_FAILED) { HSH_Deref(&wrk->stats, NULL, &req->obj); VBO_DerefBusyObj(wrk, &req->busyobj); req->err_code = 503; req->req_step = R_STP_ERROR; return (0); } VBO_DerefBusyObj(wrk, &req->busyobj); } AZ(req->busyobj); req->director = NULL; req->restarts = 0; RES_WriteObj(req); /* No point in saving the body if it is hit-for-pass */ if (req->obj->objcore->flags & OC_F_PASS) STV_Freestore(req->obj); assert(WRW_IsReleased(wrk)); (void)HSH_Deref(&wrk->stats, NULL, &req->obj); http_Teardown(req->resp); return (1); }
static void * exp_timer(struct sess *sp, void *priv) { struct objcore *oc; struct object *o; double t; struct objcore_head *lru; (void)priv; AZ(sleep(10)); /* XXX: Takes time for VCL to arrive */ VCL_Get(&sp->vcl); t = TIM_real(); while (1) { Lck_Lock(&exp_mtx); oc = binheap_root(exp_heap); CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC); if (oc == NULL || oc->timer_when > t) { /* XXX: > or >= ? */ Lck_Unlock(&exp_mtx); WSL_Flush(sp->wrk, 0); WRK_SumStat(sp->wrk); AZ(sleep(1)); VCL_Refresh(&sp->vcl); t = TIM_real(); continue; } o = oc->obj; CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(o->objhead, OBJHEAD_MAGIC); assert(oc->flags & OC_F_ONLRU); assert(oc->timer_idx != BINHEAP_NOIDX); binheap_delete(exp_heap, oc->timer_idx); assert(oc->timer_idx == BINHEAP_NOIDX); lru = STV_lru(o->objstore); AN(lru); VTAILQ_REMOVE(lru, o->objcore, lru_list); oc->flags &= ~OC_F_ONLRU; { /* Sanity checking */ struct objcore *oc2 = binheap_root(exp_heap); if (oc2 != NULL) { assert(oc2->timer_idx != BINHEAP_NOIDX); assert(oc2->timer_when >= oc->timer_when); } } VSL_stats->n_expired++; Lck_Unlock(&exp_mtx); WSL(sp->wrk, SLT_ExpKill, 0, "%u %d", o->xid, (int)(o->ttl - t)); HSH_Deref(sp->wrk, &o); } }
static int cnt_hit(struct sess *sp) { struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); AZ(wrk->busyobj); assert(!(wrk->obj->objcore->flags & OC_F_PASS)); VCL_hit_method(sp); if (sp->handling == VCL_RET_DELIVER) { /* Dispose of any body part of the request */ (void)FetchReqBody(sp); //AZ(wrk->busyobj->bereq->ws); //AZ(wrk->busyobj->beresp->ws); sp->step = STP_PREPRESP; return (0); } /* Drop our object, we won't need it */ (void)HSH_Deref(wrk, NULL, &wrk->obj); wrk->objcore = NULL; switch(sp->handling) { case VCL_RET_PASS: sp->step = STP_PASS; return (0); case VCL_RET_ERROR: sp->step = STP_ERROR; return (0); case VCL_RET_RESTART: sp->director = NULL; sp->restarts++; sp->step = STP_RECV; return (0); default: WRONG("Illegal action in vcl_hit{}"); } }
static int cnt_hit(struct worker *wrk, struct req *req) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); AZ(req->objcore); AZ(req->busyobj); assert(!(req->obj->objcore->flags & OC_F_PASS)); VCL_hit_method(req); if (req->handling == VCL_RET_DELIVER) { //AZ(req->busyobj->bereq->ws); //AZ(req->busyobj->beresp->ws); (void)FetchReqBody(req, 0); req->req_step = R_STP_PREPRESP; return (0); } /* Drop our object, we won't need it */ (void)HSH_Deref(&wrk->stats, NULL, &req->obj); req->objcore = NULL; switch(req->handling) { case VCL_RET_PASS: req->req_step = R_STP_PASS; return (0); case VCL_RET_ERROR: req->req_step = R_STP_ERROR; return (0); case VCL_RET_RESTART: req->req_step = R_STP_RESTART; return (0); default: WRONG("Illegal action in vcl_hit{}"); } }
static int cnt_deliver(struct sess *sp) { CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); sp->t_resp = TIM_real(); if (sp->obj->objhead != NULL) { if ((sp->t_resp - sp->obj->last_lru) > params->lru_timeout && EXP_Touch(sp->obj)) sp->obj->last_lru = sp->t_resp; /* XXX: locking ? */ sp->obj->last_use = sp->t_resp; /* XXX: locking ? */ } sp->wrk->resp = &sp->wrk->http[2]; http_Setup(sp->wrk->resp, sp->wrk->ws); RES_BuildHttp(sp); VCL_deliver_method(sp); switch (sp->handling) { case VCL_RET_DELIVER: break; case VCL_RET_RESTART: INCOMPL(); break; default: WRONG("Illegal action in vcl_deliver{}"); } sp->director = NULL; sp->restarts = 0; RES_WriteObj(sp); AZ(sp->wrk->wfd); HSH_Deref(sp->wrk, &sp->obj); sp->wrk->resp = NULL; sp->step = STP_DONE; return (0); }
static int cnt_deliver(struct sess *sp) { struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); AZ(sp->wrk->busyobj); sp->director = NULL; sp->restarts = 0; RES_WriteObj(sp); assert(WRW_IsReleased(wrk)); assert(wrk->wrw.ciov == wrk->wrw.siov); (void)HSH_Deref(wrk, NULL, &wrk->obj); http_Setup(wrk->resp, NULL); sp->step = STP_DONE; return (0); }
static int cnt_lookup(struct worker *wrk, struct req *req) { struct objcore *oc; struct object *o; struct objhead *oh; struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AZ(req->objcore); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); AZ(req->busyobj); VRY_Prep(req); AZ(req->objcore); oc = HSH_Lookup(req); if (oc == NULL) { /* * We lost the session to a busy object, disembark the * worker thread. We return to STP_LOOKUP when the busy * object has been unbusied, and still have the objhead * around to restart the lookup with. */ return (2); } AZ(req->objcore); CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); oh = oc->objhead; CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); /* If we inserted a new object it's a miss */ if (oc->flags & OC_F_BUSY) { AZ(req->busyobj); bo = VBO_GetBusyObj(wrk, req); req->busyobj = bo; /* One ref for req, one for FetchBody */ bo->refcount = 2; VRY_Finish(req, bo); oc->busyobj = bo; wrk->stats.cache_miss++; req->objcore = oc; req->req_step = R_STP_MISS; return (0); } /* We are not prepared to do streaming yet */ XXXAZ(req->busyobj); o = oc_getobj(&wrk->stats, oc); CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); req->obj = o; VRY_Finish(req, NULL); if (oc->flags & OC_F_PASS) { wrk->stats.cache_hitpass++; VSLb(req->vsl, SLT_HitPass, "%u", req->obj->vxid); (void)HSH_Deref(&wrk->stats, NULL, &req->obj); AZ(req->objcore); req->req_step = R_STP_PASS; return (0); } wrk->stats.cache_hit++; VSLb(req->vsl, SLT_Hit, "%u", req->obj->vxid); req->req_step = R_STP_HIT; return (0); }
static int cnt_fetchbody(struct worker *wrk, struct req *req) { struct http *hp, *hp2; char *b; uint16_t nhttp; unsigned l; struct vsb *vary = NULL; int varyl = 0, pass; struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); bo = req->busyobj; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); assert(req->handling == VCL_RET_DELIVER); if (req->objcore->objhead == NULL) { /* This is a pass from vcl_recv */ pass = 1; /* VCL may have fiddled this, but that doesn't help */ bo->exp.ttl = -1.; } else if (bo->do_pass) { pass = 1; } else { /* regular object */ pass = 0; } /* * 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; /* 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); /* ESI takes precedence and handles gzip/gunzip itself */ if (bo->do_esi) bo->vfp = &vfp_esi; else if (bo->do_gunzip) bo->vfp = &vfp_gunzip; else if (bo->do_gzip) bo->vfp = &vfp_gzip; else if (bo->is_gzip) bo->vfp = &vfp_testgzip; if (bo->do_esi || req->esi_level > 0) bo->do_stream = 0; if (!req->wantbody) bo->do_stream = 0; /* No reason to try streaming a non-existing body */ if (bo->body_status == BS_NONE) bo->do_stream = 0; l = http_EstimateWS(bo->beresp, pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp); /* Create Vary instructions */ if (req->objcore->objhead != NULL) { CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); vary = VRY_Create(req, bo->beresp); if (vary != NULL) { varyl = VSB_len(vary); assert(varyl > 0); l += varyl; } } /* * Space for producing a Content-Length: header including padding * A billion gigabytes is enough for anybody. */ l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *); if (bo->exp.ttl < cache_param->shortlived || req->objcore == NULL) req->storage_hint = TRANSIENT_STORAGE; AZ(bo->stats); bo->stats = &wrk->stats; req->obj = STV_NewObject(bo, &req->objcore, req->storage_hint, l, nhttp); if (req->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; req->obj = STV_NewObject(bo, &req->objcore, TRANSIENT_STORAGE, l, nhttp); } bo->stats = NULL; if (req->obj == NULL) { req->err_code = 503; req->req_step = R_STP_ERROR; VDI_CloseFd(&bo->vbc); VBO_DerefBusyObj(wrk, &req->busyobj); return (0); } CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); req->storage_hint = NULL; AZ(bo->fetch_obj); bo->fetch_obj = req->obj; if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) req->obj->gziped = 1; if (vary != NULL) { req->obj->vary = (void *)WS_Copy(req->obj->http->ws, VSB_data(vary), varyl); AN(req->obj->vary); VRY_Validate(req->obj->vary); VSB_delete(vary); } req->obj->vxid = bo->vsl->wid; req->obj->response = req->err_code; WS_Assert(req->obj->ws_o); /* Filter into object */ hp = bo->beresp; hp2 = req->obj->http; hp2->logtag = HTTP_Obj; http_FilterResp(hp, hp2, pass ? HTTPH_R_PASS : HTTPH_A_INS); http_CopyHome(hp2); if (http_GetHdr(hp, H_Last_Modified, &b)) req->obj->last_modified = VTIM_parse(b); else req->obj->last_modified = floor(bo->exp.entered); assert(WRW_IsReleased(wrk)); /* * If we can deliver a 304 reply, we don't bother streaming. * Notice that vcl_deliver{} could still nuke the headers * that allow the 304, in which case we return 200 non-stream. */ if (req->obj->response == 200 && req->http->conds && RFC2616_Do_Cond(req)) bo->do_stream = 0; /* * Ready to fetch the body */ bo->fetch_task.func = FetchBody; bo->fetch_task.priv = bo; assert(bo->refcount == 2); /* one for each thread */ if (req->obj->objcore->objhead != NULL) { EXP_Insert(req->obj); AN(req->obj->objcore->ban); AZ(req->obj->ws_o->overflow); HSH_Unbusy(&wrk->stats, req->obj->objcore); } if (!bo->do_stream || Pool_Task(wrk->pool, &bo->fetch_task, POOL_NO_QUEUE)) FetchBody(wrk, bo); if (req->obj->objcore->objhead != NULL) HSH_Ref(req->obj->objcore); if (bo->state == BOS_FINISHED) { VBO_DerefBusyObj(wrk, &req->busyobj); } else if (bo->state == BOS_FAILED) { /* handle early failures */ HSH_Deref(&wrk->stats, NULL, &req->obj); VBO_DerefBusyObj(wrk, &req->busyobj); req->err_code = 503; req->req_step = R_STP_ERROR; return (0); } assert(WRW_IsReleased(wrk)); req->req_step = R_STP_PREPRESP; return (0); }
static int cnt_fetch(struct worker *wrk, struct req *req) { int i, need_host_hdr; struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); bo = req->busyobj; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AN(req->director); AZ(bo->vbc); AZ(bo->should_close); AZ(req->storage_hint); HTTP_Setup(bo->beresp, bo->ws, bo->vsl, HTTP_Beresp); need_host_hdr = !http_GetHdr(bo->bereq, H_Host, NULL); req->acct_req.fetch++; i = FetchHdr(req, need_host_hdr, req->objcore->objhead == NULL); /* * If we recycle a backend connection, there is a finite chance * that the backend closed it before we get a request to it. * Do a single retry in that case. */ if (i == 1) { VSC_C_main->backend_retry++; i = FetchHdr(req, need_host_hdr, req->objcore->objhead == NULL); } if (i) { req->handling = VCL_RET_ERROR; req->err_code = 503; } else { /* * 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 * NB: Also sets other wrk variables */ bo->body_status = RFC2616_Body(bo, &wrk->stats); req->err_code = http_GetStatus(bo->beresp); /* * What does RFC2616 think about TTL ? */ EXP_Clr(&bo->exp); bo->exp.entered = W_TIM_real(wrk); RFC2616_Ttl(bo); /* pass from vclrecv{} has negative TTL */ if (req->objcore->objhead == NULL) bo->exp.ttl = -1.; AZ(bo->do_esi); AZ(bo->do_pass); VCL_fetch_method(req); if (bo->do_pass) req->objcore->flags |= OC_F_PASS; switch (req->handling) { case VCL_RET_DELIVER: req->req_step = R_STP_FETCHBODY; return (0); default: break; } /* We are not going to fetch the body, Close the connection */ VDI_CloseFd(&bo->vbc); } /* Clean up partial fetch */ AZ(bo->vbc); if (req->objcore->objhead != NULL || req->handling == VCL_RET_ERROR) { CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); AZ(HSH_Deref(&wrk->stats, req->objcore, NULL)); req->objcore = NULL; } assert(bo->refcount == 2); VBO_DerefBusyObj(wrk, &bo); VBO_DerefBusyObj(wrk, &req->busyobj); req->director = NULL; req->storage_hint = NULL; switch (req->handling) { case VCL_RET_RESTART: req->req_step = R_STP_RESTART; return (0); case VCL_RET_ERROR: req->req_step = R_STP_ERROR; return (0); default: WRONG("Illegal action in vcl_fetch{}"); } }
static int cnt_streambody(struct sess *sp) { int i; struct stream_ctx sctx; uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ? cache_param->gzip_stack_buffer : 1]; struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); memset(&sctx, 0, sizeof sctx); sctx.magic = STREAM_CTX_MAGIC; AZ(wrk->sctx); wrk->sctx = &sctx; if (wrk->res_mode & RES_GUNZIP) { sctx.vgz = VGZ_NewUngzip(wrk, "U S -"); sctx.obuf = obuf; sctx.obuf_len = sizeof (obuf); } RES_StreamStart(sp); AssertObjCorePassOrBusy(wrk->obj->objcore); i = FetchBody(wrk, wrk->obj); http_Setup(wrk->busyobj->bereq, NULL); http_Setup(wrk->busyobj->beresp, NULL); wrk->busyobj->vfp = NULL; AZ(wrk->busyobj->vbc); AN(sp->director); if (!i && wrk->obj->objcore != NULL) { EXP_Insert(wrk->obj); AN(wrk->obj->objcore); AN(wrk->obj->objcore->ban); HSH_Unbusy(wrk); } else { sp->doclose = "Stream error"; } wrk->acct_tmp.fetch++; sp->director = NULL; sp->restarts = 0; RES_StreamEnd(sp); if (wrk->res_mode & RES_GUNZIP) (void)VGZ_Destroy(&sctx.vgz, sp->vsl_id); wrk->sctx = NULL; assert(WRW_IsReleased(wrk)); assert(wrk->wrw.ciov == wrk->wrw.siov); (void)HSH_Deref(wrk, NULL, &wrk->obj); VBO_DerefBusyObj(wrk, &wrk->busyobj); http_Setup(wrk->resp, NULL); sp->step = STP_DONE; return (0); }
static int cnt_fetch(struct sess *sp) { int i, need_host_hdr; struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); AN(sp->director); AZ(wrk->busyobj->vbc); AZ(wrk->busyobj->should_close); AZ(wrk->storage_hint); http_Setup(wrk->busyobj->beresp, wrk->ws); need_host_hdr = !http_GetHdr(wrk->busyobj->bereq, H_Host, NULL); i = FetchHdr(sp, need_host_hdr); /* * If we recycle a backend connection, there is a finite chance * that the backend closed it before we get a request to it. * Do a single retry in that case. */ if (i == 1) { VSC_C_main->backend_retry++; i = FetchHdr(sp, need_host_hdr); } if (i) { sp->handling = VCL_RET_ERROR; sp->err_code = 503; } else { /* * 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(wrk->busyobj->beresp, H_Cache_Control); http_CollectHdr(wrk->busyobj->beresp, H_Vary); /* * Figure out how the fetch is supposed to happen, before the * headers are adultered by VCL * NB: Also sets other wrk variables */ wrk->busyobj->body_status = RFC2616_Body(sp); sp->err_code = http_GetStatus(wrk->busyobj->beresp); /* * What does RFC2616 think about TTL ? */ EXP_Clr(&wrk->busyobj->exp); wrk->busyobj->exp.entered = W_TIM_real(wrk); RFC2616_Ttl(sp); /* pass from vclrecv{} has negative TTL */ if (wrk->objcore == NULL) wrk->busyobj->exp.ttl = -1.; AZ(wrk->busyobj->do_esi); VCL_fetch_method(sp); switch (sp->handling) { case VCL_RET_HIT_FOR_PASS: if (wrk->objcore != NULL) wrk->objcore->flags |= OC_F_PASS; sp->step = STP_FETCHBODY; return (0); case VCL_RET_DELIVER: AssertObjCorePassOrBusy(wrk->objcore); sp->step = STP_FETCHBODY; return (0); default: break; } /* We are not going to fetch the body, Close the connection */ VDI_CloseFd(wrk, &wrk->busyobj->vbc); } /* Clean up partial fetch */ AZ(wrk->busyobj->vbc); if (wrk->objcore != NULL) { CHECK_OBJ_NOTNULL(wrk->objcore, OBJCORE_MAGIC); AZ(HSH_Deref(wrk, wrk->objcore, NULL)); wrk->objcore = NULL; } VBO_DerefBusyObj(wrk, &wrk->busyobj); sp->director = NULL; wrk->storage_hint = NULL; switch (sp->handling) { case VCL_RET_RESTART: sp->restarts++; sp->step = STP_RECV; return (0); case VCL_RET_ERROR: sp->step = STP_ERROR; return (0); default: WRONG("Illegal action in vcl_fetch{}"); } }
static int cnt_prepresp(struct sess *sp) { struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(wrk->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); if (wrk->busyobj != NULL) { CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); AN(wrk->busyobj->do_stream); AssertObjCorePassOrBusy(wrk->obj->objcore); } wrk->res_mode = 0; if (wrk->busyobj == NULL) wrk->res_mode |= RES_LEN; if (wrk->busyobj != NULL && (wrk->busyobj->h_content_length != NULL || !wrk->busyobj->do_stream) && !wrk->busyobj->do_gzip && !wrk->busyobj->do_gunzip) wrk->res_mode |= RES_LEN; if (!sp->disable_esi && wrk->obj->esidata != NULL) { /* In ESI mode, we don't know the aggregate length */ wrk->res_mode &= ~RES_LEN; wrk->res_mode |= RES_ESI; } if (sp->esi_level > 0) { wrk->res_mode &= ~RES_LEN; wrk->res_mode |= RES_ESI_CHILD; } if (cache_param->http_gzip_support && wrk->obj->gziped && !RFC2616_Req_Gzip(sp)) { /* * We don't know what it uncompresses to * XXX: we could cache that */ wrk->res_mode &= ~RES_LEN; wrk->res_mode |= RES_GUNZIP; } if (!(wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) { if (wrk->obj->len == 0 && (wrk->busyobj == NULL || !wrk->busyobj->do_stream)) /* * If the object is empty, neither ESI nor GUNZIP * can make it any different size */ wrk->res_mode |= RES_LEN; else if (!sp->wantbody) { /* Nothing */ } else if (sp->http->protover >= 11) { wrk->res_mode |= RES_CHUNKED; } else { wrk->res_mode |= RES_EOF; sp->doclose = "EOF mode"; } } sp->t_resp = W_TIM_real(wrk); if (wrk->obj->objcore != NULL) { if ((sp->t_resp - wrk->obj->last_lru) > cache_param->lru_timeout && EXP_Touch(wrk->obj->objcore)) wrk->obj->last_lru = sp->t_resp; wrk->obj->last_use = sp->t_resp; /* XXX: locking ? */ } http_Setup(wrk->resp, wrk->ws); RES_BuildHttp(sp); VCL_deliver_method(sp); switch (sp->handling) { case VCL_RET_DELIVER: break; case VCL_RET_RESTART: if (sp->restarts >= cache_param->max_restarts) break; if (wrk->busyobj != NULL) { AN(wrk->busyobj->do_stream); VDI_CloseFd(wrk, &wrk->busyobj->vbc); HSH_Drop(wrk); VBO_DerefBusyObj(wrk, &wrk->busyobj); } else { (void)HSH_Deref(wrk, NULL, &wrk->obj); } AZ(wrk->obj); sp->restarts++; sp->director = NULL; http_Setup(wrk->resp, NULL); sp->step = STP_RECV; return (0); default: WRONG("Illegal action in vcl_deliver{}"); } if (wrk->busyobj != NULL && wrk->busyobj->do_stream) { AssertObjCorePassOrBusy(wrk->obj->objcore); sp->step = STP_STREAMBODY; } else { sp->step = STP_DELIVER; } return (0); }
static int cnt_miss(struct sess *sp) { struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); AZ(wrk->obj); AN(wrk->objcore); CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); WS_Reset(wrk->ws, NULL); wrk->busyobj = VBO_GetBusyObj(wrk); http_Setup(wrk->busyobj->bereq, wrk->ws); http_FilterHeader(sp, HTTPH_R_FETCH); http_ForceGet(wrk->busyobj->bereq); if (cache_param->http_gzip_support) { /* * We always ask the backend for gzip, even if the * client doesn't grok it. We will uncompress for * the minority of clients which don't. */ http_Unset(wrk->busyobj->bereq, H_Accept_Encoding); http_SetHeader(wrk, sp->vsl_id, wrk->busyobj->bereq, "Accept-Encoding: gzip"); } wrk->connect_timeout = 0; wrk->first_byte_timeout = 0; wrk->between_bytes_timeout = 0; VCL_miss_method(sp); switch(sp->handling) { case VCL_RET_ERROR: AZ(HSH_Deref(wrk, wrk->objcore, NULL)); wrk->objcore = NULL; http_Setup(wrk->busyobj->bereq, NULL); VBO_DerefBusyObj(wrk, &wrk->busyobj); sp->step = STP_ERROR; return (0); case VCL_RET_PASS: AZ(HSH_Deref(wrk, wrk->objcore, NULL)); wrk->objcore = NULL; VBO_DerefBusyObj(wrk, &wrk->busyobj); sp->step = STP_PASS; return (0); case VCL_RET_FETCH: CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); sp->step = STP_FETCH; return (0); case VCL_RET_RESTART: AZ(HSH_Deref(wrk, wrk->objcore, NULL)); wrk->objcore = NULL; VBO_DerefBusyObj(wrk, &wrk->busyobj); INCOMPL(); default: WRONG("Illegal action in vcl_miss{}"); } }
static int cnt_lookup(struct sess *sp) { struct objcore *oc; struct object *o; struct objhead *oh; struct worker *wrk; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); wrk = sp->wrk; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); AZ(wrk->busyobj); if (sp->hash_objhead == NULL) { /* Not a waiting list return */ AZ(sp->vary_b); AZ(sp->vary_l); AZ(sp->vary_e); (void)WS_Reserve(sp->ws, 0); } else { AN(sp->ws->r); } sp->vary_b = (void*)sp->ws->f; sp->vary_e = (void*)sp->ws->r; sp->vary_b[2] = '\0'; oc = HSH_Lookup(sp, &oh); if (oc == NULL) { /* * We lost the session to a busy object, disembark the * worker thread. The hash code to restart the session, * still in STP_LOOKUP, later when the busy object isn't. * NB: Do not access sp any more ! */ return (1); } CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); /* If we inserted a new object it's a miss */ if (oc->flags & OC_F_BUSY) { wrk->stats.cache_miss++; if (sp->vary_l != NULL) { assert(oc->busyobj->vary == sp->vary_b); VRY_Validate(oc->busyobj->vary); WS_ReleaseP(sp->ws, (void*)sp->vary_l); } else { AZ(oc->busyobj->vary); WS_Release(sp->ws, 0); } sp->vary_b = NULL; sp->vary_l = NULL; sp->vary_e = NULL; wrk->objcore = oc; CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC); sp->step = STP_MISS; return (0); } o = oc_getobj(wrk, oc); CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); wrk->obj = o; WS_Release(sp->ws, 0); sp->vary_b = NULL; sp->vary_l = NULL; sp->vary_e = NULL; if (oc->flags & OC_F_PASS) { wrk->stats.cache_hitpass++; WSP(sp, SLT_HitPass, "%u", wrk->obj->xid); (void)HSH_Deref(wrk, NULL, &wrk->obj); wrk->objcore = NULL; sp->step = STP_PASS; return (0); } wrk->stats.cache_hit++; WSP(sp, SLT_Hit, "%u", wrk->obj->xid); sp->step = STP_HIT; return (0); }
static int cnt_prepresp(struct worker *wrk, struct req *req) { struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); bo = req->busyobj; CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); req->res_mode = 0; if (bo == NULL) { if (!req->disable_esi && req->obj->esidata != NULL) { /* In ESI mode, we can't know the aggregate length */ req->res_mode &= ~RES_LEN; req->res_mode |= RES_ESI; } else { req->res_mode |= RES_LEN; } } else { AZ(bo->do_esi); } if (req->esi_level > 0) { /* Included ESI object, always CHUNKED or EOF */ req->res_mode &= ~RES_LEN; req->res_mode |= RES_ESI_CHILD; } if (cache_param->http_gzip_support && req->obj->gziped && !RFC2616_Req_Gzip(req->http)) { /* * We don't know what it uncompresses to * XXX: we could cache that */ req->res_mode &= ~RES_LEN; req->res_mode |= RES_GUNZIP; } if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) { /* We havn't chosen yet, do so */ if (!req->wantbody) { /* Nothing */ } else if (req->http->protover >= 11) { req->res_mode |= RES_CHUNKED; } else { req->res_mode |= RES_EOF; req->doclose = SC_TX_EOF; } } req->t_resp = W_TIM_real(wrk); if (req->obj->objcore->objhead != NULL) { if ((req->t_resp - req->obj->last_lru) > cache_param->lru_timeout && EXP_Touch(req->obj->objcore)) req->obj->last_lru = req->t_resp; if (!cache_param->obj_readonly) req->obj->last_use = req->t_resp; /* XXX: locking ? */ } HTTP_Setup(req->resp, req->ws, req->vsl, HTTP_Resp); RES_BuildHttp(req); VCL_deliver_method(req); switch (req->handling) { case VCL_RET_DELIVER: break; case VCL_RET_RESTART: if (req->restarts >= cache_param->max_restarts) break; if (bo != NULL) { AN(bo->do_stream); (void)HSH_Deref(&wrk->stats, NULL, &req->obj); VBO_DerefBusyObj(wrk, &req->busyobj); } else { (void)HSH_Deref(&wrk->stats, NULL, &req->obj); } AZ(req->obj); http_Teardown(req->resp); req->req_step = R_STP_RESTART; return (0); default: WRONG("Illegal action in vcl_deliver{}"); } req->req_step = R_STP_DELIVER; return (0); }
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; 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); /* ESI takes precedence and handles gzip/gunzip itself */ if (bo->do_esi) bo->vfp = &vfp_esi; else if (bo->do_gunzip) bo->vfp = &vfp_gunzip; else if (bo->do_gzip) bo->vfp = &vfp_gzip; else if (bo->is_gzip) bo->vfp = &vfp_testgzip; if (bo->fetch_objcore->objhead == NULL) AN(bo->uncacheable); /* No reason to try streaming a non-existing body */ if (bo->htc.body_status == BS_NONE) bo->do_stream = 0; l = http_EstimateWS(bo->beresp, bo->uncacheable ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp); /* Create Vary instructions */ if (bo->fetch_objcore->objhead != NULL) { 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); } if (bo->uncacheable) bo->fetch_objcore->flags |= OC_F_PASS; if (bo->exp.ttl < cache_param->shortlived || bo->uncacheable == 1) bo->storage_hint = TRANSIENT_STORAGE; /* * Space for producing a Content-Length: header including padding * A billion gigabytes is enough for anybody. */ l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *); AZ(bo->stats); bo->stats = &wrk->stats; 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); } bo->stats = NULL; if (obj == NULL) { AZ(HSH_Deref(&wrk->stats, bo->fetch_objcore, NULL)); bo->fetch_objcore = NULL; VDI_CloseFd(&bo->vbc); VBO_setstate(bo, BOS_FAILED); return (F_STP_ABANDON); } 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); VRY_Validate(obj->vary); VSB_delete(vary); } obj->vxid = bo->vsl->wid; obj->response = bo->err_code; WS_Assert(obj->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.entered); assert(WRW_IsReleased(wrk)); /* * Ready to fetch the body */ assert(bo->refcount >= 1); if (obj->objcore->objhead != NULL) { EXP_Insert(obj); AN(obj->objcore->ban); AZ(obj->ws_o->overflow); HSH_Unbusy(&wrk->stats, obj->objcore); } if (bo->vfp == NULL) bo->vfp = &VFP_nop; assert(bo->state == BOS_INVALID); VBO_setstate(bo, BOS_FETCHING); V1F_fetch_body(wrk, bo); assert(bo->refcount >= 1); if (obj->objcore->objhead != NULL) HSH_Ref(obj->objcore); if (bo->state == BOS_FAILED) { /* handle early failures */ (void)HSH_Deref(&wrk->stats, NULL, &obj); return (F_STP_ABANDON); } VBO_setstate(bo, BOS_FINISHED); VBO_DerefBusyObj(wrk, &bo); // XXX ? return (F_STP_DONE); }
static int cnt_lookup(struct sess *sp) { struct objcore *oc; struct object *o; struct objhead *oh; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); if (sp->obj == NULL) { HSH_BeforeVclHash(sp, sp->vcl->nhashcount); VCL_hash_method(sp); assert(sp->handling == VCL_RET_HASH); HSH_AfterVclHash(sp); } oc = HSH_Lookup(sp, &oh); if (oc == NULL) { /* * We lost the session to a busy object, disembark the * worker thread. The hash code to restart the session, * still in STP_LOOKUP, later when the busy object isn't. */ return (1); } CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); /* If we inserted a new object it's a miss */ if (oc->flags & OC_F_BUSY) { VSL_stats->cache_miss++; AZ(oc->obj); sp->objhead = oh; sp->objcore = oc; sp->step = STP_MISS; return (0); } o = oc->obj; CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); sp->obj = o; if (oc->flags & OC_F_PASS) { VSL_stats->cache_hitpass++; WSP(sp, SLT_HitPass, "%u", sp->obj->xid); HSH_Deref(sp->wrk, &sp->obj); sp->objcore = NULL; sp->objhead = NULL; sp->step = STP_PASS; return (0); } VSL_stats->cache_hit++; WSP(sp, SLT_Hit, "%u", sp->obj->xid); sp->step = STP_HIT; return (0); }