ssize_t V1F_Setup_Fetch(struct busyobj *bo) { struct http_conn *htc; ssize_t cl; CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); htc = &bo->htc; CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); CHECK_OBJ_NOTNULL(bo->vbc, VBC_MAGIC); switch(htc->body_status) { case BS_EOF: VFP_Push(bo, v1f_pull_eof, 0); return(-1); case BS_LENGTH: cl = vbf_fetch_number(bo->h_content_length, 10); VFP_Push(bo, v1f_pull_straight, cl); return (cl); case BS_CHUNKED: VFP_Push(bo, v1f_pull_chunked, -1); return (-1); default: break; } return (-1); }
static int vbf_fetch_chunked(struct busyobj *bo, struct http_conn *htc) { int i; char buf[20]; /* XXX: 20 is arbitrary */ unsigned u; ssize_t cl; assert(htc->body_status == BS_CHUNKED); do { /* Skip leading whitespace */ do { if (HTTP1_Read(htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (vct_islws(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(bo, "chunked header non-hex")); /* Collect hex digits, skipping leading zeros */ for (u = 1; u < sizeof buf; u++) { do { if (HTTP1_Read(htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (u == 1 && buf[0] == '0' && buf[u] == '0'); if (!vct_ishex(buf[u])) break; } if (u >= sizeof buf) return (VFP_Error(bo,"chunked header too long")); /* Skip trailing white space */ while(vct_islws(buf[u]) && buf[u] != '\n') if (HTTP1_Read(htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(bo,"chunked header no NL")); buf[u] = '\0'; cl = vbf_fetch_number(buf, 16); if (cl < 0) return (VFP_Error(bo,"chunked header number syntax")); if (cl > 0 && bo->vfp->bytes(bo, htc, cl) <= 0) return (VFP_Error(bo, "chunked read err")); i = HTTP1_Read(htc, buf, 1); if (i <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] == '\r' && HTTP1_Read( htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] != '\n') return (VFP_Error(bo,"chunked tail no NL")); } while (cl > 0); return (0); }
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; }
v1f_pull_chunked(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv) { int i; char buf[20]; /* XXX: 20 is arbitrary */ unsigned u; ssize_t cl, 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 == -1) { /* Skip leading whitespace */ do { if (HTTP1_Read(&bo->htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (vct_islws(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(bo, "chunked header non-hex")); /* Collect hex digits, skipping leading zeros */ for (u = 1; u < sizeof buf; u++) { do { if (HTTP1_Read(&bo->htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); } while (u == 1 && buf[0] == '0' && buf[u] == '0'); if (!vct_ishex(buf[u])) break; } if (u >= sizeof buf) return (VFP_Error(bo,"chunked header too long")); /* Skip trailing white space */ while(vct_islws(buf[u]) && buf[u] != '\n') if (HTTP1_Read(&bo->htc, buf + u, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(bo,"chunked header no NL")); buf[u] = '\0'; cl = vbf_fetch_number(buf, 16); if (cl < 0) return (VFP_Error(bo,"chunked header number syntax")); *priv = cl; } if (*priv > 0) { 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) *priv = -1; return (VFP_OK); } AZ(*priv); i = HTTP1_Read(&bo->htc, buf, 1); if (i <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] == '\r' && HTTP1_Read(&bo->htc, buf, 1) <= 0) return (VFP_Error(bo, "chunked read err")); if (buf[0] != '\n') return (VFP_Error(bo,"chunked tail no NL")); return (VFP_END); }