[master] 24c031d Shift HTTP/1 protocol specific source files into a subdirectory

Poul-Henning Kamp phk at FreeBSD.org
Wed Oct 1 21:05:14 CEST 2014


commit 24c031dd8c3a84df30199af67493ad489c85766b
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Wed Oct 1 18:28:58 2014 +0000

    Shift HTTP/1 protocol specific source files into a subdirectory

diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index ab7705d..fdec53b 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -26,11 +26,6 @@ varnishd_SOURCES = \
 	cache/cache_gzip.c \
 	cache/cache_hash.c \
 	cache/cache_http.c \
-	cache/cache_http1_fetch.c \
-	cache/cache_http1_fsm.c \
-	cache/cache_http1_pipe.c \
-	cache/cache_http1_proto.c \
-	cache/cache_http1_vfp.c \
 	cache/cache_lck.c \
 	cache/cache_main.c \
 	cache/cache_mempool.c \
@@ -39,7 +34,6 @@ varnishd_SOURCES = \
 	cache/cache_pool.c \
 	cache/cache_req_body.c \
 	cache/cache_req_fsm.c \
-	cache/cache_http1_deliver.c \
 	cache/cache_rfc2616.c \
 	cache/cache_session.c \
 	cache/cache_shmlog.c \
@@ -59,6 +53,12 @@ varnishd_SOURCES = \
 	hash/hash_critbit.c \
 	hash/hash_mgt.c \
 	hash/hash_simple_list.c \
+	http1/cache_http1_deliver.c \
+	http1/cache_http1_fetch.c \
+	http1/cache_http1_fsm.c \
+	http1/cache_http1_pipe.c \
+	http1/cache_http1_proto.c \
+	http1/cache_http1_vfp.c \
 	mgt/mgt_child.c \
 	mgt/mgt_cli.c \
 	mgt/mgt_main.c \
diff --git a/bin/varnishd/cache/cache_http1_deliver.c b/bin/varnishd/cache/cache_http1_deliver.c
deleted file mode 100644
index 766e493..0000000
--- a/bin/varnishd/cache/cache_http1_deliver.c
+++ /dev/null
@@ -1,463 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2014 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "vct.h"
-
-/*--------------------------------------------------------------------*/
-
-static int __match_proto__(vdp_bytes)
-v1d_bytes(struct req *req, enum vdp_action act, const void *ptr, ssize_t len)
-{
-	ssize_t wl = 0;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-
-	assert(req->vdp_nxt == -1);	/* always at the bottom of the pile */
-
-	if (len > 0)
-		wl = WRW_Write(req->wrk, ptr, len);
-	req->resp_bodybytes += wl;
-	if (act > VDP_NULL && WRW_Flush(req->wrk))
-		return (-1);
-	if (len != wl)
-		return (-1);
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int __match_proto__(vdp_bytes)
-v1d_range_bytes(struct req *req, enum vdp_action act, const void *ptr,
-    ssize_t len)
-{
-	int retval = 0;
-	ssize_t l;
-	const char *p = ptr;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	l = req->range_low - req->range_off;
-	if (l > 0) {
-		if (l > len)
-			l = len;
-		req->range_off += l;
-		p += l;
-		len -= l;
-	}
-	l = req->range_high - req->range_off;
-	if (l > len)
-		l = len;
-	if (l > 0)
-		retval = VDP_bytes(req, act, p, l);
-	else if (act > VDP_NULL)
-		retval = VDP_bytes(req, act, p, 0);
-	req->range_off += len;
-	return (retval);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-v1d_dorange(struct req *req, struct busyobj *bo, const char *r)
-{
-	ssize_t len, low, high, has_low;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
-	assert(http_IsStatus(req->resp, 200));
-
-	/* We must snapshot the length if we're streaming from the backend */
-	if (bo != NULL)
-		len = VBO_waitlen(req->wrk, bo, -1);
-	else
-		len = ObjGetLen(req->wrk, req->objcore);
-
-	if (strncmp(r, "bytes=", 6))
-		return;
-	r += 6;
-
-	/* The low end of range */
-	has_low = low = 0;
-	if (!vct_isdigit(*r) && *r != '-')
-		return;
-	while (vct_isdigit(*r)) {
-		has_low = 1;
-		low *= 10;
-		low += *r - '0';
-		r++;
-	}
-
-	if (low >= len)
-		return;
-
-	if (*r != '-')
-		return;
-	r++;
-
-	/* The high end of range */
-	if (vct_isdigit(*r)) {
-		high = 0;
-		while (vct_isdigit(*r)) {
-			high *= 10;
-			high += *r - '0';
-			r++;
-		}
-		if (!has_low) {
-			low = len - high;
-			if (low < 0)
-				low = 0;
-			high = len - 1;
-		}
-	} else
-		high = len - 1;
-	if (*r != '\0')
-		return;
-
-	if (high >= len)
-		high = len - 1;
-
-	if (low > high)
-		return;
-
-	http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
-	    (intmax_t)low, (intmax_t)high, (intmax_t)len);
-	http_Unset(req->resp, H_Content_Length);
-	if (req->res_mode & RES_LEN)
-		http_PrintfHeader(req->resp, "Content-Length: %jd",
-		    (intmax_t)(1 + high - low));
-	http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
-
-	req->range_off = 0;
-	req->range_low = low;
-	req->range_high = high + 1;
-	VDP_push(req, v1d_range_bytes);
-}
-
-/*--------------------------------------------------------------------*/
-
-static enum objiter_status
-v1d_WriteDirObj(struct req *req)
-{
-	enum objiter_status ois;
-	ssize_t len;
-	void *oi;
-	void *ptr;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-
-	oi = ObjIterBegin(req->wrk, req->objcore);
-	XXXAN(oi);
-
-	do {
-		ois = ObjIter(req->objcore, oi, &ptr, &len);
-		switch(ois) {
-		case OIS_DONE:
-			AZ(len);
-			break;
-		case OIS_ERROR:
-			break;
-		case OIS_DATA:
-		case OIS_STREAM:
-			if (VDP_bytes(req,
-			     ois == OIS_DATA ? VDP_NULL : VDP_FLUSH,  ptr, len))
-				ois = OIS_ERROR;
-			break;
-		default:
-			WRONG("Wrong OIS value");
-		}
-	} while (ois == OIS_DATA || ois == OIS_STREAM);
-	(void)VDP_bytes(req, VDP_FINISH,  NULL, 0);
-	ObjIterEnd(req->objcore, &oi);
-	return (ois);
-}
-
-/*--------------------------------------------------------------------
- * V1D_FlushReleaseAcct()
- * Call WRW_FlushRelease on the worker and update the requests
- * byte accounting with the number of bytes transmitted
- *
- * Returns the return value from WRW_FlushRelease()
- */
-unsigned
-V1D_FlushReleaseAcct(struct req *req)
-{
-	unsigned u;
-	uint64_t txcnt = 0, hdrbytes;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CHECK_OBJ_NOTNULL(req->wrk, WORKER_MAGIC);
-	u = WRW_FlushRelease(req->wrk, &txcnt);
-	if (req->acct.resp_hdrbytes < req->resp_hdrbytes) {
-		hdrbytes = req->resp_hdrbytes - req->acct.resp_hdrbytes;
-		if (hdrbytes > txcnt)
-			hdrbytes = txcnt;
-	} else
-		hdrbytes = 0;
-	req->acct.resp_hdrbytes += hdrbytes;
-	req->acct.resp_bodybytes += txcnt - hdrbytes;
-	return (u);
-}
-
-void
-V1D_Deliver(struct req *req, struct busyobj *bo)
-{
-	const char *r;
-	enum objiter_status ois;
-	ssize_t l;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
-
-	req->res_mode = 0;
-
-	if (!req->disable_esi && ObjGetattr(req->wrk, req->objcore,
-	    OA_ESIDATA, NULL) != NULL) {
-		/* In ESI mode, we can't know the aggregate length */
-		req->res_mode &= ~RES_LEN;
-		req->res_mode |= RES_ESI;
-	} else if (http_IsStatus(req->resp, 304)) {
-		req->res_mode &= ~RES_LEN;
-		http_Unset(req->resp, H_Content_Length);
-		req->wantbody = 0;
-	} else if (bo == NULL) {
-		/* XXX: Not happy with this convoluted test */
-		req->res_mode |= RES_LEN;
-		if (!(req->objcore->flags & OC_F_PASS) ||
-		    ObjGetLen(req->wrk, req->objcore) != 0) {
-			http_Unset(req->resp, H_Content_Length);
-			http_PrintfHeader(req->resp,
-			    "Content-Length: %ju", (uintmax_t)ObjGetLen(
-			    req->wrk, req->objcore));
-		}
-	}
-
-	if (req->esi_level > 0) {
-		/* Included ESI object, always CHUNKED or EOF */
-		req->res_mode &= ~RES_LEN;
-		req->res_mode |= RES_ESI_CHILD;
-	}
-
-	if (cache_param->http_gzip_support &&
-	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) &&
-	    !RFC2616_Req_Gzip(req->http)) {
-		/*
-		 * We don't know what it uncompresses to
-		 * XXX: we could cache that, but would still deliver
-		 * XXX: with multiple writes because of the gunzip buffer
-		 */
-		req->res_mode &= ~RES_LEN;
-		req->res_mode |= RES_GUNZIP;
-	}
-
-	if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
-		/* We havn't chosen yet, do so */
-		if (!req->wantbody) {
-			/* Nothing */
-		} else if (req->http->protover >= 11) {
-			req->res_mode |= RES_CHUNKED;
-		} else {
-			req->res_mode |= RES_EOF;
-			req->doclose = SC_TX_EOF;
-		}
-	}
-	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);
-
-	if (!(req->res_mode & RES_LEN))
-		http_Unset(req->resp, H_Content_Length);
-
-	if (req->res_mode & RES_GUNZIP)
-		http_Unset(req->resp, H_Content_Encoding);
-
-	if (req->res_mode & RES_CHUNKED)
-		http_SetHeader(req->resp, "Transfer-Encoding: chunked");
-
-	http_SetHeader(req->resp,
-	    req->doclose ? "Connection: close" : "Connection: keep-alive");
-
-	req->vdps[0] = v1d_bytes;
-	req->vdp_nxt = 0;
-
-	if (
-	    req->wantbody &&
-	    !(req->res_mode & (RES_ESI|RES_ESI_CHILD)) &&
-	    cache_param->http_range_support &&
-	    http_IsStatus(req->resp, 200)) {
-		http_SetHeader(req->resp, "Accept-Ranges: bytes");
-		if (http_GetHdr(req->http, H_Range, &r))
-			v1d_dorange(req, bo, r);
-	}
-
-	if (req->res_mode & RES_ESI)
-		RFC2616_Weaken_Etag(req->resp);
-
-	WRW_Reserve(req->wrk, &req->sp->fd, req->vsl, req->t_prev);
-
-	/*
-	 * Send HTTP protocol header, unless interior ESI object
-	 */
-	if (!(req->res_mode & RES_ESI_CHILD))
-		req->resp_hdrbytes +=
-		    HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
-
-	if (req->res_mode & RES_CHUNKED)
-		WRW_Chunked(req->wrk);
-
-	ois = OIS_DONE;
-	if (!req->wantbody) {
-		/* This was a HEAD or conditional request */
-	} else if (req->res_mode & RES_ESI) {
-		ESI_Deliver(req);
-	} else if (req->res_mode & RES_ESI_CHILD && req->gzip_resp) {
-		l = -1;
-		while (req->objcore->busyobj) {
-			assert(bo != NULL);
-			l = VBO_waitlen(req->wrk, bo, l);
-		}
-		ESI_DeliverChild(req);
-	} else if (req->res_mode & RES_GUNZIP ||
-	    (req->res_mode & RES_ESI_CHILD &&
-	    !req->gzip_resp &&
-	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED))) {
-		VDP_push(req, VDP_gunzip);
-		req->vgz = VGZ_NewUngzip(req->vsl, "U D -");
-		AZ(VGZ_WrwInit(req->vgz));
-		ois = v1d_WriteDirObj(req);
-		(void)VGZ_Destroy(&req->vgz);
-		VDP_pop(req, VDP_gunzip);
-	} else {
-		ois = v1d_WriteDirObj(req);
-	}
-
-	if (ois == OIS_DONE &&
-	    (req->res_mode & RES_CHUNKED) &&
-	    !(req->res_mode & RES_ESI_CHILD))
-		WRW_EndChunk(req->wrk);
-
-	if ((V1D_FlushReleaseAcct(req) || ois != OIS_DONE) && req->sp->fd >= 0)
-		SES_Close(req->sp, SC_REM_CLOSE);
-}
-
-void
-V1D_Deliver_Synth(struct req *req)
-{
-	const char *r;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	AN(req->synth_body);
-
-	req->res_mode = 0;
-	if (http_IsStatus(req->resp, 304)) {
-		req->res_mode &= ~RES_LEN;
-		http_Unset(req->resp, H_Content_Length);
-		req->wantbody = 0;
-	} else {
-		req->res_mode |= RES_LEN;
-		http_Unset(req->resp, H_Content_Length);
-		http_PrintfHeader(req->resp, "Content-Length: %zd",
-		    VSB_len(req->synth_body));
-	}
-
-	if (req->esi_level > 0) {
-		/* Included ESI object, always CHUNKED or EOF */
-		req->res_mode &= ~RES_LEN;
-		req->res_mode |= RES_ESI_CHILD;
-	}
-
-	if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
-		/* We havn't chosen yet, do so */
-		if (!req->wantbody) {
-			/* Nothing */
-		} else if (req->http->protover >= 11) {
-			req->res_mode |= RES_CHUNKED;
-		} else {
-			req->res_mode |= RES_EOF;
-			req->doclose = SC_TX_EOF;
-		}
-	}
-	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);
-
-	if (!(req->res_mode & RES_LEN))
-		http_Unset(req->resp, H_Content_Length);
-
-	if (req->res_mode & RES_GUNZIP)
-		http_Unset(req->resp, H_Content_Encoding);
-
-	if (req->res_mode & RES_CHUNKED)
-		http_SetHeader(req->resp, "Transfer-Encoding: chunked");
-
-	http_SetHeader(req->resp,
-	    req->doclose ? "Connection: close" : "Connection: keep-alive");
-
-	req->vdps[0] = v1d_bytes;
-	req->vdp_nxt = 0;
-
-	if (
-	    req->wantbody &&
-	    !(req->res_mode & RES_ESI_CHILD) &&
-	    cache_param->http_range_support &&
-	    http_IsStatus(req->resp, 200)) {
-		http_SetHeader(req->resp, "Accept-Ranges: bytes");
-		if (http_GetHdr(req->http, H_Range, &r))
-			v1d_dorange(req, NULL, r);
-	}
-
-	WRW_Reserve(req->wrk, &req->sp->fd, req->vsl, req->t_prev);
-
-	/*
-	 * Send HTTP protocol header, unless interior ESI object
-	 */
-	if (!(req->res_mode & RES_ESI_CHILD))
-		req->resp_hdrbytes +=
-		    HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
-
-	if (req->res_mode & RES_CHUNKED)
-		WRW_Chunked(req->wrk);
-
-	if (!req->wantbody) {
-		/* This was a HEAD or conditional request */
-#if 0
-	XXX: Missing pretend GZIP for esi-children
-	} else if (req->res_mode & RES_ESI_CHILD && req->gzip_resp) {
-		ESI_DeliverChild(req);
-#endif
-	} else {
-		(void)VDP_bytes(req, VDP_FLUSH, VSB_data(req->synth_body),
-		    VSB_len(req->synth_body));
-		(void)VDP_bytes(req, VDP_FINISH,  NULL, 0);
-	}
-
-	if (req->res_mode & RES_CHUNKED && !(req->res_mode & RES_ESI_CHILD))
-		WRW_EndChunk(req->wrk);
-
-	if (V1D_FlushReleaseAcct(req) && req->sp->fd >= 0)
-		SES_Close(req->sp, SC_REM_CLOSE);
-}
diff --git a/bin/varnishd/cache/cache_http1_fetch.c b/bin/varnishd/cache/cache_http1_fetch.c
deleted file mode 100644
index 1b3514a..0000000
--- a/bin/varnishd/cache/cache_http1_fetch.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2014 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "hash/hash_slinger.h"
-
-#include "cache_backend.h"
-#include "cache_director.h"
-#include "vcli_priv.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-/*--------------------------------------------------------------------
- * Pass the request body to the backend
- */
-
-static int __match_proto__(req_body_iter_f)
-vbf_iter_req_body(struct req *req, void *priv, void *ptr, size_t l)
-{
-	struct worker *wrk;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CAST_OBJ_NOTNULL(wrk, priv, WORKER_MAGIC);
-
-	if (l > 0) {
-		(void)WRW_Write(wrk, ptr, l);
-		if (WRW_Flush(wrk))
-			return (-1);
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Send request, and receive the HTTP protocol response, but not the
- * response body.
- *
- * Return value:
- *	-1 failure, not retryable
- *	 0 success
- *	 1 failure which can be retried.
- */
-
-int
-V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo)
-{
-	struct vbc *vc;
-	struct http *hp;
-	enum http1_status_e hs;
-	int retry = -1;
-	int i, j, first;
-	struct http_conn *htc;
-	ssize_t hdrbytes;
-	int do_chunked = 0;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
-	CHECK_OBJ_NOTNULL(bo->vbc, VBC_MAGIC);
-	CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC);
-
-	htc = bo->htc;
-	hp = bo->bereq;
-	vc = bo->vbc;
-	if (vc->recycled)
-		retry = 1;
-
-	/*
-	 * Now that we know our backend, we can set a default Host:
-	 * header if one is necessary.  This cannot be done in the VCL
-	 * because the backend may be chosen by a director.
-	 */
-	if (!http_GetHdr(bo->bereq, H_Host, NULL))
-		VDI_AddHostHeader(bo->bereq, vc);
-
-	if (bo->req != NULL &&
-	    bo->req->req_body_status == REQ_BODY_WITHOUT_LEN) {
-		http_PrintfHeader(hp, "Transfer-Encoding: chunked");
-		do_chunked = 1;
-	}
-
-	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
-	WRW_Reserve(wrk, &vc->fd, bo->vsl, bo->t_prev);
-	hdrbytes = HTTP1_Write(wrk, hp, HTTP1_Req);
-
-	/* Deal with any message-body the request might (still) have */
-	i = 0;
-
-	if (bo->req != NULL) {
-		if (do_chunked)
-			WRW_Chunked(wrk);
-		i = VRB_Iterate(bo->req, vbf_iter_req_body, wrk);
-
-		if (bo->req->req_body_status == REQ_BODY_TAKEN) {
-			retry = -1;
-		} else if (bo->req->req_body_status == REQ_BODY_FAIL) {
-			VSLb(bo->vsl, SLT_FetchError,
-			    "req.body read error: %d (%s)",
-			    errno, strerror(errno));
-			bo->req->doclose = SC_RX_BODY;
-			retry = -1;
-		}
-		if (do_chunked)
-			WRW_EndChunk(wrk);
-	}
-
-	j = WRW_FlushRelease(wrk, &bo->acct.bereq_hdrbytes);
-	if (bo->acct.bereq_hdrbytes > hdrbytes) {
-		bo->acct.bereq_bodybytes = bo->acct.bereq_hdrbytes - hdrbytes;
-		bo->acct.bereq_hdrbytes = hdrbytes;
-	}
-	if (j != 0 || i != 0) {
-		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
-		    errno, strerror(errno));
-		VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
-		VBE_CloseFd(&bo->vbc, &bo->acct);
-		/* XXX: other cleanup ? */
-		return (retry);
-	}
-	VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
-
-	VSC_C_main->backend_req++;
-
-	/* Receive response */
-
-	HTTP1_Init(htc, bo->ws, vc->fd, bo->vsl,
-	    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(vc->fd, vc->first_byte_timeout);
-
-	first = 1;
-	do {
-		hs = HTTP1_Rx(htc);
-		if (hs == HTTP1_OVERFLOW) {
-			bo->acct.beresp_hdrbytes +=
-			    htc->rxbuf_e - htc->rxbuf_b;
-			VSLb(bo->vsl, SLT_FetchError,
-			    "http %sread error: overflow",
-			    first ? "first " : "");
-			VBE_CloseFd(&bo->vbc, &bo->acct);
-			/* XXX: other cleanup ? */
-			return (-1);
-		}
-		if (hs == HTTP1_ERROR_EOF) {
-			bo->acct.beresp_hdrbytes +=
-			    htc->rxbuf_e - htc->rxbuf_b;
-			VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF",
-			    first ? "first " : "");
-			VBE_CloseFd(&bo->vbc, &bo->acct);
-			/* XXX: other cleanup ? */
-			return (retry);
-		}
-		if (first) {
-			retry = -1;
-			first = 0;
-			VTCP_set_read_timeout(vc->fd,
-			    vc->between_bytes_timeout);
-		}
-	} while (hs != HTTP1_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");
-		VBE_CloseFd(&bo->vbc, &bo->acct);
-		/* XXX: other cleanup ? */
-		return (-1);
-	}
-
-	bo->doclose = http_DoConnection(hp);
-
-	return (0);
-}
diff --git a/bin/varnishd/cache/cache_http1_fsm.c b/bin/varnishd/cache/cache_http1_fsm.c
deleted file mode 100644
index d3438f8..0000000
--- a/bin/varnishd/cache/cache_http1_fsm.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2014 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * This file contains the two central state machine for pushing HTTP1
- * sessions through their states.
- *
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-#include "hash/hash_slinger.h"
-
-#include "vcl.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-/*----------------------------------------------------------------------
- * Collect a request from the client.
- */
-
-static enum req_fsm_nxt
-http1_wait(struct sess *sp, struct worker *wrk, struct req *req)
-{
-	int j, tmo;
-	struct pollfd pfd[1];
-	double now, when;
-	enum sess_close why = SC_NULL;
-	enum http1_status_e hs;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-
-	assert(req->sp == sp);
-
-	AZ(req->vcl);
-	AZ(req->esi_level);
-	AZ(isnan(sp->t_idle));
-	assert(isnan(req->t_first));
-	assert(isnan(req->t_prev));
-	assert(isnan(req->t_req));
-
-	tmo = (int)(1e3 * cache_param->timeout_linger);
-	while (1) {
-		pfd[0].fd = sp->fd;
-		pfd[0].events = POLLIN;
-		pfd[0].revents = 0;
-		j = poll(pfd, 1, tmo);
-		assert(j >= 0);
-		now = VTIM_real();
-		if (j != 0)
-			hs = HTTP1_Rx(req->htc);
-		else
-			hs = HTTP1_Complete(req->htc);
-		if (hs == HTTP1_COMPLETE) {
-			/* Got it, run with it */
-			if (isnan(req->t_first))
-				req->t_first = now;
-			if (isnan(req->t_req))
-				req->t_req = now;
-			req->acct.req_hdrbytes +=
-			    req->htc->rxbuf_e - req->htc->rxbuf_b;
-			return (REQ_FSM_MORE);
-		} else if (hs == HTTP1_ERROR_EOF) {
-			why = SC_REM_CLOSE;
-			break;
-		} else if (hs == HTTP1_OVERFLOW) {
-			why = SC_RX_OVERFLOW;
-			break;
-		} else if (hs == HTTP1_ALL_WHITESPACE) {
-			/* Nothing but whitespace */
-			when = sp->t_idle + cache_param->timeout_idle;
-			if (when < now) {
-				why = SC_RX_TIMEOUT;
-				break;
-			}
-			when = sp->t_idle + cache_param->timeout_linger;
-			tmo = (int)(1e3 * (when - now));
-			if (when < now || tmo == 0) {
-				wrk->stats->sess_herd++;
-				SES_ReleaseReq(req);
-				WAIT_Enter(sp);
-				return (REQ_FSM_DONE);
-			}
-		} else {
-			/* Working on it */
-			if (isnan(req->t_first))
-				/* Record first byte received time stamp */
-				req->t_first = now;
-			when = sp->t_idle + cache_param->timeout_req;
-			tmo = (int)(1e3 * (when - now));
-			if (when < now || tmo == 0) {
-				why = SC_RX_TIMEOUT;
-				break;
-			}
-		}
-	}
-	req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b;
-	CNT_AcctLogCharge(wrk->stats, req);
-	SES_ReleaseReq(req);
-	assert(why != SC_NULL);
-	SES_Delete(sp, why, now);
-	return (REQ_FSM_DONE);
-}
-
-/*----------------------------------------------------------------------
- * This is the final state, figure out if we should close or recycle
- * the client connection
- */
-
-enum http1_cleanup_ret {
-	SESS_DONE_RET_GONE,
-	SESS_DONE_RET_WAIT,
-	SESS_DONE_RET_START,
-};
-
-static enum http1_cleanup_ret
-http1_cleanup(struct sess *sp, struct worker *wrk, struct req *req)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CHECK_OBJ_ORNULL(req->vcl, VCL_CONF_MAGIC);
-
-	req->director_hint = NULL;
-	req->restarts = 0;
-
-	AZ(req->esi_level);
-
-	if (req->vcl != NULL) {
-		if (wrk->vcl != NULL)
-			VCL_Rel(&wrk->vcl);
-		wrk->vcl = req->vcl;
-		req->vcl = NULL;
-	}
-
-	VRTPRIV_dynamic_kill(sp, (uintptr_t)req);
-	/* Charge and log byte counters */
-	AN(req->vsl->wid);
-	CNT_AcctLogCharge(wrk->stats, req);
-	req->req_bodybytes = 0;
-	req->resp_hdrbytes = 0;
-	req->resp_bodybytes = 0;
-
-	VSL_End(req->vsl);
-
-	if (!isnan(req->t_prev) && req->t_prev > 0.)
-		sp->t_idle = req->t_prev;
-	else
-		sp->t_idle = W_TIM_real(wrk);
-
-	req->t_first = NAN;
-	req->t_prev = NAN;
-	req->t_req = NAN;
-	req->req_body_status = REQ_BODY_INIT;
-
-	req->hash_always_miss = 0;
-	req->hash_ignore_busy = 0;
-
-	if (sp->fd >= 0 && req->doclose != SC_NULL)
-		SES_Close(sp, req->doclose);
-
-	if (sp->fd < 0) {
-		wrk->stats->sess_closed++;
-		AZ(req->vcl);
-		SES_ReleaseReq(req);
-		SES_Delete(sp, SC_NULL, NAN);
-		return (SESS_DONE_RET_GONE);
-	}
-
-	WS_Reset(req->ws, NULL);
-	WS_Reset(wrk->aws, NULL);
-
-	if (HTTP1_Reinit(req->htc) == HTTP1_COMPLETE) {
-		AZ(req->vsl->wid);
-		req->t_first = req->t_req = sp->t_idle;
-		wrk->stats->sess_pipeline++;
-		req->acct.req_hdrbytes +=
-		    req->htc->rxbuf_e - req->htc->rxbuf_b;
-		return (SESS_DONE_RET_START);
-	} else {
-		if (req->htc->rxbuf_e != req->htc->rxbuf_b)
-			wrk->stats->sess_readahead++;
-		return (SESS_DONE_RET_WAIT);
-	}
-}
-
-/*----------------------------------------------------------------------
- */
-
-static enum req_fsm_nxt
-http1_dissect(struct worker *wrk, struct req *req)
-{
-	const char *r_100 = "HTTP/1.1 100 Continue\r\n\r\n";
-	const char *r_400 = "HTTP/1.1 400 Bad Request\r\n\r\n";
-	const char *r_417 = "HTTP/1.1 417 Expectation Failed\r\n\r\n";
-	const char *p;
-	ssize_t r;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-
-	/* Allocate a new vxid now that we know we'll need it. */
-	AZ(req->vsl->wid);
-	req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
-
-	VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid));
-	VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid));
-	AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
-	AZ(isnan(req->t_req));	 /* Complete req rcvd set by http1_wait */
-	req->t_prev = req->t_first;
-	VSLb_ts_req(req, "Start", req->t_first);
-	VSLb_ts_req(req, "Req", req->t_req);
-
-	/* Borrow VCL reference from worker thread */
-	VCL_Refresh(&wrk->vcl);
-	req->vcl = wrk->vcl;
-	wrk->vcl = NULL;
-
-	HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
-	req->err_code = HTTP1_DissectRequest(req->htc, req->http);
-
-	/* If we could not even parse the request, just close */
-	if (req->err_code != 0) {
-		VSLb(req->vsl, SLT_HttpGarbage, "%.*s",
-		    (int)(req->htc->rxbuf_e - req->htc->rxbuf_b),
-		    req->htc->rxbuf_b);
-		wrk->stats->client_req_400++;
-		r = write(req->sp->fd, r_400, strlen(r_400));
-		if (r > 0)
-			req->acct.resp_hdrbytes += r;
-		SES_Close(req->sp, SC_RX_JUNK);
-		return (REQ_FSM_DONE);
-	}
-
-	assert (req->req_body_status == REQ_BODY_INIT);
-
-	if (req->htc->body_status == BS_CHUNKED) {
-		req->req_body_status = REQ_BODY_WITHOUT_LEN;
-	} else if (req->htc->body_status == BS_LENGTH) {
-		req->req_body_status = REQ_BODY_WITH_LEN;
-	} else if (req->htc->body_status == BS_NONE) {
-		req->req_body_status = REQ_BODY_NONE;
-	} else if (req->htc->body_status == BS_EOF) {
-		req->req_body_status = REQ_BODY_WITHOUT_LEN;
-	} else {
-		WRONG("Unknown req.body_length situation");
-	}
-
-	if (http_GetHdr(req->http, H_Expect, &p)) {
-		if (strcasecmp(p, "100-continue")) {
-			wrk->stats->client_req_417++;
-			req->err_code = 417;
-			r = write(req->sp->fd, r_417, strlen(r_417));
-			if (r > 0)
-				req->resp_hdrbytes += r;
-			SES_Close(req->sp, SC_RX_JUNK);
-			return (REQ_FSM_DONE);
-		}
-		r = write(req->sp->fd, r_100, strlen(r_100));
-		if (r > 0)
-			req->acct.resp_hdrbytes += r;
-		if (r != strlen(r_100)) {
-			SES_Close(req->sp, SC_REM_CLOSE);
-			return (REQ_FSM_DONE);
-		}
-		http_Unset(req->http, H_Expect);
-	}
-
-	wrk->stats->client_req++;
-	wrk->stats->s_req++;
-
-	AZ(req->err_code);
-	req->ws_req = WS_Snapshot(req->ws);
-
-	assert(req->req_body_status != REQ_BODY_INIT);
-
-	HTTP_Copy(req->http0, req->http);	// For ESI & restart
-
-	return (REQ_FSM_MORE);
-}
-
-/*----------------------------------------------------------------------
- */
-
-void
-HTTP1_Session(struct worker *wrk, struct req *req)
-{
-	enum req_fsm_nxt nxt = REQ_FSM_MORE;
-	struct sess *sp;
-	enum http1_cleanup_ret sdr;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	sp = req->sp;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	/*
-	 * Whenever we come in from the acceptor or waiter, we need to set
-	 * blocking mode.  It would be simpler to do this in the acceptor
-	 * or waiter, but we'd rather do the syscall in the worker thread.
-	 * On systems which return errors for ioctl, we close early
-	 */
-	if (sp->sess_step == S_STP_NEWREQ && VTCP_blocking(sp->fd)) {
-		if (errno == ECONNRESET)
-			SES_Close(sp, SC_REM_CLOSE);
-		else
-			SES_Close(sp, SC_TX_ERROR);
-		sdr = http1_cleanup(sp, wrk, req);
-		assert(sdr == SESS_DONE_RET_GONE);
-		return;
-	}
-
-	/*
-	 * Return from waitinglist. Check to see if the remote has left.
-	 */
-	if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) {
-		AN(req->hash_objhead);
-		(void)HSH_DerefObjHead(wrk, &req->hash_objhead);
-		AZ(req->hash_objhead);
-		SES_Close(sp, SC_REM_CLOSE);
-		sdr = http1_cleanup(sp, wrk, req);
-		assert(sdr == SESS_DONE_RET_GONE);
-		return;
-	}
-
-	if (sp->sess_step == S_STP_NEWREQ) {
-		HTTP1_Init(req->htc, req->ws, sp->fd, req->vsl,
-		    cache_param->http_req_size,
-		    cache_param->http_req_hdr_len);
-	}
-
-	while (1) {
-		assert(
-		    sp->sess_step == S_STP_NEWREQ ||
-		    req->req_step == R_STP_LOOKUP ||
-		    req->req_step == R_STP_RECV);
-
-		if (sp->sess_step == S_STP_WORKING) {
-			if (req->req_step == R_STP_RECV)
-				nxt = http1_dissect(wrk, req);
-			if (nxt == REQ_FSM_MORE)
-				nxt = CNT_Request(wrk, req);
-			if (nxt == REQ_FSM_DISEMBARK)
-				return;
-			assert(nxt == REQ_FSM_DONE);
-			sdr = http1_cleanup(sp, wrk, req);
-			switch (sdr) {
-			case SESS_DONE_RET_GONE:
-				return;
-			case SESS_DONE_RET_WAIT:
-				sp->sess_step = S_STP_NEWREQ;
-				break;
-			case SESS_DONE_RET_START:
-				sp->sess_step = S_STP_WORKING;
-				req->req_step = R_STP_RECV;
-				break;
-			default:
-				WRONG("Illegal enum http1_cleanup_ret");
-			}
-		}
-
-		if (sp->sess_step == S_STP_NEWREQ) {
-			nxt = http1_wait(sp, wrk, req);
-			if (nxt != REQ_FSM_MORE)
-				return;
-			sp->sess_step = S_STP_WORKING;
-			req->req_step = R_STP_RECV;
-		}
-	}
-}
diff --git a/bin/varnishd/cache/cache_http1_pipe.c b/bin/varnishd/cache/cache_http1_pipe.c
deleted file mode 100644
index 6a67566..0000000
--- a/bin/varnishd/cache/cache_http1_pipe.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2011 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * XXX: charge bytes to srcaddr
- */
-
-#include "config.h"
-
-#include <poll.h>
-#include <stdio.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "cache_director.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-static struct lock pipestat_mtx;
-
-struct acct_pipe {
-	uint64_t	req;
-	uint64_t	bereq;
-	uint64_t	in;
-	uint64_t	out;
-};
-
-static int
-rdf(int fd0, int fd1, uint64_t *pcnt)
-{
-	int i, j;
-	char buf[BUFSIZ], *p;
-
-	i = read(fd0, buf, sizeof buf);
-	if (i <= 0)
-		return (1);
-	for (p = buf; i > 0; i -= j, p += j) {
-		j = write(fd1, p, i);
-		if (j <= 0)
-			return (1);
-		*pcnt += j;
-		if (i != j)
-			(void)usleep(100000);		/* XXX hack */
-	}
-	return (0);
-}
-
-static void
-pipecharge(struct req *req, const struct acct_pipe *a, struct VSC_C_vbe *b)
-{
-
-	VSLb(req->vsl, SLT_PipeAcct, "%ju %ju %ju %ju",
-	    (uintmax_t)a->req,
-	    (uintmax_t)a->bereq,
-	    (uintmax_t)a->in,
-	    (uintmax_t)a->out);
-
-	Lck_Lock(&pipestat_mtx);
-	VSC_C_main->s_pipe_hdrbytes += a->req;
-	VSC_C_main->s_pipe_in += a->in;
-	VSC_C_main->s_pipe_out += a->out;
-	if (b != NULL) {
-		b->pipe_hdrbytes += a->bereq;
-		b->pipe_out += a->in;
-		b->pipe_in += a->out;
-	}
-	Lck_Unlock(&pipestat_mtx);
-}
-
-void
-V1P_Process(struct req *req, struct busyobj *bo)
-{
-	struct vbc *vc;
-	struct worker *wrk;
-	struct pollfd fds[2];
-	int i;
-	struct acct_pipe acct_pipe;
-	ssize_t hdrbytes;
-
-	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
-	CHECK_OBJ_NOTNULL(req->sp, SESS_MAGIC);
-	wrk = req->wrk;
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
-
-	req->res_mode = RES_PIPE;
-
-	memset(&acct_pipe, 0, sizeof acct_pipe);
-	acct_pipe.req = req->acct.req_hdrbytes;
-	req->acct.req_hdrbytes = 0;
-
-	vc = VDI_GetFd(bo->director_req, wrk, bo);
-	if (vc == NULL) {
-		pipecharge(req, &acct_pipe, NULL);
-		SES_Close(req->sp, SC_OVERLOAD);
-		return;
-	}
-	bo->vbc = vc;		/* For panic dumping */
-	(void)VTCP_blocking(vc->fd);
-
-	WRW_Reserve(wrk, &vc->fd, bo->vsl, req->t_req);
-	hdrbytes = HTTP1_Write(wrk, bo->bereq, HTTP1_Req);
-
-	if (req->htc->pipeline_b != NULL)
-		(void)WRW_Write(wrk, req->htc->pipeline_b,
-		    req->htc->pipeline_e - req->htc->pipeline_b);
-
-	i = WRW_FlushRelease(wrk, &acct_pipe.bereq);
-	if (acct_pipe.bereq > hdrbytes) {
-		acct_pipe.in = acct_pipe.bereq - hdrbytes;
-		acct_pipe.bereq = hdrbytes;
-	}
-
-	VSLb_ts_req(req, "Pipe", W_TIM_real(wrk));
-
-	if (i) {
-		pipecharge(req, &acct_pipe, vc->backend->vsc);
-		SES_Close(req->sp, SC_TX_PIPE);
-		VBE_CloseFd(&vc, NULL);
-		return;
-	}
-
-	memset(fds, 0, sizeof fds);
-
-	// XXX: not yet (void)VTCP_linger(vc->fd, 0);
-	fds[0].fd = vc->fd;
-	fds[0].events = POLLIN | POLLERR;
-
-	// XXX: not yet (void)VTCP_linger(req->sp->fd, 0);
-	fds[1].fd = req->sp->fd;
-	fds[1].events = POLLIN | POLLERR;
-
-	while (fds[0].fd > -1 || fds[1].fd > -1) {
-		fds[0].revents = 0;
-		fds[1].revents = 0;
-		i = poll(fds, 2, (int)(cache_param->pipe_timeout * 1e3));
-		if (i < 1)
-			break;
-		if (fds[0].revents &&
-		    rdf(vc->fd, req->sp->fd, &acct_pipe.out)) {
-			if (fds[1].fd == -1)
-				break;
-			(void)shutdown(vc->fd, SHUT_RD);
-			(void)shutdown(req->sp->fd, SHUT_WR);
-			fds[0].events = 0;
-			fds[0].fd = -1;
-		}
-		if (fds[1].revents &&
-		    rdf(req->sp->fd, vc->fd, &acct_pipe.in)) {
-			if (fds[0].fd == -1)
-				break;
-			(void)shutdown(req->sp->fd, SHUT_RD);
-			(void)shutdown(vc->fd, SHUT_WR);
-			fds[1].events = 0;
-			fds[1].fd = -1;
-		}
-	}
-	VSLb_ts_req(req, "PipeSess", W_TIM_real(wrk));
-	pipecharge(req, &acct_pipe, vc->backend->vsc);
-	SES_Close(req->sp, SC_TX_PIPE);
-	VBE_CloseFd(&vc, NULL);
-	bo->vbc = NULL;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-V1P_Init(void)
-{
-
-	Lck_New(&pipestat_mtx, lck_pipestat);
-}
diff --git a/bin/varnishd/cache/cache_http1_proto.c b/bin/varnishd/cache/cache_http1_proto.c
deleted file mode 100644
index ca0a4d1..0000000
--- a/bin/varnishd/cache/cache_http1_proto.c
+++ /dev/null
@@ -1,576 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2014 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * HTTP protocol requests
- *
- * The trouble with the "until magic sequence" design of HTTP protocol messages
- * is that either you have to read a single character at a time, which is
- * inefficient, or you risk reading too much, and pre-read some of the object,
- * or even the next pipelined request, which follows the one you want.
- *
- * HTC reads a HTTP protocol header into a workspace, subject to limits,
- * and stops when we see the magic marker (double [CR]NL), and if we overshoot,
- * it keeps track of the "pipelined" data.
- *
- * Until we see the magic marker, we have to keep the rxbuf NUL terminated
- * because we use strchr(3) on it.
- *
- * We use this both for client and backend connections.
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "vct.h"
-
-const int HTTP1_Req[3] = {
-	HTTP_HDR_METHOD, HTTP_HDR_URL, HTTP_HDR_PROTO
-};
-
-const int HTTP1_Resp[3] = {
-	HTTP_HDR_PROTO, HTTP_HDR_STATUS, HTTP_HDR_REASON
-};
-
-/*--------------------------------------------------------------------*/
-
-void
-HTTP1_Init(struct http_conn *htc, struct ws *ws, int fd, struct vsl_log *vsl,
-    unsigned maxbytes, unsigned maxhdr)
-{
-
-	htc->magic = HTTP_CONN_MAGIC;
-	htc->ws = ws;
-	htc->fd = fd;
-	htc->vsl = vsl;
-	htc->maxbytes = maxbytes;
-	htc->maxhdr = maxhdr;
-
-	(void)WS_Reserve(htc->ws, htc->maxbytes);
-	htc->rxbuf_b = ws->f;
-	htc->rxbuf_e = ws->f;
-	*htc->rxbuf_e = '\0';
-	htc->pipeline_b = NULL;
-	htc->pipeline_e = NULL;
-}
-
-/*--------------------------------------------------------------------
- * Start over, and recycle any pipelined input.
- * The WS_Reset is safe, even though the pipelined input is stored in
- * the ws somewhere, because WS_Reset only fiddles pointers.
- */
-
-enum http1_status_e
-HTTP1_Reinit(struct http_conn *htc)
-{
-	ssize_t l;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	(void)WS_Reserve(htc->ws, htc->maxbytes);
-	htc->rxbuf_b = htc->ws->f;
-	htc->rxbuf_e = htc->ws->f;
-	if (htc->pipeline_b != NULL) {
-		l = htc->pipeline_e - htc->pipeline_b;
-		assert(l > 0);
-		memmove(htc->rxbuf_b, htc->pipeline_b, l);
-		htc->rxbuf_e += l;
-		htc->pipeline_b = NULL;
-		htc->pipeline_e = NULL;
-	}
-	*htc->rxbuf_e = '\0';
-	return (HTTP1_Complete(htc));
-}
-
-/*--------------------------------------------------------------------
- * Check if we have a complete HTTP request or response yet
- */
-
-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);
-}
-
-/*--------------------------------------------------------------------
- * Receive more HTTP protocol bytes
- */
-
-enum http1_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));
-}
-
-/*--------------------------------------------------------------------
- * Read up to len bytes, returning pipelined data first.
- */
-
-ssize_t
-HTTP1_Read(struct http_conn *htc, void *d, size_t len)
-{
-	ssize_t l;
-	unsigned char *p;
-	ssize_t i = 0;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	l = 0;
-	p = d;
-	if (htc->pipeline_b) {
-		l = htc->pipeline_e - htc->pipeline_b;
-		assert(l > 0);
-		if (l > len)
-			l = len;
-		memcpy(p, htc->pipeline_b, l);
-		p += l;
-		len -= l;
-		htc->pipeline_b += l;
-		if (htc->pipeline_b == htc->pipeline_e)
-			htc->pipeline_b = htc->pipeline_e = NULL;
-	}
-	if (len > 0) {
-		i = read(htc->fd, p, len);
-		if (i < 0) {
-			VSLb(htc->vsl, SLT_FetchError, "%s", strerror(errno));
-			return (i);
-		}
-	}
-	return (i + l);
-}
-
-/*--------------------------------------------------------------------
- * Dissect the headers of the HTTP protocol message.
- * Detect conditionals (headers which start with '^[Ii][Ff]-')
- */
-
-static uint16_t
-http1_dissect_hdrs(struct http *hp, char *p, const struct http_conn *htc)
-{
-	char *q, *r;
-
-	assert(p > htc->rxbuf_b);
-	assert(p <= htc->rxbuf_e);
-	hp->nhd = HTTP_HDR_FIRST;
-	hp->conds = 0;
-	r = NULL;		/* For FlexeLint */
-	for (; p < htc->rxbuf_e; p = r) {
-
-		/* Find end of next header */
-		q = r = p;
-		while (r < htc->rxbuf_e) {
-			if (!vct_iscrlf(r)) {
-				r++;
-				continue;
-			}
-			q = r;
-			assert(r < htc->rxbuf_e);
-			r += vct_skipcrlf(r);
-			if (r >= htc->rxbuf_e)
-				break;
-			/* If line does not continue: got it. */
-			if (!vct_issp(*r))
-				break;
-
-			/* Clear line continuation LWS to spaces */
-			while (vct_islws(*q))
-				*q++ = ' ';
-		}
-
-		if (q - p > htc->maxhdr) {
-			VSLb(hp->vsl, SLT_BogoHeader, "Header too long: %.*s",
-			    (int)(q - p > 20 ? 20 : q - p), p);
-			return (400);
-		}
-
-		/* Empty header = end of headers */
-		if (p == q)
-			break;
-
-		if (vct_islws(*p)) {
-			VSLb(hp->vsl, SLT_BogoHeader,
-			    "1st header has white space: %.*s",
-			    (int)(q - p > 20 ? 20 : q - p), p);
-			return (400);
-		}
-
-		if ((p[0] == 'i' || p[0] == 'I') &&
-		    (p[1] == 'f' || p[1] == 'F') &&
-		    p[2] == '-')
-			hp->conds = 1;
-
-		while (q > p && vct_issp(q[-1]))
-			q--;
-		*q = '\0';
-
-		if (strchr(p, ':') == NULL) {
-			VSLb(hp->vsl, SLT_BogoHeader, "Header without ':' %.*s",
-			    (int)(q - p > 20 ? 20 : q - p), p);
-			return (400);
-		}
-
-		if (hp->nhd < hp->shd) {
-			hp->hdf[hp->nhd] = 0;
-			hp->hd[hp->nhd].b = p;
-			hp->hd[hp->nhd].e = q;
-			hp->nhd++;
-		} else {
-			VSLb(hp->vsl, SLT_BogoHeader, "Too many headers: %.*s",
-			    (int)(q - p > 20 ? 20 : q - p), p);
-			return (400);
-		}
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Deal with first line of HTTP protocol message.
- */
-
-static uint16_t
-http1_splitline(struct http *hp, const struct http_conn *htc, const int *hf)
-{
-	char *p;
-	int i;
-
-	assert(hf == HTTP1_Req || hf == HTTP1_Resp);
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	assert(htc->rxbuf_e >= htc->rxbuf_b);
-
-	AZ(hp->hd[hf[0]].b);
-	AZ(hp->hd[hf[1]].b);
-	AZ(hp->hd[hf[2]].b);
-
-	/* Skip leading LWS */
-	for (p = htc->rxbuf_b ; vct_islws(*p); p++)
-		continue;
-	hp->hd[hf[0]].b = p;
-
-	/* First field cannot contain SP or CTL */
-	for (; !vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[hf[0]].e = p;
-	assert(Tlen(hp->hd[hf[0]]));
-	*p++ = '\0';
-
-	/* Skip SP */
-	for (; vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[hf[1]].b = p;
-
-	/* Second field cannot contain LWS or CTL */
-	for (; !vct_islws(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[hf[1]].e = p;
-	if (!Tlen(hp->hd[hf[1]]))
-		return (400);
-	*p++ = '\0';
-
-	/* Skip SP */
-	for (; vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[hf[2]].b = p;
-
-	/* Third field is optional and cannot contain CTL except TAB */
-	for (; !vct_iscrlf(p); p++) {
-		if (vct_isctl(*p) && !vct_issp(*p)) {
-			hp->hd[hf[2]].b = NULL;
-			return (400);
-		}
-	}
-	hp->hd[hf[2]].e = p;
-
-	/* Skip CRLF */
-	i = vct_skipcrlf(p);
-	*p = '\0';
-	p += i;
-
-	return (http1_dissect_hdrs(hp, p, htc));
-}
-
-/*--------------------------------------------------------------------*/
-
-static enum body_status
-http1_body_status(const struct http *hp, struct http_conn *htc)
-{
-	ssize_t cl;
-	const char *b;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-
-	htc->content_length = -1;
-
-	if (http_HdrIs(hp, H_Transfer_Encoding, "chunked"))
-		return (BS_CHUNKED);
-
-	if (http_GetHdr(hp, H_Transfer_Encoding, &b))
-		return (BS_ERROR);
-
-	cl = http_GetContentLength(hp);
-	if (cl == -2)
-		return (BS_ERROR);
-	if (cl >= 0) {
-		htc->content_length = cl;
-		return (cl == 0 ? BS_NONE : BS_LENGTH);
-	}
-
-	if (http_HdrIs(hp, H_Connection, "keep-alive")) {
-		/*
-		 * Keep alive with neither TE=Chunked or C-Len is impossible.
-		 * We assume a zero length body.
-		 */
-		return (BS_NONE);
-	}
-
-	if (http_HdrIs(hp, H_Connection, "close")) {
-		/*
-		 * In this case, it is safe to just read what comes.
-		 */
-		return (BS_EOF);
-	}
-
-	if (hp->protover < 11) {
-		/*
-		 * With no Connection header, assume EOF.
-		 */
-		return (BS_EOF);
-	}
-
-	/*
-	 * Fall back to EOF transfer.
-	 */
-	return (BS_EOF);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-http1_proto_ver(struct http *hp)
-{
-	if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.0"))
-		hp->protover = 10;
-	else if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.1"))
-		hp->protover = 11;
-	else
-		hp->protover = 9;
-}
-
-/*--------------------------------------------------------------------*/
-
-uint16_t
-HTTP1_DissectRequest(struct http_conn *htc, struct http *hp)
-{
-	uint16_t retval;
-	const char *p;
-	const char *b, *e;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-
-	retval = http1_splitline(hp, htc, HTTP1_Req);
-	if (retval != 0)
-		return (retval);
-	http1_proto_ver(hp);
-
-	if (http_CountHdr(hp, H_Host) > 1)
-		return (400);
-
-	if (http_CountHdr(hp, H_Content_Length) > 1)
-		return (400);
-
-	/* RFC2616, section 5.2, point 1 */
-	if (!strncasecmp(hp->hd[HTTP_HDR_URL].b, "http://", 7)) {
-		b = e = hp->hd[HTTP_HDR_URL].b + 7;
-		while (*e != '/' && *e != '\0')
-			e++;
-		if (*e == '/') {
-			http_Unset(hp, H_Host);
-			http_PrintfHeader(hp, "Host: %.*s", (int)(e - b), b);
-			hp->hd[HTTP_HDR_URL].b = e;
-		}
-	}
-
-	htc->body_status = http1_body_status(hp, htc);
-	if (htc->body_status == BS_ERROR)
-		return (400);
-
-	p = http_GetMethod(hp);
-	AN(p);
-
-	/* We handle EOF bodies only for PUT and POST */
-	if (htc->body_status == BS_EOF &&
-	    strcasecmp(p, "put") && strcasecmp(p, "post"))
-		htc->body_status = BS_NONE;
-
-	/* HEAD with a body is a hard error */
-	if (htc->body_status != BS_NONE && !strcasecmp(p, "head"))
-		return (400);
-
-	return (retval);
-}
-
-/*--------------------------------------------------------------------*/
-
-uint16_t
-HTTP1_DissectResponse(struct http *hp, struct http_conn *htc)
-{
-	uint16_t retval = 0;
-	const char *p;
-
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-
-	if (http1_splitline(hp, htc, HTTP1_Resp))
-		retval = 503;
-
-	if (retval == 0) {
-		http1_proto_ver(hp);
-		if (hp->protover != 10 && hp->protover != 11)
-			retval = 503;
-	}
-
-	if (retval == 0 && Tlen(hp->hd[HTTP_HDR_STATUS]) != 3)
-		retval = 503;
-
-	if (retval == 0) {
-		p = hp->hd[HTTP_HDR_STATUS].b;
-
-		if (p[0] >= '1' && p[0] <= '9' &&
-		    p[1] >= '0' && p[1] <= '9' &&
-		    p[2] >= '0' && p[2] <= '9')
-			hp->status =
-			    100 * (p[0] - '0') + 10 * (p[1] - '0') + p[2] - '0';
-		else
-			retval = 503;
-	}
-
-	if (retval != 0) {
-		VSLb(hp->vsl, SLT_HttpGarbage, "%.*s",
-		    (int)(htc->rxbuf_e - htc->rxbuf_b), htc->rxbuf_b);
-		assert(retval >= 100 && retval <= 999);
-		assert(retval == 503);
-		hp->status = retval;
-		http_SetH(hp, HTTP_HDR_STATUS, "503");
-		http_SetH(hp, HTTP_HDR_REASON, http_Status2Reason(retval));
-	}
-
-	if (hp->hd[HTTP_HDR_REASON].b == NULL ||
-	    !Tlen(hp->hd[HTTP_HDR_REASON]))
-		http_SetH(hp, HTTP_HDR_REASON, http_Status2Reason(hp->status));
-
-	htc->body_status = http1_body_status(hp, htc);
-
-	return (retval);
-}
-
-/*--------------------------------------------------------------------*/
-
-unsigned
-HTTP1_Write(const struct worker *w, const struct http *hp, const int *hf)
-{
-	unsigned u, l;
-
-	assert(hf == HTTP1_Req || hf == HTTP1_Resp);
-	AN(hp->hd[hf[0]].b);
-	AN(hp->hd[hf[1]].b);
-	AN(hp->hd[hf[2]].b);
-	l = WRW_WriteH(w, &hp->hd[hf[0]], " ");
-	l += WRW_WriteH(w, &hp->hd[hf[1]], " ");
-	l += WRW_WriteH(w, &hp->hd[hf[2]], "\r\n");
-
-	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++)
-		l += WRW_WriteH(w, &hp->hd[u], "\r\n");
-	l += WRW_Write(w, "\r\n", -1);
-	return (l);
-}
diff --git a/bin/varnishd/cache/cache_http1_vfp.c b/bin/varnishd/cache/cache_http1_vfp.c
deleted file mode 100644
index 33a48c5..0000000
--- a/bin/varnishd/cache/cache_http1_vfp.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2014 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * HTTP1 Fetch Filters
- *
- * These filters are used for both req.body and beresp.body to handle
- * the HTTP/1 aspects (C-L/Chunked/EOF)
- *
- */
-
-#include "config.h"
-
-#include <inttypes.h>
-
-#include "cache.h"
-
-#include "vct.h"
-
-/*--------------------------------------------------------------------
- * Read a chunked HTTP object.
- *
- * XXX: Reading one byte at a time is pretty pessimal.
- */
-
-static enum vfp_status __match_proto__(vfp_pull_f)
-v1f_pull_chunked(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr,
-    ssize_t *lp)
-{
-	struct http_conn *htc;
-	int i;
-	char buf[20];		/* XXX: 20 is arbitrary */
-	char *q;
-	unsigned u;
-	uintmax_t cll;
-	ssize_t cl, l, lr;
-
-	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
-	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
-	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
-	AN(ptr);
-	AN(lp);
-
-	AN(ptr);
-	AN(lp);
-	l = *lp;
-	*lp = 0;
-	if (vfe->priv2 == -1) {
-		/* Skip leading whitespace */
-		do {
-			lr = HTTP1_Read(htc, buf, 1);
-			if (lr <= 0)
-				return (VFP_Error(vc, "chunked read err"));
-		} while (vct_islws(buf[0]));
-
-		if (!vct_ishex(buf[0]))
-			 return (VFP_Error(vc, "chunked header non-hex"));
-
-		/* Collect hex digits, skipping leading zeros */
-		for (u = 1; u < sizeof buf; u++) {
-			do {
-				lr = HTTP1_Read(htc, buf + u, 1);
-				if (lr <= 0)
-					return (VFP_Error(vc,
-					    "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(vc, "chunked header too long"));
-
-		/* Skip trailing white space */
-		while(vct_islws(buf[u]) && buf[u] != '\n') {
-			lr = HTTP1_Read(htc, buf + u, 1);
-			if (lr <= 0)
-				return (VFP_Error(vc, "chunked read err"));
-		}
-
-		if (buf[u] != '\n')
-			return (VFP_Error(vc, "chunked header no NL"));
-
-		buf[u] = '\0';
-
-		cll = strtoumax(buf, &q, 16);
-		if (q == NULL || *q != '\0')
-			return (VFP_Error(vc, "chunked header number syntax"));
-		cl = (ssize_t)cll;
-		if((uintmax_t)cl != cll)
-			return (VFP_Error(vc, "bogusly large chunk size"));
-
-		vfe->priv2 = cl;
-	}
-	if (vfe->priv2 > 0) {
-		if (vfe->priv2 < l)
-			l = vfe->priv2;
-		lr = HTTP1_Read(htc, ptr, l);
-		if (lr <= 0)
-			return (VFP_Error(vc, "straight insufficient bytes"));
-		*lp = lr;
-		vfe->priv2 -= lr;
-		if (vfe->priv2 == 0)
-			vfe->priv2 = -1;
-		return (VFP_OK);
-	}
-	AZ(vfe->priv2);
-	i = HTTP1_Read(htc, buf, 1);
-	if (i <= 0)
-		return (VFP_Error(vc, "chunked read err"));
-	if (buf[0] == '\r' && HTTP1_Read(htc, buf, 1) <= 0)
-		return (VFP_Error(vc, "chunked read err"));
-	if (buf[0] != '\n')
-		return (VFP_Error(vc, "chunked tail no NL"));
-	return (VFP_END);
-}
-
-static const struct vfp v1f_chunked = {
-	.name = "V1F_CHUNKED",
-	.pull = v1f_pull_chunked,
-};
-
-
-/*--------------------------------------------------------------------*/
-
-static enum vfp_status __match_proto__(vfp_pull_f)
-v1f_pull_straight(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
-    ssize_t *lp)
-{
-	ssize_t l, lr;
-	struct http_conn *htc;
-
-	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
-	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
-	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
-	AN(p);
-	AN(lp);
-
-	l = *lp;
-	*lp = 0;
-
-	if (vfe->priv2 == 0) // XXX: Optimize Content-Len: 0 out earlier
-		return (VFP_END);
-	if (vfe->priv2 < l)
-		l = vfe->priv2;
-	lr = HTTP1_Read(htc, p, l);
-	if (lr <= 0)
-		return (VFP_Error(vc, "straight insufficient bytes"));
-	*lp = lr;
-	vfe->priv2 -= lr;
-	if (vfe->priv2 == 0)
-		return (VFP_END);
-	return (VFP_OK);
-}
-
-static const struct vfp v1f_straight = {
-	.name = "V1F_STRAIGHT",
-	.pull = v1f_pull_straight,
-};
-
-/*--------------------------------------------------------------------*/
-
-static enum vfp_status __match_proto__(vfp_pull_f)
-v1f_pull_eof(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
-    ssize_t *lp)
-{
-	ssize_t l, lr;
-	struct http_conn *htc;
-
-	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
-	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
-	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
-	AN(p);
-
-	AN(lp);
-
-	l = *lp;
-	*lp = 0;
-	lr = HTTP1_Read(htc, p, l);
-	if (lr < 0)
-		return (VFP_Error(vc, "eof socket fail"));
-	if (lr == 0)
-		return (VFP_END);
-	*lp = lr;
-	return (VFP_OK);
-}
-
-static const struct vfp v1f_eof = {
-	.name = "V1F_EOF",
-	.pull = v1f_pull_eof,
-};
-
-/*--------------------------------------------------------------------
- */
-
-void
-V1F_Setup_Fetch(struct vfp_ctx *vfc, struct http_conn *htc)
-{
-	struct vfp_entry *vfe;
-
-	CHECK_OBJ_NOTNULL(vfc, VFP_CTX_MAGIC);
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-
-	switch(htc->body_status) {
-	case BS_EOF:
-		assert(htc->content_length == -1);
-		vfe = VFP_Push(vfc, &v1f_eof, 0);
-		vfe->priv2 = 0;
-		break;
-	case BS_LENGTH:
-		assert(htc->content_length > 0);
-		vfe = VFP_Push(vfc, &v1f_straight, 0);
-		vfe->priv2 = htc->content_length;
-		break;
-	case BS_CHUNKED:
-		assert(htc->content_length == -1);
-		vfe = VFP_Push(vfc, &v1f_chunked, 0);
-		vfe->priv2 = -1;
-		break;
-	default:
-		WRONG("Wrong body_status");
-		break;
-	}
-	vfe->priv1 = htc;
-}
diff --git a/bin/varnishd/flint.sh b/bin/varnishd/flint.sh
index 4f471cf..7a44dad 100755
--- a/bin/varnishd/flint.sh
+++ b/bin/varnishd/flint.sh
@@ -23,6 +23,7 @@ flexelint \
 	storage/*.c \
 	waiter/*.c \
 	hash/*.c \
+	http1/*.c \
 	mgt/*.c \
 	../../lib/libvarnish/*.c \
 	../../lib/libvarnishcompat/execinfo.c \
diff --git a/bin/varnishd/http1/cache_http1_deliver.c b/bin/varnishd/http1/cache_http1_deliver.c
new file mode 100644
index 0000000..0e63c41
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_deliver.c
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2014 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "cache/cache.h"
+
+#include "vct.h"
+
+/*--------------------------------------------------------------------*/
+
+static int __match_proto__(vdp_bytes)
+v1d_bytes(struct req *req, enum vdp_action act, const void *ptr, ssize_t len)
+{
+	ssize_t wl = 0;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+	assert(req->vdp_nxt == -1);	/* always at the bottom of the pile */
+
+	if (len > 0)
+		wl = WRW_Write(req->wrk, ptr, len);
+	req->resp_bodybytes += wl;
+	if (act > VDP_NULL && WRW_Flush(req->wrk))
+		return (-1);
+	if (len != wl)
+		return (-1);
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+static int __match_proto__(vdp_bytes)
+v1d_range_bytes(struct req *req, enum vdp_action act, const void *ptr,
+    ssize_t len)
+{
+	int retval = 0;
+	ssize_t l;
+	const char *p = ptr;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	l = req->range_low - req->range_off;
+	if (l > 0) {
+		if (l > len)
+			l = len;
+		req->range_off += l;
+		p += l;
+		len -= l;
+	}
+	l = req->range_high - req->range_off;
+	if (l > len)
+		l = len;
+	if (l > 0)
+		retval = VDP_bytes(req, act, p, l);
+	else if (act > VDP_NULL)
+		retval = VDP_bytes(req, act, p, 0);
+	req->range_off += len;
+	return (retval);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+v1d_dorange(struct req *req, struct busyobj *bo, const char *r)
+{
+	ssize_t len, low, high, has_low;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
+	assert(http_IsStatus(req->resp, 200));
+
+	/* We must snapshot the length if we're streaming from the backend */
+	if (bo != NULL)
+		len = VBO_waitlen(req->wrk, bo, -1);
+	else
+		len = ObjGetLen(req->wrk, req->objcore);
+
+	if (strncmp(r, "bytes=", 6))
+		return;
+	r += 6;
+
+	/* The low end of range */
+	has_low = low = 0;
+	if (!vct_isdigit(*r) && *r != '-')
+		return;
+	while (vct_isdigit(*r)) {
+		has_low = 1;
+		low *= 10;
+		low += *r - '0';
+		r++;
+	}
+
+	if (low >= len)
+		return;
+
+	if (*r != '-')
+		return;
+	r++;
+
+	/* The high end of range */
+	if (vct_isdigit(*r)) {
+		high = 0;
+		while (vct_isdigit(*r)) {
+			high *= 10;
+			high += *r - '0';
+			r++;
+		}
+		if (!has_low) {
+			low = len - high;
+			if (low < 0)
+				low = 0;
+			high = len - 1;
+		}
+	} else
+		high = len - 1;
+	if (*r != '\0')
+		return;
+
+	if (high >= len)
+		high = len - 1;
+
+	if (low > high)
+		return;
+
+	http_PrintfHeader(req->resp, "Content-Range: bytes %jd-%jd/%jd",
+	    (intmax_t)low, (intmax_t)high, (intmax_t)len);
+	http_Unset(req->resp, H_Content_Length);
+	if (req->res_mode & RES_LEN)
+		http_PrintfHeader(req->resp, "Content-Length: %jd",
+		    (intmax_t)(1 + high - low));
+	http_PutResponse(req->resp, "HTTP/1.1", 206, NULL);
+
+	req->range_off = 0;
+	req->range_low = low;
+	req->range_high = high + 1;
+	VDP_push(req, v1d_range_bytes);
+}
+
+/*--------------------------------------------------------------------*/
+
+static enum objiter_status
+v1d_WriteDirObj(struct req *req)
+{
+	enum objiter_status ois;
+	ssize_t len;
+	void *oi;
+	void *ptr;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+	oi = ObjIterBegin(req->wrk, req->objcore);
+	XXXAN(oi);
+
+	do {
+		ois = ObjIter(req->objcore, oi, &ptr, &len);
+		switch(ois) {
+		case OIS_DONE:
+			AZ(len);
+			break;
+		case OIS_ERROR:
+			break;
+		case OIS_DATA:
+		case OIS_STREAM:
+			if (VDP_bytes(req,
+			     ois == OIS_DATA ? VDP_NULL : VDP_FLUSH,  ptr, len))
+				ois = OIS_ERROR;
+			break;
+		default:
+			WRONG("Wrong OIS value");
+		}
+	} while (ois == OIS_DATA || ois == OIS_STREAM);
+	(void)VDP_bytes(req, VDP_FINISH,  NULL, 0);
+	ObjIterEnd(req->objcore, &oi);
+	return (ois);
+}
+
+/*--------------------------------------------------------------------
+ * V1D_FlushReleaseAcct()
+ * Call WRW_FlushRelease on the worker and update the requests
+ * byte accounting with the number of bytes transmitted
+ *
+ * Returns the return value from WRW_FlushRelease()
+ */
+unsigned
+V1D_FlushReleaseAcct(struct req *req)
+{
+	unsigned u;
+	uint64_t txcnt = 0, hdrbytes;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->wrk, WORKER_MAGIC);
+	u = WRW_FlushRelease(req->wrk, &txcnt);
+	if (req->acct.resp_hdrbytes < req->resp_hdrbytes) {
+		hdrbytes = req->resp_hdrbytes - req->acct.resp_hdrbytes;
+		if (hdrbytes > txcnt)
+			hdrbytes = txcnt;
+	} else
+		hdrbytes = 0;
+	req->acct.resp_hdrbytes += hdrbytes;
+	req->acct.resp_bodybytes += txcnt - hdrbytes;
+	return (u);
+}
+
+void
+V1D_Deliver(struct req *req, struct busyobj *bo)
+{
+	const char *r;
+	enum objiter_status ois;
+	ssize_t l;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
+
+	req->res_mode = 0;
+
+	if (!req->disable_esi && ObjGetattr(req->wrk, req->objcore,
+	    OA_ESIDATA, NULL) != NULL) {
+		/* In ESI mode, we can't know the aggregate length */
+		req->res_mode &= ~RES_LEN;
+		req->res_mode |= RES_ESI;
+	} else if (http_IsStatus(req->resp, 304)) {
+		req->res_mode &= ~RES_LEN;
+		http_Unset(req->resp, H_Content_Length);
+		req->wantbody = 0;
+	} else if (bo == NULL) {
+		/* XXX: Not happy with this convoluted test */
+		req->res_mode |= RES_LEN;
+		if (!(req->objcore->flags & OC_F_PASS) ||
+		    ObjGetLen(req->wrk, req->objcore) != 0) {
+			http_Unset(req->resp, H_Content_Length);
+			http_PrintfHeader(req->resp,
+			    "Content-Length: %ju", (uintmax_t)ObjGetLen(
+			    req->wrk, req->objcore));
+		}
+	}
+
+	if (req->esi_level > 0) {
+		/* Included ESI object, always CHUNKED or EOF */
+		req->res_mode &= ~RES_LEN;
+		req->res_mode |= RES_ESI_CHILD;
+	}
+
+	if (cache_param->http_gzip_support &&
+	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED) &&
+	    !RFC2616_Req_Gzip(req->http)) {
+		/*
+		 * We don't know what it uncompresses to
+		 * XXX: we could cache that, but would still deliver
+		 * XXX: with multiple writes because of the gunzip buffer
+		 */
+		req->res_mode &= ~RES_LEN;
+		req->res_mode |= RES_GUNZIP;
+	}
+
+	if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
+		/* We havn't chosen yet, do so */
+		if (!req->wantbody) {
+			/* Nothing */
+		} else if (req->http->protover >= 11) {
+			req->res_mode |= RES_CHUNKED;
+		} else {
+			req->res_mode |= RES_EOF;
+			req->doclose = SC_TX_EOF;
+		}
+	}
+	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);
+
+	if (!(req->res_mode & RES_LEN))
+		http_Unset(req->resp, H_Content_Length);
+
+	if (req->res_mode & RES_GUNZIP)
+		http_Unset(req->resp, H_Content_Encoding);
+
+	if (req->res_mode & RES_CHUNKED)
+		http_SetHeader(req->resp, "Transfer-Encoding: chunked");
+
+	http_SetHeader(req->resp,
+	    req->doclose ? "Connection: close" : "Connection: keep-alive");
+
+	req->vdps[0] = v1d_bytes;
+	req->vdp_nxt = 0;
+
+	if (
+	    req->wantbody &&
+	    !(req->res_mode & (RES_ESI|RES_ESI_CHILD)) &&
+	    cache_param->http_range_support &&
+	    http_IsStatus(req->resp, 200)) {
+		http_SetHeader(req->resp, "Accept-Ranges: bytes");
+		if (http_GetHdr(req->http, H_Range, &r))
+			v1d_dorange(req, bo, r);
+	}
+
+	if (req->res_mode & RES_ESI)
+		RFC2616_Weaken_Etag(req->resp);
+
+	WRW_Reserve(req->wrk, &req->sp->fd, req->vsl, req->t_prev);
+
+	/*
+	 * Send HTTP protocol header, unless interior ESI object
+	 */
+	if (!(req->res_mode & RES_ESI_CHILD))
+		req->resp_hdrbytes +=
+		    HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
+
+	if (req->res_mode & RES_CHUNKED)
+		WRW_Chunked(req->wrk);
+
+	ois = OIS_DONE;
+	if (!req->wantbody) {
+		/* This was a HEAD or conditional request */
+	} else if (req->res_mode & RES_ESI) {
+		ESI_Deliver(req);
+	} else if (req->res_mode & RES_ESI_CHILD && req->gzip_resp) {
+		l = -1;
+		while (req->objcore->busyobj) {
+			assert(bo != NULL);
+			l = VBO_waitlen(req->wrk, bo, l);
+		}
+		ESI_DeliverChild(req);
+	} else if (req->res_mode & RES_GUNZIP ||
+	    (req->res_mode & RES_ESI_CHILD &&
+	    !req->gzip_resp &&
+	    ObjCheckFlag(req->wrk, req->objcore, OF_GZIPED))) {
+		VDP_push(req, VDP_gunzip);
+		req->vgz = VGZ_NewUngzip(req->vsl, "U D -");
+		AZ(VGZ_WrwInit(req->vgz));
+		ois = v1d_WriteDirObj(req);
+		(void)VGZ_Destroy(&req->vgz);
+		VDP_pop(req, VDP_gunzip);
+	} else {
+		ois = v1d_WriteDirObj(req);
+	}
+
+	if (ois == OIS_DONE &&
+	    (req->res_mode & RES_CHUNKED) &&
+	    !(req->res_mode & RES_ESI_CHILD))
+		WRW_EndChunk(req->wrk);
+
+	if ((V1D_FlushReleaseAcct(req) || ois != OIS_DONE) && req->sp->fd >= 0)
+		SES_Close(req->sp, SC_REM_CLOSE);
+}
+
+void
+V1D_Deliver_Synth(struct req *req)
+{
+	const char *r;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	AN(req->synth_body);
+
+	req->res_mode = 0;
+	if (http_IsStatus(req->resp, 304)) {
+		req->res_mode &= ~RES_LEN;
+		http_Unset(req->resp, H_Content_Length);
+		req->wantbody = 0;
+	} else {
+		req->res_mode |= RES_LEN;
+		http_Unset(req->resp, H_Content_Length);
+		http_PrintfHeader(req->resp, "Content-Length: %zd",
+		    VSB_len(req->synth_body));
+	}
+
+	if (req->esi_level > 0) {
+		/* Included ESI object, always CHUNKED or EOF */
+		req->res_mode &= ~RES_LEN;
+		req->res_mode |= RES_ESI_CHILD;
+	}
+
+	if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
+		/* We havn't chosen yet, do so */
+		if (!req->wantbody) {
+			/* Nothing */
+		} else if (req->http->protover >= 11) {
+			req->res_mode |= RES_CHUNKED;
+		} else {
+			req->res_mode |= RES_EOF;
+			req->doclose = SC_TX_EOF;
+		}
+	}
+	VSLb(req->vsl, SLT_Debug, "RES_MODE %x", req->res_mode);
+
+	if (!(req->res_mode & RES_LEN))
+		http_Unset(req->resp, H_Content_Length);
+
+	if (req->res_mode & RES_GUNZIP)
+		http_Unset(req->resp, H_Content_Encoding);
+
+	if (req->res_mode & RES_CHUNKED)
+		http_SetHeader(req->resp, "Transfer-Encoding: chunked");
+
+	http_SetHeader(req->resp,
+	    req->doclose ? "Connection: close" : "Connection: keep-alive");
+
+	req->vdps[0] = v1d_bytes;
+	req->vdp_nxt = 0;
+
+	if (
+	    req->wantbody &&
+	    !(req->res_mode & RES_ESI_CHILD) &&
+	    cache_param->http_range_support &&
+	    http_IsStatus(req->resp, 200)) {
+		http_SetHeader(req->resp, "Accept-Ranges: bytes");
+		if (http_GetHdr(req->http, H_Range, &r))
+			v1d_dorange(req, NULL, r);
+	}
+
+	WRW_Reserve(req->wrk, &req->sp->fd, req->vsl, req->t_prev);
+
+	/*
+	 * Send HTTP protocol header, unless interior ESI object
+	 */
+	if (!(req->res_mode & RES_ESI_CHILD))
+		req->resp_hdrbytes +=
+		    HTTP1_Write(req->wrk, req->resp, HTTP1_Resp);
+
+	if (req->res_mode & RES_CHUNKED)
+		WRW_Chunked(req->wrk);
+
+	if (!req->wantbody) {
+		/* This was a HEAD or conditional request */
+#if 0
+	XXX: Missing pretend GZIP for esi-children
+	} else if (req->res_mode & RES_ESI_CHILD && req->gzip_resp) {
+		ESI_DeliverChild(req);
+#endif
+	} else {
+		(void)VDP_bytes(req, VDP_FLUSH, VSB_data(req->synth_body),
+		    VSB_len(req->synth_body));
+		(void)VDP_bytes(req, VDP_FINISH,  NULL, 0);
+	}
+
+	if (req->res_mode & RES_CHUNKED && !(req->res_mode & RES_ESI_CHILD))
+		WRW_EndChunk(req->wrk);
+
+	if (V1D_FlushReleaseAcct(req) && req->sp->fd >= 0)
+		SES_Close(req->sp, SC_REM_CLOSE);
+}
diff --git a/bin/varnishd/http1/cache_http1_fetch.c b/bin/varnishd/http1/cache_http1_fetch.c
new file mode 100644
index 0000000..6793739
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_fetch.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2014 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache/cache.h"
+
+#include "hash/hash_slinger.h"
+
+#include "cache/cache_backend.h"
+#include "cache/cache_director.h"
+#include "vcli_priv.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+/*--------------------------------------------------------------------
+ * Pass the request body to the backend
+ */
+
+static int __match_proto__(req_body_iter_f)
+vbf_iter_req_body(struct req *req, void *priv, void *ptr, size_t l)
+{
+	struct worker *wrk;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CAST_OBJ_NOTNULL(wrk, priv, WORKER_MAGIC);
+
+	if (l > 0) {
+		(void)WRW_Write(wrk, ptr, l);
+		if (WRW_Flush(wrk))
+			return (-1);
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Send request, and receive the HTTP protocol response, but not the
+ * response body.
+ *
+ * Return value:
+ *	-1 failure, not retryable
+ *	 0 success
+ *	 1 failure which can be retried.
+ */
+
+int
+V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo)
+{
+	struct vbc *vc;
+	struct http *hp;
+	enum http1_status_e hs;
+	int retry = -1;
+	int i, j, first;
+	struct http_conn *htc;
+	ssize_t hdrbytes;
+	int do_chunked = 0;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
+	CHECK_OBJ_NOTNULL(bo->vbc, VBC_MAGIC);
+	CHECK_OBJ_ORNULL(bo->req, REQ_MAGIC);
+
+	htc = bo->htc;
+	hp = bo->bereq;
+	vc = bo->vbc;
+	if (vc->recycled)
+		retry = 1;
+
+	/*
+	 * Now that we know our backend, we can set a default Host:
+	 * header if one is necessary.  This cannot be done in the VCL
+	 * because the backend may be chosen by a director.
+	 */
+	if (!http_GetHdr(bo->bereq, H_Host, NULL))
+		VDI_AddHostHeader(bo->bereq, vc);
+
+	if (bo->req != NULL &&
+	    bo->req->req_body_status == REQ_BODY_WITHOUT_LEN) {
+		http_PrintfHeader(hp, "Transfer-Encoding: chunked");
+		do_chunked = 1;
+	}
+
+	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
+	WRW_Reserve(wrk, &vc->fd, bo->vsl, bo->t_prev);
+	hdrbytes = HTTP1_Write(wrk, hp, HTTP1_Req);
+
+	/* Deal with any message-body the request might (still) have */
+	i = 0;
+
+	if (bo->req != NULL) {
+		if (do_chunked)
+			WRW_Chunked(wrk);
+		i = VRB_Iterate(bo->req, vbf_iter_req_body, wrk);
+
+		if (bo->req->req_body_status == REQ_BODY_TAKEN) {
+			retry = -1;
+		} else if (bo->req->req_body_status == REQ_BODY_FAIL) {
+			VSLb(bo->vsl, SLT_FetchError,
+			    "req.body read error: %d (%s)",
+			    errno, strerror(errno));
+			bo->req->doclose = SC_RX_BODY;
+			retry = -1;
+		}
+		if (do_chunked)
+			WRW_EndChunk(wrk);
+	}
+
+	j = WRW_FlushRelease(wrk, &bo->acct.bereq_hdrbytes);
+	if (bo->acct.bereq_hdrbytes > hdrbytes) {
+		bo->acct.bereq_bodybytes = bo->acct.bereq_hdrbytes - hdrbytes;
+		bo->acct.bereq_hdrbytes = hdrbytes;
+	}
+	if (j != 0 || i != 0) {
+		VSLb(bo->vsl, SLT_FetchError, "backend write error: %d (%s)",
+		    errno, strerror(errno));
+		VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
+		VBE_CloseFd(&bo->vbc, &bo->acct);
+		/* XXX: other cleanup ? */
+		return (retry);
+	}
+	VSLb_ts_busyobj(bo, "Bereq", W_TIM_real(wrk));
+
+	VSC_C_main->backend_req++;
+
+	/* Receive response */
+
+	HTTP1_Init(htc, bo->ws, vc->fd, bo->vsl,
+	    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(vc->fd, vc->first_byte_timeout);
+
+	first = 1;
+	do {
+		hs = HTTP1_Rx(htc);
+		if (hs == HTTP1_OVERFLOW) {
+			bo->acct.beresp_hdrbytes +=
+			    htc->rxbuf_e - htc->rxbuf_b;
+			VSLb(bo->vsl, SLT_FetchError,
+			    "http %sread error: overflow",
+			    first ? "first " : "");
+			VBE_CloseFd(&bo->vbc, &bo->acct);
+			/* XXX: other cleanup ? */
+			return (-1);
+		}
+		if (hs == HTTP1_ERROR_EOF) {
+			bo->acct.beresp_hdrbytes +=
+			    htc->rxbuf_e - htc->rxbuf_b;
+			VSLb(bo->vsl, SLT_FetchError, "http %sread error: EOF",
+			    first ? "first " : "");
+			VBE_CloseFd(&bo->vbc, &bo->acct);
+			/* XXX: other cleanup ? */
+			return (retry);
+		}
+		if (first) {
+			retry = -1;
+			first = 0;
+			VTCP_set_read_timeout(vc->fd,
+			    vc->between_bytes_timeout);
+		}
+	} while (hs != HTTP1_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");
+		VBE_CloseFd(&bo->vbc, &bo->acct);
+		/* XXX: other cleanup ? */
+		return (-1);
+	}
+
+	bo->doclose = http_DoConnection(hp);
+
+	return (0);
+}
diff --git a/bin/varnishd/http1/cache_http1_fsm.c b/bin/varnishd/http1/cache_http1_fsm.c
new file mode 100644
index 0000000..dc586cc
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_fsm.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2014 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file contains the two central state machine for pushing HTTP1
+ * sessions through their states.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache/cache.h"
+#include "hash/hash_slinger.h"
+
+#include "vcl.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+/*----------------------------------------------------------------------
+ * Collect a request from the client.
+ */
+
+static enum req_fsm_nxt
+http1_wait(struct sess *sp, struct worker *wrk, struct req *req)
+{
+	int j, tmo;
+	struct pollfd pfd[1];
+	double now, when;
+	enum sess_close why = SC_NULL;
+	enum http1_status_e hs;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+	assert(req->sp == sp);
+
+	AZ(req->vcl);
+	AZ(req->esi_level);
+	AZ(isnan(sp->t_idle));
+	assert(isnan(req->t_first));
+	assert(isnan(req->t_prev));
+	assert(isnan(req->t_req));
+
+	tmo = (int)(1e3 * cache_param->timeout_linger);
+	while (1) {
+		pfd[0].fd = sp->fd;
+		pfd[0].events = POLLIN;
+		pfd[0].revents = 0;
+		j = poll(pfd, 1, tmo);
+		assert(j >= 0);
+		now = VTIM_real();
+		if (j != 0)
+			hs = HTTP1_Rx(req->htc);
+		else
+			hs = HTTP1_Complete(req->htc);
+		if (hs == HTTP1_COMPLETE) {
+			/* Got it, run with it */
+			if (isnan(req->t_first))
+				req->t_first = now;
+			if (isnan(req->t_req))
+				req->t_req = now;
+			req->acct.req_hdrbytes +=
+			    req->htc->rxbuf_e - req->htc->rxbuf_b;
+			return (REQ_FSM_MORE);
+		} else if (hs == HTTP1_ERROR_EOF) {
+			why = SC_REM_CLOSE;
+			break;
+		} else if (hs == HTTP1_OVERFLOW) {
+			why = SC_RX_OVERFLOW;
+			break;
+		} else if (hs == HTTP1_ALL_WHITESPACE) {
+			/* Nothing but whitespace */
+			when = sp->t_idle + cache_param->timeout_idle;
+			if (when < now) {
+				why = SC_RX_TIMEOUT;
+				break;
+			}
+			when = sp->t_idle + cache_param->timeout_linger;
+			tmo = (int)(1e3 * (when - now));
+			if (when < now || tmo == 0) {
+				wrk->stats->sess_herd++;
+				SES_ReleaseReq(req);
+				WAIT_Enter(sp);
+				return (REQ_FSM_DONE);
+			}
+		} else {
+			/* Working on it */
+			if (isnan(req->t_first))
+				/* Record first byte received time stamp */
+				req->t_first = now;
+			when = sp->t_idle + cache_param->timeout_req;
+			tmo = (int)(1e3 * (when - now));
+			if (when < now || tmo == 0) {
+				why = SC_RX_TIMEOUT;
+				break;
+			}
+		}
+	}
+	req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b;
+	CNT_AcctLogCharge(wrk->stats, req);
+	SES_ReleaseReq(req);
+	assert(why != SC_NULL);
+	SES_Delete(sp, why, now);
+	return (REQ_FSM_DONE);
+}
+
+/*----------------------------------------------------------------------
+ * This is the final state, figure out if we should close or recycle
+ * the client connection
+ */
+
+enum http1_cleanup_ret {
+	SESS_DONE_RET_GONE,
+	SESS_DONE_RET_WAIT,
+	SESS_DONE_RET_START,
+};
+
+static enum http1_cleanup_ret
+http1_cleanup(struct sess *sp, struct worker *wrk, struct req *req)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_ORNULL(req->vcl, VCL_CONF_MAGIC);
+
+	req->director_hint = NULL;
+	req->restarts = 0;
+
+	AZ(req->esi_level);
+
+	if (req->vcl != NULL) {
+		if (wrk->vcl != NULL)
+			VCL_Rel(&wrk->vcl);
+		wrk->vcl = req->vcl;
+		req->vcl = NULL;
+	}
+
+	VRTPRIV_dynamic_kill(sp, (uintptr_t)req);
+	/* Charge and log byte counters */
+	AN(req->vsl->wid);
+	CNT_AcctLogCharge(wrk->stats, req);
+	req->req_bodybytes = 0;
+	req->resp_hdrbytes = 0;
+	req->resp_bodybytes = 0;
+
+	VSL_End(req->vsl);
+
+	if (!isnan(req->t_prev) && req->t_prev > 0.)
+		sp->t_idle = req->t_prev;
+	else
+		sp->t_idle = W_TIM_real(wrk);
+
+	req->t_first = NAN;
+	req->t_prev = NAN;
+	req->t_req = NAN;
+	req->req_body_status = REQ_BODY_INIT;
+
+	req->hash_always_miss = 0;
+	req->hash_ignore_busy = 0;
+
+	if (sp->fd >= 0 && req->doclose != SC_NULL)
+		SES_Close(sp, req->doclose);
+
+	if (sp->fd < 0) {
+		wrk->stats->sess_closed++;
+		AZ(req->vcl);
+		SES_ReleaseReq(req);
+		SES_Delete(sp, SC_NULL, NAN);
+		return (SESS_DONE_RET_GONE);
+	}
+
+	WS_Reset(req->ws, NULL);
+	WS_Reset(wrk->aws, NULL);
+
+	if (HTTP1_Reinit(req->htc) == HTTP1_COMPLETE) {
+		AZ(req->vsl->wid);
+		req->t_first = req->t_req = sp->t_idle;
+		wrk->stats->sess_pipeline++;
+		req->acct.req_hdrbytes +=
+		    req->htc->rxbuf_e - req->htc->rxbuf_b;
+		return (SESS_DONE_RET_START);
+	} else {
+		if (req->htc->rxbuf_e != req->htc->rxbuf_b)
+			wrk->stats->sess_readahead++;
+		return (SESS_DONE_RET_WAIT);
+	}
+}
+
+/*----------------------------------------------------------------------
+ */
+
+static enum req_fsm_nxt
+http1_dissect(struct worker *wrk, struct req *req)
+{
+	const char *r_100 = "HTTP/1.1 100 Continue\r\n\r\n";
+	const char *r_400 = "HTTP/1.1 400 Bad Request\r\n\r\n";
+	const char *r_417 = "HTTP/1.1 417 Expectation Failed\r\n\r\n";
+	const char *p;
+	ssize_t r;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+	/* Allocate a new vxid now that we know we'll need it. */
+	AZ(req->vsl->wid);
+	req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
+
+	VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid));
+	VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid));
+	AZ(isnan(req->t_first)); /* First byte timestamp set by http1_wait */
+	AZ(isnan(req->t_req));	 /* Complete req rcvd set by http1_wait */
+	req->t_prev = req->t_first;
+	VSLb_ts_req(req, "Start", req->t_first);
+	VSLb_ts_req(req, "Req", req->t_req);
+
+	/* Borrow VCL reference from worker thread */
+	VCL_Refresh(&wrk->vcl);
+	req->vcl = wrk->vcl;
+	wrk->vcl = NULL;
+
+	HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
+	req->err_code = HTTP1_DissectRequest(req->htc, req->http);
+
+	/* If we could not even parse the request, just close */
+	if (req->err_code != 0) {
+		VSLb(req->vsl, SLT_HttpGarbage, "%.*s",
+		    (int)(req->htc->rxbuf_e - req->htc->rxbuf_b),
+		    req->htc->rxbuf_b);
+		wrk->stats->client_req_400++;
+		r = write(req->sp->fd, r_400, strlen(r_400));
+		if (r > 0)
+			req->acct.resp_hdrbytes += r;
+		SES_Close(req->sp, SC_RX_JUNK);
+		return (REQ_FSM_DONE);
+	}
+
+	assert (req->req_body_status == REQ_BODY_INIT);
+
+	if (req->htc->body_status == BS_CHUNKED) {
+		req->req_body_status = REQ_BODY_WITHOUT_LEN;
+	} else if (req->htc->body_status == BS_LENGTH) {
+		req->req_body_status = REQ_BODY_WITH_LEN;
+	} else if (req->htc->body_status == BS_NONE) {
+		req->req_body_status = REQ_BODY_NONE;
+	} else if (req->htc->body_status == BS_EOF) {
+		req->req_body_status = REQ_BODY_WITHOUT_LEN;
+	} else {
+		WRONG("Unknown req.body_length situation");
+	}
+
+	if (http_GetHdr(req->http, H_Expect, &p)) {
+		if (strcasecmp(p, "100-continue")) {
+			wrk->stats->client_req_417++;
+			req->err_code = 417;
+			r = write(req->sp->fd, r_417, strlen(r_417));
+			if (r > 0)
+				req->resp_hdrbytes += r;
+			SES_Close(req->sp, SC_RX_JUNK);
+			return (REQ_FSM_DONE);
+		}
+		r = write(req->sp->fd, r_100, strlen(r_100));
+		if (r > 0)
+			req->acct.resp_hdrbytes += r;
+		if (r != strlen(r_100)) {
+			SES_Close(req->sp, SC_REM_CLOSE);
+			return (REQ_FSM_DONE);
+		}
+		http_Unset(req->http, H_Expect);
+	}
+
+	wrk->stats->client_req++;
+	wrk->stats->s_req++;
+
+	AZ(req->err_code);
+	req->ws_req = WS_Snapshot(req->ws);
+
+	assert(req->req_body_status != REQ_BODY_INIT);
+
+	HTTP_Copy(req->http0, req->http);	// For ESI & restart
+
+	return (REQ_FSM_MORE);
+}
+
+/*----------------------------------------------------------------------
+ */
+
+void
+HTTP1_Session(struct worker *wrk, struct req *req)
+{
+	enum req_fsm_nxt nxt = REQ_FSM_MORE;
+	struct sess *sp;
+	enum http1_cleanup_ret sdr;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	sp = req->sp;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	/*
+	 * Whenever we come in from the acceptor or waiter, we need to set
+	 * blocking mode.  It would be simpler to do this in the acceptor
+	 * or waiter, but we'd rather do the syscall in the worker thread.
+	 * On systems which return errors for ioctl, we close early
+	 */
+	if (sp->sess_step == S_STP_NEWREQ && VTCP_blocking(sp->fd)) {
+		if (errno == ECONNRESET)
+			SES_Close(sp, SC_REM_CLOSE);
+		else
+			SES_Close(sp, SC_TX_ERROR);
+		sdr = http1_cleanup(sp, wrk, req);
+		assert(sdr == SESS_DONE_RET_GONE);
+		return;
+	}
+
+	/*
+	 * Return from waitinglist. Check to see if the remote has left.
+	 */
+	if (req->req_step == R_STP_LOOKUP && VTCP_check_hup(sp->fd)) {
+		AN(req->hash_objhead);
+		(void)HSH_DerefObjHead(wrk, &req->hash_objhead);
+		AZ(req->hash_objhead);
+		SES_Close(sp, SC_REM_CLOSE);
+		sdr = http1_cleanup(sp, wrk, req);
+		assert(sdr == SESS_DONE_RET_GONE);
+		return;
+	}
+
+	if (sp->sess_step == S_STP_NEWREQ) {
+		HTTP1_Init(req->htc, req->ws, sp->fd, req->vsl,
+		    cache_param->http_req_size,
+		    cache_param->http_req_hdr_len);
+	}
+
+	while (1) {
+		assert(
+		    sp->sess_step == S_STP_NEWREQ ||
+		    req->req_step == R_STP_LOOKUP ||
+		    req->req_step == R_STP_RECV);
+
+		if (sp->sess_step == S_STP_WORKING) {
+			if (req->req_step == R_STP_RECV)
+				nxt = http1_dissect(wrk, req);
+			if (nxt == REQ_FSM_MORE)
+				nxt = CNT_Request(wrk, req);
+			if (nxt == REQ_FSM_DISEMBARK)
+				return;
+			assert(nxt == REQ_FSM_DONE);
+			sdr = http1_cleanup(sp, wrk, req);
+			switch (sdr) {
+			case SESS_DONE_RET_GONE:
+				return;
+			case SESS_DONE_RET_WAIT:
+				sp->sess_step = S_STP_NEWREQ;
+				break;
+			case SESS_DONE_RET_START:
+				sp->sess_step = S_STP_WORKING;
+				req->req_step = R_STP_RECV;
+				break;
+			default:
+				WRONG("Illegal enum http1_cleanup_ret");
+			}
+		}
+
+		if (sp->sess_step == S_STP_NEWREQ) {
+			nxt = http1_wait(sp, wrk, req);
+			if (nxt != REQ_FSM_MORE)
+				return;
+			sp->sess_step = S_STP_WORKING;
+			req->req_step = R_STP_RECV;
+		}
+	}
+}
diff --git a/bin/varnishd/http1/cache_http1_pipe.c b/bin/varnishd/http1/cache_http1_pipe.c
new file mode 100644
index 0000000..e451f35
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_pipe.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2011 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * XXX: charge bytes to srcaddr
+ */
+
+#include "config.h"
+
+#include <poll.h>
+#include <stdio.h>
+
+#include "cache/cache.h"
+
+#include "cache/cache_backend.h"
+#include "cache/cache_director.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+static struct lock pipestat_mtx;
+
+struct acct_pipe {
+	uint64_t	req;
+	uint64_t	bereq;
+	uint64_t	in;
+	uint64_t	out;
+};
+
+static int
+rdf(int fd0, int fd1, uint64_t *pcnt)
+{
+	int i, j;
+	char buf[BUFSIZ], *p;
+
+	i = read(fd0, buf, sizeof buf);
+	if (i <= 0)
+		return (1);
+	for (p = buf; i > 0; i -= j, p += j) {
+		j = write(fd1, p, i);
+		if (j <= 0)
+			return (1);
+		*pcnt += j;
+		if (i != j)
+			(void)usleep(100000);		/* XXX hack */
+	}
+	return (0);
+}
+
+static void
+pipecharge(struct req *req, const struct acct_pipe *a, struct VSC_C_vbe *b)
+{
+
+	VSLb(req->vsl, SLT_PipeAcct, "%ju %ju %ju %ju",
+	    (uintmax_t)a->req,
+	    (uintmax_t)a->bereq,
+	    (uintmax_t)a->in,
+	    (uintmax_t)a->out);
+
+	Lck_Lock(&pipestat_mtx);
+	VSC_C_main->s_pipe_hdrbytes += a->req;
+	VSC_C_main->s_pipe_in += a->in;
+	VSC_C_main->s_pipe_out += a->out;
+	if (b != NULL) {
+		b->pipe_hdrbytes += a->bereq;
+		b->pipe_out += a->in;
+		b->pipe_in += a->out;
+	}
+	Lck_Unlock(&pipestat_mtx);
+}
+
+void
+V1P_Process(struct req *req, struct busyobj *bo)
+{
+	struct vbc *vc;
+	struct worker *wrk;
+	struct pollfd fds[2];
+	int i;
+	struct acct_pipe acct_pipe;
+	ssize_t hdrbytes;
+
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+	CHECK_OBJ_NOTNULL(req->sp, SESS_MAGIC);
+	wrk = req->wrk;
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
+
+	req->res_mode = RES_PIPE;
+
+	memset(&acct_pipe, 0, sizeof acct_pipe);
+	acct_pipe.req = req->acct.req_hdrbytes;
+	req->acct.req_hdrbytes = 0;
+
+	vc = VDI_GetFd(bo->director_req, wrk, bo);
+	if (vc == NULL) {
+		pipecharge(req, &acct_pipe, NULL);
+		SES_Close(req->sp, SC_OVERLOAD);
+		return;
+	}
+	bo->vbc = vc;		/* For panic dumping */
+	(void)VTCP_blocking(vc->fd);
+
+	WRW_Reserve(wrk, &vc->fd, bo->vsl, req->t_req);
+	hdrbytes = HTTP1_Write(wrk, bo->bereq, HTTP1_Req);
+
+	if (req->htc->pipeline_b != NULL)
+		(void)WRW_Write(wrk, req->htc->pipeline_b,
+		    req->htc->pipeline_e - req->htc->pipeline_b);
+
+	i = WRW_FlushRelease(wrk, &acct_pipe.bereq);
+	if (acct_pipe.bereq > hdrbytes) {
+		acct_pipe.in = acct_pipe.bereq - hdrbytes;
+		acct_pipe.bereq = hdrbytes;
+	}
+
+	VSLb_ts_req(req, "Pipe", W_TIM_real(wrk));
+
+	if (i) {
+		pipecharge(req, &acct_pipe, vc->backend->vsc);
+		SES_Close(req->sp, SC_TX_PIPE);
+		VBE_CloseFd(&vc, NULL);
+		return;
+	}
+
+	memset(fds, 0, sizeof fds);
+
+	// XXX: not yet (void)VTCP_linger(vc->fd, 0);
+	fds[0].fd = vc->fd;
+	fds[0].events = POLLIN | POLLERR;
+
+	// XXX: not yet (void)VTCP_linger(req->sp->fd, 0);
+	fds[1].fd = req->sp->fd;
+	fds[1].events = POLLIN | POLLERR;
+
+	while (fds[0].fd > -1 || fds[1].fd > -1) {
+		fds[0].revents = 0;
+		fds[1].revents = 0;
+		i = poll(fds, 2, (int)(cache_param->pipe_timeout * 1e3));
+		if (i < 1)
+			break;
+		if (fds[0].revents &&
+		    rdf(vc->fd, req->sp->fd, &acct_pipe.out)) {
+			if (fds[1].fd == -1)
+				break;
+			(void)shutdown(vc->fd, SHUT_RD);
+			(void)shutdown(req->sp->fd, SHUT_WR);
+			fds[0].events = 0;
+			fds[0].fd = -1;
+		}
+		if (fds[1].revents &&
+		    rdf(req->sp->fd, vc->fd, &acct_pipe.in)) {
+			if (fds[0].fd == -1)
+				break;
+			(void)shutdown(req->sp->fd, SHUT_RD);
+			(void)shutdown(vc->fd, SHUT_WR);
+			fds[1].events = 0;
+			fds[1].fd = -1;
+		}
+	}
+	VSLb_ts_req(req, "PipeSess", W_TIM_real(wrk));
+	pipecharge(req, &acct_pipe, vc->backend->vsc);
+	SES_Close(req->sp, SC_TX_PIPE);
+	VBE_CloseFd(&vc, NULL);
+	bo->vbc = NULL;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+V1P_Init(void)
+{
+
+	Lck_New(&pipestat_mtx, lck_pipestat);
+}
diff --git a/bin/varnishd/http1/cache_http1_proto.c b/bin/varnishd/http1/cache_http1_proto.c
new file mode 100644
index 0000000..4d0e025
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_proto.c
@@ -0,0 +1,576 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2014 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * HTTP protocol requests
+ *
+ * The trouble with the "until magic sequence" design of HTTP protocol messages
+ * is that either you have to read a single character at a time, which is
+ * inefficient, or you risk reading too much, and pre-read some of the object,
+ * or even the next pipelined request, which follows the one you want.
+ *
+ * HTC reads a HTTP protocol header into a workspace, subject to limits,
+ * and stops when we see the magic marker (double [CR]NL), and if we overshoot,
+ * it keeps track of the "pipelined" data.
+ *
+ * Until we see the magic marker, we have to keep the rxbuf NUL terminated
+ * because we use strchr(3) on it.
+ *
+ * We use this both for client and backend connections.
+ */
+
+#include "config.h"
+
+#include "cache/cache.h"
+
+#include "vct.h"
+
+const int HTTP1_Req[3] = {
+	HTTP_HDR_METHOD, HTTP_HDR_URL, HTTP_HDR_PROTO
+};
+
+const int HTTP1_Resp[3] = {
+	HTTP_HDR_PROTO, HTTP_HDR_STATUS, HTTP_HDR_REASON
+};
+
+/*--------------------------------------------------------------------*/
+
+void
+HTTP1_Init(struct http_conn *htc, struct ws *ws, int fd, struct vsl_log *vsl,
+    unsigned maxbytes, unsigned maxhdr)
+{
+
+	htc->magic = HTTP_CONN_MAGIC;
+	htc->ws = ws;
+	htc->fd = fd;
+	htc->vsl = vsl;
+	htc->maxbytes = maxbytes;
+	htc->maxhdr = maxhdr;
+
+	(void)WS_Reserve(htc->ws, htc->maxbytes);
+	htc->rxbuf_b = ws->f;
+	htc->rxbuf_e = ws->f;
+	*htc->rxbuf_e = '\0';
+	htc->pipeline_b = NULL;
+	htc->pipeline_e = NULL;
+}
+
+/*--------------------------------------------------------------------
+ * Start over, and recycle any pipelined input.
+ * The WS_Reset is safe, even though the pipelined input is stored in
+ * the ws somewhere, because WS_Reset only fiddles pointers.
+ */
+
+enum http1_status_e
+HTTP1_Reinit(struct http_conn *htc)
+{
+	ssize_t l;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	(void)WS_Reserve(htc->ws, htc->maxbytes);
+	htc->rxbuf_b = htc->ws->f;
+	htc->rxbuf_e = htc->ws->f;
+	if (htc->pipeline_b != NULL) {
+		l = htc->pipeline_e - htc->pipeline_b;
+		assert(l > 0);
+		memmove(htc->rxbuf_b, htc->pipeline_b, l);
+		htc->rxbuf_e += l;
+		htc->pipeline_b = NULL;
+		htc->pipeline_e = NULL;
+	}
+	*htc->rxbuf_e = '\0';
+	return (HTTP1_Complete(htc));
+}
+
+/*--------------------------------------------------------------------
+ * Check if we have a complete HTTP request or response yet
+ */
+
+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);
+}
+
+/*--------------------------------------------------------------------
+ * Receive more HTTP protocol bytes
+ */
+
+enum http1_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));
+}
+
+/*--------------------------------------------------------------------
+ * Read up to len bytes, returning pipelined data first.
+ */
+
+ssize_t
+HTTP1_Read(struct http_conn *htc, void *d, size_t len)
+{
+	ssize_t l;
+	unsigned char *p;
+	ssize_t i = 0;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	l = 0;
+	p = d;
+	if (htc->pipeline_b) {
+		l = htc->pipeline_e - htc->pipeline_b;
+		assert(l > 0);
+		if (l > len)
+			l = len;
+		memcpy(p, htc->pipeline_b, l);
+		p += l;
+		len -= l;
+		htc->pipeline_b += l;
+		if (htc->pipeline_b == htc->pipeline_e)
+			htc->pipeline_b = htc->pipeline_e = NULL;
+	}
+	if (len > 0) {
+		i = read(htc->fd, p, len);
+		if (i < 0) {
+			VSLb(htc->vsl, SLT_FetchError, "%s", strerror(errno));
+			return (i);
+		}
+	}
+	return (i + l);
+}
+
+/*--------------------------------------------------------------------
+ * Dissect the headers of the HTTP protocol message.
+ * Detect conditionals (headers which start with '^[Ii][Ff]-')
+ */
+
+static uint16_t
+http1_dissect_hdrs(struct http *hp, char *p, const struct http_conn *htc)
+{
+	char *q, *r;
+
+	assert(p > htc->rxbuf_b);
+	assert(p <= htc->rxbuf_e);
+	hp->nhd = HTTP_HDR_FIRST;
+	hp->conds = 0;
+	r = NULL;		/* For FlexeLint */
+	for (; p < htc->rxbuf_e; p = r) {
+
+		/* Find end of next header */
+		q = r = p;
+		while (r < htc->rxbuf_e) {
+			if (!vct_iscrlf(r)) {
+				r++;
+				continue;
+			}
+			q = r;
+			assert(r < htc->rxbuf_e);
+			r += vct_skipcrlf(r);
+			if (r >= htc->rxbuf_e)
+				break;
+			/* If line does not continue: got it. */
+			if (!vct_issp(*r))
+				break;
+
+			/* Clear line continuation LWS to spaces */
+			while (vct_islws(*q))
+				*q++ = ' ';
+		}
+
+		if (q - p > htc->maxhdr) {
+			VSLb(hp->vsl, SLT_BogoHeader, "Header too long: %.*s",
+			    (int)(q - p > 20 ? 20 : q - p), p);
+			return (400);
+		}
+
+		/* Empty header = end of headers */
+		if (p == q)
+			break;
+
+		if (vct_islws(*p)) {
+			VSLb(hp->vsl, SLT_BogoHeader,
+			    "1st header has white space: %.*s",
+			    (int)(q - p > 20 ? 20 : q - p), p);
+			return (400);
+		}
+
+		if ((p[0] == 'i' || p[0] == 'I') &&
+		    (p[1] == 'f' || p[1] == 'F') &&
+		    p[2] == '-')
+			hp->conds = 1;
+
+		while (q > p && vct_issp(q[-1]))
+			q--;
+		*q = '\0';
+
+		if (strchr(p, ':') == NULL) {
+			VSLb(hp->vsl, SLT_BogoHeader, "Header without ':' %.*s",
+			    (int)(q - p > 20 ? 20 : q - p), p);
+			return (400);
+		}
+
+		if (hp->nhd < hp->shd) {
+			hp->hdf[hp->nhd] = 0;
+			hp->hd[hp->nhd].b = p;
+			hp->hd[hp->nhd].e = q;
+			hp->nhd++;
+		} else {
+			VSLb(hp->vsl, SLT_BogoHeader, "Too many headers: %.*s",
+			    (int)(q - p > 20 ? 20 : q - p), p);
+			return (400);
+		}
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Deal with first line of HTTP protocol message.
+ */
+
+static uint16_t
+http1_splitline(struct http *hp, const struct http_conn *htc, const int *hf)
+{
+	char *p;
+	int i;
+
+	assert(hf == HTTP1_Req || hf == HTTP1_Resp);
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	assert(htc->rxbuf_e >= htc->rxbuf_b);
+
+	AZ(hp->hd[hf[0]].b);
+	AZ(hp->hd[hf[1]].b);
+	AZ(hp->hd[hf[2]].b);
+
+	/* Skip leading LWS */
+	for (p = htc->rxbuf_b ; vct_islws(*p); p++)
+		continue;
+	hp->hd[hf[0]].b = p;
+
+	/* First field cannot contain SP or CTL */
+	for (; !vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[hf[0]].e = p;
+	assert(Tlen(hp->hd[hf[0]]));
+	*p++ = '\0';
+
+	/* Skip SP */
+	for (; vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[hf[1]].b = p;
+
+	/* Second field cannot contain LWS or CTL */
+	for (; !vct_islws(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[hf[1]].e = p;
+	if (!Tlen(hp->hd[hf[1]]))
+		return (400);
+	*p++ = '\0';
+
+	/* Skip SP */
+	for (; vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[hf[2]].b = p;
+
+	/* Third field is optional and cannot contain CTL except TAB */
+	for (; !vct_iscrlf(p); p++) {
+		if (vct_isctl(*p) && !vct_issp(*p)) {
+			hp->hd[hf[2]].b = NULL;
+			return (400);
+		}
+	}
+	hp->hd[hf[2]].e = p;
+
+	/* Skip CRLF */
+	i = vct_skipcrlf(p);
+	*p = '\0';
+	p += i;
+
+	return (http1_dissect_hdrs(hp, p, htc));
+}
+
+/*--------------------------------------------------------------------*/
+
+static enum body_status
+http1_body_status(const struct http *hp, struct http_conn *htc)
+{
+	ssize_t cl;
+	const char *b;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	htc->content_length = -1;
+
+	if (http_HdrIs(hp, H_Transfer_Encoding, "chunked"))
+		return (BS_CHUNKED);
+
+	if (http_GetHdr(hp, H_Transfer_Encoding, &b))
+		return (BS_ERROR);
+
+	cl = http_GetContentLength(hp);
+	if (cl == -2)
+		return (BS_ERROR);
+	if (cl >= 0) {
+		htc->content_length = cl;
+		return (cl == 0 ? BS_NONE : BS_LENGTH);
+	}
+
+	if (http_HdrIs(hp, H_Connection, "keep-alive")) {
+		/*
+		 * Keep alive with neither TE=Chunked or C-Len is impossible.
+		 * We assume a zero length body.
+		 */
+		return (BS_NONE);
+	}
+
+	if (http_HdrIs(hp, H_Connection, "close")) {
+		/*
+		 * In this case, it is safe to just read what comes.
+		 */
+		return (BS_EOF);
+	}
+
+	if (hp->protover < 11) {
+		/*
+		 * With no Connection header, assume EOF.
+		 */
+		return (BS_EOF);
+	}
+
+	/*
+	 * Fall back to EOF transfer.
+	 */
+	return (BS_EOF);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+http1_proto_ver(struct http *hp)
+{
+	if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.0"))
+		hp->protover = 10;
+	else if (!strcasecmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.1"))
+		hp->protover = 11;
+	else
+		hp->protover = 9;
+}
+
+/*--------------------------------------------------------------------*/
+
+uint16_t
+HTTP1_DissectRequest(struct http_conn *htc, struct http *hp)
+{
+	uint16_t retval;
+	const char *p;
+	const char *b, *e;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	retval = http1_splitline(hp, htc, HTTP1_Req);
+	if (retval != 0)
+		return (retval);
+	http1_proto_ver(hp);
+
+	if (http_CountHdr(hp, H_Host) > 1)
+		return (400);
+
+	if (http_CountHdr(hp, H_Content_Length) > 1)
+		return (400);
+
+	/* RFC2616, section 5.2, point 1 */
+	if (!strncasecmp(hp->hd[HTTP_HDR_URL].b, "http://", 7)) {
+		b = e = hp->hd[HTTP_HDR_URL].b + 7;
+		while (*e != '/' && *e != '\0')
+			e++;
+		if (*e == '/') {
+			http_Unset(hp, H_Host);
+			http_PrintfHeader(hp, "Host: %.*s", (int)(e - b), b);
+			hp->hd[HTTP_HDR_URL].b = e;
+		}
+	}
+
+	htc->body_status = http1_body_status(hp, htc);
+	if (htc->body_status == BS_ERROR)
+		return (400);
+
+	p = http_GetMethod(hp);
+	AN(p);
+
+	/* We handle EOF bodies only for PUT and POST */
+	if (htc->body_status == BS_EOF &&
+	    strcasecmp(p, "put") && strcasecmp(p, "post"))
+		htc->body_status = BS_NONE;
+
+	/* HEAD with a body is a hard error */
+	if (htc->body_status != BS_NONE && !strcasecmp(p, "head"))
+		return (400);
+
+	return (retval);
+}
+
+/*--------------------------------------------------------------------*/
+
+uint16_t
+HTTP1_DissectResponse(struct http *hp, struct http_conn *htc)
+{
+	uint16_t retval = 0;
+	const char *p;
+
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	if (http1_splitline(hp, htc, HTTP1_Resp))
+		retval = 503;
+
+	if (retval == 0) {
+		http1_proto_ver(hp);
+		if (hp->protover != 10 && hp->protover != 11)
+			retval = 503;
+	}
+
+	if (retval == 0 && Tlen(hp->hd[HTTP_HDR_STATUS]) != 3)
+		retval = 503;
+
+	if (retval == 0) {
+		p = hp->hd[HTTP_HDR_STATUS].b;
+
+		if (p[0] >= '1' && p[0] <= '9' &&
+		    p[1] >= '0' && p[1] <= '9' &&
+		    p[2] >= '0' && p[2] <= '9')
+			hp->status =
+			    100 * (p[0] - '0') + 10 * (p[1] - '0') + p[2] - '0';
+		else
+			retval = 503;
+	}
+
+	if (retval != 0) {
+		VSLb(hp->vsl, SLT_HttpGarbage, "%.*s",
+		    (int)(htc->rxbuf_e - htc->rxbuf_b), htc->rxbuf_b);
+		assert(retval >= 100 && retval <= 999);
+		assert(retval == 503);
+		hp->status = retval;
+		http_SetH(hp, HTTP_HDR_STATUS, "503");
+		http_SetH(hp, HTTP_HDR_REASON, http_Status2Reason(retval));
+	}
+
+	if (hp->hd[HTTP_HDR_REASON].b == NULL ||
+	    !Tlen(hp->hd[HTTP_HDR_REASON]))
+		http_SetH(hp, HTTP_HDR_REASON, http_Status2Reason(hp->status));
+
+	htc->body_status = http1_body_status(hp, htc);
+
+	return (retval);
+}
+
+/*--------------------------------------------------------------------*/
+
+unsigned
+HTTP1_Write(const struct worker *w, const struct http *hp, const int *hf)
+{
+	unsigned u, l;
+
+	assert(hf == HTTP1_Req || hf == HTTP1_Resp);
+	AN(hp->hd[hf[0]].b);
+	AN(hp->hd[hf[1]].b);
+	AN(hp->hd[hf[2]].b);
+	l = WRW_WriteH(w, &hp->hd[hf[0]], " ");
+	l += WRW_WriteH(w, &hp->hd[hf[1]], " ");
+	l += WRW_WriteH(w, &hp->hd[hf[2]], "\r\n");
+
+	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++)
+		l += WRW_WriteH(w, &hp->hd[u], "\r\n");
+	l += WRW_Write(w, "\r\n", -1);
+	return (l);
+}
diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c
new file mode 100644
index 0000000..404a2f5
--- /dev/null
+++ b/bin/varnishd/http1/cache_http1_vfp.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2014 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * HTTP1 Fetch Filters
+ *
+ * These filters are used for both req.body and beresp.body to handle
+ * the HTTP/1 aspects (C-L/Chunked/EOF)
+ *
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+
+#include "cache/cache.h"
+
+#include "vct.h"
+
+/*--------------------------------------------------------------------
+ * Read a chunked HTTP object.
+ *
+ * XXX: Reading one byte at a time is pretty pessimal.
+ */
+
+static enum vfp_status __match_proto__(vfp_pull_f)
+v1f_pull_chunked(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr,
+    ssize_t *lp)
+{
+	struct http_conn *htc;
+	int i;
+	char buf[20];		/* XXX: 20 is arbitrary */
+	char *q;
+	unsigned u;
+	uintmax_t cll;
+	ssize_t cl, l, lr;
+
+	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
+	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
+	AN(ptr);
+	AN(lp);
+
+	AN(ptr);
+	AN(lp);
+	l = *lp;
+	*lp = 0;
+	if (vfe->priv2 == -1) {
+		/* Skip leading whitespace */
+		do {
+			lr = HTTP1_Read(htc, buf, 1);
+			if (lr <= 0)
+				return (VFP_Error(vc, "chunked read err"));
+		} while (vct_islws(buf[0]));
+
+		if (!vct_ishex(buf[0]))
+			 return (VFP_Error(vc, "chunked header non-hex"));
+
+		/* Collect hex digits, skipping leading zeros */
+		for (u = 1; u < sizeof buf; u++) {
+			do {
+				lr = HTTP1_Read(htc, buf + u, 1);
+				if (lr <= 0)
+					return (VFP_Error(vc,
+					    "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(vc, "chunked header too long"));
+
+		/* Skip trailing white space */
+		while(vct_islws(buf[u]) && buf[u] != '\n') {
+			lr = HTTP1_Read(htc, buf + u, 1);
+			if (lr <= 0)
+				return (VFP_Error(vc, "chunked read err"));
+		}
+
+		if (buf[u] != '\n')
+			return (VFP_Error(vc, "chunked header no NL"));
+
+		buf[u] = '\0';
+
+		cll = strtoumax(buf, &q, 16);
+		if (q == NULL || *q != '\0')
+			return (VFP_Error(vc, "chunked header number syntax"));
+		cl = (ssize_t)cll;
+		if((uintmax_t)cl != cll)
+			return (VFP_Error(vc, "bogusly large chunk size"));
+
+		vfe->priv2 = cl;
+	}
+	if (vfe->priv2 > 0) {
+		if (vfe->priv2 < l)
+			l = vfe->priv2;
+		lr = HTTP1_Read(htc, ptr, l);
+		if (lr <= 0)
+			return (VFP_Error(vc, "straight insufficient bytes"));
+		*lp = lr;
+		vfe->priv2 -= lr;
+		if (vfe->priv2 == 0)
+			vfe->priv2 = -1;
+		return (VFP_OK);
+	}
+	AZ(vfe->priv2);
+	i = HTTP1_Read(htc, buf, 1);
+	if (i <= 0)
+		return (VFP_Error(vc, "chunked read err"));
+	if (buf[0] == '\r' && HTTP1_Read(htc, buf, 1) <= 0)
+		return (VFP_Error(vc, "chunked read err"));
+	if (buf[0] != '\n')
+		return (VFP_Error(vc, "chunked tail no NL"));
+	return (VFP_END);
+}
+
+static const struct vfp v1f_chunked = {
+	.name = "V1F_CHUNKED",
+	.pull = v1f_pull_chunked,
+};
+
+
+/*--------------------------------------------------------------------*/
+
+static enum vfp_status __match_proto__(vfp_pull_f)
+v1f_pull_straight(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
+    ssize_t *lp)
+{
+	ssize_t l, lr;
+	struct http_conn *htc;
+
+	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
+	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
+	AN(p);
+	AN(lp);
+
+	l = *lp;
+	*lp = 0;
+
+	if (vfe->priv2 == 0) // XXX: Optimize Content-Len: 0 out earlier
+		return (VFP_END);
+	if (vfe->priv2 < l)
+		l = vfe->priv2;
+	lr = HTTP1_Read(htc, p, l);
+	if (lr <= 0)
+		return (VFP_Error(vc, "straight insufficient bytes"));
+	*lp = lr;
+	vfe->priv2 -= lr;
+	if (vfe->priv2 == 0)
+		return (VFP_END);
+	return (VFP_OK);
+}
+
+static const struct vfp v1f_straight = {
+	.name = "V1F_STRAIGHT",
+	.pull = v1f_pull_straight,
+};
+
+/*--------------------------------------------------------------------*/
+
+static enum vfp_status __match_proto__(vfp_pull_f)
+v1f_pull_eof(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
+    ssize_t *lp)
+{
+	ssize_t l, lr;
+	struct http_conn *htc;
+
+	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
+	CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC);
+	AN(p);
+
+	AN(lp);
+
+	l = *lp;
+	*lp = 0;
+	lr = HTTP1_Read(htc, p, l);
+	if (lr < 0)
+		return (VFP_Error(vc, "eof socket fail"));
+	if (lr == 0)
+		return (VFP_END);
+	*lp = lr;
+	return (VFP_OK);
+}
+
+static const struct vfp v1f_eof = {
+	.name = "V1F_EOF",
+	.pull = v1f_pull_eof,
+};
+
+/*--------------------------------------------------------------------
+ */
+
+void
+V1F_Setup_Fetch(struct vfp_ctx *vfc, struct http_conn *htc)
+{
+	struct vfp_entry *vfe;
+
+	CHECK_OBJ_NOTNULL(vfc, VFP_CTX_MAGIC);
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+
+	switch(htc->body_status) {
+	case BS_EOF:
+		assert(htc->content_length == -1);
+		vfe = VFP_Push(vfc, &v1f_eof, 0);
+		vfe->priv2 = 0;
+		break;
+	case BS_LENGTH:
+		assert(htc->content_length > 0);
+		vfe = VFP_Push(vfc, &v1f_straight, 0);
+		vfe->priv2 = htc->content_length;
+		break;
+	case BS_CHUNKED:
+		assert(htc->content_length == -1);
+		vfe = VFP_Push(vfc, &v1f_chunked, 0);
+		vfe->priv2 = -1;
+		break;
+	default:
+		WRONG("Wrong body_status");
+		break;
+	}
+	vfe->priv1 = htc;
+}



More information about the varnish-commit mailing list