static int
cnt_fetch(struct sess *sp)
{
    int i, transient;
    struct http *hp, *hp2;
    char *b;
    unsigned handling;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);

    AN(sp->director);
    AZ(sp->vbe);

    /* sp->wrk->http[0] is (still) bereq */
    sp->wrk->beresp = &sp->wrk->http[1];
    http_Setup(sp->wrk->beresp, sp->wrk->ws);

    i = FetchHdr(sp);

    /*
     * Save a copy before it might get mangled in VCL.  When it comes to
     * dealing with the body, we want to see the unadultered headers.
     */
    sp->wrk->beresp1 = &sp->wrk->http[2];
    *sp->wrk->beresp1 = *sp->wrk->beresp;

    if (i) {
        if (sp->objhead) {
            CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC);
            CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
            HSH_DerefObjCore(sp);
        }
        AZ(sp->obj);
        sp->wrk->bereq = NULL;
        sp->wrk->beresp = NULL;
        sp->wrk->beresp1 = NULL;
        sp->err_code = 503;
        sp->step = STP_ERROR;
        return (0);
    }

    sp->err_code = http_GetStatus(sp->wrk->beresp);

    /*
     * Initial cacheability determination per [RFC2616, 13.4]
     * We do not support ranges yet, so 206 is out.
     */
    switch (sp->err_code) {
    case 200: /* OK */
    case 203: /* Non-Authoritative Information */
    case 300: /* Multiple Choices */
    case 301: /* Moved Permanently */
    case 302: /* Moved Temporarily */
    case 410: /* Gone */
    case 404: /* Not Found */
        sp->wrk->cacheable = 1;
        break;
    default:
        sp->wrk->cacheable = 0;
        break;
    }

    sp->wrk->entered = TIM_real();
    sp->wrk->age = 0;
    sp->wrk->ttl = RFC2616_Ttl(sp);

    if (sp->wrk->ttl == 0.)
        sp->wrk->cacheable = 0;

    sp->wrk->do_esi = 0;
    sp->wrk->grace = NAN;

    VCL_fetch_method(sp);

    /*
     * When we fetch the body, we may hit the LRU cleanup and that
     * will overwrite sp->handling, so we have to save our plans
     * here.
     */
    handling = sp->handling;

    if (sp->objhead == NULL)
        transient = 1;
    else if (sp->handling == VCL_RET_DELIVER)
        transient = 0;
    else
        transient = 1;

    /*
     * XXX: If we have a Length: header, we should allocate the body
     * XXX: also.
     */
    sp->obj = HSH_NewObject(sp, transient);

    if (sp->objhead != NULL) {
        CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC);
        CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
        sp->objcore->obj = sp->obj;
        sp->obj->objcore = sp->objcore;
        sp->obj->objhead = sp->objhead;
        sp->objhead = NULL;	/* refcnt follows pointer. */
        sp->objcore = NULL;	/* refcnt follows pointer. */
    }

    BAN_NewObj(sp->obj);

    sp->obj->xid = sp->xid;
    sp->obj->response = sp->err_code;
    sp->obj->cacheable = sp->wrk->cacheable;
    sp->obj->ttl = sp->wrk->ttl;
    sp->obj->grace = sp->wrk->grace;
    if (sp->obj->ttl == 0. && sp->obj->grace == 0.)
        sp->obj->cacheable = 0;
    sp->obj->age = sp->wrk->age;
    sp->obj->entered = sp->wrk->entered;
    WS_Assert(sp->obj->ws_o);

    /* Filter into object */
    hp = sp->wrk->beresp;
    hp2 = sp->obj->http;

    hp2->logtag = HTTP_Obj;
    http_CopyResp(hp2, hp);
    http_FilterFields(sp->wrk, sp->fd, hp2, hp, HTTPH_A_INS);
    http_CopyHome(sp->wrk, sp->fd, hp2);

    if (http_GetHdr(hp, H_Last_Modified, &b))
        sp->obj->last_modified = TIM_parse(b);

    i = FetchBody(sp);
    AZ(sp->wrk->wfd);
    AZ(sp->vbe);
    AN(sp->director);

    if (i) {
        HSH_Drop(sp);
        AZ(sp->obj);
        sp->wrk->bereq = NULL;
        sp->wrk->beresp = NULL;
        sp->wrk->beresp1 = NULL;
        sp->err_code = 503;
        sp->step = STP_ERROR;
        return (0);
    }

    if (!transient)
        HSH_Object(sp);

    if (sp->wrk->do_esi)
        ESI_Parse(sp);

    switch (handling) {
    case VCL_RET_RESTART:
        HSH_Drop(sp);
        sp->director = NULL;
        sp->restarts++;
        sp->wrk->bereq = NULL;
        sp->wrk->beresp = NULL;
        sp->wrk->beresp1 = NULL;
        sp->step = STP_RECV;
        return (0);
    case VCL_RET_PASS:
        if (sp->obj->objcore != NULL)
            sp->obj->objcore->flags |= OC_F_PASS;
        if (sp->obj->ttl - sp->t_req < params->default_ttl)
            sp->obj->ttl = sp->t_req + params->default_ttl;
        break;
    case VCL_RET_DELIVER:
        break;
    case VCL_RET_ERROR:
        HSH_Drop(sp);
        sp->wrk->bereq = NULL;
        sp->wrk->beresp = NULL;
        sp->wrk->beresp1 = NULL;
        sp->step = STP_ERROR;
        return (0);
    default:
        WRONG("Illegal action in vcl_fetch{}");
    }

    sp->obj->cacheable = 1;
    if (sp->obj->objhead != NULL) {
        VRY_Create(sp);
        EXP_Insert(sp->obj);
        AN(sp->obj->ban);
        HSH_Unbusy(sp);
    }
    sp->acct_req.fetch++;
    sp->wrk->bereq = NULL;
    sp->wrk->beresp = NULL;
    sp->wrk->beresp1 = NULL;
    sp->step = STP_DELIVER;
    return (0);
}
Example #2
0
static int
cnt_fetchbody(struct sess *sp)
{
    int i;
    struct http *hp, *hp2;
    char *b;
    uint16_t nhttp;
    unsigned l;
    struct vsb *vary = NULL;
    int varyl = 0, pass;
    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);

    assert(sp->handling == VCL_RET_HIT_FOR_PASS ||
           sp->handling == VCL_RET_DELIVER);

    if (wrk->objcore == NULL) {
        /* This is a pass from vcl_recv */
        pass = 1;
        /* VCL may have fiddled this, but that doesn't help */
        wrk->busyobj->exp.ttl = -1.;
    } else if (sp->handling == VCL_RET_HIT_FOR_PASS) {
        /* pass from vcl_fetch{} -> hit-for-pass */
        /* XXX: the bereq was not filtered 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)
        wrk->busyobj->do_gzip = wrk->busyobj->do_gunzip = 0;

    wrk->busyobj->is_gzip =
        http_HdrIs(wrk->busyobj->beresp, H_Content_Encoding, "gzip");

    wrk->busyobj->is_gunzip =
        !http_GetHdr(wrk->busyobj->beresp, H_Content_Encoding, NULL);

    /* It can't be both */
    assert(wrk->busyobj->is_gzip == 0 || wrk->busyobj->is_gunzip == 0);

    /* We won't gunzip unless it is gzip'ed */
    if (wrk->busyobj->do_gunzip && !wrk->busyobj->is_gzip)
        wrk->busyobj->do_gunzip = 0;

    /* If we do gunzip, remove the C-E header */
    if (wrk->busyobj->do_gunzip)
        http_Unset(wrk->busyobj->beresp, H_Content_Encoding);

    /* We wont gzip unless it is ungziped */
    if (wrk->busyobj->do_gzip && !wrk->busyobj->is_gunzip)
        wrk->busyobj->do_gzip = 0;

    /* If we do gzip, add the C-E header */
    if (wrk->busyobj->do_gzip)
        http_SetHeader(wrk, sp->vsl_id, wrk->busyobj->beresp,
                       "Content-Encoding: gzip");

    /* But we can't do both at the same time */
    assert(wrk->busyobj->do_gzip == 0 || wrk->busyobj->do_gunzip == 0);

    /* ESI takes precedence and handles gzip/gunzip itself */
    if (wrk->busyobj->do_esi)
        wrk->busyobj->vfp = &vfp_esi;
    else if (wrk->busyobj->do_gunzip)
        wrk->busyobj->vfp = &vfp_gunzip;
    else if (wrk->busyobj->do_gzip)
        wrk->busyobj->vfp = &vfp_gzip;
    else if (wrk->busyobj->is_gzip)
        wrk->busyobj->vfp = &vfp_testgzip;

    if (wrk->busyobj->do_esi || sp->esi_level > 0)
        wrk->busyobj->do_stream = 0;
    if (!sp->wantbody)
        wrk->busyobj->do_stream = 0;

    l = http_EstimateWS(wrk->busyobj->beresp,
                        pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);

    /* Create Vary instructions */
    if (wrk->objcore != NULL) {
        CHECK_OBJ_NOTNULL(wrk->objcore, OBJCORE_MAGIC);
        vary = VRY_Create(sp, wrk->busyobj->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 (wrk->busyobj->exp.ttl < cache_param->shortlived ||
            wrk->objcore == NULL)
        wrk->storage_hint = TRANSIENT_STORAGE;

    wrk->obj = STV_NewObject(wrk, wrk->storage_hint, l, nhttp);
    if (wrk->obj == NULL) {
        /*
         * Try to salvage the transaction by allocating a
         * shortlived object on Transient storage.
         */
        wrk->obj = STV_NewObject(wrk, TRANSIENT_STORAGE, l, nhttp);
        if (wrk->busyobj->exp.ttl > cache_param->shortlived)
            wrk->busyobj->exp.ttl = cache_param->shortlived;
        wrk->busyobj->exp.grace = 0.0;
        wrk->busyobj->exp.keep = 0.0;
    }
    if (wrk->obj == NULL) {
        sp->err_code = 503;
        sp->step = STP_ERROR;
        VDI_CloseFd(wrk, &wrk->busyobj->vbc);
        VBO_DerefBusyObj(wrk, &wrk->busyobj);
        return (0);
    }
    CHECK_OBJ_NOTNULL(wrk->obj, OBJECT_MAGIC);

    wrk->storage_hint = NULL;

    if (wrk->busyobj->do_gzip ||
            (wrk->busyobj->is_gzip && !wrk->busyobj->do_gunzip))
        wrk->obj->gziped = 1;

    if (vary != NULL) {
        wrk->obj->vary = (void *)WS_Alloc(wrk->obj->http->ws, varyl);
        AN(wrk->obj->vary);
        memcpy(wrk->obj->vary, VSB_data(vary), varyl);
        VRY_Validate(wrk->obj->vary);
        VSB_delete(vary);
    }

    wrk->obj->xid = sp->xid;
    wrk->obj->response = sp->err_code;
    WS_Assert(wrk->obj->ws_o);

    /* Filter into object */
    hp = wrk->busyobj->beresp;
    hp2 = wrk->obj->http;

    hp2->logtag = HTTP_Obj;
    http_CopyResp(hp2, hp);
    http_FilterFields(wrk, sp->vsl_id, hp2, hp,
                      pass ? HTTPH_R_PASS : HTTPH_A_INS);
    http_CopyHome(wrk, sp->vsl_id, hp2);

    if (http_GetHdr(hp, H_Last_Modified, &b))
        wrk->obj->last_modified = VTIM_parse(b);
    else
        wrk->obj->last_modified = floor(wrk->busyobj->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 (wrk->obj->response == 200 &&
            sp->http->conds &&
            RFC2616_Do_Cond(sp))
        wrk->busyobj->do_stream = 0;

    AssertObjCorePassOrBusy(wrk->obj->objcore);

    if (wrk->busyobj->do_stream) {
        sp->step = STP_PREPRESP;
        return (0);
    }

    /* Use unmodified headers*/
    i = FetchBody(wrk, wrk->obj);

    http_Setup(wrk->busyobj->bereq, NULL);
    http_Setup(wrk->busyobj->beresp, NULL);
    wrk->busyobj->vfp = NULL;
    assert(WRW_IsReleased(wrk));
    AZ(wrk->busyobj->vbc);
    AN(sp->director);

    if (i) {
        HSH_Drop(wrk);
        VBO_DerefBusyObj(wrk, &wrk->busyobj);
        AZ(wrk->obj);
        sp->err_code = 503;
        sp->step = STP_ERROR;
        return (0);
    }

    if (wrk->obj->objcore != NULL) {
        EXP_Insert(wrk->obj);
        AN(wrk->obj->objcore);
        AN(wrk->obj->objcore->ban);
        HSH_Unbusy(wrk);
    }
    VBO_DerefBusyObj(wrk, &wrk->busyobj);
    wrk->acct_tmp.fetch++;
    sp->step = STP_PREPRESP;
    return (0);
}