vfp_nop_end(struct worker *wrk) { struct storage *st; st = VTAILQ_LAST(&wrk->busyobj->fetch_obj->store, storagehead); if (st == NULL) return (0); if (st->len == 0) { VTAILQ_REMOVE(&wrk->busyobj->fetch_obj->store, st, list); STV_free(st); return (0); } if (st->len < st->space) STV_trim(st, st->len); return (0); }
vfp_nop_end(struct sess *sp) { struct storage *st; st = VTAILQ_LAST(&sp->obj->store, storagehead); if (st == NULL) return (0); if (st->len == 0) { VTAILQ_REMOVE(&sp->obj->store, st, list); STV_free(st); return (0); } if (st->len < st->space) STV_trim(st, st->len); return (0); }
enum req_fsm_nxt CNT_Request(struct worker *wrk, struct req *req) { enum req_fsm_nxt nxt; struct storage *st; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); /* * Possible entrance states */ assert( req->req_step == R_STP_LOOKUP || req->req_step == R_STP_RECV); AN(req->vsl->wid & VSL_CLIENTMARKER); req->wrk = wrk; for (nxt = REQ_FSM_MORE; nxt == REQ_FSM_MORE; ) { /* * This is a good place to be paranoid about the various * pointers still pointing to the things we expect. */ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); /* * We don't want the thread workspace to be used for * anything of long duration, so mandate that it be * empty on state-transitions. */ WS_Assert(wrk->aws); assert(wrk->aws->s == wrk->aws->f); switch (req->req_step) { #define REQ_STEP(l,u,arg) \ case R_STP_##u: \ if (DO_DEBUG(DBG_REQ_STATE)) \ cnt_diag(req, #u); \ nxt = cnt_##l arg; \ break; #include "tbl/steps.h" #undef REQ_STEP default: WRONG("State engine misfire"); } WS_Assert(wrk->aws); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); } if (nxt == REQ_FSM_DONE) { AN(req->vsl->wid); if (req->res_mode & (RES_ESI|RES_ESI_CHILD)) VSLb(req->vsl, SLT_ESI_BodyBytes, "%ju", (uintmax_t)req->resp_bodybytes); while (!VTAILQ_EMPTY(&req->body->list)) { st = VTAILQ_FIRST(&req->body->list); VTAILQ_REMOVE(&req->body->list, st, list); STV_free(st); } req->wrk = NULL; } assert(WRW_IsReleased(wrk)); return (nxt); }
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; }
enum req_fsm_nxt CNT_Request(struct worker *wrk, struct req *req) { enum req_fsm_nxt nxt; struct storage *st; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); /* * Possible entrance states */ assert( req->req_step == R_STP_LOOKUP || req->req_step == R_STP_RECV); AN(req->vsl->wid & VSL_CLIENTMARKER); req->wrk = wrk; for (nxt = REQ_FSM_MORE; nxt == REQ_FSM_MORE; ) { /* * This is a good place to be paranoid about the various * pointers still pointing to the things we expect. */ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(req, REQ_MAGIC); /* * We don't want the thread workspace to be used for * anything of long duration, so mandate that it be * empty on state-transitions. */ WS_Assert(wrk->aws); assert(wrk->aws->s == wrk->aws->f); switch (req->req_step) { #define REQ_STEP(l,u,arg) \ case R_STP_##u: \ if (DO_DEBUG(DBG_REQ_STATE)) \ cnt_diag(req, #u); \ nxt = cnt_##l arg; \ break; #include "tbl/steps.h" #undef REQ_STEP default: WRONG("State engine misfire"); } WS_Assert(wrk->aws); CHECK_OBJ_ORNULL(wrk->nobjhead, OBJHEAD_MAGIC); } if (nxt == REQ_FSM_DONE) { /* XXX: Workaround for pipe */ if (req->sp->fd >= 0) { VSLb(req->vsl, SLT_Length, "%ju", (uintmax_t)req->resp_bodybytes); } VSLb(req->vsl, SLT_ReqEnd, "%.9f %.9f %.9f %.9f %.9f", req->t_req, req->sp->t_idle, req->sp->t_idle - req->t_resp, req->t_resp - req->t_req, req->sp->t_idle - req->t_resp); while (!VTAILQ_EMPTY(&req->body)) { st = VTAILQ_FIRST(&req->body); VTAILQ_REMOVE(&req->body, st, list); STV_free(st); } /* done == 2 was charged by cache_hash.c */ SES_Charge(wrk, req); /* * Nuke the VXID, cache_http1_fsm.c::http1_dissect() will * allocate a new one when necessary. */ VSLb(req->vsl, SLT_End, "%s", ""); req->vsl->wid = 0; } req->wrk = NULL; assert(WRW_IsReleased(wrk)); return (nxt); }
void VFP_Fetch_Body(struct busyobj *bo, ssize_t est) { ssize_t l; enum vfp_status vfps = VFP_ERROR; struct storage *st = NULL; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AN(bo->vfp_nxt); if (est < 0) est = 0; if (vfp_suck_init(bo) != VFP_OK) { (void)VFP_Error(bo, "Fetch Pipeline failed to initialize"); bo->should_close = 1; return; } do { if (bo->abandon) { /* * A pass object and delivery was terminted * We don't fail the fetch, in order for hit-for-pass * objects to be created. */ AN(bo->fetch_objcore->flags & OC_F_PASS); VSLb(bo->vsl, SLT_FetchError, "Pass delivery abandoned"); vfps = VFP_END; bo->should_close = 1; break; } AZ(bo->failed); if (st == NULL) { st = VFP_GetStorage(bo, est); est = 0; } if (st == NULL) { bo->should_close = 1; (void)VFP_Error(bo, "Out of storage"); break; } CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC); assert(st == VTAILQ_LAST(&bo->fetch_obj->store, storagehead)); l = st->space - st->len; AZ(bo->failed); vfps = VFP_Suck(bo, st->ptr + st->len, &l); if (l > 0 && vfps != VFP_ERROR) { AZ(VTAILQ_EMPTY(&bo->fetch_obj->store)); VBO_extend(bo, l); } if (st->len == st->space) st = NULL; } while (vfps == VFP_OK); if (vfps == VFP_ERROR) { AN(bo->failed); (void)VFP_Error(bo, "Fetch Pipeline failed to process"); bo->should_close = 1; } vfp_suck_fini(bo); /* * 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) { /* None of this this is 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); } } }
int FetchBody(struct worker *wrk, struct object *obj) { int cls; struct storage *st; int mklen; ssize_t cl; struct http_conn *htc; struct busyobj *bo; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); bo = wrk->busyobj; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); AZ(bo->fetch_obj); CHECK_OBJ_NOTNULL(bo->vbc, VBC_MAGIC); CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC); htc = &bo->htc; if (bo->vfp == NULL) bo->vfp = &vfp_nop; AssertObjCorePassOrBusy(obj->objcore); AZ(bo->vgz_rx); AZ(VTAILQ_FIRST(&obj->store)); bo->fetch_obj = obj; bo->fetch_failed = 0; /* XXX: pick up estimate from objdr ? */ cl = 0; switch (bo->body_status) { case BS_NONE: cls = 0; mklen = 0; break; case BS_ZERO: cls = 0; mklen = 1; break; case BS_LENGTH: cl = fetch_number(bo->h_content_length, 10); bo->vfp->begin(wrk, cl > 0 ? cl : 0); cls = fetch_straight(wrk, htc, cl); mklen = 1; if (bo->vfp->end(wrk)) cls = -1; break; case BS_CHUNKED: bo->vfp->begin(wrk, cl); cls = fetch_chunked(wrk, htc); mklen = 1; if (bo->vfp->end(wrk)) cls = -1; break; case BS_EOF: bo->vfp->begin(wrk, cl); cls = fetch_eof(wrk, htc); mklen = 1; if (bo->vfp->end(wrk)) cls = -1; break; case BS_ERROR: cls = 1; mklen = 0; break; default: cls = 0; mklen = 0; INCOMPL(); } AZ(bo->vgz_rx); /* * It is OK for ->end to just leave the last storage segment * sitting on wrk->storage, we will always call vfp_nop_end() * to get it trimmed or thrown out if empty. */ AZ(vfp_nop_end(wrk)); bo->fetch_obj = NULL; WSLB(wrk, SLT_Fetch_Body, "%u(%s) cls %d mklen %d", bo->body_status, body_status(bo->body_status), cls, mklen); if (bo->body_status == BS_ERROR) { VDI_CloseFd(wrk, &bo->vbc); return (__LINE__); } if (cls < 0) { wrk->stats.fetch_failed++; /* XXX: Wouldn't this store automatically be released ? */ while (!VTAILQ_EMPTY(&obj->store)) { st = VTAILQ_FIRST(&obj->store); VTAILQ_REMOVE(&obj->store, st, list); STV_free(st); } VDI_CloseFd(wrk, &bo->vbc); obj->len = 0; return (__LINE__); } AZ(bo->fetch_failed); if (cls == 0 && bo->should_close) cls = 1; WSLB(wrk, 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); } if (mklen > 0) { http_Unset(obj->http, H_Content_Length); http_PrintfHeader(wrk, bo->vbc->vsl_id, obj->http, "Content-Length: %zd", obj->len); } if (cls) VDI_CloseFd(wrk, &bo->vbc); else VDI_RecycleFd(wrk, &bo->vbc); return (0); }
int FetchBody(struct sess *sp) { int cls; struct storage *st; struct worker *w; int mklen; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC); w = sp->wrk; CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(sp->obj->http, HTTP_MAGIC); if (w->vfp == NULL) w->vfp = &vfp_nop; AN(sp->director); AssertObjCorePassOrBusy(sp->obj->objcore); AZ(w->vgz_rx); AZ(VTAILQ_FIRST(&sp->obj->store)); switch (w->body_status) { case BS_NONE: cls = 0; mklen = 0; break; case BS_ZERO: cls = 0; mklen = 1; break; case BS_LENGTH: cls = fetch_straight(sp, w->htc, w->h_content_length); mklen = 1; XXXAZ(w->vfp->end(sp)); break; case BS_CHUNKED: cls = fetch_chunked(sp, w->htc); mklen = 1; XXXAZ(w->vfp->end(sp)); break; case BS_EOF: cls = fetch_eof(sp, w->htc); mklen = 1; XXXAZ(w->vfp->end(sp)); break; case BS_ERROR: cls = 1; mklen = 0; break; default: cls = 0; mklen = 0; INCOMPL(); } AZ(w->vgz_rx); /* * It is OK for ->end to just leave the last storage segment * sitting on w->storage, we will always call vfp_nop_end() * to get it trimmed or thrown out if empty. */ AZ(vfp_nop_end(sp)); WSL(w, SLT_Fetch_Body, sp->vbc->vsl_id, "%u(%s) cls %d mklen %u", w->body_status, body_status(w->body_status), cls, mklen); if (w->body_status == BS_ERROR) { VDI_CloseFd(sp); return (__LINE__); } if (cls < 0) { w->stats.fetch_failed++; /* XXX: Wouldn't this store automatically be released ? */ while (!VTAILQ_EMPTY(&sp->obj->store)) { st = VTAILQ_FIRST(&sp->obj->store); VTAILQ_REMOVE(&sp->obj->store, st, list); STV_free(st); } VDI_CloseFd(sp); sp->obj->len = 0; return (__LINE__); } if (cls == 0 && w->do_close) cls = 1; WSL(w, SLT_Length, sp->vbc->vsl_id, "%u", sp->obj->len); { /* Sanity check fetch methods accounting */ ssize_t uu; uu = 0; VTAILQ_FOREACH(st, &sp->obj->store, list) uu += st->len; if (sp->objcore == NULL || (sp->objcore->flags & OC_F_PASS)) /* Streaming might have started freeing stuff */ assert (uu <= sp->obj->len); else assert(uu == sp->obj->len); } if (mklen > 0) { http_Unset(sp->obj->http, H_Content_Length); http_PrintfHeader(w, sp->vsl_id, sp->obj->http, "Content-Length: %jd", (intmax_t)sp->obj->len); } if (cls) VDI_CloseFd(sp); else VDI_RecycleFd(sp); return (0); }