static void 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(bo->doclose == SC_NULL); 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; while (stp != F_STP_DONE) { CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); assert(bo->refcount >= 1); switch(stp) { #define FETCH_STEP(l, U, arg) \ case F_STP_##U: \ stp = vbf_stp_##l arg; \ break; #include "tbl/steps.h" #undef FETCH_STEP default: WRONG("Illegal fetch_step"); } } assert(WRW_IsReleased(wrk)); assert(bo->director_state == DIR_S_NULL); http_Teardown(bo->bereq); http_Teardown(bo->beresp); if (bo->state == BOS_FINISHED) { AZ(bo->fetch_objcore->flags & OC_F_FAILED); HSH_Complete(bo->fetch_objcore); VSLb(bo->vsl, SLT_Length, "%ju", (uintmax_t)ObjGetLen(bo->wrk, bo->fetch_objcore)); } AZ(bo->fetch_objcore->busyobj); if (bo->ims_oc != NULL) (void)HSH_DerefObjCore(wrk, &bo->ims_oc); wrk->vsl = NULL; VBO_DerefBusyObj(wrk, &bo); THR_SetBusyobj(NULL); }
static int cnt_pass(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); AZ(req->objcore); AZ(req->obj); AZ(req->busyobj); req->busyobj = VBO_GetBusyObj(wrk, req); bo = req->busyobj; bo->refcount = 2; HTTP_Setup(bo->bereq, bo->ws, bo->vsl, HTTP_Bereq); http_FilterReq(req, HTTPH_R_PASS); VCL_pass_method(req); if (req->handling == VCL_RET_ERROR) { http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &req->busyobj); req->req_step = R_STP_ERROR; return (0); } assert(req->handling == VCL_RET_PASS); req->acct_req.pass++; req->req_step = R_STP_FETCH; req->objcore = HSH_NewObjCore(wrk); req->objcore->busyobj = bo; return (0); }
static int cnt_pipe(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); AZ(req->busyobj); req->acct_req.pipe++; req->busyobj = VBO_GetBusyObj(wrk, req); bo = req->busyobj; HTTP_Setup(bo->bereq, bo->ws, bo->vsl, HTTP_Bereq); http_FilterReq(req, 0); VCL_pipe_method(req); if (req->handling == VCL_RET_ERROR) INCOMPL(); assert(req->handling == VCL_RET_PIPE); PipeRequest(req); assert(WRW_IsReleased(wrk)); http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &req->busyobj); return (1); }
void http_Setup(struct http *hp, struct ws *ws, struct vsl_log *vsl) { http_Teardown(hp); hp->ws = ws; hp->vsl = vsl; }
static enum req_fsm_nxt cnt_pipe(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); req->acct_req.pipe++; bo = VBO_GetBusyObj(wrk, req); HTTP_Setup(bo->bereq, bo->ws, bo->vsl, HTTP_Bereq); http_FilterReq(bo->bereq, req->http, 0); // XXX: 0 ? http_PrintfHeader(bo->bereq, "X-Varnish: %u", req->vsl->wid & VSL_IDENTMASK); VCL_pipe_method(req->vcl, wrk, req, bo, req->http->ws); if (wrk->handling == VCL_RET_ERROR) INCOMPL(); assert(wrk->handling == VCL_RET_PIPE); PipeRequest(req, bo); assert(WRW_IsReleased(wrk)); http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &bo); return (REQ_FSM_DONE); }
static enum req_fsm_nxt cnt_pipe(struct worker *wrk, struct req *req) { struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AN(req->vcl); wrk->stats->s_pipe++; bo = VBO_GetBusyObj(wrk, req); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); VSLb(bo->vsl, SLT_Begin, "bereq %u pipe", VXID(req->vsl->wid)); VSLb(req->vsl, SLT_Link, "bereq %u pipe", VXID(bo->vsl->wid)); THR_SetBusyobj(bo); HTTP_Setup(bo->bereq, bo->ws, bo->vsl, SLT_BereqMethod); http_FilterReq(bo->bereq, req->http, 0); // XXX: 0 ? http_PrintfHeader(bo->bereq, "X-Varnish: %u", VXID(req->vsl->wid)); http_SetHeader(bo->bereq, "Connection: close"); VCL_pipe_method(req->vcl, wrk, req, bo, NULL); if (wrk->handling == VCL_RET_SYNTH) INCOMPL(); assert(wrk->handling == VCL_RET_PIPE); SES_Close(req->sp, VDI_Http1Pipe(req, bo)); http_Teardown(bo->bereq); VBO_ReleaseBusyObj(wrk, &bo); THR_SetBusyobj(NULL); return (REQ_FSM_DONE); }
void HTTP_Setup(struct http *hp, struct ws *ws, struct vsl_log *vsl, enum httpwhence whence) { http_Teardown(hp); hp->logtag = whence; hp->ws = ws; hp->vsl = vsl; }
void HTTP_Setup(struct http *hp, struct ws *ws, struct vsl_log *vsl, enum VSL_tag_e whence) { http_Teardown(hp); hp->nhd = HTTP_HDR_FIRST; hp->logtag = whence; hp->ws = ws; hp->vsl = vsl; }
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 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); }
/* Possible error returns: * * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header * block. This is a connection level error. * * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header. */ int h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d, const uint8_t *in, size_t in_l) { struct http *hp; size_t in_u = 0; CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); hp = h2->new_req->http; CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC); AN(hp->ws->r); CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue processing. Other errors should have been returned and handled by the caller. */ assert(d->error == 0 || d->error == H2E_ENHANCE_YOUR_CALM); while (1) { AN(d->out); assert(d->out_u <= d->out_l); d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u, d->out, d->out_l, &d->out_u); if (d->vhd_ret < 0) { VSLb(hp->vsl, SLT_BogoHeader, "HPACK compression error (%s)", VHD_Error(d->vhd_ret)); d->error = H2E_COMPRESSION_ERROR; break; } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) { assert(in_u == in_l); break; } if (d->error == H2E_ENHANCE_YOUR_CALM) { d->out_u = 0; assert(d->out_u < d->out_l); continue; } switch (d->vhd_ret) { case VHD_NAME_SEC: /* XXX: header flag for never-indexed header */ case VHD_NAME: assert(d->namelen == 0); if (d->out_l - d->out_u < 2) { d->error = H2E_ENHANCE_YOUR_CALM; break; } d->out[d->out_u++] = ':'; d->out[d->out_u++] = ' '; d->namelen = d->out_u; break; case VHD_VALUE_SEC: /* XXX: header flag for never-indexed header */ case VHD_VALUE: assert(d->namelen > 0); if (d->out_l - d->out_u < 1) { d->error = H2E_ENHANCE_YOUR_CALM; break; } d->error = h2h_checkhdr(hp, d->out, d->namelen, d->out_u); if (d->error) break; d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u); if (d->error) break; d->out[d->out_u++] = '\0'; /* Zero guard */ d->out += d->out_u; d->out_l -= d->out_u; d->out_u = 0; d->namelen = 0; break; case VHD_BUF: d->error = H2E_ENHANCE_YOUR_CALM; break; default: WRONG("Unhandled return value"); break; } if (d->error == H2E_ENHANCE_YOUR_CALM) { http_Teardown(hp); d->out = d->reset; d->out_l = hp->ws->r - d->out; d->out_u = 0; assert(d->out_u < d->out_l); } else if (d->error) break; } if (d->error == H2E_ENHANCE_YOUR_CALM) return (0); /* Stream error, delay reporting until h2h_decode_fini so that we can process the complete header block */ return (d->error); }
static enum req_fsm_nxt cnt_error(struct worker *wrk, struct req *req) { struct http *h; struct busyobj *bo; char date[40]; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AZ(req->objcore); AZ(req->obj); req->acct_req.error++; bo = VBO_GetBusyObj(wrk, req); AZ(bo->stats); bo->stats = &wrk->stats; bo->fetch_objcore = HSH_Private(wrk); bo->fetch_obj = STV_NewObject(bo, TRANSIENT_STORAGE, cache_param->http_resp_size, (uint16_t)cache_param->http_max_hdr); req->obj = bo->fetch_obj; bo->stats = NULL; if (req->obj == NULL) { req->doclose = SC_OVERLOAD; req->director = NULL; AZ(HSH_DerefObjCore(&wrk->stats, &bo->fetch_objcore)); bo->fetch_objcore = NULL; http_Teardown(bo->beresp); http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &bo); return (REQ_FSM_DONE); } CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); AZ(req->objcore); req->obj->vxid = bo->vsl->wid; req->obj->exp.entered = req->t_req; h = req->obj->http; if (req->err_code < 100 || req->err_code > 999) req->err_code = 501; http_PutProtocol(h, "HTTP/1.1"); http_PutStatus(h, req->err_code); VTIM_format(W_TIM_real(wrk), date); http_PrintfHeader(h, "Date: %s", date); http_SetHeader(h, "Server: Varnish"); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); HSH_Ref(req->obj->objcore); if (req->err_reason != NULL) http_PutResponse(h, req->err_reason); else http_PutResponse(h, http_StatusMessage(req->err_code)); VCL_error_method(req->vcl, wrk, req, NULL, req->http->ws); /* 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_RESTART) { VBO_DerefBusyObj(wrk, &bo); HSH_Drop(wrk, &req->obj); req->req_step = R_STP_RESTART; return (REQ_FSM_MORE); } CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); /* We always close when we take this path */ req->doclose = SC_TX_ERROR; req->wantbody = 1; assert(wrk->handling == VCL_RET_DELIVER); req->err_code = 0; req->err_reason = NULL; http_Teardown(bo->bereq); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); VBO_DerefBusyObj(wrk, &bo); req->req_step = R_STP_DELIVER; CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); return (REQ_FSM_MORE); }
static void 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(bo->doclose == SC_NULL); assert(isnan(bo->t_first)); assert(isnan(bo->t_prev)); VSLb_ts_busyobj(bo, "Start", W_TIM_real(wrk)); bo->stats = &wrk->stats; while (stp != F_STP_DONE) { CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); assert(bo->refcount >= 1); switch(stp) { #define FETCH_STEP(l, U, arg) \ case F_STP_##U: \ stp = vbf_stp_##l arg; \ break; #include "tbl/steps.h" #undef FETCH_STEP default: WRONG("Illegal fetch_step"); } } assert(WRW_IsReleased(wrk)); bo->stats = NULL; if (bo->vbc != NULL) { if (bo->doclose != SC_NULL) VDI_CloseFd(&bo->vbc, &bo->acct); else VDI_RecycleFd(&bo->vbc, &bo->acct); AZ(bo->vbc); } http_Teardown(bo->bereq); http_Teardown(bo->beresp); if (bo->state == BOS_FINISHED) { AZ(bo->fetch_objcore->flags & OC_F_FAILED); HSH_Complete(bo->fetch_objcore); VSLb(bo->vsl, SLT_Length, "%zd", bo->fetch_obj->len); { /* Sanity check fetch methods accounting */ ssize_t uu; struct storage *st; uu = 0; VTAILQ_FOREACH(st, &bo->fetch_obj->body->list, list) uu += st->len; if (bo->do_stream) /* Streaming might have started freeing stuff */ assert(uu <= bo->fetch_obj->len); else assert(uu == bo->fetch_obj->len); } } AZ(bo->fetch_objcore->busyobj); if (bo->ims_obj != NULL) (void)HSH_DerefObj(&wrk->stats, &bo->ims_obj); VBO_DerefBusyObj(wrk, &bo); THR_SetBusyobj(NULL); }
static enum req_fsm_nxt cnt_deliver(struct worker *wrk, struct req *req) { char time_str[30]; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore->objhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); assert(WRW_IsReleased(wrk)); assert(req->obj->objcore->refcnt > 0); req->t_resp = W_TIM_real(wrk); if (!(req->obj->objcore->flags & OC_F_PRIVATE)) { if ((req->t_resp - req->obj->objcore->last_lru) > cache_param->lru_timeout && EXP_Touch(req->obj->objcore)) req->obj->objcore->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); http_ClrHeader(req->resp); http_FilterResp(req->obj->http, req->resp, 0); http_Unset(req->resp, H_Date); VTIM_format(req->t_resp, time_str); http_PrintfHeader(req->resp, "Date: %s", time_str); if (req->wrk->stats.cache_hit) http_PrintfHeader(req->resp, "X-Varnish: %u %u", req->vsl->wid & VSL_IDENTMASK, req->obj->vxid & VSL_IDENTMASK); else http_PrintfHeader(req->resp, "X-Varnish: %u", req->vsl->wid & VSL_IDENTMASK); http_PrintfHeader(req->resp, "Age: %.0f", req->obj->exp.age + req->t_resp - req->obj->exp.entered); http_SetHeader(req->resp, "Via: 1.1 varnish"); VCL_deliver_method(req->vcl, wrk, req, NULL, req->http->ws); /* 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_RESTART) { (void)HSH_DerefObj(&wrk->stats, &req->obj); AZ(req->obj); http_Teardown(req->resp); req->req_step = R_STP_RESTART; return (REQ_FSM_MORE); } assert(wrk->handling == VCL_RET_DELIVER); if (!(req->obj->objcore->flags & OC_F_PASS) && req->obj->response == 200 && req->http->conds && RFC2616_Do_Cond(req)) { req->wantbody = 0; http_SetResp(req->resp, "HTTP/1.1", 304, "Not Modified"); // http_Unset(req->resp, H_Content_Length); } V1D_Deliver(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)); VSLb(req->vsl, SLT_Debug, "XXX REF %d", req->obj->objcore->refcnt); (void)HSH_DerefObj(&wrk->stats, &req->obj); http_Teardown(req->resp); return (REQ_FSM_DONE); }
void V1F_fetch_body(struct worker *wrk, struct busyobj *bo) { int cls; struct storage *st; ssize_t cl; struct http_conn *htc; struct object *obj; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); htc = &bo->htc; CHECK_OBJ_ORNULL(bo->vbc, VBC_MAGIC); obj = bo->fetch_obj; CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC); assert(bo->state == BOS_FETCHING); /* * XXX: The busyobj needs a dstat, but it is not obvious which one * XXX: it should be (own/borrowed). For now borrow the wrk's. */ AZ(bo->stats); bo->stats = &wrk->stats; AN(bo->vfp); AZ(bo->vgz_rx); assert(VTAILQ_EMPTY(&obj->store)); /* XXX: pick up estimate from objdr ? */ cl = 0; cls = bo->should_close; switch (htc->body_status) { case BS_NONE: break; case BS_ZERO: break; case BS_LENGTH: cl = vbf_fetch_number(bo->h_content_length, 10); bo->vfp->begin(bo, cl); if (bo->state == BOS_FETCHING && cl > 0) cls |= vbf_fetch_straight(bo, htc, cl); if (bo->vfp->end(bo)) assert(bo->state == BOS_FAILED); break; case BS_CHUNKED: bo->vfp->begin(bo, cl > 0 ? cl : 0); if (bo->state == BOS_FETCHING) cls |= vbf_fetch_chunked(bo, htc); if (bo->vfp->end(bo)) assert(bo->state == BOS_FAILED); break; case BS_EOF: bo->vfp->begin(bo, cl > 0 ? cl : 0); if (bo->state == BOS_FETCHING) vbf_fetch_eof(bo, htc); cls = 1; if (bo->vfp->end(bo)) assert(bo->state == BOS_FAILED); break; case BS_ERROR: cls |= VFP_Error(bo, "error incompatible Transfer-Encoding"); break; default: INCOMPL(); } bo->t_body = VTIM_mono(); AZ(bo->vgz_rx); /* * Trim or delete the last segment, if any */ st = VTAILQ_LAST(&bo->fetch_obj->store, storagehead); /* XXX: Temporary: Only trim if we are not streaming */ if (st != NULL && !bo->do_stream) { /* XXX: is any of this safe under streaming ? */ if (st->len == 0) { VTAILQ_REMOVE(&bo->fetch_obj->store, st, list); STV_free(st); } else if (st->len < st->space) { STV_trim(st, st->len, 1); } } bo->vfp = NULL; VSLb(bo->vsl, SLT_Fetch_Body, "%u(%s) cls %d", htc->body_status, body_status_2str(htc->body_status), cls); http_Teardown(bo->bereq); http_Teardown(bo->beresp); if (bo->vbc != NULL) { if (cls) VDI_CloseFd(&bo->vbc); else VDI_RecycleFd(&bo->vbc); } AZ(bo->vbc); 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; 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); } } bo->stats = NULL; }
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->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore->objhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); assert(WRW_IsReleased(wrk)); assert(req->obj->objcore->refcnt > 0); if (req->obj->objcore->exp_flags & OC_EF_EXP) EXP_Touch(req->obj->objcore, req->t_prev); HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); http_FilterResp(req->obj->http, req->resp, 0); http_ForceField(req->resp, HTTP_HDR_PROTO, "HTTP/1.1"); if (req->wrk->stats.cache_hit) http_PrintfHeader(req->resp, "X-Varnish: %u %u", VXID(req->vsl->wid), VXID(req->obj->vxid)); 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->obj->objcore->exp.t_origin)); http_SetHeader(req->resp, "Via: 1.1 varnish-v4"); if (cache_param->http_gzip_support && req->obj->gziped && !RFC2616_Req_Gzip(req->http)) RFC2616_Weaken_Etag(req->resp); VCL_deliver_method(req->vcl, wrk, req, NULL, req->http->ws); 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_DerefObj(&wrk->stats, &req->obj); AZ(req->obj); 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->obj->objcore->flags & OC_F_PASS) && req->esi_level == 0 && http_GetStatus(req->obj->http) == 200 && req->http->conds && RFC2616_Do_Cond(req)) { http_PutResponse(req->resp, "HTTP/1.1", 304, NULL); req->wantbody = 0; } /* Grab a ref to the bo if there is one, and hand it down */ bo = HSH_RefBusy(req->obj->objcore); V1D_Deliver(req, bo); if (bo != NULL) VBO_DerefBusyObj(req->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->obj->objcore->flags & OC_F_PASS) { /* * No point in saving the body if it is hit-for-pass, * but we can't yank it until the fetching thread has * finished/abandoned also. */ while (req->obj->objcore->busyobj != NULL) (void)usleep(100000); STV_Freestore(req->obj); } assert(WRW_IsReleased(wrk)); VSLb(req->vsl, SLT_Debug, "XXX REF %d", req->obj->objcore->refcnt); (void)HSH_DerefObj(&wrk->stats, &req->obj); http_Teardown(req->resp); return (REQ_FSM_DONE); }
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 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 req_fsm_nxt cnt_synth(struct worker *wrk, struct req *req) { struct http *h; double now; struct vsb *synth_body; ssize_t sz, szl; uint8_t *ptr; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); wrk->stats->s_synth++; now = W_TIM_real(wrk); VSLb_ts_req(req, "Process", now); if (req->err_code < 100 || req->err_code > 999) req->err_code = 501; HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); h = req->resp; http_TimeHeader(h, "Date: ", now); http_SetHeader(h, "Server: Varnish"); http_PrintfHeader(req->resp, "X-Varnish: %u", VXID(req->vsl->wid)); http_PutResponse(h, "HTTP/1.1", req->err_code, req->err_reason); synth_body = VSB_new_auto(); AN(synth_body); VCL_synth_method(req->vcl, wrk, req, NULL, synth_body); AZ(VSB_finish(synth_body)); http_Unset(h, H_Content_Length); http_PrintfHeader(req->resp, "Content-Length: %zd", VSB_len(synth_body)); /* Discard any lingering request body before delivery */ (void)VRB_Ignore(req); if (wrk->handling == VCL_RET_RESTART) { HTTP_Setup(h, req->ws, req->vsl, SLT_RespMethod); VSB_destroy(&synth_body); req->req_step = R_STP_RESTART; return (REQ_FSM_MORE); } assert(wrk->handling == VCL_RET_DELIVER); req->objcore = HSH_Private(wrk); CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); szl = -1; if (STV_NewObject(wrk, req->objcore, TRANSIENT_STORAGE, 1024)) { szl = VSB_len(synth_body); assert(szl >= 0); sz = szl; if (sz > 0 && ObjGetSpace(wrk, req->objcore, &sz, &ptr) && sz >= szl) { memcpy(ptr, VSB_data(synth_body), szl); ObjExtend(wrk, req->objcore, szl); } else if (sz > 0) { szl = -1; } } if (szl >= 0) AZ(ObjSetU64(wrk, req->objcore, OA_LEN, szl)); HSH_DerefBoc(wrk, req->objcore); VSB_destroy(&synth_body); if (szl < 0) { VSLb(req->vsl, SLT_Error, "Could not get storage"); req->doclose = SC_OVERLOAD; VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); (void)HSH_DerefObjCore(wrk, &req->objcore); http_Teardown(req->resp); return (REQ_FSM_DONE); } req->req_step = R_STP_TRANSMIT; return (REQ_FSM_MORE); }
static int cnt_error(struct worker *wrk, struct req *req) { struct http *h; struct busyobj *bo; char date[40]; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AZ(req->objcore); AZ(req->obj); AZ(req->busyobj); bo = VBO_GetBusyObj(wrk, req); req->busyobj = bo; AZ(bo->stats); bo->stats = &wrk->stats; req->objcore = HSH_NewObjCore(wrk); req->obj = STV_NewObject(bo, &req->objcore, TRANSIENT_STORAGE, cache_param->http_resp_size, (uint16_t)cache_param->http_max_hdr); bo->stats = NULL; if (req->obj == NULL) { req->doclose = SC_OVERLOAD; req->director = NULL; http_Teardown(bo->beresp); http_Teardown(bo->bereq); return(1); } CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); req->obj->vxid = bo->vsl->wid; req->obj->exp.entered = req->t_req; h = req->obj->http; if (req->err_code < 100 || req->err_code > 999) req->err_code = 501; http_PutProtocol(h, "HTTP/1.1"); http_PutStatus(h, req->err_code); VTIM_format(W_TIM_real(wrk), date); http_PrintfHeader(h, "Date: %s", date); http_SetHeader(h, "Server: Varnish"); if (req->err_reason != NULL) http_PutResponse(h, req->err_reason); else http_PutResponse(h, http_StatusMessage(req->err_code)); VCL_error_method(req); if (req->handling == VCL_RET_RESTART && req->restarts < cache_param->max_restarts) { HSH_Drop(wrk, &req->obj); VBO_DerefBusyObj(wrk, &req->busyobj); req->req_step = R_STP_RESTART; return (0); } else if (req->handling == VCL_RET_RESTART) req->handling = VCL_RET_DELIVER; /* We always close when we take this path */ req->doclose = SC_TX_ERROR; req->wantbody = 1; assert(req->handling == VCL_RET_DELIVER); req->err_code = 0; req->err_reason = NULL; http_Teardown(bo->bereq); VBO_DerefBusyObj(wrk, &req->busyobj); req->req_step = R_STP_PREPRESP; return (0); }
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 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); }
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 req_fsm_nxt cnt_deliver(struct worker *wrk, struct req *req) { char time_str[30]; double now; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); CHECK_OBJ_NOTNULL(req->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore, OBJCORE_MAGIC); CHECK_OBJ_NOTNULL(req->obj->objcore->objhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(req->vcl, VCL_CONF_MAGIC); assert(WRW_IsReleased(wrk)); assert(req->obj->objcore->refcnt > 0); now = W_TIM_real(wrk); VSLb_ts_req(req, "Process", now); if (req->obj->objcore->exp_flags & OC_EF_EXP) EXP_Touch(req->obj->objcore, now); HTTP_Setup(req->resp, req->ws, req->vsl, SLT_RespMethod); http_ClrHeader(req->resp); http_FilterResp(req->obj->http, req->resp, 0); http_Unset(req->resp, H_Date); VTIM_format(now, time_str); http_PrintfHeader(req->resp, "Date: %s", time_str); if (req->wrk->stats.cache_hit) http_PrintfHeader(req->resp, "X-Varnish: %u %u", req->vsl->wid & VSL_IDENTMASK, req->obj->vxid & VSL_IDENTMASK); else http_PrintfHeader(req->resp, "X-Varnish: %u", req->vsl->wid & VSL_IDENTMASK); http_PrintfHeader(req->resp, "Age: %.0f", now - req->obj->exp.t_origin); http_SetHeader(req->resp, "Via: 1.1 varnish (v4)"); if (cache_param->http_gzip_support && req->obj->gziped && !RFC2616_Req_Gzip(req->http)) RFC2616_Weaken_Etag(req->resp); VCL_deliver_method(req->vcl, wrk, req, NULL, req->http->ws); /* 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_RESTART) { (void)HSH_DerefObj(&wrk->stats, &req->obj); AZ(req->obj); http_Teardown(req->resp); req->req_step = R_STP_RESTART; return (REQ_FSM_MORE); } assert(wrk->handling == VCL_RET_DELIVER); if (!(req->obj->objcore->flags & OC_F_PASS) && req->esi_level == 0 && http_GetStatus(req->obj->http) == 200 && req->http->conds && RFC2616_Do_Cond(req)) http_SetResp(req->resp, "HTTP/1.1", 304, "Not Modified"); V1D_Deliver(req); VSLb_ts_req(req, "Resp", W_TIM_real(wrk)); if (http_HdrIs(req->resp, H_Connection, "close")) req->doclose = SC_RESP_CLOSE; if (req->obj->objcore->flags & OC_F_PASS) { /* * No point in saving the body if it is hit-for-pass, * but we can't yank it until the fetching thread has * finished/abandoned also. */ while (req->obj->objcore->busyobj != NULL) (void)usleep(100000); STV_Freestore(req->obj); } assert(WRW_IsReleased(wrk)); VSLb(req->vsl, SLT_Debug, "XXX REF %d", req->obj->objcore->refcnt); (void)HSH_DerefObj(&wrk->stats, &req->obj); http_Teardown(req->resp); return (REQ_FSM_DONE); }