int
HTC_Rx(struct http_conn *htc)
{
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->ws->r);
	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-2);
	}
	i = read(htc->fd, htc->rxbuf.e, i);
	if (i <= 0) {
		/*
		 * We wouldn't come here if we had a complete HTTP header
		 * so consequently an EOF can not be OK
		 */
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-1);
	}
	htc->rxbuf.e += i;
	*htc->rxbuf.e = '\0';
	return (HTC_Complete(htc));
}
enum htc_status_e
HTTP1_Rx(struct http_conn *htc)
{
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->ws->r);
	AZ(htc->pipeline.b);
	AZ(htc->pipeline.e);
	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (HTTP1_OVERFLOW);
	}
	i = read(htc->fd, htc->rxbuf.e, i);
	if (i <= 0) {
		/*
		 * We wouldn't come here if we had a complete HTTP header
		 * so consequently an EOF can not be OK
		 */
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (HTTP1_ERROR_EOF);
	}
	htc->rxbuf.e += i;
	*htc->rxbuf.e = '\0';
	return (HTTP1_Complete(htc));
}
Exemple #3
0
void
http_CollectHdr(struct http *hp, const char *hdr)
{
	unsigned u, v, ml, f = 0, x;
	char *b = NULL, *e = NULL;

	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
		while (u < hp->nhd && http_IsHdr(&hp->hd[u], hdr)) {
			Tcheck(hp->hd[u]);
			if (f == 0) {
				/* Found first header, just record the fact */
				f = u;
				break;
			}
			if (b == NULL) {
				/* Found second header, start our collection */
				ml = WS_Reserve(hp->ws, 0);
				b = hp->ws->f;
				e = b + ml;
				x = Tlen(hp->hd[f]);
				if (b + x < e) {
					memcpy(b, hp->hd[f].b, x);
					b += x;
				} else
					b = e;
			}

			AN(b);
			AN(e);

			/* Append the Nth header we found */
			if (b < e)
				*b++ = ',';
			x = Tlen(hp->hd[u]) - *hdr;
			if (b + x < e) {
				memcpy(b, hp->hd[u].b + *hdr, x);
				b += x;
			} else
				b = e;

			/* Shift remaining headers up one slot */
			for (v = u; v < hp->nhd - 1; v++)
				hp->hd[v] = hp->hd[v + 1];
			hp->nhd--;
		}

	}
	if (b == NULL)
		return;
	AN(e);
	if (b >= e) {
		WS_Release(hp->ws, 0);
		return;
	}
	*b = '\0';
	hp->hd[f].b = hp->ws->f;
	hp->hd[f].e = b;
	WS_ReleaseP(hp->ws, b + 1);
}
int
HTC_Rx(struct http_conn *htc)
{
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->ws->r);
	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-2);
	}
	i = read(htc->fd, htc->rxbuf.e, i);
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-1);
	}
	htc->rxbuf.e += i;
	*htc->rxbuf.e = '\0';
	return (HTC_Complete(htc));
}
int
HTC_RxNoCompleteCheck(struct http_conn *htc)
{
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->ws->r);
	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-2);
	}
	i = CFD_read(&htc->fds, htc->rxbuf.e, i);
	if (i == -2)
		return (1);
	if (i <= 0) {
		WS_ReleaseP(htc->ws, htc->rxbuf.b);
		return (-1);
	}
	htc->rxbuf.e += i;
	*htc->rxbuf.e = '\0';
	return (0);
}
int
HTC_Complete(struct http_conn *htc)
{
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	i = htc_header_complete(&htc->rxbuf);
	assert(i >= 0);
	if (i == 0)
		return (0);
	WS_ReleaseP(htc->ws, htc->rxbuf.e);
	if (htc->rxbuf.b + i < htc->rxbuf.e) {
		htc->pipeline.b = htc->rxbuf.b + i;
		htc->pipeline.e = htc->rxbuf.e;
		htc->rxbuf.e = htc->pipeline.b;
	}
	return (1);
}
enum htc_status_e
HTTP1_Complete(struct http_conn *htc)
{
	char *p;
	txt *t;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AZ(htc->pipeline.b);
	AZ(htc->pipeline.e);

	t = &htc->rxbuf;
	Tcheck(*t);
	assert(*t->e == '\0');

	/* Skip any leading white space */
	for (p = t->b ; vct_islws(*p); p++)
		continue;
	if (p == t->e) {
		/* All white space */
		t->e = t->b;
		*t->e = '\0';
		return (HTTP1_ALL_WHITESPACE);
	}
	while (1) {
		p = strchr(p, '\n');
		if (p == NULL)
			return (HTTP1_NEED_MORE);
		p++;
		if (*p == '\r')
			p++;
		if (*p == '\n')
			break;
	}
	p++;
	WS_ReleaseP(htc->ws, t->e);
	if (p < t->e) {
		htc->pipeline.b = p;
		htc->pipeline.e = t->e;
		t->e = p;
	}
	return (HTTP1_COMPLETE);
}
Exemple #8
0
/* Possible error returns:
 *
 * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
 * block. This is a connection level error.
 *
 * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
 * is a stream level error.
 */
int
h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d)
{
	int ret;

	CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
	CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
	CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
	WS_ReleaseP(h2->new_req->http->ws, d->out);
	if (d->vhd_ret != VHD_OK) {
		/* HPACK header block didn't finish at an instruction
		   boundary */
		VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
		    "HPACK compression error (%s)", VHD_Error(d->vhd_ret));
		ret = H2E_COMPRESSION_ERROR;
	} else
		ret = d->error;
	d->magic = 0;
	return (ret);
}
enum http1_status_e
HTTP1_Complete(struct http_conn *htc)
{
	char *p;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AZ(htc->pipeline_b);
	AZ(htc->pipeline_e);

	assert(htc->rxbuf_e >= htc->rxbuf_b);
	assert(*htc->rxbuf_e == '\0');

	/* Skip any leading white space */
	for (p = htc->rxbuf_b ; vct_islws(*p); p++)
		continue;
	if (p == htc->rxbuf_e) {
		/* All white space */
		htc->rxbuf_e = htc->rxbuf_b;
		*htc->rxbuf_e = '\0';
		return (HTTP1_ALL_WHITESPACE);
	}
	while (1) {
		p = strchr(p, '\n');
		if (p == NULL)
			return (HTTP1_NEED_MORE);
		p++;
		if (*p == '\r')
			p++;
		if (*p == '\n')
			break;
	}
	p++;
	WS_ReleaseP(htc->ws, htc->rxbuf_e);
	if (p < htc->rxbuf_e) {
		htc->pipeline_b = p;
		htc->pipeline_e = htc->rxbuf_e;
		htc->rxbuf_e = p;
	}
	return (HTTP1_COMPLETE);
}
const char *
VRT_regsub(const struct sess *sp, int all, const char *str, void *re,
    const char *sub)
{
	int ovector[30];
	vre_t *t;
	int i, l;
	txt res;
	char *b0;
	const char *s;
	unsigned u, x;

	AN(re);
	if (str == NULL)
		str = "";
	t = re;
	memset(ovector, 0, sizeof(ovector));
	i = VRE_exec(t, str, strlen(str), 0, 0, ovector, 30,
	    &params->vre_limits);

	/* If it didn't match, we can return the original string */
	if (i == VRE_ERROR_NOMATCH)
		return(str);
	if (i < VRE_ERROR_NOMATCH ) {
		WSP(sp, SLT_VCL_Error, "Regexp matching returned %d", i);
		return(str);
	}

	u = WS_Reserve(sp->http->ws, 0);
	res.e = res.b = b0 = sp->http->ws->f;
	res.e += u;

	do {
		/* Copy prefix to match */
		Tadd(&res, str, ovector[0]);
		for (s = sub ; *s != '\0'; s++ ) {
			if (*s != '\\' || s[1] == '\0') {
				if (res.b < res.e)
					*res.b++ = *s;
				continue;
			}
			s++;
			if (isdigit(*s)) {
				x = *s - '0';
				l = ovector[2*x+1] - ovector[2*x];
				Tadd(&res, str + ovector[2*x], l);
				continue;
			} else {
				if (res.b < res.e)
					*res.b++ = *s;
			}
		}
		str += ovector[1];
		if (!all)
			break;
		memset(&ovector, 0, sizeof(ovector));
		i = VRE_exec(t, str, strlen(str), 0, 0, ovector, 30,
		    &params->vre_limits);
		if (i < VRE_ERROR_NOMATCH ) {
			WSP(sp, SLT_VCL_Error,
			    "Regexp matching returned %d", i);
			return(str);
		}
	} while (i != VRE_ERROR_NOMATCH);

	/* Copy suffix to match */
	l = strlen(str) + 1;
	Tadd(&res, str, l);
	if (res.b >= res.e) {
		WS_Release(sp->http->ws, 0);
		return (str);
	}
	Tcheck(res);
	WS_ReleaseP(sp->http->ws, res.b);
	return (b0);
}
int
V1F_FetchRespHdr(struct busyobj *bo)
{

	struct http *hp;
	enum htc_status_e hs;
	int first;
	struct http_conn *htc;

	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
	CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC);

	htc = bo->htc;

	VSC_C_main->backend_req++;

	/* Receive response */

	SES_RxInit(htc, bo->ws, cache_param->http_resp_size,
	    cache_param->http_resp_hdr_len);
	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);

	VTCP_set_read_timeout(htc->fd, htc->first_byte_timeout);

	first = 1;
	do {
		hs = SES_Rx(htc, 0);
		if (hs == HTC_S_MORE)
			hs = HTTP1_Complete(htc);
		if (hs == HTC_S_OVERFLOW) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			bo->acct.beresp_hdrbytes +=
			    htc->rxbuf_e - htc->rxbuf_b;
			VSLb(bo->vsl, SLT_FetchError,
			    "http %sread error: overflow",
			    first ? "first " : "");
			htc->doclose = SC_RX_OVERFLOW;
			return (-1);
		}
		if (hs == HTC_S_EOF) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			bo->acct.beresp_hdrbytes +=
			    htc->rxbuf_e - htc->rxbuf_b;
			VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF",
			    first ? "first " : "");
			htc->doclose = SC_RX_TIMEOUT;
			return (first ? 1 : -1);
		}
		if (first) {
			first = 0;
			VTCP_set_read_timeout(htc->fd,
			    htc->between_bytes_timeout);
		}
	} while (hs != HTC_S_COMPLETE);
	bo->acct.beresp_hdrbytes += htc->rxbuf_e - htc->rxbuf_b;

	hp = bo->beresp;

	if (HTTP1_DissectResponse(hp, htc)) {
		VSLb(bo->vsl, SLT_FetchError, "http format error");
		htc->doclose = SC_RX_JUNK;
		return (-1);
	}

	htc->doclose = http_DoConnection(hp);

	return (0);
}
Exemple #12
0
enum htc_status_e
HTC_RxStuff(struct http_conn *htc, htc_complete_f *func,
    double *t1, double *t2, double ti, double tn, int maxbytes)
{
	double tmo;
	double now;
	enum htc_status_e hs;
	ssize_t z;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->rfd);
	assert(*htc->rfd > 0);
	AN(htc->ws->r);
	AN(htc->rxbuf_b);
	assert(htc->rxbuf_b <= htc->rxbuf_e);
	assert(htc->rxbuf_e <= htc->ws->r);

	AZ(isnan(tn));
	if (t1 != NULL)
		assert(isnan(*t1));

	if (htc->rxbuf_e == htc->ws->r) {
		/* Can't work with a zero size buffer */
		WS_ReleaseP(htc->ws, htc->rxbuf_b);
		return (HTC_S_OVERFLOW);
	}
	z = (htc->ws->r - htc->rxbuf_b);
	if (z < maxbytes)
		maxbytes = z;	/* Cap maxbytes at available WS */

	while (1) {
		now = VTIM_real();
		AZ(htc->pipeline_b);
		AZ(htc->pipeline_e);
		assert(htc->rxbuf_e <= htc->ws->r);

		hs = func(htc);
		if (hs == HTC_S_OVERFLOW || hs == HTC_S_JUNK) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (hs);
		}
		if (hs == HTC_S_COMPLETE) {
			WS_ReleaseP(htc->ws, htc->rxbuf_e);
			/* Got it, run with it */
			if (t1 != NULL && isnan(*t1))
				*t1 = now;
			if (t2 != NULL)
				*t2 = now;
			return (HTC_S_COMPLETE);
		}
		if (hs == HTC_S_MORE) {
			/* Working on it */
			if (t1 != NULL && isnan(*t1))
				*t1 = now;
		} else if (hs == HTC_S_EMPTY)
			htc->rxbuf_e = htc->rxbuf_b;
		else
			WRONG("htc_status_e");

		tmo = tn - now;
		if (!isnan(ti) && ti < tn && hs == HTC_S_EMPTY)
			tmo = ti - now;
		z = maxbytes - (htc->rxbuf_e - htc->rxbuf_b);
		if (z <= 0) {
			/* maxbytes reached but not HTC_S_COMPLETE. Return
			 * overflow. */
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (HTC_S_OVERFLOW);
		}
		if (tmo <= 0.0)
			tmo = 1e-3;
		z = VTCP_read(*htc->rfd, htc->rxbuf_e, z, tmo);
		if (z == 0 || z == -1) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (HTC_S_EOF);
		} else if (z > 0)
			htc->rxbuf_e += z;
		else if (z == -2) {
			if (hs == HTC_S_EMPTY && ti <= now) {
				WS_ReleaseP(htc->ws, htc->rxbuf_b);
				return (HTC_S_IDLE);
			}
			if (tn <= now) {
				WS_ReleaseP(htc->ws, htc->rxbuf_b);
				return (HTC_S_TIMEOUT);
			}
		}
	}
}
static int
cnt_lookup(struct sess *sp)
{
    struct objcore *oc;
    struct object *o;
    struct objhead *oh;
    struct worker *wrk;

    CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
    wrk = sp->wrk;
    CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);

    CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
    AZ(wrk->busyobj);

    if (sp->hash_objhead == NULL) {
        /* Not a waiting list return */
        AZ(sp->vary_b);
        AZ(sp->vary_l);
        AZ(sp->vary_e);
        (void)WS_Reserve(sp->ws, 0);
    } else {
        AN(sp->ws->r);
    }
    sp->vary_b = (void*)sp->ws->f;
    sp->vary_e = (void*)sp->ws->r;
    sp->vary_b[2] = '\0';

    oc = HSH_Lookup(sp, &oh);

    if (oc == NULL) {
        /*
         * We lost the session to a busy object, disembark the
         * worker thread.   The hash code to restart the session,
         * still in STP_LOOKUP, later when the busy object isn't.
         * NB:  Do not access sp any more !
         */
        return (1);
    }


    CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
    CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);

    /* If we inserted a new object it's a miss */
    if (oc->flags & OC_F_BUSY) {
        wrk->stats.cache_miss++;

        if (sp->vary_l != NULL) {
            assert(oc->busyobj->vary == sp->vary_b);
            VRY_Validate(oc->busyobj->vary);
            WS_ReleaseP(sp->ws, (void*)sp->vary_l);
        } else {
            AZ(oc->busyobj->vary);
            WS_Release(sp->ws, 0);
        }
        sp->vary_b = NULL;
        sp->vary_l = NULL;
        sp->vary_e = NULL;

        wrk->objcore = oc;
        CHECK_OBJ_NOTNULL(wrk->busyobj, BUSYOBJ_MAGIC);
        sp->step = STP_MISS;
        return (0);
    }

    o = oc_getobj(wrk, oc);
    CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
    wrk->obj = o;

    WS_Release(sp->ws, 0);
    sp->vary_b = NULL;
    sp->vary_l = NULL;
    sp->vary_e = NULL;

    if (oc->flags & OC_F_PASS) {
        wrk->stats.cache_hitpass++;
        WSP(sp, SLT_HitPass, "%u", wrk->obj->xid);
        (void)HSH_Deref(wrk, NULL, &wrk->obj);
        wrk->objcore = NULL;
        sp->step = STP_PASS;
        return (0);
    }

    wrk->stats.cache_hit++;
    WSP(sp, SLT_Hit, "%u", wrk->obj->xid);
    sp->step = STP_HIT;
    return (0);
}
void
http_CollectHdr(struct http *hp, const char *hdr)
{
	unsigned u, l, ml, f, x, d;
	char *b = NULL, *e = NULL;

	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	if (WS_Overflowed(hp->ws))
		return;
	l = hdr[0];
	assert(l == strlen(hdr + 1));
	assert(hdr[l] == ':');
	f = http_findhdr(hp, l - 1, hdr + 1);
	if (f == 0)
		return;

	for (d = u = f + 1; u < hp->nhd; u++) {
		Tcheck(hp->hd[u]);
		if (!http_IsHdr(&hp->hd[u], hdr)) {
			if (d != u) {
				hp->hd[d] = hp->hd[u];
				hp->hdf[d] = hp->hdf[u];
			}
			d++;
			continue;
		}
		if (b == NULL) {
			/* Found second header, start our collection */
			ml = WS_Reserve(hp->ws, 0);
			b = hp->ws->f;
			e = b + ml;
			x = Tlen(hp->hd[f]);
			if (b + x >= e) {
				http_fail(hp);
				VSLb(hp->vsl, SLT_LostHeader, "%s", hdr + 1);
				WS_Release(hp->ws, 0);
				return;
			}
			memcpy(b, hp->hd[f].b, x);
			b += x;
		}

		AN(b);
		AN(e);

		/* Append the Nth header we found */
		if (b < e)
			*b++ = ',';
		x = Tlen(hp->hd[u]) - l;
		if (b + x >= e) {
			http_fail(hp);
			VSLb(hp->vsl, SLT_LostHeader, "%s", hdr + 1);
			WS_Release(hp->ws, 0);
			return;
		}
		memcpy(b, hp->hd[u].b + *hdr, x);
		b += x;
	}
	if (b == NULL)
		return;
	hp->nhd = (uint16_t)d;
	AN(e);
	*b = '\0';
	hp->hd[f].b = hp->ws->f;
	hp->hd[f].e = b;
	WS_ReleaseP(hp->ws, b + 1);
}
Exemple #15
0
enum htc_status_e
SES_RxReq(const struct worker *wrk, struct req *req, htc_complete_f *func)
{
	double tmo;
	double now, when;
	struct sess *sp;
	enum htc_status_e hs;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	sp = req->sp;
	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);

	AZ(isnan(sp->t_idle));
	assert(isnan(req->t_first));

	when = sp->t_idle + cache_param->timeout_idle;
	tmo = cache_param->timeout_linger;
	while (1) {
		hs = SES_Rx(req->htc, tmo);
		now = VTIM_real();
		if (hs == HTC_S_EOF) {
			WS_ReleaseP(req->htc->ws, req->htc->rxbuf_b);
			return (HTC_S_CLOSE);
		}
		if (hs == HTC_S_OVERFLOW) {
			WS_ReleaseP(req->htc->ws, req->htc->rxbuf_b);
			return (HTC_S_OVERFLOW);
		}
		hs = func(req->htc);
		if (hs == HTC_S_OVERFLOW) {
			WS_ReleaseP(req->htc->ws, req->htc->rxbuf_b);
			return (HTC_S_OVERFLOW);
		}
		if (hs == HTC_S_JUNK) {
			WS_ReleaseP(req->htc->ws, req->htc->rxbuf_b);
			return (HTC_S_JUNK);
		}
		if (hs == HTC_S_COMPLETE) {
			/* Got it, run with it */
			if (isnan(req->t_first))
				req->t_first = now;
			req->t_req = now;
			return (HTC_S_COMPLETE);
		}
		if (when < now)
			return (HTC_S_TIMEOUT);
		if (hs == HTC_S_MORE) {
			/* Working on it */
			if (isnan(req->t_first))
				req->t_first = now;
			tmo = when - now;
			continue;
		}
		assert(hs == HTC_S_EMPTY);
		/* Nothing but whitespace */
		tmo = sp->t_idle + cache_param->timeout_linger - now;
		if (tmo < 0)
			return (HTC_S_IDLE);
	}
}
const char *
VRT_regsub(struct req *req, int all, const char *str, void *re,
    const char *sub)
{
	int ovector[30];
	vre_t *t;
	int i, l;
	txt res;
	char *b0;
	const char *s;
	unsigned u, x;
	int options = 0;
	size_t len;

	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	AN(re);
	if (str == NULL)
		str = "";
	if (sub == NULL)
		sub = "";
	t = re;
	memset(ovector, 0, sizeof(ovector));
	len = strlen(str);
	i = VRE_exec(t, str, len, 0, options, ovector, 30,
	    &cache_param->vre_limits);

	/* If it didn't match, we can return the original string */
	if (i == VRE_ERROR_NOMATCH)
		return(str);
	if (i < VRE_ERROR_NOMATCH ) {
		VSLb(req->vsl, SLT_VCL_Error, "Regexp matching returned %d", i);
		return(str);
	}

	u = WS_Reserve(req->http->ws, 0);
	res.e = res.b = b0 = req->http->ws->f;
	res.e += u;

	do {
		/* Copy prefix to match */
		Tadd(&res, str, ovector[0]);
		for (s = sub ; *s != '\0'; s++ ) {
			if (*s != '\\' || s[1] == '\0') {
				if (res.b < res.e)
					*res.b++ = *s;
				continue;
			}
			s++;
			if (isdigit(*s)) {
				x = *s - '0';
				l = ovector[2*x+1] - ovector[2*x];
				Tadd(&res, str + ovector[2*x], l);
				continue;
			} else {
				if (res.b < res.e)
					*res.b++ = *s;
			}
		}
		str += ovector[1];
		len -= ovector[1];
		if (!all)
			break;
		memset(&ovector, 0, sizeof(ovector));
		options |= VRE_NOTEMPTY;
		i = VRE_exec(t, str, len, 0, options, ovector, 30,
		    &cache_param->vre_limits);
		if (i < VRE_ERROR_NOMATCH ) {
			WS_Release(req->http->ws, 0);
			VSLb(req->vsl, SLT_VCL_Error,
			    "Regexp matching returned %d", i);
			return(str);
		}
	} while (i != VRE_ERROR_NOMATCH);

	/* Copy suffix to match */
	Tadd(&res, str, len+1);
	if (res.b >= res.e) {
		WS_Release(req->http->ws, 0);
		return (str);
	}
	Tcheck(res);
	WS_ReleaseP(req->http->ws, res.b);
	return (b0);
}
enum htc_status_e
HTC_RxStuff(struct http_conn *htc, htc_complete_f *func,
    double *t1, double *t2, double ti, double tn, int maxbytes)
{
	double tmo;
	double now;
	enum htc_status_e hs;
	int i;

	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
	AN(htc->ws->r);
	AN(htc->rxbuf_b);
	assert(htc->rxbuf_b <= htc->rxbuf_e);

	AZ(isnan(tn));
	if (t1 != NULL)
		assert(isnan(*t1));

	if (htc->rxbuf_e == htc->ws->r) {
		/* Can't work with a zero size buffer */
		WS_ReleaseP(htc->ws, htc->rxbuf_b);
		return (HTC_S_OVERFLOW);
	}
	if ((htc->ws->r - htc->rxbuf_b) - 1L < maxbytes)
		maxbytes = (htc->ws->r - htc->rxbuf_b) - 1L; /* Space for NUL */

	while (1) {
		now = VTIM_real();
		AZ(htc->pipeline_b);
		AZ(htc->pipeline_e);
		assert(htc->rxbuf_e < htc->ws->r);
		*htc->rxbuf_e = '\0';
		hs = func(htc);
		if (hs == HTC_S_OVERFLOW || hs == HTC_S_JUNK) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (hs);
		}
		if (hs == HTC_S_COMPLETE) {
			WS_ReleaseP(htc->ws, htc->rxbuf_e);
			/* Got it, run with it */
			if (t1 != NULL && isnan(*t1))
				*t1 = now;
			if (t2 != NULL)
				*t2 = now;
			return (HTC_S_COMPLETE);
		}
		if (hs == HTC_S_MORE) {
			/* Working on it */
			if (t1 != NULL && isnan(*t1))
				*t1 = now;
		} else if (hs == HTC_S_EMPTY)
			htc->rxbuf_e = htc->rxbuf_b;
		else
			WRONG("htc_status_e");

		tmo = tn - now;
		if (!isnan(ti) && ti < tn)
			tmo = ti - now;
		i = maxbytes - (htc->rxbuf_e - htc->rxbuf_b);
		assert(i >= 0);
		if (i == 0) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (HTC_S_OVERFLOW);
		}
		if (tmo <= 0.0)
			tmo = 1e-3;
		i = VTCP_read(htc->fd, htc->rxbuf_e, i, tmo);
		if (i == 0 || i == -1) {
			WS_ReleaseP(htc->ws, htc->rxbuf_b);
			return (HTC_S_EOF);
		} else if (i > 0)
			htc->rxbuf_e += i;
		else if (i == -2) {
			if (hs == HTC_S_EMPTY && ti <= now) {
				WS_ReleaseP(htc->ws, htc->rxbuf_b);
				return (HTC_S_IDLE);
			}
			if (tn <= now) {
				WS_ReleaseP(htc->ws, htc->rxbuf_b);
				return (HTC_S_TIMEOUT);
			}
		}
	}
}