vmod_exec(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING sql) { struct vmod_sqlite3_result r; struct vmod_sqlite3 *v; char *e, *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); AN(priv); if (!priv->priv) { e = (char *)sqlite3_errstr(SQLITE_ERROR); p = WS_Copy(ctx->ws, e, -1); return (p); } CAST_OBJ_NOTNULL(v, priv->priv, VMOD_SQLITE3_MAGIC); r.u = WS_Reserve(ctx->ws, 0); r.p = ctx->ws->f; r.d = v->d; r.l = 0; r.r = 0; *(r.p) = '\0'; if (sqlite3_exec(v->db, sql, vmod_exec_cb, &r, &e) != SQLITE_OK) { WS_Release(ctx->ws, 0); p = WS_Copy(ctx->ws, e, -1); sqlite3_free(e); return (p); } WS_Release(ctx->ws, r.l + 1); return (r.p); }
static void ses_vsl_socket(struct sess *sp, const char *lsockname) { struct sockaddr_storage ss; socklen_t sl; char laddr[ADDR_BUFSIZE]; char lport[PORT_BUFSIZE]; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); AN(lsockname); AN(sp->addrs); sl = sizeof ss; AZ(getsockname(sp->fd, (void*)&ss, &sl)); AN(VSA_Build(sess_local_addr(sp), &ss, sl)); assert(VSA_Sane(sess_local_addr(sp))); VTCP_name(sess_remote_addr(sp), laddr, sizeof laddr, lport, sizeof lport); sp->client_addr_str = WS_Copy(sp->ws, laddr, -1); sp->client_port_str = WS_Copy(sp->ws, lport, -1); VTCP_name(sess_local_addr(sp), laddr, sizeof laddr, lport, sizeof lport); VSL(SLT_Begin, sp->vxid, "sess 0 HTTP/1"); VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d", sp->client_addr_str, sp->client_port_str, lsockname, laddr, lport, sp->t_open, sp->fd); }
static const char * absolutize_link(struct ws* ws, const char* path, const char* link) { unsigned available, written; char *front, *path_copy; if (link[0] == '/') { return link; } path_copy = (char*) WS_Copy(ws, path, -1); if (path_copy == NULL) { return NULL; } available = WS_Reserve(ws, 0); front = ws->f; written = snprintf(front, available, "%s/%s", dirname(path_copy), link); written++; // null-terminating char if (written > available) { WS_Release(ws, 0); return NULL; } WS_Release(ws, written); return front; }
void http_CopyHome(const struct http *hp) { unsigned u, l; char *p; for (u = 0; u < hp->nhd; u++) { if (hp->hd[u].b == NULL) continue; if (hp->hd[u].b >= hp->ws->s && hp->hd[u].e <= hp->ws->e) { http_VSLH(hp, u); continue; } l = Tlen(hp->hd[u]); p = WS_Copy(hp->ws, hp->hd[u].b, l + 1L); if (p != NULL) { http_VSLH(hp, u); hp->hd[u].b = p; hp->hd[u].e = p + l; } else { /* XXX This leaves a slot empty */ VSC_C_main->losthdr++; VSLbt(hp->vsl, SLT_LostHeader, hp->hd[u]); hp->hd[u].b = NULL; hp->hd[u].e = NULL; } } }
VCL_STRING vmod_argtest(VRT_CTX, VCL_STRING one, VCL_REAL two, VCL_STRING three) { char buf[100]; bprintf(buf, "%s %g %s", one, two, three); return WS_Copy(ctx->ws, buf, -1); }
VCL_STRING vmod_argtest(VRT_CTX, VCL_STRING one, VCL_REAL two, VCL_STRING three, VCL_STRING comma, VCL_INT four) { char buf[100]; bprintf(buf, "%s %g %s %s %ld", one, two, three, comma, four); return (WS_Copy(ctx->ws, buf, -1)); }
VCL_BLOB vmod_str2blob(const struct vrt_ctx *ctx, VCL_STRING s) { struct vmod_priv *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); p = (void*)WS_Alloc(ctx->ws, sizeof *p); AN(p); memset(p, 0, sizeof *p); p->len = strlen(s); p->priv = WS_Copy(ctx->ws, s, -1); return (p); }
/** * Organization */ VCL_STRING vmod_organization(const struct vrt_ctx *ctx, struct vmod_priv *pp, const char *ip) { const char *org = NULL; if (pp->priv) { struct GeoIP_databases* db = (struct GeoIP_databases*)pp->priv; if (db && db->org) org = GeoIP_org_by_addr(db->org, ip); } if (!org) org = unknownOrg; return WS_Copy(ctx->ws, org, -1); }
/** * Country code. */ VCL_STRING vmod_country(const struct vrt_ctx *ctx, struct vmod_priv *pp, const char *ip) { const char *country = NULL; if (pp->priv) { struct GeoIP_databases* db = (struct GeoIP_databases*)pp->priv; if (db && db->country) country = GeoIP_country_code_by_addr(db->country, ip); } if (!country) country = unknownCountry; return WS_Copy(ctx->ws, country, -1); }
static void http_PutField(const struct http *to, int field, const char *string) { char *p; CHECK_OBJ_NOTNULL(to, HTTP_MAGIC); p = WS_Copy(to->ws, string, -1); if (p == NULL) { http_fail(to); VSLb(to->vsl, SLT_LostHeader, "%s", string); return; } to->hd[field].b = p; to->hd[field].e = strchr(p, '\0'); to->hdf[field] = 0; http_VSLH(to, field); }
VCL_STRING vmod_rresolve(const struct vrt_ctx *ctx, VCL_STRING hostname) { char node[NI_MAXHOST]; struct addrinfo *res; char *p = NULL; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (getaddrinfo(hostname, NULL, NULL, &res)) return (NULL); if (!getnameinfo(res->ai_addr, res->ai_addrlen, node, sizeof(node), NULL, 0, 0)) p = WS_Copy(ctx->ws, node, -1); freeaddrinfo(res); return (p); }
static const char * normalize_link(struct vmod_fsdirector_file_system *fs, const char* link) { char *real_path, *location; unsigned root_len; real_path = realpath(link, NULL); if (real_path == NULL) { return NULL; } root_len = strlen(fs->root); if (memcmp(real_path, fs->root, root_len) || real_path[root_len] != '/') { return NULL; } location = (char*) WS_Copy(fs->htc.ws, &real_path[root_len], -1); free(real_path); return location; }
/** * Region */ VCL_STRING vmod_region(const struct vrt_ctx *ctx, struct vmod_priv *pp, const char *ip) { const char *region = NULL; if (pp->priv) { struct GeoIP_databases* db = (struct GeoIP_databases*)pp->priv; if (db && db->region) { GeoIPRegion *gir; if ((gir = GeoIP_region_by_addr(db->region, ip)) != NULL) { region = GeoIP_region_name_by_code(gir->country_code, gir->region); GeoIPRegion_delete(gir); } } } if (!region) region = unknownRegion; return WS_Copy(ctx->ws, region, -1); }
void http_CopyHome(const struct http *hp) { unsigned u, l; char *p; for (u = 0; u < hp->nhd; u++) { if (hp->hd[u].b == NULL) continue; if (hp->hd[u].b >= hp->ws->s && hp->hd[u].e <= hp->ws->e) continue; l = Tlen(hp->hd[u]); p = WS_Copy(hp->ws, hp->hd[u].b, l + 1L); if (p == NULL) { http_fail(hp); VSLb(hp->vsl, SLT_LostHeader, "%s", hp->hd[u].b); return; } hp->hd[u].b = p; hp->hd[u].e = p + l; } }
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 enum fetch_step vbf_stp_condfetch(struct worker *wrk, struct busyobj *bo) { unsigned l; uint16_t nhttp; struct object *obj; struct objiter *oi; void *sp; ssize_t sl, al, tl, vl; struct storage *st; enum objiter_status ois; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); l = 0; if (bo->ims_obj->vary != NULL) { vl = VRY_Validate(bo->ims_obj->vary); l += vl; } else vl = 0; l += http_EstimateWS(bo->ims_obj->http, 0, &nhttp); bo->stats = &wrk->stats; obj = STV_NewObject(bo, bo->storage_hint, l, nhttp); if (obj == NULL) { (void)VFP_Error(bo, "Could not get storage"); VDI_CloseFd(&bo->vbc); return (F_STP_DONE); } AZ(bo->fetch_obj); bo->fetch_obj = obj; obj->gziped = bo->ims_obj->gziped; obj->gzip_start = bo->ims_obj->gzip_start; obj->gzip_last = bo->ims_obj->gzip_last; obj->gzip_stop = bo->ims_obj->gzip_stop; /* XXX: ESI */ if (bo->ims_obj->vary != NULL) { obj->vary = (void *)WS_Copy(obj->http->ws, bo->ims_obj->vary, vl); assert(vl == VRY_Validate(obj->vary)); } obj->vxid = bo->vsl->wid; obj->http->logtag = HTTP_Obj; /* XXX: we should have our own HTTP_A_CONDFETCH */ http_FilterResp(bo->ims_obj->http, obj->http, HTTPH_A_INS); http_CopyHome(obj->http); AZ(WS_Overflowed(bo->ws_o)); VBO_setstate(bo, BOS_FETCHING); HSH_Unbusy(&wrk->stats, obj->objcore); if (!(obj->objcore->flags & OC_F_PRIVATE)) { EXP_Insert(obj->objcore); AN(obj->objcore->ban); } st = NULL; al = 0; oi = ObjIterBegin(wrk, bo->ims_obj); do { ois = ObjIter(oi, &sp, &sl); while (sl > 0) { if (st == NULL) { st = VFP_GetStorage(bo, bo->ims_obj->len - al); XXXAN(st); } tl = sl; if (tl > st->space - st->len) tl = st->space - st->len; memcpy(st->ptr + st->len, sp, tl); al += tl; sp = (char *)sp + tl; sl -= tl; VBO_extend(bo, al); if (st->len == st->space) st = NULL; } } while (ois == OIS_DATA || ois == OIS_STREAM); ObjIterEnd(&oi); bo->stats = NULL; assert(al == bo->ims_obj->len); assert(obj->len == al); if (bo->state != BOS_FAILED) VBO_setstate(bo, BOS_FINISHED); HSH_Complete(obj->objcore); return (F_STP_DONE); }
static enum fetch_step vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) { struct http *hp, *hp2; char *b; uint16_t nhttp; unsigned l; struct vsb *vary = NULL; int varyl = 0; struct object *obj; ssize_t est = -1; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); assert(wrk->handling == VCL_RET_DELIVER); /* * The VCL variables beresp.do_g[un]zip tells us how we want the * object processed before it is stored. * * The backend Content-Encoding header tells us what we are going * to receive, which we classify in the following three classes: * * "Content-Encoding: gzip" --> object is gzip'ed. * no Content-Encoding --> object is not gzip'ed. * anything else --> do nothing wrt gzip * * XXX: BS_NONE/cl==0 should avoid gzip/gunzip */ /* We do nothing unless the param is set */ if (!cache_param->http_gzip_support) bo->do_gzip = bo->do_gunzip = 0; bo->is_gzip = http_HdrIs(bo->beresp, H_Content_Encoding, "gzip"); bo->is_gunzip = !http_GetHdr(bo->beresp, H_Content_Encoding, NULL); /* It can't be both */ assert(bo->is_gzip == 0 || bo->is_gunzip == 0); /* We won't gunzip unless it is gzip'ed */ if (bo->do_gunzip && !bo->is_gzip) bo->do_gunzip = 0; /* If we do gunzip, remove the C-E header */ if (bo->do_gunzip) http_Unset(bo->beresp, H_Content_Encoding); /* We wont gzip unless it is ungziped */ if (bo->do_gzip && !bo->is_gunzip) bo->do_gzip = 0; /* If we do gzip, add the C-E header */ if (bo->do_gzip) http_SetHeader(bo->beresp, "Content-Encoding: gzip"); /* But we can't do both at the same time */ assert(bo->do_gzip == 0 || bo->do_gunzip == 0); if (bo->vbc != NULL) est = V1F_Setup_Fetch(bo); if (bo->do_gunzip || (bo->is_gzip && bo->do_esi)) { RFC2616_Weaken_Etag(bo->beresp); VFP_Push(bo, vfp_gunzip_pull, 0); } if (bo->do_esi && bo->do_gzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi && bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_esi_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->do_esi) { VFP_Push(bo, vfp_esi_pull, 0); } else if (bo->do_gzip) { VFP_Push(bo, vfp_gzip_pull, 0); RFC2616_Weaken_Etag(bo->beresp); } else if (bo->is_gzip && !bo->do_gunzip) { VFP_Push(bo, vfp_testgunzip_pull, 0); } if (bo->fetch_objcore->flags & OC_F_PRIVATE) AN(bo->uncacheable); /* No reason to try streaming a non-existing body */ if (bo->htc.body_status == BS_NONE) bo->do_stream = 0; l = 0; /* Create Vary instructions */ if (!(bo->fetch_objcore->flags & OC_F_PRIVATE)) { varyl = VRY_Create(bo, &vary); if (varyl > 0) { AN(vary); assert(varyl == VSB_len(vary)); l += varyl; } else if (varyl < 0) { /* * Vary parse error * Complain about it, and make this a pass. */ VSLb(bo->vsl, SLT_Error, "Illegal 'Vary' header from backend, " "making this a pass."); bo->uncacheable = 1; AZ(vary); } else /* No vary */ AZ(vary); } l += http_EstimateWS(bo->beresp, bo->uncacheable ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp); if (bo->uncacheable) bo->fetch_objcore->flags |= OC_F_PASS; if (bo->uncacheable || bo->exp.ttl+bo->exp.grace+bo->exp.keep < cache_param->shortlived) bo->storage_hint = TRANSIENT_STORAGE; AZ(bo->stats); bo->stats = &wrk->stats; AN(bo->fetch_objcore); obj = STV_NewObject(bo, bo->storage_hint, l, nhttp); if (obj == NULL) { /* * Try to salvage the transaction by allocating a * shortlived object on Transient storage. */ if (bo->exp.ttl > cache_param->shortlived) bo->exp.ttl = cache_param->shortlived; bo->exp.grace = 0.0; bo->exp.keep = 0.0; obj = STV_NewObject(bo, TRANSIENT_STORAGE, l, nhttp); } if (obj == NULL) { bo->stats = NULL; (void)VFP_Error(bo, "Could not get storage"); VDI_CloseFd(&bo->vbc); return (F_STP_DONE); } CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); bo->storage_hint = NULL; AZ(bo->fetch_obj); bo->fetch_obj = obj; if (bo->do_gzip || (bo->is_gzip && !bo->do_gunzip)) obj->gziped = 1; if (vary != NULL) { obj->vary = (void *)WS_Copy(obj->http->ws, VSB_data(vary), varyl); AN(obj->vary); (void)VRY_Validate(obj->vary); VSB_delete(vary); } obj->vxid = bo->vsl->wid; obj->response = bo->err_code; WS_Assert(bo->ws_o); /* Filter into object */ hp = bo->beresp; hp2 = obj->http; hp2->logtag = HTTP_Obj; http_FilterResp(hp, hp2, bo->uncacheable ? HTTPH_R_PASS : HTTPH_A_INS); http_CopyHome(hp2); if (http_GetHdr(hp, H_Last_Modified, &b)) obj->last_modified = VTIM_parse(b); else obj->last_modified = floor(bo->exp.t_origin); assert(WRW_IsReleased(wrk)); /* * Ready to fetch the body */ assert(bo->refcount >= 1); AZ(WS_Overflowed(bo->ws_o)); if (bo->do_stream) HSH_Unbusy(&wrk->stats, obj->objcore); assert(bo->state == BOS_REQ_DONE); VBO_setstate(bo, BOS_FETCHING); switch (bo->htc.body_status) { case BS_NONE: break; case BS_ERROR: /* XXX: Why not earlier ? */ bo->should_close |= VFP_Error(bo, "error incompatible Transfer-Encoding"); break; default: if (bo->vbc == NULL) (void)VFP_Error(bo, "Backend connection gone"); else VFP_Fetch_Body(bo, est); } bo->stats = NULL; bo->t_body = VTIM_mono(); if (bo->vbc != NULL) { if (bo->should_close) VDI_CloseFd(&bo->vbc); else VDI_RecycleFd(&bo->vbc); AZ(bo->vbc); } http_Teardown(bo->bereq); http_Teardown(bo->beresp); VSLb(bo->vsl, SLT_Fetch_Body, "%u(%s)", bo->htc.body_status, body_status_2str(bo->htc.body_status)); if (bo->state == BOS_FAILED) { wrk->stats.fetch_failed++; } else { assert(bo->state == BOS_FETCHING); VSLb(bo->vsl, SLT_Length, "%zd", obj->len); { /* Sanity check fetch methods accounting */ ssize_t uu; struct storage *st; uu = 0; VTAILQ_FOREACH(st, &obj->store, list) uu += st->len; if (bo->do_stream) /* Streaming might have started freeing stuff */ assert(uu <= obj->len); else assert(uu == obj->len); } } if (!bo->do_stream && bo->state != BOS_FAILED) HSH_Unbusy(&wrk->stats, obj->objcore); if (bo->state != BOS_FAILED && !(obj->objcore->flags & OC_F_PRIVATE)) { EXP_Insert(obj->objcore); AN(obj->objcore->ban); } HSH_Complete(obj->objcore); assert(bo->refcount >= 1); if (bo->state != BOS_FAILED) VBO_setstate(bo, BOS_FINISHED); VSLb(bo->vsl, SLT_Debug, "YYY REF %d %d", bo->refcount, bo->fetch_obj->objcore->refcnt); return (F_STP_DONE); }
static 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 vbf_beresp2obj(struct busyobj *bo) { unsigned l; char *b; struct vsb *vary = NULL; int varyl = 0; uint16_t nhttp; struct object *obj; struct http *hp, *hp2; 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; obj = vbf_allocobj(bo, l, nhttp); if (obj == NULL) return (-1); CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC); AZ(bo->fetch_obj); bo->fetch_obj = obj; 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; WS_Assert(bo->ws_o); /* Filter into object */ hp = bo->beresp; hp2 = obj->http; hp2->logtag = SLT_ObjMethod; 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->fetch_objcore->exp.t_origin); /* Disassociate the obj from the bo's workspace */ hp2->ws = NULL; return (0); }
vmod_querysort(const struct vrt_ctx *ctx, VCL_STRING url) { const char *cq, *cu; char *p, *r; const char **pp; const char **pe; int np; int i; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (url == NULL) return (NULL); /* Split :query from :url */ cu = strchr(url, '?'); if (cu == NULL) return (url); /* Spot single-param queries */ cq = strchr(cu, '&'); if (cq == NULL) return (url); r = WS_Copy(ctx->ws, url, -1); if (r == NULL) return (url); (void)WS_Reserve(ctx->ws, 0); /* We trust cache_ws.c to align sensibly */ pp = (const char**)(void*)(ctx->ws->f); pe = (const char**)(void*)(ctx->ws->e); if (pp + 4 > pe) { WS_Release(ctx->ws, 0); WS_MarkOverflow(ctx->ws); return (url); } /* Collect params as pointer pairs */ np = 0; pp[np++] = 1 + cu; for (cq = 1 + cu; *cq != '\0'; cq++) { if (*cq == '&') { if (pp + 3 > pe) { WS_Release(ctx->ws, 0); WS_MarkOverflow(ctx->ws); return (url); } pp[np++] = cq; /* Skip trivially empty params */ while(cq[1] == '&') cq++; pp[np++] = cq + 1; } } pp[np++] = cq; assert(!(np & 1)); qsort(pp, np / 2, sizeof(*pp) * 2, compa); /* Emit sorted params */ p = 1 + r + (cu - url); cq = ""; for (i = 0; i < np; i += 2) { /* Ignore any edge-case zero length params */ if (pp[i + 1] == pp[i]) continue; assert(pp[i + 1] > pp[i]); if (*cq) *p++ = *cq; memcpy(p, pp[i], pp[i + 1] - pp[i]); p += pp[i + 1] - pp[i]; cq = "&"; } *p = '\0'; WS_Release(ctx->ws, 0); return (r); }