[experimental-ims] f3a414f Stuff all of the worker process into a cache/ subdirectory.

Geoff Simmons geoff at varnish-cache.org
Mon Jan 9 21:52:33 CET 2012


commit f3a414fbf75eb4e4927aa7d46bd7e92a8abf5fd4
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Sun Nov 13 10:55:14 2011 +0000

    Stuff all of the worker process into a cache/ subdirectory.
    
    Yes, I should have done this five years ago...

diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index 245bc6b..850a18a 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -3,6 +3,7 @@
 INCLUDES = \
 	-I$(top_srcdir)/include \
 	-I$(top_srcdir)/lib/libvgz \
+	-I$(top_builddir)/bin/varnishd \
 	-I$(top_builddir)/include
 
 sbin_PROGRAMS = varnishd
@@ -10,44 +11,44 @@ sbin_PROGRAMS = varnishd
 dist_man_MANS = varnishd.1
 
 varnishd_SOURCES = \
-	cache_acceptor.c \
-	cache_backend.c \
-	cache_backend_cfg.c \
-	cache_backend_poll.c \
-	cache_ban.c \
-	cache_center.c \
-	cache_cli.c \
-	cache_dir.c \
-	cache_dir_dns.c \
-	cache_dir_random.c \
-	cache_dir_round_robin.c \
-	cache_esi_deliver.c \
-	cache_esi_fetch.c \
-	cache_esi_parse.c \
-	cache_expire.c \
-	cache_fetch.c \
-	cache_gzip.c \
-	cache_hash.c \
-	cache_http.c \
-	cache_httpconn.c \
-	cache_lck.c \
-	cache_main.c \
-	cache_panic.c \
-	cache_pipe.c \
-	cache_pool.c \
-	cache_response.c \
-	cache_rfc2616.c \
-	cache_session.c \
-	cache_shmlog.c \
-	cache_vary.c \
-	cache_vcl.c \
-	cache_vrt.c \
-	cache_vrt_re.c \
-	cache_vrt_var.c \
-	cache_vrt_vmod.c \
-	cache_wrk.c \
-	cache_wrw.c \
-	cache_ws.c \
+	cache/cache_acceptor.c \
+	cache/cache_backend.c \
+	cache/cache_backend_cfg.c \
+	cache/cache_backend_poll.c \
+	cache/cache_ban.c \
+	cache/cache_center.c \
+	cache/cache_cli.c \
+	cache/cache_dir.c \
+	cache/cache_dir_dns.c \
+	cache/cache_dir_random.c \
+	cache/cache_dir_round_robin.c \
+	cache/cache_esi_deliver.c \
+	cache/cache_esi_fetch.c \
+	cache/cache_esi_parse.c \
+	cache/cache_expire.c \
+	cache/cache_fetch.c \
+	cache/cache_gzip.c \
+	cache/cache_hash.c \
+	cache/cache_http.c \
+	cache/cache_httpconn.c \
+	cache/cache_lck.c \
+	cache/cache_main.c \
+	cache/cache_panic.c \
+	cache/cache_pipe.c \
+	cache/cache_pool.c \
+	cache/cache_response.c \
+	cache/cache_rfc2616.c \
+	cache/cache_session.c \
+	cache/cache_shmlog.c \
+	cache/cache_vary.c \
+	cache/cache_vcl.c \
+	cache/cache_vrt.c \
+	cache/cache_vrt_re.c \
+	cache/cache_vrt_var.c \
+	cache/cache_vrt_vmod.c \
+	cache/cache_wrk.c \
+	cache/cache_wrw.c \
+	cache/cache_ws.c \
 	common/common_vsm.c \
 	hash/hash_classic.c \
 	hash/hash_critbit.c \
@@ -79,9 +80,9 @@ varnishd_SOURCES = \
 	waiter/cache_waiter_ports.c 
 
 noinst_HEADERS = \
-	cache.h \
-	cache_backend.h \
-	cache_esi.h \
+	cache/cache.h \
+	cache/cache_backend.h \
+	cache/cache_esi.h \
 	common/common.h \
 	common/heritage.h \
 	common/params.h \
diff --git a/bin/varnishd/cache.h b/bin/varnishd/cache.h
deleted file mode 100644
index 653fe77..0000000
--- a/bin/varnishd/cache.h
+++ /dev/null
@@ -1,1039 +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.
- *
- */
-
-/*
- * This macro can be used in .h files to isolate bits that the manager
- * should not (need to) see, such as pthread mutexes etc.
- */
-#define VARNISH_CACHE_CHILD	1
-
-#include "common/common.h"
-
-#include "vapi/vsc_int.h"
-#include "vapi/vsl_int.h"
-
-#include <sys/socket.h>
-
-#include <pthread.h>
-#ifdef HAVE_PTHREAD_NP_H
-#include <pthread_np.h>
-#endif
-#include <stdarg.h>
-#include <string.h>
-#include <limits.h>
-#include <unistd.h>
-
-#if defined(HAVE_EPOLL_CTL)
-#include <sys/epoll.h>
-#endif
-
-
-#include "common/params.h"
-
-enum body_status {
-#define BODYSTATUS(U,l)	BS_##U,
-#include "tbl/body_status.h"
-#undef BODYSTATUS
-};
-
-static inline const char *
-body_status(enum body_status e)
-{
-	switch(e) {
-#define BODYSTATUS(U,l)	case BS_##U: return (#l);
-#include "tbl/body_status.h"
-#undef BODYSTATUS
-	default:
-		return ("?");
-	}
-}
-
-/*
- * NB: HDR_STATUS is only used in cache_http.c, everybody else uses the
- * http->status integer field.
- */
-
-enum {
-	/* Fields from the first line of HTTP proto */
-	HTTP_HDR_REQ,
-	HTTP_HDR_URL,
-	HTTP_HDR_PROTO,
-	HTTP_HDR_STATUS,
-	HTTP_HDR_RESPONSE,
-	/* HTTP header lines */
-	HTTP_HDR_FIRST,
-};
-
-struct SHA256Context;
-struct VSC_C_lck;
-struct ban;
-struct busyobj;
-struct cli;
-struct cli_proto;
-struct director;
-struct iovec;
-struct objcore;
-struct object;
-struct objhead;
-struct pool;
-struct sess;
-struct sesspool;
-struct vbc;
-struct vef_priv;
-struct vrt_backend;
-struct vsb;
-struct waitinglist;
-struct worker;
-
-#define DIGEST_LEN		32
-
-/* Name of transient storage */
-#define TRANSIENT_STORAGE	"Transient"
-
-/*--------------------------------------------------------------------
- * Pointer aligment magic
- */
-
-#define PALGN		(sizeof(void *) - 1)
-#define PAOK(p)		(((uintptr_t)(p) & PALGN) == 0)
-#define PRNDDN(p)	((uintptr_t)(p) & ~PALGN)
-#define PRNDUP(p)	(((uintptr_t)(p) + PALGN) & ~PALGN)
-
-/*--------------------------------------------------------------------*/
-
-typedef struct {
-	char			*b;
-	char			*e;
-} txt;
-
-/*--------------------------------------------------------------------*/
-
-enum step {
-#define STEP(l, u)	STP_##u,
-#include "tbl/steps.h"
-#undef STEP
-};
-
-/*--------------------------------------------------------------------
- * Workspace structure for quick memory allocation.
- */
-
-struct ws {
-	unsigned		magic;
-#define WS_MAGIC		0x35fac554
-	unsigned		overflow;	/* workspace overflowed */
-	const char		*id;		/* identity */
-	char			*s;		/* (S)tart of buffer */
-	char			*f;		/* (F)ree pointer */
-	char			*r;		/* (R)eserved length */
-	char			*e;		/* (E)nd of buffer */
-};
-
-/*--------------------------------------------------------------------
- * HTTP Request/Response/Header handling structure.
- */
-
-enum httpwhence {
-	HTTP_Rx	 = 1,
-	HTTP_Tx  = 2,
-	HTTP_Obj = 3
-};
-
-/* NB: remember to update http_Copy() if you add fields */
-struct http {
-	unsigned		magic;
-#define HTTP_MAGIC		0x6428b5c9
-
-	enum httpwhence		logtag;
-
-	struct ws		*ws;
-	txt			*hd;
-	unsigned char		*hdf;
-#define HDF_FILTER		(1 << 0)	/* Filtered by Connection */
-	uint16_t		shd;		/* Size of hd space */
-	uint16_t		nhd;		/* Next free hd */
-	uint16_t		status;
-	uint8_t			protover;
-	uint8_t			conds;		/* If-* headers present */
-};
-
-/*--------------------------------------------------------------------
- * HTTP Protocol connection structure
- */
-
-struct http_conn {
-	unsigned		magic;
-#define HTTP_CONN_MAGIC		0x3e19edd1
-
-	int			fd;
-	unsigned		vsl_id;
-	unsigned		maxbytes;
-	unsigned		maxhdr;
-	struct ws		*ws;
-	txt			rxbuf;
-	txt			pipeline;
-};
-
-/*--------------------------------------------------------------------*/
-
-struct acct {
-	double			first;
-#define ACCT(foo)	uint64_t	foo;
-#include "tbl/acct_fields.h"
-#undef ACCT
-};
-
-/*--------------------------------------------------------------------*/
-
-#define L0(t, n)
-#define L1(t, n)		t n;
-#define VSC_F(n, t, l, f, e,d)	L##l(t, n)
-#define VSC_DO_MAIN
-struct dstat {
-#include "tbl/vsc_fields.h"
-};
-#undef VSC_F
-#undef VSC_DO_MAIN
-#undef L0
-#undef L1
-
-/* Fetch processors --------------------------------------------------*/
-
-typedef void vfp_begin_f(struct worker *, size_t );
-typedef int vfp_bytes_f(struct worker *, struct http_conn *, ssize_t);
-typedef int vfp_end_f(struct worker *);
-
-struct vfp {
-	vfp_begin_f	*begin;
-	vfp_bytes_f	*bytes;
-	vfp_end_f	*end;
-};
-
-extern struct vfp vfp_gunzip;
-extern struct vfp vfp_gzip;
-extern struct vfp vfp_testgzip;
-extern struct vfp vfp_esi;
-
-/*--------------------------------------------------------------------*/
-
-struct exp {
-	double			ttl;
-	double			grace;
-	double			keep;
-	double			age;
-	double			entered;
-};
-
-/*--------------------------------------------------------------------*/
-
-struct wrw {
-	int			*wfd;
-	unsigned		werr;	/* valid after WRW_Flush() */
-	struct iovec		*iov;
-	unsigned		siov;
-	unsigned		niov;
-	ssize_t			liov;
-	ssize_t			cliov;
-	unsigned		ciov;	/* Chunked header marker */
-};
-
-/*--------------------------------------------------------------------*/
-
-struct stream_ctx {
-	unsigned		magic;
-#define STREAM_CTX_MAGIC	0x8213728b
-
-	struct vgz		*vgz;
-	void			*obuf;
-	ssize_t			obuf_len;
-	ssize_t			obuf_ptr;
-
-	/* Next byte we will take from storage */
-	ssize_t			stream_next;
-
-	/* First byte of storage if we free it as we go (pass) */
-	ssize_t			stream_front;
-};
-
-/*--------------------------------------------------------------------*/
-
-struct wrk_accept {
-	unsigned		magic;
-#define WRK_ACCEPT_MAGIC	0x8c4b4d59
-
-	/* Accept stuff */
-	struct sockaddr_storage	acceptaddr;
-	socklen_t		acceptaddrlen;
-	int			acceptsock;
-	struct listen_sock	*acceptlsock;
-};
-
-/*--------------------------------------------------------------------*/
-
-struct worker {
-	unsigned		magic;
-#define WORKER_MAGIC		0x6391adcf
-	struct pool		*pool;
-	struct objhead		*nobjhead;
-	struct objcore		*nobjcore;
-	struct waitinglist	*nwaitinglist;
-	struct busyobj		*nbusyobj;
-	void			*nhashpriv;
-	struct dstat		stats;
-
-	/* Pool stuff */
-	double			lastused;
-
-	struct wrw		wrw;
-
-	pthread_cond_t		cond;
-
-	VTAILQ_ENTRY(worker)	list;
-	struct sess		*sp;
-
-	struct VCL_conf		*vcl;
-
-	uint32_t		*wlb, *wlp, *wle;
-	unsigned		wlr;
-
-	/* Lookup stuff */
-	struct SHA256Context	*sha256ctx;
-
-	struct http_conn	htc[1];
-	struct ws		ws[1];
-	struct http		*bereq;
-	struct http		*beresp;
-	struct http		*resp;
-
-	struct exp		exp;
-
-	/* This is only here so VRT can find it */
-	const char		*storage_hint;
-
-	/* Fetch stuff */
-	struct vbc		*vbc;
-	struct object		*fetch_obj;
-	enum body_status	body_status;
-	struct vfp		*vfp;
-	struct vgz		*vgz_rx;
-	struct vef_priv		*vef_priv;
-	unsigned		fetch_failed;
-	unsigned		do_stream;
-	unsigned		do_esi;
-	unsigned		do_gzip;
-	unsigned		is_gzip;
-	unsigned		do_gunzip;
-	unsigned		is_gunzip;
-	unsigned		do_close;
-	char			*h_content_length;
-
-	/* Stream state */
-	struct stream_ctx	*sctx;
-
-	/* ESI stuff */
-	struct vep_state	*vep;
-	int			gzip_resp;
-	ssize_t			l_crc;
-	uint32_t		crc;
-
-	/* Timeouts */
-	double			connect_timeout;
-	double			first_byte_timeout;
-	double			between_bytes_timeout;
-
-	/* Delivery mode */
-	unsigned		res_mode;
-#define RES_LEN			(1<<1)
-#define RES_EOF			(1<<2)
-#define RES_CHUNKED		(1<<3)
-#define RES_ESI			(1<<4)
-#define RES_ESI_CHILD		(1<<5)
-#define RES_GUNZIP		(1<<6)
-
-	/* Temporary accounting */
-	struct acct		acct_tmp;
-};
-
-/* LRU ---------------------------------------------------------------*/
-
-struct lru {
-	unsigned		magic;
-#define LRU_MAGIC		0x3fec7bb0
-	VTAILQ_HEAD(,objcore)	lru_head;
-	struct lock		mtx;
-};
-
-/* Storage -----------------------------------------------------------*/
-
-struct storage {
-	unsigned		magic;
-#define STORAGE_MAGIC		0x1a4e51c0
-
-#ifdef SENDFILE_WORKS
-	int			fd;
-	off_t			where;
-#endif
-
-	VTAILQ_ENTRY(storage)	list;
-	struct stevedore	*stevedore;
-	void			*priv;
-
-	unsigned char		*ptr;
-	unsigned		len;
-	unsigned		space;
-};
-
-/* Object core structure ---------------------------------------------
- * Objects have sideways references in the binary heap and the LRU list
- * and we want to avoid paging in a lot of objects just to move them up
- * or down the binheap or to move a unrelated object on the LRU list.
- * To avoid this we use a proxy object, objcore, to hold the relevant
- * housekeeping fields parts of an object.
- */
-
-typedef struct object *getobj_f(struct worker *wrk, struct objcore *oc);
-typedef void updatemeta_f(struct objcore *oc);
-typedef void freeobj_f(struct objcore *oc);
-typedef struct lru *getlru_f(const struct objcore *oc);
-
-struct objcore_methods {
-	getobj_f	*getobj;
-	updatemeta_f	*updatemeta;
-	freeobj_f	*freeobj;
-	getlru_f	*getlru;
-};
-
-struct objcore {
-	unsigned		magic;
-#define OBJCORE_MAGIC		0x4d301302
-	unsigned		refcnt;
-	struct objcore_methods	*methods;
-	void			*priv;
-	unsigned		priv2;
-	struct objhead		*objhead;
-	struct busyobj		*busyobj;
-	double			timer_when;
-	unsigned		flags;
-#define OC_F_BUSY		(1<<1)
-#define OC_F_PASS		(1<<2)
-#define OC_F_LRUDONTMOVE	(1<<4)
-#define OC_F_PRIV		(1<<5)		/* Stevedore private flag */
-#define OC_F_LURK		(3<<6)		/* Ban-lurker-color */
-	unsigned		timer_idx;
-	VTAILQ_ENTRY(objcore)	list;
-	VTAILQ_ENTRY(objcore)	lru_list;
-	VTAILQ_ENTRY(objcore)	ban_list;
-	struct ban		*ban;
-};
-
-static inline struct object *
-oc_getobj(struct worker *wrk, struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AZ(oc->flags & OC_F_BUSY);
-	AN(oc->methods);
-	AN(oc->methods->getobj);
-	return (oc->methods->getobj(wrk, oc));
-}
-
-static inline void
-oc_updatemeta(struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AN(oc->methods);
-	if (oc->methods->updatemeta != NULL)
-		oc->methods->updatemeta(oc);
-}
-
-static inline void
-oc_freeobj(struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AN(oc->methods);
-	AN(oc->methods->freeobj);
-	oc->methods->freeobj(oc);
-}
-
-static inline struct lru *
-oc_getlru(const struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AN(oc->methods);
-	AN(oc->methods->getlru);
-	return (oc->methods->getlru(oc));
-}
-
-/* Busy Object structure ---------------------------------------------*/
-
-struct busyobj {
-	unsigned		magic;
-#define BUSYOBJ_MAGIC		0x23b95567
-	uint8_t			*vary;
-};
-
-/* Object structure --------------------------------------------------*/
-
-VTAILQ_HEAD(storagehead, storage);
-
-struct object {
-	unsigned		magic;
-#define OBJECT_MAGIC		0x32851d42
-	unsigned		xid;
-	struct storage		*objstore;
-	struct objcore		*objcore;
-
-	struct ws		ws_o[1];
-
-	uint8_t			*vary;
-	unsigned		hits;
-	uint16_t		response;
-
-	/* XXX: make bitmap */
-	uint8_t			gziped;
-	/* Bit positions in the gzip stream */
-	ssize_t			gzip_start;
-	ssize_t			gzip_last;
-	ssize_t			gzip_stop;
-
-	ssize_t			len;
-
-	struct exp		exp;
-
-	double			last_modified;
-	double			last_lru;
-
-	struct http		*http;
-
-	struct storagehead	store;
-
-	struct storage		*esidata;
-
-	double			last_use;
-
-};
-
-/* -------------------------------------------------------------------*/
-
-struct sess {
-	unsigned		magic;
-#define SESS_MAGIC		0x2c2f9c5a
-	int			fd;
-	unsigned		vsl_id;
-	unsigned		xid;
-
-	int			restarts;
-	int			esi_level;
-	int			disable_esi;
-
-	uint8_t			hash_ignore_busy;
-	uint8_t			hash_always_miss;
-
-	struct worker		*wrk;
-
-	socklen_t		sockaddrlen;
-	socklen_t		mysockaddrlen;
-	struct sockaddr_storage	sockaddr;
-	struct sockaddr_storage	mysockaddr;
-	struct listen_sock	*mylsock;
-
-	/* formatted ascii client address */
-	char			*addr;
-	char			*port;
-	char			*client_identity;
-
-	/* HTTP request */
-	const char		*doclose;
-	struct http		*http;
-	struct http		*http0;
-
-	struct ws		ws[1];
-	char			*ws_ses;	/* WS above session data */
-	char			*ws_req;	/* WS above request data */
-
-	unsigned char		digest[DIGEST_LEN];
-
-	/* Built Vary string */
-	uint8_t			*vary_b;
-	uint8_t			*vary_l;
-	uint8_t			*vary_e;
-
-	struct http_conn	htc[1];
-
-	/* Timestamps, all on TIM_real() timescale */
-	double			t_open;
-	double			t_req;
-	double			t_resp;
-	double			t_end;
-
-	/* Acceptable grace period */
-	struct exp		exp;
-
-	enum step		step;
-	unsigned		cur_method;
-	unsigned		handling;
-	unsigned char		sendbody;
-	unsigned char		wantbody;
-	uint16_t		err_code;
-	const char		*err_reason;
-
-	VTAILQ_ENTRY(sess)	list;
-
-	struct director		*director;
-	struct object		*obj;
-	struct objcore		*objcore;
-	struct VCL_conf		*vcl;
-
-	/* The busy objhead we sleep on */
-	struct objhead		*hash_objhead;
-
-	/* Various internal stuff */
-	struct sessmem		*mem;
-
-	VTAILQ_ENTRY(sess)	poollist;
-	uint64_t		req_bodybytes;
-	struct acct		acct_ses;
-
-#if defined(HAVE_EPOLL_CTL)
-	struct epoll_event ev;
-#endif
-};
-
-/* Prototypes etc ----------------------------------------------------*/
-
-/* cache_acceptor.c */
-void VCA_Prep(struct sess *sp);
-void VCA_Init(void);
-void VCA_Shutdown(void);
-int VCA_Accept(struct listen_sock *ls, struct wrk_accept *wa);
-void VCA_SetupSess(struct worker *w);
-void VCA_FailSess(struct worker *w);
-
-/* cache_backend.c */
-void VBE_UseHealth(const struct director *vdi);
-
-struct vbc *VDI_GetFd(const struct director *, struct sess *sp);
-int VDI_Healthy(const struct director *, const struct sess *sp);
-void VDI_CloseFd(struct worker *wrk);
-void VDI_RecycleFd(struct worker *wrk);
-void VDI_AddHostHeader(const struct sess *sp);
-void VBE_Poll(void);
-
-/* cache_backend_cfg.c */
-void VBE_Init(void);
-struct backend *VBE_AddBackend(struct cli *cli, const struct vrt_backend *vb);
-
-/* cache_backend_poll.c */
-void VBP_Init(void);
-
-/* cache_ban.c */
-struct ban *BAN_New(void);
-int BAN_AddTest(struct cli *, struct ban *, const char *, const char *,
-    const char *);
-void BAN_Free(struct ban *b);
-void BAN_Insert(struct ban *b);
-void BAN_Init(void);
-void BAN_NewObjCore(struct objcore *oc);
-void BAN_DestroyObj(struct objcore *oc);
-int BAN_CheckObject(struct object *o, const struct sess *sp);
-void BAN_Reload(const uint8_t *ban, unsigned len);
-struct ban *BAN_TailRef(void);
-void BAN_Compile(void);
-struct ban *BAN_RefBan(struct objcore *oc, double t0, const struct ban *tail);
-void BAN_TailDeref(struct ban **ban);
-double BAN_Time(const struct ban *ban);
-
-/* cache_center.c [CNT] */
-void CNT_Session(struct sess *sp);
-void CNT_Init(void);
-
-/* cache_cli.c [CLI] */
-void CLI_Init(void);
-void CLI_Run(void);
-void CLI_AddFuncs(struct cli_proto *p);
-extern pthread_t cli_thread;
-#define ASSERT_CLI() do {assert(pthread_self() == cli_thread);} while (0)
-
-/* cache_expiry.c */
-void EXP_Clr(struct exp *e);
-double EXP_Get_ttl(const struct exp *e);
-double EXP_Get_grace(const struct exp *e);
-double EXP_Get_keep(const struct exp *e);
-void EXP_Set_ttl(struct exp *e, double v);
-void EXP_Set_grace(struct exp *e, double v);
-void EXP_Set_keep(struct exp *e, double v);
-
-double EXP_Ttl(const struct sess *, const struct object*);
-double EXP_Grace(const struct sess *, const struct object*);
-void EXP_Insert(struct object *o);
-void EXP_Inject(struct objcore *oc, struct lru *lru, double when);
-void EXP_Init(void);
-void EXP_Rearm(const struct object *o);
-int EXP_Touch(struct objcore *oc);
-int EXP_NukeOne(struct worker *w, struct lru *lru);
-
-/* cache_fetch.c */
-struct storage *FetchStorage(struct worker *w, ssize_t sz);
-int FetchError(struct worker *w, const char *error);
-int FetchError2(struct worker *w, const char *error, const char *more);
-int FetchHdr(struct sess *sp);
-int FetchBody(struct worker *w, struct object *obj);
-int FetchReqBody(struct sess *sp);
-void Fetch_Init(void);
-
-/* cache_gzip.c */
-struct vgz;
-
-enum vgz_flag { VGZ_NORMAL, VGZ_ALIGN, VGZ_RESET, VGZ_FINISH };
-struct vgz *VGZ_NewUngzip(struct worker *wrk, const char *id);
-struct vgz *VGZ_NewGzip(struct worker *wrk, const char *id);
-void VGZ_Ibuf(struct vgz *, const void *, ssize_t len);
-int VGZ_IbufEmpty(const struct vgz *vg);
-void VGZ_Obuf(struct vgz *, void *, ssize_t len);
-int VGZ_ObufFull(const struct vgz *vg);
-int VGZ_ObufStorage(struct worker *w, struct vgz *vg);
-int VGZ_Gzip(struct vgz *, const void **, size_t *len, enum vgz_flag);
-int VGZ_Gunzip(struct vgz *, const void **, size_t *len);
-int VGZ_Destroy(struct vgz **, int vsl_id);
-void VGZ_UpdateObj(const struct vgz*, struct object *);
-int VGZ_WrwGunzip(struct worker *w, struct vgz *, const void *ibuf,
-    ssize_t ibufl, char *obuf, ssize_t obufl, ssize_t *obufp);
-
-/* Return values */
-#define VGZ_ERROR	-1
-#define VGZ_OK		0
-#define VGZ_END		1
-#define VGZ_STUCK	2
-
-/* cache_http.c */
-unsigned HTTP_estimate(unsigned nhttp);
-void HTTP_Copy(struct http *to, const struct http * const fm);
-struct http *HTTP_create(void *p, uint16_t nhttp);
-const char *http_StatusMessage(unsigned);
-unsigned http_EstimateWS(const struct http *fm, unsigned how, uint16_t *nhd);
-void HTTP_Init(void);
-void http_ClrHeader(struct http *to);
-unsigned http_Write(struct worker *w, unsigned vsl_id, const struct http *hp,
-    int resp);
-void http_CopyResp(struct http *to, const struct http *fm);
-void http_SetResp(struct http *to, const char *proto, uint16_t status,
-    const char *response);
-void http_FilterFields(struct worker *w, unsigned vsl_id, struct http *to,
-    const struct http *fm, unsigned how);
-void http_FilterHeader(const struct sess *sp, unsigned how);
-void http_PutProtocol(struct worker *w, unsigned vsl_id, const struct http *to,
-    const char *protocol);
-void http_PutStatus(struct http *to, uint16_t status);
-void http_PutResponse(struct worker *w, unsigned vsl_id, const struct http *to,
-    const char *response);
-void http_PrintfHeader(struct worker *w, unsigned vsl_id, struct http *to,
-    const char *fmt, ...);
-void http_SetHeader(struct worker *w, unsigned vsl_id, struct http *to,
-    const char *hdr);
-void http_SetH(const struct http *to, unsigned n, const char *fm);
-void http_ForceGet(const struct http *to);
-void http_Setup(struct http *ht, struct ws *ws);
-int http_GetHdr(const struct http *hp, const char *hdr, char **ptr);
-int http_GetHdrData(const struct http *hp, const char *hdr,
-    const char *field, char **ptr);
-int http_GetHdrField(const struct http *hp, const char *hdr,
-    const char *field, char **ptr);
-double http_GetHdrQ(const struct http *hp, const char *hdr, const char *field);
-uint16_t http_GetStatus(const struct http *hp);
-const char *http_GetReq(const struct http *hp);
-int http_HdrIs(const struct http *hp, const char *hdr, const char *val);
-uint16_t http_DissectRequest(struct sess *sp);
-uint16_t http_DissectResponse(struct worker *w, const struct http_conn *htc,
-    struct http *sp);
-const char *http_DoConnection(const struct http *hp);
-void http_CopyHome(struct worker *w, unsigned vsl_id, const struct http *hp);
-void http_Unset(struct http *hp, const char *hdr);
-void http_CollectHdr(struct http *hp, const char *hdr);
-
-/* cache_httpconn.c */
-void HTC_Init(struct http_conn *htc, struct ws *ws, int fd, unsigned vsl_id,
-    unsigned maxbytes, unsigned maxhdr);
-int HTC_Reinit(struct http_conn *htc);
-int HTC_Rx(struct http_conn *htc);
-ssize_t HTC_Read(struct worker *w, struct http_conn *htc, void *d, size_t len);
-int HTC_Complete(struct http_conn *htc);
-
-#define HTTPH(a, b, c, d, e, f, g) extern char b[];
-#include "tbl/http_headers.h"
-#undef HTTPH
-
-/* cache_main.c */
-void THR_SetName(const char *name);
-const char* THR_GetName(void);
-void THR_SetSession(const struct sess *sp);
-const struct sess * THR_GetSession(void);
-
-/* cache_lck.c */
-
-/* Internal functions, call only through macros below */
-void Lck__Lock(struct lock *lck, const char *p, const char *f, int l);
-void Lck__Unlock(struct lock *lck, const char *p, const char *f, int l);
-int Lck__Trylock(struct lock *lck, const char *p, const char *f, int l);
-void Lck__New(struct lock *lck, struct VSC_C_lck *, const char *);
-void Lck__Assert(const struct lock *lck, int held);
-
-/* public interface: */
-void LCK_Init(void);
-void Lck_Delete(struct lock *lck);
-int Lck_CondWait(pthread_cond_t *cond, struct lock *lck, struct timespec *ts);
-
-#define Lck_New(a, b) Lck__New(a, b, #b)
-#define Lck_Lock(a) Lck__Lock(a, __func__, __FILE__, __LINE__)
-#define Lck_Unlock(a) Lck__Unlock(a, __func__, __FILE__, __LINE__)
-#define Lck_Trylock(a) Lck__Trylock(a, __func__, __FILE__, __LINE__)
-#define Lck_AssertHeld(a) Lck__Assert(a, 1)
-
-#define LOCK(nam) extern struct VSC_C_lck *lck_##nam;
-#include "tbl/locks.h"
-#undef LOCK
-
-/* cache_panic.c */
-void PAN_Init(void);
-
-/* cache_pipe.c */
-void PipeSession(struct sess *sp);
-
-/* cache_pool.c */
-void Pool_Init(void);
-void Pool_Work_Thread(void *priv, struct worker *w);
-void Pool_Wait(struct sess *sp);
-int Pool_Schedule(struct pool *pp, struct sess *sp);
-
-#define WRW_IsReleased(w)	((w)->wrw.wfd == NULL)
-int WRW_Error(const struct worker *w);
-void WRW_Chunked(struct worker *w);
-void WRW_EndChunk(struct worker *w);
-void WRW_Reserve(struct worker *w, int *fd);
-unsigned WRW_Flush(struct worker *w);
-unsigned WRW_FlushRelease(struct worker *w);
-unsigned WRW_Write(struct worker *w, const void *ptr, int len);
-unsigned WRW_WriteH(struct worker *w, const txt *hh, const char *suf);
-#ifdef SENDFILE_WORKS
-void WRW_Sendfile(struct worker *w, int fd, off_t off, unsigned len);
-#endif  /* SENDFILE_WORKS */
-
-/* cache_session.c [SES] */
-struct sess *SES_New(struct worker *wrk, struct sesspool *pp);
-struct sess *SES_Alloc(void);
-void SES_Close(struct sess *sp, const char *reason);
-void SES_Delete(struct sess *sp, const char *reason);
-void SES_Charge(struct sess *sp);
-struct sesspool *SES_NewPool(struct pool *pp);
-void SES_DeletePool(struct sesspool *sp, struct worker *wrk);
-int SES_Schedule(struct sess *sp);
-
-
-/* cache_shmlog.c */
-void VSL_Init(void);
-void *VSM_Alloc(unsigned size, const char *class, const char *type,
-    const char *ident);
-void VSM_Free(const void *ptr);
-#ifdef VSL_ENDMARKER
-void VSL(enum VSL_tag_e tag, int id, const char *fmt, ...);
-void WSLR(struct worker *w, enum VSL_tag_e tag, int id, txt t);
-void WSL(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, ...);
-void WSLB(struct worker *w, enum VSL_tag_e tag, const char *fmt, ...);
-
-void WSL_Flush(struct worker *w, int overflow);
-
-#define DSL(flag, tag, id, ...)					\
-	do {							\
-		if (cache_param->diag_bitmap & (flag))		\
-			VSL((tag), (id), __VA_ARGS__);		\
-	} while (0)
-
-#define WSP(sess, tag, ...)					\
-	WSL((sess)->wrk, tag, (sess)->vsl_id, __VA_ARGS__)
-
-#define WSPR(sess, tag, txt)					\
-	WSLR((sess)->wrk, tag, (sess)->vsl_id, txt)
-
-#define INCOMPL() do {							\
-	VSL(SLT_Debug, 0, "INCOMPLETE AT: %s(%d)", __func__, __LINE__); \
-	fprintf(stderr,							\
-	    "INCOMPLETE AT: %s(%d)\n",					\
-	    (const char *)__func__, __LINE__);				\
-	abort();							\
-	} while (0)
-#endif
-
-/* cache_response.c */
-void RES_BuildHttp(const struct sess *sp);
-void RES_WriteObj(struct sess *sp);
-void RES_StreamStart(struct sess *sp);
-void RES_StreamEnd(struct sess *sp);
-void RES_StreamPoll(struct worker *);
-
-/* cache_vary.c */
-struct vsb *VRY_Create(const struct sess *sp, const struct http *hp);
-int VRY_Match(struct sess *sp, const uint8_t *vary);
-void VRY_Validate(const uint8_t *vary);
-
-/* cache_vcl.c */
-void VCL_Init(void);
-void VCL_Refresh(struct VCL_conf **vcc);
-void VCL_Rel(struct VCL_conf **vcc);
-void VCL_Poll(void);
-const char *VCL_Return_Name(unsigned method);
-
-#define VCL_MET_MAC(l,u,b) void VCL_##l##_method(struct sess *);
-#include "tbl/vcl_returns.h"
-#undef VCL_MET_MAC
-
-/* cache_vrt.c */
-
-char *VRT_String(struct ws *ws, const char *h, const char *p, va_list ap);
-char *VRT_StringList(char *d, unsigned dl, const char *p, va_list ap);
-
-void ESI_Deliver(struct sess *);
-void ESI_DeliverChild(const struct sess *);
-
-/* cache_vrt_vmod.c */
-void VMOD_Init(void);
-
-/* cache_wrk.c */
-
-void WRK_Init(void);
-int WRK_TrySumStat(struct worker *w);
-void WRK_SumStat(struct worker *w);
-void *WRK_thread(void *priv);
-typedef void *bgthread_t(struct sess *, void *priv);
-void WRK_BgThread(pthread_t *thr, const char *name, bgthread_t *func,
-    void *priv);
-
-/* cache_ws.c */
-
-void WS_Init(struct ws *ws, const char *id, void *space, unsigned len);
-unsigned WS_Reserve(struct ws *ws, unsigned bytes);
-void WS_Release(struct ws *ws, unsigned bytes);
-void WS_ReleaseP(struct ws *ws, char *ptr);
-void WS_Assert(const struct ws *ws);
-void WS_Reset(struct ws *ws, char *p);
-char *WS_Alloc(struct ws *ws, unsigned bytes);
-char *WS_Dup(struct ws *ws, const char *);
-char *WS_Snapshot(struct ws *ws);
-unsigned WS_Free(const struct ws *ws);
-
-/* rfc2616.c */
-void RFC2616_Ttl(const struct sess *sp);
-enum body_status RFC2616_Body(const struct sess *sp);
-unsigned RFC2616_Req_Gzip(const struct sess *sp);
-int RFC2616_Do_Cond(const struct sess *sp);
-
-/* stevedore.c */
-struct object *STV_NewObject(struct sess *sp, const char *hint, unsigned len,
-    struct exp *, uint16_t nhttp);
-struct storage *STV_alloc(struct worker *w, size_t size);
-void STV_trim(struct storage *st, size_t size);
-void STV_free(struct storage *st);
-void STV_open(void);
-void STV_close(void);
-void STV_Freestore(struct object *o);
-
-/* storage_synth.c */
-struct vsb *SMS_Makesynth(struct object *obj);
-void SMS_Finish(struct object *obj);
-void SMS_Init(void);
-
-/* storage_persistent.c */
-void SMP_Init(void);
-void SMP_Ready(void);
-void SMP_NewBan(const uint8_t *ban, unsigned len);
-
-/*
- * A normal pointer difference is signed, but we never want a negative value
- * so this little tool will make sure we don't get that.
- */
-
-static inline unsigned
-pdiff(const void *b, const void *e)
-{
-
-	assert(b <= e);
-	return
-	    ((unsigned)((const unsigned char *)e - (const unsigned char *)b));
-}
-
-static inline void
-Tcheck(const txt t)
-{
-
-	AN(t.b);
-	AN(t.e);
-	assert(t.b <= t.e);
-}
-
-/*
- * unsigned length of a txt
- */
-
-static inline unsigned
-Tlen(const txt t)
-{
-
-	Tcheck(t);
-	return ((unsigned)(t.e - t.b));
-}
-
-static inline void
-Tadd(txt *t, const char *p, int l)
-{
-	Tcheck(*t);
-
-	if (l <= 0) {
-	} if (t->b + l < t->e) {
-		memcpy(t->b, p, l);
-		t->b += l;
-	} else {
-		t->b = t->e;
-	}
-}
-
-static inline void
-AssertObjBusy(const struct object *o)
-{
-	AN(o->objcore);
-	AN (o->objcore->flags & OC_F_BUSY);
-}
-
-static inline void
-AssertObjCorePassOrBusy(const struct objcore *oc)
-{
-	if (oc != NULL)
-		AN (oc->flags & OC_F_BUSY);
-}
diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
new file mode 100644
index 0000000..653fe77
--- /dev/null
+++ b/bin/varnishd/cache/cache.h
@@ -0,0 +1,1039 @@
+/*-
+ * 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.
+ *
+ */
+
+/*
+ * This macro can be used in .h files to isolate bits that the manager
+ * should not (need to) see, such as pthread mutexes etc.
+ */
+#define VARNISH_CACHE_CHILD	1
+
+#include "common/common.h"
+
+#include "vapi/vsc_int.h"
+#include "vapi/vsl_int.h"
+
+#include <sys/socket.h>
+
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+
+#if defined(HAVE_EPOLL_CTL)
+#include <sys/epoll.h>
+#endif
+
+
+#include "common/params.h"
+
+enum body_status {
+#define BODYSTATUS(U,l)	BS_##U,
+#include "tbl/body_status.h"
+#undef BODYSTATUS
+};
+
+static inline const char *
+body_status(enum body_status e)
+{
+	switch(e) {
+#define BODYSTATUS(U,l)	case BS_##U: return (#l);
+#include "tbl/body_status.h"
+#undef BODYSTATUS
+	default:
+		return ("?");
+	}
+}
+
+/*
+ * NB: HDR_STATUS is only used in cache_http.c, everybody else uses the
+ * http->status integer field.
+ */
+
+enum {
+	/* Fields from the first line of HTTP proto */
+	HTTP_HDR_REQ,
+	HTTP_HDR_URL,
+	HTTP_HDR_PROTO,
+	HTTP_HDR_STATUS,
+	HTTP_HDR_RESPONSE,
+	/* HTTP header lines */
+	HTTP_HDR_FIRST,
+};
+
+struct SHA256Context;
+struct VSC_C_lck;
+struct ban;
+struct busyobj;
+struct cli;
+struct cli_proto;
+struct director;
+struct iovec;
+struct objcore;
+struct object;
+struct objhead;
+struct pool;
+struct sess;
+struct sesspool;
+struct vbc;
+struct vef_priv;
+struct vrt_backend;
+struct vsb;
+struct waitinglist;
+struct worker;
+
+#define DIGEST_LEN		32
+
+/* Name of transient storage */
+#define TRANSIENT_STORAGE	"Transient"
+
+/*--------------------------------------------------------------------
+ * Pointer aligment magic
+ */
+
+#define PALGN		(sizeof(void *) - 1)
+#define PAOK(p)		(((uintptr_t)(p) & PALGN) == 0)
+#define PRNDDN(p)	((uintptr_t)(p) & ~PALGN)
+#define PRNDUP(p)	(((uintptr_t)(p) + PALGN) & ~PALGN)
+
+/*--------------------------------------------------------------------*/
+
+typedef struct {
+	char			*b;
+	char			*e;
+} txt;
+
+/*--------------------------------------------------------------------*/
+
+enum step {
+#define STEP(l, u)	STP_##u,
+#include "tbl/steps.h"
+#undef STEP
+};
+
+/*--------------------------------------------------------------------
+ * Workspace structure for quick memory allocation.
+ */
+
+struct ws {
+	unsigned		magic;
+#define WS_MAGIC		0x35fac554
+	unsigned		overflow;	/* workspace overflowed */
+	const char		*id;		/* identity */
+	char			*s;		/* (S)tart of buffer */
+	char			*f;		/* (F)ree pointer */
+	char			*r;		/* (R)eserved length */
+	char			*e;		/* (E)nd of buffer */
+};
+
+/*--------------------------------------------------------------------
+ * HTTP Request/Response/Header handling structure.
+ */
+
+enum httpwhence {
+	HTTP_Rx	 = 1,
+	HTTP_Tx  = 2,
+	HTTP_Obj = 3
+};
+
+/* NB: remember to update http_Copy() if you add fields */
+struct http {
+	unsigned		magic;
+#define HTTP_MAGIC		0x6428b5c9
+
+	enum httpwhence		logtag;
+
+	struct ws		*ws;
+	txt			*hd;
+	unsigned char		*hdf;
+#define HDF_FILTER		(1 << 0)	/* Filtered by Connection */
+	uint16_t		shd;		/* Size of hd space */
+	uint16_t		nhd;		/* Next free hd */
+	uint16_t		status;
+	uint8_t			protover;
+	uint8_t			conds;		/* If-* headers present */
+};
+
+/*--------------------------------------------------------------------
+ * HTTP Protocol connection structure
+ */
+
+struct http_conn {
+	unsigned		magic;
+#define HTTP_CONN_MAGIC		0x3e19edd1
+
+	int			fd;
+	unsigned		vsl_id;
+	unsigned		maxbytes;
+	unsigned		maxhdr;
+	struct ws		*ws;
+	txt			rxbuf;
+	txt			pipeline;
+};
+
+/*--------------------------------------------------------------------*/
+
+struct acct {
+	double			first;
+#define ACCT(foo)	uint64_t	foo;
+#include "tbl/acct_fields.h"
+#undef ACCT
+};
+
+/*--------------------------------------------------------------------*/
+
+#define L0(t, n)
+#define L1(t, n)		t n;
+#define VSC_F(n, t, l, f, e,d)	L##l(t, n)
+#define VSC_DO_MAIN
+struct dstat {
+#include "tbl/vsc_fields.h"
+};
+#undef VSC_F
+#undef VSC_DO_MAIN
+#undef L0
+#undef L1
+
+/* Fetch processors --------------------------------------------------*/
+
+typedef void vfp_begin_f(struct worker *, size_t );
+typedef int vfp_bytes_f(struct worker *, struct http_conn *, ssize_t);
+typedef int vfp_end_f(struct worker *);
+
+struct vfp {
+	vfp_begin_f	*begin;
+	vfp_bytes_f	*bytes;
+	vfp_end_f	*end;
+};
+
+extern struct vfp vfp_gunzip;
+extern struct vfp vfp_gzip;
+extern struct vfp vfp_testgzip;
+extern struct vfp vfp_esi;
+
+/*--------------------------------------------------------------------*/
+
+struct exp {
+	double			ttl;
+	double			grace;
+	double			keep;
+	double			age;
+	double			entered;
+};
+
+/*--------------------------------------------------------------------*/
+
+struct wrw {
+	int			*wfd;
+	unsigned		werr;	/* valid after WRW_Flush() */
+	struct iovec		*iov;
+	unsigned		siov;
+	unsigned		niov;
+	ssize_t			liov;
+	ssize_t			cliov;
+	unsigned		ciov;	/* Chunked header marker */
+};
+
+/*--------------------------------------------------------------------*/
+
+struct stream_ctx {
+	unsigned		magic;
+#define STREAM_CTX_MAGIC	0x8213728b
+
+	struct vgz		*vgz;
+	void			*obuf;
+	ssize_t			obuf_len;
+	ssize_t			obuf_ptr;
+
+	/* Next byte we will take from storage */
+	ssize_t			stream_next;
+
+	/* First byte of storage if we free it as we go (pass) */
+	ssize_t			stream_front;
+};
+
+/*--------------------------------------------------------------------*/
+
+struct wrk_accept {
+	unsigned		magic;
+#define WRK_ACCEPT_MAGIC	0x8c4b4d59
+
+	/* Accept stuff */
+	struct sockaddr_storage	acceptaddr;
+	socklen_t		acceptaddrlen;
+	int			acceptsock;
+	struct listen_sock	*acceptlsock;
+};
+
+/*--------------------------------------------------------------------*/
+
+struct worker {
+	unsigned		magic;
+#define WORKER_MAGIC		0x6391adcf
+	struct pool		*pool;
+	struct objhead		*nobjhead;
+	struct objcore		*nobjcore;
+	struct waitinglist	*nwaitinglist;
+	struct busyobj		*nbusyobj;
+	void			*nhashpriv;
+	struct dstat		stats;
+
+	/* Pool stuff */
+	double			lastused;
+
+	struct wrw		wrw;
+
+	pthread_cond_t		cond;
+
+	VTAILQ_ENTRY(worker)	list;
+	struct sess		*sp;
+
+	struct VCL_conf		*vcl;
+
+	uint32_t		*wlb, *wlp, *wle;
+	unsigned		wlr;
+
+	/* Lookup stuff */
+	struct SHA256Context	*sha256ctx;
+
+	struct http_conn	htc[1];
+	struct ws		ws[1];
+	struct http		*bereq;
+	struct http		*beresp;
+	struct http		*resp;
+
+	struct exp		exp;
+
+	/* This is only here so VRT can find it */
+	const char		*storage_hint;
+
+	/* Fetch stuff */
+	struct vbc		*vbc;
+	struct object		*fetch_obj;
+	enum body_status	body_status;
+	struct vfp		*vfp;
+	struct vgz		*vgz_rx;
+	struct vef_priv		*vef_priv;
+	unsigned		fetch_failed;
+	unsigned		do_stream;
+	unsigned		do_esi;
+	unsigned		do_gzip;
+	unsigned		is_gzip;
+	unsigned		do_gunzip;
+	unsigned		is_gunzip;
+	unsigned		do_close;
+	char			*h_content_length;
+
+	/* Stream state */
+	struct stream_ctx	*sctx;
+
+	/* ESI stuff */
+	struct vep_state	*vep;
+	int			gzip_resp;
+	ssize_t			l_crc;
+	uint32_t		crc;
+
+	/* Timeouts */
+	double			connect_timeout;
+	double			first_byte_timeout;
+	double			between_bytes_timeout;
+
+	/* Delivery mode */
+	unsigned		res_mode;
+#define RES_LEN			(1<<1)
+#define RES_EOF			(1<<2)
+#define RES_CHUNKED		(1<<3)
+#define RES_ESI			(1<<4)
+#define RES_ESI_CHILD		(1<<5)
+#define RES_GUNZIP		(1<<6)
+
+	/* Temporary accounting */
+	struct acct		acct_tmp;
+};
+
+/* LRU ---------------------------------------------------------------*/
+
+struct lru {
+	unsigned		magic;
+#define LRU_MAGIC		0x3fec7bb0
+	VTAILQ_HEAD(,objcore)	lru_head;
+	struct lock		mtx;
+};
+
+/* Storage -----------------------------------------------------------*/
+
+struct storage {
+	unsigned		magic;
+#define STORAGE_MAGIC		0x1a4e51c0
+
+#ifdef SENDFILE_WORKS
+	int			fd;
+	off_t			where;
+#endif
+
+	VTAILQ_ENTRY(storage)	list;
+	struct stevedore	*stevedore;
+	void			*priv;
+
+	unsigned char		*ptr;
+	unsigned		len;
+	unsigned		space;
+};
+
+/* Object core structure ---------------------------------------------
+ * Objects have sideways references in the binary heap and the LRU list
+ * and we want to avoid paging in a lot of objects just to move them up
+ * or down the binheap or to move a unrelated object on the LRU list.
+ * To avoid this we use a proxy object, objcore, to hold the relevant
+ * housekeeping fields parts of an object.
+ */
+
+typedef struct object *getobj_f(struct worker *wrk, struct objcore *oc);
+typedef void updatemeta_f(struct objcore *oc);
+typedef void freeobj_f(struct objcore *oc);
+typedef struct lru *getlru_f(const struct objcore *oc);
+
+struct objcore_methods {
+	getobj_f	*getobj;
+	updatemeta_f	*updatemeta;
+	freeobj_f	*freeobj;
+	getlru_f	*getlru;
+};
+
+struct objcore {
+	unsigned		magic;
+#define OBJCORE_MAGIC		0x4d301302
+	unsigned		refcnt;
+	struct objcore_methods	*methods;
+	void			*priv;
+	unsigned		priv2;
+	struct objhead		*objhead;
+	struct busyobj		*busyobj;
+	double			timer_when;
+	unsigned		flags;
+#define OC_F_BUSY		(1<<1)
+#define OC_F_PASS		(1<<2)
+#define OC_F_LRUDONTMOVE	(1<<4)
+#define OC_F_PRIV		(1<<5)		/* Stevedore private flag */
+#define OC_F_LURK		(3<<6)		/* Ban-lurker-color */
+	unsigned		timer_idx;
+	VTAILQ_ENTRY(objcore)	list;
+	VTAILQ_ENTRY(objcore)	lru_list;
+	VTAILQ_ENTRY(objcore)	ban_list;
+	struct ban		*ban;
+};
+
+static inline struct object *
+oc_getobj(struct worker *wrk, struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AZ(oc->flags & OC_F_BUSY);
+	AN(oc->methods);
+	AN(oc->methods->getobj);
+	return (oc->methods->getobj(wrk, oc));
+}
+
+static inline void
+oc_updatemeta(struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AN(oc->methods);
+	if (oc->methods->updatemeta != NULL)
+		oc->methods->updatemeta(oc);
+}
+
+static inline void
+oc_freeobj(struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AN(oc->methods);
+	AN(oc->methods->freeobj);
+	oc->methods->freeobj(oc);
+}
+
+static inline struct lru *
+oc_getlru(const struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AN(oc->methods);
+	AN(oc->methods->getlru);
+	return (oc->methods->getlru(oc));
+}
+
+/* Busy Object structure ---------------------------------------------*/
+
+struct busyobj {
+	unsigned		magic;
+#define BUSYOBJ_MAGIC		0x23b95567
+	uint8_t			*vary;
+};
+
+/* Object structure --------------------------------------------------*/
+
+VTAILQ_HEAD(storagehead, storage);
+
+struct object {
+	unsigned		magic;
+#define OBJECT_MAGIC		0x32851d42
+	unsigned		xid;
+	struct storage		*objstore;
+	struct objcore		*objcore;
+
+	struct ws		ws_o[1];
+
+	uint8_t			*vary;
+	unsigned		hits;
+	uint16_t		response;
+
+	/* XXX: make bitmap */
+	uint8_t			gziped;
+	/* Bit positions in the gzip stream */
+	ssize_t			gzip_start;
+	ssize_t			gzip_last;
+	ssize_t			gzip_stop;
+
+	ssize_t			len;
+
+	struct exp		exp;
+
+	double			last_modified;
+	double			last_lru;
+
+	struct http		*http;
+
+	struct storagehead	store;
+
+	struct storage		*esidata;
+
+	double			last_use;
+
+};
+
+/* -------------------------------------------------------------------*/
+
+struct sess {
+	unsigned		magic;
+#define SESS_MAGIC		0x2c2f9c5a
+	int			fd;
+	unsigned		vsl_id;
+	unsigned		xid;
+
+	int			restarts;
+	int			esi_level;
+	int			disable_esi;
+
+	uint8_t			hash_ignore_busy;
+	uint8_t			hash_always_miss;
+
+	struct worker		*wrk;
+
+	socklen_t		sockaddrlen;
+	socklen_t		mysockaddrlen;
+	struct sockaddr_storage	sockaddr;
+	struct sockaddr_storage	mysockaddr;
+	struct listen_sock	*mylsock;
+
+	/* formatted ascii client address */
+	char			*addr;
+	char			*port;
+	char			*client_identity;
+
+	/* HTTP request */
+	const char		*doclose;
+	struct http		*http;
+	struct http		*http0;
+
+	struct ws		ws[1];
+	char			*ws_ses;	/* WS above session data */
+	char			*ws_req;	/* WS above request data */
+
+	unsigned char		digest[DIGEST_LEN];
+
+	/* Built Vary string */
+	uint8_t			*vary_b;
+	uint8_t			*vary_l;
+	uint8_t			*vary_e;
+
+	struct http_conn	htc[1];
+
+	/* Timestamps, all on TIM_real() timescale */
+	double			t_open;
+	double			t_req;
+	double			t_resp;
+	double			t_end;
+
+	/* Acceptable grace period */
+	struct exp		exp;
+
+	enum step		step;
+	unsigned		cur_method;
+	unsigned		handling;
+	unsigned char		sendbody;
+	unsigned char		wantbody;
+	uint16_t		err_code;
+	const char		*err_reason;
+
+	VTAILQ_ENTRY(sess)	list;
+
+	struct director		*director;
+	struct object		*obj;
+	struct objcore		*objcore;
+	struct VCL_conf		*vcl;
+
+	/* The busy objhead we sleep on */
+	struct objhead		*hash_objhead;
+
+	/* Various internal stuff */
+	struct sessmem		*mem;
+
+	VTAILQ_ENTRY(sess)	poollist;
+	uint64_t		req_bodybytes;
+	struct acct		acct_ses;
+
+#if defined(HAVE_EPOLL_CTL)
+	struct epoll_event ev;
+#endif
+};
+
+/* Prototypes etc ----------------------------------------------------*/
+
+/* cache_acceptor.c */
+void VCA_Prep(struct sess *sp);
+void VCA_Init(void);
+void VCA_Shutdown(void);
+int VCA_Accept(struct listen_sock *ls, struct wrk_accept *wa);
+void VCA_SetupSess(struct worker *w);
+void VCA_FailSess(struct worker *w);
+
+/* cache_backend.c */
+void VBE_UseHealth(const struct director *vdi);
+
+struct vbc *VDI_GetFd(const struct director *, struct sess *sp);
+int VDI_Healthy(const struct director *, const struct sess *sp);
+void VDI_CloseFd(struct worker *wrk);
+void VDI_RecycleFd(struct worker *wrk);
+void VDI_AddHostHeader(const struct sess *sp);
+void VBE_Poll(void);
+
+/* cache_backend_cfg.c */
+void VBE_Init(void);
+struct backend *VBE_AddBackend(struct cli *cli, const struct vrt_backend *vb);
+
+/* cache_backend_poll.c */
+void VBP_Init(void);
+
+/* cache_ban.c */
+struct ban *BAN_New(void);
+int BAN_AddTest(struct cli *, struct ban *, const char *, const char *,
+    const char *);
+void BAN_Free(struct ban *b);
+void BAN_Insert(struct ban *b);
+void BAN_Init(void);
+void BAN_NewObjCore(struct objcore *oc);
+void BAN_DestroyObj(struct objcore *oc);
+int BAN_CheckObject(struct object *o, const struct sess *sp);
+void BAN_Reload(const uint8_t *ban, unsigned len);
+struct ban *BAN_TailRef(void);
+void BAN_Compile(void);
+struct ban *BAN_RefBan(struct objcore *oc, double t0, const struct ban *tail);
+void BAN_TailDeref(struct ban **ban);
+double BAN_Time(const struct ban *ban);
+
+/* cache_center.c [CNT] */
+void CNT_Session(struct sess *sp);
+void CNT_Init(void);
+
+/* cache_cli.c [CLI] */
+void CLI_Init(void);
+void CLI_Run(void);
+void CLI_AddFuncs(struct cli_proto *p);
+extern pthread_t cli_thread;
+#define ASSERT_CLI() do {assert(pthread_self() == cli_thread);} while (0)
+
+/* cache_expiry.c */
+void EXP_Clr(struct exp *e);
+double EXP_Get_ttl(const struct exp *e);
+double EXP_Get_grace(const struct exp *e);
+double EXP_Get_keep(const struct exp *e);
+void EXP_Set_ttl(struct exp *e, double v);
+void EXP_Set_grace(struct exp *e, double v);
+void EXP_Set_keep(struct exp *e, double v);
+
+double EXP_Ttl(const struct sess *, const struct object*);
+double EXP_Grace(const struct sess *, const struct object*);
+void EXP_Insert(struct object *o);
+void EXP_Inject(struct objcore *oc, struct lru *lru, double when);
+void EXP_Init(void);
+void EXP_Rearm(const struct object *o);
+int EXP_Touch(struct objcore *oc);
+int EXP_NukeOne(struct worker *w, struct lru *lru);
+
+/* cache_fetch.c */
+struct storage *FetchStorage(struct worker *w, ssize_t sz);
+int FetchError(struct worker *w, const char *error);
+int FetchError2(struct worker *w, const char *error, const char *more);
+int FetchHdr(struct sess *sp);
+int FetchBody(struct worker *w, struct object *obj);
+int FetchReqBody(struct sess *sp);
+void Fetch_Init(void);
+
+/* cache_gzip.c */
+struct vgz;
+
+enum vgz_flag { VGZ_NORMAL, VGZ_ALIGN, VGZ_RESET, VGZ_FINISH };
+struct vgz *VGZ_NewUngzip(struct worker *wrk, const char *id);
+struct vgz *VGZ_NewGzip(struct worker *wrk, const char *id);
+void VGZ_Ibuf(struct vgz *, const void *, ssize_t len);
+int VGZ_IbufEmpty(const struct vgz *vg);
+void VGZ_Obuf(struct vgz *, void *, ssize_t len);
+int VGZ_ObufFull(const struct vgz *vg);
+int VGZ_ObufStorage(struct worker *w, struct vgz *vg);
+int VGZ_Gzip(struct vgz *, const void **, size_t *len, enum vgz_flag);
+int VGZ_Gunzip(struct vgz *, const void **, size_t *len);
+int VGZ_Destroy(struct vgz **, int vsl_id);
+void VGZ_UpdateObj(const struct vgz*, struct object *);
+int VGZ_WrwGunzip(struct worker *w, struct vgz *, const void *ibuf,
+    ssize_t ibufl, char *obuf, ssize_t obufl, ssize_t *obufp);
+
+/* Return values */
+#define VGZ_ERROR	-1
+#define VGZ_OK		0
+#define VGZ_END		1
+#define VGZ_STUCK	2
+
+/* cache_http.c */
+unsigned HTTP_estimate(unsigned nhttp);
+void HTTP_Copy(struct http *to, const struct http * const fm);
+struct http *HTTP_create(void *p, uint16_t nhttp);
+const char *http_StatusMessage(unsigned);
+unsigned http_EstimateWS(const struct http *fm, unsigned how, uint16_t *nhd);
+void HTTP_Init(void);
+void http_ClrHeader(struct http *to);
+unsigned http_Write(struct worker *w, unsigned vsl_id, const struct http *hp,
+    int resp);
+void http_CopyResp(struct http *to, const struct http *fm);
+void http_SetResp(struct http *to, const char *proto, uint16_t status,
+    const char *response);
+void http_FilterFields(struct worker *w, unsigned vsl_id, struct http *to,
+    const struct http *fm, unsigned how);
+void http_FilterHeader(const struct sess *sp, unsigned how);
+void http_PutProtocol(struct worker *w, unsigned vsl_id, const struct http *to,
+    const char *protocol);
+void http_PutStatus(struct http *to, uint16_t status);
+void http_PutResponse(struct worker *w, unsigned vsl_id, const struct http *to,
+    const char *response);
+void http_PrintfHeader(struct worker *w, unsigned vsl_id, struct http *to,
+    const char *fmt, ...);
+void http_SetHeader(struct worker *w, unsigned vsl_id, struct http *to,
+    const char *hdr);
+void http_SetH(const struct http *to, unsigned n, const char *fm);
+void http_ForceGet(const struct http *to);
+void http_Setup(struct http *ht, struct ws *ws);
+int http_GetHdr(const struct http *hp, const char *hdr, char **ptr);
+int http_GetHdrData(const struct http *hp, const char *hdr,
+    const char *field, char **ptr);
+int http_GetHdrField(const struct http *hp, const char *hdr,
+    const char *field, char **ptr);
+double http_GetHdrQ(const struct http *hp, const char *hdr, const char *field);
+uint16_t http_GetStatus(const struct http *hp);
+const char *http_GetReq(const struct http *hp);
+int http_HdrIs(const struct http *hp, const char *hdr, const char *val);
+uint16_t http_DissectRequest(struct sess *sp);
+uint16_t http_DissectResponse(struct worker *w, const struct http_conn *htc,
+    struct http *sp);
+const char *http_DoConnection(const struct http *hp);
+void http_CopyHome(struct worker *w, unsigned vsl_id, const struct http *hp);
+void http_Unset(struct http *hp, const char *hdr);
+void http_CollectHdr(struct http *hp, const char *hdr);
+
+/* cache_httpconn.c */
+void HTC_Init(struct http_conn *htc, struct ws *ws, int fd, unsigned vsl_id,
+    unsigned maxbytes, unsigned maxhdr);
+int HTC_Reinit(struct http_conn *htc);
+int HTC_Rx(struct http_conn *htc);
+ssize_t HTC_Read(struct worker *w, struct http_conn *htc, void *d, size_t len);
+int HTC_Complete(struct http_conn *htc);
+
+#define HTTPH(a, b, c, d, e, f, g) extern char b[];
+#include "tbl/http_headers.h"
+#undef HTTPH
+
+/* cache_main.c */
+void THR_SetName(const char *name);
+const char* THR_GetName(void);
+void THR_SetSession(const struct sess *sp);
+const struct sess * THR_GetSession(void);
+
+/* cache_lck.c */
+
+/* Internal functions, call only through macros below */
+void Lck__Lock(struct lock *lck, const char *p, const char *f, int l);
+void Lck__Unlock(struct lock *lck, const char *p, const char *f, int l);
+int Lck__Trylock(struct lock *lck, const char *p, const char *f, int l);
+void Lck__New(struct lock *lck, struct VSC_C_lck *, const char *);
+void Lck__Assert(const struct lock *lck, int held);
+
+/* public interface: */
+void LCK_Init(void);
+void Lck_Delete(struct lock *lck);
+int Lck_CondWait(pthread_cond_t *cond, struct lock *lck, struct timespec *ts);
+
+#define Lck_New(a, b) Lck__New(a, b, #b)
+#define Lck_Lock(a) Lck__Lock(a, __func__, __FILE__, __LINE__)
+#define Lck_Unlock(a) Lck__Unlock(a, __func__, __FILE__, __LINE__)
+#define Lck_Trylock(a) Lck__Trylock(a, __func__, __FILE__, __LINE__)
+#define Lck_AssertHeld(a) Lck__Assert(a, 1)
+
+#define LOCK(nam) extern struct VSC_C_lck *lck_##nam;
+#include "tbl/locks.h"
+#undef LOCK
+
+/* cache_panic.c */
+void PAN_Init(void);
+
+/* cache_pipe.c */
+void PipeSession(struct sess *sp);
+
+/* cache_pool.c */
+void Pool_Init(void);
+void Pool_Work_Thread(void *priv, struct worker *w);
+void Pool_Wait(struct sess *sp);
+int Pool_Schedule(struct pool *pp, struct sess *sp);
+
+#define WRW_IsReleased(w)	((w)->wrw.wfd == NULL)
+int WRW_Error(const struct worker *w);
+void WRW_Chunked(struct worker *w);
+void WRW_EndChunk(struct worker *w);
+void WRW_Reserve(struct worker *w, int *fd);
+unsigned WRW_Flush(struct worker *w);
+unsigned WRW_FlushRelease(struct worker *w);
+unsigned WRW_Write(struct worker *w, const void *ptr, int len);
+unsigned WRW_WriteH(struct worker *w, const txt *hh, const char *suf);
+#ifdef SENDFILE_WORKS
+void WRW_Sendfile(struct worker *w, int fd, off_t off, unsigned len);
+#endif  /* SENDFILE_WORKS */
+
+/* cache_session.c [SES] */
+struct sess *SES_New(struct worker *wrk, struct sesspool *pp);
+struct sess *SES_Alloc(void);
+void SES_Close(struct sess *sp, const char *reason);
+void SES_Delete(struct sess *sp, const char *reason);
+void SES_Charge(struct sess *sp);
+struct sesspool *SES_NewPool(struct pool *pp);
+void SES_DeletePool(struct sesspool *sp, struct worker *wrk);
+int SES_Schedule(struct sess *sp);
+
+
+/* cache_shmlog.c */
+void VSL_Init(void);
+void *VSM_Alloc(unsigned size, const char *class, const char *type,
+    const char *ident);
+void VSM_Free(const void *ptr);
+#ifdef VSL_ENDMARKER
+void VSL(enum VSL_tag_e tag, int id, const char *fmt, ...);
+void WSLR(struct worker *w, enum VSL_tag_e tag, int id, txt t);
+void WSL(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, ...);
+void WSLB(struct worker *w, enum VSL_tag_e tag, const char *fmt, ...);
+
+void WSL_Flush(struct worker *w, int overflow);
+
+#define DSL(flag, tag, id, ...)					\
+	do {							\
+		if (cache_param->diag_bitmap & (flag))		\
+			VSL((tag), (id), __VA_ARGS__);		\
+	} while (0)
+
+#define WSP(sess, tag, ...)					\
+	WSL((sess)->wrk, tag, (sess)->vsl_id, __VA_ARGS__)
+
+#define WSPR(sess, tag, txt)					\
+	WSLR((sess)->wrk, tag, (sess)->vsl_id, txt)
+
+#define INCOMPL() do {							\
+	VSL(SLT_Debug, 0, "INCOMPLETE AT: %s(%d)", __func__, __LINE__); \
+	fprintf(stderr,							\
+	    "INCOMPLETE AT: %s(%d)\n",					\
+	    (const char *)__func__, __LINE__);				\
+	abort();							\
+	} while (0)
+#endif
+
+/* cache_response.c */
+void RES_BuildHttp(const struct sess *sp);
+void RES_WriteObj(struct sess *sp);
+void RES_StreamStart(struct sess *sp);
+void RES_StreamEnd(struct sess *sp);
+void RES_StreamPoll(struct worker *);
+
+/* cache_vary.c */
+struct vsb *VRY_Create(const struct sess *sp, const struct http *hp);
+int VRY_Match(struct sess *sp, const uint8_t *vary);
+void VRY_Validate(const uint8_t *vary);
+
+/* cache_vcl.c */
+void VCL_Init(void);
+void VCL_Refresh(struct VCL_conf **vcc);
+void VCL_Rel(struct VCL_conf **vcc);
+void VCL_Poll(void);
+const char *VCL_Return_Name(unsigned method);
+
+#define VCL_MET_MAC(l,u,b) void VCL_##l##_method(struct sess *);
+#include "tbl/vcl_returns.h"
+#undef VCL_MET_MAC
+
+/* cache_vrt.c */
+
+char *VRT_String(struct ws *ws, const char *h, const char *p, va_list ap);
+char *VRT_StringList(char *d, unsigned dl, const char *p, va_list ap);
+
+void ESI_Deliver(struct sess *);
+void ESI_DeliverChild(const struct sess *);
+
+/* cache_vrt_vmod.c */
+void VMOD_Init(void);
+
+/* cache_wrk.c */
+
+void WRK_Init(void);
+int WRK_TrySumStat(struct worker *w);
+void WRK_SumStat(struct worker *w);
+void *WRK_thread(void *priv);
+typedef void *bgthread_t(struct sess *, void *priv);
+void WRK_BgThread(pthread_t *thr, const char *name, bgthread_t *func,
+    void *priv);
+
+/* cache_ws.c */
+
+void WS_Init(struct ws *ws, const char *id, void *space, unsigned len);
+unsigned WS_Reserve(struct ws *ws, unsigned bytes);
+void WS_Release(struct ws *ws, unsigned bytes);
+void WS_ReleaseP(struct ws *ws, char *ptr);
+void WS_Assert(const struct ws *ws);
+void WS_Reset(struct ws *ws, char *p);
+char *WS_Alloc(struct ws *ws, unsigned bytes);
+char *WS_Dup(struct ws *ws, const char *);
+char *WS_Snapshot(struct ws *ws);
+unsigned WS_Free(const struct ws *ws);
+
+/* rfc2616.c */
+void RFC2616_Ttl(const struct sess *sp);
+enum body_status RFC2616_Body(const struct sess *sp);
+unsigned RFC2616_Req_Gzip(const struct sess *sp);
+int RFC2616_Do_Cond(const struct sess *sp);
+
+/* stevedore.c */
+struct object *STV_NewObject(struct sess *sp, const char *hint, unsigned len,
+    struct exp *, uint16_t nhttp);
+struct storage *STV_alloc(struct worker *w, size_t size);
+void STV_trim(struct storage *st, size_t size);
+void STV_free(struct storage *st);
+void STV_open(void);
+void STV_close(void);
+void STV_Freestore(struct object *o);
+
+/* storage_synth.c */
+struct vsb *SMS_Makesynth(struct object *obj);
+void SMS_Finish(struct object *obj);
+void SMS_Init(void);
+
+/* storage_persistent.c */
+void SMP_Init(void);
+void SMP_Ready(void);
+void SMP_NewBan(const uint8_t *ban, unsigned len);
+
+/*
+ * A normal pointer difference is signed, but we never want a negative value
+ * so this little tool will make sure we don't get that.
+ */
+
+static inline unsigned
+pdiff(const void *b, const void *e)
+{
+
+	assert(b <= e);
+	return
+	    ((unsigned)((const unsigned char *)e - (const unsigned char *)b));
+}
+
+static inline void
+Tcheck(const txt t)
+{
+
+	AN(t.b);
+	AN(t.e);
+	assert(t.b <= t.e);
+}
+
+/*
+ * unsigned length of a txt
+ */
+
+static inline unsigned
+Tlen(const txt t)
+{
+
+	Tcheck(t);
+	return ((unsigned)(t.e - t.b));
+}
+
+static inline void
+Tadd(txt *t, const char *p, int l)
+{
+	Tcheck(*t);
+
+	if (l <= 0) {
+	} if (t->b + l < t->e) {
+		memcpy(t->b, p, l);
+		t->b += l;
+	} else {
+		t->b = t->e;
+	}
+}
+
+static inline void
+AssertObjBusy(const struct object *o)
+{
+	AN(o->objcore);
+	AN (o->objcore->flags & OC_F_BUSY);
+}
+
+static inline void
+AssertObjCorePassOrBusy(const struct objcore *oc)
+{
+	if (oc != NULL)
+		AN (oc->flags & OC_F_BUSY);
+}
diff --git a/bin/varnishd/cache/cache_acceptor.c b/bin/varnishd/cache/cache_acceptor.c
new file mode 100644
index 0000000..8c83121
--- /dev/null
+++ b/bin/varnishd/cache/cache_acceptor.c
@@ -0,0 +1,430 @@
+/*-
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include "cache.h"
+#include "common/heritage.h"
+
+#include "vcli.h"
+#include "vcli_priv.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+static pthread_t	VCA_thread;
+static struct timeval	tv_sndtimeo;
+static struct timeval	tv_rcvtimeo;
+
+/*--------------------------------------------------------------------
+ * We want to get out of any kind of trouble-hit TCP connections as fast
+ * as absolutely possible, so we set them LINGER enabled with zero timeout,
+ * so that even if there are outstanding write data on the socket, a close(2)
+ * will return immediately.
+ */
+static const struct linger linger = {
+	.l_onoff	=	0,
+};
+
+static unsigned char	need_sndtimeo, need_rcvtimeo, need_linger, need_test;
+
+static void
+sock_test(int fd)
+{
+	struct linger lin;
+	struct timeval tv;
+	socklen_t l;
+	int i;
+
+	l = sizeof lin;
+	i = getsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, &l);
+	if (i) {
+		VTCP_Assert(i);
+		return;
+	}
+	assert(l == sizeof lin);
+	if (memcmp(&lin, &linger, l))
+		need_linger = 1;
+
+#ifdef SO_SNDTIMEO_WORKS
+	l = sizeof tv;
+	i = getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &l);
+	if (i) {
+		VTCP_Assert(i);
+		return;
+	}
+	assert(l == sizeof tv);
+	if (memcmp(&tv, &tv_sndtimeo, l))
+		need_sndtimeo = 1;
+#else
+	(void)tv;
+	(void)tv_sndtimeo;
+	(void)need_sndtimeo;
+#endif
+
+#ifdef SO_RCVTIMEO_WORKS
+	l = sizeof tv;
+	i = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &l);
+	if (i) {
+		VTCP_Assert(i);
+		return;
+	}
+	assert(l == sizeof tv);
+	if (memcmp(&tv, &tv_rcvtimeo, l))
+		need_rcvtimeo = 1;
+#else
+	(void)tv;
+	(void)tv_rcvtimeo;
+	(void)need_rcvtimeo;
+#endif
+
+	need_test = 0;
+}
+
+/*--------------------------------------------------------------------
+ * Called once the workerthread gets hold of the session, to do setup
+ * setup overhead, we don't want to bother the acceptor thread with.
+ */
+
+void
+VCA_Prep(struct sess *sp)
+{
+	char addr[VTCP_ADDRBUFSIZE];
+	char port[VTCP_PORTBUFSIZE];
+
+	VTCP_name(&sp->sockaddr, sp->sockaddrlen,
+	    addr, sizeof addr, port, sizeof port);
+	sp->addr = WS_Dup(sp->ws, addr);
+	sp->port = WS_Dup(sp->ws, port);
+	if (cache_param->log_local_addr) {
+		AZ(getsockname(sp->fd, (void*)&sp->mysockaddr, &sp->mysockaddrlen));
+		VTCP_name(&sp->mysockaddr, sp->mysockaddrlen,
+		    addr, sizeof addr, port, sizeof port);
+		WSP(sp, SLT_SessionOpen, "%s %s %s %s",
+		    sp->addr, sp->port, addr, port);
+	} else {
+		WSP(sp, SLT_SessionOpen, "%s %s %s",
+		    sp->addr, sp->port, sp->mylsock->name);
+	}
+	sp->acct_ses.first = sp->t_open;
+	if (need_test)
+		sock_test(sp->fd);
+	if (need_linger)
+		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_LINGER,
+		    &linger, sizeof linger));
+#ifdef SO_SNDTIMEO_WORKS
+	if (need_sndtimeo)
+		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_SNDTIMEO,
+		    &tv_sndtimeo, sizeof tv_sndtimeo));
+#endif
+#ifdef SO_RCVTIMEO_WORKS
+	if (need_rcvtimeo)
+		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_RCVTIMEO,
+		    &tv_rcvtimeo, sizeof tv_rcvtimeo));
+#endif
+}
+
+/*--------------------------------------------------------------------
+ * If accept(2)'ing fails, we pace ourselves to relive any resource
+ * shortage if possible.
+ */
+
+static double vca_pace = 0.0;
+static struct lock pace_mtx;
+
+static void
+vca_pace_check(void)
+{
+	double p;
+
+	if (vca_pace == 0.0)
+		return;
+	Lck_Lock(&pace_mtx);
+	p = vca_pace;
+	Lck_Unlock(&pace_mtx);
+	if (p > 0.0)
+		VTIM_sleep(p);
+}
+
+static void
+vca_pace_bad(void)
+{
+
+	Lck_Lock(&pace_mtx);
+	vca_pace += cache_param->acceptor_sleep_incr;
+	if (vca_pace > cache_param->acceptor_sleep_max)
+		vca_pace = cache_param->acceptor_sleep_max;
+	Lck_Unlock(&pace_mtx);
+}
+
+static void
+vca_pace_good(void)
+{
+
+	if (vca_pace == 0.0)
+		return;
+	Lck_Lock(&pace_mtx);
+	vca_pace *= cache_param->acceptor_sleep_decay;
+	if (vca_pace < cache_param->acceptor_sleep_incr)
+		vca_pace = 0.0;
+	Lck_Unlock(&pace_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Accept on a listen socket, and handle error returns.
+ */
+
+static int hack_ready;
+
+int
+VCA_Accept(struct listen_sock *ls, struct wrk_accept *wa)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+	vca_pace_check();
+
+	while(!hack_ready)
+		(void)usleep(100*1000);
+
+	wa->acceptaddrlen = sizeof wa->acceptaddr;
+	i = accept(ls->sock, (void*)&wa->acceptaddr, &wa->acceptaddrlen);
+
+	if (i < 0) {
+		switch (errno) {
+		case ECONNABORTED:
+			break;
+		case EMFILE:
+			VSL(SLT_Debug, ls->sock, "Too many open files");
+			vca_pace_bad();
+			break;
+		default:
+			VSL(SLT_Debug, ls->sock, "Accept failed: %s",
+			    strerror(errno));
+			vca_pace_bad();
+			break;
+		}
+	}
+	wa->acceptlsock = ls;
+	wa->acceptsock = i;
+	return (i);
+}
+
+/*--------------------------------------------------------------------
+ * Fail a session
+ *
+ * This happens if we accept the socket, but cannot get a session
+ * structure.
+ *
+ * We consider this a DoS situation (false positive:  Extremely popular
+ * busy objects) and silently close the connection with minimum effort
+ * and fuzz, rather than try to send an intelligent message back.
+ */
+
+void
+VCA_FailSess(struct worker *w)
+{
+	struct wrk_accept *wa;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CAST_OBJ_NOTNULL(wa, (void*)w->ws->f, WRK_ACCEPT_MAGIC);
+	AZ(w->sp);
+	AZ(close(wa->acceptsock));
+	w->stats.sess_drop++;
+	vca_pace_bad();
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VCA_SetupSess(struct worker *w)
+{
+	struct sess *sp;
+	struct wrk_accept *wa;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CAST_OBJ_NOTNULL(wa, (void*)w->ws->f, WRK_ACCEPT_MAGIC);
+	sp = w->sp;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	sp->fd = wa->acceptsock;
+	sp->vsl_id = wa->acceptsock | VSL_CLIENTMARKER ;
+	wa->acceptsock = -1;
+	sp->t_open = VTIM_real();
+	sp->t_end = sp->t_open;
+	sp->mylsock = wa->acceptlsock;
+	CHECK_OBJ_NOTNULL(sp->mylsock, LISTEN_SOCK_MAGIC);
+	assert(wa->acceptaddrlen <= sp->sockaddrlen);
+	memcpy(&sp->sockaddr, &wa->acceptaddr, wa->acceptaddrlen);
+	sp->sockaddrlen = wa->acceptaddrlen;
+	sp->step = STP_FIRST;
+	vca_pace_good();
+	w->stats.sess_conn++;
+}
+
+/*--------------------------------------------------------------------*/
+
+static void *
+vca_acct(void *arg)
+{
+#ifdef SO_RCVTIMEO_WORKS
+	double sess_timeout = 0;
+#endif
+#ifdef SO_SNDTIMEO_WORKS
+	double send_timeout = 0;
+#endif
+	struct listen_sock *ls;
+	double t0, now;
+
+	THR_SetName("cache-acceptor");
+	(void)arg;
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (ls->sock < 0)
+			continue;
+		AZ(listen(ls->sock, cache_param->listen_depth));
+		AZ(setsockopt(ls->sock, SOL_SOCKET, SO_LINGER,
+		    &linger, sizeof linger));
+	}
+
+	hack_ready = 1;
+
+	need_test = 1;
+	t0 = VTIM_real();
+	while (1) {
+		(void)sleep(1);
+#ifdef SO_SNDTIMEO_WORKS
+		if (cache_param->idle_send_timeout != send_timeout) {
+			need_test = 1;
+			send_timeout = cache_param->idle_send_timeout;
+			tv_sndtimeo = VTIM_timeval(send_timeout);
+			VTAILQ_FOREACH(ls, &heritage.socks, list) {
+				if (ls->sock < 0)
+					continue;
+				AZ(setsockopt(ls->sock, SOL_SOCKET,
+				    SO_SNDTIMEO,
+				    &tv_sndtimeo, sizeof tv_sndtimeo));
+			}
+		}
+#endif
+#ifdef SO_RCVTIMEO_WORKS
+		if (cache_param->sess_timeout != sess_timeout) {
+			need_test = 1;
+			sess_timeout = cache_param->sess_timeout;
+			tv_rcvtimeo = VTIM_timeval(sess_timeout);
+			VTAILQ_FOREACH(ls, &heritage.socks, list) {
+				if (ls->sock < 0)
+					continue;
+				AZ(setsockopt(ls->sock, SOL_SOCKET,
+				    SO_RCVTIMEO,
+				    &tv_rcvtimeo, sizeof tv_rcvtimeo));
+			}
+		}
+#endif
+		now = VTIM_real();
+		VSC_C_main->uptime = (uint64_t)(now - t0);
+	}
+	NEEDLESS_RETURN(NULL);
+}
+
+
+/*--------------------------------------------------------------------*/
+
+static void
+ccf_start(struct cli *cli, const char * const *av, void *priv)
+{
+
+	(void)cli;
+	(void)av;
+	(void)priv;
+
+	AZ(pthread_create(&VCA_thread, NULL, vca_acct, NULL));
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+ccf_listen_address(struct cli *cli, const char * const *av, void *priv)
+{
+	struct listen_sock *ls;
+	char h[32], p[32];
+
+	(void)cli;
+	(void)av;
+	(void)priv;
+
+	/*
+	 * This CLI command is primarily used by varnishtest.  Don't
+	 * respond until liste(2) has been called, in order to avoid
+	 * a race where varnishtest::client would attempt to connect(2)
+	 * before listen(2) has been called.
+	 */
+	while(!hack_ready)
+		(void)usleep(100*1000);
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (ls->sock < 0)
+			continue;
+		VTCP_myname(ls->sock, h, sizeof h, p, sizeof p);
+		VCLI_Out(cli, "%s %s\n", h, p);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct cli_proto vca_cmds[] = {
+	{ CLI_SERVER_START,	"i", ccf_start },
+	{ "debug.listen_address",
+	    "debug.listen_address",
+	    "Report the actual listen address\n", 0, 0,
+	    "d", ccf_listen_address, NULL },
+	{ NULL }
+};
+
+void
+VCA_Init(void)
+{
+
+	CLI_AddFuncs(vca_cmds);
+	Lck_New(&pace_mtx, lck_vcapace);
+}
+
+void
+VCA_Shutdown(void)
+{
+	struct listen_sock *ls;
+	int i;
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (ls->sock < 0)
+			continue;
+		i = ls->sock;
+		ls->sock = -1;
+		(void)close(i);
+	}
+}
diff --git a/bin/varnishd/cache/cache_backend.c b/bin/varnishd/cache/cache_backend.c
new file mode 100644
index 0000000..1f290c3
--- /dev/null
+++ b/bin/varnishd/cache/cache_backend.c
@@ -0,0 +1,517 @@
+/*-
+ * 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.
+ *
+ * Handle backend connections and backend request structures.
+ *
+ */
+
+#include "config.h"
+
+#include <poll.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vrt.h"
+#include "vtcp.h"
+
+/*--------------------------------------------------------------------
+ * The "simple" director really isn't, since thats where all the actual
+ * connections happen.  Nontheless, pretend it is simple by sequestering
+ * the directoricity of it under this line.
+ */
+
+struct vdi_simple {
+	unsigned		magic;
+#define VDI_SIMPLE_MAGIC	0x476d25b7
+	struct director		dir;
+	struct backend		*backend;
+	const struct vrt_backend *vrt;
+};
+
+/*--------------------------------------------------------------------
+ * Create default Host: header for backend request
+ */
+void
+VDI_AddHostHeader(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->bereq, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->vbc->vdis, VDI_SIMPLE_MAGIC);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->bereq,
+	    "Host: %s", sp->wrk->vbc->vdis->vrt->hosthdr);
+}
+
+/*--------------------------------------------------------------------*/
+
+/* Private interface from backend_cfg.c */
+void
+VBE_ReleaseConn(struct vbc *vc)
+{
+
+	CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
+	assert(vc->backend == NULL);
+	assert(vc->fd < 0);
+
+	vc->addr = NULL;
+	vc->addrlen = 0;
+	vc->recycled = 0;
+	Lck_Lock(&VBE_mtx);
+	VSC_C_main->n_vbc--;
+	Lck_Unlock(&VBE_mtx);
+	FREE_OBJ(vc);
+}
+
+#define FIND_TMO(tmx, dst, sp, be)		\
+	do {					\
+		dst = sp->wrk->tmx;		\
+		if (dst == 0.0)			\
+			dst = be->tmx;		\
+		if (dst == 0.0)			\
+			dst = cache_param->tmx;	\
+	} while (0)
+
+/*--------------------------------------------------------------------
+ * Attempt to connect to a given addrinfo entry.
+ *
+ * Must be called with locked backend, but will release the backend
+ * lock during the slow/sleeping stuff, so that other worker threads
+ * can have a go, while we ponder.
+ *
+ */
+
+static int
+vbe_TryConnect(const struct sess *sp, int pf, const struct sockaddr_storage *sa,
+    socklen_t salen, const struct vdi_simple *vs)
+{
+	int s, i, tmo;
+	double tmod;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
+
+	s = socket(pf, SOCK_STREAM, 0);
+	if (s < 0)
+		return (s);
+
+	FIND_TMO(connect_timeout, tmod, sp, vs->vrt);
+
+	tmo = (int)(tmod * 1000.0);
+
+	i = VTCP_connect(s, sa, salen, tmo);
+
+	if (i != 0) {
+		AZ(close(s));
+		return (-1);
+	}
+
+	return (s);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+bes_conn_try(const struct sess *sp, struct vbc *vc, const struct vdi_simple *vs)
+{
+	int s;
+	struct backend *bp = vs->backend;
+	char abuf1[VTCP_ADDRBUFSIZE];
+	char pbuf1[VTCP_PORTBUFSIZE];
+
+	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
+
+	Lck_Lock(&bp->mtx);
+	bp->refcount++;
+	bp->n_conn++;		/* It mostly works */
+	Lck_Unlock(&bp->mtx);
+
+	s = -1;
+	assert(bp->ipv6 != NULL || bp->ipv4 != NULL);
+
+	/* release lock during stuff that can take a long time */
+
+	if (cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
+		s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
+		vc->addr = bp->ipv6;
+		vc->addrlen = bp->ipv6len;
+	}
+	if (s == -1 && bp->ipv4 != NULL) {
+		s = vbe_TryConnect(sp, PF_INET, bp->ipv4, bp->ipv4len, vs);
+		vc->addr = bp->ipv4;
+		vc->addrlen = bp->ipv4len;
+	}
+	if (s == -1 && !cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
+		s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
+		vc->addr = bp->ipv6;
+		vc->addrlen = bp->ipv6len;
+	}
+
+	vc->fd = s;
+	if (s < 0) {
+		Lck_Lock(&bp->mtx);
+		bp->n_conn--;
+		bp->refcount--;		/* Only keep ref on success */
+		Lck_Unlock(&bp->mtx);
+		vc->addr = NULL;
+		vc->addrlen = 0;
+	} else {
+		vc->vsl_id = s | VSL_BACKENDMARKER;
+		VTCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
+		WSL(sp->wrk, SLT_BackendOpen, vc->vsl_id, "%s %s %s ",
+		    vs->backend->display_name, abuf1, pbuf1);
+	}
+
+}
+
+/*--------------------------------------------------------------------
+ * Check that there is still something at the far end of a given socket.
+ * We poll the fd with instant timeout, if there are any events we can't
+ * use it (backends are not allowed to pipeline).
+ */
+
+static int
+vbe_CheckFd(int fd)
+{
+	struct pollfd pfd;
+
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+	pfd.revents = 0;
+	return(poll(&pfd, 1, 0) == 0);
+}
+
+/*--------------------------------------------------------------------
+ * Manage a pool of vbc structures.
+ * XXX: as an experiment, make this caching controled by a parameter
+ * XXX: so we can see if it has any effect.
+ */
+
+static struct vbc *
+vbe_NewConn(void)
+{
+	struct vbc *vc;
+
+	ALLOC_OBJ(vc, VBC_MAGIC);
+	XXXAN(vc);
+	vc->fd = -1;
+	Lck_Lock(&VBE_mtx);
+	VSC_C_main->n_vbc++;
+	Lck_Unlock(&VBE_mtx);
+	return (vc);
+}
+
+/*--------------------------------------------------------------------
+ * It evaluates if a backend is healthy _for_a_specific_object_.
+ * That means that it relies on sp->objcore->objhead. This is mainly for
+ * saint-mode, but also takes backend->healthy into account. If
+ * cache_param->saintmode_threshold is 0, this is basically just a test of
+ * backend->healthy.
+ *
+ * The threshold has to be evaluated _after_ the timeout check, otherwise
+ * items would never time out once the threshold is reached.
+ */
+
+static unsigned int
+vbe_Healthy(const struct vdi_simple *vs, const struct sess *sp)
+{
+	struct trouble *tr;
+	struct trouble *tr2;
+	struct trouble *old;
+	unsigned i = 0, retval;
+	unsigned int threshold;
+	struct backend *backend;
+	uintptr_t target;
+	double now;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
+	backend = vs->backend;
+	CHECK_OBJ_NOTNULL(backend, BACKEND_MAGIC);
+
+	if (backend->admin_health == ah_probe && !backend->healthy)
+		return (0);
+
+	if (backend->admin_health == ah_sick)
+		return (0);
+
+	/* VRT/VCC sets threshold to UINT_MAX to mark that it's not
+	 * specified by VCL (thus use param).
+	 */
+	if (vs->vrt->saintmode_threshold == UINT_MAX)
+		threshold = cache_param->saintmode_threshold;
+	else
+		threshold = vs->vrt->saintmode_threshold;
+
+	if (backend->admin_health == ah_healthy)
+		threshold = UINT_MAX;
+
+	/* Saintmode is disabled */
+	if (threshold == 0)
+		return (1);
+
+	if (sp->objcore == NULL)
+		return (1);
+
+	now = sp->t_req;
+	target = (uintptr_t)(sp->objcore->objhead);
+
+	old = NULL;
+	retval = 1;
+	Lck_Lock(&backend->mtx);
+	VTAILQ_FOREACH_SAFE(tr, &backend->troublelist, list, tr2) {
+		CHECK_OBJ_NOTNULL(tr, TROUBLE_MAGIC);
+
+		if (tr->timeout < now) {
+			VTAILQ_REMOVE(&backend->troublelist, tr, list);
+			old = tr;
+			retval = 1;
+			break;
+		}
+
+		if (tr->target == target) {
+			retval = 0;
+			break;
+		}
+
+		/* If the threshold is at 1, a single entry on the list
+		 * will disable the backend. Since 0 is disable, ++i
+		 * instead of i++ to allow this behavior.
+		 */
+		if (++i >= threshold) {
+			retval = 0;
+			break;
+		}
+	}
+	Lck_Unlock(&backend->mtx);
+
+	if (old != NULL)
+		FREE_OBJ(old);
+
+	return (retval);
+}
+
+/*--------------------------------------------------------------------
+ * Get a connection to a particular backend.
+ */
+
+static struct vbc *
+vbe_GetVbe(const struct sess *sp, struct vdi_simple *vs)
+{
+	struct vbc *vc;
+	struct backend *bp;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
+	bp = vs->backend;
+	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
+
+	/* first look for vbc's we can recycle */
+	while (1) {
+		Lck_Lock(&bp->mtx);
+		vc = VTAILQ_FIRST(&bp->connlist);
+		if (vc != NULL) {
+			bp->refcount++;
+			assert(vc->backend == bp);
+			assert(vc->fd >= 0);
+			AN(vc->addr);
+			VTAILQ_REMOVE(&bp->connlist, vc, list);
+		}
+		Lck_Unlock(&bp->mtx);
+		if (vc == NULL)
+			break;
+		if (vbe_CheckFd(vc->fd)) {
+			/* XXX locking of stats */
+			VSC_C_main->backend_reuse += 1;
+			WSP(sp, SLT_Backend, "%d %s %s",
+			    vc->fd, sp->director->vcl_name, bp->display_name);
+			vc->vdis = vs;
+			vc->recycled = 1;
+			return (vc);
+		}
+		VSC_C_main->backend_toolate++;
+		WSL(sp->wrk, SLT_BackendClose, vc->vsl_id, "%s", bp->display_name);
+
+		/* Checkpoint log to flush all info related to this connection
+		   before the OS reuses the FD */
+		WSL_Flush(sp->wrk, 0);
+
+		VTCP_close(&vc->fd);
+		VBE_DropRefConn(bp);
+		vc->backend = NULL;
+		VBE_ReleaseConn(vc);
+	}
+
+	if (!vbe_Healthy(vs, sp)) {
+		VSC_C_main->backend_unhealthy++;
+		return (NULL);
+	}
+
+	if (vs->vrt->max_connections > 0 &&
+	    bp->n_conn >= vs->vrt->max_connections) {
+		VSC_C_main->backend_busy++;
+		return (NULL);
+	}
+
+	vc = vbe_NewConn();
+	assert(vc->fd == -1);
+	AZ(vc->backend);
+	bes_conn_try(sp, vc, vs);
+	if (vc->fd < 0) {
+		VBE_ReleaseConn(vc);
+		VSC_C_main->backend_fail++;
+		return (NULL);
+	}
+	vc->backend = bp;
+	VSC_C_main->backend_conn++;
+	WSP(sp, SLT_Backend, "%d %s %s",
+	    vc->fd, sp->director->vcl_name, bp->display_name);
+	vc->vdis = vs;
+	return (vc);
+}
+
+/*--------------------------------------------------------------------
+ * Returns the backend if and only if the this is a simple director.
+ * XXX: Needs a better name and possibly needs a better general approach.
+ * XXX: This is mainly used by the DNS director to fetch the actual backend
+ * XXX: so it can compare DNS lookups with the actual IP.
+ */
+
+struct backend *
+vdi_get_backend_if_simple(const struct director *d)
+{
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	struct vdi_simple *vs, *vs2;
+
+	vs2 = d->priv;
+	if (vs2->magic != VDI_SIMPLE_MAGIC)
+		return (NULL);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
+	return (vs->backend);
+}
+
+/*--------------------------------------------------------------------
+ *
+ */
+
+void
+VBE_UseHealth(const struct director *vdi)
+{
+	struct vdi_simple *vs;
+
+	ASSERT_CLI();
+
+	if (strcmp(vdi->name, "simple"))
+		return;
+	CAST_OBJ_NOTNULL(vs, vdi->priv, VDI_SIMPLE_MAGIC);
+	if (vs->vrt->probe == NULL)
+		return;
+	VBP_Use(vs->backend, vs->vrt->probe);
+}
+
+/*--------------------------------------------------------------------
+ *
+ */
+
+static struct vbc * __match_proto__(vdi_getfd_f)
+vdi_simple_getfd(const struct director *d, struct sess *sp)
+{
+	struct vdi_simple *vs;
+	struct vbc *vc;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
+	vc = vbe_GetVbe(sp, vs);
+	if (vc != NULL) {
+		FIND_TMO(first_byte_timeout,
+		    vc->first_byte_timeout, sp, vs->vrt);
+		FIND_TMO(between_bytes_timeout,
+		    vc->between_bytes_timeout, sp, vs->vrt);
+	}
+	return (vc);
+}
+
+static unsigned
+vdi_simple_healthy(const struct director *d, const struct sess *sp)
+{
+	struct vdi_simple *vs;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
+	return (vbe_Healthy(vs, sp));
+}
+
+static void
+vdi_simple_fini(const struct director *d)
+{
+	struct vdi_simple *vs;
+
+	ASSERT_CLI();
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
+
+	if (vs->vrt->probe != NULL)
+		VBP_Remove(vs->backend, vs->vrt->probe);
+	VBE_DropRefVcl(vs->backend);
+	free(vs->dir.vcl_name);
+	vs->dir.magic = 0;
+	FREE_OBJ(vs);
+}
+
+void
+VRT_init_dir_simple(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	const struct vrt_backend *t;
+	struct vdi_simple *vs;
+
+	ASSERT_CLI();
+	(void)cli;
+	t = priv;
+
+	ALLOC_OBJ(vs, VDI_SIMPLE_MAGIC);
+	XXXAN(vs);
+	vs->dir.magic = DIRECTOR_MAGIC;
+	vs->dir.priv = vs;
+	vs->dir.name = "simple";
+	REPLACE(vs->dir.vcl_name, t->vcl_name);
+	vs->dir.getfd = vdi_simple_getfd;
+	vs->dir.fini = vdi_simple_fini;
+	vs->dir.healthy = vdi_simple_healthy;
+
+	vs->vrt = t;
+
+	vs->backend = VBE_AddBackend(cli, t);
+	if (vs->vrt->probe != NULL)
+		VBP_Insert(vs->backend, vs->vrt->probe, vs->vrt->hosthdr);
+
+	bp[idx] = &vs->dir;
+}
diff --git a/bin/varnishd/cache/cache_backend.h b/bin/varnishd/cache/cache_backend.h
new file mode 100644
index 0000000..72a1283
--- /dev/null
+++ b/bin/varnishd/cache/cache_backend.h
@@ -0,0 +1,192 @@
+/*-
+ * 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.
+ *
+ * This is the central switch-board for backend connections and it is
+ * slightly complicated by a number of optimizations.
+ *
+ * The data structures:
+ *
+ *    A vrt_backend is a definition of a backend in a VCL program.
+ *
+ *    A backend is a TCP destination, possibly multi-homed and it has a
+ *    number of associated properties and statistics.
+ *
+ *    A vbc is an open TCP connection to a backend.
+ *
+ *    A bereq is a memory carrier for handling a HTTP transaction with
+ *    a backend over a vbc.
+ *
+ *    A director is a piece of code that selects which backend to use,
+ *    by whatever method or metric it chooses.
+ *
+ * The relationships:
+ *
+ *    Backends and directors get instantiated when VCL's are loaded,
+ *    and this always happen in the CLI thread.
+ *
+ *    When a VCL tries to instantiate a backend, any existing backend
+ *    with the same identity (== definition in VCL) will be used instead
+ *    so that vbc's can be reused across VCL changes.
+ *
+ *    Directors disapper with the VCL that created them.
+ *
+ *    Backends disappear when their reference count drop to zero.
+ *
+ *    Backends have their host/port name looked up to addrinfo structures
+ *    when they are instantiated, and we just cache that result and cycle
+ *    through the entries (for multihomed backends) on failure only.
+ *    XXX: add cli command to redo lookup.
+ *
+ *    bereq is sort of a step-child here, we just manage the pool of them.
+ *
+ */
+
+struct vbp_target;
+struct vbc;
+struct vrt_backend_probe;
+
+/*--------------------------------------------------------------------
+ * A director is a piece of code which selects one of possibly multiple
+ * backends to use.
+ */
+
+typedef struct vbc *vdi_getfd_f(const struct director *, struct sess *sp);
+typedef void vdi_fini_f(const struct director *);
+typedef unsigned vdi_healthy(const struct director *, const struct sess *sp);
+
+struct director {
+	unsigned		magic;
+#define DIRECTOR_MAGIC		0x3336351d
+	const char		*name;
+	char			*vcl_name;
+	vdi_getfd_f		*getfd;
+	vdi_fini_f		*fini;
+	vdi_healthy		*healthy;
+	void			*priv;
+};
+
+/*--------------------------------------------------------------------
+ * List of objectheads that have recently been rejected by VCL.
+ */
+
+struct trouble {
+	unsigned		magic;
+#define TROUBLE_MAGIC		0x4211ab21
+	uintptr_t		target;
+	double			timeout;
+	VTAILQ_ENTRY(trouble)	list;
+};
+
+/*--------------------------------------------------------------------
+ * An instance of a backend from a VCL program.
+ */
+
+enum admin_health {
+	ah_invalid = 0,
+	ah_healthy,
+	ah_sick,
+	ah_probe
+};
+
+struct backend {
+	unsigned		magic;
+#define BACKEND_MAGIC		0x64c4c7c6
+
+	VTAILQ_ENTRY(backend)	list;
+	int			refcount;
+	struct lock		mtx;
+
+	char			*vcl_name;
+	char			*display_name;
+	char			*ipv4_addr;
+	char			*ipv6_addr;
+	char			*port;
+
+	struct sockaddr_storage	*ipv4;
+	socklen_t		ipv4len;
+	struct sockaddr_storage	*ipv6;
+	socklen_t		ipv6len;
+
+	unsigned		n_conn;
+	VTAILQ_HEAD(, vbc)	connlist;
+
+	struct vbp_target	*probe;
+	unsigned		healthy;
+	enum admin_health	admin_health;
+	VTAILQ_HEAD(, trouble)	troublelist;
+
+	struct VSC_C_vbe	*vsc;
+};
+
+/* -------------------------------------------------------------------*/
+
+/* Backend connection */
+struct vbc {
+	unsigned		magic;
+#define VBC_MAGIC		0x0c5e6592
+	VTAILQ_ENTRY(vbc)	list;
+	struct backend		*backend;
+	struct vdi_simple	*vdis;
+	unsigned		vsl_id;
+	int			fd;
+
+	struct sockaddr_storage	*addr;
+	socklen_t		addrlen;
+
+	uint8_t			recycled;
+
+	/* Timeouts */
+	double			first_byte_timeout;
+	double			between_bytes_timeout;
+};
+
+/* cache_backend.c */
+void VBE_ReleaseConn(struct vbc *vc);
+struct backend *vdi_get_backend_if_simple(const struct director *d);
+
+/* cache_backend_cfg.c */
+extern struct lock VBE_mtx;
+void VBE_DropRefConn(struct backend *);
+void VBE_DropRefVcl(struct backend *);
+void VBE_DropRefLocked(struct backend *b);
+
+/* cache_backend_poll.c */
+void VBP_Insert(struct backend *b, struct vrt_backend_probe const *p, const char *hosthdr);
+void VBP_Remove(struct backend *b, struct vrt_backend_probe const *p);
+void VBP_Use(const struct backend *b, const struct vrt_backend_probe const *p);
+void VBP_Summary(struct cli *cli, const struct vbp_target *vt);
+
+/* Init functions for directors */
+typedef void dir_init_f(struct cli *, struct director **, int , const void*);
+dir_init_f VRT_init_dir_simple;
+dir_init_f VRT_init_dir_dns;
+dir_init_f VRT_init_dir_hash;
+dir_init_f VRT_init_dir_random;
+dir_init_f VRT_init_dir_round_robin;
+dir_init_f VRT_init_dir_fallback;
+dir_init_f VRT_init_dir_client;
diff --git a/bin/varnishd/cache/cache_backend_cfg.c b/bin/varnishd/cache/cache_backend_cfg.c
new file mode 100644
index 0000000..cbb8c85
--- /dev/null
+++ b/bin/varnishd/cache/cache_backend_cfg.c
@@ -0,0 +1,507 @@
+/*-
+ * 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.
+ *
+ * Handle configuration of backends from VCL programs.
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vcli.h"
+#include "vcli_priv.h"
+#include "vrt.h"
+
+struct lock VBE_mtx;
+
+
+/*
+ * The list of backends is not locked, it is only ever accessed from
+ * the CLI thread, so there is no need.
+ */
+static VTAILQ_HEAD(, backend) backends = VTAILQ_HEAD_INITIALIZER(backends);
+
+/*--------------------------------------------------------------------
+ */
+
+static void
+VBE_Nuke(struct backend *b)
+{
+
+	ASSERT_CLI();
+	VTAILQ_REMOVE(&backends, b, list);
+	free(b->ipv4);
+	free(b->ipv4_addr);
+	free(b->ipv6);
+	free(b->ipv6_addr);
+	free(b->port);
+	VSM_Free(b->vsc);
+	FREE_OBJ(b);
+	VSC_C_main->n_backend--;
+}
+
+/*--------------------------------------------------------------------
+ */
+
+void
+VBE_Poll(void)
+{
+	struct backend *b, *b2;
+
+	ASSERT_CLI();
+	VTAILQ_FOREACH_SAFE(b, &backends, list, b2) {
+		assert(
+			b->admin_health == ah_healthy ||
+			b->admin_health == ah_sick ||
+			b->admin_health == ah_probe
+		);
+		if (b->refcount == 0 && b->probe == NULL)
+			VBE_Nuke(b);
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Drop a reference to a backend.
+ * The last reference must come from the watcher in the CLI thread,
+ * as only that thread is allowed to clean up the backend list.
+ */
+
+void
+VBE_DropRefLocked(struct backend *b)
+{
+	int i;
+	struct vbc *vbe, *vbe2;
+
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+	assert(b->refcount > 0);
+
+	i = --b->refcount;
+	Lck_Unlock(&b->mtx);
+	if (i > 0)
+		return;
+
+	ASSERT_CLI();
+	VTAILQ_FOREACH_SAFE(vbe, &b->connlist, list, vbe2) {
+		VTAILQ_REMOVE(&b->connlist, vbe, list);
+		if (vbe->fd >= 0) {
+			AZ(close(vbe->fd));
+			vbe->fd = -1;
+		}
+		vbe->backend = NULL;
+		VBE_ReleaseConn(vbe);
+	}
+	VBE_Nuke(b);
+}
+
+void
+VBE_DropRefVcl(struct backend *b)
+{
+
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+
+	Lck_Lock(&b->mtx);
+	b->vsc->vcls--;
+	VBE_DropRefLocked(b);
+}
+
+void
+VBE_DropRefConn(struct backend *b)
+{
+
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+
+	Lck_Lock(&b->mtx);
+	assert(b->n_conn > 0);
+	b->n_conn--;
+	VBE_DropRefLocked(b);
+}
+
+/*--------------------------------------------------------------------
+ * See lib/libvcl/vcc_backend.c::emit_sockaddr()
+ */
+
+static void
+copy_sockaddr(struct sockaddr_storage **sa, socklen_t *len,
+    const unsigned char *src)
+{
+
+	assert(*src > 0);
+	*sa = calloc(sizeof **sa, 1);
+	XXXAN(*sa);
+	memcpy(*sa, src + 1, *src);
+	*len = *src;
+}
+
+/*--------------------------------------------------------------------
+ * Add a backend/director instance when loading a VCL.
+ * If an existing backend is matched, grab a refcount and return.
+ * Else create a new backend structure with reference initialized to one.
+ */
+
+struct backend *
+VBE_AddBackend(struct cli *cli, const struct vrt_backend *vb)
+{
+	struct backend *b;
+	char buf[128];
+
+	AN(vb->vcl_name);
+	assert(vb->ipv4_sockaddr != NULL || vb->ipv6_sockaddr != NULL);
+	(void)cli;
+	ASSERT_CLI();
+
+	/* Run through the list and see if we already have this backend */
+	VTAILQ_FOREACH(b, &backends, list) {
+		CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+		if (strcmp(b->vcl_name, vb->vcl_name))
+			continue;
+		if (vb->ipv4_sockaddr != NULL && (
+		    b->ipv4len != vb->ipv4_sockaddr[0] ||
+		    memcmp(b->ipv4, vb->ipv4_sockaddr + 1, b->ipv4len)))
+			continue;
+		if (vb->ipv6_sockaddr != NULL && (
+		    b->ipv6len != vb->ipv6_sockaddr[0] ||
+		    memcmp(b->ipv6, vb->ipv6_sockaddr + 1, b->ipv6len)))
+			continue;
+		b->refcount++;
+		b->vsc->vcls++;
+		return (b);
+	}
+
+	/* Create new backend */
+	ALLOC_OBJ(b, BACKEND_MAGIC);
+	XXXAN(b);
+	Lck_New(&b->mtx, lck_backend);
+	b->refcount = 1;
+
+	bprintf(buf, "%s(%s,%s,%s)",
+	    vb->vcl_name,
+	    vb->ipv4_addr == NULL ? "" : vb->ipv4_addr,
+	    vb->ipv6_addr == NULL ? "" : vb->ipv6_addr, vb->port);
+
+	b->vsc = VSM_Alloc(sizeof *b->vsc, VSC_CLASS, VSC_TYPE_VBE, buf);
+	b->vsc->vcls++;
+
+	VTAILQ_INIT(&b->connlist);
+
+	VTAILQ_INIT(&b->troublelist);
+
+	/*
+	 * This backend may live longer than the VCL that instantiated it
+	 * so we cannot simply reference the VCL's copy of things.
+	 */
+	REPLACE(b->vcl_name, vb->vcl_name);
+	REPLACE(b->display_name, buf);
+	REPLACE(b->ipv4_addr, vb->ipv4_addr);
+	REPLACE(b->ipv6_addr, vb->ipv6_addr);
+	REPLACE(b->port, vb->port);
+
+	/*
+	 * Copy over the sockaddrs
+	 */
+	if (vb->ipv4_sockaddr != NULL)
+		copy_sockaddr(&b->ipv4, &b->ipv4len, vb->ipv4_sockaddr);
+	if (vb->ipv6_sockaddr != NULL)
+		copy_sockaddr(&b->ipv6, &b->ipv6len, vb->ipv6_sockaddr);
+
+	assert(b->ipv4 != NULL || b->ipv6 != NULL);
+
+	b->healthy = 1;
+	b->admin_health = ah_probe;
+
+	VTAILQ_INSERT_TAIL(&backends, b, list);
+	VSC_C_main->n_backend++;
+	return (b);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
+    int idx, const void *priv)
+{
+
+	ASSERT_CLI();
+	if (!strcmp(name, "simple"))
+		VRT_init_dir_simple(cli, dir, idx, priv);
+	else if (!strcmp(name, "hash"))
+		VRT_init_dir_hash(cli, dir, idx, priv);
+	else if (!strcmp(name, "random"))
+		VRT_init_dir_random(cli, dir, idx, priv);
+	else if (!strcmp(name, "dns"))
+		VRT_init_dir_dns(cli, dir, idx, priv);
+	else if (!strcmp(name, "round-robin"))
+		VRT_init_dir_round_robin(cli, dir, idx, priv);
+	else if (!strcmp(name, "fallback"))
+		VRT_init_dir_fallback(cli, dir, idx, priv);
+	else if (!strcmp(name, "client"))
+		VRT_init_dir_client(cli, dir, idx, priv);
+	else
+		INCOMPL();
+}
+
+void
+VRT_fini_dir(struct cli *cli, struct director *b)
+{
+
+	(void)cli;
+	ASSERT_CLI();
+	CHECK_OBJ_NOTNULL(b, DIRECTOR_MAGIC);
+	b->fini(b);
+	b->priv = NULL;
+}
+
+/*---------------------------------------------------------------------
+ * String to admin_health
+ */
+
+static enum admin_health
+vbe_str2adminhealth(const char *wstate)
+{
+
+	if (strcasecmp(wstate, "healthy") == 0)
+		return(ah_healthy);
+	if (strcasecmp(wstate, "sick") == 0)
+		return(ah_sick);
+	if (strcmp(wstate, "auto") == 0)
+		return(ah_probe);
+	return (ah_invalid);
+}
+
+/*---------------------------------------------------------------------
+ * A general function for finding backends and doing things with them.
+ *
+ * Return -1 on match-argument parse errors.
+ *
+ * If the call-back function returns non-zero, the search is terminated
+ * and we relay that return value.
+ *
+ * Otherwise we return the number of matches.
+ */
+
+typedef int bf_func(struct cli *cli, struct backend *b, void *priv);
+
+static int
+backend_find(struct cli *cli, const char *matcher, bf_func *func, void *priv)
+{
+	struct backend *b;
+	const char *s;
+	const char *name_b;
+	ssize_t name_l = 0;
+	const char *ip_b = NULL;
+	ssize_t ip_l = 0;
+	const char *port_b = NULL;
+	ssize_t port_l = 0;
+	int found = 0;
+	int i;
+
+	name_b = matcher;
+	if (matcher != NULL) {
+		s = strchr(matcher,'(');
+
+		if (s != NULL)
+			name_l = s - name_b;
+		else
+			name_l = strlen(name_b);
+
+		if (s != NULL) {
+			s++;
+			while (isspace(*s))
+				s++;
+			ip_b = s;
+			while (*s != '\0' &&
+			    *s != ')' &&
+			    *s != ':' &&
+			    !isspace(*s))
+				s++;
+			ip_l = s - ip_b;
+			while (isspace(*s))
+				s++;
+			if (*s == ':') {
+				s++;
+				while (isspace(*s))
+					s++;
+				port_b = s;
+				while (*s != '\0' && *s != ')' && !isspace(*s))
+					s++;
+				port_l = s - port_b;
+			}
+			while (isspace(*s))
+				s++;
+			if (*s != ')') {
+				VCLI_Out(cli,
+				    "Match string syntax error:"
+				    " ')' not found.");
+				VCLI_SetResult(cli, CLIS_CANT);
+				return (-1);
+			}
+			s++;
+			while (isspace(*s))
+				s++;
+			if (*s != '\0') {
+				VCLI_Out(cli,
+				    "Match string syntax error:"
+				    " junk after ')'");
+				VCLI_SetResult(cli, CLIS_CANT);
+				return (-1);
+			}
+		}
+	}
+	VTAILQ_FOREACH(b, &backends, list) {
+		CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+		if (port_b != NULL && strncmp(b->port, port_b, port_l) != 0)
+			continue;
+		if (name_b != NULL && strncmp(b->vcl_name, name_b, name_l) != 0)
+			continue;
+		if (ip_b != NULL &&
+		    (b->ipv4_addr == NULL ||
+		      strncmp(b->ipv4_addr, ip_b, ip_l)) &&
+		    (b->ipv6_addr == NULL ||
+		      strncmp(b->ipv6_addr, ip_b, ip_l)))
+			continue;
+		found++;
+		i = func(cli, b, priv);
+		if (i)
+			return(i);
+	}
+	return (found);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int __match_proto__()
+do_list(struct cli *cli, struct backend *b, void *priv)
+{
+	int *hdr;
+
+	AN(priv);
+	hdr = priv;
+	if (!*hdr) {
+		VCLI_Out(cli, "%-30s %-6s %-10s %s",
+		    "Backend name", "Refs", "Admin", "Probe");
+		*hdr = 1;
+	}
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+
+	VCLI_Out(cli, "\n%-30s %-6d", b->display_name, b->refcount);
+
+	if (b->admin_health == ah_probe)
+		VCLI_Out(cli, " %-10s", "probe");
+	else if (b->admin_health == ah_sick)
+		VCLI_Out(cli, " %-10s", "sick");
+	else if (b->admin_health == ah_healthy)
+		VCLI_Out(cli, " %-10s", "healthy");
+	else
+		VCLI_Out(cli, " %-10s", "invalid");
+
+	if (b->probe == NULL)
+		VCLI_Out(cli, " %s", "Healthy (no probe)");
+	else {
+		if (b->healthy)
+			VCLI_Out(cli, " %s", "Healthy ");
+		else
+			VCLI_Out(cli, " %s", "Sick ");
+		VBP_Summary(cli, b->probe);
+	}
+
+	return (0);
+}
+
+static void
+cli_backend_list(struct cli *cli, const char * const *av, void *priv)
+{
+	int hdr = 0;
+
+	(void)av;
+	(void)priv;
+	ASSERT_CLI();
+	(void)backend_find(cli, av[2], do_list, &hdr);
+}
+
+/*---------------------------------------------------------------------*/
+
+static int __match_proto__()
+do_set_health(struct cli *cli, struct backend *b, void *priv)
+{
+	enum admin_health state;
+
+	(void)cli;
+	state = *(enum admin_health*)priv;
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+	b->admin_health = state;
+	return (0);
+}
+
+static void
+cli_backend_set_health(struct cli *cli, const char * const *av, void *priv)
+{
+	enum admin_health state;
+	int n;
+
+	(void)av;
+	(void)priv;
+	ASSERT_CLI();
+	AN(av[2]);
+	AN(av[3]);
+	state = vbe_str2adminhealth(av[3]);
+	if (state == ah_invalid) {
+		VCLI_Out(cli, "Invalid state %s", av[3]);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return;
+	}
+	n = backend_find(cli, av[2], do_set_health, &state);
+	if (n == 0) {
+		VCLI_Out(cli, "No Backends matches");
+		VCLI_SetResult(cli, CLIS_PARAM);
+	}
+}
+
+/*---------------------------------------------------------------------*/
+
+static struct cli_proto backend_cmds[] = {
+	{ "backend.list", "backend.list",
+	    "\tList all backends\n", 0, 1, "d", cli_backend_list },
+	{ "backend.set_health", "backend.set_health matcher state",
+	    "\tShow a backend\n", 2, 2, "d", cli_backend_set_health },
+	{ NULL }
+};
+
+/*---------------------------------------------------------------------*/
+
+void
+VBE_Init(void)
+{
+
+	Lck_New(&VBE_mtx, lck_vbe);
+	CLI_AddFuncs(backend_cmds);
+}
diff --git a/bin/varnishd/cache/cache_backend_poll.c b/bin/varnishd/cache/cache_backend_poll.c
new file mode 100644
index 0000000..efd64cb
--- /dev/null
+++ b/bin/varnishd/cache/cache_backend_poll.c
@@ -0,0 +1,597 @@
+/*-
+ * 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.
+ *
+ * Poll backends for collection of health statistics
+ *
+ * We co-opt threads from the worker pool for probing the backends,
+ * but we want to avoid a potentially messy cleanup operation when we
+ * retire the backend, so the thread owns the health information, which
+ * the backend references, rather than the other way around.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vcli_priv.h"
+#include "vrt.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+/* Default averaging rate, we want something pretty responsive */
+#define AVG_RATE			4
+
+struct vbp_vcl {
+	unsigned			magic;
+#define VBP_VCL_MAGIC			0x70829764
+
+	VTAILQ_ENTRY(vbp_vcl)		list;
+	const struct vrt_backend_probe	*probep;
+	struct vrt_backend_probe	probe;
+	const char			*hosthdr;
+};
+
+struct vbp_target {
+	unsigned			magic;
+#define VBP_TARGET_MAGIC		0x6b7cb656
+
+	struct backend			*backend;
+	VTAILQ_HEAD( ,vbp_vcl)		vcls;
+
+	struct vrt_backend_probe	probe;
+	int				stop;
+	struct vsb			*vsb;
+	char				*req;
+	int				req_len;
+
+	char				resp_buf[128];
+	unsigned			good;
+
+	/* Collected statistics */
+#define BITMAP(n, c, t, b)	uint64_t	n;
+#include "tbl/backend_poll.h"
+#undef BITMAP
+
+	double				last;
+	double				avg;
+	double				rate;
+
+	VTAILQ_ENTRY(vbp_target)	list;
+	pthread_t			thread;
+};
+
+static VTAILQ_HEAD(, vbp_target)	vbp_list =
+    VTAILQ_HEAD_INITIALIZER(vbp_list);
+
+static struct lock			vbp_mtx;
+
+/*--------------------------------------------------------------------
+ * Poke one backend, once, but possibly at both IPv4 and IPv6 addresses.
+ *
+ * We do deliberately not use the stuff in cache_backend.c, because we
+ * want to measure the backends response without local distractions.
+ */
+
+static int
+vbp_connect(int pf, const struct sockaddr_storage *sa, socklen_t salen, int tmo)
+{
+	int s, i;
+
+	s = socket(pf, SOCK_STREAM, 0);
+	if (s < 0)
+		return (s);
+
+	i = VTCP_connect(s, sa, salen, tmo);
+	if (i == 0)
+		return (s);
+	VTCP_close(&s);
+	return (-1);
+}
+
+static void
+vbp_poke(struct vbp_target *vt)
+{
+	int s, tmo, i;
+	double t_start, t_now, t_end;
+	unsigned rlen, resp;
+	struct backend *bp;
+	char buf[8192], *p;
+	struct pollfd pfda[1], *pfd = pfda;
+
+	bp = vt->backend;
+	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
+
+	t_start = t_now = VTIM_real();
+	t_end = t_start + vt->probe.timeout;
+	tmo = (int)round((t_end - t_now) * 1e3);
+
+	s = -1;
+	if (cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
+		s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
+		t_now = VTIM_real();
+		tmo = (int)round((t_end - t_now) * 1e3);
+		if (s >= 0)
+			vt->good_ipv6 |= 1;
+	}
+	if (tmo > 0 && s < 0 && bp->ipv4 != NULL) {
+		s = vbp_connect(PF_INET, bp->ipv4, bp->ipv4len, tmo);
+		t_now = VTIM_real();
+		tmo = (int)round((t_end - t_now) * 1e3);
+		if (s >= 0)
+			vt->good_ipv4 |= 1;
+	}
+	if (tmo > 0 && s < 0 && bp->ipv6 != NULL) {
+		s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
+		t_now = VTIM_real();
+		tmo = (int)round((t_end - t_now) * 1e3);
+		if (s >= 0)
+			vt->good_ipv6 |= 1;
+	}
+	if (s < 0) {
+		/* Got no connection: failed */
+		return;
+	}
+	if (tmo <= 0) {
+		/* Spent too long time getting it */
+		VTCP_close(&s);
+		return;
+	}
+
+	/* Send the request */
+	i = write(s, vt->req, vt->req_len);
+	if (i != vt->req_len) {
+		if (i < 0)
+			vt->err_xmit |= 1;
+		VTCP_close(&s);
+		return;
+	}
+	vt->good_xmit |= 1;
+
+	pfd->fd = s;
+	rlen = 0;
+	do {
+		pfd->events = POLLIN;
+		pfd->revents = 0;
+		tmo = (int)round((t_end - t_now) * 1e3);
+		if (tmo > 0)
+			i = poll(pfd, 1, tmo);
+		if (i == 0 || tmo <= 0) {
+			VTCP_close(&s);
+			return;
+		}
+		if (rlen < sizeof vt->resp_buf)
+			i = read(s, vt->resp_buf + rlen,
+			    sizeof vt->resp_buf - rlen);
+		else
+			i = read(s, buf, sizeof buf);
+		rlen += i;
+	} while (i > 0);
+
+	VTCP_close(&s);
+
+	if (i < 0) {
+		vt->err_recv |= 1;
+		return;
+	}
+
+	if (rlen == 0)
+		return;
+
+	/* So we have a good receive ... */
+	t_now = VTIM_real();
+	vt->last = t_now - t_start;
+	vt->good_recv |= 1;
+
+	/* Now find out if we like the response */
+	vt->resp_buf[sizeof vt->resp_buf - 1] = '\0';
+	p = strchr(vt->resp_buf, '\r');
+	if (p != NULL)
+		*p = '\0';
+	p = strchr(vt->resp_buf, '\n');
+	if (p != NULL)
+		*p = '\0';
+
+	i = sscanf(vt->resp_buf, "HTTP/%*f %u %s", &resp, buf);
+
+	if (i == 2 && resp == vt->probe.exp_status)
+		vt->happy |= 1;
+}
+
+/*--------------------------------------------------------------------
+ * Record pokings...
+ */
+
+static void
+vbp_start_poke(struct vbp_target *vt)
+{
+	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
+
+#define BITMAP(n, c, t, b)	vt->n <<= 1;
+#include "tbl/backend_poll.h"
+#undef BITMAP
+
+	vt->last = 0;
+	vt->resp_buf[0] = '\0';
+}
+
+static void
+vbp_has_poked(struct vbp_target *vt)
+{
+	unsigned i, j;
+	uint64_t u;
+	const char *logmsg;
+	char bits[10];
+
+	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
+
+	/* Calculate exponential average */
+	if (vt->happy & 1) {
+		if (vt->rate < AVG_RATE)
+			vt->rate += 1.0;
+		vt->avg += (vt->last - vt->avg) / vt->rate;
+	}
+
+	i = 0;
+#define BITMAP(n, c, t, b)	bits[i++] = (vt->n & 1) ? c : '-';
+#include "tbl/backend_poll.h"
+#undef BITMAP
+	bits[i] = '\0';
+
+	u = vt->happy;
+	for (i = j = 0; i < vt->probe.window; i++) {
+		if (u & 1)
+			j++;
+		u >>= 1;
+	}
+	vt->good = j;
+
+	if (vt->good >= vt->probe.threshold) {
+		if (vt->backend->healthy)
+			logmsg = "Still healthy";
+		else
+			logmsg = "Back healthy";
+		vt->backend->healthy = 1;
+	} else {
+		if (vt->backend->healthy)
+			logmsg = "Went sick";
+		else
+			logmsg = "Still sick";
+		vt->backend->healthy = 0;
+	}
+	VSL(SLT_Backend_health, 0, "%s %s %s %u %u %u %.6f %.6f %s",
+	    vt->backend->vcl_name, logmsg, bits,
+	    vt->good, vt->probe.threshold, vt->probe.window,
+	    vt->last, vt->avg, vt->resp_buf);
+	vt->backend->vsc->happy = vt->happy;
+}
+
+/*--------------------------------------------------------------------
+ * Build request from probe spec
+ */
+
+static void
+vbp_build_req(struct vsb *vsb, const struct vbp_vcl *vcl)
+{
+
+	XXXAN(vsb);
+	XXXAN(vcl);
+	VSB_clear(vsb);
+	if(vcl->probe.request != NULL) {
+		VSB_cat(vsb, vcl->probe.request);
+	} else {
+		VSB_printf(vsb, "GET %s HTTP/1.1\r\n",
+		    vcl->probe.url != NULL ?  vcl->probe.url : "/");
+		if (vcl->hosthdr != NULL)
+			VSB_printf(vsb, "Host: %s\r\n", vcl->hosthdr);
+		VSB_printf(vsb, "Connection: close\r\n");
+		VSB_printf(vsb, "\r\n");
+	}
+	AZ(VSB_finish(vsb));
+}
+
+/*--------------------------------------------------------------------
+ * One thread per backend to be poked.
+ */
+
+static void *
+vbp_wrk_poll_backend(void *priv)
+{
+	struct vbp_target *vt;
+	struct vbp_vcl *vcl = NULL;
+
+	THR_SetName("backend poll");
+
+	CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC);
+
+	while (!vt->stop) {
+		Lck_Lock(&vbp_mtx);
+		if (VTAILQ_FIRST(&vt->vcls) != vcl) {
+			vcl = VTAILQ_FIRST(&vt->vcls);
+			vbp_build_req(vt->vsb, vcl);
+			vt->probe = vcl->probe;
+		}
+		Lck_Unlock(&vbp_mtx);
+
+		vt->req = VSB_data(vt->vsb);
+		vt->req_len = VSB_len(vt->vsb);
+
+		vbp_start_poke(vt);
+		vbp_poke(vt);
+		vbp_has_poked(vt);
+
+		if (!vt->stop)
+			VTIM_sleep(vt->probe.interval);
+	}
+	return (NULL);
+}
+
+/*--------------------------------------------------------------------
+ * Cli functions
+ */
+
+void
+VBP_Summary(struct cli *cli, const struct vbp_target *vt)
+{
+
+	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
+	VCLI_Out(cli, "%d/%d", vt->good, vt->probe.window);
+}
+
+static void
+vbp_bitmap(struct cli *cli, char c, uint64_t map, const char *lbl)
+{
+	int i;
+	uint64_t u = (1ULL << 63);
+
+	for (i = 0; i < 64; i++) {
+		if (map & u)
+			VCLI_Out(cli, "%c", c);
+		else
+			VCLI_Out(cli, "-");
+		map <<= 1;
+	}
+	VCLI_Out(cli, " %s\n", lbl);
+}
+
+/*lint -e{506} constant value boolean */
+/*lint -e{774} constant value boolean */
+static void
+vbp_health_one(struct cli *cli, const struct vbp_target *vt)
+{
+
+	VCLI_Out(cli, "Backend %s is %s\n",
+	    vt->backend->vcl_name,
+	    vt->backend->healthy ? "Healthy" : "Sick");
+	VCLI_Out(cli, "Current states  good: %2u threshold: %2u window: %2u\n",
+	    vt->good, vt->probe.threshold, vt->probe.window);
+	VCLI_Out(cli, "Average responsetime of good probes: %.6f\n", vt->avg);
+	VCLI_Out(cli,
+	    "Oldest                       "
+	    "                             Newest\n");
+	VCLI_Out(cli,
+	    "============================="
+	    "===================================\n");
+
+#define BITMAP(n, c, t, b)					\
+		if ((vt->n != 0) || (b))			\
+			vbp_bitmap(cli, (c), vt->n, (t));
+#include "tbl/backend_poll.h"
+#undef BITMAP
+}
+
+static void
+vbp_health(struct cli *cli, const char * const *av, void *priv)
+{
+	struct vbp_target *vt;
+
+	ASSERT_CLI();
+	(void)av;
+	(void)priv;
+
+	VTAILQ_FOREACH(vt, &vbp_list, list)
+		vbp_health_one(cli, vt);
+}
+
+static struct cli_proto debug_cmds[] = {
+	{ "debug.health", "debug.health",
+		"\tDump backend health stuff\n",
+		0, 0, "d", vbp_health },
+	{ NULL }
+};
+
+/*--------------------------------------------------------------------
+ * A new VCL wants to probe this backend,
+ */
+
+static struct vbp_vcl *
+vbp_new_vcl(const struct vrt_backend_probe *p, const char *hosthdr)
+{
+	struct vbp_vcl *vcl;
+
+	ALLOC_OBJ(vcl, VBP_VCL_MAGIC);
+	XXXAN(vcl);
+	vcl->probep = p;
+	vcl->probe = *p;
+	vcl->hosthdr = hosthdr;
+
+	/*
+	 * Sanitize and insert defaults
+	 * XXX: we could make these defaults parameters
+	 */
+	if (vcl->probe.timeout == 0.0)
+		vcl->probe.timeout = 2.0;
+	if (vcl->probe.interval == 0.0)
+		vcl->probe.interval = 5.0;
+	if (vcl->probe.window == 0)
+		vcl->probe.window = 8;
+	if (vcl->probe.threshold == 0)
+		vcl->probe.threshold = 3;
+	if (vcl->probe.exp_status == 0)
+		vcl->probe.exp_status = 200;
+
+	if (vcl->probe.threshold == ~0U)
+		vcl->probe.initial = vcl->probe.threshold - 1;
+
+	if (vcl->probe.initial > vcl->probe.threshold)
+		vcl->probe.initial = vcl->probe.threshold;
+	return (vcl);
+}
+
+/*--------------------------------------------------------------------
+ * Insert/Remove/Use called from cache_backend.c
+ */
+
+void
+VBP_Insert(struct backend *b, const struct vrt_backend_probe *p, const char *hosthdr)
+{
+	struct vbp_target *vt;
+	struct vbp_vcl *vcl;
+	int startthread = 0;
+	unsigned u;
+
+	ASSERT_CLI();
+	AN(p);
+
+	if (b->probe == NULL) {
+		ALLOC_OBJ(vt, VBP_TARGET_MAGIC);
+		XXXAN(vt);
+		VTAILQ_INIT(&vt->vcls);
+		vt->backend = b;
+		vt->vsb = VSB_new_auto();
+		XXXAN(vt->vsb);
+		b->probe = vt;
+		startthread = 1;
+		VTAILQ_INSERT_TAIL(&vbp_list, vt, list);
+	} else {
+		vt = b->probe;
+	}
+
+	VTAILQ_FOREACH(vcl, &vt->vcls, list)
+		assert (vcl->probep != p);
+
+	vcl = vbp_new_vcl(p, hosthdr);
+	Lck_Lock(&vbp_mtx);
+	VTAILQ_INSERT_TAIL(&vt->vcls, vcl, list);
+	Lck_Unlock(&vbp_mtx);
+
+	if (startthread) {
+		for (u = 0; u < vcl->probe.initial; u++) {
+			vbp_start_poke(vt);
+			vt->happy |= 1;
+			vbp_has_poked(vt);
+		}
+		AZ(pthread_create(&vt->thread, NULL, vbp_wrk_poll_backend, vt));
+	}
+}
+
+void
+VBP_Use(const struct backend *b, const struct vrt_backend_probe *p)
+{
+	struct vbp_target *vt;
+	struct vbp_vcl *vcl;
+
+	ASSERT_CLI();
+	AN(p);
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+	AN(b->probe);
+	vt = b->probe;
+
+	VTAILQ_FOREACH(vcl, &vt->vcls, list) {
+		if (vcl->probep != p)
+			continue;
+
+		Lck_Lock(&vbp_mtx);
+		VTAILQ_REMOVE(&vt->vcls, vcl, list);
+		VTAILQ_INSERT_HEAD(&vt->vcls, vcl, list);
+		Lck_Unlock(&vbp_mtx);
+		return;
+	}
+}
+
+void
+VBP_Remove(struct backend *b, struct vrt_backend_probe const *p)
+{
+	struct vbp_target *vt;
+	struct vbp_vcl *vcl;
+	void *ret;
+
+	ASSERT_CLI();
+	AN(p);
+	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
+	AN(b->probe);
+	vt = b->probe;
+
+	VTAILQ_FOREACH(vcl, &vt->vcls, list)
+		if (vcl->probep == p)
+			break;
+
+	AN(vcl);
+
+	Lck_Lock(&vbp_mtx);
+	VTAILQ_REMOVE(&vt->vcls, vcl, list);
+	Lck_Unlock(&vbp_mtx);
+
+	FREE_OBJ(vcl);
+
+	if (!VTAILQ_EMPTY(&vt->vcls))
+		return;
+
+	/* No more polling for this backend */
+
+	b->healthy = 1;
+
+	vt->stop = 1;
+	AZ(pthread_cancel(vt->thread));
+	AZ(pthread_join(vt->thread, &ret));
+
+	b->healthy = 1;
+
+	VTAILQ_REMOVE(&vbp_list, vt, list);
+	b->probe = NULL;
+	VSB_delete(vt->vsb);
+	FREE_OBJ(vt);
+}
+
+/*--------------------------------------------------------------------
+ * Initialize the backend probe subsystem
+ */
+
+void
+VBP_Init(void)
+{
+
+	Lck_New(&vbp_mtx, lck_vbp);
+	CLI_AddFuncs(debug_cmds);
+}
diff --git a/bin/varnishd/cache/cache_ban.c b/bin/varnishd/cache/cache_ban.c
new file mode 100644
index 0000000..768e631
--- /dev/null
+++ b/bin/varnishd/cache/cache_ban.c
@@ -0,0 +1,1108 @@
+/*-
+ * 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.
+ *
+ * Ban processing
+ *
+ * A ban consists of a number of conditions (or tests), all of which must be
+ * satisfied.  Here are some potential bans we could support:
+ *
+ *	req.url == "/foo"
+ *	req.url ~ ".iso" && obj.size > 10MB
+ *	req.http.host ~ "web1.com" && obj.set-cookie ~ "USER=29293"
+ *
+ * We make the "&&" mandatory from the start, leaving the syntax space
+ * for latter handling of "||" as well.
+ *
+ * Bans are compiled into bytestrings as follows:
+ *	8 bytes	- double: timestamp		XXX: Byteorder ?
+ *	4 bytes - be32: length
+ *	1 byte - flags: 0x01: BAN_F_REQ
+ *	N tests
+ * A test have this form:
+ *	1 byte - arg (see ban_vars.h col 3 "BAN_ARG_XXX")
+ *	(n bytes) - http header name, canonical encoding
+ *	lump - comparison arg
+ *	1 byte - operation (BAN_OPER_)
+ *	(lump) - compiled regexp
+ * A lump is:
+ *	4 bytes - be32: length
+ *	n bytes - content
+ *
+ * In a perfect world, we should vector through VRE to get to PCRE,
+ * but since we rely on PCRE's ability to encode the regexp into a
+ * byte string, that would be a little bit artificial, so this is
+ * the exception that confirmes the rule.
+ *
+ */
+
+#include "config.h"
+
+#include <pcre.h>
+#include <stdio.h>
+
+#include "cache.h"
+
+#include "hash/hash_slinger.h"
+#include "vcli.h"
+#include "vcli_priv.h"
+#include "vend.h"
+#include "vtim.h"
+
+struct ban {
+	unsigned		magic;
+#define BAN_MAGIC		0x700b08ea
+	VTAILQ_ENTRY(ban)	list;
+	unsigned		refcount;
+	unsigned		flags;
+#define BAN_F_GONE		(1 << 0)
+#define BAN_F_REQ		(1 << 2)
+#define BAN_F_LURK		(3 << 6)	/* ban-lurker-color */
+	VTAILQ_HEAD(,objcore)	objcore;
+	struct vsb		*vsb;
+	uint8_t			*spec;
+};
+
+#define LURK_SHIFT 6
+
+struct ban_test {
+	uint8_t			arg1;
+	const char		*arg1_spec;
+	uint8_t			oper;
+	const char		*arg2;
+	const void		*arg2_spec;
+};
+
+static VTAILQ_HEAD(banhead_s,ban) ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
+static struct lock ban_mtx;
+static struct ban *ban_magic;
+static pthread_t ban_thread;
+static struct ban * volatile ban_start;
+static bgthread_t ban_lurker;
+
+/*--------------------------------------------------------------------
+ * BAN string magic markers
+ */
+
+#define	BAN_OPER_EQ	0x10
+#define	BAN_OPER_NEQ	0x11
+#define	BAN_OPER_MATCH	0x12
+#define	BAN_OPER_NMATCH	0x13
+
+#define BAN_ARG_URL	0x18
+#define BAN_ARG_REQHTTP	0x19
+#define BAN_ARG_OBJHTTP	0x1a
+
+/*--------------------------------------------------------------------
+ * Variables we can purge on
+ */
+
+static const struct pvar {
+	const char		*name;
+	unsigned		flag;
+	uint8_t			tag;
+} pvars[] = {
+#define PVAR(a, b, c)	{ (a), (b), (c) },
+#include "tbl/ban_vars.h"
+#undef PVAR
+	{ 0, 0, 0}
+};
+
+/*--------------------------------------------------------------------
+ * Storage handling of bans
+ */
+
+struct ban *
+BAN_New(void)
+{
+	struct ban *b;
+
+	ALLOC_OBJ(b, BAN_MAGIC);
+	if (b == NULL)
+		return (b);
+	b->vsb = VSB_new_auto();
+	if (b->vsb == NULL) {
+		FREE_OBJ(b);
+		return (NULL);
+	}
+	VTAILQ_INIT(&b->objcore);
+	return (b);
+}
+
+void
+BAN_Free(struct ban *b)
+{
+
+	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+	AZ(b->refcount);
+	assert(VTAILQ_EMPTY(&b->objcore));
+
+	if (b->vsb != NULL)
+		VSB_delete(b->vsb);
+	if (b->spec != NULL)
+		free(b->spec);
+	FREE_OBJ(b);
+}
+
+/*--------------------------------------------------------------------
+ * Get & Release a tail reference, used to hold the list stable for
+ * traversals etc.
+ */
+
+struct ban *
+BAN_TailRef(void)
+{
+	struct ban *b;
+
+	ASSERT_CLI();
+	Lck_Lock(&ban_mtx);
+	b = VTAILQ_LAST(&ban_head, banhead_s);
+	AN(b);
+	b->refcount++;
+	Lck_Unlock(&ban_mtx);
+	return (b);
+}
+
+void
+BAN_TailDeref(struct ban **bb)
+{
+	struct ban *b;
+
+	b = *bb;
+	*bb = NULL;
+	Lck_Lock(&ban_mtx);
+	b->refcount--;
+	Lck_Unlock(&ban_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Extract time and length from ban-spec
+ */
+
+static double
+ban_time(const uint8_t *banspec)
+{
+	double t;
+
+	assert(sizeof t == 8);
+	memcpy(&t, banspec, sizeof t);
+	return (t);
+}
+
+static unsigned
+ban_len(const uint8_t *banspec)
+{
+	unsigned u;
+
+	u = vbe32dec(banspec + 8);
+	return (u);
+}
+
+/*--------------------------------------------------------------------
+ * Access a lump of bytes in a ban test spec
+ */
+
+static void
+ban_add_lump(const struct ban *b, const void *p, uint32_t len)
+{
+	uint8_t buf[sizeof len];
+
+	vbe32enc(buf, len);
+	VSB_bcat(b->vsb, buf, sizeof buf);
+	VSB_bcat(b->vsb, p, len);
+}
+
+static const void *
+ban_get_lump(const uint8_t **bs)
+{
+	const void *r;
+	unsigned ln;
+
+	ln = vbe32dec(*bs);
+	*bs += 4;
+	r = (const void*)*bs;
+	*bs += ln;
+	return (r);
+}
+
+/*--------------------------------------------------------------------
+ * Pick a test apart from a spec string
+ */
+
+static void
+ban_iter(const uint8_t **bs, struct ban_test *bt)
+{
+
+	memset(bt, 0, sizeof *bt);
+	bt->arg1 = *(*bs)++;
+	if (bt->arg1 == BAN_ARG_REQHTTP || bt->arg1 == BAN_ARG_OBJHTTP) {
+		bt->arg1_spec = (const char *)*bs;
+		(*bs) += (*bs)[0] + 2;
+	}
+	bt->arg2 = ban_get_lump(bs);
+	bt->oper = *(*bs)++;
+	if (bt->oper == BAN_OPER_MATCH || bt->oper == BAN_OPER_NMATCH)
+		bt->arg2_spec = ban_get_lump(bs);
+}
+
+/*--------------------------------------------------------------------
+ * Parse and add a http argument specification
+ * Output something which HTTP_GetHdr understands
+ */
+
+static void
+ban_parse_http(const struct ban *b, const char *a1)
+{
+	int l;
+
+	l = strlen(a1) + 1;
+	assert(l <= 127);
+	VSB_putc(b->vsb, (char)l);
+	VSB_cat(b->vsb, a1);
+	VSB_putc(b->vsb, ':');
+	VSB_putc(b->vsb, '\0');
+}
+
+/*--------------------------------------------------------------------
+ * Parse and add a ban test specification
+ */
+
+static int
+ban_parse_regexp(struct cli *cli, const struct ban *b, const char *a3)
+{
+	const char *error;
+	int erroroffset, rc;
+	size_t sz;
+	pcre *re;
+
+	re = pcre_compile(a3, 0, &error, &erroroffset, NULL);
+	if (re == NULL) {
+		VSL(SLT_Debug, 0, "REGEX: <%s>", error);
+		VCLI_Out(cli, "%s", error);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return (-1);
+	}
+	rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &sz);
+	AZ(rc);
+	ban_add_lump(b, re, sz);
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Add a (and'ed) test-condition to a ban
+ */
+
+int
+BAN_AddTest(struct cli *cli, struct ban *b, const char *a1, const char *a2,
+    const char *a3)
+{
+	const struct pvar *pv;
+	int i;
+
+	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+
+	for (pv = pvars; pv->name != NULL; pv++)
+		if (!strncmp(a1, pv->name, strlen(pv->name)))
+			break;
+	if (pv->name == NULL) {
+		VCLI_Out(cli, "unknown or unsupported field \"%s\"", a1);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return (-1);
+	}
+
+	if (pv->flag & PVAR_REQ)
+		b->flags |= BAN_F_REQ;
+
+	VSB_putc(b->vsb, pv->tag);
+	if (pv->flag & PVAR_HTTP)
+		ban_parse_http(b, a1 + strlen(pv->name));
+
+	ban_add_lump(b, a3, strlen(a3) + 1);
+	if (!strcmp(a2, "~")) {
+		VSB_putc(b->vsb, BAN_OPER_MATCH);
+		i = ban_parse_regexp(cli, b, a3);
+		if (i)
+			return (i);
+	} else if (!strcmp(a2, "!~")) {
+		VSB_putc(b->vsb, BAN_OPER_NMATCH);
+		i = ban_parse_regexp(cli, b, a3);
+		if (i)
+			return (i);
+	} else if (!strcmp(a2, "==")) {
+		VSB_putc(b->vsb, BAN_OPER_EQ);
+	} else if (!strcmp(a2, "!=")) {
+		VSB_putc(b->vsb, BAN_OPER_NEQ);
+	} else {
+		VCLI_Out(cli,
+		    "expected conditional (~, !~, == or !=) got \"%s\"", a2);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return (-1);
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * We maintain ban_start as a pointer to the first element of the list
+ * as a separate variable from the VTAILQ, to avoid depending on the
+ * internals of the VTAILQ macros.  We tacitly assume that a pointer
+ * write is always atomic in doing so.
+ */
+
+void
+BAN_Insert(struct ban *b)
+{
+	struct ban  *bi, *be;
+	ssize_t ln;
+	double t0;
+
+	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+
+	AZ(VSB_finish(b->vsb));
+	ln = VSB_len(b->vsb);
+	assert(ln >= 0);
+
+	b->spec = malloc(ln + 13L);	/* XXX */
+	XXXAN(b->spec);
+
+	t0 = VTIM_real();
+	memcpy(b->spec, &t0, sizeof t0);
+	b->spec[12] = (b->flags & BAN_F_REQ) ? 1 : 0;
+	memcpy(b->spec + 13, VSB_data(b->vsb), ln);
+	ln += 13;
+	vbe32enc(b->spec + 8, ln);
+
+	VSB_delete(b->vsb);
+	b->vsb = NULL;
+
+	Lck_Lock(&ban_mtx);
+	VTAILQ_INSERT_HEAD(&ban_head, b, list);
+	ban_start = b;
+	VSC_C_main->bans++;
+	VSC_C_main->bans_added++;
+	if (b->flags & BAN_F_REQ)
+		VSC_C_main->bans_req++;
+
+	be = VTAILQ_LAST(&ban_head, banhead_s);
+	if (cache_param->ban_dups && be != b)
+		be->refcount++;
+	else
+		be = NULL;
+
+	SMP_NewBan(b->spec, ln);
+	Lck_Unlock(&ban_mtx);
+
+	if (be == NULL)
+		return;
+
+	/* Hunt down duplicates, and mark them as gone */
+	bi = b;
+	Lck_Lock(&ban_mtx);
+	while(bi != be) {
+		bi = VTAILQ_NEXT(bi, list);
+		if (bi->flags & BAN_F_GONE)
+			continue;
+		/* Safe because the length is part of the fixed size hdr */
+		if (memcmp(b->spec + 8, bi->spec + 8, ln - 8))
+			continue;
+		bi->flags |= BAN_F_GONE;
+		VSC_C_main->bans_gone++;
+		VSC_C_main->bans_dups++;
+	}
+	be->refcount--;
+	Lck_Unlock(&ban_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * A new object is created, grab a reference to the newest ban
+ */
+
+void
+BAN_NewObjCore(struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AZ(oc->ban);
+	Lck_Lock(&ban_mtx);
+	oc->ban = ban_start;
+	ban_start->refcount++;
+	VTAILQ_INSERT_TAIL(&ban_start->objcore, oc, ban_list);
+	Lck_Unlock(&ban_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * An object is destroyed, release its ban reference
+ */
+
+void
+BAN_DestroyObj(struct objcore *oc)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	if (oc->ban == NULL)
+		return;
+	CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
+	Lck_Lock(&ban_mtx);
+	assert(oc->ban->refcount > 0);
+	oc->ban->refcount--;
+	VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
+	oc->ban = NULL;
+	Lck_Unlock(&ban_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Find and/or Grab a reference to an objects ban based on timestamp
+ * Assume we hold a TailRef, so list traversal is safe.
+ */
+
+struct ban *
+BAN_RefBan(struct objcore *oc, double t0, const struct ban *tail)
+{
+	struct ban *b;
+	double t1 = 0;
+
+	VTAILQ_FOREACH(b, &ban_head, list) {
+		t1 = ban_time(b->spec);
+		if (t1 <= t0)
+			break;
+		if (b == tail)
+			break;
+	}
+	AN(b);
+	assert(t1 == t0);
+	Lck_Lock(&ban_mtx);
+	b->refcount++;
+	VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
+	Lck_Unlock(&ban_mtx);
+	return (b);
+}
+
+/*--------------------------------------------------------------------
+ * Put a skeleton ban in the list, unless there is an identical,
+ * time & condition, ban already in place.
+ *
+ * If a newer ban has same condition, mark the new ban GONE.
+ * mark any older bans, with the same condition, GONE as well.
+ */
+
+void
+BAN_Reload(const uint8_t *ban, unsigned len)
+{
+	struct ban *b, *b2;
+	int gone = 0;
+	double t0, t1, t2 = 9e99;
+
+	ASSERT_CLI();
+
+	t0 = ban_time(ban);
+	assert(len == ban_len(ban));
+
+	Lck_Lock(&ban_mtx);
+
+	VTAILQ_FOREACH(b, &ban_head, list) {
+		t1 = ban_time(b->spec);
+		assert (t1 < t2);
+		t2 = t1;
+		if (t1 == t0) {
+			Lck_Unlock(&ban_mtx);
+			return;
+		}
+		if (t1 < t0)
+			break;
+		if (!memcmp(b->spec + 8, ban + 8, len - 8)) {
+			gone |= BAN_F_GONE;
+			VSC_C_main->bans_dups++;
+			VSC_C_main->bans_gone++;
+		}
+	}
+
+	VSC_C_main->bans++;
+	VSC_C_main->bans_added++;
+
+	b2 = BAN_New();
+	AN(b2);
+	b2->spec = malloc(len);
+	AN(b2->spec);
+	memcpy(b2->spec, ban, len);
+	b2->flags |= gone;
+	if (ban[12])
+		b2->flags |= BAN_F_REQ;
+	if (b == NULL)
+		VTAILQ_INSERT_TAIL(&ban_head, b2, list);
+	else
+		VTAILQ_INSERT_BEFORE(b, b2, list);
+
+	/* Hunt down older duplicates */
+	for (b = VTAILQ_NEXT(b2, list); b != NULL; b = VTAILQ_NEXT(b, list)) {
+		if (b->flags & BAN_F_GONE)
+			continue;
+		if (!memcmp(b->spec + 8, ban + 8, len - 8)) {
+			b->flags |= BAN_F_GONE;
+			VSC_C_main->bans_dups++;
+			VSC_C_main->bans_gone++;
+		}
+	}
+	Lck_Unlock(&ban_mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Get a bans timestamp
+ */
+
+double
+BAN_Time(const struct ban *b)
+{
+
+	if (b == NULL)
+		return (0.0);
+
+	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+	return (ban_time(b->spec));
+}
+
+/*--------------------------------------------------------------------
+ * All silos have read their bans, ready for action
+ */
+
+void
+BAN_Compile(void)
+{
+
+	ASSERT_CLI();
+
+	SMP_NewBan(ban_magic->spec, ban_len(ban_magic->spec));
+	ban_start = VTAILQ_FIRST(&ban_head);
+	WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
+}
+
+/*--------------------------------------------------------------------
+ * Evaluate ban-spec
+ */
+
+static int
+ban_evaluate(const uint8_t *bs, const struct http *objhttp,
+    const struct http *reqhttp, unsigned *tests)
+{
+	struct ban_test bt;
+	const uint8_t *be;
+	char *arg1;
+
+	be = bs + ban_len(bs);
+	bs += 13;
+	while (bs < be) {
+		(*tests)++;
+		ban_iter(&bs, &bt);
+		arg1 = NULL;
+		switch (bt.arg1) {
+		case BAN_ARG_URL:
+			arg1 = reqhttp->hd[HTTP_HDR_URL].b;
+			break;
+		case BAN_ARG_REQHTTP:
+			(void)http_GetHdr(reqhttp, bt.arg1_spec, &arg1);
+			break;
+		case BAN_ARG_OBJHTTP:
+			(void)http_GetHdr(objhttp, bt.arg1_spec, &arg1);
+			break;
+		default:
+			INCOMPL();
+		}
+
+		switch (bt.oper) {
+		case BAN_OPER_EQ:
+			if (arg1 == NULL || strcmp(arg1, bt.arg2))
+				return (0);
+			break;
+		case BAN_OPER_NEQ:
+			if (arg1 != NULL && !strcmp(arg1, bt.arg2))
+				return (0);
+			break;
+		case BAN_OPER_MATCH:
+			if (arg1 == NULL ||
+			    pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
+			    0, 0, NULL, 0) < 0)
+				return (0);
+			break;
+		case BAN_OPER_NMATCH:
+			if (arg1 != NULL &&
+			    pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
+			    0, 0, NULL, 0) >= 0)
+				return (0);
+			break;
+		default:
+			INCOMPL();
+		}
+	}
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Check an object against all applicable bans
+ *
+ * Return:
+ *	-1 not all bans checked, but none of the checked matched
+ *		Only if !has_req
+ *	0 No bans matched, object moved to ban_start.
+ *	1 Ban matched, object removed from ban list.
+ */
+
+static int
+ban_check_object(struct object *o, const struct sess *sp, int has_req)
+{
+	struct ban *b;
+	struct objcore *oc;
+	struct ban * volatile b0;
+	unsigned tests, skipped;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	oc = o->objcore;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
+
+	b0 = ban_start;
+	CHECK_OBJ_NOTNULL(b0, BAN_MAGIC);
+
+	if (b0 == oc->ban)
+		return (0);
+
+	/*
+	 * This loop is safe without locks, because we know we hold
+	 * a refcount on a ban somewhere in the list and we do not
+	 * inspect the list past that ban.
+	 */
+	tests = 0;
+	skipped = 0;
+	for (b = b0; b != oc->ban; b = VTAILQ_NEXT(b, list)) {
+		CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+		if (b->flags & BAN_F_GONE)
+			continue;
+		if ((b->flags & BAN_F_LURK) &&
+		    (b->flags & BAN_F_LURK) == (oc->flags & OC_F_LURK)) {
+			AZ(b->flags & BAN_F_REQ);
+			/* Lurker already tested this */
+			continue;
+		}
+		if (!has_req && (b->flags & BAN_F_REQ)) {
+			/*
+			 * We cannot test this one, but there might
+			 * be other bans that match, so we soldier on
+			 */
+			skipped++;
+		} else if (ban_evaluate(b->spec, o->http, sp->http, &tests))
+			break;
+	}
+
+	Lck_Lock(&ban_mtx);
+	VSC_C_main->bans_tested++;
+	VSC_C_main->bans_tests_tested += tests;
+
+	if (b == oc->ban && skipped > 0) {
+		AZ(has_req);
+		Lck_Unlock(&ban_mtx);
+		/*
+		 * Not banned, but some tests were skipped, so we cannot know
+		 * for certain that it cannot be, so we just have to give up.
+		 */
+		return (-1);
+	}
+
+	oc->ban->refcount--;
+	VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
+	if (b == oc->ban) {	/* not banned */
+		b->flags &= ~BAN_F_LURK;
+		VTAILQ_INSERT_TAIL(&b0->objcore, oc, ban_list);
+		b0->refcount++;
+	}
+	Lck_Unlock(&ban_mtx);
+
+	if (b == oc->ban) {	/* not banned */
+		oc->ban = b0;
+		oc_updatemeta(oc);
+		return (0);
+	} else {
+		EXP_Clr(&o->exp);
+		oc->ban = NULL;
+		oc_updatemeta(oc);
+		/* BAN also changed, but that is not important any more */
+		WSP(sp, SLT_ExpBan, "%u was banned", o->xid);
+		EXP_Rearm(o);
+		return (1);
+	}
+}
+
+int
+BAN_CheckObject(struct object *o, const struct sess *sp)
+{
+
+	return (ban_check_object(o, sp, 1) > 0);
+}
+
+static struct ban *
+ban_CheckLast(void)
+{
+	struct ban *b;
+
+	Lck_AssertHeld(&ban_mtx);
+	b = VTAILQ_LAST(&ban_head, banhead_s);
+	if (b != VTAILQ_FIRST(&ban_head) && b->refcount == 0) {
+		if (b->flags & BAN_F_GONE)
+			VSC_C_main->bans_gone--;
+		if (b->flags & BAN_F_REQ)
+			VSC_C_main->bans_req--;
+		VSC_C_main->bans--;
+		VSC_C_main->bans_deleted++;
+		VTAILQ_REMOVE(&ban_head, b, list);
+	} else {
+		b = NULL;
+	}
+	return (b);
+}
+
+/*--------------------------------------------------------------------
+ * Ban lurker thread
+ */
+
+static int
+ban_lurker_work(const struct sess *sp, unsigned pass)
+{
+	struct ban *b, *b0, *b2;
+	struct objhead *oh;
+	struct objcore *oc, *oc2;
+	struct object *o;
+	int i;
+
+	AN(pass & BAN_F_LURK);
+	AZ(pass & ~BAN_F_LURK);
+
+	/* First route the last ban(s) */
+	do {
+		Lck_Lock(&ban_mtx);
+		b2 = ban_CheckLast();
+		Lck_Unlock(&ban_mtx);
+		if (b2 != NULL)
+			BAN_Free(b2);
+	} while (b2 != NULL);
+
+	/*
+	 * Find out if we have any bans we can do something about
+	 * If we find any, tag them with our pass number.
+	 */
+	i = 0;
+	b0 = NULL;
+	VTAILQ_FOREACH(b, &ban_head, list) {
+		if (b->flags & BAN_F_GONE)
+			continue;
+		if (b->flags & BAN_F_REQ)
+			continue;
+		if (b == VTAILQ_LAST(&ban_head, banhead_s))
+			continue;
+		if (b0 == NULL)
+			b0 = b;
+		i++;
+		b->flags &= ~BAN_F_LURK;
+		b->flags |= pass;
+	}
+	if (cache_param->diag_bitmap & 0x80000)
+		VSL(SLT_Debug, 0, "lurker: %d actionable bans", i);
+	if (i == 0)
+		return (0);
+
+	VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list) {
+		if (cache_param->diag_bitmap & 0x80000)
+			VSL(SLT_Debug, 0, "lurker doing %f %d",
+			    ban_time(b->spec), b->refcount);
+		while (1) {
+			Lck_Lock(&ban_mtx);
+			oc = VTAILQ_FIRST(&b->objcore);
+			if (oc == NULL)
+				break;
+			CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+			if (cache_param->diag_bitmap & 0x80000)
+				VSL(SLT_Debug, 0, "test: %p %d %d",
+				    oc, oc->flags & OC_F_LURK, pass);
+			if ((oc->flags & OC_F_LURK) == pass)
+				break;
+			oh = oc->objhead;
+			CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+			if (Lck_Trylock(&oh->mtx)) {
+				Lck_Unlock(&ban_mtx);
+				VTIM_sleep(cache_param->ban_lurker_sleep);
+				continue;
+			}
+			/*
+			 * See if the objcore is still on the objhead since
+			 * we race against HSH_Deref() which comes in the
+			 * opposite locking order.
+			 */
+			VTAILQ_FOREACH(oc2, &oh->objcs, list)
+				if (oc == oc2)
+					break;
+			if (oc2 == NULL) {
+				Lck_Unlock(&oh->mtx);
+				Lck_Unlock(&ban_mtx);
+				VTIM_sleep(cache_param->ban_lurker_sleep);
+				continue;
+			}
+			/*
+			 * Grab a reference to the OC and we can let go of
+			 * the BAN mutex
+			 */
+			AN(oc->refcnt);
+			oc->refcnt++;
+			oc->flags &= ~OC_F_LURK;
+			Lck_Unlock(&ban_mtx);
+			/*
+			 * Get the object and check it against all relevant bans
+			 */
+			o = oc_getobj(sp->wrk, oc);
+			i = ban_check_object(o, sp, 0);
+			if (cache_param->diag_bitmap & 0x80000)
+				VSL(SLT_Debug, 0, "lurker got: %p %d",
+				    oc, i);
+			if (i == -1) {
+				/* Not banned, not moved */
+				oc->flags |= pass;
+				Lck_Lock(&ban_mtx);
+				VTAILQ_REMOVE(&b->objcore, oc, ban_list);
+				VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
+				Lck_Unlock(&ban_mtx);
+			}
+			Lck_Unlock(&oh->mtx);
+			if (cache_param->diag_bitmap & 0x80000)
+				VSL(SLT_Debug, 0, "lurker done: %p %d %d",
+				    oc, oc->flags & OC_F_LURK, pass);
+			(void)HSH_Deref(sp->wrk, NULL, &o);
+			VTIM_sleep(cache_param->ban_lurker_sleep);
+		}
+		Lck_AssertHeld(&ban_mtx);
+		if (!(b->flags & BAN_F_REQ)) {
+			if (!(b->flags & BAN_F_GONE)) {
+				b->flags |= BAN_F_GONE;
+				VSC_C_main->bans_gone++;
+			}
+			if (cache_param->diag_bitmap & 0x80000)
+				VSL(SLT_Debug, 0, "lurker BAN %f now gone",
+				    ban_time(b->spec));
+		}
+		Lck_Unlock(&ban_mtx);
+		VTIM_sleep(cache_param->ban_lurker_sleep);
+		if (b == b0)
+			break;
+	}
+	return (1);
+}
+
+static void * __match_proto__(bgthread_t)
+ban_lurker(struct sess *sp, void *priv)
+{
+	struct ban *bf;
+	unsigned pass = (1 << LURK_SHIFT);
+
+	int i = 0;
+	(void)priv;
+	while (1) {
+
+		while (cache_param->ban_lurker_sleep == 0.0) {
+			/*
+			 * Ban-lurker is disabled:
+			 * Clean the last ban, if possible, and sleep
+			 */
+			Lck_Lock(&ban_mtx);
+			bf = ban_CheckLast();
+			Lck_Unlock(&ban_mtx);
+			if (bf != NULL)
+				BAN_Free(bf);
+			else
+				VTIM_sleep(1.0);
+		}
+
+		i = ban_lurker_work(sp, pass);
+		WSL_Flush(sp->wrk, 0);
+		WRK_SumStat(sp->wrk);
+		if (i) {
+			pass += (1 << LURK_SHIFT);
+			pass &= BAN_F_LURK;
+			if (pass == 0)
+				pass += (1 << LURK_SHIFT);
+			VTIM_sleep(cache_param->ban_lurker_sleep);
+		} else {
+			VTIM_sleep(1.0);
+		}
+	}
+	NEEDLESS_RETURN(NULL);
+}
+
+/*--------------------------------------------------------------------
+ * CLI functions to add bans
+ */
+
+static void
+ccf_ban(struct cli *cli, const char * const *av, void *priv)
+{
+	int narg, i;
+	struct ban *b;
+
+	(void)priv;
+
+	/* First do some cheap checks on the arguments */
+	for (narg = 0; av[narg + 2] != NULL; narg++)
+		continue;
+	if ((narg % 4) != 3) {
+		VCLI_Out(cli, "Wrong number of arguments");
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return;
+	}
+	for (i = 3; i < narg; i += 4) {
+		if (strcmp(av[i + 2], "&&")) {
+			VCLI_Out(cli, "Found \"%s\" expected &&", av[i + 2]);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+	}
+
+	b = BAN_New();
+	if (b == NULL) {
+		VCLI_Out(cli, "Out of Memory");
+		VCLI_SetResult(cli, CLIS_CANT);
+		return;
+	}
+	for (i = 0; i < narg; i += 4)
+		if (BAN_AddTest(cli, b, av[i + 2], av[i + 3], av[i + 4])) {
+			BAN_Free(b);
+			return;
+		}
+	BAN_Insert(b);
+}
+
+static void
+ccf_ban_url(struct cli *cli, const char * const *av, void *priv)
+{
+	const char *aav[6];
+
+	(void)priv;
+	aav[0] = NULL;
+	aav[1] = "ban";
+	aav[2] = "req.url";
+	aav[3] = "~";
+	aav[4] = av[2];
+	aav[5] = NULL;
+	ccf_ban(cli, aav, priv);
+}
+
+static void
+ban_render(struct cli *cli, const uint8_t *bs)
+{
+	struct ban_test bt;
+	const uint8_t *be;
+
+	be = bs + ban_len(bs);
+	bs += 13;
+	while (bs < be) {
+		ban_iter(&bs, &bt);
+		switch (bt.arg1) {
+		case BAN_ARG_URL:
+			VCLI_Out(cli, "req.url");
+			break;
+		case BAN_ARG_REQHTTP:
+			VCLI_Out(cli, "req.http.%.*s",
+			    bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
+			break;
+		case BAN_ARG_OBJHTTP:
+			VCLI_Out(cli, "obj.http.%.*s",
+			    bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
+			break;
+		default:
+			INCOMPL();
+		}
+		switch (bt.oper) {
+		case BAN_OPER_EQ:	VCLI_Out(cli, " == "); break;
+		case BAN_OPER_NEQ:	VCLI_Out(cli, " != "); break;
+		case BAN_OPER_MATCH:	VCLI_Out(cli, " ~ "); break;
+		case BAN_OPER_NMATCH:	VCLI_Out(cli, " !~ "); break;
+		default:
+			INCOMPL();
+		}
+		VCLI_Out(cli, "%s", bt.arg2);
+		if (bs < be)
+			VCLI_Out(cli, " && ");
+	}
+}
+
+static void
+ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
+{
+	struct ban *b, *bl;
+
+	(void)av;
+	(void)priv;
+
+	/* Get a reference so we are safe to traverse the list */
+	bl = BAN_TailRef();
+
+	VCLI_Out(cli, "Present bans:\n");
+	VTAILQ_FOREACH(b, &ban_head, list) {
+		if (b == bl && !(cache_param->diag_bitmap & 0x80000))
+			break;
+		VCLI_Out(cli, "%10.6f %5u%s\t", ban_time(b->spec),
+		    bl == b ? b->refcount - 1 : b->refcount,
+		    b->flags & BAN_F_GONE ? "G" : " ");
+		ban_render(cli, b->spec);
+		VCLI_Out(cli, "\n");
+		if (cache_param->diag_bitmap & 0x80000) {
+			Lck_Lock(&ban_mtx);
+			struct objcore *oc;
+			VTAILQ_FOREACH(oc, &b->objcore, ban_list)
+				VCLI_Out(cli, "     %p\n", oc);
+			Lck_Unlock(&ban_mtx);
+		}
+	}
+
+	BAN_TailDeref(&bl);
+}
+
+static struct cli_proto ban_cmds[] = {
+	{ CLI_BAN_URL,				"", ccf_ban_url },
+	{ CLI_BAN,				"", ccf_ban },
+	{ CLI_BAN_LIST,				"", ccf_ban_list },
+	{ NULL }
+};
+
+void
+BAN_Init(void)
+{
+
+	Lck_New(&ban_mtx, lck_ban);
+	CLI_AddFuncs(ban_cmds);
+	assert(BAN_F_LURK == OC_F_LURK);
+	AN((1 << LURK_SHIFT) & BAN_F_LURK);
+	AN((2 << LURK_SHIFT) & BAN_F_LURK);
+
+	ban_magic = BAN_New();
+	AN(ban_magic);
+	ban_magic->flags |= BAN_F_GONE;
+	VSC_C_main->bans_gone++;
+	BAN_Insert(ban_magic);
+}
diff --git a/bin/varnishd/cache/cache_center.c b/bin/varnishd/cache/cache_center.c
new file mode 100644
index 0000000..e42fac8
--- /dev/null
+++ b/bin/varnishd/cache/cache_center.c
@@ -0,0 +1,1691 @@
+/*-
+ * 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.
+ *
+ * This file contains the central state machine for pushing requests.
+ *
+ * We cannot just use direct calls because it is possible to kick a
+ * request back to the lookup stage (usually after a rewrite).  The
+ * state engine also allows us to break the processing up into some
+ * logical chunks which improves readability a little bit.
+ *
+ * Since the states are rather nasty in detail, I have decided to embedd
+ * a dot(1) graph in the source code comments.  So to see the big picture,
+ * extract the DOT lines and run though dot(1), for instance with the
+ * command:
+ *	sed -n '/^DOT/s///p' cache_center.c | dot -Tps > /tmp/_.ps
+ */
+
+/*
+DOT digraph vcl_center {
+xDOT	page="8.2,11.5"
+DOT	size="7.2,10.5"
+DOT	margin="0.5"
+DOT	center="1"
+DOT acceptor [
+DOT	shape=hexagon
+DOT	label="Request received"
+DOT ]
+DOT ERROR [shape=plaintext]
+DOT RESTART [shape=plaintext]
+DOT acceptor -> start [style=bold,color=green]
+ */
+
+#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 "vcli_priv.h"
+#include "vsha256.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+#ifndef HAVE_SRANDOMDEV
+#include "compat/srandomdev.h"
+#endif
+
+static unsigned xids;
+
+/*--------------------------------------------------------------------
+ * WAIT
+ * Wait (briefly) until we have a full request in our htc.
+ */
+
+static int
+cnt_wait(struct sess *sp)
+{
+	int i;
+	struct pollfd pfd[1];
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	AZ(sp->vcl);
+	AZ(sp->obj);
+	assert(sp->xid == 0);
+
+	i = HTC_Complete(sp->htc);
+	if (i == 0 && cache_param->session_linger > 0) {
+		pfd[0].fd = sp->fd;
+		pfd[0].events = POLLIN;
+		pfd[0].revents = 0;
+		i = poll(pfd, 1, cache_param->session_linger);
+		if (i)
+			i = HTC_Rx(sp->htc);
+	}
+	if (i == 0) {
+		WSP(sp, SLT_Debug, "herding");
+		sp->wrk->stats.sess_herd++;
+		SES_Charge(sp);
+		sp->wrk = NULL;
+		Pool_Wait(sp);
+		return (1);
+	}
+	if (i == 1) {
+		sp->step = STP_START;
+		return (0);
+	}
+	if (i == -2) {
+		SES_Close(sp, "overflow");
+		return (0);
+	}
+	if (i == -1 && Tlen(sp->htc->rxbuf) == 0 &&
+	    (errno == 0 || errno == ECONNRESET))
+		SES_Close(sp, "EOF");
+	else
+		SES_Close(sp, "error");
+	sp->step = STP_DONE;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * We have a refcounted object on the session, now deliver it.
+ *
+DOT subgraph xcluster_prepresp {
+DOT	prepresp [
+DOT		shape=ellipse
+DOT		label="Filter obj.->resp."
+DOT	]
+DOT	vcl_deliver [
+DOT		shape=record
+DOT		label="vcl_deliver()|resp."
+DOT	]
+DOT	prepresp -> vcl_deliver [style=bold,color=green]
+DOT	prepresp -> vcl_deliver [style=bold,color=cyan]
+DOT	prepresp -> vcl_deliver [style=bold,color=red]
+DOT	prepresp -> vcl_deliver [style=bold,color=blue,]
+DOT	vcl_deliver -> deliver [style=bold,color=green,label=deliver]
+DOT	vcl_deliver -> deliver [style=bold,color=red]
+DOT	vcl_deliver -> deliver [style=bold,color=blue]
+DOT     vcl_deliver -> errdeliver [label="error"]
+DOT     errdeliver [label="ERROR",shape=plaintext]
+DOT     vcl_deliver -> rstdeliver [label="restart",color=purple]
+DOT     rstdeliver [label="RESTART",shape=plaintext]
+DOT     vcl_deliver -> streambody [style=bold,color=cyan,label="deliver"]
+DOT }
+ *
+ */
+
+static int
+cnt_prepresp(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	if (sp->wrk->do_stream)
+		AssertObjCorePassOrBusy(sp->obj->objcore);
+
+	sp->wrk->res_mode = 0;
+
+	if ((sp->wrk->h_content_length != NULL || !sp->wrk->do_stream) &&
+	    !sp->wrk->do_gzip && !sp->wrk->do_gunzip)
+		sp->wrk->res_mode |= RES_LEN;
+
+	if (!sp->disable_esi && sp->obj->esidata != NULL) {
+		/* In ESI mode, we don't know the aggregate length */
+		sp->wrk->res_mode &= ~RES_LEN;
+		sp->wrk->res_mode |= RES_ESI;
+	}
+
+	if (sp->esi_level > 0) {
+		sp->wrk->res_mode &= ~RES_LEN;
+		sp->wrk->res_mode |= RES_ESI_CHILD;
+	}
+
+	if (cache_param->http_gzip_support && sp->obj->gziped &&
+	    !RFC2616_Req_Gzip(sp)) {
+		/*
+		 * We don't know what it uncompresses to
+		 * XXX: we could cache that
+		 */
+		sp->wrk->res_mode &= ~RES_LEN;
+		sp->wrk->res_mode |= RES_GUNZIP;
+	}
+
+	if (!(sp->wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
+		if (sp->obj->len == 0 && !sp->wrk->do_stream)
+			/*
+			 * If the object is empty, neither ESI nor GUNZIP
+			 * can make it any different size
+			 */
+			sp->wrk->res_mode |= RES_LEN;
+		else if (!sp->wantbody) {
+			/* Nothing */
+		} else if (sp->http->protover >= 11) {
+			sp->wrk->res_mode |= RES_CHUNKED;
+		} else {
+			sp->wrk->res_mode |= RES_EOF;
+			sp->doclose = "EOF mode";
+		}
+	}
+
+	sp->t_resp = VTIM_real();
+	if (sp->obj->objcore != NULL) {
+		if ((sp->t_resp - sp->obj->last_lru) > cache_param->lru_timeout &&
+		    EXP_Touch(sp->obj->objcore))
+			sp->obj->last_lru = sp->t_resp;
+		sp->obj->last_use = sp->t_resp;	/* XXX: locking ? */
+	}
+	http_Setup(sp->wrk->resp, sp->wrk->ws);
+	RES_BuildHttp(sp);
+	VCL_deliver_method(sp);
+	switch (sp->handling) {
+	case VCL_RET_DELIVER:
+		break;
+	case VCL_RET_RESTART:
+		if (sp->restarts >= cache_param->max_restarts)
+			break;
+		if (sp->wrk->do_stream) {
+			VDI_CloseFd(sp->wrk);
+			HSH_Drop(sp);
+		} else {
+			(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+		}
+		AZ(sp->obj);
+		sp->restarts++;
+		sp->director = NULL;
+		sp->wrk->h_content_length = NULL;
+		http_Setup(sp->wrk->bereq, NULL);
+		http_Setup(sp->wrk->beresp, NULL);
+		http_Setup(sp->wrk->resp, NULL);
+		sp->step = STP_RECV;
+		return (0);
+	default:
+		WRONG("Illegal action in vcl_deliver{}");
+	}
+	if (sp->wrk->do_stream) {
+		AssertObjCorePassOrBusy(sp->obj->objcore);
+		sp->step = STP_STREAMBODY;
+	} else {
+		sp->step = STP_DELIVER;
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Deliver an already stored object
+ *
+DOT subgraph xcluster_deliver {
+DOT	deliver [
+DOT		shape=ellipse
+DOT		label="Send body"
+DOT	]
+DOT }
+DOT deliver -> DONE [style=bold,color=green]
+DOT deliver -> DONE [style=bold,color=red]
+DOT deliver -> DONE [style=bold,color=blue]
+ *
+ */
+
+static int
+cnt_deliver(struct sess *sp)
+{
+
+	sp->director = NULL;
+	sp->restarts = 0;
+
+	RES_WriteObj(sp);
+
+	assert(WRW_IsReleased(sp->wrk));
+	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
+	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+	http_Setup(sp->wrk->resp, NULL);
+	sp->step = STP_DONE;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * This is the final state, figure out if we should close or recycle
+ * the client connection
+ *
+DOT	DONE [
+DOT		shape=hexagon
+DOT		label="Request completed"
+DOT	]
+ */
+
+static int
+cnt_done(struct sess *sp)
+{
+	double dh, dp, da;
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	AZ(sp->obj);
+	AZ(sp->wrk->vbc);
+	sp->director = NULL;
+	sp->restarts = 0;
+
+	sp->wrk->do_esi = 0;
+	sp->wrk->do_gunzip = 0;
+	sp->wrk->do_gzip = 0;
+	sp->wrk->do_stream = 0;
+	sp->wrk->is_gunzip = 0;
+	sp->wrk->is_gzip = 0;
+
+	if (sp->vcl != NULL && sp->esi_level == 0) {
+		if (sp->wrk->vcl != NULL)
+			VCL_Rel(&sp->wrk->vcl);
+		sp->wrk->vcl = sp->vcl;
+		sp->vcl = NULL;
+	}
+
+	SES_Charge(sp);
+
+	sp->t_end = VTIM_real();
+	sp->wrk->lastused = sp->t_end;
+	if (sp->xid == 0) {
+		sp->t_req = sp->t_end;
+		sp->t_resp = sp->t_end;
+	} else if (sp->esi_level == 0) {
+		dp = sp->t_resp - sp->t_req;
+		da = sp->t_end - sp->t_resp;
+		dh = sp->t_req - sp->t_open;
+		/* XXX: Add StatReq == StatSess */
+		/* XXX: Workaround for pipe */
+		if (sp->fd >= 0) {
+			WSP(sp, SLT_Length, "%ju",
+			    (uintmax_t)sp->req_bodybytes);
+		}
+		WSP(sp, SLT_ReqEnd, "%u %.9f %.9f %.9f %.9f %.9f",
+		    sp->xid, sp->t_req, sp->t_end, dh, dp, da);
+	}
+	sp->xid = 0;
+	sp->t_open = sp->t_end;
+	sp->t_resp = NAN;
+	WSL_Flush(sp->wrk, 0);
+
+	/* If we did an ESI include, don't mess up our state */
+	if (sp->esi_level > 0)
+		return (1);
+
+	sp->req_bodybytes = 0;
+
+	sp->t_req = NAN;
+	sp->hash_always_miss = 0;
+	sp->hash_ignore_busy = 0;
+
+	if (sp->fd >= 0 && sp->doclose != NULL) {
+		/*
+		 * This is an orderly close of the connection; ditch nolinger
+		 * before we close, to get queued data transmitted.
+		 */
+		// XXX: not yet (void)VTCP_linger(sp->fd, 0);
+		SES_Close(sp, sp->doclose);
+	}
+
+	if (sp->fd < 0) {
+		sp->wrk->stats.sess_closed++;
+		SES_Delete(sp, NULL);
+		return (1);
+	}
+
+	if (sp->wrk->stats.client_req >= cache_param->wthread_stats_rate)
+		WRK_SumStat(sp->wrk);
+	/* Reset the workspace to the session-watermark */
+	WS_Reset(sp->ws, sp->ws_ses);
+	WS_Reset(sp->wrk->ws, NULL);
+
+	i = HTC_Reinit(sp->htc);
+	if (i == 1) {
+		sp->wrk->stats.sess_pipeline++;
+		sp->step = STP_START;
+		return (0);
+	}
+	if (Tlen(sp->htc->rxbuf)) {
+		sp->wrk->stats.sess_readahead++;
+		sp->step = STP_WAIT;
+		return (0);
+	}
+	if (cache_param->session_linger > 0) {
+		sp->wrk->stats.sess_linger++;
+		sp->step = STP_WAIT;
+		return (0);
+	}
+	sp->wrk->stats.sess_herd++;
+	sp->wrk = NULL;
+	Pool_Wait(sp);
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Emit an error
+ *
+DOT subgraph xcluster_error {
+DOT	vcl_error [
+DOT		shape=record
+DOT		label="vcl_error()|resp."
+DOT	]
+DOT	ERROR -> vcl_error
+DOT	vcl_error-> prepresp [label=deliver]
+DOT }
+DOT vcl_error-> rsterr [label="restart",color=purple]
+DOT rsterr [label="RESTART",shape=plaintext]
+ */
+
+static int
+cnt_error(struct sess *sp)
+{
+	struct worker *w;
+	struct http *h;
+	char date[40];
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	sp->wrk->do_esi = 0;
+	sp->wrk->is_gzip = 0;
+	sp->wrk->is_gunzip = 0;
+	sp->wrk->do_gzip = 0;
+	sp->wrk->do_gunzip = 0;
+	sp->wrk->do_stream = 0;
+
+	w = sp->wrk;
+	if (sp->obj == NULL) {
+		HSH_Prealloc(sp);
+		EXP_Clr(&w->exp);
+		sp->obj = STV_NewObject(sp, NULL, cache_param->http_resp_size,
+		     &w->exp, (uint16_t)cache_param->http_max_hdr);
+		if (sp->obj == NULL)
+			sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE,
+			    cache_param->http_resp_size, &w->exp,
+			    (uint16_t)cache_param->http_max_hdr);
+		if (sp->obj == NULL) {
+			sp->doclose = "Out of objects";
+			sp->director = NULL;
+			sp->wrk->h_content_length = NULL;
+			http_Setup(sp->wrk->beresp, NULL);
+			http_Setup(sp->wrk->bereq, NULL);
+			sp->step = STP_DONE;
+			return(0);
+		}
+		AN(sp->obj);
+		sp->obj->xid = sp->xid;
+		sp->obj->exp.entered = sp->t_req;
+	} else {
+		/* XXX: Null the headers ? */
+	}
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+	h = sp->obj->http;
+
+	if (sp->err_code < 100 || sp->err_code > 999)
+		sp->err_code = 501;
+
+	http_PutProtocol(w, sp->vsl_id, h, "HTTP/1.1");
+	http_PutStatus(h, sp->err_code);
+	VTIM_format(VTIM_real(), date);
+	http_PrintfHeader(w, sp->vsl_id, h, "Date: %s", date);
+	http_SetHeader(w, sp->vsl_id, h, "Server: Varnish");
+
+	if (sp->err_reason != NULL)
+		http_PutResponse(w, sp->vsl_id, h, sp->err_reason);
+	else
+		http_PutResponse(w, sp->vsl_id, h,
+		    http_StatusMessage(sp->err_code));
+	VCL_error_method(sp);
+
+	if (sp->handling == VCL_RET_RESTART &&
+	    sp->restarts <  cache_param->max_restarts) {
+		HSH_Drop(sp);
+		sp->director = NULL;
+		sp->restarts++;
+		sp->step = STP_RECV;
+		return (0);
+	} else if (sp->handling == VCL_RET_RESTART)
+		sp->handling = VCL_RET_DELIVER;
+
+
+	/* We always close when we take this path */
+	sp->doclose = "error";
+	sp->wantbody = 1;
+
+	assert(sp->handling == VCL_RET_DELIVER);
+	sp->err_code = 0;
+	sp->err_reason = NULL;
+	http_Setup(sp->wrk->bereq, NULL);
+	sp->step = STP_PREPRESP;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Fetch response headers from the backend
+ *
+DOT subgraph xcluster_fetch {
+DOT	fetch [
+DOT		shape=ellipse
+DOT		label="fetch hdr\nfrom backend\n(find obj.ttl)"
+DOT	]
+DOT	vcl_fetch [
+DOT		shape=record
+DOT		label="vcl_fetch()|req.\nbereq.\nberesp."
+DOT	]
+DOT	fetch -> vcl_fetch [style=bold,color=blue]
+DOT	fetch -> vcl_fetch [style=bold,color=red]
+DOT	fetch_pass [
+DOT		shape=ellipse
+DOT		label="obj.f.pass=true"
+DOT	]
+DOT	vcl_fetch -> fetch_pass [label="hit_for_pass",style=bold,color=red]
+DOT }
+DOT fetch_pass -> fetchbody [style=bold,color=red]
+DOT vcl_fetch -> fetchbody [label="deliver",style=bold,color=blue]
+DOT vcl_fetch -> rstfetch [label="restart",color=purple]
+DOT rstfetch [label="RESTART",shape=plaintext]
+DOT fetch -> errfetch
+DOT vcl_fetch -> errfetch [label="error"]
+DOT errfetch [label="ERROR",shape=plaintext]
+ */
+
+static int
+cnt_fetch(struct sess *sp)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	AN(sp->director);
+	AZ(sp->wrk->vbc);
+	AZ(sp->wrk->h_content_length);
+	AZ(sp->wrk->do_close);
+	AZ(sp->wrk->storage_hint);
+
+	http_Setup(sp->wrk->beresp, sp->wrk->ws);
+
+	i = FetchHdr(sp);
+	/*
+	 * If we recycle a backend connection, there is a finite chance
+	 * that the backend closed it before we get a request to it.
+	 * Do a single retry in that case.
+	 */
+	if (i == 1) {
+		VSC_C_main->backend_retry++;
+		i = FetchHdr(sp);
+	}
+
+	if (i) {
+		sp->handling = VCL_RET_ERROR;
+		sp->err_code = 503;
+	} else {
+		/*
+		 * These two headers can be spread over multiple actual headers
+		 * and we rely on their content outside of VCL, so collect them
+		 * into one line here.
+		 */
+		http_CollectHdr(sp->wrk->beresp, H_Cache_Control);
+		http_CollectHdr(sp->wrk->beresp, H_Vary);
+
+		/*
+		 * Figure out how the fetch is supposed to happen, before the
+		 * headers are adultered by VCL
+		 * NB: Also sets other sp->wrk variables
+		 */
+		sp->wrk->body_status = RFC2616_Body(sp);
+
+		sp->err_code = http_GetStatus(sp->wrk->beresp);
+
+		/*
+		 * What does RFC2616 think about TTL ?
+		 */
+		EXP_Clr(&sp->wrk->exp);
+		sp->wrk->exp.entered = VTIM_real();
+		RFC2616_Ttl(sp);
+
+		/* pass from vclrecv{} has negative TTL */
+		if (sp->objcore == NULL)
+			sp->wrk->exp.ttl = -1.;
+
+		AZ(sp->wrk->do_esi);
+
+		VCL_fetch_method(sp);
+
+		switch (sp->handling) {
+		case VCL_RET_HIT_FOR_PASS:
+			if (sp->objcore != NULL)
+				sp->objcore->flags |= OC_F_PASS;
+			sp->step = STP_FETCHBODY;
+			return (0);
+		case VCL_RET_DELIVER:
+			AssertObjCorePassOrBusy(sp->objcore);
+			sp->step = STP_FETCHBODY;
+			return (0);
+		default:
+			break;
+		}
+
+		/* We are not going to fetch the body, Close the connection */
+		VDI_CloseFd(sp->wrk);
+	}
+
+	/* Clean up partial fetch */
+	AZ(sp->wrk->vbc);
+
+	if (sp->objcore != NULL) {
+		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
+		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
+		sp->objcore = NULL;
+	}
+	http_Setup(sp->wrk->bereq, NULL);
+	http_Setup(sp->wrk->beresp, NULL);
+	sp->wrk->h_content_length = NULL;
+	sp->director = NULL;
+	sp->wrk->storage_hint = NULL;
+
+	switch (sp->handling) {
+	case VCL_RET_RESTART:
+		sp->restarts++;
+		sp->step = STP_RECV;
+		return (0);
+	case VCL_RET_ERROR:
+		sp->step = STP_ERROR;
+		return (0);
+	default:
+		WRONG("Illegal action in vcl_fetch{}");
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Fetch response body from the backend
+ *
+DOT subgraph xcluster_body {
+DOT	fetchbody [
+DOT		shape=diamond
+DOT		label="stream ?"
+DOT	]
+DOT	fetchbody2 [
+DOT		shape=ellipse
+DOT		label="fetch body\nfrom backend\n"
+DOT	]
+DOT }
+DOT fetchbody -> fetchbody2 [label=no,style=bold,color=red]
+DOT fetchbody -> fetchbody2 [style=bold,color=blue]
+DOT fetchbody -> prepresp [label=yes,style=bold,color=cyan]
+DOT fetchbody2 -> prepresp [style=bold,color=red]
+DOT fetchbody2 -> prepresp [style=bold,color=blue]
+ */
+
+
+static int
+cnt_fetchbody(struct sess *sp)
+{
+	int i;
+	struct http *hp, *hp2;
+	char *b;
+	uint16_t nhttp;
+	unsigned l;
+	struct vsb *vary = NULL;
+	int varyl = 0, pass;
+
+	assert(sp->handling == VCL_RET_HIT_FOR_PASS ||
+	    sp->handling == VCL_RET_DELIVER);
+
+	if (sp->objcore == NULL) {
+		/* This is a pass from vcl_recv */
+		pass = 1;
+		/* VCL may have fiddled this, but that doesn't help */
+		sp->wrk->exp.ttl = -1.;
+	} else if (sp->handling == VCL_RET_HIT_FOR_PASS) {
+		/* pass from vcl_fetch{} -> hit-for-pass */
+		/* XXX: the bereq was not filtered pass... */
+		pass = 1;
+	} else {
+		/* regular object */
+		pass = 0;
+	}
+
+	/*
+	 * The VCL variables beresp.do_g[un]zip tells us how we want the
+	 * object processed before it is stored.
+	 *
+	 * The backend Content-Encoding header tells us what we are going
+	 * to receive, which we classify in the following three classes:
+	 *
+	 *	"Content-Encoding: gzip"	--> object is gzip'ed.
+	 *	no Content-Encoding		--> object is not gzip'ed.
+	 *	anything else			--> do nothing wrt gzip
+	 *
+	 */
+
+	AZ(sp->wrk->vfp);
+
+	/* We do nothing unless the param is set */
+	if (!cache_param->http_gzip_support)
+		sp->wrk->do_gzip = sp->wrk->do_gunzip = 0;
+
+	sp->wrk->is_gzip =
+	    http_HdrIs(sp->wrk->beresp, H_Content_Encoding, "gzip");
+
+	sp->wrk->is_gunzip =
+	    !http_GetHdr(sp->wrk->beresp, H_Content_Encoding, NULL);
+
+	/* It can't be both */
+	assert(sp->wrk->is_gzip == 0 || sp->wrk->is_gunzip == 0);
+
+	/* We won't gunzip unless it is gzip'ed */
+	if (sp->wrk->do_gunzip && !sp->wrk->is_gzip)
+		sp->wrk->do_gunzip = 0;
+
+	/* If we do gunzip, remove the C-E header */
+	if (sp->wrk->do_gunzip)
+		http_Unset(sp->wrk->beresp, H_Content_Encoding);
+
+	/* We wont gzip unless it is ungziped */
+	if (sp->wrk->do_gzip && !sp->wrk->is_gunzip)
+		sp->wrk->do_gzip = 0;
+
+	/* If we do gzip, add the C-E header */
+	if (sp->wrk->do_gzip)
+		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->beresp,
+		    "Content-Encoding: gzip");
+
+	/* But we can't do both at the same time */
+	assert(sp->wrk->do_gzip == 0 || sp->wrk->do_gunzip == 0);
+
+	/* ESI takes precedence and handles gzip/gunzip itself */
+	if (sp->wrk->do_esi)
+		sp->wrk->vfp = &vfp_esi;
+	else if (sp->wrk->do_gunzip)
+		sp->wrk->vfp = &vfp_gunzip;
+	else if (sp->wrk->do_gzip)
+		sp->wrk->vfp = &vfp_gzip;
+	else if (sp->wrk->is_gzip)
+		sp->wrk->vfp = &vfp_testgzip;
+
+	if (sp->wrk->do_esi || sp->esi_level > 0)
+		sp->wrk->do_stream = 0;
+	if (!sp->wantbody)
+		sp->wrk->do_stream = 0;
+
+	l = http_EstimateWS(sp->wrk->beresp,
+	    pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);
+
+	/* Create Vary instructions */
+	if (sp->objcore != NULL) {
+		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
+		vary = VRY_Create(sp, sp->wrk->beresp);
+		if (vary != NULL) {
+			varyl = VSB_len(vary);
+			assert(varyl > 0);
+			l += varyl;
+		}
+	}
+
+	/*
+	 * Space for producing a Content-Length: header including padding
+	 * A billion gigabytes is enough for anybody.
+	 */
+	l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *);
+
+	if (sp->wrk->exp.ttl < cache_param->shortlived || sp->objcore == NULL)
+		sp->wrk->storage_hint = TRANSIENT_STORAGE;
+
+	sp->obj = STV_NewObject(sp, sp->wrk->storage_hint, l,
+	    &sp->wrk->exp, nhttp);
+	if (sp->obj == NULL) {
+		/*
+		 * Try to salvage the transaction by allocating a
+		 * shortlived object on Transient storage.
+		 */
+		sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE, l,
+		    &sp->wrk->exp, nhttp);
+		if (sp->wrk->exp.ttl > cache_param->shortlived)
+			sp->wrk->exp.ttl = cache_param->shortlived;
+		sp->wrk->exp.grace = 0.0;
+		sp->wrk->exp.keep = 0.0;
+	}
+	if (sp->obj == NULL) {
+		sp->err_code = 503;
+		sp->step = STP_ERROR;
+		VDI_CloseFd(sp->wrk);
+		return (0);
+	}
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+
+	sp->wrk->storage_hint = NULL;
+
+	if (sp->wrk->do_gzip || (sp->wrk->is_gzip && !sp->wrk->do_gunzip))
+		sp->obj->gziped = 1;
+
+	if (vary != NULL) {
+		sp->obj->vary =
+		    (void *)WS_Alloc(sp->obj->http->ws, varyl);
+		AN(sp->obj->vary);
+		memcpy(sp->obj->vary, VSB_data(vary), varyl);
+		VRY_Validate(sp->obj->vary);
+		VSB_delete(vary);
+	}
+
+	sp->obj->xid = sp->xid;
+	sp->obj->response = sp->err_code;
+	WS_Assert(sp->obj->ws_o);
+
+	/* Filter into object */
+	hp = sp->wrk->beresp;
+	hp2 = sp->obj->http;
+
+	hp2->logtag = HTTP_Obj;
+	http_CopyResp(hp2, hp);
+	http_FilterFields(sp->wrk, sp->vsl_id, hp2, hp,
+	    pass ? HTTPH_R_PASS : HTTPH_A_INS);
+	http_CopyHome(sp->wrk, sp->vsl_id, hp2);
+
+	if (http_GetHdr(hp, H_Last_Modified, &b))
+		sp->obj->last_modified = VTIM_parse(b);
+	else
+		sp->obj->last_modified = floor(sp->wrk->exp.entered);
+
+	assert(WRW_IsReleased(sp->wrk));
+
+	/*
+	 * If we can deliver a 304 reply, we don't bother streaming.
+	 * Notice that vcl_deliver{} could still nuke the headers
+	 * that allow the 304, in which case we return 200 non-stream.
+	 */
+	if (sp->obj->response == 200 &&
+	    sp->http->conds &&
+	    RFC2616_Do_Cond(sp))
+		sp->wrk->do_stream = 0;
+
+	AssertObjCorePassOrBusy(sp->obj->objcore);
+
+	if (sp->wrk->do_stream) {
+		sp->step = STP_PREPRESP;
+		return (0);
+	}
+
+	/* Use unmodified headers*/
+	i = FetchBody(sp->wrk, sp->obj);
+
+	sp->wrk->h_content_length = NULL;
+
+	http_Setup(sp->wrk->bereq, NULL);
+	http_Setup(sp->wrk->beresp, NULL);
+	sp->wrk->vfp = NULL;
+	assert(WRW_IsReleased(sp->wrk));
+	AZ(sp->wrk->vbc);
+	AN(sp->director);
+
+	if (i) {
+		HSH_Drop(sp);
+		AZ(sp->obj);
+		sp->err_code = 503;
+		sp->step = STP_ERROR;
+		return (0);
+	}
+
+	if (sp->obj->objcore != NULL) {
+		EXP_Insert(sp->obj);
+		AN(sp->obj->objcore);
+		AN(sp->obj->objcore->ban);
+		HSH_Unbusy(sp);
+	}
+	sp->wrk->acct_tmp.fetch++;
+	sp->step = STP_PREPRESP;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Stream the body as we fetch it
+DOT subgraph xstreambody {
+DOT	streambody [
+DOT		shape=ellipse
+DOT		label="streaming\nfetch/deliver"
+DOT	]
+DOT }
+DOT streambody -> DONE [style=bold,color=cyan]
+ */
+
+static int
+cnt_streambody(struct sess *sp)
+{
+	int i;
+	struct stream_ctx sctx;
+	uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ?
+	    cache_param->gzip_stack_buffer : 1];
+
+	memset(&sctx, 0, sizeof sctx);
+	sctx.magic = STREAM_CTX_MAGIC;
+	AZ(sp->wrk->sctx);
+	sp->wrk->sctx = &sctx;
+
+	if (sp->wrk->res_mode & RES_GUNZIP) {
+		sctx.vgz = VGZ_NewUngzip(sp->wrk, "U S -");
+		sctx.obuf = obuf;
+		sctx.obuf_len = sizeof (obuf);
+	}
+
+	RES_StreamStart(sp);
+
+	AssertObjCorePassOrBusy(sp->obj->objcore);
+
+	i = FetchBody(sp->wrk, sp->obj);
+
+	sp->wrk->h_content_length = NULL;
+
+	http_Setup(sp->wrk->bereq, NULL);
+	http_Setup(sp->wrk->beresp, NULL);
+	sp->wrk->vfp = NULL;
+	AZ(sp->wrk->vbc);
+	AN(sp->director);
+
+	if (!i && sp->obj->objcore != NULL) {
+		EXP_Insert(sp->obj);
+		AN(sp->obj->objcore);
+		AN(sp->obj->objcore->ban);
+		HSH_Unbusy(sp);
+	} else {
+		sp->doclose = "Stream error";
+	}
+	sp->wrk->acct_tmp.fetch++;
+	sp->director = NULL;
+	sp->restarts = 0;
+
+	RES_StreamEnd(sp);
+	if (sp->wrk->res_mode & RES_GUNZIP)
+		(void)VGZ_Destroy(&sctx.vgz, sp->vsl_id);
+
+	sp->wrk->sctx = NULL;
+	assert(WRW_IsReleased(sp->wrk));
+	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
+	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+	http_Setup(sp->wrk->resp, NULL);
+	sp->step = STP_DONE;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * The very first request
+ */
+static int
+cnt_first(struct sess *sp)
+{
+
+	/*
+	 * XXX: If we don't have acceptfilters we are somewhat subject
+	 * XXX: to DoS'ing here.  One remedy would be to set a shorter
+	 * XXX: SO_RCVTIMEO and once we have received something here
+	 * XXX: increase it to the normal value.
+	 */
+
+	assert(sp->xid == 0);
+	assert(sp->restarts == 0);
+	VCA_Prep(sp);
+
+	/* Record the session watermark */
+	sp->ws_ses = WS_Snapshot(sp->ws);
+
+	/* Receive a HTTP protocol request */
+	HTC_Init(sp->htc, sp->ws, sp->fd, sp->vsl_id, cache_param->http_req_size,
+	    cache_param->http_req_hdr_len);
+	sp->wrk->lastused = sp->t_open;
+	sp->wrk->acct_tmp.sess++;
+
+	sp->step = STP_WAIT;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * HIT
+ * We had a cache hit.  Ask VCL, then march off as instructed.
+ *
+DOT subgraph xcluster_hit {
+DOT	hit [
+DOT		shape=record
+DOT		label="vcl_hit()|req.\nobj."
+DOT	]
+DOT }
+DOT hit -> err_hit [label="error"]
+DOT err_hit [label="ERROR",shape=plaintext]
+DOT hit -> rst_hit [label="restart",color=purple]
+DOT rst_hit [label="RESTART",shape=plaintext]
+DOT hit -> pass [label=pass,style=bold,color=red]
+DOT hit -> prepresp [label="deliver",style=bold,color=green]
+ */
+
+static int
+cnt_hit(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	assert(!(sp->obj->objcore->flags & OC_F_PASS));
+
+	AZ(sp->wrk->do_stream);
+
+	VCL_hit_method(sp);
+
+	if (sp->handling == VCL_RET_DELIVER) {
+		/* Dispose of any body part of the request */
+		(void)FetchReqBody(sp);
+		AZ(sp->wrk->bereq->ws);
+		AZ(sp->wrk->beresp->ws);
+		sp->step = STP_PREPRESP;
+		return (0);
+	}
+
+	/* Drop our object, we won't need it */
+	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+	sp->objcore = NULL;
+
+	switch(sp->handling) {
+	case VCL_RET_PASS:
+		sp->step = STP_PASS;
+		return (0);
+	case VCL_RET_ERROR:
+		sp->step = STP_ERROR;
+		return (0);
+	case VCL_RET_RESTART:
+		sp->director = NULL;
+		sp->restarts++;
+		sp->step = STP_RECV;
+		return (0);
+	default:
+		WRONG("Illegal action in vcl_hit{}");
+	}
+}
+
+/*--------------------------------------------------------------------
+ * LOOKUP
+ * Hash things together and look object up in hash-table.
+ *
+ * LOOKUP consists of two substates so that we can reenter if we
+ * encounter a busy object.
+ *
+DOT subgraph xcluster_lookup {
+DOT	hash [
+DOT		shape=record
+DOT		label="vcl_hash()|req."
+DOT	]
+DOT	lookup [
+DOT		shape=diamond
+DOT		label="obj in cache ?\ncreate if not"
+DOT	]
+DOT	lookup2 [
+DOT		shape=diamond
+DOT		label="obj.f.pass ?"
+DOT	]
+DOT	hash -> lookup [label="hash",style=bold,color=green]
+DOT	lookup -> lookup2 [label="yes",style=bold,color=green]
+DOT }
+DOT lookup2 -> hit [label="no", style=bold,color=green]
+DOT lookup2 -> pass [label="yes",style=bold,color=red]
+DOT lookup -> miss [label="no",style=bold,color=blue]
+ */
+
+static int
+cnt_lookup(struct sess *sp)
+{
+	struct objcore *oc;
+	struct object *o;
+	struct objhead *oh;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	if (sp->hash_objhead == NULL) {
+		/* Not a waiting list return */
+		AZ(sp->vary_b);
+		AZ(sp->vary_l);
+		AZ(sp->vary_e);
+		(void)WS_Reserve(sp->ws, 0);
+	} else {
+		AN(sp->ws->r);
+	}
+	sp->vary_b = (void*)sp->ws->f;
+	sp->vary_e = (void*)sp->ws->r;
+	sp->vary_b[2] = '\0';
+
+	oc = HSH_Lookup(sp, &oh);
+
+	if (oc == NULL) {
+		/*
+		 * We lost the session to a busy object, disembark the
+		 * worker thread.   The hash code to restart the session,
+		 * still in STP_LOOKUP, later when the busy object isn't.
+		 * NB:  Do not access sp any more !
+		 */
+		return (1);
+	}
+
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+
+	/* If we inserted a new object it's a miss */
+	if (oc->flags & OC_F_BUSY) {
+		sp->wrk->stats.cache_miss++;
+
+		if (sp->vary_l != NULL) {
+			assert(oc->busyobj->vary == sp->vary_b);
+			VRY_Validate(oc->busyobj->vary);
+			WS_ReleaseP(sp->ws, (void*)sp->vary_l);
+		} else {
+			AZ(oc->busyobj->vary);
+			WS_Release(sp->ws, 0);
+		}
+		sp->vary_b = NULL;
+		sp->vary_l = NULL;
+		sp->vary_e = NULL;
+
+		sp->objcore = oc;
+		sp->step = STP_MISS;
+		return (0);
+	}
+
+	o = oc_getobj(sp->wrk, oc);
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	sp->obj = o;
+
+	WS_Release(sp->ws, 0);
+	sp->vary_b = NULL;
+	sp->vary_l = NULL;
+	sp->vary_e = NULL;
+
+	if (oc->flags & OC_F_PASS) {
+		sp->wrk->stats.cache_hitpass++;
+		WSP(sp, SLT_HitPass, "%u", sp->obj->xid);
+		(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+		sp->objcore = NULL;
+		sp->step = STP_PASS;
+		return (0);
+	}
+
+	sp->wrk->stats.cache_hit++;
+	WSP(sp, SLT_Hit, "%u", sp->obj->xid);
+	sp->step = STP_HIT;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * We had a miss, ask VCL, proceed as instructed
+ *
+DOT subgraph xcluster_miss {
+DOT	miss [
+DOT		shape=ellipse
+DOT		label="filter req.->bereq."
+DOT	]
+DOT	vcl_miss [
+DOT		shape=record
+DOT		label="vcl_miss()|req.\nbereq."
+DOT	]
+DOT	miss -> vcl_miss [style=bold,color=blue]
+DOT }
+DOT vcl_miss -> rst_miss [label="restart",color=purple]
+DOT rst_miss [label="RESTART",shape=plaintext]
+DOT vcl_miss -> err_miss [label="error"]
+DOT err_miss [label="ERROR",shape=plaintext]
+DOT vcl_miss -> fetch [label="fetch",style=bold,color=blue]
+DOT vcl_miss -> pass [label="pass",style=bold,color=red]
+DOT
+ */
+
+static int
+cnt_miss(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	AZ(sp->obj);
+	AN(sp->objcore);
+	WS_Reset(sp->wrk->ws, NULL);
+	http_Setup(sp->wrk->bereq, sp->wrk->ws);
+	http_FilterHeader(sp, HTTPH_R_FETCH);
+	http_ForceGet(sp->wrk->bereq);
+	if (cache_param->http_gzip_support) {
+		/*
+		 * We always ask the backend for gzip, even if the
+		 * client doesn't grok it.  We will uncompress for
+		 * the minority of clients which don't.
+		 */
+		http_Unset(sp->wrk->bereq, H_Accept_Encoding);
+		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->bereq,
+		    "Accept-Encoding: gzip");
+	}
+	sp->wrk->connect_timeout = 0;
+	sp->wrk->first_byte_timeout = 0;
+	sp->wrk->between_bytes_timeout = 0;
+	VCL_miss_method(sp);
+	switch(sp->handling) {
+	case VCL_RET_ERROR:
+		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
+		sp->objcore = NULL;
+		http_Setup(sp->wrk->bereq, NULL);
+		sp->step = STP_ERROR;
+		return (0);
+	case VCL_RET_PASS:
+		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
+		sp->objcore = NULL;
+		sp->step = STP_PASS;
+		return (0);
+	case VCL_RET_FETCH:
+		sp->step = STP_FETCH;
+		return (0);
+	case VCL_RET_RESTART:
+		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
+		sp->objcore = NULL;
+		INCOMPL();
+	default:
+		WRONG("Illegal action in vcl_miss{}");
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Start pass processing by getting headers from backend, then
+ * continue in passbody.
+ *
+DOT subgraph xcluster_pass {
+DOT	pass [
+DOT		shape=ellipse
+DOT		label="deref obj."
+DOT	]
+DOT	pass2 [
+DOT		shape=ellipse
+DOT		label="filter req.->bereq."
+DOT	]
+DOT	vcl_pass [
+DOT		shape=record
+DOT		label="vcl_pass()|req.\nbereq."
+DOT	]
+DOT	pass_do [
+DOT		shape=ellipse
+DOT		label="create anon object\n"
+DOT	]
+DOT	pass -> pass2 [style=bold, color=red]
+DOT	pass2 -> vcl_pass [style=bold, color=red]
+DOT	vcl_pass -> pass_do [label="pass"] [style=bold, color=red]
+DOT }
+DOT pass_do -> fetch [style=bold, color=red]
+DOT vcl_pass -> rst_pass [label="restart",color=purple]
+DOT rst_pass [label="RESTART",shape=plaintext]
+DOT vcl_pass -> err_pass [label="error"]
+DOT err_pass [label="ERROR",shape=plaintext]
+ */
+
+static int
+cnt_pass(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+	AZ(sp->obj);
+
+	WS_Reset(sp->wrk->ws, NULL);
+	http_Setup(sp->wrk->bereq, sp->wrk->ws);
+	http_FilterHeader(sp, HTTPH_R_PASS);
+
+	sp->wrk->connect_timeout = 0;
+	sp->wrk->first_byte_timeout = 0;
+	sp->wrk->between_bytes_timeout = 0;
+	VCL_pass_method(sp);
+	if (sp->handling == VCL_RET_ERROR) {
+		http_Setup(sp->wrk->bereq, NULL);
+		sp->step = STP_ERROR;
+		return (0);
+	}
+	assert(sp->handling == VCL_RET_PASS);
+	sp->wrk->acct_tmp.pass++;
+	sp->sendbody = 1;
+	sp->step = STP_FETCH;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Ship the request header to the backend unchanged, then pipe
+ * until one of the ends close the connection.
+ *
+DOT subgraph xcluster_pipe {
+DOT	pipe [
+DOT		shape=ellipse
+DOT		label="Filter req.->bereq."
+DOT	]
+DOT	vcl_pipe [
+DOT		shape=record
+DOT		label="vcl_pipe()|req.\nbereq\."
+DOT	]
+DOT	pipe_do [
+DOT		shape=ellipse
+DOT		label="send bereq.\npipe until close"
+DOT	]
+DOT	vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
+DOT	pipe -> vcl_pipe [style=bold,color=orange]
+DOT }
+DOT pipe_do -> DONE [style=bold,color=orange]
+DOT vcl_pipe -> err_pipe [label="error"]
+DOT err_pipe [label="ERROR",shape=plaintext]
+ */
+
+static int
+cnt_pipe(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+
+	sp->wrk->acct_tmp.pipe++;
+	WS_Reset(sp->wrk->ws, NULL);
+	http_Setup(sp->wrk->bereq, sp->wrk->ws);
+	http_FilterHeader(sp, HTTPH_R_PIPE);
+
+	VCL_pipe_method(sp);
+
+	if (sp->handling == VCL_RET_ERROR)
+		INCOMPL();
+	assert(sp->handling == VCL_RET_PIPE);
+
+	PipeSession(sp);
+	assert(WRW_IsReleased(sp->wrk));
+	http_Setup(sp->wrk->bereq, NULL);
+	sp->step = STP_DONE;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * RECV
+ * We have a complete request, set everything up and start it.
+ *
+DOT subgraph xcluster_recv {
+DOT	recv [
+DOT		shape=record
+DOT		label="vcl_recv()|req."
+DOT	]
+DOT }
+DOT RESTART -> recv
+DOT recv -> pipe [label="pipe",style=bold,color=orange]
+DOT recv -> pass2 [label="pass",style=bold,color=red]
+DOT recv -> err_recv [label="error"]
+DOT err_recv [label="ERROR",shape=plaintext]
+DOT recv -> hash [label="lookup",style=bold,color=green]
+ */
+
+static int
+cnt_recv(struct sess *sp)
+{
+	unsigned recv_handling;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
+	AZ(sp->obj);
+	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
+
+	/* By default we use the first backend */
+	AZ(sp->director);
+	sp->director = sp->vcl->director[0];
+	AN(sp->director);
+
+	sp->disable_esi = 0;
+	sp->hash_always_miss = 0;
+	sp->hash_ignore_busy = 0;
+	sp->client_identity = NULL;
+
+	http_CollectHdr(sp->http, H_Cache_Control);
+
+	VCL_recv_method(sp);
+	recv_handling = sp->handling;
+
+	if (sp->restarts >= cache_param->max_restarts) {
+		if (sp->err_code == 0)
+			sp->err_code = 503;
+		sp->step = STP_ERROR;
+		return (0);
+	}
+
+	/* Zap these, in case we came here through restart */
+	sp->wrk->do_esi = 0;
+	sp->wrk->is_gzip = 0;
+	sp->wrk->is_gunzip = 0;
+	sp->wrk->do_gzip = 0;
+	sp->wrk->do_gunzip = 0;
+	sp->wrk->do_stream = 0;
+
+	if (cache_param->http_gzip_support &&
+	     (recv_handling != VCL_RET_PIPE) &&
+	     (recv_handling != VCL_RET_PASS)) {
+		if (RFC2616_Req_Gzip(sp)) {
+			http_Unset(sp->http, H_Accept_Encoding);
+			http_SetHeader(sp->wrk, sp->vsl_id, sp->http,
+			    "Accept-Encoding: gzip");
+		} else {
+			http_Unset(sp->http, H_Accept_Encoding);
+		}
+	}
+
+	SHA256_Init(sp->wrk->sha256ctx);
+	VCL_hash_method(sp);
+	assert(sp->handling == VCL_RET_HASH);
+	SHA256_Final(sp->digest, sp->wrk->sha256ctx);
+
+	if (!strcmp(sp->http->hd[HTTP_HDR_REQ].b, "HEAD"))
+		sp->wantbody = 0;
+	else
+		sp->wantbody = 1;
+
+	sp->sendbody = 0;
+	switch(recv_handling) {
+	case VCL_RET_LOOKUP:
+		/* XXX: discard req body, if any */
+		sp->step = STP_LOOKUP;
+		return (0);
+	case VCL_RET_PIPE:
+		if (sp->esi_level > 0) {
+			/* XXX: VSL something */
+			INCOMPL();
+			/* sp->step = STP_DONE; */
+			return (1);
+		}
+		sp->step = STP_PIPE;
+		return (0);
+	case VCL_RET_PASS:
+		sp->step = STP_PASS;
+		return (0);
+	case VCL_RET_ERROR:
+		/* XXX: discard req body, if any */
+		sp->step = STP_ERROR;
+		return (0);
+	default:
+		WRONG("Illegal action in vcl_recv{}");
+	}
+}
+
+/*--------------------------------------------------------------------
+ * START
+ * Handle a request, wherever it came from recv/restart.
+ *
+DOT start [shape=box,label="Dissect request"]
+DOT start -> recv [style=bold,color=green]
+ */
+
+static int
+cnt_start(struct sess *sp)
+{
+	uint16_t done;
+	char *p;
+	const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	AZ(sp->restarts);
+	AZ(sp->obj);
+	AZ(sp->vcl);
+
+	/* Update stats of various sorts */
+	sp->wrk->stats.client_req++;
+	sp->t_req = VTIM_real();
+	sp->wrk->lastused = sp->t_req;
+	sp->wrk->acct_tmp.req++;
+
+	/* Assign XID and log */
+	sp->xid = ++xids;				/* XXX not locked */
+	WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  sp->xid);
+
+	/* Borrow VCL reference from worker thread */
+	VCL_Refresh(&sp->wrk->vcl);
+	sp->vcl = sp->wrk->vcl;
+	sp->wrk->vcl = NULL;
+
+	http_Setup(sp->http, sp->ws);
+	done = http_DissectRequest(sp);
+
+	/* If we could not even parse the request, just close */
+	if (done == 400) {
+		sp->step = STP_DONE;
+		SES_Close(sp, "junk");
+		return (0);
+	}
+
+	/* Catch request snapshot */
+	sp->ws_req = WS_Snapshot(sp->ws);
+
+	/* Catch original request, before modification */
+	HTTP_Copy(sp->http0, sp->http);
+
+	if (done != 0) {
+		sp->err_code = done;
+		sp->step = STP_ERROR;
+		return (0);
+	}
+
+	sp->doclose = http_DoConnection(sp->http);
+
+	/* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */
+
+	/*
+	 * Handle Expect headers
+	 */
+	if (http_GetHdr(sp->http, H_Expect, &p)) {
+		if (strcasecmp(p, "100-continue")) {
+			sp->err_code = 417;
+			sp->step = STP_ERROR;
+			return (0);
+		}
+
+		/* XXX: Don't bother with write failures for now */
+		(void)write(sp->fd, r, strlen(r));
+		/* XXX: When we do ESI includes, this is not removed
+		 * XXX: because we use http0 as our basis.  Believed
+		 * XXX: safe, but potentially confusing.
+		 */
+		http_Unset(sp->http, H_Expect);
+	}
+
+	sp->step = STP_RECV;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Central state engine dispatcher.
+ *
+ * Kick the session around until it has had enough.
+ *
+ */
+
+static void
+cnt_diag(struct sess *sp, const char *state)
+{
+	if (sp->wrk != NULL) {
+		WSP(sp, SLT_Debug, "thr %p STP_%s sp %p obj %p vcl %p",
+		    pthread_self(), state, sp, sp->obj, sp->vcl);
+		WSL_Flush(sp->wrk, 0);
+	} else {
+		VSL(SLT_Debug, sp->vsl_id,
+		    "thr %p STP_%s sp %p obj %p vcl %p",
+		    pthread_self(), state, sp, sp->obj, sp->vcl);
+	}
+}
+
+void
+CNT_Session(struct sess *sp)
+{
+	int done;
+	struct worker *w;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	w = sp->wrk;
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+
+	/*
+	 * Possible entrance states
+	 */
+	assert(
+	    sp->step == STP_FIRST ||
+	    sp->step == STP_START ||
+	    sp->step == STP_LOOKUP ||
+	    sp->step == STP_RECV);
+
+	AZ(w->do_stream);
+	AZ(w->is_gzip);
+	AZ(w->do_gzip);
+	AZ(w->is_gunzip);
+	AZ(w->do_gunzip);
+	AZ(w->do_esi);
+
+	/*
+	 * Whenever we come in from the acceptor or waiter, we need to set
+	 * blocking mode, but there is no point in setting it when we come from
+	 * ESI or when a parked sessions returns.
+	 * 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->step == STP_FIRST || sp->step == STP_START) &&
+	    VTCP_blocking(sp->fd)) {
+		if (errno == ECONNRESET)
+			SES_Close(sp, "remote closed");
+		else
+			SES_Close(sp, "error");
+		sp->step = STP_DONE;
+	}
+
+	/*
+	 * NB: Once done is set, we can no longer touch sp!
+	 */
+	for (done = 0; !done; ) {
+		assert(sp->wrk == w);
+		/*
+		 * This is a good place to be paranoid about the various
+		 * pointers still pointing to the things we expect.
+		 */
+		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+		CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
+		CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+		CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
+		WS_Assert(w->ws);
+
+		switch (sp->step) {
+#define STEP(l,u) \
+		    case STP_##u: \
+			if (cache_param->diag_bitmap & 0x01) \
+				cnt_diag(sp, #u); \
+			done = cnt_##l(sp); \
+		        break;
+#include "tbl/steps.h"
+#undef STEP
+		default:
+			WRONG("State engine misfire");
+		}
+		WS_Assert(w->ws);
+		CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
+	}
+	WSL_Flush(w, 0);
+	AZ(w->do_stream);
+	AZ(w->is_gzip);
+	AZ(w->do_gzip);
+	AZ(w->is_gunzip);
+	AZ(w->do_gunzip);
+	AZ(w->do_esi);
+#define ACCT(foo)	AZ(w->acct_tmp.foo);
+#include "tbl/acct_fields.h"
+#undef ACCT
+	assert(WRW_IsReleased(w));
+}
+
+/*
+DOT }
+*/
+
+/*--------------------------------------------------------------------
+ * Debugging aids
+ */
+
+static void
+cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
+{
+	(void)priv;
+	if (av[2] != NULL)
+		xids = strtoul(av[2], NULL, 0);
+	VCLI_Out(cli, "XID is %u", xids);
+}
+
+/*
+ * Default to seed=1, this is the only seed value POSIXl guarantees will
+ * result in a reproducible random number sequence.
+ */
+static void
+cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
+{
+	(void)priv;
+	unsigned seed = 1;
+
+	if (av[2] != NULL)
+		seed = strtoul(av[2], NULL, 0);
+	srandom(seed);
+	srand48(random());
+	VCLI_Out(cli, "Random(3) seeded with %lu", seed);
+}
+
+static struct cli_proto debug_cmds[] = {
+	{ "debug.xid", "debug.xid",
+		"\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
+	{ "debug.srandom", "debug.srandom",
+		"\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
+	{ NULL }
+};
+
+/*--------------------------------------------------------------------
+ *
+ */
+
+void
+CNT_Init(void)
+{
+
+	srandomdev();
+	srand48(random());
+	xids = random();
+	CLI_AddFuncs(debug_cmds);
+}
+
+
diff --git a/bin/varnishd/cache/cache_cli.c b/bin/varnishd/cache/cache_cli.c
new file mode 100644
index 0000000..f29f86a
--- /dev/null
+++ b/bin/varnishd/cache/cache_cli.c
@@ -0,0 +1,243 @@
+/*-
+ * 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.
+ *
+ * Caching process CLI handling.
+ *
+ * We only have one CLI source, the stdin/stdout pipes from the manager
+ * process, but we complicate things by having undocumented commands that
+ * we do not want to show in a plain help, and by having commands that the
+ * manager has already shown in help before asking us.
+ */
+
+#include "config.h"
+
+#include <stddef.h>			// offsetof
+
+#include "cache.h"
+#include "common/heritage.h"
+
+#include "cache_backend.h"		// struct vbc
+#include "hash/hash_slinger.h"		// struct objhead
+#include "vcli.h"
+#include "vcli_common.h"
+#include "vcli_priv.h"
+#include "vcli_serve.h"
+
+pthread_t		cli_thread;
+static struct lock	cli_mtx;
+static int		add_check;
+static struct VCLS	*cls;
+
+/*
+ * The CLI commandlist is split in three:
+ *  - Commands we get from/share with the manager, we don't show these
+ *	in help, as the manager already did that.
+ *  - Cache process commands, show in help
+ *  - Undocumented debug commands, show in undocumented "help -d"
+ */
+
+/*--------------------------------------------------------------------
+ * Add CLI functions to the appropriate command set
+ */
+
+void
+CLI_AddFuncs(struct cli_proto *p)
+{
+
+	AZ(add_check);
+	Lck_Lock(&cli_mtx);
+	AZ(VCLS_AddFunc(cls, 0, p));
+	Lck_Unlock(&cli_mtx);
+}
+
+static void
+cli_cb_before(const struct cli *cli)
+{
+
+	ASSERT_CLI();
+	VSL(SLT_CLI, 0, "Rd %s", cli->cmd);
+	VCL_Poll();
+	VBE_Poll();
+	Lck_Lock(&cli_mtx);
+}
+
+static void
+cli_cb_after(const struct cli *cli)
+{
+
+	ASSERT_CLI();
+	Lck_Unlock(&cli_mtx);
+	VSL(SLT_CLI, 0, "Wr %03u %u %s",
+	    cli->result, VSB_len(cli->sb), VSB_data(cli->sb));
+}
+
+void
+CLI_Run(void)
+{
+	int i;
+
+	add_check = 1;
+
+	AN(VCLS_AddFd(cls, heritage.cli_in, heritage.cli_out, NULL, NULL));
+
+	do {
+		i = VCLS_Poll(cls, -1);
+	} while(i > 0);
+	VSL(SLT_CLI, 0, "EOF on CLI connection, worker stops");
+	VCA_Shutdown();
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+cli_debug_sizeof(struct cli *cli, const char * const *av, void *priv)
+{
+	(void)av;
+	(void)priv;
+
+#define SZOF(foo)       VCLI_Out(cli, \
+    "sizeof(%s) = %zd = 0x%zx\n", #foo, sizeof(foo), sizeof(foo))
+	SZOF(struct ws);
+	SZOF(struct http);
+	SZOF(struct http_conn);
+	SZOF(struct acct);
+	SZOF(struct worker);
+	SZOF(struct storage);
+	SZOF(struct object);
+	SZOF(struct objcore);
+	SZOF(struct objhead);
+	SZOF(struct sess);
+	SZOF(struct vbc);
+	SZOF(struct VSC_C_main);
+	SZOF(struct lock);
+#if 0
+#define OFOF(foo, bar)	{ foo __foo; VCLI_Out(cli, \
+    "%-30s = 0x%4zx @ 0x%4zx\n", \
+	#foo "." #bar, sizeof(__foo.bar), offsetof(foo, bar)); }
+#if 0
+	OFOF(struct objhead, magic);
+	OFOF(struct objhead, refcnt);
+	OFOF(struct objhead, mtx);
+	OFOF(struct objhead, objcs);
+	OFOF(struct objhead, digest);
+	OFOF(struct objhead, waitinglist);
+	OFOF(struct objhead, _u);
+#endif
+#if 0
+	OFOF(struct http, magic);
+	OFOF(struct http, logtag);
+	OFOF(struct http, ws);
+	OFOF(struct http, hd);
+	OFOF(struct http, hdf);
+	OFOF(struct http, shd);
+	OFOF(struct http, nhd);
+	OFOF(struct http, status);
+	OFOF(struct http, protover);
+	OFOF(struct http, conds);
+#endif
+#if 0
+	OFOF(struct storage, magic);
+	OFOF(struct storage, fd);
+	OFOF(struct storage, where);
+	OFOF(struct storage, list);
+	OFOF(struct storage, stevedore);
+	OFOF(struct storage, priv);
+	OFOF(struct storage, ptr);
+	OFOF(struct storage, len);
+	OFOF(struct storage, space);
+#endif
+#if 0
+	OFOF(struct object, magic);
+	OFOF(struct object, xid);
+	OFOF(struct object, objstore);
+	OFOF(struct object, objcore);
+	OFOF(struct object, ws_o);
+	OFOF(struct object, vary);
+	OFOF(struct object, hits);
+	OFOF(struct object, response);
+	OFOF(struct object, gziped);
+	OFOF(struct object, gzip_start);
+	OFOF(struct object, gzip_last);
+	OFOF(struct object, gzip_stop);
+	OFOF(struct object, len);
+	OFOF(struct object, age);
+	OFOF(struct object, entered);
+	OFOF(struct object, exp);
+	OFOF(struct object, last_modified);
+	OFOF(struct object, last_lru);
+	OFOF(struct object, http);
+	OFOF(struct object, store);
+	OFOF(struct object, esidata);
+	OFOF(struct object, last_use);
+#endif
+#undef OFOF
+#endif
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+ccf_panic(struct cli *cli, const char * const *av, void *priv)
+{
+
+	(void)cli;
+	(void)av;
+	(void)priv;
+	assert(!strcmp("", "You asked for it"));
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct cli_proto master_cmds[] = {
+	{ CLI_PING,		"i", VCLS_func_ping },
+	{ CLI_HELP,             "i", VCLS_func_help },
+	{ "debug.sizeof", "debug.sizeof",
+		"\tDump sizeof various data structures\n",
+		0, 0, "d", cli_debug_sizeof },
+	{ "debug.panic.worker", "debug.panic.worker",
+		"\tPanic the worker process.\n",
+		0, 0, "d", ccf_panic },
+	{ NULL }
+};
+
+/*--------------------------------------------------------------------
+ * Initialize the CLI subsystem
+ */
+
+void
+CLI_Init(void)
+{
+
+	Lck_New(&cli_mtx, lck_cli);
+	cli_thread = pthread_self();
+
+	cls = VCLS_New(cli_cb_before, cli_cb_after, cache_param->cli_buffer);
+	AN(cls);
+
+	CLI_AddFuncs(master_cmds);
+}
diff --git a/bin/varnishd/cache/cache_dir.c b/bin/varnishd/cache/cache_dir.c
new file mode 100644
index 0000000..d4794f9
--- /dev/null
+++ b/bin/varnishd/cache/cache_dir.c
@@ -0,0 +1,121 @@
+/*-
+ * 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.
+ *
+ * Handle backend connections and backend request structures.
+ *
+ */
+
+#include "config.h"
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vtcp.h"
+
+/* Close a connection ------------------------------------------------*/
+
+void
+VDI_CloseFd(struct worker *wrk)
+{
+	struct backend *bp;
+
+	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
+	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
+	assert(wrk->vbc->fd >= 0);
+
+	bp = wrk->vbc->backend;
+
+	WSL(wrk, SLT_BackendClose, wrk->vbc->vsl_id, "%s", bp->display_name);
+
+	/* Checkpoint log to flush all info related to this connection
+	   before the OS reuses the FD */
+	WSL_Flush(wrk, 0);
+
+	VTCP_close(&wrk->vbc->fd);
+	VBE_DropRefConn(bp);
+	wrk->vbc->backend = NULL;
+	VBE_ReleaseConn(wrk->vbc);
+	wrk->vbc = NULL;
+	wrk->do_close = 0;
+}
+
+/* Recycle a connection ----------------------------------------------*/
+
+void
+VDI_RecycleFd(struct worker *wrk)
+{
+	struct backend *bp;
+
+	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
+	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
+	assert(wrk->vbc->fd >= 0);
+	AZ(wrk->do_close);
+
+	bp = wrk->vbc->backend;
+
+	WSL(wrk, SLT_BackendReuse, wrk->vbc->vsl_id, "%s", bp->display_name);
+	/*
+	 * Flush the shmlog, so that another session reusing this backend
+	 * will log chronologically later than our use of it.
+	 */
+	WSL_Flush(wrk, 0);
+	Lck_Lock(&bp->mtx);
+	VSC_C_main->backend_recycle++;
+	VTAILQ_INSERT_HEAD(&bp->connlist, wrk->vbc, list);
+	wrk->vbc = NULL;
+	VBE_DropRefLocked(bp);
+}
+
+/* Get a connection --------------------------------------------------*/
+
+struct vbc *
+VDI_GetFd(const struct director *d, struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	if (d == NULL)
+		d = sp->director;
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	return (d->getfd(d, sp));
+}
+
+/* Check health ------------------------------------------------------
+ *
+ * The target is really an objhead pointer, but since it can not be
+ * dereferenced during health-checks, we pass it as uintptr_t, which
+ * hopefully will make people investigate, before mucking about with it.
+ */
+
+int
+VDI_Healthy(const struct director *d, const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	return (d->healthy(d, sp));
+}
diff --git a/bin/varnishd/cache/cache_dir_dns.c b/bin/varnishd/cache/cache_dir_dns.c
new file mode 100644
index 0000000..ae96dbb
--- /dev/null
+++ b/bin/varnishd/cache/cache_dir_dns.c
@@ -0,0 +1,469 @@
+/*-
+ * Copyright (c) 2009-2010 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Kristian Lyngstol <kristian at redpill-linpro.com>
+ *
+ * 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 <netinet/in.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vrt.h"
+
+/*--------------------------------------------------------------------*/
+
+/* FIXME: Should eventually be a configurable variable. */
+#define VDI_DNS_MAX_CACHE		1024
+#define VDI_DNS_GROUP_MAX_BACKENDS	1024
+
+/* DNS Cache entry
+ */
+struct vdi_dns_hostgroup {
+	unsigned			magic;
+#define VDI_DNSDIR_MAGIC		0x1bacab21
+	char				*hostname;
+	struct director			*hosts[VDI_DNS_GROUP_MAX_BACKENDS];
+	unsigned			nhosts;
+	unsigned			next_host; /* Next to use...*/
+	double				ttl;
+	VTAILQ_ENTRY(vdi_dns_hostgroup)	list;
+};
+
+struct vdi_dns {
+	unsigned			magic;
+#define VDI_DNS_MAGIC			0x1337a178
+	struct director			dir;
+	struct director			**hosts;
+	unsigned			nhosts;
+	VTAILQ_HEAD(_cachelist,vdi_dns_hostgroup)	cachelist;
+	unsigned			ncachelist;
+	pthread_rwlock_t		rwlock;
+	const char			*suffix;
+	double			ttl;
+};
+
+/* Compare an IPv4 backend to a IPv4 addr/len */
+static int
+vdi_dns_comp_addrinfo4(const struct backend *bp,
+		       const struct sockaddr_storage *addr,
+		       const socklen_t len)
+{
+	uint32_t u, p;
+	const struct sockaddr_in *bps = (const void *)bp->ipv4;
+	const struct sockaddr_in *bpd = (const void *)addr;
+
+	if (bp->ipv4len != len || len <= 0)
+		return (0);
+
+	u = bpd->sin_addr.s_addr;
+	p = bps->sin_addr.s_addr;
+
+	return (u == p);
+}
+
+/* Compare an IPv6 backend to a IPv6 addr/len */
+static int
+vdi_dns_comp_addrinfo6(const struct backend *bp,
+		       const struct sockaddr_storage *addr,
+		       const socklen_t len)
+{
+	const uint8_t *u, *p;
+	const struct sockaddr_in6 *bps = (const void *)bp->ipv6;
+	const struct sockaddr_in6 *bpd = (const void *)addr;
+
+	if (bp->ipv6len != len || len <= 0)
+		return (0);
+
+	u = bpd->sin6_addr.s6_addr;
+	p = bps->sin6_addr.s6_addr;
+
+	return (!memcmp(u, p, 16));
+}
+
+/* Check if a backends socket is the same as addr */
+static int
+vdi_dns_comp_addrinfo(const struct director *dir,
+		      const struct sockaddr_storage *addr,
+		      const socklen_t len)
+{
+	struct backend *bp;
+
+	bp = vdi_get_backend_if_simple(dir);
+	AN(bp);
+	if (addr->ss_family == PF_INET && bp->ipv4) {
+		return (vdi_dns_comp_addrinfo4(bp, addr, len));
+	} else if (addr->ss_family == PF_INET6 && bp->ipv6) {
+		return (vdi_dns_comp_addrinfo6(bp, addr, len));
+	}
+	return (0);
+}
+
+/* Pick a host from an existing hostgroup.
+ * Balance on round-robin if multiple backends are available and only pick
+ * healthy ones.
+ */
+static struct director *
+vdi_dns_pick_host(const struct sess *sp, struct vdi_dns_hostgroup *group) {
+	int initial, i, nhosts, current;
+	if (group->nhosts == 0)
+		return (NULL); // In case of error.
+	if (group->next_host >= group->nhosts)
+		group->next_host = 0;
+
+	/* Pick a healthy backend */
+	initial = group->next_host;
+	nhosts = group->nhosts;
+	for (i=0; i < nhosts; i++) {
+		if (i + initial >= nhosts)
+			current = i + initial - nhosts;
+		else
+			current = i + initial;
+		if (VDI_Healthy(group->hosts[current], sp)) {
+			group->next_host = current+1;
+			return (group->hosts[current]);
+		}
+	}
+
+	return (NULL);
+}
+
+/* Remove an item from the dns cache.
+ * If *group is NULL, the head is popped.
+ * Remember locking.
+ */
+static void
+vdi_dns_pop_cache(struct vdi_dns *vs,
+		  struct vdi_dns_hostgroup *group)
+{
+	if (group == NULL)
+		group = VTAILQ_LAST( &vs->cachelist, _cachelist );
+	assert(group != NULL);
+	free(group->hostname);
+	VTAILQ_REMOVE(&vs->cachelist, group, list);
+	FREE_OBJ(group);
+	vs->ncachelist--;
+}
+
+/* Dummy in case someone feels like optimizing it? meh...
+ */
+static inline int
+vdi_dns_groupmatch(const struct vdi_dns_hostgroup *group, const char *hostname)
+{
+	return (!strcmp(group->hostname, hostname));
+}
+
+/* Search the cache for 'hostname' and put a backend-pointer as necessary,
+ * return true for cache hit. This could still be a NULL backend if we did
+ * a lookup earlier and didn't find a host (ie: cache failed too)
+ *
+ * if rwlock is true, the first timed out object found (if any) is popped
+ * and freed.
+ */
+static int
+vdi_dns_cache_has(const struct sess *sp,
+		  struct vdi_dns *vs,
+		  const char *hostname,
+		  struct director **backend,
+		  int rwlock)
+{
+	struct director *ret;
+	struct vdi_dns_hostgroup *hostgr;
+	struct vdi_dns_hostgroup *hostgr2;
+	VTAILQ_FOREACH_SAFE(hostgr, &vs->cachelist, list, hostgr2) {
+		CHECK_OBJ_NOTNULL(hostgr, VDI_DNSDIR_MAGIC);
+		if (hostgr->ttl <= sp->t_req) {
+			if (rwlock)
+				vdi_dns_pop_cache(vs, hostgr);
+			return (0);
+		}
+		if (vdi_dns_groupmatch(hostgr, hostname)) {
+			ret = (vdi_dns_pick_host(sp, hostgr));
+			*backend = ret;
+			if (*backend != NULL)
+				CHECK_OBJ_NOTNULL(*backend, DIRECTOR_MAGIC);
+			return (1);
+		}
+	}
+	return (0);
+}
+
+/* Add a newly cached item to the dns cache list.
+ * (Sorry for the list_add/_add confusion...)
+ */
+static void
+vdi_dns_cache_list_add(const struct sess *sp,
+		       struct vdi_dns *vs,
+		       struct vdi_dns_hostgroup *new)
+{
+	if (vs->ncachelist >= VDI_DNS_MAX_CACHE) {
+		VSC_C_main->dir_dns_cache_full++;
+		vdi_dns_pop_cache(vs, NULL);
+	}
+	CHECK_OBJ_NOTNULL(new, VDI_DNSDIR_MAGIC);
+	assert(new->hostname != 0);
+	new->ttl = sp->t_req + vs->ttl;
+	VTAILQ_INSERT_HEAD(&vs->cachelist, new, list);
+	vs->ncachelist++;
+}
+
+/* Add an item to the dns cache.
+ * XXX: Might want to factor the getaddrinfo() out of the lock and do the
+ * cache_has() afterwards to do multiple dns lookups in parallel...
+ */
+static int
+vdi_dns_cache_add(const struct sess *sp,
+		  struct vdi_dns *vs,
+		  const char *hostname,
+		  struct director **backend)
+{
+	int error, i, host = 0;
+	struct addrinfo *res0, *res, hint;
+	struct vdi_dns_hostgroup *new;
+
+	/* Due to possible race while upgrading the lock, we have to
+	 * recheck if the result is already looked up. The overhead for
+	 * this is insignificant unless dns isn't cached properly (all
+	 * unique names or something equally troublesome).
+	 */
+
+	if (vdi_dns_cache_has(sp, vs, hostname, backend, 1))
+		return (1);
+
+	memset(&hint, 0, sizeof hint);
+	hint.ai_family = PF_UNSPEC;
+	hint.ai_socktype = SOCK_STREAM;
+
+	ALLOC_OBJ(new, VDI_DNSDIR_MAGIC);
+	XXXAN(new);
+
+	REPLACE(new->hostname, hostname);
+
+	error = getaddrinfo(hostname, "80", &hint, &res0);
+	VSC_C_main->dir_dns_lookups++;
+	if (error) {
+		vdi_dns_cache_list_add(sp, vs, new);
+		VSC_C_main->dir_dns_failed++;
+		return (0);
+	}
+
+	for (res = res0; res; res = res->ai_next) {
+		if (res->ai_family != PF_INET && res->ai_family != PF_INET6)
+			continue;
+
+		for (i = 0; i < vs->nhosts; i++) {
+			struct sockaddr_storage ss_hack;
+			memcpy(&ss_hack, res->ai_addr, res->ai_addrlen);
+			if (vdi_dns_comp_addrinfo(vs->hosts[i],
+			    &ss_hack, res->ai_addrlen)) {
+				new->hosts[host] = vs->hosts[i];
+				CHECK_OBJ_NOTNULL(new->hosts[host],
+				    DIRECTOR_MAGIC);
+				host++;
+			}
+		}
+	}
+	freeaddrinfo(res0);
+
+	new->nhosts = host;
+	vdi_dns_cache_list_add(sp, vs, new);
+	*backend = vdi_dns_pick_host(sp, new);
+	return (1);
+}
+
+/* Walk through the cached lookups looking for the relevant host, add one
+ * if it isn't already cached.
+ *
+ * Returns a backend or NULL.
+ */
+static struct director *
+vdi_dns_walk_cache(const struct sess *sp,
+		   struct vdi_dns *vs,
+		   const char *hostname)
+{
+	struct director *backend = NULL;
+	int ret;
+
+	AZ(pthread_rwlock_rdlock(&vs->rwlock));
+	ret = vdi_dns_cache_has(sp, vs, hostname, &backend, 0);
+	AZ(pthread_rwlock_unlock(&vs->rwlock));
+	if (!ret) {
+		/*
+		 * XXX: Isn't there a race here where another thread
+		 * XXX: could grab the lock and add it before we do ?
+		 * XXX: Should 'ret' be checked for that ?
+		 */
+		AZ(pthread_rwlock_wrlock(&vs->rwlock));
+		ret = vdi_dns_cache_add(sp, vs, hostname, &backend);
+		AZ(pthread_rwlock_unlock(&vs->rwlock));
+	} else
+		VSC_C_main->dir_dns_hit++;
+
+	/* Bank backend == cached a failure, so to speak */
+	if (backend != NULL)
+		CHECK_OBJ_NOTNULL(backend, DIRECTOR_MAGIC);
+	return (backend);
+}
+
+/* Parses the Host:-header and heads out to find a backend.
+ */
+static struct director *
+vdi_dns_find_backend(const struct sess *sp, struct vdi_dns *vs)
+{
+	struct director *ret;
+	struct http *hp;
+	char *p, *q;
+	char hostname[NI_MAXHOST];
+
+	/* bereq is only present after recv et. al, otherwise use req (ie:
+	 * use req for health checks in vcl_recv and such).
+	 */
+	if (sp->wrk->bereq)
+		hp = sp->wrk->bereq;
+	else
+		hp = sp->http;
+
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	if (http_GetHdr(hp, H_Host, &p) == 0)
+		return (NULL);
+
+	q = strchr(p, ':');
+	if (q == NULL)
+		q = strchr(p, '\0');
+	AN(q);
+
+	bprintf(hostname, "%.*s%s", (int)(q - p), p,
+	    vs->suffix ? vs->suffix : "");
+
+	ret = vdi_dns_walk_cache(sp, vs, hostname);
+	return (ret);
+}
+
+static struct vbc *
+vdi_dns_getfd(const struct director *director, struct sess *sp)
+{
+	struct vdi_dns *vs;
+	struct director *dir;
+	struct vbc *vbe;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(director, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, director->priv, VDI_DNS_MAGIC);
+
+	dir = vdi_dns_find_backend(sp, vs);
+	if (!dir || !VDI_Healthy(dir, sp))
+		return (NULL);
+
+	vbe = VDI_GetFd(dir, sp);
+	return (vbe);
+}
+
+static unsigned
+vdi_dns_healthy(const struct director *dir, const struct sess *sp)
+{
+	/* XXX: Fooling -Werror for a bit until it's actually implemented.
+	 */
+	(void)dir;
+	(void)sp;
+	return (1);
+
+	/*
+	struct vdi_dns *vs;
+	struct director *dir;
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_DNS_MAGIC);
+
+	dir = vdi_dns_find_backend(sp, vs);
+
+	if (dir)
+		return (1);
+	return (0);
+	*/
+}
+
+static void
+vdi_dns_fini(const struct director *d)
+{
+	struct vdi_dns *vs;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_DNS_MAGIC);
+
+	free(vs->hosts);
+	free(vs->dir.vcl_name);
+	vs->dir.magic = 0;
+	/* FIXME: Free the cache */
+	AZ(pthread_rwlock_destroy(&vs->rwlock));
+	FREE_OBJ(vs);
+}
+
+void
+VRT_init_dir_dns(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	const struct vrt_dir_dns *t;
+	struct vdi_dns *vs;
+	const struct vrt_dir_dns_entry *te;
+	int i;
+
+	ASSERT_CLI();
+	(void)cli;
+	t = priv;
+	ALLOC_OBJ(vs, VDI_DNS_MAGIC);
+	XXXAN(vs);
+	vs->hosts = calloc(sizeof(struct director *), t->nmember);
+	XXXAN(vs->hosts);
+
+	vs->dir.magic = DIRECTOR_MAGIC;
+	vs->dir.priv = vs;
+	vs->dir.name = "dns";
+	REPLACE(vs->dir.vcl_name, t->name);
+	vs->dir.getfd = vdi_dns_getfd;
+	vs->dir.fini = vdi_dns_fini;
+	vs->dir.healthy = vdi_dns_healthy;
+
+	vs->suffix = t->suffix;
+	vs->ttl = t->ttl;
+
+	te = t->members;
+	for (i = 0; i < t->nmember; i++, te++)
+		vs->hosts[i] = bp[te->host];
+	vs->nhosts = t->nmember;
+	vs->ttl = t->ttl;
+	VTAILQ_INIT(&vs->cachelist);
+	AZ(pthread_rwlock_init(&vs->rwlock, NULL));
+	bp[idx] = &vs->dir;
+}
diff --git a/bin/varnishd/cache/cache_dir_random.c b/bin/varnishd/cache/cache_dir_random.c
new file mode 100644
index 0000000..d6570ed
--- /dev/null
+++ b/bin/varnishd/cache/cache_dir_random.c
@@ -0,0 +1,285 @@
+/*-
+ * 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.
+ *
+ * This code is shared between the random, client and hash directors, because
+ * they share the same properties and most of the same selection logic.
+ *
+ * The random director picks a backend on random.
+ *
+ * The hash director picks based on the hash from vcl_hash{}
+ *
+ * The client director picks based on client identity or IP-address
+ *
+ * In all cases, the choice is by weight of the healthy subset of
+ * configured backends.
+ *
+ * Failures to get a connection are retried, here all three policies
+ * fall back to a deterministically random choice, by weight in the
+ * healthy subset.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vend.h"
+#include "vrt.h"
+#include "vsha256.h"
+
+/*--------------------------------------------------------------------*/
+
+struct vdi_random_host {
+	struct director		*backend;
+	double			weight;
+};
+
+enum crit_e {c_random, c_hash, c_client};
+
+struct vdi_random {
+	unsigned		magic;
+#define VDI_RANDOM_MAGIC	0x3771ae23
+	struct director		dir;
+
+	enum crit_e		criteria;
+	unsigned		retries;
+	double			tot_weight;
+	struct vdi_random_host	*hosts;
+	unsigned		nhosts;
+};
+
+/*
+ * Applies sha256 using the given context and input/length, and returns
+ * a double in the range [0...1[ based on the hash.
+ */
+static double
+vdi_random_sha(const char *input, ssize_t len)
+{
+	struct SHA256Context ctx;
+	uint8_t sign[SHA256_LEN];
+
+	AN(input);
+	SHA256_Init(&ctx);
+	SHA256_Update(&ctx, input, len);
+	SHA256_Final(sign, &ctx);
+	return (scalbn(vle32dec(sign), -32));
+}
+
+/*
+ * Sets up the initial seed for picking a backend according to policy.
+ */
+static double
+vdi_random_init_seed(const struct vdi_random *vs, const struct sess *sp)
+{
+	const char *p;
+	double retval;
+
+	switch (vs->criteria) {
+	case c_client:
+		if (sp->client_identity != NULL)
+			p = sp->client_identity;
+		else
+			p = sp->addr;
+		retval = vdi_random_sha(p, strlen(p));
+		break;
+	case c_hash:
+		AN(sp->digest);
+		retval = scalbn(vle32dec(sp->digest), -32);
+		break;
+	case c_random:
+	default:
+		retval = scalbn(random(), -31);
+		break;
+	}
+	return (retval);
+}
+
+/*
+ * Find the healthy backend corresponding to the weight r [0...1[
+ */
+static struct vbc *
+vdi_random_pick_one(struct sess *sp, const struct vdi_random *vs, double r)
+{
+	double w[vs->nhosts];
+	int i;
+	double s1;
+
+	assert(r >= 0.0 && r < 1.0);
+
+	memset(w, 0, sizeof w);
+	/* Sum up the weights of healty backends */
+	s1 = 0.0;
+	for (i = 0; i < vs->nhosts; i++) {
+		if (VDI_Healthy(vs->hosts[i].backend, sp))
+			w[i] = vs->hosts[i].weight;
+		s1 += w[i];
+	}
+
+	if (s1 == 0.0)
+		return (NULL);
+
+	r *= s1;
+	s1 = 0.0;
+	for (i = 0; i < vs->nhosts; i++)  {
+		s1 += w[i];
+		if (r < s1)
+			return(VDI_GetFd(vs->hosts[i].backend, sp));
+	}
+	return (NULL);
+}
+
+/*
+ * Try the specified number of times to get a backend.
+ * First one according to policy, after that, deterministically
+ * random by rehashing the key.
+ */
+static struct vbc *
+vdi_random_getfd(const struct director *d, struct sess *sp)
+{
+	int k;
+	struct vdi_random *vs;
+	double r;
+	struct vbc *vbe;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
+
+	r = vdi_random_init_seed(vs, sp);
+
+	for (k = 0; k < vs->retries; k++) {
+		vbe = vdi_random_pick_one(sp, vs, r);
+		if (vbe != NULL)
+			return (vbe);
+		r = vdi_random_sha((void *)&r, sizeof(r));
+	}
+	return (NULL);
+}
+
+/*
+ * Healthy if just a single backend is...
+ */
+static unsigned
+vdi_random_healthy(const struct director *d, const struct sess *sp)
+{
+	struct vdi_random *vs;
+	int i;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
+
+	for (i = 0; i < vs->nhosts; i++) {
+		if (VDI_Healthy(vs->hosts[i].backend, sp))
+			return (1);
+	}
+	return (0);
+}
+
+static void
+vdi_random_fini(const struct director *d)
+{
+	struct vdi_random *vs;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
+
+	free(vs->hosts);
+	free(vs->dir.vcl_name);
+	vs->dir.magic = 0;
+	FREE_OBJ(vs);
+}
+
+static void
+vrt_init(struct cli *cli, struct director **bp, int idx,
+    const void *priv, enum crit_e criteria)
+{
+	const struct vrt_dir_random *t;
+	struct vdi_random *vs;
+	const struct vrt_dir_random_entry *te;
+	struct vdi_random_host *vh;
+	int i;
+
+	ASSERT_CLI();
+	(void)cli;
+	t = priv;
+
+	ALLOC_OBJ(vs, VDI_RANDOM_MAGIC);
+	XXXAN(vs);
+	vs->hosts = calloc(sizeof *vh, t->nmember);
+	XXXAN(vs->hosts);
+
+	vs->dir.magic = DIRECTOR_MAGIC;
+	vs->dir.priv = vs;
+	vs->dir.name = "random";
+	REPLACE(vs->dir.vcl_name, t->name);
+	vs->dir.getfd = vdi_random_getfd;
+	vs->dir.fini = vdi_random_fini;
+	vs->dir.healthy = vdi_random_healthy;
+
+	vs->criteria = criteria;
+	vs->retries = t->retries;
+	if (vs->retries == 0)
+		vs->retries = t->nmember;
+	vh = vs->hosts;
+	te = t->members;
+	vs->tot_weight = 0.;
+	for (i = 0; i < t->nmember; i++, vh++, te++) {
+		assert(te->weight > 0.0);
+		vh->weight = te->weight;
+		vs->tot_weight += vh->weight;
+		vh->backend = bp[te->host];
+		AN(vh->backend);
+	}
+	vs->nhosts = t->nmember;
+	bp[idx] = &vs->dir;
+}
+
+void
+VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init(cli, bp, idx, priv, c_random);
+}
+
+void
+VRT_init_dir_hash(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init(cli, bp, idx, priv, c_hash);
+}
+
+void
+VRT_init_dir_client(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init(cli, bp, idx, priv, c_client);
+}
diff --git a/bin/varnishd/cache/cache_dir_round_robin.c b/bin/varnishd/cache/cache_dir_round_robin.c
new file mode 100644
index 0000000..7d75473
--- /dev/null
+++ b/bin/varnishd/cache/cache_dir_round_robin.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2008-2011 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Petter Knudsen <petter at linpro.no>
+ *
+ * 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 <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vrt.h"
+
+/*--------------------------------------------------------------------*/
+
+struct vdi_round_robin_host {
+	struct director			*backend;
+};
+
+enum mode_e { m_round_robin, m_fallback };
+
+struct vdi_round_robin {
+	unsigned			magic;
+#define VDI_ROUND_ROBIN_MAGIC		0x2114a178
+	struct director			dir;
+	enum mode_e			mode;
+	struct vdi_round_robin_host	*hosts;
+	unsigned			nhosts;
+	unsigned			next_host;
+};
+
+static struct vbc *
+vdi_round_robin_getfd(const struct director *d, struct sess *sp)
+{
+	int i;
+	struct vdi_round_robin *vs;
+	struct director *backend;
+	struct vbc *vbe;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
+
+	/*
+	 * In fallback mode we ignore the next_host and always grab the
+	 * first healthy backend we can find.
+	 */
+	for (i = 0; i < vs->nhosts; i++) {
+		if (vs->mode == m_round_robin) {
+			backend = vs->hosts[vs->next_host].backend;
+			vs->next_host = (vs->next_host + 1) % vs->nhosts;
+		} else /* m_fallback */ {
+			backend = vs->hosts[i].backend;
+		}
+		if (!VDI_Healthy(backend, sp))
+			continue;
+		vbe = VDI_GetFd(backend, sp);
+		if (vbe != NULL)
+			return (vbe);
+	}
+
+	return (NULL);
+}
+
+static unsigned
+vdi_round_robin_healthy(const struct director *d, const struct sess *sp)
+{
+	struct vdi_round_robin *vs;
+	struct director *backend;
+	int i;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
+
+	for (i = 0; i < vs->nhosts; i++) {
+		backend = vs->hosts[i].backend;
+		if (VDI_Healthy(backend, sp))
+			return (1);
+	}
+	return (0);
+}
+
+static void
+vdi_round_robin_fini(const struct director *d)
+{
+	struct vdi_round_robin *vs;
+
+	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
+
+	free(vs->hosts);
+	free(vs->dir.vcl_name);
+	vs->dir.magic = 0;
+	vs->next_host = 0;
+	FREE_OBJ(vs);
+}
+
+static void
+vrt_init_dir(struct cli *cli, struct director **bp, int idx,
+    const void *priv, enum mode_e mode)
+{
+	const struct vrt_dir_round_robin *t;
+	struct vdi_round_robin *vs;
+	const struct vrt_dir_round_robin_entry *te;
+	struct vdi_round_robin_host *vh;
+	int i;
+
+	ASSERT_CLI();
+	(void)cli;
+	t = priv;
+
+	ALLOC_OBJ(vs, VDI_ROUND_ROBIN_MAGIC);
+	XXXAN(vs);
+	vs->hosts = calloc(sizeof *vh, t->nmember);
+	XXXAN(vs->hosts);
+
+	vs->dir.magic = DIRECTOR_MAGIC;
+	vs->dir.priv = vs;
+	vs->dir.name = "round_robin";
+	REPLACE(vs->dir.vcl_name, t->name);
+	vs->dir.getfd = vdi_round_robin_getfd;
+	vs->dir.fini = vdi_round_robin_fini;
+	vs->dir.healthy = vdi_round_robin_healthy;
+
+	vs->mode = mode;
+	vh = vs->hosts;
+	te = t->members;
+	for (i = 0; i < t->nmember; i++, vh++, te++) {
+		vh->backend = bp[te->host];
+		AN (vh->backend);
+	}
+	vs->nhosts = t->nmember;
+	vs->next_host = 0;
+
+	bp[idx] = &vs->dir;
+}
+
+void
+VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init_dir(cli, bp, idx, priv, m_round_robin);
+}
+
+void
+VRT_init_dir_fallback(struct cli *cli, struct director **bp, int idx,
+    const void *priv)
+{
+	vrt_init_dir(cli, bp, idx, priv, m_fallback);
+}
+
diff --git a/bin/varnishd/cache/cache_esi.h b/bin/varnishd/cache/cache_esi.h
new file mode 100644
index 0000000..ff84d41
--- /dev/null
+++ b/bin/varnishd/cache/cache_esi.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 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.
+ *
+ */
+
+#define	VEC_GZ	(0x21)
+#define	VEC_V1	(0x40 + 1)
+#define	VEC_V2	(0x40 + 2)
+#define	VEC_V8	(0x40 + 8)
+#define	VEC_C1	(0x50 + 1)
+#define	VEC_C2	(0x50 + 2)
+#define	VEC_C8	(0x50 + 8)
+#define	VEC_S1	(0x60 + 1)
+#define	VEC_S2	(0x60 + 2)
+#define	VEC_S8	(0x60 + 8)
+#define	VEC_INCL	'I'
+
+typedef ssize_t vep_callback_t(struct worker *w, ssize_t l, enum vgz_flag flg);
+
+void VEP_Init(struct worker *w, vep_callback_t *cb);
+void VEP_Parse(const struct worker *w, const char *p, size_t l);
+struct vsb *VEP_Finish(struct worker *w);
+
+
diff --git a/bin/varnishd/cache/cache_esi_deliver.c b/bin/varnishd/cache/cache_esi_deliver.c
new file mode 100644
index 0000000..4051027
--- /dev/null
+++ b/bin/varnishd/cache/cache_esi_deliver.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * VED - Varnish Esi Delivery
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_esi.h"
+#include "vend.h"
+#include "vgz.h"
+
+/*--------------------------------------------------------------------*/
+
+static void
+ved_include(struct sess *sp, const char *src, const char *host)
+{
+	struct object *obj;
+	struct worker *w;
+	char *sp_ws_wm;
+	char *wrk_ws_wm;
+	unsigned sxid, res_mode;
+
+	w = sp->wrk;
+
+	if (sp->esi_level >= cache_param->max_esi_depth)
+		return;
+	sp->esi_level++;
+
+	(void)WRW_FlushRelease(w);
+
+	obj = sp->obj;
+	sp->obj = NULL;
+	res_mode = sp->wrk->res_mode;
+
+	/* Reset request to status before we started messing with it */
+	HTTP_Copy(sp->http, sp->http0);
+
+	/* Take a workspace snapshot */
+	sp_ws_wm = WS_Snapshot(sp->ws);
+	wrk_ws_wm = WS_Snapshot(w->ws);
+
+	http_SetH(sp->http, HTTP_HDR_URL, src);
+	if (host != NULL && *host != '\0')  {
+		http_Unset(sp->http, H_Host);
+		http_Unset(sp->http, H_If_Modified_Since);
+		http_SetHeader(w, sp->vsl_id, sp->http, host);
+	}
+	/*
+	 * XXX: We should decide if we should cache the director
+	 * XXX: or not (for session/backend coupling).  Until then
+	 * XXX: make sure we don't trip up the check in vcl_recv.
+	 */
+	sp->director = NULL;
+	sp->step = STP_RECV;
+	http_ForceGet(sp->http);
+
+	/* Don't do conditionals */
+	sp->http->conds = 0;
+	http_Unset(sp->http, H_If_Modified_Since);
+
+	/* Client content already taken care of */
+	http_Unset(sp->http, H_Content_Length);
+
+	sp->wrk->do_esi = 0;
+	sp->wrk->is_gzip = 0;
+	sp->wrk->is_gunzip = 0;
+	sp->wrk->do_gzip = 0;
+	sp->wrk->do_gunzip = 0;
+	sp->wrk->do_stream = 0;
+
+	sxid = sp->xid;
+	while (1) {
+		sp->wrk = w;
+		CNT_Session(sp);
+		if (sp->step == STP_DONE)
+			break;
+		AZ(sp->wrk);
+		WSL_Flush(w, 0);
+		DSL(0x20, SLT_Debug, sp->vsl_id, "loop waiting for ESI");
+		(void)usleep(10000);
+	}
+	sp->xid = sxid;
+	AN(sp->wrk);
+	assert(sp->step == STP_DONE);
+	sp->esi_level--;
+	sp->obj = obj;
+	sp->wrk->res_mode = res_mode;
+
+	/* Reset the workspace */
+	WS_Reset(sp->ws, sp_ws_wm);
+	WS_Reset(w->ws, wrk_ws_wm);
+
+	WRW_Reserve(sp->wrk, &sp->fd);
+	if (sp->wrk->res_mode & RES_CHUNKED)
+		WRW_Chunked(sp->wrk);
+}
+
+/*--------------------------------------------------------------------*/
+
+
+//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
+#define Debug(fmt, ...) /**/
+
+static ssize_t
+ved_decode_len(uint8_t **pp)
+{
+	uint8_t *p;
+	ssize_t l;
+
+	p = *pp;
+	switch (*p & 15) {
+	case 1:
+		l = p[1];
+		p += 2;
+		break;
+	case 2:
+		l = vbe16dec(p + 1);
+		p += 3;
+		break;
+	case 8:
+		l = vbe64dec(p + 1);
+		p += 9;
+		break;
+	default:
+		printf("Illegal Length %d %d\n", *p, (*p & 15));
+		INCOMPL();
+	}
+	*pp = p;
+	assert(l > 0);
+	return (l);
+}
+
+/*---------------------------------------------------------------------
+ * If a gzip'ed ESI object includes a ungzip'ed object, we need to make
+ * it looked like a gzip'ed data stream.  The official way to do so would
+ * be to fire up libvgz and gzip it, but we don't, we fake it.
+ *
+ * First, we cannot know if it is ungzip'ed on purpose, the admin may
+ * know something we don't.
+ *
+ * What do you mean "BS ?"
+ *
+ * All right then...
+ *
+ * The matter of the fact is that we simply will not fire up a gzip in
+ * the output path because it costs too much memory and CPU, so we simply
+ * wrap the data in very convenient "gzip copy-blocks" and send it down
+ * the stream with a bit more overhead.
+ */
+
+static void
+ved_pretend_gzip(const struct sess *sp, const uint8_t *p, ssize_t l)
+{
+	uint8_t buf1[5], buf2[5];
+	uint16_t lx;
+
+	lx = 65535;
+	buf1[0] = 0;
+	vle16enc(buf1 + 1, lx);
+	vle16enc(buf1 + 3, ~lx);
+
+	while (l > 0) {
+		if (l >= 65535) {
+			lx = 65535;
+			(void)WRW_Write(sp->wrk, buf1, sizeof buf1);
+		} else {
+			lx = (uint16_t)l;
+			buf2[0] = 0;
+			vle16enc(buf2 + 1, lx);
+			vle16enc(buf2 + 3, ~lx);
+			(void)WRW_Write(sp->wrk, buf2, sizeof buf2);
+		}
+		(void)WRW_Write(sp->wrk, p, lx);
+		sp->wrk->crc = crc32(sp->wrk->crc, p, lx);
+		sp->wrk->l_crc += lx;
+		l -= lx;
+		p += lx;
+	}
+	/* buf2 is local, have to flush */
+	(void)WRW_Flush(sp->wrk);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static const uint8_t gzip_hdr[] = {
+	0x1f, 0x8b, 0x08,
+	0x00, 0x00, 0x00, 0x00,
+	0x00,
+	0x02, 0x03
+};
+
+void
+ESI_Deliver(struct sess *sp)
+{
+	struct storage *st;
+	uint8_t *p, *e, *q, *r;
+	unsigned off;
+	ssize_t l, l2, l_icrc = 0;
+	uint32_t icrc = 0;
+	uint8_t tailbuf[8 + 5];
+	int isgzip;
+	struct vgz *vgz = NULL;
+	char obuf[cache_param->gzip_stack_buffer];
+	ssize_t obufl = 0;
+	size_t dl;
+	const void *dp;
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	st = sp->obj->esidata;
+	AN(st);
+	assert(sizeof obuf >= 1024);
+
+	obuf[0] = 0;	/* For flexelint */
+
+	p = st->ptr;
+	e = st->ptr + st->len;
+
+	if (*p == VEC_GZ) {
+		isgzip = 1;
+		p++;
+	} else {
+		isgzip = 0;
+	}
+
+	if (sp->esi_level == 0) {
+		/*
+		 * Only the top level document gets to decide this.
+		 */
+		sp->wrk->gzip_resp = 0;
+		if (isgzip && !(sp->wrk->res_mode & RES_GUNZIP)) {
+			assert(sizeof gzip_hdr == 10);
+			/* Send out the gzip header */
+			(void)WRW_Write(sp->wrk, gzip_hdr, 10);
+			sp->wrk->l_crc = 0;
+			sp->wrk->gzip_resp = 1;
+			sp->wrk->crc = crc32(0L, Z_NULL, 0);
+		}
+	}
+
+	if (isgzip && !sp->wrk->gzip_resp) {
+		vgz = VGZ_NewUngzip(sp->wrk, "U D E");
+
+		/* Feed a gzip header to gunzip to make it happy */
+		VGZ_Ibuf(vgz, gzip_hdr, sizeof gzip_hdr);
+		VGZ_Obuf(vgz, obuf, sizeof obuf);
+		i = VGZ_Gunzip(vgz, &dp, &dl);
+		assert(i == VGZ_OK);
+		assert(VGZ_IbufEmpty(vgz));
+		assert(dl == 0);
+
+		obufl = 0;
+	}
+
+	st = VTAILQ_FIRST(&sp->obj->store);
+	off = 0;
+
+	while (p < e) {
+		switch (*p) {
+		case VEC_V1:
+		case VEC_V2:
+		case VEC_V8:
+			l = ved_decode_len(&p);
+			if (isgzip) {
+				assert(*p == VEC_C1 || *p == VEC_C2 ||
+				    *p == VEC_C8);
+				l_icrc = ved_decode_len(&p);
+				icrc = vbe32dec(p);
+				p += 4;
+				if (sp->wrk->gzip_resp) {
+					sp->wrk->crc = crc32_combine(
+					    sp->wrk->crc, icrc, l_icrc);
+					sp->wrk->l_crc += l_icrc;
+				}
+			}
+			/*
+			 * There is no guarantee that the 'l' bytes are all
+			 * in the same storage segment, so loop over storage
+			 * until we have processed them all.
+			 */
+			while (l > 0) {
+				l2 = l;
+				if (l2 > st->len - off)
+					l2 = st->len - off;
+				l -= l2;
+
+				if (sp->wrk->gzip_resp && isgzip) {
+					/*
+					 * We have a gzip'ed VEC and delivers
+					 * a gzip'ed ESI response.
+					 */
+					(void)WRW_Write(sp->wrk, st->ptr + off, l2);
+				} else if (sp->wrk->gzip_resp) {
+					/*
+					 * A gzip'ed ESI response, but the VEC
+					 * was not gzip'ed.
+					 */
+					ved_pretend_gzip(sp, st->ptr + off, l2);
+				} else if (isgzip) {
+					/*
+					 * A gzip'ed VEC, but ungzip'ed ESI
+					 * response
+					 */
+					AN(vgz);
+					i = VGZ_WrwGunzip(sp->wrk, vgz,
+						st->ptr + off, l2,
+						obuf, sizeof obuf, &obufl);
+					if (WRW_Error(sp->wrk)) {
+						SES_Close(sp, "remote closed");
+						p = e;
+						break;
+					}
+					assert (i == VGZ_OK || i == VGZ_END);
+				} else {
+					/*
+					 * Ungzip'ed VEC, ungzip'ed ESI response
+					 */
+					(void)WRW_Write(sp->wrk, st->ptr + off, l2);
+				}
+				off += l2;
+				if (off == st->len) {
+					st = VTAILQ_NEXT(st, list);
+					off = 0;
+				}
+			}
+			break;
+		case VEC_S1:
+		case VEC_S2:
+		case VEC_S8:
+			l = ved_decode_len(&p);
+			Debug("SKIP1(%d)\n", (int)l);
+			/*
+			 * There is no guarantee that the 'l' bytes are all
+			 * in the same storage segment, so loop over storage
+			 * until we have processed them all.
+			 */
+			while (l > 0) {
+				l2 = l;
+				if (l2 > st->len - off)
+					l2 = st->len - off;
+				l -= l2;
+				off += l2;
+				if (off == st->len) {
+					st = VTAILQ_NEXT(st, list);
+					off = 0;
+				}
+			}
+			break;
+		case VEC_INCL:
+			p++;
+			q = (void*)strchr((const char*)p, '\0');
+			AN(q);
+			q++;
+			r = (void*)strchr((const char*)q, '\0');
+			AN(r);
+			if (obufl > 0) {
+				(void)WRW_Write(sp->wrk, obuf, obufl);
+				obufl = 0;
+			}
+			if (WRW_Flush(sp->wrk)) {
+				SES_Close(sp, "remote closed");
+				p = e;
+				break;
+			}
+			Debug("INCL [%s][%s] BEGIN\n", q, p);
+			ved_include(sp, (const char*)q, (const char*)p);
+			Debug("INCL [%s][%s] END\n", q, p);
+			p = r + 1;
+			break;
+		default:
+			printf("XXXX 0x%02x [%s]\n", *p, p);
+			INCOMPL();
+		}
+	}
+	if (vgz != NULL) {
+		if (obufl > 0)
+			(void)WRW_Write(sp->wrk, obuf, obufl);
+		(void)VGZ_Destroy(&vgz, sp->vsl_id);
+	}
+	if (sp->wrk->gzip_resp && sp->esi_level == 0) {
+		/* Emit a gzip literal block with finish bit set */
+		tailbuf[0] = 0x01;
+		tailbuf[1] = 0x00;
+		tailbuf[2] = 0x00;
+		tailbuf[3] = 0xff;
+		tailbuf[4] = 0xff;
+
+		/* Emit CRC32 */
+		vle32enc(tailbuf + 5, sp->wrk->crc);
+
+		/* MOD(2^32) length */
+		vle32enc(tailbuf + 9, sp->wrk->l_crc);
+
+		(void)WRW_Write(sp->wrk, tailbuf, 13);
+	}
+	(void)WRW_Flush(sp->wrk);
+}
+
+/*---------------------------------------------------------------------
+ * Include an object in a gzip'ed ESI object delivery
+ */
+
+static uint8_t
+ved_deliver_byterange(const struct sess *sp, ssize_t low, ssize_t high)
+{
+	struct storage *st;
+	ssize_t l, lx;
+	u_char *p;
+
+//printf("BR %jd %jd\n", low, high);
+	lx = 0;
+	VTAILQ_FOREACH(st, &sp->obj->store, list) {
+		p = st->ptr;
+		l = st->len;
+//printf("[0-] %jd %jd\n", lx, lx + l);
+		if (lx + l < low) {
+			lx += l;
+			continue;
+		}
+		if (lx == high)
+			return (p[0]);
+		assert(lx < high);
+		if (lx < low) {
+			p += (low - lx);
+			l -= (low - lx);
+			lx = low;
+		}
+//printf("[1-] %jd %jd\n", lx, lx + l);
+		if (lx + l >= high)
+			l = high - lx;
+//printf("[2-] %jd %jd\n", lx, lx + l);
+		assert(lx >= low && lx + l <= high);
+		if (l != 0)
+			(void)WRW_Write(sp->wrk, p, l);
+		if (lx + st->len > high)
+			return(p[l]);
+		lx += st->len;
+	}
+	INCOMPL();
+}
+
+void
+ESI_DeliverChild(const struct sess *sp)
+{
+	struct storage *st;
+	struct object *obj;
+	ssize_t start, last, stop, lpad;
+	u_char *p, cc;
+	uint32_t icrc;
+	uint32_t ilen;
+	uint8_t *dbits;
+
+	if (!sp->obj->gziped) {
+		VTAILQ_FOREACH(st, &sp->obj->store, list)
+			ved_pretend_gzip(sp, st->ptr, st->len);
+		return;
+	}
+	/*
+	 * This is the interesting case: Deliver all the deflate
+	 * blocks, stripping the "LAST" bit of the last one and
+	 * padding it, as necessary, to a byte boundary.
+	 */
+
+	dbits = (void*)WS_Alloc(sp->wrk->ws, 8);
+	AN(dbits);
+	obj = sp->obj;
+	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
+	start = obj->gzip_start;
+	last = obj->gzip_last;
+	stop = obj->gzip_stop;
+	assert(start > 0 && start < obj->len * 8);
+	assert(last > 0 && last < obj->len * 8);
+	assert(stop > 0 && stop < obj->len * 8);
+	assert(last >= start);
+	assert(last < stop);
+
+	/* The start bit must be byte aligned. */
+	AZ(start & 7);
+
+	/*
+	 * XXX: optimize for the case where the 'last'
+	 * XXX: bit is in a empty copy block
+	 */
+	*dbits = ved_deliver_byterange(sp, start/8, last/8);
+	*dbits &= ~(1U << (last & 7));
+	(void)WRW_Write(sp->wrk, dbits, 1);
+	cc = ved_deliver_byterange(sp, 1 + last/8, stop/8);
+	switch((int)(stop & 7)) {
+	case 0: /* xxxxxxxx */
+		/* I think we have an off by one here, but that's OK */
+		lpad = 0;
+		break;
+	case 1: /* x000.... 00000000 00000000 11111111 11111111 */
+	case 3: /* xxx000.. 00000000 00000000 11111111 11111111 */
+	case 5: /* xxxxx000 00000000 00000000 11111111 11111111 */
+		dbits[1] = cc | 0x00;
+		dbits[2] = 0x00; dbits[3] = 0x00;
+	        dbits[4] = 0xff; dbits[5] = 0xff;
+		lpad = 5;
+		break;
+	case 2: /* xx010000 00000100 00000001 00000000 */
+		dbits[1] = cc | 0x08;
+		dbits[2] = 0x20;
+		dbits[3] = 0x80;
+		dbits[4] = 0x00;
+		lpad = 4;
+		break;
+	case 4: /* xxxx0100 00000001 00000000 */
+		dbits[1] = cc | 0x20;
+		dbits[2] = 0x80;
+		dbits[3] = 0x00;
+		lpad = 3;
+		break;
+	case 6: /* xxxxxx01 00000000 */
+		dbits[1] = cc | 0x80;
+		dbits[2] = 0x00;
+		lpad = 2;
+		break;
+	case 7:	/* xxxxxxx0 00...... 00000000 00000000 11111111 11111111 */
+		dbits[1] = cc | 0x00;
+		dbits[2] = 0x00;
+		dbits[3] = 0x00; dbits[4] = 0x00;
+	        dbits[5] = 0xff; dbits[6] = 0xff;
+		lpad = 6;
+		break;
+	default:
+		INCOMPL();
+	}
+	if (lpad > 0)
+		(void)WRW_Write(sp->wrk, dbits + 1, lpad);
+	st = VTAILQ_LAST(&sp->obj->store, storagehead);
+	assert(st->len > 8);
+
+	p = st->ptr + st->len - 8;
+	icrc = vle32dec(p);
+	ilen = vle32dec(p + 4);
+	sp->wrk->crc = crc32_combine(sp->wrk->crc, icrc, ilen);
+	sp->wrk->l_crc += ilen;
+}
diff --git a/bin/varnishd/cache/cache_esi_fetch.c b/bin/varnishd/cache/cache_esi_fetch.c
new file mode 100644
index 0000000..5ec8f6b
--- /dev/null
+++ b/bin/varnishd/cache/cache_esi_fetch.c
@@ -0,0 +1,405 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * VEF Varnish Esi Fetching
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_esi.h"
+
+/*---------------------------------------------------------------------
+ * Read some bytes.
+ *
+ * If the esi_syntax&8 bit is set, we read only a couple of bytes at
+ * a time, in order to stress the parse/pending/callback code.
+ */
+
+static ssize_t
+vef_read(struct worker *w, struct http_conn *htc, void *buf, ssize_t buflen,
+    ssize_t bytes)
+{
+	ssize_t d;
+
+	if (buflen < bytes)
+		bytes = buflen;
+	if (cache_param->esi_syntax & 0x8) {
+		d = (random() & 3) + 1;
+		if (d < bytes)
+			bytes = d;
+	}
+	return (HTC_Read(w, htc, buf, bytes));
+}
+
+/*---------------------------------------------------------------------
+ * We receive a ungzip'ed object, and want to store it ungzip'ed.
+ */
+
+static int
+vfp_esi_bytes_uu(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	ssize_t wl;
+	struct storage *st;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+
+	while (bytes > 0) {
+		st = FetchStorage(w, 0);
+		if (st == NULL)
+			return (-1);
+		wl = vef_read(w, htc,
+		    st->ptr + st->len, st->space - st->len, bytes);
+		if (wl <= 0)
+			return (wl);
+		VEP_Parse(w, (const char *)st->ptr + st->len, wl);
+		st->len += wl;
+		w->fetch_obj->len += wl;
+		bytes -= wl;
+	}
+	return (1);
+}
+
+/*---------------------------------------------------------------------
+ * We receive a gzip'ed object, and want to store it ungzip'ed.
+ */
+
+static int
+vfp_esi_bytes_gu(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	struct vgz *vg;
+	ssize_t wl;
+	uint8_t	ibuf[cache_param->gzip_stack_buffer];
+	int i;
+	size_t dl;
+	const void *dp;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vg = w->vgz_rx;
+
+	while (bytes > 0) {
+		if (VGZ_IbufEmpty(vg) && bytes > 0) {
+			wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
+			if (wl <= 0)
+				return (wl);
+			VGZ_Ibuf(vg, ibuf, wl);
+			bytes -= wl;
+		}
+		if (VGZ_ObufStorage(w, vg))
+			return(-1);
+		i = VGZ_Gunzip(vg, &dp, &dl);
+		xxxassert(i == VGZ_OK || i == VGZ_END);
+		VEP_Parse(w, dp, dl);
+		w->fetch_obj->len += dl;
+	}
+	return (1);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+struct vef_priv {
+	unsigned		magic;
+#define VEF_MAGIC		0xf104b51f
+	struct vgz		*vgz;
+
+	char			*bufp;
+	ssize_t			tot;
+	int			error;
+	char			pending[20];
+	ssize_t			npend;
+};
+
+/*---------------------------------------------------------------------
+ * We receive a [un]gzip'ed object, and want to store it gzip'ed.
+ */
+
+static ssize_t
+vfp_vep_callback(struct worker *w, ssize_t l, enum vgz_flag flg)
+{
+	struct vef_priv *vef;
+	size_t dl, px;
+	const void *dp;
+	int i;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vef = w->vef_priv;
+	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
+	assert(l >= 0);
+
+	if (vef->error) {
+		vef->tot += l;
+		return (vef->tot);
+	}
+
+	/*
+	 * l == 0 is valid when 'flg' calls for action, but in the
+	 * normal case we can just ignore a l==0 request.
+	 * (It would cause Z_BUF_ERROR anyway)
+	 */
+	if (l == 0 && flg == VGZ_NORMAL)
+		return (vef->tot);
+
+	do {
+		px = vef->npend;
+		if (l < px)
+			px = l;
+		if (px != 0) {
+			VGZ_Ibuf(vef->vgz, vef->pending, px);
+			l -= px;
+		} else {
+			VGZ_Ibuf(vef->vgz, vef->bufp, l);
+			vef->bufp += l;
+			l = 0;
+		}
+		do {
+			if (VGZ_ObufStorage(w, vef->vgz)) {
+				vef->error = ENOMEM;
+				vef->tot += l;
+				return (vef->tot);
+			}
+			i = VGZ_Gzip(vef->vgz, &dp, &dl, flg);
+			vef->tot += dl;
+			w->fetch_obj->len += dl;
+		} while (!VGZ_IbufEmpty(vef->vgz) ||
+		    (flg != VGZ_NORMAL && VGZ_ObufFull(vef->vgz)));
+		if (px != 0) {
+			memmove(vef->pending, vef->pending + px,
+			    vef->npend - px);
+			vef->npend -= px;
+		}
+	} while (l > 0);
+	if (flg == VGZ_FINISH)
+		assert(i == 1);			/* XXX */
+	else
+		assert(i == 0);			/* XXX */
+	return (vef->tot);
+}
+
+static int
+vfp_esi_bytes_ug(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	ssize_t wl;
+	char ibuf[cache_param->gzip_stack_buffer];
+	struct vef_priv *vef;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vef = w->vef_priv;
+	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
+
+	while (bytes > 0) {
+		wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
+		if (wl <= 0)
+			return (wl);
+		bytes -= wl;
+		vef->bufp = ibuf;
+		VEP_Parse(w, ibuf, wl);
+		assert(vef->bufp >= ibuf && vef->bufp <= ibuf + wl);
+		if (vef->error) {
+			errno = vef->error;
+			return (-1);
+		}
+		if (vef->bufp < ibuf + wl) {
+			wl = (ibuf + wl) - vef->bufp;
+			assert(wl + vef->npend < sizeof vef->pending);
+			memmove(vef->pending + vef->npend, vef->bufp, wl);
+			vef->npend += wl;
+		}
+	}
+	return (1);
+}
+
+/*---------------------------------------------------------------------
+ * We receive a gzip'ed object, and want to store it gzip'ed.
+ */
+
+static int
+vfp_esi_bytes_gg(struct worker *w, struct http_conn *htc, size_t bytes)
+{
+	ssize_t wl;
+	char ibuf[cache_param->gzip_stack_buffer];
+	char ibuf2[cache_param->gzip_stack_buffer];
+	struct vef_priv *vef;
+	size_t dl;
+	const void *dp;
+	int i;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vef = w->vef_priv;
+	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
+	assert(sizeof ibuf >= 1024);
+	ibuf2[0] = 0; /* For Flexelint */
+
+	while (bytes > 0) {
+		wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
+		if (wl <= 0)
+			return (wl);
+		bytes -= wl;
+
+		vef->bufp = ibuf;
+		VGZ_Ibuf(w->vgz_rx, ibuf, wl);
+		do {
+			VGZ_Obuf(w->vgz_rx, ibuf2, sizeof ibuf2);
+			i = VGZ_Gunzip(w->vgz_rx, &dp, &dl);
+			/* XXX: check i */
+			assert(i >= VGZ_OK);
+			vef->bufp = ibuf2;
+			if (dl > 0)
+				VEP_Parse(w, ibuf2, dl);
+			if (vef->error) {
+				errno = vef->error;
+				return (-1);
+			}
+			if (vef->bufp < ibuf2 + dl) {
+				dl = (ibuf2 + dl) - vef->bufp;
+				assert(dl + vef->npend < sizeof vef->pending);
+				memmove(vef->pending + vef->npend,
+				    vef->bufp, dl);
+				vef->npend += dl;
+			}
+		} while (!VGZ_IbufEmpty(w->vgz_rx));
+	}
+	return (1);
+}
+
+
+/*---------------------------------------------------------------------*/
+
+static void __match_proto__()
+vfp_esi_begin(struct worker *w, size_t estimate)
+{
+	struct vef_priv *vef;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+
+	AZ(w->vgz_rx);
+	if (w->is_gzip && w->do_gunzip) {
+		w->vgz_rx = VGZ_NewUngzip(w, "U F E");
+		VEP_Init(w, NULL);
+	} else if (w->is_gunzip && w->do_gzip) {
+		ALLOC_OBJ(vef, VEF_MAGIC);
+		AN(vef);
+		vef->vgz = VGZ_NewGzip(w, "G F E");
+		AZ(w->vef_priv);
+		w->vef_priv = vef;
+		VEP_Init(w, vfp_vep_callback);
+	} else if (w->is_gzip) {
+		w->vgz_rx = VGZ_NewUngzip(w, "U F E");
+		ALLOC_OBJ(vef, VEF_MAGIC);
+		AN(vef);
+		vef->vgz = VGZ_NewGzip(w, "G F E");
+		AZ(w->vef_priv);
+		w->vef_priv = vef;
+		VEP_Init(w, vfp_vep_callback);
+	} else {
+		AZ(w->vef_priv);
+		VEP_Init(w, NULL);
+	}
+
+	(void)estimate;
+	AN(w->vep);
+}
+
+static int __match_proto__()
+vfp_esi_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AZ(w->fetch_failed);
+	AN(w->vep);
+	assert(w->htc == htc);
+	if (w->is_gzip && w->do_gunzip)
+		i = vfp_esi_bytes_gu(w, htc, bytes);
+	else if (w->is_gunzip && w->do_gzip)
+		i = vfp_esi_bytes_ug(w, htc, bytes);
+	else if (w->is_gzip)
+		i = vfp_esi_bytes_gg(w, htc, bytes);
+	else
+		i = vfp_esi_bytes_uu(w, htc, bytes);
+	AN(w->vep);
+	return (i);
+}
+
+static int __match_proto__()
+vfp_esi_end(struct worker *w)
+{
+	struct vsb *vsb;
+	struct vef_priv *vef;
+	ssize_t l;
+	int retval;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AN(w->vep);
+
+	retval = w->fetch_failed;
+
+	if (w->vgz_rx != NULL && VGZ_Destroy(&w->vgz_rx, -1) != VGZ_END)
+		retval = FetchError(w,
+		    "Gunzip+ESI Failed at the very end");
+
+	vsb = VEP_Finish(w);
+
+	if (vsb != NULL) {
+		if (!retval) {
+			l = VSB_len(vsb);
+			assert(l > 0);
+			/* XXX: This is a huge waste of storage... */
+			w->fetch_obj->esidata = STV_alloc(w, l);
+			if (w->fetch_obj->esidata != NULL) {
+				memcpy(w->fetch_obj->esidata->ptr,
+				    VSB_data(vsb), l);
+				w->fetch_obj->esidata->len = l;
+			} else {
+				retval = FetchError(w,
+				    "Could not allocate storage for esidata");
+			}
+		}
+		VSB_delete(vsb);
+	}
+
+	if (w->vef_priv != NULL) {
+		vef = w->vef_priv;
+		CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
+		w->vef_priv = NULL;
+		VGZ_UpdateObj(vef->vgz, w->fetch_obj);
+		if (VGZ_Destroy(&vef->vgz,  -1) != VGZ_END)
+			retval = FetchError(w,
+			    "ESI+Gzip Failed at the very end");
+		FREE_OBJ(vef);
+	}
+	return (retval);
+}
+
+struct vfp vfp_esi = {
+        .begin  =       vfp_esi_begin,
+        .bytes  =       vfp_esi_bytes,
+        .end    =       vfp_esi_end,
+};
diff --git a/bin/varnishd/cache/cache_esi_parse.c b/bin/varnishd/cache/cache_esi_parse.c
new file mode 100644
index 0000000..9e2b4f6
--- /dev/null
+++ b/bin/varnishd/cache/cache_esi_parse.c
@@ -0,0 +1,1189 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * VEP Varnish Esi Parsing
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_esi.h"
+#include "vct.h"
+#include "vend.h"
+#include "vgz.h"
+
+//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
+#define Debug(fmt, ...) /**/
+
+struct vep_state;
+
+enum dowhat {DO_ATTR, DO_TAG};
+typedef void dostuff_f(struct vep_state *, enum dowhat);
+
+struct vep_match {
+	const char	*match;
+	const char	* const *state;
+};
+
+enum vep_mark { VERBATIM = 0, SKIP };
+
+struct vep_state {
+	unsigned		magic;
+#define VEP_MAGIC		0x55cb9b82
+	struct vsb		*vsb;
+
+	struct worker		*wrk;
+	int			dogzip;
+	vep_callback_t		*cb;
+
+	/* Internal Counter for default call-back function */
+	ssize_t			cb_x;
+
+	/* parser state */
+	const char		*state;
+	unsigned		startup;
+	unsigned		esi_found;
+
+	unsigned		endtag;
+	unsigned		emptytag;
+	unsigned		canattr;
+
+	unsigned		remove;
+
+	ssize_t			o_wait;
+	ssize_t			o_pending;
+	ssize_t			o_total;
+	uint32_t		crc;
+	ssize_t			o_crc;
+	uint32_t		crcp;
+	ssize_t			o_last;
+
+const char		*hack_p;
+	const char		*ver_p;
+
+	const char		*until;
+	const char		*until_p;
+	const char		*until_s;
+
+	int			in_esi_tag;
+
+	const char		*esicmt;
+	const char		*esicmt_p;
+
+	struct vep_match	*attr;
+	struct vsb		*attr_vsb;
+	int			attr_delim;
+
+	struct vep_match	*match;
+	struct vep_match	*match_hit;
+
+	char			tag[10];
+	int			tag_i;
+
+	dostuff_f		*dostuff;
+
+	struct vsb		*include_src;
+
+	unsigned		nm_skip;
+	unsigned		nm_verbatim;
+	unsigned		nm_pending;
+	enum vep_mark		last_mark;
+};
+
+/*---------------------------------------------------------------------*/
+
+static const char * const VEP_START =		"[Start]";
+static const char * const VEP_TESTXML =		"[TestXml]";
+static const char * const VEP_NOTXML =		"[NotXml]";
+
+static const char * const VEP_NEXTTAG =		"[NxtTag]";
+static const char * const VEP_NOTMYTAG =	"[NotMyTag]";
+
+static const char * const VEP_STARTTAG =	"[StartTag]";
+static const char * const VEP_COMMENT =		"[Comment]";
+static const char * const VEP_CDATA =		"[CDATA]";
+static const char * const VEP_ESITAG =		"[ESITag]";
+
+static const char * const VEP_ESIREMOVE =	"[ESI:Remove]";
+static const char * const VEP_ESIINCLUDE =	"[ESI:Include]";
+static const char * const VEP_ESICOMMENT =	"[ESI:Comment]";
+static const char * const VEP_ESIBOGON =	"[ESI:Bogon]";
+
+static const char * const VEP_INTAG =		"[InTag]";
+static const char * const VEP_TAGERROR =	"[TagError]";
+
+static const char * const VEP_ATTR =		"[Attribute]";
+static const char * const VEP_SKIPATTR =	"[SkipAttribute]";
+static const char * const VEP_ATTRDELIM =	"[AttrDelim]";
+static const char * const VEP_ATTRGETVAL =	"[AttrGetValue]";
+static const char * const VEP_ATTRVAL =		"[AttrValue]";
+
+static const char * const VEP_UNTIL =		"[Until]";
+static const char * const VEP_MATCHBUF =	"[MatchBuf]";
+static const char * const VEP_MATCH =		"[Match]";
+
+/*---------------------------------------------------------------------*/
+
+static struct vep_match vep_match_starttag[] = {
+	{ "!--",	&VEP_COMMENT },
+	{ "esi:",	&VEP_ESITAG },
+	{ "![CDATA[",	&VEP_CDATA },
+	{ NULL,		&VEP_NOTMYTAG }
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct vep_match vep_match_esi[] = {
+	{ "include",	&VEP_ESIINCLUDE },
+	{ "remove",	&VEP_ESIREMOVE },
+	{ "comment",	&VEP_ESICOMMENT },
+	{ NULL,		&VEP_ESIBOGON }
+};
+
+/*---------------------------------------------------------------------*/
+
+static struct vep_match vep_match_attr_include[] = {
+	{ "src=",	&VEP_ATTRGETVAL },
+	{ NULL,		&VEP_SKIPATTR }
+};
+
+/*--------------------------------------------------------------------
+ * Report a parsing error
+ */
+
+static void
+vep_error(const struct vep_state *vep, const char *p)
+{
+	intmax_t l;
+
+	VSC_C_main->esi_errors++;
+	l = (intmax_t)(vep->ver_p - vep->hack_p);
+	WSLB(vep->wrk, SLT_ESI_xmlerror, "ERR at %jd %s", l, p);
+
+}
+
+/*--------------------------------------------------------------------
+ * Report a parsing warning
+ */
+
+static void
+vep_warn(const struct vep_state *vep, const char *p)
+{
+	intmax_t l;
+
+	VSC_C_main->esi_warnings++;
+	l = (intmax_t)(vep->ver_p - vep->hack_p);
+	printf("WARNING at %jd %s\n", l, p);
+	WSLB(vep->wrk, SLT_ESI_xmlerror, "WARN at %jd %s", l, p);
+
+}
+
+/*---------------------------------------------------------------------
+ * return match or NULL if more input needed.
+ */
+
+static struct vep_match *
+vep_match(struct vep_state *vep, const char *b, const char *e)
+{
+	struct vep_match *vm;
+	const char *q, *r;
+	ssize_t l;
+
+	for (vm = vep->match; vm->match; vm++) {
+		r = b;
+		for (q = vm->match; *q && r < e; q++, r++)
+			if (*q != *r)
+				break;
+		if (*q != '\0' && r == e) {
+			if (b != vep->tag) {
+				l = e - b;
+				assert(l < sizeof vep->tag);
+				memmove(vep->tag, b, l);
+				vep->tag_i = l;
+			}
+			return (NULL);
+		}
+		if (*q == '\0')
+			return (vm);
+	}
+	return (vm);
+}
+
+/*---------------------------------------------------------------------
+ *
+ */
+
+static void
+vep_emit_len(const struct vep_state *vep, ssize_t l, int m8, int m16, int m64)
+{
+	uint8_t buf[9];
+
+	assert(l > 0);
+	if (l < 256) {
+		buf[0] = (uint8_t)m8;
+		buf[1] = (uint8_t)l;
+		assert((ssize_t)buf[1] == l);
+		VSB_bcat(vep->vsb, buf, 2);
+	} else if (l < 65536) {
+		buf[0] = (uint8_t)m16;
+		vbe16enc(buf + 1, (uint16_t)l);
+		assert((ssize_t)vbe16dec(buf + 1) == l);
+		VSB_bcat(vep->vsb, buf, 3);
+	} else {
+		buf[0] = (uint8_t)m64;
+		vbe64enc(buf + 1, l);
+		assert((ssize_t)vbe64dec(buf + 1) == l);
+		VSB_bcat(vep->vsb, buf, 9);
+	}
+}
+
+static void
+vep_emit_skip(const struct vep_state *vep, ssize_t l)
+{
+
+	if (cache_param->esi_syntax & 0x20) {
+		Debug("---> SKIP(%jd)\n", (intmax_t)l);
+	}
+	vep_emit_len(vep, l, VEC_S1, VEC_S2, VEC_S8);
+}
+
+static void
+vep_emit_verbatim(const struct vep_state *vep, ssize_t l, ssize_t l_crc)
+{
+	uint8_t buf[4];
+
+	if (cache_param->esi_syntax & 0x20) {
+		Debug("---> VERBATIM(%jd)\n", (intmax_t)l);
+	}
+	vep_emit_len(vep, l, VEC_V1, VEC_V2, VEC_V8);
+	if (vep->dogzip) {
+		vep_emit_len(vep, l_crc, VEC_C1, VEC_C2, VEC_C8);
+		vbe32enc(buf, vep->crc);
+		VSB_bcat(vep->vsb, buf, sizeof buf);
+	}
+}
+
+static void
+vep_emit_common(struct vep_state *vep, ssize_t l, enum vep_mark mark)
+{
+
+	assert(l > 0);
+	assert(mark == SKIP || mark == VERBATIM);
+	if (mark == SKIP)
+		vep_emit_skip(vep, l);
+	else
+		vep_emit_verbatim(vep, l, vep->o_crc);
+
+	vep->crc = crc32(0L, Z_NULL, 0);
+	vep->o_crc = 0;
+	vep->o_total += l;
+}
+
+/*---------------------------------------------------------------------
+ *
+ */
+
+static void
+vep_mark_common(struct vep_state *vep, const char *p, enum vep_mark mark)
+{
+	ssize_t l, lcb;
+
+	assert(mark == SKIP || mark == VERBATIM);
+
+	/* The NO-OP case, no data, no pending data & no change of mode */
+	if (vep->last_mark == mark && p == vep->ver_p && vep->o_pending == 0)
+		return;
+
+	/*
+	 * If we changed mode, emit whatever the opposite mode
+	 * assembled before the pending bytes.
+	 */
+
+	if (vep->last_mark != mark && (vep->o_wait > 0 || vep->startup)) {
+		lcb = vep->cb(vep->wrk, 0,
+		    mark == VERBATIM ? VGZ_RESET : VGZ_ALIGN);
+		if (lcb - vep->o_last > 0)
+			vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
+		vep->o_last = lcb;
+		vep->o_wait = 0;
+	}
+
+	/* Transfer pending bytes CRC into active mode CRC */
+	if (vep->o_pending) {
+		(void)vep->cb(vep->wrk, vep->o_pending, VGZ_NORMAL);
+		if (vep->o_crc == 0) {
+			vep->crc = vep->crcp;
+			vep->o_crc = vep->o_pending;
+		} else {
+			vep->crc = crc32_combine(vep->crc,
+			    vep->crcp, vep->o_pending);
+			vep->o_crc += vep->o_pending;
+		}
+		vep->crcp = crc32(0L, Z_NULL, 0);
+		vep->o_wait += vep->o_pending;
+		vep->o_pending = 0;
+	}
+
+	/* * Process this bit of input */
+	AN(vep->ver_p);
+	l = p - vep->ver_p;
+	assert(l >= 0);
+	vep->crc = crc32(vep->crc, (const void*)vep->ver_p, l);
+	vep->o_crc += l;
+	vep->ver_p = p;
+
+	vep->o_wait += l;
+	vep->last_mark = mark;
+	(void)vep->cb(vep->wrk, l, VGZ_NORMAL);
+}
+
+static void
+vep_mark_verbatim(struct vep_state *vep, const char *p)
+{
+
+	vep_mark_common(vep, p, VERBATIM);
+	vep->nm_verbatim++;
+}
+
+static void
+vep_mark_skip(struct vep_state *vep, const char *p)
+{
+
+	vep_mark_common(vep, p, SKIP);
+	vep->nm_skip++;
+}
+
+static void
+vep_mark_pending(struct vep_state *vep, const char *p)
+{
+	ssize_t l;
+
+	AN(vep->ver_p);
+	l = p - vep->ver_p;
+	assert(l > 0);
+	assert(l >= 0);
+	vep->crcp = crc32(vep->crcp, (const void *)vep->ver_p, l);
+	vep->ver_p = p;
+
+	vep->o_pending += l;
+	vep->nm_pending++;
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static void __match_proto__()
+vep_do_comment(struct vep_state *vep, enum dowhat what)
+{
+	Debug("DO_COMMENT(%d)\n", what);
+	assert(what == DO_TAG);
+	if (!vep->emptytag)
+		vep_error(vep, "ESI 1.0 <esi:comment> needs final '/'");
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static void __match_proto__()
+vep_do_remove(struct vep_state *vep, enum dowhat what)
+{
+	Debug("DO_REMOVE(%d, end %d empty %d remove %d)\n",
+	    what, vep->endtag, vep->emptytag, vep->remove);
+	assert(what == DO_TAG);
+	if (vep->emptytag) {
+		vep_error(vep,
+		    "ESI 1.0 <esi:remove/> not legal");
+	} else {
+		if (vep->remove && !vep->endtag)
+			vep_error(vep,
+			    "ESI 1.0 <esi:remove> already open");
+		else if (!vep->remove && vep->endtag)
+			vep_error(vep,
+			    "ESI 1.0 <esi:remove> not open");
+		else
+			vep->remove = !vep->endtag;
+	}
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static void __match_proto__()
+vep_do_include(struct vep_state *vep, enum dowhat what)
+{
+	char *p, *q, *h;
+	ssize_t l;
+	txt url;
+
+	Debug("DO_INCLUDE(%d)\n", what);
+	if (what == DO_ATTR) {
+		Debug("ATTR (%s) (%s)\n", vep->match_hit->match,
+			VSB_data(vep->attr_vsb));
+		if (vep->include_src != NULL) {
+			vep_error(vep,
+			    "ESI 1.0 <esi:include> "
+			    "has multiple src= attributes");
+			vep->state = VEP_TAGERROR;
+			VSB_delete(vep->attr_vsb);
+			VSB_delete(vep->include_src);
+			vep->attr_vsb = NULL;
+			vep->include_src = NULL;
+			return;
+		}
+		XXXAZ(vep->include_src);	/* multiple src= */
+		vep->include_src = vep->attr_vsb;
+		return;
+	}
+	assert(what == DO_TAG);
+	if (!vep->emptytag)
+		vep_warn(vep,
+		    "ESI 1.0 <esi:include> lacks final '/'");
+	if (vep->include_src == NULL) {
+		vep_error(vep,
+		    "ESI 1.0 <esi:include> lacks src attr");
+		return;
+	}
+
+	/*
+	 * Strictly speaking, we ought to spit out any piled up skip before
+	 * emitting the VEC for the include, but objectively that makes no
+	 * difference and robs us of a chance to collapse another skip into
+	 * this on so we don't do that.
+	 * However, we cannot tolerate any verbatim stuff piling up.
+	 * The mark_skip() before calling dostuff should have taken
+	 * care of that.  Make sure.
+	 */
+	assert(vep->o_wait == 0 || vep->last_mark == SKIP);
+	/* XXX: what if it contains NUL bytes ?? */
+	p = VSB_data(vep->include_src);
+	l = VSB_len(vep->include_src);
+	h = 0;
+
+	VSB_printf(vep->vsb, "%c", VEC_INCL);
+	if (l > 7 && !memcmp(p, "http://", 7)) {
+		h = p + 7;
+		p = strchr(h, '/');
+		AN(p);
+		Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
+		VSB_printf(vep->vsb, "Host: %.*s%c",
+		    (int)(p-h), h, 0);
+	} else if (*p == '/') {
+		VSB_printf(vep->vsb, "%c", 0);
+	} else {
+		VSB_printf(vep->vsb, "%c", 0);
+		url = vep->wrk->bereq->hd[HTTP_HDR_URL];
+		/* Look for the last / before a '?' */
+		h = NULL;
+		for (q = url.b; q < url.e && *q != '?'; q++)
+			if (*q == '/')
+				h = q;
+		if (h == NULL)
+			h = q + 1;
+
+		Debug("INCL:: [%.*s]/[%s]\n",
+		    (int)(h - url.b), url.b, p);
+		VSB_printf(vep->vsb, "%.*s/", (int)(h - url.b), url.b);
+	}
+	l -= (p - VSB_data(vep->include_src));
+	for (q = p; *q != '\0'; ) {
+		if (*q == '&') {
+#define R(w,f,r)							\
+			if (q + w <= p + l && !memcmp(q, f, w)) { \
+				VSB_printf(vep->vsb, "%c", r);	\
+				q += w;				\
+				continue;			\
+			}
+			R(6, "'", '\'');
+			R(6, """, '"');
+			R(4, "<", '<');
+			R(4, ">", '>');
+			R(5, "&", '&');
+		}
+		VSB_printf(vep->vsb, "%c", *q++);
+	}
+#undef R
+	VSB_printf(vep->vsb, "%c", 0);
+
+	VSB_delete(vep->include_src);
+	vep->include_src = NULL;
+}
+
+/*---------------------------------------------------------------------
+ * Lex/Parse object for ESI instructions
+ *
+ * This function is called with the input object piecemal so do not
+ * assume that we have more than one char available at at time, but
+ * optimize for getting huge chunks.
+ *
+ * NB: At the bottom of this source-file, there is a dot-diagram matching
+ * NB: the state-machine.  Please maintain it along with the code.
+ */
+
+void
+VEP_Parse(const struct worker *w, const char *p, size_t l)
+{
+	struct vep_state *vep;
+	const char *e;
+	struct vep_match *vm;
+	int i;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vep = w->vep;
+	CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
+	assert(l > 0);
+
+	/* XXX: Really need to fix this */
+	if (vep->hack_p == NULL)
+		vep->hack_p = p;
+
+	vep->ver_p = p;
+
+	e = p + l;
+
+	while (p < e) {
+		AN(vep->state);
+		i = e - p;
+		if (i > 10)
+			i = 10;
+		Debug("EP %s %d (%.*s) [%.*s]\n",
+		    vep->state,
+		    vep->remove,
+		    vep->tag_i, vep->tag,
+		    i, p);
+		assert(p >= vep->ver_p);
+
+		/******************************************************
+		 * SECTION A
+		 */
+
+		if (vep->state == VEP_START) {
+			if (cache_param->esi_syntax & 0x1)
+				vep->state = VEP_NEXTTAG;
+			else
+				vep->state = VEP_TESTXML;
+		} else if (vep->state == VEP_TESTXML) {
+			/*
+			 * If the first non-whitespace char is different
+			 * from '<' we assume this is not XML.
+			 */
+			while (p < e && vct_islws(*p))
+				p++;
+			vep_mark_verbatim(vep, p);
+			if (p < e && *p == '<') {
+				p++;
+				vep->state = VEP_STARTTAG;
+			} else if (p < e) {
+				WSLB(vep->wrk, SLT_ESI_xmlerror,
+				    "No ESI processing, first char not '<'");
+				vep->state = VEP_NOTXML;
+			}
+		} else if (vep->state == VEP_NOTXML) {
+			/*
+			 * This is not recognized as XML, just skip thru
+			 * vfp_esi_end() will handle the rest
+			 */
+			p = e;
+			vep_mark_verbatim(vep, p);
+
+		/******************************************************
+		 * SECTION B
+		 */
+
+		} else if (vep->state == VEP_NOTMYTAG) {
+			if (cache_param->esi_syntax & 0x2) {
+				p++;
+				vep->state = VEP_NEXTTAG;
+			} else {
+				vep->tag_i = 0;
+				while (p < e) {
+					if (*p++ == '>') {
+						vep->state = VEP_NEXTTAG;
+						break;
+					}
+				}
+			}
+			if (p == e && !vep->remove)
+				vep_mark_verbatim(vep, p);
+		} else if (vep->state == VEP_NEXTTAG) {
+			/*
+			 * Hunt for start of next tag and keep an eye
+			 * out for end of EsiCmt if armed.
+			 */
+			vep->emptytag = 0;
+			vep->endtag = 0;
+			vep->attr = NULL;
+			vep->dostuff = NULL;
+			while (p < e && *p != '<') {
+				if (vep->esicmt_p == NULL) {
+					p++;
+					continue;
+				}
+				if (*p != *vep->esicmt_p) {
+					p++;
+					vep->esicmt_p = vep->esicmt;
+					continue;
+				}
+				if (!vep->remove &&
+				    vep->esicmt_p == vep->esicmt)
+					vep_mark_verbatim(vep, p);
+				p++;
+				if (*++vep->esicmt_p == '\0') {
+					vep->esi_found = 1;
+					vep->esicmt = NULL;
+					vep->esicmt_p = NULL;
+					/*
+					 * The end of the esicmt
+					 * should not be emitted.
+					 * But the stuff before should
+					 */
+					vep_mark_skip(vep, p);
+				}
+			}
+			if (p < e) {
+				if (!vep->remove)
+					vep_mark_verbatim(vep, p);
+				assert(*p == '<');
+				p++;
+				vep->state = VEP_STARTTAG;
+			} else if (vep->esicmt_p == vep->esicmt && !vep->remove)
+				vep_mark_verbatim(vep, p);
+
+		/******************************************************
+		 * SECTION C
+		 */
+
+		} else if (vep->state == VEP_STARTTAG) {
+			/*
+			 * Start of tag, set up match table
+			 */
+			if (p < e) {
+				if (*p == '/') {
+					vep->endtag = 1;
+					p++;
+				}
+				vep->match = vep_match_starttag;
+				vep->state = VEP_MATCH;
+			}
+		} else if (vep->state == VEP_COMMENT) {
+			/*
+			 * We are in a comment, find out if it is an
+			 * ESI comment or a regular comment
+			 */
+			if (vep->esicmt == NULL)
+				vep->esicmt_p = vep->esicmt = "esi";
+			while (p < e) {
+				if (*p != *vep->esicmt_p) {
+					vep->esicmt_p = vep->esicmt = NULL;
+					vep->until_p = vep->until = "-->";
+					vep->until_s = VEP_NEXTTAG;
+					vep->state = VEP_UNTIL;
+					vep_mark_verbatim(vep, p);
+					break;
+				}
+				p++;
+				if (*++vep->esicmt_p != '\0')
+					continue;
+				if (vep->remove)
+					vep_error(vep,
+					    "ESI 1.0 Nested <!--esi"
+					    " element in <esi:remove>");
+				vep->esicmt_p = vep->esicmt = "-->";
+				vep->state = VEP_NEXTTAG;
+				vep_mark_skip(vep, p);
+				break;
+			}
+		} else if (vep->state == VEP_CDATA) {
+			/*
+			 * Easy: just look for the end of CDATA
+			 */
+			vep->until_p = vep->until = "]]>";
+			vep->until_s = VEP_NEXTTAG;
+			vep->state = VEP_UNTIL;
+		} else if (vep->state == VEP_ESITAG) {
+			vep->in_esi_tag = 1;
+			vep->esi_found = 1;
+			vep_mark_skip(vep, p);
+			vep->match = vep_match_esi;
+			vep->state = VEP_MATCH;
+		} else if (vep->state == VEP_ESIINCLUDE) {
+			if (vep->remove) {
+				vep_error(vep,
+				    "ESI 1.0 <esi:include> element"
+				    " nested in <esi:remove>");
+				vep->state = VEP_TAGERROR;
+			} else if (vep->endtag) {
+				vep_error(vep,
+				    "ESI 1.0 </esi:include> illegal end-tag");
+				vep->state = VEP_TAGERROR;
+			} else {
+				vep->dostuff = vep_do_include;
+				vep->state = VEP_INTAG;
+				vep->attr = vep_match_attr_include;
+			}
+		} else if (vep->state == VEP_ESIREMOVE) {
+			vep->dostuff = vep_do_remove;
+			vep->state = VEP_INTAG;
+		} else if (vep->state == VEP_ESICOMMENT) {
+			if (vep->remove) {
+				vep_error(vep,
+				    "ESI 1.0 <esi:comment> element"
+				    " nested in <esi:remove>");
+				vep->state = VEP_TAGERROR;
+			} else if (vep->endtag) {
+				vep_error(vep,
+				    "ESI 1.0 </esi:comment> illegal end-tag");
+				vep->state = VEP_TAGERROR;
+			} else {
+				vep->dostuff = vep_do_comment;
+				vep->state = VEP_INTAG;
+			}
+		} else if (vep->state == VEP_ESIBOGON) {
+			vep_error(vep,
+			    "ESI 1.0 <esi:bogus> element");
+			vep->state = VEP_TAGERROR;
+
+		/******************************************************
+		 * SECTION D
+		 */
+
+		} else if (vep->state == VEP_INTAG) {
+			vep->tag_i = 0;
+			while (p < e && vct_islws(*p) && !vep->emptytag) {
+				p++;
+				vep->canattr = 1;
+			}
+			if (p < e && *p == '/' && !vep->emptytag) {
+				p++;
+				vep->emptytag = 1;
+				vep->canattr = 0;
+			}
+			if (p < e && *p == '>') {
+				p++;
+				AN(vep->dostuff);
+				vep_mark_skip(vep, p);
+				vep->dostuff(vep, DO_TAG);
+				vep->in_esi_tag = 0;
+				vep->state = VEP_NEXTTAG;
+			} else if (p < e && vep->emptytag) {
+				vep_error(vep,
+				    "XML 1.0 '>' does not follow '/' in tag");
+				vep->state = VEP_TAGERROR;
+			} else if (p < e && vep->canattr &&
+			    vct_isxmlnamestart(*p)) {
+				vep->state = VEP_ATTR;
+			} else if (p < e) {
+				vep_error(vep,
+				    "XML 1.0 Illegal attribute start char");
+				vep->state = VEP_TAGERROR;
+			}
+		} else if (vep->state == VEP_TAGERROR) {
+			while (p < e && *p != '>')
+				p++;
+			if (p < e) {
+				p++;
+				vep_mark_skip(vep, p);
+				vep->in_esi_tag = 0;
+				vep->state = VEP_NEXTTAG;
+			}
+
+		/******************************************************
+		 * SECTION E
+		 */
+
+		} else if (vep->state == VEP_ATTR) {
+			AZ(vep->attr_delim);
+			if (vep->attr == NULL) {
+				p++;
+				AZ(vep->attr_vsb);
+				vep->state = VEP_SKIPATTR;
+			} else {
+				vep->match = vep->attr;
+				vep->state = VEP_MATCH;
+			}
+		} else if (vep->state == VEP_SKIPATTR) {
+			while (p < e && vct_isxmlname(*p))
+				p++;
+			if (p < e && *p == '=') {
+				p++;
+				vep->state = VEP_ATTRDELIM;
+			} else if (p < e && *p == '>') {
+				vep->state = VEP_INTAG;
+			} else if (p < e && *p == '/') {
+				vep->state = VEP_INTAG;
+			} else if (p < e && vct_issp(*p)) {
+				vep->state = VEP_INTAG;
+			} else if (p < e) {
+				vep_error(vep,
+				    "XML 1.0 Illegal attr char");
+				vep->state = VEP_TAGERROR;
+			}
+		} else if (vep->state == VEP_ATTRGETVAL) {
+			vep->attr_vsb = VSB_new_auto();
+			vep->state = VEP_ATTRDELIM;
+		} else if (vep->state == VEP_ATTRDELIM) {
+			AZ(vep->attr_delim);
+			if (*p == '"' || *p == '\'') {
+				vep->attr_delim = *p++;
+				vep->state = VEP_ATTRVAL;
+			} else if (!vct_issp(*p)) {
+				vep->attr_delim = ' ';
+				vep->state = VEP_ATTRVAL;
+			} else {
+				vep_error(vep,
+				    "XML 1.0 Illegal attribute delimiter");
+				vep->state = VEP_TAGERROR;
+			}
+
+		} else if (vep->state == VEP_ATTRVAL) {
+			while (p < e && *p != '>' && *p != vep->attr_delim &&
+			   (vep->attr_delim != ' ' || !vct_issp(*p))) {
+				if (vep->attr_vsb != NULL)
+					VSB_bcat(vep->attr_vsb, p, 1);
+				p++;
+			}
+			if (p < e && *p == '>') {
+				vep_error(vep,
+				    "XML 1.0 Missing end attribute delimiter");
+				vep->state = VEP_TAGERROR;
+				vep->attr_delim = 0;
+				if (vep->attr_vsb != NULL) {
+					AZ(VSB_finish(vep->attr_vsb));
+					VSB_delete(vep->attr_vsb);
+					vep->attr_vsb = NULL;
+				}
+			} else if (p < e) {
+				vep->attr_delim = 0;
+				p++;
+				vep->state = VEP_INTAG;
+				if (vep->attr_vsb != NULL) {
+					AZ(VSB_finish(vep->attr_vsb));
+					AN(vep->dostuff);
+					vep->dostuff(vep, DO_ATTR);
+					vep->attr_vsb = NULL;
+				}
+			}
+
+		/******************************************************
+		 * Utility Section
+		 */
+
+		} else if (vep->state == VEP_MATCH) {
+			/*
+			 * Match against a table
+			 */
+			vm = vep_match(vep, p, e);
+			vep->match_hit = vm;
+			if (vm != NULL) {
+				if (vm->match != NULL)
+					p += strlen(vm->match);
+				vep->state = *vm->state;
+				vep->match = NULL;
+				vep->tag_i = 0;
+			} else {
+				memcpy(vep->tag, p, e - p);
+				vep->tag_i = e - p;
+				vep->state = VEP_MATCHBUF;
+				p = e;
+			}
+		} else if (vep->state == VEP_MATCHBUF) {
+			/*
+			 * Match against a table while split over input
+			 * sections.
+			 */
+			do {
+				if (*p == '>') {
+					for (vm = vep->match;
+					    vm->match != NULL; vm++)
+						continue;
+					AZ(vm->match);
+				} else {
+					vep->tag[vep->tag_i++] = *p++;
+					vm = vep_match(vep,
+					    vep->tag, vep->tag + vep->tag_i);
+					if (vm && vm->match == NULL) {
+						vep->tag_i--;
+						p--;
+					}
+				}
+			} while (vm == NULL && p < e);
+			vep->match_hit = vm;
+			if (vm == NULL) {
+				assert(p == e);
+			} else {
+				vep->state = *vm->state;
+				vep->match = NULL;
+			}
+		} else if (vep->state == VEP_UNTIL) {
+			/*
+			 * Skip until we see magic string
+			 */
+			while (p < e) {
+				if (*p++ != *vep->until_p++) {
+					vep->until_p = vep->until;
+				} else if (*vep->until_p == '\0') {
+					vep->state = vep->until_s;
+					break;
+				}
+			}
+			if (p == e && !vep->remove)
+				vep_mark_verbatim(vep, p);
+		} else {
+			Debug("*** Unknown state %s\n", vep->state);
+			INCOMPL();
+		}
+	}
+	/*
+	 * We must always mark up the storage we got, try to do so
+	 * in the most efficient way, in particular with respect to
+	 * minimizing and limiting use of pending.
+	 */
+	if (p == vep->ver_p)
+		;
+	else if (vep->in_esi_tag)
+		vep_mark_skip(vep, p);
+	else if (vep->remove)
+		vep_mark_skip(vep, p);
+	else
+		vep_mark_pending(vep, p);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static ssize_t __match_proto__()
+vep_default_cb(struct worker *w, ssize_t l, enum vgz_flag flg)
+{
+
+	(void)flg;
+	AN(w->vep);
+	w->vep->cb_x += l;
+Debug("CB(%jd,%d) = %jd\n", (intmax_t)l, flg, (intmax_t)w->vep->cb_x);
+	return (w->vep->cb_x);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+void
+VEP_Init(struct worker *w, vep_callback_t *cb)
+{
+	struct vep_state *vep;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AZ(w->vep);
+	w->vep = (void*)WS_Alloc(w->ws, sizeof *vep);
+	AN(w->vep);
+
+	vep = w->vep;
+	memset(vep, 0, sizeof *vep);
+	vep->magic = VEP_MAGIC;
+	vep->wrk = w;
+	vep->vsb = VSB_new_auto();
+	AN(vep->vsb);
+
+	if (cb != NULL) {
+		vep->dogzip = 1;
+		/* XXX */
+		VSB_printf(vep->vsb, "%c", VEC_GZ);
+		vep->cb = cb;
+	} else {
+		vep->cb = vep_default_cb;
+	}
+
+	vep->state = VEP_START;
+	vep->crc = crc32(0L, Z_NULL, 0);
+	vep->crcp = crc32(0L, Z_NULL, 0);
+
+	/*
+	 * We must force the GZIP header out as a SKIP string, otherwise
+	 * an object starting with <esi:include would have its GZIP header
+	 * appear after the included object (e000026.vtc)
+	 */
+	vep->startup = 1;
+	vep->ver_p = "";
+	vep->last_mark = SKIP;
+	vep_mark_common(vep, vep->ver_p, VERBATIM);
+	vep->startup = 0;
+}
+
+/*---------------------------------------------------------------------
+ */
+
+struct vsb *
+VEP_Finish(struct worker *w)
+{
+	struct vep_state *vep;
+	ssize_t l, lcb;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	vep = w->vep;
+	CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
+
+	if (vep->o_pending)
+		vep_mark_common(vep, vep->ver_p, vep->last_mark);
+	if (vep->o_wait > 0) {
+		lcb = vep->cb(vep->wrk, 0, VGZ_ALIGN);
+		vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
+	}
+	(void)vep->cb(vep->wrk, 0, VGZ_FINISH);
+
+	w->vep = NULL;
+
+	AZ(VSB_finish(vep->vsb));
+	l = VSB_len(vep->vsb);
+	if (vep->esi_found && l > 0)
+		return (vep->vsb);
+	VSB_delete(vep->vsb);
+	return (NULL);
+}
+
+#if 0
+
+digraph xml {
+	rankdir="LR"
+	size="7,10"
+#################################################################
+# SECTION A
+#
+
+START		[shape=ellipse]
+TESTXML		[shape=ellipse]
+NOTXML		[shape=ellipse]
+NEXTTAGa	[shape=hexagon, label="NEXTTAG"]
+STARTTAGa	[shape=hexagon, label="STARTTAG"]
+START		-> TESTXML
+START		-> NEXTTAGa	[style=dotted, label="syntax:1"]
+TESTXML		-> TESTXML	[label="lws"]
+TESTXML		-> NOTXML
+TESTXML		-> STARTTAGa	[label="'<'"]
+
+#################################################################
+# SECTION B
+
+NOTMYTAG	[shape=ellipse]
+NEXTTAG		[shape=ellipse]
+NOTMYTAG	-> NEXTTAG	[style=dotted, label="syntax:2"]
+STARTTAGb	[shape=hexagon, label="STARTTAG"]
+NOTMYTAG	-> NEXTTAG	[label="'>'"]
+NOTMYTAG	-> NOTMYTAG	[label="*"]
+NEXTTAG		-> NEXTTAG	[label="'-->'"]
+NEXTTAG		-> NEXTTAG	[label="*"]
+NEXTTAG		-> STARTTAGb	[label="'<'"]
+
+#################################################################
+# SECTION C
+
+STARTTAG	[shape=ellipse]
+COMMENT		[shape=ellipse]
+CDATA		[shape=ellipse]
+ESITAG		[shape=ellipse]
+ESIETAG		[shape=ellipse]
+ESIINCLUDE	[shape=ellipse]
+ESIREMOVE	[shape=ellipse]
+ESICOMMENT	[shape=ellipse]
+ESIBOGON	[shape=ellipse]
+INTAGc		[shape=hexagon, label="INTAG"]
+NOTMYTAGc	[shape=hexagon, label="NOTMYTAG"]
+NEXTTAGc	[shape=hexagon, label="NEXTTAG"]
+TAGERRORc	[shape=hexagon, label="TAGERROR"]
+C1		[shape=circle,label=""]
+STARTTAG	-> COMMENT	[label="'<!--'"]
+STARTTAG	-> ESITAG	[label="'<esi'"]
+STARTTAG	-> CDATA	[label="'<![CDATA['"]
+STARTTAG	-> NOTMYTAGc	[label="'*'"]
+COMMENT		-> NEXTTAGc	[label="'esi'"]
+COMMENT		-> C1		[label="*"]
+C1		-> C1		[label="*"]
+C1		-> NEXTTAGc	[label="-->"]
+CDATA		-> CDATA	[label="*"]
+CDATA		-> NEXTTAGc	[label="]]>"]
+ESITAG		-> ESIINCLUDE	[label="'include'"]
+ESITAG		-> ESIREMOVE	[label="'remove'"]
+ESITAG		-> ESICOMMENT	[label="'comment'"]
+ESITAG		-> ESIBOGON	[label="*"]
+ESICOMMENT	-> INTAGc
+ESICOMMENT	-> TAGERRORc
+ESICOMMENT	-> TAGERRORc	[style=dotted, label="nested\nin\nremove"]
+ESIREMOVE	-> INTAGc
+ESIREMOVE	-> TAGERRORc
+ESIINCLUDE	-> INTAGc
+ESIINCLUDE	-> TAGERRORc
+ESIINCLUDE	-> TAGERRORc	[style=dotted, label="nested\nin\nremove"]
+ESIBOGON	-> TAGERRORc
+
+#################################################################
+# SECTION D
+
+INTAG		[shape=ellipse]
+TAGERROR	[shape=ellipse]
+NEXTTAGd	[shape=hexagon, label="NEXTTAG"]
+ATTRd		[shape=hexagon, label="ATTR"]
+D1		[shape=circle, label=""]
+D2		[shape=circle, label=""]
+INTAG		-> D1		[label="lws"]
+D1		-> D2		[label="/"]
+INTAG		-> D2		[label="/"]
+INTAG		-> NEXTTAGd	[label=">"]
+D1		-> NEXTTAGd	[label=">"]
+D2		-> NEXTTAGd	[label=">"]
+D1		-> ATTRd	[label="XMLstartchar"]
+D1		-> TAGERROR	[label="*"]
+D2		-> TAGERROR	[label="*"]
+TAGERROR	-> TAGERROR	[label="*"]
+TAGERROR	-> NEXTTAGd	[label="'>'"]
+
+#################################################################
+# SECTION E
+
+ATTR		[shape=ellipse]
+SKIPATTR	[shape=ellipse]
+ATTRGETVAL	[shape=ellipse]
+ATTRDELIM	[shape=ellipse]
+ATTRVAL		[shape=ellipse]
+TAGERRORe	[shape=hexagon, label="TAGERROR"]
+INTAGe		[shape=hexagon, label="INTAG"]
+ATTR		-> SKIPATTR	[label="*"]
+ATTR		-> ATTRGETVAL	[label="wanted attr"]
+SKIPATTR	-> SKIPATTR	[label="XMLname"]
+SKIPATTR	-> ATTRDELIM	[label="'='"]
+SKIPATTR	-> TAGERRORe	[label="*"]
+ATTRGETVAL	-> ATTRDELIM
+ATTRDELIM	-> ATTRVAL	[label="\""]
+ATTRDELIM	-> ATTRVAL	[label="\'"]
+ATTRDELIM	-> ATTRVAL	[label="*"]
+ATTRDELIM	-> TAGERRORe	[label="lws"]
+ATTRVAL		-> TAGERRORe	[label="'>'"]
+ATTRVAL		-> INTAGe	[label="delim"]
+ATTRVAL		-> ATTRVAL	[label="*"]
+
+}
+
+#endif
diff --git a/bin/varnishd/cache/cache_expire.c b/bin/varnishd/cache/cache_expire.c
new file mode 100644
index 0000000..23e3fc6
--- /dev/null
+++ b/bin/varnishd/cache/cache_expire.c
@@ -0,0 +1,490 @@
+/*-
+ * 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.
+ *
+ * LRU and object timer handling.
+ *
+ * We have two data structures, a LRU-list and a binary heap for the timers
+ * and two ways to kill objects: TTL-timeouts and LRU cleanups.
+ *
+ * Any object on the LRU is also on the binheap and vice versa.
+ *
+ * We hold a single object reference for both data structures.
+ *
+ * An attempted overview:
+ *
+ *	                        EXP_Ttl()      EXP_Grace()   EXP_Keep()
+ *				   |                |            |
+ *      entered                    v                v            |
+ *         |                       +--------------->+            |
+ *         v                       |      grace                  |
+ *         +---------------------->+                             |
+ *                  ttl            |                             v
+ *                                 +---------------------------->+
+ *                                     keep
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#include "cache.h"
+
+#include "binary_heap.h"
+#include "hash/hash_slinger.h"
+#include "vtim.h"
+
+static pthread_t exp_thread;
+static struct binheap *exp_heap;
+static struct lock exp_mtx;
+
+/*--------------------------------------------------------------------
+ * struct exp manipulations
+ *
+ * The Get/Set functions encapsulate the mutual magic between the
+ * fields in one single place.
+ */
+
+void
+EXP_Clr(struct exp *e)
+{
+
+	e->ttl = -1;
+	e->grace = -1;
+	e->keep = -1;
+	e->age = 0;
+	e->entered = 0;
+}
+
+#define EXP_ACCESS(fld, low_val, extra)				\
+	double							\
+	EXP_Get_##fld(const struct exp *e)			\
+	{							\
+		return (e->fld > 0. ? e->fld : low_val);	\
+	}							\
+								\
+	void							\
+	EXP_Set_##fld(struct exp *e, double v)			\
+	{							\
+		if (v > 0.)					\
+			e->fld = v;				\
+		else {						\
+			e->fld = -1.;				\
+			extra;					\
+		}						\
+	}							\
+
+EXP_ACCESS(ttl, -1., (e->grace = e->keep = -1.))
+EXP_ACCESS(grace, 0., )
+EXP_ACCESS(keep, 0.,)
+
+/*--------------------------------------------------------------------
+ * Calculate an objects effective keep, grace or ttl time, suitably
+ * adjusted for defaults and by per-session limits.
+ */
+
+static double
+EXP_Keep(const struct sess *sp, const struct object *o)
+{
+	double r;
+
+	r = (double)cache_param->default_keep;
+	if (o->exp.keep > 0.)
+		r = o->exp.keep;
+	if (sp != NULL && sp->exp.keep > 0. && sp->exp.keep < r)
+		r = sp->exp.keep;
+	return (EXP_Ttl(sp, o) + r);
+}
+
+double
+EXP_Grace(const struct sess *sp, const struct object *o)
+{
+	double r;
+
+	r = (double)cache_param->default_grace;
+	if (o->exp.grace >= 0.)
+		r = o->exp.grace;
+	if (sp != NULL && sp->exp.grace > 0. && sp->exp.grace < r)
+		r = sp->exp.grace;
+	return (EXP_Ttl(sp, o) + r);
+}
+
+double
+EXP_Ttl(const struct sess *sp, const struct object *o)
+{
+	double r;
+
+	r = o->exp.ttl;
+	if (sp != NULL && sp->exp.ttl > 0. && sp->exp.ttl < r)
+		r = sp->exp.ttl;
+	return (o->exp.entered + r);
+}
+
+/*--------------------------------------------------------------------
+ * When & why does the timer fire for this object ?
+ */
+
+static int
+update_object_when(const struct object *o)
+{
+	struct objcore *oc;
+	double when, w2;
+
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	oc = o->objcore;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	Lck_AssertHeld(&exp_mtx);
+
+	when = EXP_Keep(NULL, o);
+	w2 = EXP_Grace(NULL, o);
+	if (w2 > when)
+		when = w2;
+	assert(!isnan(when));
+	if (when == oc->timer_when)
+		return (0);
+	oc->timer_when = when;
+	return (1);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+exp_insert(struct objcore *oc, struct lru *lru)
+{
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
+
+	Lck_AssertHeld(&lru->mtx);
+	Lck_AssertHeld(&exp_mtx);
+	assert(oc->timer_idx == BINHEAP_NOIDX);
+	binheap_insert(exp_heap, oc);
+	assert(oc->timer_idx != BINHEAP_NOIDX);
+	VTAILQ_INSERT_TAIL(&lru->lru_head, oc, lru_list);
+}
+
+/*--------------------------------------------------------------------
+ * Object has been added to cache, record in lru & binheap.
+ *
+ * The objcore comes with a reference, which we inherit.
+ */
+
+void
+EXP_Inject(struct objcore *oc, struct lru *lru, double when)
+{
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
+
+	Lck_Lock(&lru->mtx);
+	Lck_Lock(&exp_mtx);
+	oc->timer_when = when;
+	exp_insert(oc, lru);
+	Lck_Unlock(&exp_mtx);
+	Lck_Unlock(&lru->mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Object has been added to cache, record in lru & binheap.
+ *
+ * We grab a reference to the object, which will keep it around until
+ * we decide its time to let it go.
+ */
+
+void
+EXP_Insert(struct object *o)
+{
+	struct objcore *oc;
+	struct lru *lru;
+
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	oc = o->objcore;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AssertObjBusy(o);
+	HSH_Ref(oc);
+
+	assert(o->exp.entered != 0 && !isnan(o->exp.entered));
+	o->last_lru = o->exp.entered;
+
+	lru = oc_getlru(oc);
+	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
+	Lck_Lock(&lru->mtx);
+	Lck_Lock(&exp_mtx);
+	(void)update_object_when(o);
+	exp_insert(oc, lru);
+	Lck_Unlock(&exp_mtx);
+	Lck_Unlock(&lru->mtx);
+	oc_updatemeta(oc);
+}
+
+/*--------------------------------------------------------------------
+ * Object was used, move to tail of LRU list.
+ *
+ * To avoid the exp_mtx becoming a hotspot, we only attempt to move
+ * objects if they have not been moved recently and if the lock is available.
+ * This optimization obviously leaves the LRU list imperfectly sorted.
+ */
+
+int
+EXP_Touch(struct objcore *oc)
+{
+	struct lru *lru;
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+
+	/*
+	 * For -spersistent we don't move objects on the lru list.  Each
+	 * segment has its own LRU list, and the order on it is not material
+	 * for anything.  The code below would move the objects to the
+	 * LRU list of the currently open segment, which would prevent
+	 * the cleaner from doing its job.
+	 */
+	if (oc->flags & OC_F_LRUDONTMOVE)
+		return (0);
+
+	lru = oc_getlru(oc);
+	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
+
+	/*
+	 * We only need the LRU lock here.  The locking order is LRU->EXP
+	 * so we can trust the content of the oc->timer_idx without the
+	 * EXP lock.   Since each lru list has its own lock, this should
+	 * reduce contention a fair bit
+	 */
+	if (Lck_Trylock(&lru->mtx))
+		return (0);
+
+	if (oc->timer_idx != BINHEAP_NOIDX) {
+		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
+		VTAILQ_INSERT_TAIL(&lru->lru_head, oc, lru_list);
+		VSC_C_main->n_lru_moved++;
+	}
+	Lck_Unlock(&lru->mtx);
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * We have changed one or more of the object timers, shuffle it
+ * accordingly in the binheap
+ *
+ * The VCL code can send us here on a non-cached object, just return.
+ *
+ * XXX: special case check for ttl = 0 ?
+ */
+
+void
+EXP_Rearm(const struct object *o)
+{
+	struct objcore *oc;
+	struct lru *lru;
+
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	oc = o->objcore;
+	if (oc == NULL)
+		return;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	lru = oc_getlru(oc);
+	Lck_Lock(&lru->mtx);
+	Lck_Lock(&exp_mtx);
+	/*
+	 * The hang-man might have this object of the binheap while
+	 * tending to a timer.  If so, we do not muck with it here.
+	 */
+	if (oc->timer_idx != BINHEAP_NOIDX && update_object_when(o)) {
+		assert(oc->timer_idx != BINHEAP_NOIDX);
+		binheap_reorder(exp_heap, oc->timer_idx);
+		assert(oc->timer_idx != BINHEAP_NOIDX);
+	}
+	Lck_Unlock(&exp_mtx);
+	Lck_Unlock(&lru->mtx);
+	oc_updatemeta(oc);
+}
+
+/*--------------------------------------------------------------------
+ * This thread monitors the root of the binary heap and whenever an
+ * object expires, accounting also for graceability, it is killed.
+ */
+
+static void * __match_proto__(void *start_routine(void *))
+exp_timer(struct sess *sp, void *priv)
+{
+	struct objcore *oc;
+	struct lru *lru;
+	double t;
+	struct object *o;
+
+	(void)priv;
+	t = VTIM_real();
+	oc = NULL;
+	while (1) {
+		if (oc == NULL) {
+			WSL_Flush(sp->wrk, 0);
+			WRK_SumStat(sp->wrk);
+			VTIM_sleep(cache_param->expiry_sleep);
+			t = VTIM_real();
+		}
+
+		Lck_Lock(&exp_mtx);
+		oc = binheap_root(exp_heap);
+		if (oc == NULL) {
+			Lck_Unlock(&exp_mtx);
+			continue;
+		}
+		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+
+		/*
+		 * We may have expired so many objects that our timestamp
+		 * got out of date, refresh it and check again.
+		 */
+		if (oc->timer_when > t)
+			t = VTIM_real();
+		if (oc->timer_when > t) {
+			Lck_Unlock(&exp_mtx);
+			oc = NULL;
+			continue;
+		}
+
+		/*
+		 * It's time...
+		 * Technically we should drop the exp_mtx, get the lru->mtx
+		 * get the exp_mtx again and then check that the oc is still
+		 * on the binheap.  We take the shorter route and try to
+		 * get the lru->mtx and punt if we fail.
+		 */
+
+		lru = oc_getlru(oc);
+		CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
+		if (Lck_Trylock(&lru->mtx)) {
+			Lck_Unlock(&exp_mtx);
+			oc = NULL;
+			continue;
+		}
+
+		/* Remove from binheap */
+		assert(oc->timer_idx != BINHEAP_NOIDX);
+		binheap_delete(exp_heap, oc->timer_idx);
+		assert(oc->timer_idx == BINHEAP_NOIDX);
+
+		/* And from LRU */
+		lru = oc_getlru(oc);
+		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
+
+		Lck_Unlock(&exp_mtx);
+		Lck_Unlock(&lru->mtx);
+
+		VSC_C_main->n_expired++;
+
+		CHECK_OBJ_NOTNULL(oc->objhead, OBJHEAD_MAGIC);
+		o = oc_getobj(sp->wrk, oc);
+		WSL(sp->wrk, SLT_ExpKill, 0, "%u %.0f",
+		    o->xid, EXP_Ttl(NULL, o) - t);
+		(void)HSH_Deref(sp->wrk, oc, NULL);
+	}
+	NEEDLESS_RETURN(NULL);
+}
+
+/*--------------------------------------------------------------------
+ * Attempt to make space by nuking the oldest object on the LRU list
+ * which isn't in use.
+ * Returns: 1: did, 0: didn't, -1: can't
+ */
+
+int
+EXP_NukeOne(struct worker *w, struct lru *lru)
+{
+	struct objcore *oc;
+	struct object *o;
+
+	/* Find the first currently unused object on the LRU.  */
+	Lck_Lock(&lru->mtx);
+	Lck_Lock(&exp_mtx);
+	VTAILQ_FOREACH(oc, &lru->lru_head, lru_list) {
+		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+		assert (oc->timer_idx != BINHEAP_NOIDX);
+		/*
+		 * It wont release any space if we cannot release the last
+		 * reference, besides, if somebody else has a reference,
+		 * it's a bad idea to nuke this object anyway.
+		 */
+		if (oc->refcnt == 1)
+			break;
+	}
+	if (oc != NULL) {
+		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
+		binheap_delete(exp_heap, oc->timer_idx);
+		assert(oc->timer_idx == BINHEAP_NOIDX);
+		VSC_C_main->n_lru_nuked++;
+	}
+	Lck_Unlock(&exp_mtx);
+	Lck_Unlock(&lru->mtx);
+
+	if (oc == NULL)
+		return (-1);
+
+	/* XXX: bad idea for -spersistent */
+	o = oc_getobj(w, oc);
+	WSL(w, SLT_ExpKill, 0, "%u LRU", o->xid);
+	(void)HSH_Deref(w, NULL, &o);
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * BinHeap helper functions for objcore.
+ */
+
+static int
+object_cmp(void *priv, void *a, void *b)
+{
+	struct objcore *aa, *bb;
+
+	(void)priv;
+	CAST_OBJ_NOTNULL(aa, a, OBJCORE_MAGIC);
+	CAST_OBJ_NOTNULL(bb, b, OBJCORE_MAGIC);
+	return (aa->timer_when < bb->timer_when);
+}
+
+static void
+object_update(void *priv, void *p, unsigned u)
+{
+	struct objcore *oc;
+
+	(void)priv;
+	CAST_OBJ_NOTNULL(oc, p, OBJCORE_MAGIC);
+	oc->timer_idx = u;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+EXP_Init(void)
+{
+
+	Lck_New(&exp_mtx, lck_exp);
+	exp_heap = binheap_new(NULL, object_cmp, object_update);
+	XXXAN(exp_heap);
+	WRK_BgThread(&exp_thread, "cache-timeout", exp_timer, NULL);
+}
diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c
new file mode 100644
index 0000000..a678dcc
--- /dev/null
+++ b/bin/varnishd/cache/cache_fetch.c
@@ -0,0 +1,645 @@
+/*-
+ * 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.
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "vcli_priv.h"
+#include "vct.h"
+#include "vtcp.h"
+
+static unsigned fetchfrag;
+
+/*--------------------------------------------------------------------
+ * We want to issue the first error we encounter on fetching and
+ * supress the rest.  This function does that.
+ *
+ * Other code is allowed to look at w->fetch_failed to bail out
+ *
+ * For convenience, always return -1
+ */
+
+int
+FetchError2(struct worker *w, const char *error, const char *more)
+{
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	if (!w->fetch_failed) {
+		if (more == NULL)
+			WSLB(w, SLT_FetchError, "%s", error);
+		else
+			WSLB(w, SLT_FetchError, "%s: %s", error, more);
+	}
+	w->fetch_failed = 1;
+	return (-1);
+}
+
+int
+FetchError(struct worker *w, const char *error)
+{
+	return(FetchError2(w, error, NULL));
+}
+
+/*--------------------------------------------------------------------
+ * VFP_NOP
+ *
+ * This fetch-processor does nothing but store the object.
+ * It also documents the API
+ */
+
+/*--------------------------------------------------------------------
+ * VFP_BEGIN
+ *
+ * Called to set up stuff.
+ *
+ * 'estimate' is the estimate of the number of bytes we expect to receive,
+ * as seen on the socket, or zero if unknown.
+ */
+static void __match_proto__()
+vfp_nop_begin(struct worker *w, size_t estimate)
+{
+
+	if (estimate > 0)
+		(void)FetchStorage(w, estimate);
+}
+
+/*--------------------------------------------------------------------
+ * VFP_BYTES
+ *
+ * Process (up to) 'bytes' from the socket.
+ *
+ * Return -1 on error, issue FetchError()
+ *	will not be called again, once error happens.
+ * Return 0 on EOF on socket even if bytes not reached.
+ * Return 1 when 'bytes' have been processed.
+ */
+
+static int __match_proto__()
+vfp_nop_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	ssize_t l, wl;
+	struct storage *st;
+
+	AZ(w->fetch_failed);
+	while (bytes > 0) {
+		st = FetchStorage(w, 0);
+		if (st == NULL)
+			return(-1);
+		l = st->space - st->len;
+		if (l > bytes)
+			l = bytes;
+		wl = HTC_Read(w, htc, st->ptr + st->len, l);
+		if (wl <= 0)
+			return (wl);
+		st->len += wl;
+		w->fetch_obj->len += wl;
+		bytes -= wl;
+		if (w->do_stream)
+			RES_StreamPoll(w);
+	}
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * VFP_END
+ *
+ * Finish & cleanup
+ *
+ * Return -1 for error
+ * Return 0 for OK
+ */
+
+static int __match_proto__()
+vfp_nop_end(struct worker *w)
+{
+	struct storage *st;
+
+	st = VTAILQ_LAST(&w->fetch_obj->store, storagehead);
+	if (st == NULL)
+		return (0);
+
+	if (st->len == 0) {
+		VTAILQ_REMOVE(&w->fetch_obj->store, st, list);
+		STV_free(st);
+		return (0);
+	}
+	if (st->len < st->space)
+		STV_trim(st, st->len);
+	return (0);
+}
+
+static struct vfp vfp_nop = {
+	.begin	=	vfp_nop_begin,
+	.bytes	=	vfp_nop_bytes,
+	.end	=	vfp_nop_end,
+};
+
+/*--------------------------------------------------------------------
+ * Fetch Storage to put object into.
+ *
+ */
+
+struct storage *
+FetchStorage(struct worker *w, ssize_t sz)
+{
+	ssize_t l;
+	struct storage *st;
+	struct object *obj;
+
+	obj = w->fetch_obj;
+	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
+	st = VTAILQ_LAST(&obj->store, storagehead);
+	if (st != NULL && st->len < st->space)
+		return (st);
+
+	l = fetchfrag;
+	if (l == 0)
+		l = sz;
+	if (l == 0)
+		l = cache_param->fetch_chunksize * 1024LL;
+	st = STV_alloc(w, l);
+	if (st == NULL) {
+		(void)FetchError(w, "Could not get storage");
+		return (NULL);
+	}
+	AZ(st->len);
+	VTAILQ_INSERT_TAIL(&obj->store, st, list);
+	return (st);
+}
+
+/*--------------------------------------------------------------------
+ * Convert a string to a size_t safely
+ */
+
+static ssize_t
+fetch_number(const char *nbr, int radix)
+{
+	uintmax_t cll;
+	ssize_t cl;
+	char *q;
+
+	if (*nbr == '\0')
+		return (-1);
+	cll = strtoumax(nbr, &q, radix);
+	if (q == NULL || *q != '\0')
+		return (-1);
+
+	cl = (ssize_t)cll;
+	if((uintmax_t)cl != cll) /* Protect against bogusly large values */
+		return (-1);
+	return (cl);
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+fetch_straight(struct worker *w, struct http_conn *htc, ssize_t cl)
+{
+	int i;
+
+	assert(w->body_status == BS_LENGTH);
+
+	if (cl < 0) {
+		return (FetchError(w, "straight length field bogus"));
+	} else if (cl == 0)
+		return (0);
+
+	i = w->vfp->bytes(w, htc, cl);
+	if (i <= 0)
+		return (FetchError(w, "straight insufficient bytes"));
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Read a chunked HTTP object.
+ *
+ * XXX: Reading one byte at a time is pretty pessimal.
+ */
+
+static int
+fetch_chunked(struct worker *w, struct http_conn *htc)
+{
+	int i;
+	char buf[20];		/* XXX: 20 is arbitrary */
+	unsigned u;
+	ssize_t cl;
+
+	assert(w->body_status == BS_CHUNKED);
+	do {
+		/* Skip leading whitespace */
+		do {
+			if (HTC_Read(w, htc, buf, 1) <= 0)
+				return (-1);
+		} while (vct_islws(buf[0]));
+
+		if (!vct_ishex(buf[0]))
+			return (FetchError(w,"chunked header non-hex"));
+
+		/* Collect hex digits, skipping leading zeros */
+		for (u = 1; u < sizeof buf; u++) {
+			do {
+				if (HTC_Read(w, htc, buf + u, 1) <= 0)
+					return (-1);
+			} while (u == 1 && buf[0] == '0' && buf[u] == '0');
+			if (!vct_ishex(buf[u]))
+				break;
+		}
+
+		if (u >= sizeof buf)
+			return (FetchError(w,"chunked header too long"));
+
+		/* Skip trailing white space */
+		while(vct_islws(buf[u]) && buf[u] != '\n')
+			if (HTC_Read(w, htc, buf + u, 1) <= 0)
+				return (-1);
+
+		if (buf[u] != '\n')
+			return (FetchError(w,"chunked header no NL"));
+
+		buf[u] = '\0';
+		cl = fetch_number(buf, 16);
+		if (cl < 0)
+			return (FetchError(w,"chunked header number syntax"));
+
+		if (cl > 0 && w->vfp->bytes(w, htc, cl) <= 0)
+			return (-1);
+
+		i = HTC_Read(w, htc, buf, 1);
+		if (i <= 0)
+			return (-1);
+		if (buf[0] == '\r' && HTC_Read(w, htc, buf, 1) <= 0)
+			return (-1);
+		if (buf[0] != '\n')
+			return (FetchError(w,"chunked tail no NL"));
+	} while (cl > 0);
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+fetch_eof(struct worker *w, struct http_conn *htc)
+{
+	int i;
+
+	assert(w->body_status == BS_EOF);
+	i = w->vfp->bytes(w, htc, SSIZE_MAX);
+	if (i < 0)
+		return (-1);
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Fetch any body attached to the incoming request, and either write it
+ * to the backend (if we pass) or discard it (anything else).
+ * This is mainly a separate function to isolate the stack buffer and
+ * to contain the complexity when we start handling chunked encoding.
+ */
+
+int
+FetchReqBody(struct sess *sp)
+{
+	unsigned long content_length;
+	char buf[8192];
+	char *ptr, *endp;
+	int rdcnt;
+
+	if (http_GetHdr(sp->http, H_Content_Length, &ptr)) {
+
+		content_length = strtoul(ptr, &endp, 10);
+		/* XXX should check result of conversion */
+		while (content_length) {
+			if (content_length > sizeof buf)
+				rdcnt = sizeof buf;
+			else
+				rdcnt = content_length;
+			rdcnt = HTC_Read(sp->wrk, sp->htc, buf, rdcnt);
+			if (rdcnt <= 0)
+				return (1);
+			content_length -= rdcnt;
+			if (!sp->sendbody)
+				continue;
+			(void)WRW_Write(sp->wrk, buf, rdcnt); /* XXX: stats ? */
+			if (WRW_Flush(sp->wrk))
+				return (2);
+		}
+	}
+	if (http_GetHdr(sp->http, H_Transfer_Encoding, NULL)) {
+		/* XXX: Handle chunked encoding. */
+		WSP(sp, SLT_Debug, "Transfer-Encoding in request");
+		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
+FetchHdr(struct sess *sp)
+{
+	struct vbc *vc;
+	struct worker *w;
+	char *b;
+	struct http *hp;
+	int retry = -1;
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+	w = sp->wrk;
+
+	AN(sp->director);
+	AZ(sp->obj);
+
+	if (sp->objcore != NULL) {		/* pass has no objcore */
+		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
+		AN(sp->objcore->flags & OC_F_BUSY);
+	}
+
+	hp = w->bereq;
+
+	sp->wrk->vbc = VDI_GetFd(NULL, sp);
+	if (sp->wrk->vbc == NULL) {
+		WSP(sp, SLT_FetchError, "no backend connection");
+		return (-1);
+	}
+	vc = sp->wrk->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(hp, H_Host, &b))
+		VDI_AddHostHeader(sp);
+
+	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
+	WRW_Reserve(w, &vc->fd);
+	(void)http_Write(w, vc->vsl_id, hp, 0);	/* XXX: stats ? */
+
+	/* Deal with any message-body the request might have */
+	i = FetchReqBody(sp);
+	if (WRW_FlushRelease(w) || i > 0) {
+		WSP(sp, SLT_FetchError, "backend write error: %d (%s)",
+		    errno, strerror(errno));
+		VDI_CloseFd(sp->wrk);
+		/* XXX: other cleanup ? */
+		return (retry);
+	}
+
+	/* Checkpoint the vsl.here */
+	WSL_Flush(w, 0);
+
+	/* XXX is this the right place? */
+	VSC_C_main->backend_req++;
+
+	/* Receive response */
+
+	HTC_Init(w->htc, w->ws, vc->fd, vc->vsl_id, cache_param->http_resp_size,
+	    cache_param->http_resp_hdr_len);
+
+	VTCP_set_read_timeout(vc->fd, vc->first_byte_timeout);
+
+	i = HTC_Rx(w->htc);
+
+	if (i < 0) {
+		WSP(sp, SLT_FetchError, "http first read error: %d %d (%s)",
+		    i, errno, strerror(errno));
+		VDI_CloseFd(sp->wrk);
+		/* XXX: other cleanup ? */
+		/* Retryable if we never received anything */
+		return (i == -1 ? retry : -1);
+	}
+
+	VTCP_set_read_timeout(vc->fd, vc->between_bytes_timeout);
+
+	while (i == 0) {
+		i = HTC_Rx(w->htc);
+		if (i < 0) {
+			WSP(sp, SLT_FetchError,
+			    "http first read error: %d %d (%s)",
+			    i, errno, strerror(errno));
+			VDI_CloseFd(sp->wrk);
+			/* XXX: other cleanup ? */
+			return (-1);
+		}
+	}
+
+	hp = w->beresp;
+
+	if (http_DissectResponse(w, w->htc, hp)) {
+		WSP(sp, SLT_FetchError, "http format error");
+		VDI_CloseFd(sp->wrk);
+		/* XXX: other cleanup ? */
+		return (-1);
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+FetchBody(struct worker *w, struct object *obj)
+{
+	int cls;
+	struct storage *st;
+	int mklen;
+	ssize_t cl;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AZ(w->fetch_obj);
+	CHECK_OBJ_NOTNULL(w->vbc, VBC_MAGIC);
+	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
+	CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC);
+
+	if (w->vfp == NULL)
+		w->vfp = &vfp_nop;
+
+	AssertObjCorePassOrBusy(obj->objcore);
+
+	AZ(w->vgz_rx);
+	AZ(VTAILQ_FIRST(&obj->store));
+
+	w->fetch_obj = obj;
+	w->fetch_failed = 0;
+
+	/* XXX: pick up estimate from objdr ? */
+	cl = 0;
+	switch (w->body_status) {
+	case BS_NONE:
+		cls = 0;
+		mklen = 0;
+		break;
+	case BS_ZERO:
+		cls = 0;
+		mklen = 1;
+		break;
+	case BS_LENGTH:
+		cl = fetch_number( w->h_content_length, 10);
+		w->vfp->begin(w, cl > 0 ? cl : 0);
+		cls = fetch_straight(w, w->htc, cl);
+		mklen = 1;
+		if (w->vfp->end(w))
+			cls = -1;
+		break;
+	case BS_CHUNKED:
+		w->vfp->begin(w, cl);
+		cls = fetch_chunked(w, w->htc);
+		mklen = 1;
+		if (w->vfp->end(w))
+			cls = -1;
+		break;
+	case BS_EOF:
+		w->vfp->begin(w, cl);
+		cls = fetch_eof(w, w->htc);
+		mklen = 1;
+		if (w->vfp->end(w))
+			cls = -1;
+		break;
+	case BS_ERROR:
+		cls = 1;
+		mklen = 0;
+		break;
+	default:
+		cls = 0;
+		mklen = 0;
+		INCOMPL();
+	}
+	AZ(w->vgz_rx);
+
+	/*
+	 * It is OK for ->end to just leave the last storage segment
+	 * sitting on w->storage, we will always call vfp_nop_end()
+	 * to get it trimmed or thrown out if empty.
+	 */
+	AZ(vfp_nop_end(w));
+
+	w->fetch_obj = NULL;
+
+	WSLB(w, SLT_Fetch_Body, "%u(%s) cls %d mklen %u",
+	    w->body_status, body_status(w->body_status),
+	    cls, mklen);
+
+	if (w->body_status == BS_ERROR) {
+		VDI_CloseFd(w);
+		return (__LINE__);
+	}
+
+	if (cls < 0) {
+		w->stats.fetch_failed++;
+		/* XXX: Wouldn't this store automatically be released ? */
+		while (!VTAILQ_EMPTY(&obj->store)) {
+			st = VTAILQ_FIRST(&obj->store);
+			VTAILQ_REMOVE(&obj->store, st, list);
+			STV_free(st);
+		}
+		VDI_CloseFd(w);
+		obj->len = 0;
+		return (__LINE__);
+	}
+	AZ(w->fetch_failed);
+
+	if (cls == 0 && w->do_close)
+		cls = 1;
+
+	WSLB(w, SLT_Length, "%u", obj->len);
+
+	{
+	/* Sanity check fetch methods accounting */
+		ssize_t uu;
+
+		uu = 0;
+		VTAILQ_FOREACH(st, &obj->store, list)
+			uu += st->len;
+		if (w->do_stream)
+			/* Streaming might have started freeing stuff */
+			assert (uu <= obj->len);
+
+		else
+			assert(uu == obj->len);
+	}
+
+	if (mklen > 0) {
+		http_Unset(obj->http, H_Content_Length);
+		http_PrintfHeader(w, w->vbc->vsl_id, obj->http,
+		    "Content-Length: %jd", (intmax_t)obj->len);
+	}
+
+	if (cls)
+		VDI_CloseFd(w);
+	else
+		VDI_RecycleFd(w);
+
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Debugging aids
+ */
+
+static void
+debug_fragfetch(struct cli *cli, const char * const *av, void *priv)
+{
+	(void)priv;
+	(void)cli;
+	fetchfrag = strtoul(av[2], NULL, 0);
+}
+
+static struct cli_proto debug_cmds[] = {
+	{ "debug.fragfetch", "debug.fragfetch",
+		"\tEnable fetch fragmentation\n", 1, 1, "d", debug_fragfetch },
+	{ NULL }
+};
+
+/*--------------------------------------------------------------------
+ *
+ */
+
+void
+Fetch_Init(void)
+{
+
+	CLI_AddFuncs(debug_cmds);
+}
diff --git a/bin/varnishd/cache/cache_gzip.c b/bin/varnishd/cache/cache_gzip.c
new file mode 100644
index 0000000..32a7413
--- /dev/null
+++ b/bin/varnishd/cache/cache_gzip.c
@@ -0,0 +1,697 @@
+/*-
+ * Copyright (c) 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.
+ *
+ * Interaction with the linvgz (zlib) library.
+ *
+ * The zlib library pollutes namespace a LOT when you include the "vgz.h"
+ * (aka (zlib.h") file so we contain the damage by vectoring all access
+ * to libz through this source file.
+ *
+ * The API defined by this file, will also insulate the rest of the code,
+ * should we find a better gzip library at a later date.
+ *
+ * The absolutely worst case gzip processing path, once we have pipe-lining,
+ * will be the following, so we need to be a bit careful with the scratch
+ * space we use:
+ *
+ *	Backend		Tmp	Input	Output
+ *         |		----------------------
+ *	   v
+ *	 gunzip		wrk	stack	?
+ *         |
+ *	   v
+ *	  esi
+ *         |
+ *	   v
+ *	  gzip		wrk	?	storage
+ *         |
+ *	   v
+ *	  cache
+ *         |
+ *	   v
+ *	 gunzip		wrk	storage	stack
+ *         |
+ *	   v
+ *	 client
+ *
+ * XXXX: The two '?' are obviously the same memory, but I have yet to decide
+ * where it goes.   As usual we try to avoid the session->ws if we can but
+ * I may have to use that.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "vgz.h"
+
+struct vgz {
+	unsigned		magic;
+#define VGZ_MAGIC		0x162df0cb
+	enum {VGZ_GZ,VGZ_UN}	dir;
+	struct worker		*wrk;
+	const char		*id;
+	struct ws		*tmp;
+	char			*tmp_snapshot;
+	int			last_i;
+
+	struct storage		*obuf;
+
+	z_stream		vz;
+};
+
+/*--------------------------------------------------------------------*/
+
+static voidpf
+vgz_alloc(voidpf opaque, uInt items, uInt size)
+{
+	struct vgz *vg;
+
+	CAST_OBJ_NOTNULL(vg, opaque, VGZ_MAGIC);
+
+	return (WS_Alloc(vg->tmp, items * size));
+}
+
+static void
+vgz_free(voidpf opaque, voidpf address)
+{
+	struct vgz *vg;
+
+	CAST_OBJ_NOTNULL(vg, opaque, VGZ_MAGIC);
+	(void)address;
+}
+
+/*--------------------------------------------------------------------
+ * Set up a gunzip instance
+ */
+
+static struct vgz *
+vgz_alloc_vgz(struct worker *wrk, const char *id)
+{
+	struct vgz *vg;
+	struct ws *ws;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	ws = wrk->ws;
+	WS_Assert(ws);
+	// XXX: we restore workspace in esi:include
+	// vg = (void*)WS_Alloc(ws, sizeof *vg);
+	ALLOC_OBJ(vg, VGZ_MAGIC);
+	AN(vg);
+	memset(vg, 0, sizeof *vg);
+	vg->magic = VGZ_MAGIC;
+	vg->wrk = wrk;
+	vg->id = id;
+
+	switch (cache_param->gzip_tmp_space) {
+	case 0:
+	case 1:
+		/* malloc, the default */
+		break;
+	case 2:
+		vg->tmp = wrk->ws;
+		vg->tmp_snapshot = WS_Snapshot(vg->tmp);
+		vg->vz.zalloc = vgz_alloc;
+		vg->vz.zfree = vgz_free;
+		vg->vz.opaque = vg;
+		break;
+	default:
+		assert(0 == __LINE__);
+	}
+	return (vg);
+}
+
+struct vgz *
+VGZ_NewUngzip(struct worker *wrk, const char *id)
+{
+	struct vgz *vg;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	vg = vgz_alloc_vgz(wrk, id);
+	vg->dir = VGZ_UN;
+	VSC_C_main->n_gunzip++;
+
+	/*
+	 * Max memory usage according to zonf.h:
+	 *	mem_needed = "a few kb" + (1 << (windowBits))
+	 * Since we don't control windowBits, we have to assume
+	 * it is 15, so 34-35KB or so.
+	 */
+	assert(Z_OK == inflateInit2(&vg->vz, 31));
+	return (vg);
+}
+
+struct vgz *
+VGZ_NewGzip(struct worker *wrk, const char *id)
+{
+	struct vgz *vg;
+	int i;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	vg = vgz_alloc_vgz(wrk, id);
+	vg->dir = VGZ_GZ;
+	VSC_C_main->n_gzip++;
+
+	/*
+	 * From zconf.h:
+	 *
+	 *	mem_needed = "a few kb"
+	 *		+ (1 << (windowBits+2))
+	 *		+  (1 << (memLevel+9))
+	 *
+	 * windowBits [8..15] (-> 1K..128K)
+	 * memLevel [1..9] (-> 1K->256K)
+	 *
+	 * XXX: They probably needs to be params...
+	 *
+	 * XXX: It may be more efficent to malloc them, rather than have
+	 * XXX: too many worker threads grow the stacks.
+	 */
+	i = deflateInit2(&vg->vz,
+	    cache_param->gzip_level,		/* Level */
+	    Z_DEFLATED,			/* Method */
+	    16 + cache_param->gzip_window,	/* Window bits (16=gzip + 15) */
+	    cache_param->gzip_memlevel,	/* memLevel */
+	    Z_DEFAULT_STRATEGY);
+	assert(Z_OK == i);
+	return (vg);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
+{
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+
+	AZ(vg->vz.avail_in);
+	vg->vz.next_in = TRUST_ME(ptr);
+	vg->vz.avail_in = len;
+}
+
+int
+VGZ_IbufEmpty(const struct vgz *vg)
+{
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	return (vg->vz.avail_in == 0);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
+{
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+
+	vg->vz.next_out = TRUST_ME(ptr);
+	vg->vz.avail_out = len;
+}
+
+int
+VGZ_ObufFull(const struct vgz *vg)
+{
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	return (vg->vz.avail_out == 0);
+}
+
+/*--------------------------------------------------------------------
+ * Keep the outbuffer supplied with storage and file it under the
+ * sp->obj as it fills.
+ */
+
+int
+VGZ_ObufStorage(struct worker *w, struct vgz *vg)
+{
+	struct storage *st;
+
+	st = FetchStorage(w, 0);
+	if (st == NULL)
+		return (-1);
+
+	vg->obuf = st;
+	VGZ_Obuf(vg, st->ptr + st->len, st->space - st->len);
+
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+VGZ_Gunzip(struct vgz *vg, const void **pptr, size_t *plen)
+{
+	int i;
+	ssize_t l;
+	const uint8_t *before;
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+
+	*pptr = NULL;
+	*plen = 0;
+	AN(vg->vz.next_out);
+	AN(vg->vz.avail_out);
+	before = vg->vz.next_out;
+	i = inflate(&vg->vz, 0);
+	if (i == Z_OK || i == Z_STREAM_END) {
+		*pptr = before;
+		l = (const uint8_t *)vg->vz.next_out - before;
+		*plen = l;
+		if (vg->obuf != NULL)
+			vg->obuf->len += l;
+	}
+	vg->last_i = i;
+	if (i == Z_OK)
+		return (VGZ_OK);
+	if (i == Z_STREAM_END)
+		return (VGZ_END);
+	if (i == Z_BUF_ERROR)
+		return (VGZ_STUCK);
+	VSL(SLT_Debug, 0, "Unknown INFLATE=%d (%s)\n", i, vg->vz.msg);
+	return (VGZ_ERROR);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+VGZ_Gzip(struct vgz *vg, const void **pptr, size_t *plen, enum vgz_flag flags)
+{
+	int i;
+	int zflg;
+	ssize_t l;
+	const uint8_t *before;
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+
+	*pptr = NULL;
+	*plen = 0;
+	AN(vg->vz.next_out);
+	AN(vg->vz.avail_out);
+	before = vg->vz.next_out;
+	switch(flags) {
+	case VGZ_NORMAL:	zflg = Z_NO_FLUSH; break;
+	case VGZ_ALIGN:		zflg = Z_SYNC_FLUSH; break;
+	case VGZ_RESET:		zflg = Z_FULL_FLUSH; break;
+	case VGZ_FINISH:	zflg = Z_FINISH; break;
+	default:		INCOMPL();
+	}
+	i = deflate(&vg->vz, zflg);
+	if (i == Z_OK || i == Z_STREAM_END) {
+		*pptr = before;
+		l = (const uint8_t *)vg->vz.next_out - before;
+		*plen = l;
+		if (vg->obuf != NULL)
+			vg->obuf->len += l;
+	}
+	vg->last_i = i;
+	if (i == Z_OK)
+		return (0);
+	if (i == Z_STREAM_END)
+		return (1);
+	if (i == Z_BUF_ERROR)
+		return (2);
+	return (-1);
+}
+
+/*--------------------------------------------------------------------
+ * Gunzip ibuf into outb, if it runs full, emit it with WRW.
+ * Leave flushing to caller, more data may be coming.
+ */
+
+int
+VGZ_WrwGunzip(struct worker *w, struct vgz *vg, const void *ibuf,
+    ssize_t ibufl, char *obuf, ssize_t obufl, ssize_t *obufp)
+{
+	int i;
+	size_t dl;
+	const void *dp;
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	assert(obufl > 16);
+	VGZ_Ibuf(vg, ibuf, ibufl);
+	if (ibufl == 0)
+		return (VGZ_OK);
+	VGZ_Obuf(vg, obuf + *obufp, obufl - *obufp);
+	do {
+		if (obufl == *obufp)
+			i = VGZ_STUCK;
+		else {
+			i = VGZ_Gunzip(vg, &dp, &dl);
+			*obufp += dl;
+		}
+		if (i < VGZ_OK) {
+			/* XXX: VSL ? */
+			return (-1);
+		}
+		if (obufl == *obufp || i == VGZ_STUCK) {
+			w->acct_tmp.bodybytes += *obufp;
+			(void)WRW_Write(w, obuf, *obufp);
+			(void)WRW_Flush(w);
+			*obufp = 0;
+			VGZ_Obuf(vg, obuf + *obufp, obufl - *obufp);
+		}
+	} while (!VGZ_IbufEmpty(vg));
+	if (i == VGZ_STUCK)
+		i = VGZ_OK;
+	return (i);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VGZ_UpdateObj(const struct vgz *vg, struct object *obj)
+{
+
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
+	obj->gzip_start	= vg->vz.start_bit;
+	obj->gzip_last	= vg->vz.last_bit;
+	obj->gzip_stop	= vg->vz.stop_bit;
+}
+
+/*--------------------------------------------------------------------
+ * Passing a vsl_id of -1 means "use w->vbc->vsl_id"
+ */
+
+int
+VGZ_Destroy(struct vgz **vgp, int vsl_id)
+{
+	struct vgz *vg;
+	int i;
+
+	vg = *vgp;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	*vgp = NULL;
+
+	if (vsl_id < 0)
+		WSLB(vg->wrk, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
+		    vg->id,
+		    (intmax_t)vg->vz.total_in,
+		    (intmax_t)vg->vz.total_out,
+		    (intmax_t)vg->vz.start_bit,
+		    (intmax_t)vg->vz.last_bit,
+		    (intmax_t)vg->vz.stop_bit);
+	else
+		WSL(vg->wrk, SLT_Gzip, vsl_id, "%s %jd %jd %jd %jd %jd",
+		    vg->id,
+		    (intmax_t)vg->vz.total_in,
+		    (intmax_t)vg->vz.total_out,
+		    (intmax_t)vg->vz.start_bit,
+		    (intmax_t)vg->vz.last_bit,
+		    (intmax_t)vg->vz.stop_bit);
+	if (vg->tmp != NULL)
+		WS_Reset(vg->tmp, vg->tmp_snapshot);
+	if (vg->dir == VGZ_GZ)
+		i = deflateEnd(&vg->vz);
+	else
+		i = inflateEnd(&vg->vz);
+	if (vg->last_i == Z_STREAM_END && i == Z_OK)
+		i = Z_STREAM_END;
+	FREE_OBJ(vg);
+	if (i == Z_OK)
+		return (VGZ_OK);
+	if (i == Z_STREAM_END)
+		return (VGZ_END);
+	if (i == Z_BUF_ERROR)
+		return (VGZ_STUCK);
+	return (VGZ_ERROR);
+}
+
+/*--------------------------------------------------------------------
+ * VFP_GUNZIP
+ *
+ * A VFP for gunzip'ing an object as we receive it from the backend
+ */
+
+static void __match_proto__()
+vfp_gunzip_begin(struct worker *w, size_t estimate)
+{
+	(void)estimate;
+	AZ(w->vgz_rx);
+	w->vgz_rx = VGZ_NewUngzip(w, "U F -");
+}
+
+static int __match_proto__()
+vfp_gunzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	struct vgz *vg;
+	ssize_t l, wl;
+	int i = -100;
+	uint8_t	ibuf[cache_param->gzip_stack_buffer];
+	size_t dl;
+	const void *dp;
+
+	AZ(w->fetch_failed);
+	vg = w->vgz_rx;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	AZ(vg->vz.avail_in);
+	while (bytes > 0 || vg->vz.avail_in > 0) {
+		if (vg->vz.avail_in == 0 && bytes > 0) {
+			l = sizeof ibuf;
+			if (l > bytes)
+				l = bytes;
+			wl = HTC_Read(w, htc, ibuf, l);
+			if (wl <= 0)
+				return (wl);
+			VGZ_Ibuf(vg, ibuf, wl);
+			bytes -= wl;
+		}
+
+		if (VGZ_ObufStorage(w, vg))
+			return(-1);
+		i = VGZ_Gunzip(vg, &dp, &dl);
+		if (i != VGZ_OK && i != VGZ_END)
+			return(FetchError(w, "Gunzip data error"));
+		w->fetch_obj->len += dl;
+		if (w->do_stream)
+			RES_StreamPoll(w);
+	}
+	assert(i == Z_OK || i == Z_STREAM_END);
+	return (1);
+}
+
+static int __match_proto__()
+vfp_gunzip_end(struct worker *w)
+{
+	struct vgz *vg;
+
+	vg = w->vgz_rx;
+	w->vgz_rx = NULL;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	if (w->fetch_failed) {
+		(void)VGZ_Destroy(&vg, -1);
+		return(0);
+	}
+	if (VGZ_Destroy(&vg, -1) != VGZ_END)
+		return(FetchError(w, "Gunzip error at the very end"));
+	return (0);
+}
+
+struct vfp vfp_gunzip = {
+        .begin  =       vfp_gunzip_begin,
+        .bytes  =       vfp_gunzip_bytes,
+        .end    =       vfp_gunzip_end,
+};
+
+
+/*--------------------------------------------------------------------
+ * VFP_GZIP
+ *
+ * A VFP for gzip'ing an object as we receive it from the backend
+ */
+
+static void __match_proto__()
+vfp_gzip_begin(struct worker *w, size_t estimate)
+{
+	(void)estimate;
+
+	AZ(w->vgz_rx);
+	w->vgz_rx = VGZ_NewGzip(w, "G F -");
+}
+
+static int __match_proto__()
+vfp_gzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	struct vgz *vg;
+	ssize_t l, wl;
+	int i = -100;
+	uint8_t ibuf[cache_param->gzip_stack_buffer];
+	size_t dl;
+	const void *dp;
+
+	AZ(w->fetch_failed);
+	vg = w->vgz_rx;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	AZ(vg->vz.avail_in);
+	while (bytes > 0 || !VGZ_IbufEmpty(vg)) {
+		if (VGZ_IbufEmpty(vg) && bytes > 0) {
+			l = sizeof ibuf;
+			if (l > bytes)
+				l = bytes;
+			wl = HTC_Read(w, htc, ibuf, l);
+			if (wl <= 0)
+				return (wl);
+			VGZ_Ibuf(vg, ibuf, wl);
+			bytes -= wl;
+		}
+		if (VGZ_ObufStorage(w, vg))
+			return(-1);
+		i = VGZ_Gzip(vg, &dp, &dl, VGZ_NORMAL);
+		assert(i == Z_OK);
+		w->fetch_obj->len += dl;
+		if (w->do_stream)
+			RES_StreamPoll(w);
+	}
+	return (1);
+}
+
+static int __match_proto__()
+vfp_gzip_end(struct worker *w)
+{
+	struct vgz *vg;
+	size_t dl;
+	const void *dp;
+	int i;
+
+	vg = w->vgz_rx;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	w->vgz_rx = NULL;
+	if (w->fetch_failed) {
+		(void)VGZ_Destroy(&vg, -1);
+		return(0);
+	}
+	do {
+		VGZ_Ibuf(vg, "", 0);
+		if (VGZ_ObufStorage(w, vg))
+			return(-1);
+		i = VGZ_Gzip(vg, &dp, &dl, VGZ_FINISH);
+		w->fetch_obj->len += dl;
+	} while (i != Z_STREAM_END);
+	if (w->do_stream)
+		RES_StreamPoll(w);
+	VGZ_UpdateObj(vg, w->fetch_obj);
+	if (VGZ_Destroy(&vg, -1) != VGZ_END)
+		return(FetchError(w, "Gzip error at the very end"));
+	return (0);
+}
+
+struct vfp vfp_gzip = {
+        .begin  =       vfp_gzip_begin,
+        .bytes  =       vfp_gzip_bytes,
+        .end    =       vfp_gzip_end,
+};
+
+/*--------------------------------------------------------------------
+ * VFP_TESTGZIP
+ *
+ * A VFP for testing that received gzip data is valid, and for
+ * collecting the magic bits while we're at it.
+ */
+
+static void __match_proto__()
+vfp_testgzip_begin(struct worker *w, size_t estimate)
+{
+	(void)estimate;
+	w->vgz_rx = VGZ_NewUngzip(w, "u F -");
+	CHECK_OBJ_NOTNULL(w->vgz_rx, VGZ_MAGIC);
+}
+
+static int __match_proto__()
+vfp_testgzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
+{
+	struct vgz *vg;
+	ssize_t l, wl;
+	int i = -100;
+	uint8_t	obuf[cache_param->gzip_stack_buffer];
+	size_t dl;
+	const void *dp;
+	struct storage *st;
+
+	AZ(w->fetch_failed);
+	vg = w->vgz_rx;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	AZ(vg->vz.avail_in);
+	while (bytes > 0) {
+		st = FetchStorage(w, 0);
+		if (st == NULL)
+			return(-1);
+		l = st->space - st->len;
+		if (l > bytes)
+			l = bytes;
+		wl = HTC_Read(w, htc, st->ptr + st->len, l);
+		if (wl <= 0)
+			return (wl);
+		bytes -= wl;
+		VGZ_Ibuf(vg, st->ptr + st->len, wl);
+		st->len += wl;
+		w->fetch_obj->len += wl;
+		if (w->do_stream)
+			RES_StreamPoll(w);
+
+		while (!VGZ_IbufEmpty(vg)) {
+			VGZ_Obuf(vg, obuf, sizeof obuf);
+			i = VGZ_Gunzip(vg, &dp, &dl);
+			if (i == VGZ_END && !VGZ_IbufEmpty(vg))
+				return(FetchError(w, "Junk after gzip data"));
+			if (i != VGZ_OK && i != VGZ_END)
+				return(FetchError2(w,
+				    "Invalid Gzip data", vg->vz.msg));
+		}
+	}
+	assert(i == VGZ_OK || i == VGZ_END);
+	return (1);
+}
+
+static int __match_proto__()
+vfp_testgzip_end(struct worker *w)
+{
+	struct vgz *vg;
+
+	vg = w->vgz_rx;
+	w->vgz_rx = NULL;
+	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
+	if (w->fetch_failed) {
+		(void)VGZ_Destroy(&vg, -1);
+		return(0);
+	}
+	VGZ_UpdateObj(vg, w->fetch_obj);
+	if (VGZ_Destroy(&vg, -1) != VGZ_END)
+		return(FetchError(w, "TestGunzip error at the very end"));
+	return (0);
+}
+
+struct vfp vfp_testgzip = {
+        .begin  =       vfp_testgzip_begin,
+        .bytes  =       vfp_testgzip_bytes,
+        .end    =       vfp_testgzip_end,
+};
diff --git a/bin/varnishd/cache/cache_hash.c b/bin/varnishd/cache/cache_hash.c
new file mode 100644
index 0000000..db865de
--- /dev/null
+++ b/bin/varnishd/cache/cache_hash.c
@@ -0,0 +1,752 @@
+/*-
+ * 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.
+ *
+ * This is the central hash-table code, it relies on a chosen hash
+ * implementation only for the actual hashing, all the housekeeping
+ * happens here.
+ *
+ * We have two kinds of structures, objecthead and object.  An objecthead
+ * corresponds to a given (Host:, URL) tupple, and the objects hung from
+ * the objecthead may represent various variations (ie: Vary: header,
+ * different TTL etc) instances of that web-entity.
+ *
+ * Each objecthead has a mutex which locks both its own fields, the
+ * list of objects and fields in the objects.
+ *
+ * The hash implementation must supply a reference count facility on
+ * the objecthead, and return with a reference held after a lookup.
+ *
+ * Lookups in the hash implementation returns with a ref held and each
+ * object hung from the objhead holds a ref as well.
+ *
+ * Objects have refcounts which are locked by the objecthead mutex.
+ *
+ * New objects are always marked busy, and they can go from busy to
+ * not busy only once.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "hash/hash_slinger.h"
+#include "vsha256.h"
+
+static const struct hash_slinger *hash;
+
+/*---------------------------------------------------------------------*/
+/* Precreate an objhead and object for later use */
+void
+HSH_Prealloc(const struct sess *sp)
+{
+	struct worker *w;
+	struct objhead *oh;
+	struct objcore *oc;
+	struct waitinglist *wl;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+	w = sp->wrk;
+
+	if (w->nobjcore == NULL) {
+		ALLOC_OBJ(oc, OBJCORE_MAGIC);
+		XXXAN(oc);
+		w->nobjcore = oc;
+		w->stats.n_objectcore++;
+		oc->flags |= OC_F_BUSY;
+	}
+	CHECK_OBJ_NOTNULL(w->nobjcore, OBJCORE_MAGIC);
+
+	if (w->nobjhead == NULL) {
+		ALLOC_OBJ(oh, OBJHEAD_MAGIC);
+		XXXAN(oh);
+		oh->refcnt = 1;
+		VTAILQ_INIT(&oh->objcs);
+		Lck_New(&oh->mtx, lck_objhdr);
+		w->nobjhead = oh;
+		w->stats.n_objecthead++;
+	}
+	CHECK_OBJ_NOTNULL(w->nobjhead, OBJHEAD_MAGIC);
+
+	if (w->nwaitinglist == NULL) {
+		ALLOC_OBJ(wl, WAITINGLIST_MAGIC);
+		XXXAN(wl);
+		VTAILQ_INIT(&wl->list);
+		w->nwaitinglist = wl;
+		w->stats.n_waitinglist++;
+	}
+	CHECK_OBJ_NOTNULL(w->nwaitinglist, WAITINGLIST_MAGIC);
+
+	if (w->nbusyobj == NULL) {
+		ALLOC_OBJ(w->nbusyobj, BUSYOBJ_MAGIC);
+		XXXAN(w->nbusyobj);
+	}
+
+	if (hash->prep != NULL)
+		hash->prep(sp);
+}
+
+void
+HSH_Cleanup(struct worker *w)
+{
+
+	if (w->nobjcore != NULL) {
+		FREE_OBJ(w->nobjcore);
+		w->stats.n_objectcore--;
+		w->nobjcore = NULL;
+	}
+	if (w->nobjhead != NULL) {
+		Lck_Delete(&w->nobjhead->mtx);
+		FREE_OBJ(w->nobjhead);
+		w->nobjhead = NULL;
+		w->stats.n_objecthead--;
+	}
+	if (w->nwaitinglist != NULL) {
+		FREE_OBJ(w->nwaitinglist);
+		w->nwaitinglist = NULL;
+	}
+	if (w->nhashpriv != NULL) {
+		/* XXX: If needed, add slinger method for this */
+		free(w->nhashpriv);
+		w->nhashpriv = NULL;
+	}
+	if (w->nbusyobj != NULL) {
+		FREE_OBJ(w->nbusyobj);
+		w->nbusyobj = NULL;
+	}
+}
+
+void
+HSH_DeleteObjHead(struct worker *w, struct objhead *oh)
+{
+
+	AZ(oh->refcnt);
+	assert(VTAILQ_EMPTY(&oh->objcs));
+	Lck_Delete(&oh->mtx);
+	w->stats.n_objecthead--;
+	FREE_OBJ(oh);
+}
+
+void
+HSH_AddString(const struct sess *sp, const char *str)
+{
+	int l;
+
+	if (str == NULL)
+		str = "";
+	l = strlen(str);
+
+	SHA256_Update(sp->wrk->sha256ctx, str, l);
+	SHA256_Update(sp->wrk->sha256ctx, "#", 1);
+
+	if (cache_param->log_hash)
+		WSP(sp, SLT_Hash, "%s", str);
+}
+
+/*---------------------------------------------------------------------
+ * This is a debugging hack to enable testing of boundary conditions
+ * in the hash algorithm.
+ * We trap the first 9 different digests and translate them to different
+ * digests with edge bit conditions
+ */
+
+static struct hsh_magiclist {
+	unsigned char was[SHA256_LEN];
+	unsigned char now[SHA256_LEN];
+} hsh_magiclist[] = {
+	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } },
+	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 } },
+	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 } },
+	{ .now = {	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+	{ .now = {	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+	{ .now = {	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+	{ .now = {	0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+};
+
+#define HSH_NMAGIC (sizeof hsh_magiclist / sizeof hsh_magiclist[0])
+
+static void
+hsh_testmagic(void *result)
+{
+	int i, j;
+	static int nused = 0;
+
+	for (i = 0; i < nused; i++)
+		if (!memcmp(hsh_magiclist[i].was, result, SHA256_LEN))
+			break;
+	if (i == nused && i < HSH_NMAGIC)
+		memcpy(hsh_magiclist[nused++].was, result, SHA256_LEN);
+	if (i == nused)
+		return;
+	assert(i < HSH_NMAGIC);
+	fprintf(stderr, "HASHMAGIC: <");
+	for (j = 0; j < SHA256_LEN; j++)
+		fprintf(stderr, "%02x", ((unsigned char*)result)[j]);
+	fprintf(stderr, "> -> <");
+	memcpy(result, hsh_magiclist[i].now, SHA256_LEN);
+	for (j = 0; j < SHA256_LEN; j++)
+		fprintf(stderr, "%02x", ((unsigned char*)result)[j]);
+	fprintf(stderr, ">\n");
+}
+
+/*---------------------------------------------------------------------
+ * Insert an object which magically appears out of nowhere or, more likely,
+ * comes off some persistent storage device.
+ * Return it with a reference held.
+ */
+
+struct objcore *
+HSH_Insert(const struct sess *sp)
+{
+	struct worker *w;
+	struct objhead *oh;
+	struct objcore *oc;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+	AN(hash);
+	w = sp->wrk;
+
+	HSH_Prealloc(sp);
+	if (cache_param->diag_bitmap & 0x80000000)
+		hsh_testmagic(sp->wrk->nobjhead->digest);
+
+	AZ(sp->hash_objhead);
+	AN(w->nobjhead);
+	oh = hash->lookup(sp, w->nobjhead);
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	if (oh == w->nobjhead)
+		w->nobjhead = NULL;
+	Lck_Lock(&oh->mtx);
+	assert(oh->refcnt > 0);
+
+	/* Insert (precreated) objcore in objecthead */
+	oc = w->nobjcore;
+	w->nobjcore = NULL;
+	oc->refcnt = 1;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	AZ(oc->flags & OC_F_BUSY);
+
+	VTAILQ_INSERT_HEAD(&oh->objcs, oc, list);
+	/* NB: do not deref objhead the new object inherits our reference */
+	oc->objhead = oh;
+	Lck_Unlock(&oh->mtx);
+	sp->wrk->stats.n_vampireobject++;
+	return (oc);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+struct objcore *
+HSH_Lookup(struct sess *sp, struct objhead **poh)
+{
+	struct worker *w;
+	struct objhead *oh;
+	struct objcore *oc;
+	struct objcore *busy_oc, *grace_oc;
+	struct object *o;
+	double grace_ttl;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->http, HTTP_MAGIC);
+	AN(sp->director);
+	AN(hash);
+	w = sp->wrk;
+
+	HSH_Prealloc(sp);
+	memcpy(sp->wrk->nobjhead->digest, sp->digest, sizeof sp->digest);
+	if (cache_param->diag_bitmap & 0x80000000)
+		hsh_testmagic(sp->wrk->nobjhead->digest);
+
+	if (sp->hash_objhead != NULL) {
+		/*
+		 * This sess came off the waiting list, and brings a
+		 * oh refcnt with it.
+		 */
+		CHECK_OBJ_NOTNULL(sp->hash_objhead, OBJHEAD_MAGIC);
+		oh = sp->hash_objhead;
+		sp->hash_objhead = NULL;
+	} else {
+		AN(w->nobjhead);
+		oh = hash->lookup(sp, w->nobjhead);
+		if (oh == w->nobjhead)
+			w->nobjhead = NULL;
+	}
+
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	Lck_Lock(&oh->mtx);
+	assert(oh->refcnt > 0);
+	busy_oc = NULL;
+	grace_oc = NULL;
+	grace_ttl = NAN;
+	VTAILQ_FOREACH(oc, &oh->objcs, list) {
+		/* Must be at least our own ref + the objcore we examine */
+		assert(oh->refcnt > 1);
+		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+		assert(oc->objhead == oh);
+
+		if (oc->flags & OC_F_BUSY) {
+			CHECK_OBJ_NOTNULL(oc->busyobj, BUSYOBJ_MAGIC);
+			if (sp->hash_ignore_busy)
+				continue;
+
+			if (oc->busyobj->vary != NULL &&
+			    !VRY_Match(sp, oc->busyobj->vary))
+				continue;
+
+			busy_oc = oc;
+			continue;
+		}
+
+		o = oc_getobj(sp->wrk, oc);
+		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+
+		if (o->exp.ttl <= 0.)
+			continue;
+		if (BAN_CheckObject(o, sp))
+			continue;
+		if (o->vary != NULL && !VRY_Match(sp, o->vary))
+			continue;
+
+		/* If still valid, use it */
+		if (EXP_Ttl(sp, o) >= sp->t_req)
+			break;
+
+		/*
+		 * Remember any matching objects inside their grace period
+		 * and if there are several, use the least expired one.
+		 */
+		if (EXP_Grace(sp, o) >= sp->t_req) {
+			if (grace_oc == NULL ||
+			    grace_ttl < o->exp.entered + o->exp.ttl) {
+				grace_oc = oc;
+				grace_ttl = o->exp.entered + o->exp.ttl;
+			}
+		}
+	}
+
+	/*
+	 * If we have seen a busy object or the backend is unhealthy, and
+	 * we have an object in grace, use it, if req.grace is also
+	 * satisified.
+	 * XXX: Interesting footnote:  The busy object might be for a
+	 * XXX: different "Vary:" than we sought.  We have no way of knowing
+	 * XXX: this until the object is unbusy'ed, so in practice we
+	 * XXX: serialize fetch of all Vary's if grace is possible.
+	 */
+
+	AZ(sp->objcore);
+	sp->objcore = grace_oc;		/* XXX: Hack-ish */
+	if (oc == NULL			/* We found no live object */
+	    && grace_oc != NULL		/* There is a grace candidate */
+	    && (busy_oc != NULL		/* Somebody else is already busy */
+	    || !VDI_Healthy(sp->director, sp))) {
+					/* Or it is impossible to fetch */
+		o = oc_getobj(sp->wrk, grace_oc);
+		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+		oc = grace_oc;
+	}
+	sp->objcore = NULL;
+
+	if (oc != NULL && !sp->hash_always_miss) {
+		o = oc_getobj(sp->wrk, oc);
+		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+		assert(oc->objhead == oh);
+
+		/* We found an object we like */
+		oc->refcnt++;
+		if (o->hits < INT_MAX)
+			o->hits++;
+		assert(oh->refcnt > 1);
+		Lck_Unlock(&oh->mtx);
+		assert(hash->deref(oh));
+		*poh = oh;
+		return (oc);
+	}
+
+	if (busy_oc != NULL) {
+		/* There are one or more busy objects, wait for them */
+		if (sp->esi_level == 0) {
+			CHECK_OBJ_NOTNULL(sp->wrk->nwaitinglist,
+			    WAITINGLIST_MAGIC);
+			if (oh->waitinglist == NULL) {
+				oh->waitinglist = sp->wrk->nwaitinglist;
+				sp->wrk->nwaitinglist = NULL;
+			}
+			VTAILQ_INSERT_TAIL(&oh->waitinglist->list, sp, list);
+		}
+		if (cache_param->diag_bitmap & 0x20)
+			WSP(sp, SLT_Debug,
+				"on waiting list <%p>", oh);
+		SES_Charge(sp);
+		/*
+		 * The objhead reference transfers to the sess, we get it
+		 * back when the sess comes off the waiting list and
+		 * calls us again
+		 */
+		sp->hash_objhead = oh;
+		sp->wrk = NULL;
+		Lck_Unlock(&oh->mtx);
+		return (NULL);
+	}
+
+	/* Insert (precreated) objcore in objecthead */
+	oc = w->nobjcore;
+	w->nobjcore = NULL;
+	AN(oc->flags & OC_F_BUSY);
+	oc->refcnt = 1;
+
+	/* XXX: clear w->nbusyobj before use */
+	VRY_Validate(sp->vary_b);
+	if (sp->vary_l != NULL)
+		w->nbusyobj->vary = sp->vary_b;
+	else
+		w->nbusyobj->vary = NULL;
+	oc->busyobj = w->nbusyobj;
+	w->nbusyobj = NULL;
+
+	/*
+	 * Busy objects go on the tail, so they will not trip up searches.
+	 * HSH_Unbusy() will move them to the front.
+	 */
+	VTAILQ_INSERT_TAIL(&oh->objcs, oc, list);
+	oc->objhead = oh;
+	/* NB: do not deref objhead the new object inherits our reference */
+	Lck_Unlock(&oh->mtx);
+	*poh = oh;
+	return (oc);
+}
+
+/*---------------------------------------------------------------------
+ */
+
+static void
+hsh_rush(struct objhead *oh)
+{
+	unsigned u;
+	struct sess *sp;
+	struct waitinglist *wl;
+
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	Lck_AssertHeld(&oh->mtx);
+	wl = oh->waitinglist;
+	CHECK_OBJ_NOTNULL(wl, WAITINGLIST_MAGIC);
+	for (u = 0; u < cache_param->rush_exponent; u++) {
+		sp = VTAILQ_FIRST(&wl->list);
+		if (sp == NULL)
+			break;
+		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+		AZ(sp->wrk);
+		VTAILQ_REMOVE(&wl->list, sp, list);
+		DSL(0x20, SLT_Debug, sp->vsl_id, "off waiting list");
+		if (SES_Schedule(sp)) {
+			/*
+			 * We could not schedule the session, leave the
+			 * rest on the busy list.
+			 */
+			break;
+		}
+	}
+	if (VTAILQ_EMPTY(&wl->list)) {
+		oh->waitinglist = NULL;
+		FREE_OBJ(wl);
+	}
+}
+
+/*---------------------------------------------------------------------
+ * Purge an entire objhead
+ */
+
+void
+HSH_Purge(const struct sess *sp, struct objhead *oh, double ttl, double grace)
+{
+	struct objcore *oc, **ocp;
+	unsigned spc, nobj, n;
+	struct object *o;
+
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	spc = WS_Reserve(sp->wrk->ws, 0);
+	ocp = (void*)sp->wrk->ws->f;
+	Lck_Lock(&oh->mtx);
+	assert(oh->refcnt > 0);
+	nobj = 0;
+	VTAILQ_FOREACH(oc, &oh->objcs, list) {
+		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+		assert(oc->objhead == oh);
+		if (oc->flags & OC_F_BUSY) {
+			/*
+			 * We cannot purge busy objects here, because their
+			 * owners have special rights to them, and may nuke
+			 * them without concern for the refcount, which by
+			 * definition always must be one, so they don't check.
+			 */
+			continue;
+		}
+
+		(void)oc_getobj(sp->wrk, oc); /* XXX: still needed ? */
+
+		xxxassert(spc >= sizeof *ocp);
+		oc->refcnt++;
+		spc -= sizeof *ocp;
+		ocp[nobj++] = oc;
+	}
+	Lck_Unlock(&oh->mtx);
+
+	/* NB: inverse test to catch NAN also */
+	if (!(ttl > 0.))
+		ttl = -1.;
+	if (!(grace > 0.))
+		grace = -1.;
+	for (n = 0; n < nobj; n++) {
+		oc = ocp[n];
+		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+		o = oc_getobj(sp->wrk, oc);
+		if (o == NULL)
+			continue;
+		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+		o->exp.ttl = ttl;
+		o->exp.grace = grace;
+		EXP_Rearm(o);
+		(void)HSH_Deref(sp->wrk, NULL, &o);
+	}
+	WS_Release(sp->wrk->ws, 0);
+}
+
+
+/*---------------------------------------------------------------------
+ * Kill a busy object we don't need anyway.
+ * There may be sessions on the waiting list, so we cannot just blow
+ * it out of the water.
+ */
+
+void
+HSH_Drop(struct sess *sp)
+{
+	struct object *o;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	o = sp->obj;
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	AssertObjCorePassOrBusy(o->objcore);
+	o->exp.ttl = -1.;
+	if (o->objcore != NULL)		/* Pass has no objcore */
+		HSH_Unbusy(sp);
+	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
+}
+
+void
+HSH_Unbusy(const struct sess *sp)
+{
+	struct object *o;
+	struct objhead *oh;
+	struct objcore *oc;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	o = sp->obj;
+	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+	oc = o->objcore;
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	oh = oc->objhead;
+	CHECK_OBJ(oh, OBJHEAD_MAGIC);
+
+	AssertObjBusy(o);
+	AN(oc->ban);
+	assert(oc->refcnt > 0);
+	assert(oh->refcnt > 0);
+	if (o->ws_o->overflow)
+		sp->wrk->stats.n_objoverflow++;
+	if (cache_param->diag_bitmap & 0x40)
+		WSP(sp, SLT_Debug,
+		    "Object %u workspace free %u", o->xid, WS_Free(o->ws_o));
+
+	/* XXX: pretouch neighbors on oh->objcs to prevent page-on under mtx */
+	Lck_Lock(&oh->mtx);
+	assert(oh->refcnt > 0);
+	/* XXX: strictly speaking, we should sort in Date: order. */
+	VTAILQ_REMOVE(&oh->objcs, oc, list);
+	VTAILQ_INSERT_HEAD(&oh->objcs, oc, list);
+	oc->flags &= ~OC_F_BUSY;
+	AZ(sp->wrk->nbusyobj);
+	sp->wrk->nbusyobj = oc->busyobj;
+	oc->busyobj = NULL;
+	if (oh->waitinglist != NULL)
+		hsh_rush(oh);
+	AN(oc->ban);
+	Lck_Unlock(&oh->mtx);
+	assert(oc_getobj(sp->wrk, oc) == o);
+}
+
+void
+HSH_Ref(struct objcore *oc)
+{
+	struct objhead *oh;
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+	oh = oc->objhead;
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+	Lck_Lock(&oh->mtx);
+	assert(oc->refcnt > 0);
+	oc->refcnt++;
+	Lck_Unlock(&oh->mtx);
+}
+
+/*--------------------------------------------------------------------
+ * Dereference objcore and or object
+ *
+ * Can deal with:
+ *	bare objcore (incomplete fetch)
+ *	bare object (pass)
+ *	object with objcore
+ *	XXX later:  objcore with object (?)
+ *
+ * But you can only supply one of the two arguments at a time.
+ *
+ * Returns zero if target was destroyed.
+ */
+
+int
+HSH_Deref(struct worker *w, struct objcore *oc, struct object **oo)
+{
+	struct object *o = NULL;
+	struct objhead *oh;
+	unsigned r;
+
+	/* Only one arg at a time */
+	assert(oc == NULL || oo == NULL);
+
+	if (oo != NULL) {
+		o = *oo;
+		*oo = NULL;
+		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
+		oc = o->objcore;
+	}
+
+	if (o != NULL && oc == NULL) {
+		/*
+		 * A pass object with neither objcore nor objhdr reference.
+		 * -> simply free the (Transient) storage
+		 */
+		STV_Freestore(o);
+		STV_free(o->objstore);
+		w->stats.n_object--;
+		return (0);
+	}
+
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
+
+	oh = oc->objhead;
+	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
+
+	Lck_Lock(&oh->mtx);
+	assert(oh->refcnt > 0);
+	assert(oc->refcnt > 0);
+	r = --oc->refcnt;
+	if (!r)
+		VTAILQ_REMOVE(&oh->objcs, oc, list);
+	else {
+		/* Must have an object */
+		AN(oc->methods);
+	}
+	if (oh->waitinglist != NULL)
+		hsh_rush(oh);
+	Lck_Unlock(&oh->mtx);
+	if (r != 0)
+		return (r);
+
+	BAN_DestroyObj(oc);
+	AZ(oc->ban);
+
+	if (oc->flags & OC_F_BUSY) {
+		CHECK_OBJ_NOTNULL(oc->busyobj, BUSYOBJ_MAGIC);
+		if (w->nbusyobj == NULL)
+			w->nbusyobj = oc->busyobj;
+		else
+			FREE_OBJ(oc->busyobj);
+		oc->busyobj = NULL;
+	}
+	AZ(oc->busyobj);
+
+	if (oc->methods != NULL) {
+		oc_freeobj(oc);
+		w->stats.n_object--;
+	}
+	FREE_OBJ(oc);
+
+	w->stats.n_objectcore--;
+	/* Drop our ref on the objhead */
+	assert(oh->refcnt > 0);
+	if (hash->deref(oh))
+		return (0);
+	HSH_DeleteObjHead(w, oh);
+	return (0);
+}
+
+void
+HSH_Init(const struct hash_slinger *slinger)
+{
+
+	assert(DIGEST_LEN == SHA256_LEN);	/* avoid #include pollution */
+	hash = slinger;
+	if (hash->start != NULL)
+		hash->start();
+}
diff --git a/bin/varnishd/cache/cache_http.c b/bin/varnishd/cache/cache_http.c
new file mode 100644
index 0000000..784eb28
--- /dev/null
+++ b/bin/varnishd/cache/cache_http.c
@@ -0,0 +1,1119 @@
+/*-
+ * 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.
+ *
+ * HTTP request storage and manipulation
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "cache.h"
+
+#include "vct.h"
+
+#define HTTPH(a, b, c, d, e, f, g) char b[] = "*" a ":";
+#include "tbl/http_headers.h"
+#undef HTTPH
+
+/*lint -save -e773 not () */
+#define LOGMTX2(ax, bx, cx)	[bx] = SLT_##ax##cx
+
+#define LOGMTX1(ax) {					\
+	LOGMTX2(ax, HTTP_HDR_REQ,	Request),	\
+	LOGMTX2(ax, HTTP_HDR_RESPONSE,	Response),	\
+	LOGMTX2(ax, HTTP_HDR_STATUS,	Status),	\
+	LOGMTX2(ax, HTTP_HDR_URL,	URL),		\
+	LOGMTX2(ax, HTTP_HDR_PROTO,	Protocol),	\
+	LOGMTX2(ax, HTTP_HDR_FIRST,	Header),	\
+	}
+
+static const enum VSL_tag_e logmtx[][HTTP_HDR_FIRST + 1] = {
+	[HTTP_Rx] = LOGMTX1(Rx),
+	[HTTP_Tx] = LOGMTX1(Tx),
+	[HTTP_Obj] = LOGMTX1(Obj)
+};
+/*lint -restore */
+
+static enum VSL_tag_e
+http2shmlog(const struct http *hp, int t)
+{
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	if (t > HTTP_HDR_FIRST)
+		t = HTTP_HDR_FIRST;
+	assert(hp->logtag >= HTTP_Rx && hp->logtag <= HTTP_Obj); /*lint !e685*/
+	assert(t >= HTTP_HDR_REQ && t <= HTTP_HDR_FIRST);
+	return (logmtx[hp->logtag][t]);
+}
+
+static void
+WSLH(struct worker *w, unsigned vsl_id, const struct http *hp, unsigned hdr)
+{
+
+	AN(vsl_id & (VSL_CLIENTMARKER|VSL_BACKENDMARKER));
+	WSLR(w, http2shmlog(hp, hdr), vsl_id, hp->hd[hdr]);
+}
+
+/*--------------------------------------------------------------------*/
+/* List of canonical HTTP response code names from RFC2616 */
+
+static struct http_msg {
+	unsigned	nbr;
+	const char	*txt;
+} http_msg[] = {
+#define HTTP_RESP(n, t)	{ n, t},
+#include "tbl/http_response.h"
+	{ 0, NULL }
+};
+
+const char *
+http_StatusMessage(unsigned status)
+{
+	struct http_msg *mp;
+
+	assert(status >= 100 && status <= 999);
+	for (mp = http_msg; mp->nbr != 0 && mp->nbr <= status; mp++)
+		if (mp->nbr == status)
+			return (mp->txt);
+	return ("Unknown Error");
+}
+
+/*--------------------------------------------------------------------*/
+
+unsigned
+HTTP_estimate(unsigned nhttp)
+{
+
+	/* XXX: We trust the structs to size-aligned as necessary */
+	return (sizeof (struct http) + (sizeof (txt) + 1) * nhttp);
+}
+
+struct http *
+HTTP_create(void *p, uint16_t nhttp)
+{
+	struct http *hp;
+
+	hp = p;
+	hp->magic = HTTP_MAGIC;
+	hp->hd = (void*)(hp + 1);
+	hp->shd = nhttp;
+	hp->hdf = (void*)(hp->hd + nhttp);
+	return (hp);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_Setup(struct http *hp, struct ws *ws)
+{
+	uint16_t shd;
+	txt *hd;
+	unsigned char *hdf;
+
+	/* XXX: This is not elegant, is it efficient ? */
+	shd = hp->shd;
+	hd = hp->hd;
+	hdf = hp->hdf;
+	memset(hp, 0, sizeof *hp);
+	memset(hd, 0, sizeof *hd * shd);
+	memset(hdf, 0, sizeof *hdf * shd);
+	hp->magic = HTTP_MAGIC;
+	hp->ws = ws;
+	hp->nhd = HTTP_HDR_FIRST;
+	hp->shd = shd;
+	hp->hd = hd;
+	hp->hdf = hdf;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+http_IsHdr(const txt *hh, const char *hdr)
+{
+	unsigned l;
+
+	Tcheck(*hh);
+	AN(hdr);
+	l = hdr[0];
+	assert(l == strlen(hdr + 1));
+	assert(hdr[l] == ':');
+	hdr++;
+	return (!strncasecmp(hdr, hh->b, l));
+}
+
+/*--------------------------------------------------------------------
+ * This function collapses multiple headerlines of the same name.
+ * The lines are joined with a comma, according to [rfc2616, 4.2bot, p32]
+ */
+
+void
+http_CollectHdr(struct http *hp, const char *hdr)
+{
+	unsigned u, v, ml, f = 0, x;
+	char *b = NULL, *e = NULL;
+
+	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
+		while (u < hp->nhd && http_IsHdr(&hp->hd[u], hdr)) {
+			Tcheck(hp->hd[u]);
+			if (f == 0) {
+				/* Found first header, just record the fact */
+				f = u;
+				break;
+			}
+			if (b == NULL) {
+				/* Found second header, start our collection */
+				ml = WS_Reserve(hp->ws, 0);
+				b = hp->ws->f;
+				e = b + ml;
+				x = Tlen(hp->hd[f]);
+				if (b + x < e) {
+					memcpy(b, hp->hd[f].b, x);
+					b += x;
+				} else
+					b = e;
+			}
+
+			AN(b);
+			AN(e);
+
+			/* Append the Nth header we found */
+			if (b < e)
+				*b++ = ',';
+			x = Tlen(hp->hd[u]) - *hdr;
+			if (b + x < e) {
+				memcpy(b, hp->hd[u].b + *hdr, x);
+				b += x;
+			} else
+				b = e;
+
+			/* Shift remaining headers up one slot */
+			for (v = u; v < hp->nhd - 1; v++)
+				hp->hd[v] = hp->hd[v + 1];
+			hp->nhd--;
+		}
+
+	}
+	if (b == NULL)
+		return;
+	AN(e);
+	if (b >= e) {
+		WS_Release(hp->ws, 0);
+		return;
+	}
+	*b = '\0';
+	hp->hd[f].b = hp->ws->f;
+	hp->hd[f].e = b;
+	WS_ReleaseP(hp->ws, b + 1);
+}
+
+
+/*--------------------------------------------------------------------*/
+
+static unsigned
+http_findhdr(const struct http *hp, unsigned l, const char *hdr)
+{
+	unsigned u;
+
+	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
+		Tcheck(hp->hd[u]);
+		if (hp->hd[u].e < hp->hd[u].b + l + 1)
+			continue;
+		if (hp->hd[u].b[l] != ':')
+			continue;
+		if (strncasecmp(hdr, hp->hd[u].b, l))
+			continue;
+		return (u);
+	}
+	return (0);
+}
+
+int
+http_GetHdr(const struct http *hp, const char *hdr, char **ptr)
+{
+	unsigned u, l;
+	char *p;
+
+	l = hdr[0];
+	diagnostic(l == strlen(hdr + 1));
+	assert(hdr[l] == ':');
+	hdr++;
+	u = http_findhdr(hp, l - 1, hdr);
+	if (u == 0) {
+		if (ptr != NULL)
+			*ptr = NULL;
+		return (0);
+	}
+	if (ptr != NULL) {
+		p = hp->hd[u].b + l;
+		while (vct_issp(*p))
+			p++;
+		*ptr = p;
+	}
+	return (1);
+}
+
+
+/*--------------------------------------------------------------------
+ * Find a given data element in a header according to RFC2616's #rule
+ * (section 2.1, p15)
+ */
+
+int
+http_GetHdrData(const struct http *hp, const char *hdr,
+    const char *field, char **ptr)
+{
+	char *h, *e;
+	unsigned fl;
+
+	if (ptr != NULL)
+		*ptr = NULL;
+	if (!http_GetHdr(hp, hdr, &h))
+		return (0);
+	AN(h);
+	e = strchr(h, '\0');
+	fl = strlen(field);
+	while (h + fl <= e) {
+		/* Skip leading whitespace and commas */
+		if (vct_islws(*h) || *h == ',') {
+			h++;
+			continue;
+		}
+		/* Check for substrings before memcmp() */
+		if ((h + fl == e || vct_issepctl(h[fl])) &&
+		    !memcmp(h, field, fl)) {
+			if (ptr != NULL) {
+				h += fl;
+				while (vct_islws(*h))
+					h++;
+				*ptr = h;
+			}
+			return (1);
+		}
+		/* Skip until end of header or comma */
+		while (*h && *h != ',')
+			h++;
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Find a given headerfields Q value.
+ */
+
+double
+http_GetHdrQ(const struct http *hp, const char *hdr, const char *field)
+{
+	char *h;
+	int i;
+	double a, b;
+
+	h = NULL;
+	i = http_GetHdrData(hp, hdr, field, &h);
+	if (!i)
+		return (0.);
+
+	if (h == NULL)
+		return (1.);
+	/* Skip whitespace, looking for '=' */
+	while (*h && vct_issp(*h))
+		h++;
+	if (*h++ != ';')
+		return (1.);
+	while (*h && vct_issp(*h))
+		h++;
+	if (*h++ != 'q')
+		return (1.);
+	while (*h && vct_issp(*h))
+		h++;
+	if (*h++ != '=')
+		return (1.);
+	while (*h && vct_issp(*h))
+		h++;
+	a = 0.;
+	while (vct_isdigit(*h)) {
+		a *= 10.;
+		a += *h - '0';
+		h++;
+	}
+	if (*h++ != '.')
+		return (a);
+	b = .1;
+	while (vct_isdigit(*h)) {
+		a += b * (*h - '0');
+		b *= .1;
+		h++;
+	}
+	return (a);
+}
+
+/*--------------------------------------------------------------------
+ * Find a given headerfields value.
+ */
+
+int
+http_GetHdrField(const struct http *hp, const char *hdr,
+    const char *field, char **ptr)
+{
+	char *h;
+	int i;
+
+	if (ptr != NULL)
+		*ptr = NULL;
+
+	h = NULL;
+	i = http_GetHdrData(hp, hdr, field, &h);
+	if (!i)
+		return (i);
+
+	if (ptr != NULL && h != NULL) {
+		/* Skip whitespace, looking for '=' */
+		while (*h && vct_issp(*h))
+			h++;
+		if (*h == '=') {
+			h++;
+			while (*h && vct_issp(*h))
+				h++;
+			*ptr = h;
+		}
+	}
+	return (i);
+}
+
+/*--------------------------------------------------------------------
+ * XXX: redo with http_GetHdrField() ?
+ */
+
+const char *
+http_DoConnection(const struct http *hp)
+{
+	char *p, *q;
+	const char *ret;
+	unsigned u;
+
+	if (!http_GetHdr(hp, H_Connection, &p)) {
+		if (hp->protover < 11)
+			return ("not HTTP/1.1");
+		return (NULL);
+	}
+	ret = NULL;
+	AN(p);
+	for (; *p; p++) {
+		if (vct_issp(*p))
+			continue;
+		if (*p == ',')
+			continue;
+		for (q = p + 1; *q; q++)
+			if (*q == ',' || vct_issp(*q))
+				break;
+		u = pdiff(p, q);
+		if (u == 5 && !strncasecmp(p, "close", u))
+			ret = "Connection: close";
+		u = http_findhdr(hp, u, p);
+		if (u != 0)
+			hp->hdf[u] |= HDF_FILTER;
+		if (!*q)
+			break;
+		p = q;
+	}
+	return (ret);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+http_HdrIs(const struct http *hp, const char *hdr, const char *val)
+{
+	char *p;
+
+	if (!http_GetHdr(hp, hdr, &p))
+		return (0);
+	AN(p);
+	if (!strcasecmp(p, val))
+		return (1);
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+uint16_t
+http_GetStatus(const struct http *hp)
+{
+
+	return (hp->status);
+}
+
+const char *
+http_GetReq(const struct http *hp)
+{
+
+	Tcheck(hp->hd[HTTP_HDR_REQ]);
+	return (hp->hd[HTTP_HDR_REQ].b);
+}
+
+/*--------------------------------------------------------------------
+ * Dissect the headers of the HTTP protocol message.
+ * Detect conditionals (headers which start with '^[Ii][Ff]-')
+ */
+
+static uint16_t
+http_dissect_hdrs(struct worker *w, struct http *hp, unsigned vsl_id, char *p,
+    const struct http_conn *htc)
+{
+	char *q, *r;
+	txt t = htc->rxbuf;
+
+	if (*p == '\r')
+		p++;
+
+	hp->nhd = HTTP_HDR_FIRST;
+	hp->conds = 0;
+	r = NULL;		/* For FlexeLint */
+	for (; p < t.e; p = r) {
+
+		/* Find end of next header */
+		q = r = p;
+		while (r < t.e) {
+			if (!vct_iscrlf(*r)) {
+				r++;
+				continue;
+			}
+			q = r;
+			assert(r < t.e);
+			r += vct_skipcrlf(r);
+			if (r >= t.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) {
+			VSC_C_main->losthdr++;
+			WSL(w, SLT_LostHeader, vsl_id, "%.*s",
+			    q - p > 20 ? 20 : q - p, p);
+			return (413);
+		}
+
+		/* Empty header = end of headers */
+		if (p == q)
+			break;
+
+		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 (hp->nhd < hp->shd) {
+			hp->hdf[hp->nhd] = 0;
+			hp->hd[hp->nhd].b = p;
+			hp->hd[hp->nhd].e = q;
+			WSLH(w, vsl_id, hp, hp->nhd);
+			hp->nhd++;
+		} else {
+			VSC_C_main->losthdr++;
+			WSL(w, SLT_LostHeader, vsl_id, "%.*s",
+			    q - p > 20 ? 20 : q - p, p);
+			return (413);
+		}
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Deal with first line of HTTP protocol message.
+ */
+
+static uint16_t
+http_splitline(struct worker *w, unsigned vsl_id, struct http *hp,
+    const struct http_conn *htc, int h1, int h2, int h3)
+{
+	char *p, *q;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	/* XXX: Assert a NUL at rx.e ? */
+	Tcheck(htc->rxbuf);
+
+	/* Skip leading LWS */
+	for (p = htc->rxbuf.b ; vct_islws(*p); p++)
+		continue;
+
+	/* First field cannot contain SP, CRLF or CTL */
+	q = p;
+	for (; !vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[h1].b = q;
+	hp->hd[h1].e = p;
+
+	/* Skip SP */
+	for (; vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+
+	/* Second field cannot contain LWS or CTL */
+	q = p;
+	for (; !vct_islws(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+	hp->hd[h2].b = q;
+	hp->hd[h2].e = p;
+
+	if (!Tlen(hp->hd[h2]))
+		return (413);
+
+	/* Skip SP */
+	for (; vct_issp(*p); p++) {
+		if (vct_isctl(*p))
+			return (400);
+	}
+
+	/* Third field is optional and cannot contain CTL */
+	q = p;
+	if (!vct_iscrlf(*p)) {
+		for (; !vct_iscrlf(*p); p++)
+			if (!vct_issep(*p) && vct_isctl(*p))
+				return (400);
+	}
+	hp->hd[h3].b = q;
+	hp->hd[h3].e = p;
+
+	/* Skip CRLF */
+	p += vct_skipcrlf(p);
+
+	*hp->hd[h1].e = '\0';
+	WSLH(w, vsl_id, hp, h1);
+
+	*hp->hd[h2].e = '\0';
+	WSLH(w, vsl_id, hp, h2);
+
+	if (hp->hd[h3].e != NULL) {
+		*hp->hd[h3].e = '\0';
+		WSLH(w, vsl_id, hp, h3);
+	}
+
+	return (http_dissect_hdrs(w, hp, vsl_id, p, htc));
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+http_ProtoVer(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
+http_DissectRequest(struct sess *sp)
+{
+	struct http_conn *htc;
+	struct http *hp;
+	uint16_t retval;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	htc = sp->htc;
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	hp = sp->http;
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	hp->logtag = HTTP_Rx;
+
+	retval = http_splitline(sp->wrk, sp->vsl_id, hp, htc,
+	    HTTP_HDR_REQ, HTTP_HDR_URL, HTTP_HDR_PROTO);
+	if (retval != 0) {
+		WSPR(sp, SLT_HttpGarbage, htc->rxbuf);
+		return (retval);
+	}
+	http_ProtoVer(hp);
+	return (retval);
+}
+
+/*--------------------------------------------------------------------*/
+
+uint16_t
+http_DissectResponse(struct worker *w, const struct http_conn *htc,
+    struct http *hp)
+{
+	int j;
+	uint16_t retval = 0;
+	char *p;
+
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	hp->logtag = HTTP_Rx;
+
+	if (http_splitline(w, htc->vsl_id, hp, htc,
+	    HTTP_HDR_PROTO, HTTP_HDR_STATUS, HTTP_HDR_RESPONSE))
+		retval = 503;
+
+	if (retval == 0 && memcmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.", 7))
+		retval = 503;
+
+	if (retval == 0 && Tlen(hp->hd[HTTP_HDR_STATUS]) != 3)
+		retval = 503;
+
+	if (retval == 0) {
+		hp->status = 0;
+		p = hp->hd[HTTP_HDR_STATUS].b;
+		for (j = 100; j != 0; j /= 10) {
+			if (!vct_isdigit(*p)) {
+				retval = 503;
+				break;
+			}
+			hp->status += (uint16_t)(j * (*p - '0'));
+			p++;
+		}
+		if (*p != '\0')
+			retval = 503;
+	}
+
+	if (retval != 0) {
+		WSLR(w, SLT_HttpGarbage, htc->vsl_id, htc->rxbuf);
+		assert(retval >= 100 && retval <= 999);
+		hp->status = retval;
+	} else {
+		http_ProtoVer(hp);
+	}
+
+	if (hp->hd[HTTP_HDR_RESPONSE].b == NULL ||
+	    !Tlen(hp->hd[HTTP_HDR_RESPONSE])) {
+		/* Backend didn't send a response string, use the standard */
+		hp->hd[HTTP_HDR_RESPONSE].b =
+		    TRUST_ME(http_StatusMessage(hp->status));
+		hp->hd[HTTP_HDR_RESPONSE].e =
+		    strchr(hp->hd[HTTP_HDR_RESPONSE].b, '\0');
+	}
+	return (retval);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_SetH(const struct http *to, unsigned n, const char *fm)
+{
+
+	assert(n < to->shd);
+	AN(fm);
+	to->hd[n].b = TRUST_ME(fm);
+	to->hd[n].e = strchr(to->hd[n].b, '\0');
+	to->hdf[n] = 0;
+}
+
+static void
+http_copyh(const struct http *to, const struct http *fm, unsigned n)
+{
+
+	assert(n < HTTP_HDR_FIRST);
+	Tcheck(fm->hd[n]);
+	to->hd[n] = fm->hd[n];
+	to->hdf[n] = fm->hdf[n];
+}
+
+void
+http_ForceGet(const struct http *to)
+{
+	if (strcmp(http_GetReq(to), "GET"))
+		http_SetH(to, HTTP_HDR_REQ, "GET");
+}
+
+void
+http_CopyResp(struct http *to, const struct http *fm)
+{
+
+	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	http_SetH(to, HTTP_HDR_PROTO, "HTTP/1.1");
+	to->status = fm->status;
+	http_copyh(to, fm, HTTP_HDR_RESPONSE);
+}
+
+void
+http_SetResp(struct http *to, const char *proto, uint16_t status,
+    const char *response)
+{
+
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	http_SetH(to, HTTP_HDR_PROTO, proto);
+	assert(status >= 100 && status <= 999);
+	to->status = status;
+	http_SetH(to, HTTP_HDR_RESPONSE, response);
+}
+
+static void
+http_copyheader(struct worker *w, unsigned vsl_id, struct http *to,
+    const struct http *fm, unsigned n)
+{
+
+	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	assert(n < fm->shd);
+	Tcheck(fm->hd[n]);
+	if (to->nhd < to->shd) {
+		to->hd[to->nhd] = fm->hd[n];
+		to->hdf[to->nhd] = 0;
+		to->nhd++;
+	} else  {
+		VSC_C_main->losthdr++;
+		WSLR(w, SLT_LostHeader, vsl_id, fm->hd[n]);
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Estimate how much workspace we need to Filter this header according
+ * to 'how'.
+ */
+
+unsigned
+http_EstimateWS(const struct http *fm, unsigned how, uint16_t *nhd)
+{
+	unsigned u, l;
+
+	l = 0;
+	*nhd = HTTP_HDR_FIRST;
+	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
+	for (u = 0; u < fm->nhd; u++) {
+		if (fm->hd[u].b == NULL)
+			continue;
+		if (fm->hdf[u] & HDF_FILTER)
+			continue;
+#define HTTPH(a, b, c, d, e, f, g) \
+		if (((e) & how) && http_IsHdr(&fm->hd[u], (b))) \
+			continue;
+#include "tbl/http_headers.h"
+#undef HTTPH
+		l += PRNDUP(Tlen(fm->hd[u]) + 1);
+		(*nhd)++;
+		// fm->hdf[u] |= HDF_COPY;
+	}
+	return (l);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_FilterFields(struct worker *w, unsigned vsl_id, struct http *to,
+    const struct http *fm, unsigned how)
+{
+	unsigned u;
+
+	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	to->nhd = HTTP_HDR_FIRST;
+	to->status = fm->status;
+	for (u = HTTP_HDR_FIRST; u < fm->nhd; u++) {
+		if (fm->hd[u].b == NULL)
+			continue;
+		if (fm->hdf[u] & HDF_FILTER)
+			continue;
+#define HTTPH(a, b, c, d, e, f, g) \
+		if (((e) & how) && http_IsHdr(&fm->hd[u], (b))) \
+			continue;
+#include "tbl/http_headers.h"
+#undef HTTPH
+		http_copyheader(w, vsl_id, to, fm, u);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_FilterHeader(const struct sess *sp, unsigned how)
+{
+	struct http *hp;
+
+	hp = sp->wrk->bereq;
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	hp->logtag = HTTP_Tx;
+
+	http_copyh(hp, sp->http, HTTP_HDR_REQ);
+	http_copyh(hp, sp->http, HTTP_HDR_URL);
+	if (how == HTTPH_R_FETCH)
+		http_SetH(hp, HTTP_HDR_PROTO, "HTTP/1.1");
+	else
+		http_copyh(hp, sp->http, HTTP_HDR_PROTO);
+	http_FilterFields(sp->wrk, sp->vsl_id, hp, sp->http, how);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, hp, "X-Varnish: %u", sp->xid);
+}
+
+/*--------------------------------------------------------------------
+ * This function copies any header fields which reference foreign
+ * storage into our own WS.
+ */
+
+void
+http_CopyHome(struct worker *w, unsigned vsl_id, const struct http *hp)
+{
+	unsigned u, l;
+	char *p;
+
+	for (u = 0; u < hp->nhd; u++) {
+		if (hp->hd[u].b == NULL)
+			continue;
+		if (hp->hd[u].b >= hp->ws->s && hp->hd[u].e <= hp->ws->e) {
+			WSLH(w, vsl_id, hp, u);
+			continue;
+		}
+		l = Tlen(hp->hd[u]);
+		p = WS_Alloc(hp->ws, l + 1);
+		if (p != NULL) {
+			WSLH(w, vsl_id, hp, u);
+			memcpy(p, hp->hd[u].b, l + 1L);
+			hp->hd[u].b = p;
+			hp->hd[u].e = p + l;
+		} else {
+			/* XXX This leaves a slot empty */
+			VSC_C_main->losthdr++;
+			WSLR(w, SLT_LostHeader, vsl_id, hp->hd[u]);
+			hp->hd[u].b = NULL;
+			hp->hd[u].e = NULL;
+		}
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_ClrHeader(struct http *to)
+{
+
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	to->nhd = HTTP_HDR_FIRST;
+	to->status = 0;
+	to->protover = 0;
+	to->conds = 0;
+	memset(to->hd, 0, sizeof *to->hd * to->shd);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+http_SetHeader(struct worker *w, unsigned vsl_id, struct http *to,
+    const char *hdr)
+{
+
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	if (to->nhd >= to->shd) {
+		VSC_C_main->losthdr++;
+		WSL(w, SLT_LostHeader, vsl_id, "%s", hdr);
+		return;
+	}
+	http_SetH(to, to->nhd++, hdr);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+http_PutField(struct worker *w, unsigned vsl_id, const struct http *to,
+    int field, const char *string)
+{
+	char *p;
+	unsigned l;
+
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	l = strlen(string);
+	p = WS_Alloc(to->ws, l + 1);
+	if (p == NULL) {
+		WSL(w, SLT_LostHeader, vsl_id, "%s", string);
+		to->hd[field].b = NULL;
+		to->hd[field].e = NULL;
+		to->hdf[field] = 0;
+	} else {
+		memcpy(p, string, l + 1L);
+		to->hd[field].b = p;
+		to->hd[field].e = p + l;
+		to->hdf[field] = 0;
+	}
+}
+
+void
+http_PutProtocol(struct worker *w, unsigned vsl_id, const struct http *to,
+    const char *protocol)
+{
+
+	http_PutField(w, vsl_id, to, HTTP_HDR_PROTO, protocol);
+	if (to->hd[HTTP_HDR_PROTO].b == NULL)
+		http_SetH(to, HTTP_HDR_PROTO, "HTTP/1.1");
+	Tcheck(to->hd[HTTP_HDR_PROTO]);
+}
+
+void
+http_PutStatus(struct http *to, uint16_t status)
+{
+
+	assert(status >= 100 && status <= 999);
+	to->status = status;
+}
+
+void
+http_PutResponse(struct worker *w, unsigned vsl_id, const struct http *to,
+    const char *response)
+{
+
+	http_PutField(w, vsl_id, to, HTTP_HDR_RESPONSE, response);
+	if (to->hd[HTTP_HDR_RESPONSE].b == NULL)
+		http_SetH(to, HTTP_HDR_RESPONSE, "Lost Response");
+	Tcheck(to->hd[HTTP_HDR_RESPONSE]);
+}
+
+void
+http_PrintfHeader(struct worker *w, unsigned vsl_id, struct http *to,
+    const char *fmt, ...)
+{
+	va_list ap;
+	unsigned l, n;
+
+	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
+	l = WS_Reserve(to->ws, 0);
+	va_start(ap, fmt);
+	n = vsnprintf(to->ws->f, l, fmt, ap);
+	va_end(ap);
+	if (n + 1 >= l || to->nhd >= to->shd) {
+		VSC_C_main->losthdr++;
+		WSL(w, SLT_LostHeader, vsl_id, "%s", to->ws->f);
+		WS_Release(to->ws, 0);
+	} else {
+		to->hd[to->nhd].b = to->ws->f;
+		to->hd[to->nhd].e = to->ws->f + n;
+		to->hdf[to->nhd] = 0;
+		WS_Release(to->ws, n + 1);
+		to->nhd++;
+	}
+}
+/*--------------------------------------------------------------------*/
+
+void
+http_Unset(struct http *hp, const char *hdr)
+{
+	uint16_t u, v;
+
+	for (v = u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
+		if (hp->hd[u].b == NULL)
+			continue;
+		if (http_IsHdr(&hp->hd[u], hdr))
+			continue;
+		if (v != u) {
+			memcpy(&hp->hd[v], &hp->hd[u], sizeof *hp->hd);
+			memcpy(&hp->hdf[v], &hp->hdf[u], sizeof *hp->hdf);
+		}
+		v++;
+	}
+	hp->nhd = v;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+HTTP_Copy(struct http *to, const struct http * const fm)
+{
+
+	to->conds = fm->conds;
+	to->logtag = fm->logtag;
+	to->status = fm->status;
+	to->protover = fm->protover;
+	to->nhd = fm->nhd;
+	assert(fm->nhd <= to->shd);
+	memcpy(to->hd, fm->hd, fm->nhd * sizeof *to->hd);
+	memcpy(to->hdf, fm->hdf, fm->nhd * sizeof *to->hdf);
+}
+
+/*--------------------------------------------------------------------*/
+
+unsigned
+http_Write(struct worker *w, unsigned vsl_id, const struct http *hp, int resp)
+{
+	unsigned u, l;
+
+	if (resp) {
+		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], " ");
+		WSLH(w, vsl_id, hp, HTTP_HDR_PROTO);
+
+		hp->hd[HTTP_HDR_STATUS].b = WS_Alloc(w->ws, 4);
+		AN(hp->hd[HTTP_HDR_STATUS].b);
+
+		sprintf(hp->hd[HTTP_HDR_STATUS].b, "%3d", hp->status);
+		hp->hd[HTTP_HDR_STATUS].e = hp->hd[HTTP_HDR_STATUS].b + 3;
+
+		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_STATUS], " ");
+		WSLH(w, vsl_id, hp, HTTP_HDR_STATUS);
+
+		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_RESPONSE], "\r\n");
+		WSLH(w, vsl_id, hp, HTTP_HDR_RESPONSE);
+	} else {
+		AN(hp->hd[HTTP_HDR_URL].b);
+		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_REQ], " ");
+		WSLH(w, vsl_id, hp, HTTP_HDR_REQ);
+		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_URL], " ");
+		WSLH(w, vsl_id, hp, HTTP_HDR_URL);
+		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], "\r\n");
+		WSLH(w, vsl_id, hp, HTTP_HDR_PROTO);
+	}
+	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
+		if (hp->hd[u].b == NULL)
+			continue;
+		AN(hp->hd[u].b);
+		AN(hp->hd[u].e);
+		l += WRW_WriteH(w, &hp->hd[u], "\r\n");
+		WSLH(w, vsl_id, hp, u);
+	}
+	l += WRW_Write(w, "\r\n", -1);
+	return (l);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+HTTP_Init(void)
+{
+
+#define HTTPH(a, b, c, d, e, f, g) b[0] = (char)strlen(b + 1);
+#include "tbl/http_headers.h"
+#undef HTTPH
+}
diff --git a/bin/varnishd/cache/cache_httpconn.c b/bin/varnishd/cache/cache_httpconn.c
new file mode 100644
index 0000000..9e0a052
--- /dev/null
+++ b/bin/varnishd/cache/cache_httpconn.c
@@ -0,0 +1,229 @@
+/*-
+ * 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.
+ *
+ * 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.
+ *
+ * We use this both for client and backend connections.
+ */
+
+#include "config.h"
+
+#include "cache.h"
+
+#include "vct.h"
+
+/*--------------------------------------------------------------------
+ * Check if we have a complete HTTP request or response yet
+ *
+ * Return values:
+ *	 0  No, keep trying
+ *	>0  Yes, it is this many bytes long.
+ */
+
+static int
+htc_header_complete(txt *t)
+{
+	const char *p;
+
+	Tcheck(*t);
+	assert(*t->e == '\0');
+	/* Skip any leading white space */
+	for (p = t->b ; vct_issp(*p); p++)
+		continue;
+	if (p == t->e) {
+		/* All white space */
+		t->e = t->b;
+		*t->e = '\0';
+		return (0);
+	}
+	while (1) {
+		p = strchr(p, '\n');
+		if (p == NULL)
+			return (0);
+		p++;
+		if (*p == '\r')
+			p++;
+		if (*p == '\n')
+			break;
+	}
+	p++;
+	return (p - t->b);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+HTC_Init(struct http_conn *htc, struct ws *ws, int fd, unsigned vsl_id,
+    unsigned maxbytes, unsigned maxhdr)
+{
+
+	htc->magic = HTTP_CONN_MAGIC;
+	htc->ws = ws;
+	htc->fd = fd;
+	htc->vsl_id = vsl_id;
+	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.
+ */
+
+int
+HTC_Reinit(struct http_conn *htc)
+{
+	unsigned 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 = Tlen(htc->pipeline);
+		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 (HTC_Complete(htc));
+}
+
+/*--------------------------------------------------------------------
+ * Return 1 if we have a complete HTTP procol header
+ */
+
+int
+HTC_Complete(struct http_conn *htc)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	i = htc_header_complete(&htc->rxbuf);
+	assert(i >= 0);
+	if (i == 0)
+		return (0);
+	WS_ReleaseP(htc->ws, htc->rxbuf.e);
+	AZ(htc->pipeline.b);
+	AZ(htc->pipeline.e);
+	if (htc->rxbuf.b + i < htc->rxbuf.e) {
+		htc->pipeline.b = htc->rxbuf.b + i;
+		htc->pipeline.e = htc->rxbuf.e;
+		htc->rxbuf.e = htc->pipeline.b;
+	}
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Receive more HTTP protocol bytes
+ * Returns:
+ *	-2 overflow
+ *	-1 error
+ *	 0 more needed
+ *	 1 got complete HTTP header
+ */
+
+int
+HTC_Rx(struct http_conn *htc)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	AN(htc->ws->r);
+	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
+	if (i <= 0) {
+		WS_ReleaseP(htc->ws, htc->rxbuf.b);
+		return (-2);
+	}
+	i = read(htc->fd, htc->rxbuf.e, i);
+	if (i <= 0) {
+		/*
+		 * We wouldn't come here if we had a complete HTTP header
+		 * so consequently an EOF can not be OK
+		 */
+		WS_ReleaseP(htc->ws, htc->rxbuf.b);
+		return (-1);
+	}
+	htc->rxbuf.e += i;
+	*htc->rxbuf.e = '\0';
+	return (HTC_Complete(htc));
+}
+
+/*--------------------------------------------------------------------
+ * Read up to len bytes, returning pipelined data first.
+ */
+
+ssize_t
+HTC_Read(struct worker *w, struct http_conn *htc, void *d, size_t len)
+{
+	size_t l;
+	unsigned char *p;
+	ssize_t i;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	l = 0;
+	p = d;
+	if (htc->pipeline.b) {
+		l = Tlen(htc->pipeline);
+		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)
+		return (l);
+	i = read(htc->fd, p, len);
+	if (i < 0) {
+		WSL(w, SLT_FetchError, htc->vsl_id, "%s", strerror(errno));
+		return (i);
+	}
+	return (i + l);
+}
diff --git a/bin/varnishd/cache/cache_lck.c b/bin/varnishd/cache/cache_lck.c
new file mode 100644
index 0000000..2aef6dc
--- /dev/null
+++ b/bin/varnishd/cache/cache_lck.c
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2008-2010 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.
+ *
+ * The geniuses who came up with pthreads did not think operations like
+ * pthread_assert_mutex_held() were important enough to include them in
+ * the API.
+ *
+ * Build our own locks on top of pthread mutexes and hope that the next
+ * civilization is better at such crucial details than this one.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "cache.h"
+
+/*The constability of lck depends on platform pthreads implementation */
+
+struct ilck {
+	unsigned		magic;
+#define ILCK_MAGIC		0x7b86c8a5
+	pthread_mutex_t		mtx;
+	int			held;
+	pthread_t		owner;
+	VTAILQ_ENTRY(ilck)	list;
+	const char		*w;
+	struct VSC_C_lck	*stat;
+};
+
+static VTAILQ_HEAD(, ilck)	ilck_head =
+    VTAILQ_HEAD_INITIALIZER(ilck_head);
+
+static pthread_mutex_t		lck_mtx;
+
+void __match_proto__()
+Lck__Lock(struct lock *lck, const char *p, const char *f, int l)
+{
+	struct ilck *ilck;
+	int r;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	if (!(cache_param->diag_bitmap & 0x18)) {
+		AZ(pthread_mutex_lock(&ilck->mtx));
+		AZ(ilck->held);
+		ilck->stat->locks++;
+		ilck->owner = pthread_self();
+		ilck->held = 1;
+		return;
+	}
+	r = pthread_mutex_trylock(&ilck->mtx);
+	assert(r == 0 || r == EBUSY);
+	if (r) {
+		ilck->stat->colls++;
+		if (cache_param->diag_bitmap & 0x8)
+			VSL(SLT_Debug, 0, "MTX_CONTEST(%s,%s,%d,%s)",
+			    p, f, l, ilck->w);
+		AZ(pthread_mutex_lock(&ilck->mtx));
+	} else if (cache_param->diag_bitmap & 0x8) {
+		VSL(SLT_Debug, 0, "MTX_LOCK(%s,%s,%d,%s)", p, f, l, ilck->w);
+	}
+	AZ(ilck->held);
+	ilck->stat->locks++;
+	ilck->owner = pthread_self();
+	ilck->held = 1;
+}
+
+void __match_proto__()
+Lck__Unlock(struct lock *lck, const char *p, const char *f, int l)
+{
+	struct ilck *ilck;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	assert(pthread_equal(ilck->owner, pthread_self()));
+	AN(ilck->held);
+	ilck->held = 0;
+	AZ(pthread_mutex_unlock(&ilck->mtx));
+	if (cache_param->diag_bitmap & 0x8)
+		VSL(SLT_Debug, 0, "MTX_UNLOCK(%s,%s,%d,%s)", p, f, l, ilck->w);
+}
+
+int __match_proto__()
+Lck__Trylock(struct lock *lck, const char *p, const char *f, int l)
+{
+	struct ilck *ilck;
+	int r;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	r = pthread_mutex_trylock(&ilck->mtx);
+	assert(r == 0 || r == EBUSY);
+	if (cache_param->diag_bitmap & 0x8)
+		VSL(SLT_Debug, 0,
+		    "MTX_TRYLOCK(%s,%s,%d,%s) = %d", p, f, l, ilck->w);
+	if (r == 0) {
+		AZ(ilck->held);
+		ilck->held = 1;
+		ilck->owner = pthread_self();
+	}
+	return (r);
+}
+
+void
+Lck__Assert(const struct lock *lck, int held)
+{
+	struct ilck *ilck;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	if (held)
+		assert(ilck->held &&
+		    pthread_equal(ilck->owner, pthread_self()));
+	else
+		assert(!ilck->held ||
+		    !pthread_equal(ilck->owner, pthread_self()));
+}
+
+int __match_proto__()
+Lck_CondWait(pthread_cond_t *cond, struct lock *lck, struct timespec *ts)
+{
+	struct ilck *ilck;
+	int retval = 0;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	AN(ilck->held);
+	assert(pthread_equal(ilck->owner, pthread_self()));
+	ilck->held = 0;
+	if (ts == NULL) {
+		AZ(pthread_cond_wait(cond, &ilck->mtx));
+	} else {
+		retval = pthread_cond_timedwait(cond, &ilck->mtx, ts);
+		assert(retval == 0 || retval == ETIMEDOUT);
+	}
+	AZ(ilck->held);
+	ilck->held = 1;
+	ilck->owner = pthread_self();
+	return (retval);
+}
+
+void
+Lck__New(struct lock *lck, struct VSC_C_lck *st, const char *w)
+{
+	struct ilck *ilck;
+
+	AN(st);
+	AZ(lck->priv);
+	ALLOC_OBJ(ilck, ILCK_MAGIC);
+	AN(ilck);
+	ilck->w = w;
+	ilck->stat = st;
+	ilck->stat->creat++;
+	AZ(pthread_mutex_init(&ilck->mtx, NULL));
+	AZ(pthread_mutex_lock(&lck_mtx));
+	VTAILQ_INSERT_TAIL(&ilck_head, ilck, list);
+	AZ(pthread_mutex_unlock(&lck_mtx));
+	lck->priv = ilck;
+}
+
+void
+Lck_Delete(struct lock *lck)
+{
+	struct ilck *ilck;
+
+	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
+	ilck->stat->destroy++;
+	lck->priv = NULL;
+	AZ(pthread_mutex_lock(&lck_mtx));
+	VTAILQ_REMOVE(&ilck_head, ilck, list);
+	AZ(pthread_mutex_unlock(&lck_mtx));
+	AZ(pthread_mutex_destroy(&ilck->mtx));
+	FREE_OBJ(ilck);
+}
+
+#define LOCK(nam) struct VSC_C_lck *lck_##nam;
+#include "tbl/locks.h"
+#undef LOCK
+
+void
+LCK_Init(void)
+{
+
+	AZ(pthread_mutex_init(&lck_mtx, NULL));
+#define LOCK(nam)						\
+	lck_##nam = VSM_Alloc(sizeof(struct VSC_C_lck),		\
+	   VSC_CLASS, VSC_TYPE_LCK, #nam);
+#include "tbl/locks.h"
+#undef LOCK
+}
diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c
new file mode 100644
index 0000000..eb3fa1d
--- /dev/null
+++ b/bin/varnishd/cache/cache_main.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2009 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 <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+#include "common/heritage.h"
+
+#include "waiter/cache_waiter.h"
+#include "hash/hash_slinger.h"
+
+volatile struct params	*cache_param;
+
+/*--------------------------------------------------------------------
+ * Per thread storage for the session currently being processed by
+ * the thread.  This is used for panic messages.
+ */
+
+static pthread_key_t sp_key;
+
+void
+THR_SetSession(const struct sess *sp)
+{
+
+	AZ(pthread_setspecific(sp_key, sp));
+}
+
+const struct sess *
+THR_GetSession(void)
+{
+
+	return (pthread_getspecific(sp_key));
+}
+
+/*--------------------------------------------------------------------
+ * Name threads if our pthreads implementation supports it.
+ */
+
+static pthread_key_t name_key;
+
+void
+THR_SetName(const char *name)
+{
+
+	AZ(pthread_setspecific(name_key, name));
+#ifdef HAVE_PTHREAD_SET_NAME_NP
+	pthread_set_name_np(pthread_self(), name);
+#endif
+}
+
+const char *
+THR_GetName(void)
+{
+
+	return (pthread_getspecific(name_key));
+}
+
+/*--------------------------------------------------------------------
+ * XXX: Think more about which order we start things
+ */
+
+void
+child_main(void)
+{
+
+	setbuf(stdout, NULL);
+	setbuf(stderr, NULL);
+	printf("Child starts\n");
+
+	AZ(pthread_key_create(&sp_key, NULL));
+	AZ(pthread_key_create(&name_key, NULL));
+
+	THR_SetName("cache-main");
+
+	VSL_Init();	/* First, LCK needs it. */
+
+	LCK_Init();	/* Second, locking */
+
+	WAIT_Init();
+	PAN_Init();
+	CLI_Init();
+	Fetch_Init();
+
+	CNT_Init();
+	VCL_Init();
+
+	HTTP_Init();
+
+	VBE_Init();
+	VBP_Init();
+	WRK_Init();
+	Pool_Init();
+
+	EXP_Init();
+	HSH_Init(heritage.hash);
+	BAN_Init();
+
+	VCA_Init();
+
+	SMS_Init();
+	SMP_Init();
+	STV_open();
+
+	VMOD_Init();
+
+	BAN_Compile();
+
+	/* Wait for persistent storage to load if asked to */
+	if (cache_param->diag_bitmap & 0x00020000)
+		SMP_Ready();
+
+	CLI_Run();
+
+	STV_close();
+
+	printf("Child dies\n");
+}
diff --git a/bin/varnishd/cache/cache_panic.c b/bin/varnishd/cache/cache_panic.c
new file mode 100644
index 0000000..c626ae3
--- /dev/null
+++ b/bin/varnishd/cache/cache_panic.c
@@ -0,0 +1,387 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2011 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Dag-Erling Smørgrav <des at des.no>
+ *
+ * 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"
+
+#ifndef HAVE_EXECINFO_H
+#include "compat/execinfo.h"
+#else
+#include <execinfo.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "vapi/vsm_int.h"
+
+#include "cache_backend.h"
+#include "waiter/cache_waiter.h"
+#include "vcl.h"
+
+/*
+ * The panic string is constructed in memory, then copied to the
+ * shared memory.
+ *
+ * It can be extracted post-mortem from a core dump using gdb:
+ *
+ * (gdb) printf "%s", panicstr
+ */
+
+static struct vsb vsps, *vsp;
+static pthread_mutex_t panicstr_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_ws(const struct ws *ws, int indent)
+{
+
+	VSB_printf(vsp, "%*sws = %p { %s\n", indent, "",
+	    ws, ws->overflow ? "overflow" : "");
+	VSB_printf(vsp, "%*sid = \"%s\",\n", indent + 2, "", ws->id);
+	VSB_printf(vsp, "%*s{s,f,r,e} = {%p", indent + 2, "", ws->s);
+	if (ws->f > ws->s)
+		VSB_printf(vsp, ",+%ld", (long) (ws->f - ws->s));
+	else
+		VSB_printf(vsp, ",%p", ws->f);
+	if (ws->r > ws->s)
+		VSB_printf(vsp, ",+%ld", (long) (ws->r - ws->s));
+	else
+		VSB_printf(vsp, ",%p", ws->r);
+	if (ws->e > ws->s)
+		VSB_printf(vsp, ",+%ld", (long) (ws->e - ws->s));
+	else
+		VSB_printf(vsp, ",%p", ws->e);
+	VSB_printf(vsp, "},\n");
+	VSB_printf(vsp, "%*s},\n", indent, "" );
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_vbc(const struct vbc *vbc)
+{
+
+	struct backend *be;
+
+	be = vbc->backend;
+
+	VSB_printf(vsp, "  backend = %p fd = %d {\n", be, vbc->fd);
+	VSB_printf(vsp, "    display_name = \"%s\",\n", be->display_name);
+	VSB_printf(vsp, "  },\n");
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_storage(const struct storage *st)
+{
+	int i, j;
+
+#define MAX_BYTES (4*16)
+#define show(ch) (((ch) > 31 && (ch) < 127) ? (ch) : '.')
+
+	VSB_printf(vsp, "      %u {\n", st->len);
+	for (i = 0; i < MAX_BYTES && i < st->len; i += 16) {
+		VSB_printf(vsp, "        ");
+		for (j = 0; j < 16; ++j) {
+			if (i + j < st->len)
+				VSB_printf(vsp, "%02x ", st->ptr[i + j]);
+			else
+				VSB_printf(vsp, "   ");
+		}
+		VSB_printf(vsp, "|");
+		for (j = 0; j < 16; ++j)
+			if (i + j < st->len)
+				VSB_printf(vsp, "%c", show(st->ptr[i + j]));
+		VSB_printf(vsp, "|\n");
+	}
+	if (st->len > MAX_BYTES)
+		VSB_printf(vsp, "        [%u more]\n", st->len - MAX_BYTES);
+	VSB_printf(vsp, "      },\n");
+
+#undef show
+#undef MAX_BYTES
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_http(const char *id, const struct http *h, int indent)
+{
+	int i;
+
+	VSB_printf(vsp, "%*shttp[%s] = {\n", indent, "", id);
+	VSB_printf(vsp, "%*sws = %p[%s]\n", indent + 2, "",
+	    h->ws, h->ws ? h->ws->id : "");
+	for (i = 0; i < h->nhd; ++i) {
+		if (h->hd[i].b == NULL && h->hd[i].e == NULL)
+			continue;
+		VSB_printf(vsp, "%*s\"%.*s\",\n", indent + 4, "",
+		    (int)(h->hd[i].e - h->hd[i].b),
+		    h->hd[i].b);
+	}
+	VSB_printf(vsp, "%*s},\n", indent, "");
+}
+
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_object(const struct object *o)
+{
+	const struct storage *st;
+
+	VSB_printf(vsp, "  obj = %p {\n", o);
+	VSB_printf(vsp, "    xid = %u,\n", o->xid);
+	pan_ws(o->ws_o, 4);
+	pan_http("obj", o->http, 4);
+	VSB_printf(vsp, "    len = %jd,\n", (intmax_t)o->len);
+	VSB_printf(vsp, "    store = {\n");
+	VTAILQ_FOREACH(st, &o->store, list)
+		pan_storage(st);
+	VSB_printf(vsp, "    },\n");
+	VSB_printf(vsp, "  },\n");
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_vcl(const struct VCL_conf *vcl)
+{
+	int i;
+
+	VSB_printf(vsp, "    vcl = {\n");
+	VSB_printf(vsp, "      srcname = {\n");
+	for (i = 0; i < vcl->nsrc; ++i)
+		VSB_printf(vsp, "        \"%s\",\n", vcl->srcname[i]);
+	VSB_printf(vsp, "      },\n");
+	VSB_printf(vsp, "    },\n");
+}
+
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_wrk(const struct worker *wrk)
+{
+
+	VSB_printf(vsp, "  worker = %p {\n", wrk);
+	pan_ws(wrk->ws, 4);
+	if (wrk->bereq->ws != NULL)
+		pan_http("bereq", wrk->bereq, 4);
+	if (wrk->beresp->ws != NULL)
+		pan_http("beresp", wrk->beresp, 4);
+	if (wrk->resp->ws != NULL)
+		pan_http("resp", wrk->resp, 4);
+	VSB_printf(vsp, "    },\n");
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_sess(const struct sess *sp)
+{
+	const char *stp, *hand;
+
+	VSB_printf(vsp, "sp = %p {\n", sp);
+	VSB_printf(vsp,
+	    "  fd = %d, id = %d, xid = %u,\n",
+	    sp->fd, sp->vsl_id & VSL_IDENTMASK, sp->xid);
+	VSB_printf(vsp, "  client = %s %s,\n",
+	    sp->addr ? sp->addr : "?.?.?.?",
+	    sp->port ? sp->port : "?");
+	switch (sp->step) {
+#define STEP(l, u) case STP_##u: stp = "STP_" #u; break;
+#include "tbl/steps.h"
+#undef STEP
+		default: stp = NULL;
+	}
+	hand = VCL_Return_Name(sp->handling);
+	if (stp != NULL)
+		VSB_printf(vsp, "  step = %s,\n", stp);
+	else
+		VSB_printf(vsp, "  step = 0x%x,\n", sp->step);
+	if (hand != NULL)
+		VSB_printf(vsp, "  handling = %s,\n", hand);
+	else
+		VSB_printf(vsp, "  handling = 0x%x,\n", sp->handling);
+	if (sp->err_code)
+		VSB_printf(vsp,
+		    "  err_code = %d, err_reason = %s,\n", sp->err_code,
+		    sp->err_reason ? sp->err_reason : "(null)");
+
+	VSB_printf(vsp, "  restarts = %d, esi_level = %d\n",
+	    sp->restarts, sp->esi_level);
+
+	VSB_printf(vsp, "  flags = ");
+	if (sp->wrk->do_stream)	VSB_printf(vsp, " do_stream");
+	if (sp->wrk->do_gzip)	VSB_printf(vsp, " do_gzip");
+	if (sp->wrk->do_gunzip)	VSB_printf(vsp, " do_gunzip");
+	if (sp->wrk->do_esi)	VSB_printf(vsp, " do_esi");
+	if (sp->wrk->do_close)	VSB_printf(vsp, " do_close");
+	if (sp->wrk->is_gzip)	VSB_printf(vsp, " is_gzip");
+	if (sp->wrk->is_gunzip)	VSB_printf(vsp, " is_gunzip");
+	VSB_printf(vsp, "\n");
+	VSB_printf(vsp, "  bodystatus = %d\n", sp->wrk->body_status);
+
+	pan_ws(sp->ws, 2);
+	pan_http("req", sp->http, 2);
+
+	if (sp->wrk != NULL)
+		pan_wrk(sp->wrk);
+
+	if (VALID_OBJ(sp->vcl, VCL_CONF_MAGIC))
+		pan_vcl(sp->vcl);
+
+	if (VALID_OBJ(sp->wrk->vbc, BACKEND_MAGIC))
+		pan_vbc(sp->wrk->vbc);
+
+	if (VALID_OBJ(sp->obj, OBJECT_MAGIC))
+		pan_object(sp->obj);
+
+	VSB_printf(vsp, "},\n");
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_backtrace(void)
+{
+	void *array[10];
+	size_t size;
+	size_t i;
+
+	size = backtrace (array, 10);
+	if (size == 0)
+		return;
+	VSB_printf(vsp, "Backtrace:\n");
+	for (i = 0; i < size; i++) {
+		VSB_printf (vsp, "  ");
+		if (Symbol_Lookup(vsp, array[i]) < 0) {
+			char **strings;
+			strings = backtrace_symbols(&array[i], 1);
+			if (strings != NULL && strings[0] != NULL)
+				VSB_printf(vsp, "%p: %s", array[i], strings[0]);
+			else
+				VSB_printf(vsp, "%p: (?)", array[i]);
+		}
+		VSB_printf (vsp, "\n");
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+pan_ic(const char *func, const char *file, int line, const char *cond,
+    int err, int xxx)
+{
+	const char *q;
+	const struct sess *sp;
+
+	AZ(pthread_mutex_lock(&panicstr_mtx)); /* Won't be released,
+						  we're going to die
+						  anyway */
+	switch(xxx) {
+	case 3:
+		VSB_printf(vsp,
+		    "Wrong turn at %s:%d:\n%s\n", file, line, cond);
+		break;
+	case 2:
+		VSB_printf(vsp,
+		    "Panic from VCL:\n  %s\n", cond);
+		break;
+	case 1:
+		VSB_printf(vsp,
+		    "Missing errorhandling code in %s(), %s line %d:\n"
+		    "  Condition(%s) not true.",
+		    func, file, line, cond);
+		break;
+	default:
+	case 0:
+		VSB_printf(vsp,
+		    "Assert error in %s(), %s line %d:\n"
+		    "  Condition(%s) not true.\n",
+		    func, file, line, cond);
+		break;
+	}
+	if (err)
+		VSB_printf(vsp, "errno = %d (%s)\n", err, strerror(err));
+
+	q = THR_GetName();
+	if (q != NULL)
+		VSB_printf(vsp, "thread = (%s)\n", q);
+
+	VSB_printf(vsp, "ident = %s,%s\n",
+	    VSB_data(vident) + 1, WAIT_GetName());
+
+	pan_backtrace();
+
+	if (!(cache_param->diag_bitmap & 0x2000)) {
+		sp = THR_GetSession();
+		if (sp != NULL)
+			pan_sess(sp);
+	}
+	VSB_printf(vsp, "\n");
+	VSB_bcat(vsp, "", 1);	/* NUL termination */
+
+	if (cache_param->diag_bitmap & 0x4000)
+		(void)fputs(VSM_head->panicstr, stderr);
+
+#ifdef HAVE_ABORT2
+	if (cache_param->diag_bitmap & 0x8000) {
+		void *arg[1];
+		char *p;
+
+		for (p = VSM_head->panicstr; *p; p++)
+			if (*p == '\n')
+				*p = ' ';
+		arg[0] = VSM_head->panicstr;
+		abort2(VSM_head->panicstr, 1, arg);
+	}
+#endif
+	if (cache_param->diag_bitmap & 0x1000)
+		exit(4);
+	else
+		abort();
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+PAN_Init(void)
+{
+
+	VAS_Fail = pan_ic;
+	vsp = &vsps;
+	AN(VSB_new(vsp, VSM_head->panicstr, sizeof VSM_head->panicstr,
+	    VSB_FIXEDLEN));
+}
diff --git a/bin/varnishd/cache/cache_pipe.c b/bin/varnishd/cache/cache_pipe.c
new file mode 100644
index 0000000..4180d39
--- /dev/null
+++ b/bin/varnishd/cache/cache_pipe.c
@@ -0,0 +1,133 @@
+/*-
+ * 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 "vtcp.h"
+#include "vtim.h"
+
+static int
+rdf(int fd0, int fd1)
+{
+	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);
+		if (i != j)
+			(void)usleep(100000);		/* XXX hack */
+	}
+	return (0);
+}
+
+void
+PipeSession(struct sess *sp)
+{
+	struct vbc *vc;
+	struct worker *w;
+	struct pollfd fds[2];
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
+	w = sp->wrk;
+
+	sp->wrk->vbc = VDI_GetFd(NULL, sp);
+	if (sp->wrk->vbc == NULL)
+		return;
+	vc = sp->wrk->vbc;
+	(void)VTCP_blocking(vc->fd);
+
+	WRW_Reserve(w, &vc->fd);
+	sp->wrk->acct_tmp.hdrbytes +=
+	    http_Write(w, sp->vsl_id, sp->wrk->bereq, 0);
+
+	if (sp->htc->pipeline.b != NULL)
+		sp->wrk->acct_tmp.bodybytes +=
+		    WRW_Write(w, sp->htc->pipeline.b, Tlen(sp->htc->pipeline));
+
+	i = WRW_FlushRelease(w);
+
+	if (i) {
+		SES_Close(sp, "pipe");
+		VDI_CloseFd(sp->wrk);
+		return;
+	}
+
+	sp->t_resp = VTIM_real();
+
+	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(sp->fd, 0);
+	fds[1].fd = 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, cache_param->pipe_timeout * 1000);
+		if (i < 1)
+			break;
+		if (fds[0].revents && rdf(vc->fd, sp->fd)) {
+			if (fds[1].fd == -1)
+				break;
+			(void)shutdown(vc->fd, SHUT_RD);
+			(void)shutdown(sp->fd, SHUT_WR);
+			fds[0].events = 0;
+			fds[0].fd = -1;
+		}
+		if (fds[1].revents && rdf(sp->fd, vc->fd)) {
+			if (fds[0].fd == -1)
+				break;
+			(void)shutdown(sp->fd, SHUT_RD);
+			(void)shutdown(vc->fd, SHUT_WR);
+			fds[1].events = 0;
+			fds[1].fd = -1;
+		}
+	}
+	SES_Close(sp, "pipe");
+	VDI_CloseFd(sp->wrk);
+}
diff --git a/bin/varnishd/cache/cache_pool.c b/bin/varnishd/cache/cache_pool.c
new file mode 100644
index 0000000..79a5fcd
--- /dev/null
+++ b/bin/varnishd/cache/cache_pool.c
@@ -0,0 +1,594 @@
+/*-
+ * 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.
+ *
+ * We maintain a number of worker thread pools, to spread lock contention.
+ *
+ * Pools can be added on the fly, as a means to mitigate lock contention,
+ * but can only be removed again by a restart. (XXX: we could fix that)
+ *
+ * Two threads herd the pools, one eliminates idle threads and aggregates
+ * statistics for all the pools, the other thread creates new threads
+ * on demand, subject to various numerical constraints.
+ *
+ * The algorithm for when to create threads needs to be reactive enough
+ * to handle startup spikes, but sufficiently attenuated to not cause
+ * thread pileups.  This remains subject for improvement.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "cache.h"
+#include "common/heritage.h"
+
+#include "waiter/cache_waiter.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+/*--------------------------------------------------------------------
+ * MAC OS/X is incredibly moronic when it comes to time and such...
+ */
+
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC	0
+
+#include <sys/time.h>
+
+static int
+clock_gettime(int foo, struct timespec *ts)
+{
+	struct timeval tv;
+
+	(void)foo;
+	gettimeofday(&tv, NULL);
+	ts->tv_sec = tv.tv_sec;
+	ts->tv_nsec = tv.tv_usec * 1000;
+	return (0);
+}
+
+static int
+pthread_condattr_setclock(pthread_condattr_t *attr, int foo)
+{
+	(void)attr;
+	(void)foo;
+	return (0);
+}
+#endif /* !CLOCK_MONOTONIC */
+
+static void *waiter_priv;
+
+VTAILQ_HEAD(workerhead, worker);
+
+struct poolsock {
+	unsigned			magic;
+#define POOLSOCK_MAGIC			0x1b0a2d38
+	VTAILQ_ENTRY(poolsock)		list;
+	struct listen_sock		*lsock;
+};
+
+/* Number of work requests queued in excess of worker threads available */
+
+struct pool {
+	unsigned		magic;
+#define POOL_MAGIC		0x606658fa
+	VTAILQ_ENTRY(pool)	list;
+
+	pthread_cond_t		herder_cond;
+	struct lock		herder_mtx;
+	pthread_t		herder_thr;
+
+	struct lock		mtx;
+	struct workerhead	idle;
+	VTAILQ_HEAD(, sess)	queue;
+	VTAILQ_HEAD(, poolsock)	socks;
+	unsigned		nthr;
+	unsigned		lqueue;
+	unsigned		last_lqueue;
+	uintmax_t		ndropped;
+	uintmax_t		nqueued;
+	struct sesspool		*sesspool;
+};
+
+static struct lock		pool_mtx;
+static pthread_t		thr_pool_herder;
+
+/*--------------------------------------------------------------------
+ * Nobody is accepting on this socket, so we do.
+ *
+ * As long as we can stick the accepted connection to another thread
+ * we do so, otherwise we return and handle it ourselves.
+ *
+ * Notice calling convention:  Called locked and returns locked, but
+ * works lock in the meantime.
+ *
+ * We store data about the accept in reserved workspace, it is only used
+ * for a brief moment and it takes up around 144 bytes.
+ */
+
+static int
+pool_accept(struct pool *pp, struct worker *w, const struct poolsock *ps)
+{
+	struct worker *w2;
+	struct wrk_accept *wa, *wa2;
+
+	CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(ps, POOLSOCK_MAGIC);
+
+	CHECK_OBJ_NOTNULL(ps->lsock, LISTEN_SOCK_MAGIC);
+	Lck_AssertHeld(&pp->mtx);
+	Lck_Unlock(&pp->mtx);
+	assert(sizeof *wa == WS_Reserve(w->ws, sizeof *wa));
+	wa = (void*)w->ws->f;
+	while (1) {
+		memset(wa, 0, sizeof *wa);
+		wa->magic = WRK_ACCEPT_MAGIC;
+
+		if (ps->lsock->sock < 0) {
+			/* Socket Shutdown */
+			Lck_Lock(&pp->mtx);
+			return (-1);
+		}
+		if (VCA_Accept(ps->lsock, wa) < 0) {
+			w->stats.sess_fail++;
+			/* We're going to pace in vca anyway... */
+			(void)WRK_TrySumStat(w);
+			continue;
+		}
+
+		Lck_Lock(&pp->mtx);
+		if (VTAILQ_EMPTY(&pp->idle))
+			return (0);
+		w2 = VTAILQ_FIRST(&pp->idle);
+		VTAILQ_REMOVE(&pp->idle, w2, list);
+		Lck_Unlock(&pp->mtx);
+		assert(sizeof *wa2 == WS_Reserve(w2->ws, sizeof *wa2));
+		wa2 = (void*)w2->ws->f;
+		memcpy(wa2, wa, sizeof *wa);
+		AZ(pthread_cond_signal(&w2->cond));
+	}
+}
+
+/*--------------------------------------------------------------------
+ * This is the work function for worker threads in the pool.
+ */
+
+void
+Pool_Work_Thread(void *priv, struct worker *w)
+{
+	struct pool *pp;
+	int stats_clean, i;
+	struct poolsock *ps;
+
+	CAST_OBJ_NOTNULL(pp, priv, POOL_MAGIC);
+	w->pool = pp;
+	Lck_Lock(&pp->mtx);
+	stats_clean = 1;
+	while (1) {
+
+		Lck_AssertHeld(&pp->mtx);
+
+		CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+		CHECK_OBJ_NOTNULL(w->bereq, HTTP_MAGIC);
+		CHECK_OBJ_NOTNULL(w->beresp, HTTP_MAGIC);
+		CHECK_OBJ_NOTNULL(w->resp, HTTP_MAGIC);
+
+		WS_Reset(w->ws, NULL);
+
+		w->sp = VTAILQ_FIRST(&pp->queue);
+		if (w->sp != NULL) {
+			/* Process queued requests, if any */
+			assert(pp->lqueue > 0);
+			VTAILQ_REMOVE(&pp->queue, w->sp, poollist);
+			pp->lqueue--;
+		} else if (!VTAILQ_EMPTY(&pp->socks)) {
+			/* Accept on a socket */
+			ps = VTAILQ_FIRST(&pp->socks);
+			VTAILQ_REMOVE(&pp->socks, ps, list);
+			i = pool_accept(pp, w, ps);
+			Lck_AssertHeld(&pp->mtx);
+			if (i < 0) {
+				/* Socket Shutdown */
+				FREE_OBJ(ps);
+				WS_Release(w->ws, 0);
+				continue;
+			}
+			VTAILQ_INSERT_TAIL(&pp->socks, ps, list);
+		} else if (VTAILQ_EMPTY(&pp->socks)) {
+			/* Nothing to do: To sleep, perchance to dream ... */
+			if (isnan(w->lastused))
+				w->lastused = VTIM_real();
+			VTAILQ_INSERT_HEAD(&pp->idle, w, list);
+			if (!stats_clean)
+				WRK_SumStat(w);
+			(void)Lck_CondWait(&w->cond, &pp->mtx, NULL);
+		}
+
+		/*
+		 * If we got neither session or accepted a socket, we were
+		 * woken up to die to cull the herd.
+		 */
+		if (w->sp == NULL && w->ws->r == NULL)
+			break;
+
+		Lck_Unlock(&pp->mtx);
+
+		if (w->sp == NULL) {
+			/* Turn accepted socket into a session */
+			assert(w->ws->r != NULL);
+			w->sp = SES_New(w, pp->sesspool);
+			if (w->sp == NULL)
+				VCA_FailSess(w);
+			else
+				VCA_SetupSess(w);
+			WS_Release(w->ws, 0);
+		}
+		assert(w->ws->r == NULL);
+
+		if (w->sp != NULL) {
+			CHECK_OBJ_NOTNULL(w->sp, SESS_MAGIC);
+
+			stats_clean = 0;
+			w->lastused = NAN;
+			w->storage_hint = NULL;
+
+			AZ(w->sp->wrk);
+			THR_SetSession(w->sp);
+			w->sp->wrk = w;
+			CNT_Session(w->sp);
+			THR_SetSession(NULL);
+			w->sp = NULL;
+
+			WS_Assert(w->ws);
+			AZ(w->bereq->ws);
+			AZ(w->beresp->ws);
+			AZ(w->resp->ws);
+			AZ(w->wrw.wfd);
+			AZ(w->storage_hint);
+			assert(w->wlp == w->wlb);
+			if (cache_param->diag_bitmap & 0x00040000) {
+				if (w->vcl != NULL)
+					VCL_Rel(&w->vcl);
+			}
+		}
+		stats_clean = WRK_TrySumStat(w);
+		Lck_Lock(&pp->mtx);
+	}
+	Lck_Unlock(&pp->mtx);
+	w->pool = NULL;
+}
+
+/*--------------------------------------------------------------------
+ * Queue a workrequest if possible.
+ *
+ * Return zero if the request was queued, negative if it wasn't.
+ */
+
+static int
+pool_queue(struct pool *pp, struct sess *sp)
+{
+	struct worker *w;
+
+	Lck_Lock(&pp->mtx);
+
+	/* If there are idle threads, we tickle the first one into action */
+	w = VTAILQ_FIRST(&pp->idle);
+	if (w != NULL) {
+		VTAILQ_REMOVE(&pp->idle, w, list);
+		Lck_Unlock(&pp->mtx);
+		w->sp = sp;
+		AZ(pthread_cond_signal(&w->cond));
+		return (0);
+	}
+
+	/* If we have too much in the queue already, refuse. */
+	if (pp->lqueue > (cache_param->queue_max * pp->nthr) / 100) {
+		pp->ndropped++;
+		Lck_Unlock(&pp->mtx);
+		return (-1);
+	}
+
+	VTAILQ_INSERT_TAIL(&pp->queue, sp, poollist);
+	pp->nqueued++;
+	pp->lqueue++;
+	Lck_Unlock(&pp->mtx);
+	AZ(pthread_cond_signal(&pp->herder_cond));
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+Pool_Schedule(struct pool *pp, struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	AZ(sp->wrk);
+	if (pool_queue(pp, sp) == 0)
+		return(0);
+
+	VSC_C_main->client_drop_late++;
+
+	/*
+	 * Couldn't queue it -- kill it.
+	 *
+	 * XXX: a notice might be polite, but would potentially
+	 * XXX: sleep whichever thread got us here
+	 */
+	sp->t_end = VTIM_real();
+	if (sp->vcl != NULL) {
+		/*
+		 * A session parked on a busy object can come here
+		 * after it wakes up.  Loose the VCL reference.
+		 */
+		VCL_Rel(&sp->vcl);
+	}
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Wait for another request
+ */
+
+void
+Pool_Wait(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	AZ(sp->obj);
+	AZ(sp->vcl);
+	assert(sp->fd >= 0);
+	/*
+	 * Set nonblocking in the worker-thread, before passing to the
+	 * acceptor thread, to reduce syscall density of the latter.
+	 */
+	if (VTCP_nonblocking(sp->fd))
+		SES_Close(sp, "remote closed");
+	waiter->pass(waiter_priv, sp);
+}
+
+/*--------------------------------------------------------------------
+ * Create another thread, if necessary & possible
+ */
+
+static void
+pool_breed(struct pool *qp, const pthread_attr_t *tp_attr)
+{
+	pthread_t tp;
+
+	/*
+	 * If we need more threads, and have space, create
+	 * one more thread.
+	 */
+	if (qp->nthr < cache_param->wthread_min ||	/* Not enough threads yet */
+	    (qp->lqueue > cache_param->wthread_add_threshold && /* more needed */
+	    qp->lqueue > qp->last_lqueue)) {	/* not getting better since last */
+		if (qp->nthr > cache_param->wthread_max) {
+			Lck_Lock(&pool_mtx);
+			VSC_C_main->threads_limited++;
+			Lck_Unlock(&pool_mtx);
+		} else if (pthread_create(&tp, tp_attr, WRK_thread, qp)) {
+			VSL(SLT_Debug, 0, "Create worker thread failed %d %s",
+			    errno, strerror(errno));
+			Lck_Lock(&pool_mtx);
+			VSC_C_main->threads_limited++;
+			Lck_Unlock(&pool_mtx);
+			VTIM_sleep(cache_param->wthread_fail_delay * 1e-3);
+		} else {
+			AZ(pthread_detach(tp));
+			VTIM_sleep(cache_param->wthread_add_delay * 1e-3);
+			qp->nthr++;
+			Lck_Lock(&pool_mtx);
+			VSC_C_main->threads++;
+			VSC_C_main->threads_created++;
+			Lck_Unlock(&pool_mtx);
+		}
+	}
+	qp->last_lqueue = qp->lqueue;
+}
+
+/*--------------------------------------------------------------------
+ * Herd a single pool
+ *
+ * This thread wakes up whenever a pool queues.
+ *
+ * The trick here is to not be too aggressive about creating threads.
+ * We do this by only examining one pool at a time, and by sleeping
+ * a short while whenever we create a thread and a little while longer
+ * whenever we fail to, hopefully missing a lot of cond_signals in
+ * the meantime.
+ *
+ * XXX: probably need a lot more work.
+ *
+ */
+
+static void*
+pool_herder(void *priv)
+{
+	struct pool *pp;
+	pthread_attr_t tp_attr;
+	struct timespec ts;
+	double t_idle;
+	struct worker *w;
+	int i;
+
+	CAST_OBJ_NOTNULL(pp, priv, POOL_MAGIC);
+	AZ(pthread_attr_init(&tp_attr));
+
+	while (1) {
+		/* Set the stacksize for worker threads we create */
+		if (cache_param->wthread_stacksize != UINT_MAX)
+			AZ(pthread_attr_setstacksize(&tp_attr,
+			    cache_param->wthread_stacksize));
+		else {
+			AZ(pthread_attr_destroy(&tp_attr));
+			AZ(pthread_attr_init(&tp_attr));
+		}
+
+		pool_breed(pp, &tp_attr);
+
+		if (pp->nthr < cache_param->wthread_min)
+			continue;
+
+		AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
+		ts.tv_sec += cache_param->wthread_purge_delay / 1000;
+		ts.tv_nsec +=
+		    (cache_param->wthread_purge_delay % 1000) * 1000000;
+		if (ts.tv_nsec >= 1000000000) {
+			ts.tv_sec++;
+			ts.tv_nsec -= 1000000000;
+		}
+
+		Lck_Lock(&pp->herder_mtx);
+		i = Lck_CondWait(&pp->herder_cond, &pp->herder_mtx, &ts);
+		Lck_Unlock(&pp->herder_mtx);
+		if (!i)
+			continue;
+
+		if (pp->nthr <= cache_param->wthread_min)
+			continue;
+
+		t_idle = VTIM_real() - cache_param->wthread_timeout;
+
+		Lck_Lock(&pp->mtx);
+		VSC_C_main->sess_queued += pp->nqueued;
+		VSC_C_main->sess_dropped += pp->ndropped;
+		pp->nqueued = pp->ndropped = 0;
+		w = VTAILQ_LAST(&pp->idle, workerhead);
+		if (w != NULL &&
+		    (w->lastused < t_idle || pp->nthr > cache_param->wthread_max)) {
+			VTAILQ_REMOVE(&pp->idle, w, list);
+		} else
+			w = NULL;
+		Lck_Unlock(&pp->mtx);
+
+		/* And give it a kiss on the cheek... */
+		if (w != NULL) {
+			pp->nthr--;
+			Lck_Lock(&pool_mtx);
+			VSC_C_main->threads--;
+			VSC_C_main->threads_destroyed++;
+			Lck_Unlock(&pool_mtx);
+			AZ(w->sp);
+			AZ(pthread_cond_signal(&w->cond));
+		}
+	}
+	NEEDLESS_RETURN(NULL);
+}
+
+/*--------------------------------------------------------------------
+ * Add a thread pool
+ */
+
+static struct pool *
+pool_mkpool(void)
+{
+	struct pool *pp;
+	struct listen_sock *ls;
+	struct poolsock *ps;
+	pthread_condattr_t cv_attr;
+
+	ALLOC_OBJ(pp, POOL_MAGIC);
+	XXXAN(pp);
+	Lck_New(&pp->mtx, lck_wq);
+
+	VTAILQ_INIT(&pp->queue);
+	VTAILQ_INIT(&pp->idle);
+	VTAILQ_INIT(&pp->socks);
+	pp->sesspool = SES_NewPool(pp);
+	AN(pp->sesspool);
+
+	VTAILQ_FOREACH(ls, &heritage.socks, list) {
+		if (ls->sock < 0)
+			continue;
+		ALLOC_OBJ(ps, POOLSOCK_MAGIC);
+		XXXAN(ps);
+		ps->lsock = ls;
+		VTAILQ_INSERT_TAIL(&pp->socks, ps, list);
+	}
+
+	AZ(pthread_condattr_init(&cv_attr));
+	AZ(pthread_condattr_setclock(&cv_attr, CLOCK_MONOTONIC));
+	AZ(pthread_cond_init(&pp->herder_cond, &cv_attr));
+	AZ(pthread_condattr_destroy(&cv_attr));
+	Lck_New(&pp->herder_mtx, lck_herder);
+	AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
+
+	return (pp);
+}
+
+/*--------------------------------------------------------------------
+ * This thread adjusts the number of pools to match the parameter.
+ *
+ */
+
+static void *
+pool_poolherder(void *priv)
+{
+	unsigned nwq;
+	VTAILQ_HEAD(,pool)	pools = VTAILQ_HEAD_INITIALIZER(pools);
+	struct pool *pp;
+	uint64_t u;
+
+	THR_SetName("pool_herder");
+	(void)priv;
+
+	nwq = 0;
+	while (1) {
+		if (nwq < cache_param->wthread_pools) {
+			pp = pool_mkpool();
+			if (pp != NULL) {
+				VTAILQ_INSERT_TAIL(&pools, pp, list);
+				VSC_C_main->pools++;
+				nwq++;
+				continue;
+			}
+		}
+		/* XXX: remove pools */
+		if (0)
+			SES_DeletePool(NULL, NULL);
+		(void)sleep(1);
+		u = 0;
+		VTAILQ_FOREACH(pp, &pools, list)
+			u += pp->lqueue;
+		VSC_C_main->thread_queue_len = u;
+	}
+	NEEDLESS_RETURN(NULL);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+Pool_Init(void)
+{
+
+	waiter_priv = waiter->init();
+	Lck_New(&pool_mtx, lck_wq);
+	AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
+}
diff --git a/bin/varnishd/cache/cache_response.c b/bin/varnishd/cache/cache_response.c
new file mode 100644
index 0000000..487a514
--- /dev/null
+++ b/bin/varnishd/cache/cache_response.c
@@ -0,0 +1,427 @@
+/*-
+ * 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.
+ */
+
+#include "config.h"
+
+#include "cache.h"
+
+#include "vct.h"
+#include "vtim.h"
+
+/*--------------------------------------------------------------------*/
+
+static void
+res_dorange(const struct sess *sp, const char *r, ssize_t *plow, ssize_t *phigh)
+{
+	ssize_t low, high, has_low;
+
+	assert(sp->obj->response == 200);
+	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 >= sp->obj->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 = sp->obj->len - high;
+			high = sp->obj->len - 1;
+		}
+	} else
+		high = sp->obj->len - 1;
+	if (*r != '\0')
+		return;
+
+	if (high >= sp->obj->len)
+		high = sp->obj->len - 1;
+
+	if (low > high)
+		return;
+
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+	    "Content-Range: bytes %jd-%jd/%jd",
+	    (intmax_t)low, (intmax_t)high, (intmax_t)sp->obj->len);
+	http_Unset(sp->wrk->resp, H_Content_Length);
+	assert(sp->wrk->res_mode & RES_LEN);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+	    "Content-Length: %jd", (intmax_t)(1 + high - low));
+	http_SetResp(sp->wrk->resp, "HTTP/1.1", 206, "Partial Content");
+
+	*plow = low;
+	*phigh = high;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+RES_BuildHttp(const struct sess *sp)
+{
+	char time_str[30];
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+
+	http_ClrHeader(sp->wrk->resp);
+	sp->wrk->resp->logtag = HTTP_Tx;
+	http_CopyResp(sp->wrk->resp, sp->obj->http);
+	http_FilterFields(sp->wrk, sp->vsl_id, sp->wrk->resp, sp->obj->http,
+	    HTTPH_A_DELIVER);
+
+	if (!(sp->wrk->res_mode & RES_LEN)) {
+		http_Unset(sp->wrk->resp, H_Content_Length);
+	} else if (cache_param->http_range_support) {
+		/* We only accept ranges if we know the length */
+		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+		    "Accept-Ranges: bytes");
+	}
+
+	if (sp->wrk->res_mode & RES_CHUNKED)
+		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+		    "Transfer-Encoding: chunked");
+
+	VTIM_format(VTIM_real(), time_str);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Date: %s", time_str);
+
+	if (sp->xid != sp->obj->xid)
+		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+		    "X-Varnish: %u %u", sp->xid, sp->obj->xid);
+	else
+		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+		    "X-Varnish: %u", sp->xid);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Age: %.0f",
+	    sp->obj->exp.age + sp->t_resp - sp->obj->exp.entered);
+	http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Via: 1.1 varnish");
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Connection: %s",
+	    sp->doclose ? "close" : "keep-alive");
+}
+
+/*--------------------------------------------------------------------
+ * We have a gzip'ed object and need to ungzip it for a client which
+ * does not understand gzip.
+ * XXX: handle invalid gzip data better (how ?)
+ */
+
+static void
+res_WriteGunzipObj(const struct sess *sp)
+{
+	struct storage *st;
+	unsigned u = 0;
+	struct vgz *vg;
+	char obuf[cache_param->gzip_stack_buffer];
+	ssize_t obufl = 0;
+	int i;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	vg = VGZ_NewUngzip(sp->wrk, "U D -");
+
+	VGZ_Obuf(vg, obuf, sizeof obuf);
+	VTAILQ_FOREACH(st, &sp->obj->store, list) {
+		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
+		u += st->len;
+
+		VSC_C_main->n_objwrite++;
+
+		i = VGZ_WrwGunzip(sp->wrk, vg,
+		    st->ptr, st->len,
+		    obuf, sizeof obuf, &obufl);
+		/* XXX: error check */
+		(void)i;
+	}
+	if (obufl) {
+		(void)WRW_Write(sp->wrk, obuf, obufl);
+		(void)WRW_Flush(sp->wrk);
+	}
+	(void)VGZ_Destroy(&vg, sp->vsl_id);
+	assert(u == sp->obj->len);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+res_WriteDirObj(const struct sess *sp, ssize_t low, ssize_t high)
+{
+	ssize_t u = 0;
+	size_t ptr, off, len;
+	struct storage *st;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	ptr = 0;
+	VTAILQ_FOREACH(st, &sp->obj->store, list) {
+		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
+		u += st->len;
+		len = st->len;
+		off = 0;
+		if (ptr + len <= low) {
+			/* This segment is too early */
+			ptr += len;
+			continue;
+		}
+		if (ptr < low) {
+			/* Chop front of segment off */
+			off += (low - ptr);
+			len -= (low - ptr);
+			ptr += (low - ptr);
+		}
+		if (ptr + len > high)
+			/* Chop tail of segment off */
+			len = 1 + high - ptr;
+
+		ptr += len;
+
+		sp->wrk->acct_tmp.bodybytes += len;
+#ifdef SENDFILE_WORKS
+		/*
+		 * XXX: the overhead of setting up sendfile is not
+		 * XXX: epsilon and maybe not even delta, so avoid
+		 * XXX: engaging sendfile for small objects.
+		 * XXX: Should use getpagesize() ?
+		 */
+		if (st->fd >= 0 &&
+		    st->len >= cache_param->sendfile_threshold) {
+			VSC_C_main->n_objsendfile++;
+			WRW_Sendfile(sp->wrk, st->fd, st->where + off, len);
+			continue;
+		}
+#endif /* SENDFILE_WORKS */
+		VSC_C_main->n_objwrite++;
+		(void)WRW_Write(sp->wrk, st->ptr + off, len);
+	}
+	assert(u == sp->obj->len);
+}
+
+/*--------------------------------------------------------------------
+ * Deliver an object.
+ * Attempt optimizations like 304 and 206 here.
+ */
+
+void
+RES_WriteObj(struct sess *sp)
+{
+	char *r;
+	ssize_t low, high;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	WRW_Reserve(sp->wrk, &sp->fd);
+
+	if (sp->obj->response == 200 &&
+	    sp->http->conds &&
+	    RFC2616_Do_Cond(sp)) {
+		sp->wantbody = 0;
+		http_SetResp(sp->wrk->resp, "HTTP/1.1", 304, "Not Modified");
+		http_Unset(sp->wrk->resp, H_Content_Length);
+		http_Unset(sp->wrk->resp, H_Transfer_Encoding);
+	}
+
+	/*
+	 * If nothing special planned, we can attempt Range support
+	 */
+	low = 0;
+	high = sp->obj->len - 1;
+	if (
+	    sp->wantbody &&
+	    (sp->wrk->res_mode & RES_LEN) &&
+	    !(sp->wrk->res_mode & (RES_ESI|RES_ESI_CHILD|RES_GUNZIP)) &&
+	    cache_param->http_range_support &&
+	    sp->obj->response == 200 &&
+	    http_GetHdr(sp->http, H_Range, &r))
+		res_dorange(sp, r, &low, &high);
+
+	/*
+	 * Always remove C-E if client don't grok it
+	 */
+	if (sp->wrk->res_mode & RES_GUNZIP)
+		http_Unset(sp->wrk->resp, H_Content_Encoding);
+
+	/*
+	 * Send HTTP protocol header, unless interior ESI object
+	 */
+	if (!(sp->wrk->res_mode & RES_ESI_CHILD))
+		sp->wrk->acct_tmp.hdrbytes +=
+		    http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
+
+	if (!sp->wantbody)
+		sp->wrk->res_mode &= ~RES_CHUNKED;
+
+	if (sp->wrk->res_mode & RES_CHUNKED)
+		WRW_Chunked(sp->wrk);
+
+	if (!sp->wantbody) {
+		/* This was a HEAD or conditional request */
+	} else if (sp->obj->len == 0) {
+		/* Nothing to do here */
+	} else if (sp->wrk->res_mode & RES_ESI) {
+		ESI_Deliver(sp);
+	} else if (sp->wrk->res_mode & RES_ESI_CHILD && sp->wrk->gzip_resp) {
+		ESI_DeliverChild(sp);
+	} else if (sp->wrk->res_mode & RES_ESI_CHILD &&
+	    !sp->wrk->gzip_resp && sp->obj->gziped) {
+		res_WriteGunzipObj(sp);
+	} else if (sp->wrk->res_mode & RES_GUNZIP) {
+		res_WriteGunzipObj(sp);
+	} else {
+		res_WriteDirObj(sp, low, high);
+	}
+
+	if (sp->wrk->res_mode & RES_CHUNKED &&
+	    !(sp->wrk->res_mode & RES_ESI_CHILD))
+		WRW_EndChunk(sp->wrk);
+
+	if (WRW_FlushRelease(sp->wrk) && sp->fd >= 0)
+		SES_Close(sp, "remote closed");
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+RES_StreamStart(struct sess *sp)
+{
+	struct stream_ctx *sctx;
+
+	sctx = sp->wrk->sctx;
+	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
+
+	AZ(sp->wrk->res_mode & RES_ESI_CHILD);
+	AN(sp->wantbody);
+
+	WRW_Reserve(sp->wrk, &sp->fd);
+	/*
+	 * Always remove C-E if client don't grok it
+	 */
+	if (sp->wrk->res_mode & RES_GUNZIP)
+		http_Unset(sp->wrk->resp, H_Content_Encoding);
+
+	if (!(sp->wrk->res_mode & RES_CHUNKED) &&
+	    sp->wrk->h_content_length != NULL)
+		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
+		    "Content-Length: %s", sp->wrk->h_content_length);
+
+	sp->wrk->acct_tmp.hdrbytes +=
+	    http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
+
+	if (sp->wrk->res_mode & RES_CHUNKED)
+		WRW_Chunked(sp->wrk);
+}
+
+void
+RES_StreamPoll(struct worker *w)
+{
+	struct stream_ctx *sctx;
+	struct storage *st;
+	ssize_t l, l2;
+	void *ptr;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(w->fetch_obj, OBJECT_MAGIC);
+	sctx = w->sctx;
+	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
+	if (w->fetch_obj->len == sctx->stream_next)
+		return;
+	assert(w->fetch_obj->len > sctx->stream_next);
+	l = sctx->stream_front;
+	VTAILQ_FOREACH(st, &w->fetch_obj->store, list) {
+		if (st->len + l <= sctx->stream_next) {
+			l += st->len;
+			continue;
+		}
+		l2 = st->len + l - sctx->stream_next;
+		ptr = st->ptr + (sctx->stream_next - l);
+		if (w->res_mode & RES_GUNZIP) {
+			(void)VGZ_WrwGunzip(w, sctx->vgz, ptr, l2,
+			    sctx->obuf, sctx->obuf_len, &sctx->obuf_ptr);
+		} else {
+			(void)WRW_Write(w, ptr, l2);
+		}
+		l += st->len;
+		sctx->stream_next += l2;
+	}
+	if (!(w->res_mode & RES_GUNZIP))
+		(void)WRW_Flush(w);
+
+	if (w->fetch_obj->objcore == NULL ||
+	    (w->fetch_obj->objcore->flags & OC_F_PASS)) {
+		/*
+		 * This is a pass object, release storage as soon as we
+		 * have delivered it.
+		 */
+		while (1) {
+			st = VTAILQ_FIRST(&w->fetch_obj->store);
+			if (st == NULL ||
+			    sctx->stream_front + st->len > sctx->stream_next)
+				break;
+			VTAILQ_REMOVE(&w->fetch_obj->store, st, list);
+			sctx->stream_front += st->len;
+			STV_free(st);
+		}
+	}
+}
+
+void
+RES_StreamEnd(struct sess *sp)
+{
+	struct stream_ctx *sctx;
+
+	sctx = sp->wrk->sctx;
+	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
+
+	if (sp->wrk->res_mode & RES_GUNZIP && sctx->obuf_ptr > 0)
+		(void)WRW_Write(sp->wrk, sctx->obuf, sctx->obuf_ptr);
+	if (sp->wrk->res_mode & RES_CHUNKED &&
+	    !(sp->wrk->res_mode & RES_ESI_CHILD))
+		WRW_EndChunk(sp->wrk);
+	if (WRW_FlushRelease(sp->wrk))
+		SES_Close(sp, "remote closed");
+}
diff --git a/bin/varnishd/cache/cache_rfc2616.c b/bin/varnishd/cache/cache_rfc2616.c
new file mode 100644
index 0000000..4041f45
--- /dev/null
+++ b/bin/varnishd/cache/cache_rfc2616.c
@@ -0,0 +1,336 @@
+/*-
+ * 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.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "vtim.h"
+
+/*--------------------------------------------------------------------
+ * TTL and Age calculation in Varnish
+ *
+ * RFC2616 has a lot to say about how caches should calculate the TTL
+ * and expiry times of objects, but it sort of misses the case that
+ * applies to Varnish:  the server-side cache.
+ *
+ * A normal cache, shared or single-client, has no symbiotic relationship
+ * with the server, and therefore must take a very defensive attitude
+ * if the Data/Expiry/Age/max-age data does not make sense.  Overall
+ * the policy described in section 13 of RFC 2616 results in no caching
+ * happening on the first little sign of trouble.
+ *
+ * Varnish on the other hand tries to offload as many transactions from
+ * the backend as possible, and therefore just passing through everything
+ * if there is a clock-skew between backend and Varnish is not a workable
+ * choice.
+ *
+ * Varnish implements a policy which is RFC2616 compliant when there
+ * is no clockskew, and falls as gracefully as possible otherwise.
+ * Our "clockless cache" model is syntehsized from the bits of RFC2616
+ * that talks about how a cache should react to a clockless origin server,
+ * and more or less uses the inverse logic for the opposite relationship.
+ *
+ */
+
+void
+RFC2616_Ttl(const struct sess *sp)
+{
+	unsigned max_age, age;
+	double h_date, h_expires;
+	char *p;
+	const struct http *hp;
+
+	hp = sp->wrk->beresp;
+
+	assert(sp->wrk->exp.entered != 0.0 && !isnan(sp->wrk->exp.entered));
+	/* If all else fails, cache using default ttl */
+	sp->wrk->exp.ttl = cache_param->default_ttl;
+
+	max_age = age = 0;
+	h_expires = 0;
+	h_date = 0;
+
+	/*
+	 * Initial cacheability determination per [RFC2616, 13.4]
+	 * We do not support ranges yet, so 206 is out.
+	 */
+
+	if (http_GetHdr(hp, H_Age, &p)) {
+		age = strtoul(p, NULL, 0);
+		sp->wrk->exp.age = age;
+	}
+	if (http_GetHdr(hp, H_Expires, &p))
+		h_expires = VTIM_parse(p);
+
+	if (http_GetHdr(hp, H_Date, &p))
+		h_date = VTIM_parse(p);
+
+	switch (sp->err_code) {
+	default:
+		sp->wrk->exp.ttl = -1.;
+		break;
+	case 200: /* OK */
+	case 203: /* Non-Authoritative Information */
+	case 300: /* Multiple Choices */
+	case 301: /* Moved Permanently */
+	case 302: /* Moved Temporarily */
+	case 307: /* Temporary Redirect */
+	case 410: /* Gone */
+	case 404: /* Not Found */
+		/*
+		 * First find any relative specification from the backend
+		 * These take precedence according to RFC2616, 13.2.4
+		 */
+
+		if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
+		    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
+		    p != NULL) {
+
+			if (*p == '-')
+				max_age = 0;
+			else
+				max_age = strtoul(p, NULL, 0);
+
+			if (age > max_age)
+				sp->wrk->exp.ttl = 0;
+			else
+				sp->wrk->exp.ttl = max_age - age;
+			break;
+		}
+
+		/* No expire header, fall back to default */
+		if (h_expires == 0)
+			break;
+
+
+		/* If backend told us it is expired already, don't cache. */
+		if (h_expires < h_date) {
+			sp->wrk->exp.ttl = 0;
+			break;
+		}
+
+		if (h_date == 0 ||
+		    fabs(h_date - sp->wrk->exp.entered) < cache_param->clock_skew) {
+			/*
+			 * If we have no Date: header or if it is
+			 * sufficiently close to our clock we will
+			 * trust Expires: relative to our own clock.
+			 */
+			if (h_expires < sp->wrk->exp.entered)
+				sp->wrk->exp.ttl = 0;
+			else
+				sp->wrk->exp.ttl = h_expires -
+				    sp->wrk->exp.entered;
+			break;
+		} else {
+			/*
+			 * But even if the clocks are out of whack we can still
+			 * derive a relative time from the two headers.
+			 * (the negative ttl case is caught above)
+			 */
+			sp->wrk->exp.ttl = (int)(h_expires - h_date);
+		}
+
+	}
+
+	/* calculated TTL, Our time, Date, Expires, max-age, age */
+	WSP(sp, SLT_TTL,
+	    "%u RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u",
+	    sp->xid, sp->wrk->exp.ttl, -1., -1., sp->wrk->exp.entered,
+	    sp->wrk->exp.age, h_date, h_expires, max_age);
+}
+
+/*--------------------------------------------------------------------
+ * Body existence, fetch method and close policy.
+ */
+
+enum body_status
+RFC2616_Body(const struct sess *sp)
+{
+	struct http *hp;
+	char *b;
+
+	hp = sp->wrk->beresp;
+
+	if (hp->protover < 11 && !http_HdrIs(hp, H_Connection, "keep-alive"))
+		sp->wrk->do_close = 1;
+	else if (http_HdrIs(hp, H_Connection, "close"))
+		sp->wrk->do_close = 1;
+	else
+		sp->wrk->do_close = 0;
+
+	if (!strcasecmp(http_GetReq(sp->wrk->bereq), "head")) {
+		/*
+		 * A HEAD request can never have a body in the reply,
+		 * no matter what the headers might say.
+		 * [RFC2516 4.3 p33]
+		 */
+		sp->wrk->stats.fetch_head++;
+		return (BS_NONE);
+	}
+
+	if (hp->status <= 199) {
+		/*
+		 * 1xx responses never have a body.
+		 * [RFC2616 4.3 p33]
+		 */
+		sp->wrk->stats.fetch_1xx++;
+		return (BS_NONE);
+	}
+
+	if (hp->status == 204) {
+		/*
+		 * 204 is "No Content", obviously don't expect a body.
+		 * [RFC2616 10.2.5 p60]
+		 */
+		sp->wrk->stats.fetch_204++;
+		return (BS_NONE);
+	}
+
+	if (hp->status == 304) {
+		/*
+		 * 304 is "Not Modified" it has no body.
+		 * [RFC2616 10.3.5 p63]
+		 */
+		sp->wrk->stats.fetch_304++;
+		return (BS_NONE);
+	}
+
+	if (http_HdrIs(hp, H_Transfer_Encoding, "chunked")) {
+		 sp->wrk->stats.fetch_chunked++;
+		return (BS_CHUNKED);
+	}
+
+	if (http_GetHdr(hp, H_Transfer_Encoding, &b)) {
+		sp->wrk->stats.fetch_bad++;
+		return (BS_ERROR);
+	}
+
+	if (http_GetHdr(hp, H_Content_Length, &sp->wrk->h_content_length)) {
+		sp->wrk->stats.fetch_length++;
+		return (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.
+		 */
+		sp->wrk->stats.fetch_zero++;
+		return (BS_ZERO);
+	}
+
+	if (http_HdrIs(hp, H_Connection, "close")) {
+		/*
+		 * In this case, it is safe to just read what comes.
+		 */
+		sp->wrk->stats.fetch_close++;
+		return (BS_EOF);
+	}
+
+	if (hp->protover < 11) {
+		/*
+		 * With no Connection header, assume EOF.
+		 */
+		sp->wrk->stats.fetch_oldhttp++;
+		return (BS_EOF);
+	}
+
+	/*
+	 * Fall back to EOF transfer.
+	 */
+	sp->wrk->stats.fetch_eof++;
+	return (BS_EOF);
+}
+
+/*--------------------------------------------------------------------
+ * Find out if the request can receive a gzip'ed response
+ */
+
+unsigned
+RFC2616_Req_Gzip(const struct sess *sp)
+{
+
+
+	/*
+	 * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
+	 * p104 says to not do q values for x-gzip, so we just test
+	 * for its existence.
+	 */
+	if (http_GetHdrData(sp->http, H_Accept_Encoding, "x-gzip", NULL))
+		return (1);
+
+	/*
+	 * "gzip" is the real thing, but the 'q' value must be nonzero.
+	 * We do not care a hoot if the client prefers some other
+	 * compression more than gzip: Varnish only does gzip.
+	 */
+	if (http_GetHdrQ(sp->http, H_Accept_Encoding, "gzip") > 0.)
+		return (1);
+
+	/* Bad client, no gzip. */
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+RFC2616_Do_Cond(const struct sess *sp)
+{
+	char *p, *e;
+	double ims;
+	int do_cond = 0;
+
+	/* RFC 2616 13.3.4 states we need to match both ETag
+	   and If-Modified-Since if present*/
+
+	if (http_GetHdr(sp->http, H_If_Modified_Since, &p) ) {
+		if (!sp->obj->last_modified)
+			return (0);
+		ims = VTIM_parse(p);
+		if (ims > sp->t_req)	/* [RFC2616 14.25] */
+			return (0);
+		if (sp->obj->last_modified > ims)
+			return (0);
+		do_cond = 1;
+	}
+
+	if (http_GetHdr(sp->http, H_If_None_Match, &p) &&
+	    http_GetHdr(sp->obj->http, H_ETag, &e)) {
+		if (strcmp(p,e) != 0)
+			return (0);
+		do_cond = 1;
+	}
+
+	return (do_cond);
+}
diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c
new file mode 100644
index 0000000..7befbcc
--- /dev/null
+++ b/bin/varnishd/cache/cache_session.c
@@ -0,0 +1,419 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2010 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.
+ *
+ * Session management
+ *
+ * This is a little bit of a mixed back, containing both memory management
+ * and various state-change functions.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "waiter/cache_waiter.h"
+
+/*--------------------------------------------------------------------*/
+
+struct sessmem {
+	unsigned		magic;
+#define SESSMEM_MAGIC		0x555859c5
+
+	struct sesspool		*pool;
+
+	unsigned		workspace;
+	uint16_t		nhttp;
+	void			*wsp;
+	struct http		*http[2];
+	VTAILQ_ENTRY(sessmem)	list;
+
+	struct sess		sess;
+};
+
+struct sesspool {
+	unsigned		magic;
+#define SESSPOOL_MAGIC		0xd916e202
+	struct pool		*pool;
+	VTAILQ_HEAD(,sessmem)	freelist;
+	struct lock		mtx;
+	unsigned		nsess;
+	unsigned		dly_free_cnt;
+};
+
+/*--------------------------------------------------------------------
+ * Charge statistics from worker to request and session.
+ */
+
+void
+SES_Charge(struct sess *sp)
+{
+	struct acct *a = &sp->wrk->acct_tmp;
+
+	sp->req_bodybytes += a->bodybytes;
+
+#define ACCT(foo)				\
+	sp->wrk->stats.s_##foo += a->foo;	\
+	sp->acct_ses.foo += a->foo;		\
+	a->foo = 0;
+#include "tbl/acct_fields.h"
+#undef ACCT
+}
+
+/*--------------------------------------------------------------------
+ * This function allocates a session + assorted peripheral data
+ * structures in one single malloc operation.
+ */
+
+static struct sessmem *
+ses_sm_alloc(void)
+{
+	struct sessmem *sm;
+	unsigned char *p, *q;
+	unsigned nws;
+	uint16_t nhttp;
+	unsigned l, hl;
+
+	/*
+	 * It is not necessary to lock these, but we need to
+	 * cache them locally, to make sure we get a consistent
+	 * view of the value.
+	 */
+	nws = cache_param->sess_workspace;
+	nhttp = (uint16_t)cache_param->http_max_hdr;
+
+	hl = HTTP_estimate(nhttp);
+	l = sizeof *sm + nws + 2 * hl;
+	VSC_C_main->sessmem_size = l;
+	p = malloc(l);
+	if (p == NULL)
+		return (NULL);
+	q = p + l;
+
+	/* Don't waste time zeroing the workspace */
+	memset(p, 0, l - nws);
+
+	sm = (void*)p;
+	p += sizeof *sm;
+
+	sm->magic = SESSMEM_MAGIC;
+	sm->workspace = nws;
+	sm->nhttp = nhttp;
+
+	sm->http[0] = HTTP_create(p, nhttp);
+	p += hl;
+
+	sm->http[1] = HTTP_create(p, nhttp);
+	p += hl;
+
+	sm->wsp = p;
+	p += nws;
+
+	assert(p == q);
+
+	return (sm);
+}
+
+/*--------------------------------------------------------------------
+ * This prepares a session for use, based on its sessmem structure.
+ */
+
+static void
+ses_setup(struct sessmem *sm)
+{
+	struct sess *sp;
+
+	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
+	sp = &sm->sess;
+	memset(sp, 0, sizeof *sp);
+
+	/* We assume that the sess has been zeroed by the time we get here */
+	AZ(sp->magic);
+
+	sp->magic = SESS_MAGIC;
+	sp->mem = sm;
+	sp->sockaddrlen = sizeof(sp->sockaddr);
+	sp->mysockaddrlen = sizeof(sp->mysockaddr);
+	sp->sockaddr.ss_family = sp->mysockaddr.ss_family = PF_UNSPEC;
+	sp->t_open = NAN;
+	sp->t_req = NAN;
+	sp->t_resp = NAN;
+	sp->t_end = NAN;
+	EXP_Clr(&sp->exp);
+
+	WS_Init(sp->ws, "sess", sm->wsp, sm->workspace);
+	sp->http = sm->http[0];
+	sp->http0 = sm->http[1];
+}
+
+/*--------------------------------------------------------------------
+ * Get a new session, preferably by recycling an already ready one
+ */
+
+struct sess *
+SES_New(struct worker *wrk, struct sesspool *pp)
+{
+	struct sessmem *sm;
+	struct sess *sp;
+	int do_alloc;
+
+	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
+
+	do_alloc = 0;
+	Lck_Lock(&pp->mtx);
+	sm = VTAILQ_FIRST(&pp->freelist);
+	if (sm != NULL) {
+		VTAILQ_REMOVE(&pp->freelist, sm, list);
+	} else if (pp->nsess < cache_param->max_sess) {
+		pp->nsess++;
+		do_alloc = 1;
+	}
+	wrk->stats.sessmem_free += pp->dly_free_cnt;
+	pp->dly_free_cnt = 0;
+	Lck_Unlock(&pp->mtx);
+	if (do_alloc) {
+		sm = ses_sm_alloc();
+		if (sm != NULL) {
+			wrk->stats.sessmem_alloc++;
+			sm->pool = pp;
+			ses_setup(sm);
+		} else {
+			wrk->stats.sessmem_fail++;
+		}
+	} else if (sm == NULL) {
+		wrk->stats.sessmem_limit++;
+	}
+	if (sm == NULL)
+		return (NULL);
+	sp = &sm->sess;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return (sp);
+}
+
+/*--------------------------------------------------------------------
+ * Allocate a session for use by background threads.
+ */
+
+struct sess *
+SES_Alloc(void)
+{
+	struct sess *sp;
+	struct sessmem *sm;
+
+	sm = ses_sm_alloc();
+	AN(sm);
+	ses_setup(sm);
+	sp = &sm->sess;
+	sp->sockaddrlen = 0;
+	return (sp);
+}
+
+/*--------------------------------------------------------------------
+ * Schedule a session back on a work-thread from its pool
+ */
+
+int
+SES_Schedule(struct sess *sp)
+{
+	struct sessmem *sm;
+	struct sesspool *pp;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	AZ(sp->wrk);
+
+	sm = sp->mem;
+	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
+
+	pp = sm->pool;
+	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
+
+	AN(pp->pool);
+
+	if (Pool_Schedule(pp->pool, sp)) {
+		SES_Delete(sp, "dropped");
+		return (1);
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * Handle a session (from waiter)
+ *
+ * Status: see HTC_Rx()
+ */
+
+void
+SES_Handle(struct sess *sp, int status)
+{
+
+	switch (status) {
+	case -2:
+		SES_Delete(sp, "blast");
+		break;
+	case -1:
+		SES_Delete(sp, "no request");
+		break;
+	case 1:
+		sp->step = STP_START;
+		(void)SES_Schedule(sp);
+		break;
+	default:
+		WRONG("Unexpected return from HTC_Rx()");
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Close a sessions connection.
+ */
+
+void
+SES_Close(struct sess *sp, const char *reason)
+{
+	int i;
+
+	assert(sp->fd >= 0);
+	VSL(SLT_SessionClose, sp->vsl_id, "%s", reason);
+	i = close(sp->fd);
+	assert(i == 0 || errno != EBADF); /* XXX EINVAL seen */
+	sp->fd = -1;
+}
+
+/*--------------------------------------------------------------------
+ * (Close &) Free or Recycle a session.
+ *
+ * If the workspace has changed, deleted it, otherwise wash it, and put
+ * it up for adoption.
+ *
+ * XXX: We should also check nhttp
+ */
+
+void
+SES_Delete(struct sess *sp, const char *reason)
+{
+	struct acct *b;
+	struct sessmem *sm;
+	static char noaddr[] = "-";
+	struct worker *wrk;
+	struct sesspool *pp;
+
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	sm = sp->mem;
+	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
+	pp = sm->pool;
+	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
+	wrk = sp->wrk;
+	CHECK_OBJ_ORNULL(wrk, WORKER_MAGIC);
+
+
+	if (reason != NULL)
+		SES_Close(sp, reason);
+	assert(sp->fd < 0);
+
+	AZ(sp->obj);
+	AZ(sp->vcl);
+	if (sp->addr == NULL)
+		sp->addr = noaddr;
+	if (sp->port == NULL)
+		sp->port = noaddr;
+
+	b = &sp->acct_ses;
+	assert(!isnan(b->first));
+	assert(!isnan(sp->t_end));
+
+	VSL(SLT_StatSess, sp->vsl_id, "%s %s %.0f %ju %ju %ju %ju %ju %ju %ju",
+	    sp->addr, sp->port, sp->t_end - b->first,
+	    b->sess, b->req, b->pipe, b->pass,
+	    b->fetch, b->hdrbytes, b->bodybytes);
+
+	if (sm->workspace != cache_param->sess_workspace ||
+	    sm->nhttp != (uint16_t)cache_param->http_max_hdr ||
+	    pp->nsess > cache_param->max_sess) {
+		free(sm);
+		Lck_Lock(&pp->mtx);
+		if (wrk != NULL)
+			wrk->stats.sessmem_free++;
+		else
+			pp->dly_free_cnt++;
+		pp->nsess--;
+		Lck_Unlock(&pp->mtx);
+	} else {
+		/* Clean and prepare for reuse */
+		ses_setup(sm);
+		Lck_Lock(&pp->mtx);
+		if (wrk != NULL) {
+			wrk->stats.sessmem_free += pp->dly_free_cnt;
+			pp->dly_free_cnt = 0;
+		}
+		VTAILQ_INSERT_HEAD(&pp->freelist, sm, list);
+		Lck_Unlock(&pp->mtx);
+	}
+}
+
+/*--------------------------------------------------------------------
+ * Create and delete pools
+ */
+
+struct sesspool *
+SES_NewPool(struct pool *pp)
+{
+	struct sesspool *sp;
+
+	ALLOC_OBJ(sp, SESSPOOL_MAGIC);
+	AN(sp);
+	sp->pool = pp;
+	VTAILQ_INIT(&sp->freelist);
+	Lck_New(&sp->mtx, lck_sessmem);
+	return (sp);
+}
+
+void
+SES_DeletePool(struct sesspool *sp, struct worker *wrk)
+{
+	struct sessmem *sm;
+
+	CHECK_OBJ_NOTNULL(sp, SESSPOOL_MAGIC);
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	Lck_Lock(&sp->mtx);
+	while (!VTAILQ_EMPTY(&sp->freelist)) {
+		sm = VTAILQ_FIRST(&sp->freelist);
+		CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
+		VTAILQ_REMOVE(&sp->freelist, sm, list);
+		FREE_OBJ(sm);
+		wrk->stats.sessmem_free++;
+		sp->nsess--;
+	}
+	AZ(sp->nsess);
+	Lck_Unlock(&sp->mtx);
+	Lck_Delete(&sp->mtx);
+	FREE_OBJ(sp);
+}
diff --git a/bin/varnishd/cache/cache_shmlog.c b/bin/varnishd/cache/cache_shmlog.c
new file mode 100644
index 0000000..1252fa3
--- /dev/null
+++ b/bin/varnishd/cache/cache_shmlog.c
@@ -0,0 +1,346 @@
+/*-
+ * 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"	// For w->vbc
+
+#include "vapi/vsm_int.h"
+#include "vmb.h"
+#include "vtim.h"
+
+/* These cannot be struct lock, which depends on vsm/vsl working */
+static pthread_mutex_t vsl_mtx;
+static pthread_mutex_t vsm_mtx;
+
+static uint32_t			*vsl_start;
+static const uint32_t		*vsl_end;
+static uint32_t			*vsl_ptr;
+
+static inline uint32_t
+vsl_w0(uint32_t type, uint32_t length)
+{
+
+	assert(length < 0x10000);
+        return (((type & 0xff) << 24) | length);
+}
+
+/*--------------------------------------------------------------------*/
+
+static inline void
+vsl_hdr(enum VSL_tag_e tag, uint32_t *p, unsigned len, unsigned id)
+{
+
+	assert(((uintptr_t)p & 0x3) == 0);
+
+	p[1] = id;
+	VMB();
+	p[0] = vsl_w0(tag, len);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+vsl_wrap(void)
+{
+
+	assert(vsl_ptr >= vsl_start + 1);
+	assert(vsl_ptr < vsl_end);
+	vsl_start[1] = VSL_ENDMARKER;
+	do
+		vsl_start[0]++;
+	while (vsl_start[0] == 0);
+	VWMB();
+	if (vsl_ptr != vsl_start + 1) {
+		*vsl_ptr = VSL_WRAPMARKER;
+		vsl_ptr = vsl_start + 1;
+	}
+	VSC_C_main->shm_cycles++;
+}
+
+/*--------------------------------------------------------------------
+ * Reserve bytes for a record, wrap if necessary
+ */
+
+static uint32_t *
+vsl_get(unsigned len, unsigned records, unsigned flushes)
+{
+	uint32_t *p;
+
+	if (pthread_mutex_trylock(&vsl_mtx)) {
+		AZ(pthread_mutex_lock(&vsl_mtx));
+		VSC_C_main->shm_cont++;
+	}
+	assert(vsl_ptr < vsl_end);
+	assert(((uintptr_t)vsl_ptr & 0x3) == 0);
+
+	VSC_C_main->shm_writes++;
+	VSC_C_main->shm_flushes += flushes;
+	VSC_C_main->shm_records += records;
+
+	/* Wrap if necessary */
+	if (VSL_END(vsl_ptr, len) >= vsl_end)
+		vsl_wrap();
+
+	p = vsl_ptr;
+	vsl_ptr = VSL_END(vsl_ptr, len);
+
+	*vsl_ptr = VSL_ENDMARKER;
+
+	assert(vsl_ptr < vsl_end);
+	assert(((uintptr_t)vsl_ptr & 0x3) == 0);
+	AZ(pthread_mutex_unlock(&vsl_mtx));
+
+	return (p);
+}
+
+/*--------------------------------------------------------------------
+ * This variant copies a byte-range directly to the log, without
+ * taking the detour over sprintf()
+ */
+
+static void
+VSLR(enum VSL_tag_e tag, int id, const char *b, unsigned len)
+{
+	uint32_t *p;
+	unsigned mlen;
+
+	mlen = cache_param->shm_reclen;
+
+	/* Truncate */
+	if (len > mlen)
+		len = mlen;
+
+	p = vsl_get(len, 1, 0);
+
+	memcpy(p + 2, b, len);
+	vsl_hdr(tag, p, len, id);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VSL(enum VSL_tag_e tag, int id, const char *fmt, ...)
+{
+	va_list ap;
+	unsigned n, mlen = cache_param->shm_reclen;
+	char buf[mlen];
+
+	/*
+	 * XXX: consider formatting into a stack buffer then move into
+	 * XXX: shmlog with VSLR().
+	 */
+	AN(fmt);
+	va_start(ap, fmt);
+
+	if (strchr(fmt, '%') == NULL) {
+		VSLR(tag, id, fmt, strlen(fmt));
+	} else {
+		n = vsnprintf(buf, mlen, fmt, ap);
+		if (n > mlen)
+			n = mlen;
+		VSLR(tag, id, buf, n);
+	}
+	va_end(ap);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+WSL_Flush(struct worker *w, int overflow)
+{
+	uint32_t *p;
+	unsigned l;
+
+	l = pdiff(w->wlb, w->wlp);
+	if (l == 0)
+		return;
+
+	assert(l >= 8);
+
+	p = vsl_get(l - 8, w->wlr, overflow);
+
+	memcpy(p + 1, w->wlb + 1, l - 4);
+	VWMB();
+	p[0] = w->wlb[0];
+	w->wlp = w->wlb;
+	w->wlr = 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+WSLR(struct worker *w, enum VSL_tag_e tag, int id, txt t)
+{
+	unsigned l, mlen;
+
+	Tcheck(t);
+	mlen = cache_param->shm_reclen;
+
+	/* Truncate */
+	l = Tlen(t);
+	if (l > mlen) {
+		l = mlen;
+		t.e = t.b + l;
+	}
+
+	assert(w->wlp < w->wle);
+
+	/* Wrap if necessary */
+	if (VSL_END(w->wlp, l) >= w->wle)
+		WSL_Flush(w, 1);
+	assert (VSL_END(w->wlp, l) < w->wle);
+	memcpy(VSL_DATA(w->wlp), t.b, l);
+	vsl_hdr(tag, w->wlp, l, id);
+	w->wlp = VSL_END(w->wlp, l);
+	assert(w->wlp < w->wle);
+	w->wlr++;
+	if (cache_param->diag_bitmap & 0x10000)
+		WSL_Flush(w, 0);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+wsl(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, va_list ap)
+{
+	char *p;
+	unsigned n, mlen;
+	txt t;
+
+	AN(fmt);
+	mlen = cache_param->shm_reclen;
+
+	if (strchr(fmt, '%') == NULL) {
+		t.b = TRUST_ME(fmt);
+		t.e = strchr(t.b, '\0');
+		WSLR(w, tag, id, t);
+	} else {
+		assert(w->wlp < w->wle);
+
+		/* Wrap if we cannot fit a full size record */
+		if (VSL_END(w->wlp, mlen) >= w->wle)
+			WSL_Flush(w, 1);
+
+		p = VSL_DATA(w->wlp);
+		n = vsnprintf(p, mlen, fmt, ap);
+		if (n > mlen)
+			n = mlen;	/* we truncate long fields */
+		vsl_hdr(tag, w->wlp, n, id);
+		w->wlp = VSL_END(w->wlp, n);
+		assert(w->wlp < w->wle);
+		w->wlr++;
+	}
+	if (cache_param->diag_bitmap & 0x10000)
+		WSL_Flush(w, 0);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+WSL(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, ...)
+{
+	va_list ap;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AN(fmt);
+	va_start(ap, fmt);
+	wsl(w, tag, id, fmt, ap);
+	va_end(ap);
+}
+
+
+/*--------------------------------------------------------------------*/
+
+void
+WSLB(struct worker *w, enum VSL_tag_e tag, const char *fmt, ...)
+{
+	va_list ap;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	CHECK_OBJ_NOTNULL(w->vbc, VBC_MAGIC);
+	AN(fmt);
+	va_start(ap, fmt);
+	wsl(w, tag, w->vbc->vsl_id, fmt, ap);
+	va_end(ap);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VSL_Init(void)
+{
+	struct VSM_chunk *vsc;
+
+	AZ(pthread_mutex_init(&vsl_mtx, NULL));
+	AZ(pthread_mutex_init(&vsm_mtx, NULL));
+
+	VSM__Clean();
+
+	VSM_ITER(vsc)
+		if (!strcmp(vsc->class, VSL_CLASS))
+			break;
+	AN(vsc);
+	vsl_start = VSM_PTR(vsc);
+	vsl_end = VSM_NEXT(vsc);
+	vsl_ptr = vsl_start + 1;
+
+	vsl_wrap();
+	VSM_head->starttime = (intmax_t)VTIM_real();
+	memset(VSM_head->panicstr, '\0', sizeof *VSM_head->panicstr);
+	memset(VSC_C_main, 0, sizeof *VSC_C_main);
+	VSM_head->child_pid = getpid();
+}
+
+/*--------------------------------------------------------------------*/
+
+void *
+VSM_Alloc(unsigned size, const char *class, const char *type,
+    const char *ident)
+{
+	void *p;
+
+	AZ(pthread_mutex_lock(&vsm_mtx));
+	p = VSM__Alloc(size, class, type, ident);
+	AZ(pthread_mutex_unlock(&vsm_mtx));
+	return (p);
+}
+
+void
+VSM_Free(const void *ptr)
+{
+
+	AZ(pthread_mutex_lock(&vsm_mtx));
+	VSM__Free(ptr);
+	AZ(pthread_mutex_unlock(&vsm_mtx));
+}
diff --git a/bin/varnishd/cache/cache_vary.c b/bin/varnishd/cache/cache_vary.c
new file mode 100644
index 0000000..026f937
--- /dev/null
+++ b/bin/varnishd/cache/cache_vary.c
@@ -0,0 +1,257 @@
+/*-
+ * 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.
+ *
+ * Do Vary processing.
+ *
+ * When we insert an object into the cache which has a Vary: header,
+ * we encode a vary matching string containing the headers mentioned
+ * and their value.
+ *
+ * When we match an object in the cache, we check the present request
+ * against the vary matching string.
+ *
+ * The only kind of header-munging we do is leading & trailing space
+ * removal.  All the potential "q=foo" gymnastics is not worth the
+ * effort.
+ *
+ * The vary matching string has the following format:
+ *
+ * Sequence of: {
+ *	<msb>			\   Length of header contents.
+ *	<lsb>			/
+ *	<length of header + 1>	\
+ *	<header>		 \  Same format as argument to http_GetHdr()
+ *	':'			 /
+ *	'\0'			/
+ *      <header>		>   Only present if length != 0xffff
+ * }
+ *      '\0'
+ */
+
+#include "config.h"
+
+#include "cache.h"
+
+#include "vct.h"
+#include "vend.h"
+
+struct vsb *
+VRY_Create(const struct sess *sp, const struct http *hp)
+{
+	char *v, *p, *q, *h, *e;
+	struct vsb *sb, *sbh;
+	int l;
+
+	/* No Vary: header, no worries */
+	if (!http_GetHdr(hp, H_Vary, &v))
+		return (NULL);
+
+	/* For vary matching string */
+	sb = VSB_new_auto();
+	AN(sb);
+
+	/* For header matching strings */
+	sbh = VSB_new_auto();
+	AN(sbh);
+
+	if (*v == ':') {
+		WSP(sp, SLT_Error, "Vary header had extra ':', fix backend");
+		v++;
+	}
+	for (p = v; *p; p++) {
+
+		/* Find next header-name */
+		if (vct_issp(*p))
+			continue;
+		for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
+			continue;
+
+		/* Build a header-matching string out of it */
+		VSB_clear(sbh);
+		VSB_printf(sbh, "%c%.*s:%c",
+		    (char)(1 + (q - p)), (int)(q - p), p, 0);
+		AZ(VSB_finish(sbh));
+
+		if (http_GetHdr(sp->http, VSB_data(sbh), &h)) {
+			AZ(vct_issp(*h));
+			/* Trim trailing space */
+			e = strchr(h, '\0');
+			while (e > h && vct_issp(e[-1]))
+				e--;
+			/* Encode two byte length and contents */
+			l = e - h;
+			assert(!(l & ~0xffff));
+		} else {
+			e = h;
+			l = 0xffff;
+		}
+		VSB_printf(sb, "%c%c", (unsigned)l >> 8, l & 0xff);
+		/* Append to vary matching string */
+		VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh));
+		if (e != h)
+			VSB_bcat(sb, h, e - h);
+
+		while (vct_issp(*q))
+			q++;
+		if (*q == '\0')
+			break;
+		xxxassert(*q == ',');
+		p = q;
+	}
+	/* Terminate vary matching string */
+	VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
+
+	VSB_delete(sbh);
+	AZ(VSB_finish(sb));
+	return(sb);
+}
+
+/*
+ * Find length of a vary entry
+ */
+static unsigned
+vry_len(const uint8_t *p)
+{
+	unsigned l = vbe16dec(p);
+
+	return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
+}
+
+/*
+ * Compare two vary entries
+ */
+static int
+vry_cmp(const uint8_t * const *v1, uint8_t * const *v2)
+{
+	unsigned retval = 0;
+
+	if (!memcmp(*v1, *v2, vry_len(*v1))) {
+		/* Same same */
+		retval = 0;
+	} else if (memcmp((*v1) + 2, (*v2) + 2, (*v1)[2] + 2)) {
+		/* Different header */
+		retval = 1;
+	} else if (cache_param->http_gzip_support &&
+	    !strcasecmp(H_Accept_Encoding, (const char*)((*v1)+2))) {
+		/*
+		 * If we do gzip processing, we do not vary on Accept-Encoding,
+		 * because we want everybody to get the gzip'ed object, and
+		 * varnish will gunzip as necessary.  We implement the skip at
+		 * check time, rather than create time, so that object in
+		 * persistent storage can be used with either setting of
+		 * http_gzip_support.
+		 */
+		retval = 0;
+	} else {
+		/* Same header, different content */
+		retval = 2;
+	}
+	return (retval);
+}
+
+int
+VRY_Match(struct sess *sp, const uint8_t *vary)
+{
+	uint8_t *vsp = sp->vary_b;
+	char *h, *e;
+	unsigned lh, ln;
+	int i, retval = 1, oflo = 0;
+
+	AN(vsp);
+	while (vary[2]) {
+		i = vry_cmp(&vary, &vsp);
+		if (i == 1) {
+			/* Build a new entry */
+
+			i = http_GetHdr(sp->http, (const char*)(vary+2), &h);
+			if (i) {
+				/* Trim trailing space */
+				e = strchr(h, '\0');
+				while (e > h && vct_issp(e[-1]))
+					e--;
+				lh = e - h;
+				assert(lh < 0xffff);
+			} else {
+				e = h = NULL;
+				lh = 0xffff;
+			}
+
+			/* Length of the entire new vary entry */
+			ln = 2 + vary[2] + 2 + (lh == 0xffff ? 0 : lh);
+			if (vsp + ln >= sp->vary_e) {
+				vsp = sp->vary_b;
+				oflo = 1;
+			}
+
+			/*
+			 * We MUST have space for one entry and the end marker
+			 * after it, which prevents old junk from confusing us
+			 */
+			assert(vsp + ln + 2 < sp->vary_e);
+
+			vbe16enc(vsp, (uint16_t)lh);
+			memcpy(vsp + 2, vary + 2, vary[2] + 2);
+			if (h != NULL && e != NULL) {
+				memcpy(vsp + 2 + vsp[2] + 2, h, e - h);
+				vsp[2 + vary[2] + 2 + (e - h) + 2] = '\0';
+			} else
+				vsp[2 + vary[2] + 2 + 2] = '\0';
+
+			i = vry_cmp(&vary, &vsp);
+			assert(i != 1);	/* hdr must be the same now */
+		}
+		if (i != 0)
+			retval = 0;
+		vsp += vry_len(vsp);
+		vary += vry_len(vary);
+	}
+	if (vsp + 3 > sp->vary_e)
+		oflo = 1;
+
+	if (oflo) {
+		/* XXX: Should log this */
+		vsp = sp->vary_b;
+	}
+	vsp[0] = 0xff;
+	vsp[1] = 0xff;
+	vsp[2] = 0;
+	if (oflo)
+		sp->vary_l = NULL;
+	else
+		sp->vary_l = vsp + 3;
+	return (retval);
+}
+
+void
+VRY_Validate(const uint8_t *vary)
+{
+
+	while (vary[2] != 0) {
+		assert(strlen((const char*)vary+3) == vary[2]);
+		vary += vry_len(vary);
+	}
+}
diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c
new file mode 100644
index 0000000..068b482
--- /dev/null
+++ b/bin/varnishd/cache/cache_vcl.c
@@ -0,0 +1,365 @@
+/*-
+ * 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.
+ *
+ * Interface *to* compiled VCL code:  Loading, unloading, calling into etc.
+ *
+ * The interface *from* the compiled VCL code is in cache_vrt.c.
+ */
+
+#include "config.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "vcl.h"
+#include "vcli.h"
+#include "vcli_priv.h"
+
+struct vcls {
+	unsigned		magic;
+#define VVCLS_MAGIC		0x214188f2
+	VTAILQ_ENTRY(vcls)	list;
+	char			*name;
+	void			*dlh;
+	struct VCL_conf		conf[1];
+};
+
+/*
+ * XXX: Presently all modifications to this list happen from the
+ * CLI event-engine, so no locking is necessary
+ */
+static VTAILQ_HEAD(, vcls)	vcl_head =
+    VTAILQ_HEAD_INITIALIZER(vcl_head);
+
+
+static struct lock		vcl_mtx;
+static struct vcls		*vcl_active; /* protected by vcl_mtx */
+
+/*--------------------------------------------------------------------*/
+
+const char *
+VCL_Return_Name(unsigned method)
+{
+
+	switch (method) {
+#define VCL_RET_MAC(l, U, B) case VCL_RET_##U: return(#l);
+#include "tbl/vcl_returns.h"
+#undef VCL_RET_MAC
+	default:
+		return (NULL);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+VCL_Get(struct VCL_conf **vcc)
+{
+	static int once = 0;
+
+	while (!once && vcl_active == NULL) {
+		(void)sleep(1);
+	}
+	once = 1;
+
+	Lck_Lock(&vcl_mtx);
+	AN(vcl_active);
+	*vcc = vcl_active->conf;
+	AN(*vcc);
+	AZ((*vcc)->discard);
+	(*vcc)->busy++;
+	Lck_Unlock(&vcl_mtx);
+}
+
+void
+VCL_Refresh(struct VCL_conf **vcc)
+{
+	if (*vcc == vcl_active->conf)
+		return;
+	if (*vcc != NULL)
+		VCL_Rel(vcc);	/* XXX: optimize locking */
+	VCL_Get(vcc);
+}
+
+void
+VCL_Rel(struct VCL_conf **vcc)
+{
+	struct VCL_conf *vc;
+
+	AN(*vcc);
+	vc = *vcc;
+	*vcc = NULL;
+
+	Lck_Lock(&vcl_mtx);
+	assert(vc->busy > 0);
+	vc->busy--;
+	/*
+	 * We do not garbage collect discarded VCL's here, that happens
+	 * in VCL_Poll() which is called from the CLI thread.
+	 */
+	Lck_Unlock(&vcl_mtx);
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct vcls *
+vcl_find(const char *name)
+{
+	struct vcls *vcl;
+
+	ASSERT_CLI();
+	VTAILQ_FOREACH(vcl, &vcl_head, list) {
+		if (vcl->conf->discard)
+			continue;
+		if (!strcmp(vcl->name, name))
+			return (vcl);
+	}
+	return (NULL);
+}
+
+static int
+VCL_Load(const char *fn, const char *name, struct cli *cli)
+{
+	struct vcls *vcl;
+	struct VCL_conf const *cnf;
+
+	ASSERT_CLI();
+	vcl = vcl_find(name);
+	if (vcl != NULL) {
+		VCLI_Out(cli, "Config '%s' already loaded", name);
+		return (1);
+	}
+
+	ALLOC_OBJ(vcl, VVCLS_MAGIC);
+	XXXAN(vcl);
+
+	vcl->dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
+
+	if (vcl->dlh == NULL) {
+		VCLI_Out(cli, "dlopen(%s): %s\n", fn, dlerror());
+		FREE_OBJ(vcl);
+		return (1);
+	}
+	cnf = dlsym(vcl->dlh, "VCL_conf");
+	if (cnf == NULL) {
+		VCLI_Out(cli, "Internal error: No VCL_conf symbol\n");
+		(void)dlclose(vcl->dlh);
+		FREE_OBJ(vcl);
+		return (1);
+	}
+	memcpy(vcl->conf, cnf, sizeof *cnf);
+
+	if (vcl->conf->magic != VCL_CONF_MAGIC) {
+		VCLI_Out(cli, "Wrong VCL_CONF_MAGIC\n");
+		(void)dlclose(vcl->dlh);
+		FREE_OBJ(vcl);
+		return (1);
+	}
+	if (vcl->conf->init_vcl(cli)) {
+		VCLI_Out(cli, "VCL \"%s\" Failed to initialize", name);
+		(void)dlclose(vcl->dlh);
+		FREE_OBJ(vcl);
+		return (1);
+	}
+	REPLACE(vcl->name, name);
+	VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
+	VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
+	(void)vcl->conf->init_func(NULL);
+	Lck_Lock(&vcl_mtx);
+	if (vcl_active == NULL)
+		vcl_active = vcl;
+	Lck_Unlock(&vcl_mtx);
+	VSC_C_main->n_vcl++;
+	VSC_C_main->n_vcl_avail++;
+	return (0);
+}
+
+/*--------------------------------------------------------------------
+ * This function is polled from the CLI thread to dispose of any non-busy
+ * VCLs which have been discarded.
+ */
+
+static void
+VCL_Nuke(struct vcls *vcl)
+{
+
+	ASSERT_CLI();
+	assert(vcl != vcl_active);
+	assert(vcl->conf->discard);
+	assert(vcl->conf->busy == 0);
+	VTAILQ_REMOVE(&vcl_head, vcl, list);
+	(void)vcl->conf->fini_func(NULL);
+	vcl->conf->fini_vcl(NULL);
+	free(vcl->name);
+	(void)dlclose(vcl->dlh);
+	FREE_OBJ(vcl);
+	VSC_C_main->n_vcl--;
+	VSC_C_main->n_vcl_discard--;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VCL_Poll(void)
+{
+	struct vcls *vcl, *vcl2;
+
+	ASSERT_CLI();
+	VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2)
+		if (vcl->conf->discard && vcl->conf->busy == 0)
+			VCL_Nuke(vcl);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+ccf_config_list(struct cli *cli, const char * const *av, void *priv)
+{
+	struct vcls *vcl;
+	const char *flg;
+
+	(void)av;
+	(void)priv;
+	ASSERT_CLI();
+	VTAILQ_FOREACH(vcl, &vcl_head, list) {
+		if (vcl == vcl_active) {
+			flg = "active";
+		} else if (vcl->conf->discard) {
+			flg = "discarded";
+		} else
+			flg = "available";
+		VCLI_Out(cli, "%-10s %6u %s\n",
+		    flg,
+		    vcl->conf->busy,
+		    vcl->name);
+	}
+}
+
+static void
+ccf_config_load(struct cli *cli, const char * const *av, void *priv)
+{
+
+	(void)av;
+	(void)priv;
+	ASSERT_CLI();
+	if (VCL_Load(av[3], av[2], cli))
+		VCLI_SetResult(cli, CLIS_PARAM);
+	return;
+}
+
+static void
+ccf_config_discard(struct cli *cli, const char * const *av, void *priv)
+{
+	struct vcls *vcl;
+
+	ASSERT_CLI();
+	(void)av;
+	(void)priv;
+	vcl = vcl_find(av[2]);
+	if (vcl == NULL) {
+		VCLI_SetResult(cli, CLIS_PARAM);
+		VCLI_Out(cli, "VCL '%s' unknown", av[2]);
+		return;
+	}
+	Lck_Lock(&vcl_mtx);
+	if (vcl == vcl_active) {
+		Lck_Unlock(&vcl_mtx);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		VCLI_Out(cli, "VCL %s is the active VCL", av[2]);
+		return;
+	}
+	VSC_C_main->n_vcl_discard++;
+	VSC_C_main->n_vcl_avail--;
+	vcl->conf->discard = 1;
+	Lck_Unlock(&vcl_mtx);
+	if (vcl->conf->busy == 0)
+		VCL_Nuke(vcl);
+}
+
+static void
+ccf_config_use(struct cli *cli, const char * const *av, void *priv)
+{
+	struct vcls *vcl;
+	int i;
+
+	(void)av;
+	(void)priv;
+	vcl = vcl_find(av[2]);
+	if (vcl == NULL) {
+		VCLI_Out(cli, "No VCL named '%s'", av[2]);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return;
+	}
+	Lck_Lock(&vcl_mtx);
+	vcl_active = vcl;
+	Lck_Unlock(&vcl_mtx);
+
+	/* Tickle this VCL's backends to take over health polling */
+	for(i = 1; i < vcl->conf->ndirector; i++)
+		VBE_UseHealth(vcl->conf->director[i]);
+}
+
+/*--------------------------------------------------------------------*/
+
+#define VCL_MET_MAC(func, upper, bitmap)				\
+void									\
+VCL_##func##_method(struct sess *sp)					\
+{									\
+									\
+	sp->handling = 0;						\
+	sp->cur_method = VCL_MET_ ## upper;				\
+	WSP(sp, SLT_VCL_call, "%s", #func);				\
+	(void)sp->vcl->func##_func(sp);					\
+	WSP(sp, SLT_VCL_return, "%s", VCL_Return_Name(sp->handling));	\
+	sp->cur_method = 0;						\
+	assert((1U << sp->handling) & bitmap);				\
+	assert(!((1U << sp->handling) & ~bitmap));			\
+}
+
+#include "tbl/vcl_returns.h"
+#undef VCL_MET_MAC
+
+/*--------------------------------------------------------------------*/
+
+static struct cli_proto vcl_cmds[] = {
+	{ CLI_VCL_LOAD,         "i", ccf_config_load },
+	{ CLI_VCL_LIST,         "i", ccf_config_list },
+	{ CLI_VCL_DISCARD,      "i", ccf_config_discard },
+	{ CLI_VCL_USE,          "i", ccf_config_use },
+	{ NULL }
+};
+
+void
+VCL_Init()
+{
+
+	CLI_AddFuncs(vcl_cmds);
+	Lck_New(&vcl_mtx, lck_vcl);
+}
diff --git a/bin/varnishd/cache/cache_vrt.c b/bin/varnishd/cache/cache_vrt.c
new file mode 100644
index 0000000..c20b552
--- /dev/null
+++ b/bin/varnishd/cache/cache_vrt.c
@@ -0,0 +1,535 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2010 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.
+ *
+ * Runtime support for compiled VCL programs
+ */
+
+#include "config.h"
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "cache_backend.h"
+#include "hash/hash_slinger.h"
+#include "vav.h"
+#include "vcl.h"
+#include "vrt.h"
+#include "vrt_obj.h"
+#include "vtim.h"
+
+const void * const vrt_magic_string_end = &vrt_magic_string_end;
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_error(struct sess *sp, unsigned code, const char *reason)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	WSL(sp->wrk, SLT_Debug, 0, "VCL_error(%u, %s)", code, reason ?
+	    reason : "(null)");
+	if (code < 100 || code > 999)
+		code = 503;
+	sp->err_code = (uint16_t)code;
+	sp->err_reason = reason ? reason : http_StatusMessage(sp->err_code);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_count(const struct sess *sp, unsigned u)
+{
+
+	if (sp == NULL)
+		return;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	if (cache_param->vcl_trace)
+		WSP(sp, SLT_VCL_trace, "%u %d.%d", u,
+		    sp->vcl->ref[u].line, sp->vcl->ref[u].pos);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_acl_log(const struct sess *sp, const char *msg)
+{
+	WSP(sp, SLT_VCL_acl, msg);
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct http *
+vrt_selecthttp(const struct sess *sp, enum gethdr_e where)
+{
+	struct http *hp;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	switch (where) {
+	case HDR_REQ:
+		hp = sp->http;
+		break;
+	case HDR_BEREQ:
+		hp = sp->wrk->bereq;
+		break;
+	case HDR_BERESP:
+		hp = sp->wrk->beresp;
+		break;
+	case HDR_RESP:
+		hp = sp->wrk->resp;
+		break;
+	case HDR_OBJ:
+		CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+		hp = sp->obj->http;
+		break;
+	default:
+		INCOMPL();
+	}
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	return (hp);
+}
+
+char *
+VRT_GetHdr(const struct sess *sp, enum gethdr_e where, const char *n)
+{
+	char *p;
+	struct http *hp;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	hp = vrt_selecthttp(sp, where);
+	if (!http_GetHdr(hp, n, &p))
+		return (NULL);
+	return (p);
+}
+
+/*--------------------------------------------------------------------
+ * XXX: Optimize the single element case ?
+ */
+
+char *
+VRT_StringList(char *d, unsigned dl, const char *p, va_list ap)
+{
+	char *b, *e;
+	unsigned x;
+
+	b = d;
+	e = b + dl;
+	while (p != vrt_magic_string_end && b < e) {
+		if (p != NULL) {
+			x = strlen(p);
+			if (b + x < e)
+				memcpy(b, p, x);
+			b += x;
+		}
+		p = va_arg(ap, const char *);
+	}
+	if (b >= e)
+		return (NULL);
+	*b++ = '\0';
+	return (b);
+}
+
+/*--------------------------------------------------------------------
+ * XXX: Optimize the single element case ?
+ */
+
+char *
+VRT_String(struct ws *ws, const char *h, const char *p, va_list ap)
+{
+	char *b, *e;
+	unsigned u, x;
+
+	u = WS_Reserve(ws, 0);
+	e = b = ws->f;
+	e += u;
+	if (h != NULL) {
+		x = strlen(h);
+		if (b + x < e)
+			memcpy(b, h, x);
+		b += x;
+		if (b < e)
+			*b = ' ';
+		b++;
+	}
+	b = VRT_StringList(b, e > b ? e - b : 0, p, ap);
+	if (b == NULL || b == e) {
+		WS_Release(ws, 0);
+		return (NULL);
+	}
+	e = b;
+	b = ws->f;
+	WS_Release(ws, e - b);
+	return (b);
+}
+
+/*--------------------------------------------------------------------
+ * Build a string on the worker threads workspace
+ */
+
+const char *
+VRT_WrkString(const struct sess *sp, const char *p, ...)
+{
+	va_list ap;
+	char *b;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	va_start(ap, p);
+	b = VRT_String(sp->wrk->ws, NULL, p, ap);
+	va_end(ap);
+	return (b);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_SetHdr(const struct sess *sp , enum gethdr_e where, const char *hdr,
+    const char *p, ...)
+{
+	struct http *hp;
+	va_list ap;
+	char *b;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	hp = vrt_selecthttp(sp, where);
+	va_start(ap, p);
+	if (p == NULL) {
+		http_Unset(hp, hdr);
+	} else {
+		b = VRT_String(hp->ws, hdr + 1, p, ap);
+		if (b == NULL) {
+			WSP(sp, SLT_LostHeader, "%s", hdr + 1);
+		} else {
+			http_Unset(hp, hdr);
+			http_SetHeader(sp->wrk, sp->vsl_id, hp, b);
+		}
+	}
+	va_end(ap);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_handling(struct sess *sp, unsigned hand)
+{
+
+	if (sp == NULL) {
+		assert(hand == VCL_RET_OK);
+		return;
+	}
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	assert(hand < VCL_RET_MAX);
+	sp->handling = hand;
+}
+
+/*--------------------------------------------------------------------
+ * Add an element to the array/list of hash bits.
+ */
+
+void
+VRT_hashdata(const struct sess *sp, const char *str, ...)
+{
+	va_list ap;
+	const char *p;
+
+	HSH_AddString(sp, str);
+	va_start(ap, str);
+	while (1) {
+		p = va_arg(ap, const char *);
+		if (p == vrt_magic_string_end)
+			break;
+		HSH_AddString(sp, p);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+double
+VRT_r_now(const struct sess *sp)
+{
+
+	(void)sp;
+	return (VTIM_real());
+}
+
+/*--------------------------------------------------------------------*/
+
+char *
+VRT_IP_string(const struct sess *sp, const struct sockaddr_storage *sa)
+{
+	char *p;
+	const struct sockaddr_in *si4;
+	const struct sockaddr_in6 *si6;
+	const void *addr;
+	int len;
+
+	switch (sa->ss_family) {
+	case AF_INET:
+		len = INET_ADDRSTRLEN;
+		si4 = (const void *)sa;
+		addr = &(si4->sin_addr);
+		break;
+	case AF_INET6:
+		len = INET6_ADDRSTRLEN;
+		si6 = (const void *)sa;
+		addr = &(si6->sin6_addr);
+		break;
+	default:
+		INCOMPL();
+	}
+	XXXAN(len);
+	AN(p = WS_Alloc(sp->http->ws, len));
+	AN(inet_ntop(sa->ss_family, addr, p, len));
+	return (p);
+}
+
+char *
+VRT_int_string(const struct sess *sp, int num)
+{
+	char *p;
+	int size;
+
+	size = snprintf(NULL, 0, "%d", num) + 1;
+	AN(p = WS_Alloc(sp->http->ws, size));
+	assert(snprintf(p, size, "%d", num) < size);
+	return (p);
+}
+
+char *
+VRT_double_string(const struct sess *sp, double num)
+{
+	char *p;
+	int size;
+
+	size = snprintf(NULL, 0, "%.3f", num) + 1;
+	AN(p = WS_Alloc(sp->http->ws, size));
+	assert(snprintf(p, size, "%.3f", num) < size);
+	return (p);
+}
+
+char *
+VRT_time_string(const struct sess *sp, double t)
+{
+	char *p;
+
+	AN(p = WS_Alloc(sp->http->ws, VTIM_FORMAT_SIZE));
+	VTIM_format(t, p);
+	return (p);
+}
+
+const char *
+VRT_backend_string(const struct sess *sp, const struct director *d)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	if (d == NULL)
+		d = sp->director;
+	if (d == NULL)
+		return (NULL);
+	return (d->vcl_name);
+}
+
+const char *
+VRT_bool_string(const struct sess *sp, unsigned val)
+{
+
+	(void)sp;
+	return (val ? "true" : "false");
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_Rollback(struct sess *sp)
+{
+
+	HTTP_Copy(sp->http, sp->http0);
+	WS_Reset(sp->ws, sp->ws_req);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_panic(const struct sess *sp, const char *str, ...)
+{
+	va_list ap;
+	char *b;
+
+	va_start(ap, str);
+	b = VRT_String(sp->http->ws, "PANIC: ", str, ap);
+	va_end(ap);
+	VAS_Fail("VCL", "", 0, b, 0, 2);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_synth_page(const struct sess *sp, unsigned flags, const char *str, ...)
+{
+	va_list ap;
+	const char *p;
+	struct vsb *vsb;
+
+	(void)flags;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
+	vsb = SMS_Makesynth(sp->obj);
+	AN(vsb);
+
+	VSB_cat(vsb, str);
+	va_start(ap, str);
+	p = va_arg(ap, const char *);
+	while (p != vrt_magic_string_end) {
+		if (p == NULL)
+			p = "(null)";
+		VSB_cat(vsb, p);
+		p = va_arg(ap, const char *);
+	}
+	va_end(ap);
+	SMS_Finish(sp->obj);
+	http_Unset(sp->obj->http, H_Content_Length);
+	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->obj->http,
+	    "Content-Length: %d", sp->obj->len);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_ban(struct sess *sp, char *cmds, ...)
+{
+	char *a1, *a2, *a3;
+	va_list ap;
+	struct ban *b;
+	int good;
+
+	(void)sp;
+	b = BAN_New();
+	va_start(ap, cmds);
+	a1 = cmds;
+	good = 0;
+	while (a1 != NULL) {
+		good = 0;
+		a2 = va_arg(ap, char *);
+		if (a2 == NULL)
+			break;
+		a3 = va_arg(ap, char *);
+		if (a3 == NULL)
+			break;
+		if (BAN_AddTest(NULL, b, a1, a2, a3))
+			break;
+		a1 = va_arg(ap, char *);
+		good = 1;
+	}
+	if (!good)
+		/* XXX: report error how ? */
+		BAN_Free(b);
+	else
+		BAN_Insert(b);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_ban_string(struct sess *sp, const char *str)
+{
+	char *a1, *a2, *a3;
+	char **av;
+	struct ban *b;
+	int good;
+	int i;
+
+	(void)sp;
+	av = VAV_Parse(str, NULL, ARGV_NOESC);
+	if (av[0] != NULL) {
+		/* XXX: report error how ? */
+		VAV_Free(av);
+		return;
+	}
+	b = BAN_New();
+	good = 0;
+	for (i = 1; ;) {
+		a1 = av[i++];
+		if (a1 == NULL)
+			break;
+		good = 0;
+		a2 = av[i++];
+		if (a2 == NULL)
+			break;
+		a3 = av[i++];
+		if (a3 == NULL)
+			break;
+		if (BAN_AddTest(NULL, b, a1, a2, a3))
+			break;
+		good = 1;
+		if (av[i] == NULL)
+			break;
+		good = 0;
+		if (strcmp(av[i++], "&&"))
+			break;
+	}
+	if (!good)
+		/* XXX: report error how ? */
+		BAN_Free(b);
+	else
+		BAN_Insert(b);
+	VAV_Free(av);
+}
+
+/*--------------------------------------------------------------------
+ * "real" purges
+ */
+
+void
+VRT_purge(const struct sess *sp, double ttl, double grace)
+{
+	if (sp->cur_method == VCL_MET_HIT)
+		HSH_Purge(sp, sp->obj->objcore->objhead, ttl, grace);
+	else if (sp->cur_method == VCL_MET_MISS)
+		HSH_Purge(sp, sp->objcore->objhead, ttl, grace);
+}
+
+/*--------------------------------------------------------------------
+ * Simple stuff
+ */
+
+int
+VRT_strcmp(const char *s1, const char *s2)
+{
+	if (s1 == NULL || s2 == NULL)
+		return(1);
+	return (strcmp(s1, s2));
+}
+
+void
+VRT_memmove(void *dst, const void *src, unsigned len)
+{
+
+	(void)memmove(dst, src, len);
+}
diff --git a/bin/varnishd/cache/cache_vrt_re.c b/bin/varnishd/cache/cache_vrt_re.c
new file mode 100644
index 0000000..7759d0a
--- /dev/null
+++ b/bin/varnishd/cache/cache_vrt_re.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2009 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.
+ *
+ * Runtime support for compiled VCL programs, regexps
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+
+#include "cache.h"
+
+#include "vre.h"
+#include "vrt.h"
+
+void
+VRT_re_init(void **rep, const char *re)
+{
+	vre_t *t;
+	const char *error;
+	int erroroffset;
+
+	/* This was already check-compiled by the VCL compiler */
+	t = VRE_compile(re, 0, &error, &erroroffset);
+	AN(t);
+	*rep = t;
+}
+
+void
+VRT_re_fini(void *rep)
+{
+	vre_t *vv;
+
+	vv = rep;
+	if (rep != NULL)
+		VRE_free(&vv);
+}
+
+int
+VRT_re_match(const struct sess *sp, const char *s, void *re)
+{
+	vre_t *t;
+	int i;
+
+	if (s == NULL)
+		s = "";
+	AN(re);
+	t = re;
+	i = VRE_exec(t, s, strlen(s), 0, 0, NULL, 0, &cache_param->vre_limits);
+	if (i >= 0)
+		return (1);
+	if (i < VRE_ERROR_NOMATCH )
+		WSP(sp, SLT_VCL_Error, "Regexp matching returned %d", i);
+	return (0);
+}
+
+const char *
+VRT_regsub(const struct sess *sp, int all, const char *str, void *re,
+    const char *sub)
+{
+	int ovector[30];
+	vre_t *t;
+	int i, l;
+	txt res;
+	char *b0;
+	const char *s;
+	unsigned u, x;
+	int options = 0;
+	size_t len;
+
+	AN(re);
+	if (str == NULL)
+		str = "";
+	t = re;
+	memset(ovector, 0, sizeof(ovector));
+	len = strlen(str);
+	i = VRE_exec(t, str, len, 0, options, ovector, 30,
+	    &cache_param->vre_limits);
+
+	/* If it didn't match, we can return the original string */
+	if (i == VRE_ERROR_NOMATCH)
+		return(str);
+	if (i < VRE_ERROR_NOMATCH ) {
+		WSP(sp, SLT_VCL_Error, "Regexp matching returned %d", i);
+		return(str);
+	}
+
+	u = WS_Reserve(sp->http->ws, 0);
+	res.e = res.b = b0 = sp->http->ws->f;
+	res.e += u;
+
+	do {
+		/* Copy prefix to match */
+		Tadd(&res, str, ovector[0]);
+		for (s = sub ; *s != '\0'; s++ ) {
+			if (*s != '\\' || s[1] == '\0') {
+				if (res.b < res.e)
+					*res.b++ = *s;
+				continue;
+			}
+			s++;
+			if (isdigit(*s)) {
+				x = *s - '0';
+				l = ovector[2*x+1] - ovector[2*x];
+				Tadd(&res, str + ovector[2*x], l);
+				continue;
+			} else {
+				if (res.b < res.e)
+					*res.b++ = *s;
+			}
+		}
+		str += ovector[1];
+		len -= ovector[1];
+		if (!all)
+			break;
+		memset(&ovector, 0, sizeof(ovector));
+		options |= VRE_NOTEMPTY_ATSTART;
+		i = VRE_exec(t, str, len, 0, options, ovector, 30,
+		    &cache_param->vre_limits);
+		if (i < VRE_ERROR_NOMATCH ) {
+			WS_Release(sp->http->ws, 0);
+			WSP(sp, SLT_VCL_Error,
+			    "Regexp matching returned %d", i);
+			return(str);
+		}
+	} while (i != VRE_ERROR_NOMATCH);
+
+	/* Copy suffix to match */
+	Tadd(&res, str, len+1);
+	if (res.b >= res.e) {
+		WS_Release(sp->http->ws, 0);
+		return (str);
+	}
+	Tcheck(res);
+	WS_ReleaseP(sp->http->ws, res.b);
+	return (b0);
+}
diff --git a/bin/varnishd/cache/cache_vrt_var.c b/bin/varnishd/cache/cache_vrt_var.c
new file mode 100644
index 0000000..860c7aa
--- /dev/null
+++ b/bin/varnishd/cache/cache_vrt_var.c
@@ -0,0 +1,550 @@
+/*-
+ * 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.
+ *
+ * Runtime support for compiled VCL programs
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cache.h"
+#include "common/heritage.h"
+
+#include "cache_backend.h"
+#include "vrt_obj.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+static char vrt_hostname[255] = "";
+
+/*--------------------------------------------------------------------*/
+
+static void
+vrt_do_string(struct worker *w, int fd, const struct http *hp, int fld,
+    const char *err, const char *p, va_list ap)
+{
+	char *b;
+
+	// AN(p);
+	AN(hp);
+	b = VRT_String(hp->ws, NULL, p, ap);
+	if (b == NULL || *b == '\0') {
+		WSL(w, SLT_LostHeader, fd, err);
+	} else {
+		http_SetH(hp, fld, b);
+	}
+	va_end(ap);
+}
+
+#define VRT_DO_HDR(obj, hdr, http, fld)				\
+void								\
+VRT_l_##obj##_##hdr(const struct sess *sp, const char *p, ...)	\
+{								\
+	va_list ap;						\
+								\
+	va_start(ap, p);					\
+	vrt_do_string(sp->wrk, sp->fd,				\
+	    http, fld, #obj "." #hdr, p, ap);			\
+	va_end(ap);						\
+}								\
+								\
+const char *							\
+VRT_r_##obj##_##hdr(const struct sess *sp)			\
+{								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	CHECK_OBJ_NOTNULL(http, HTTP_MAGIC);			\
+	return (http->hd[fld].b);				\
+}
+
+VRT_DO_HDR(req,   request,	sp->http,		HTTP_HDR_REQ)
+VRT_DO_HDR(req,   url,		sp->http,		HTTP_HDR_URL)
+VRT_DO_HDR(req,   proto,	sp->http,		HTTP_HDR_PROTO)
+VRT_DO_HDR(bereq, request,	sp->wrk->bereq,		HTTP_HDR_REQ)
+VRT_DO_HDR(bereq, url,		sp->wrk->bereq,		HTTP_HDR_URL)
+VRT_DO_HDR(bereq, proto,	sp->wrk->bereq,		HTTP_HDR_PROTO)
+VRT_DO_HDR(obj,   proto,	sp->obj->http,		HTTP_HDR_PROTO)
+VRT_DO_HDR(obj,   response,	sp->obj->http,		HTTP_HDR_RESPONSE)
+VRT_DO_HDR(resp,  proto,	sp->wrk->resp,		HTTP_HDR_PROTO)
+VRT_DO_HDR(resp,  response,	sp->wrk->resp,		HTTP_HDR_RESPONSE)
+VRT_DO_HDR(beresp,  proto,	sp->wrk->beresp,	HTTP_HDR_PROTO)
+VRT_DO_HDR(beresp,  response,	sp->wrk->beresp,	HTTP_HDR_RESPONSE)
+
+/*--------------------------------------------------------------------*/
+
+#define VRT_DO_STATUS(obj, http)				\
+void								\
+VRT_l_##obj##_status(const struct sess *sp, int num)		\
+{								\
+								\
+	assert(num >= 100 && num <= 999);			\
+	http->status = (uint16_t)num;				\
+}								\
+								\
+int								\
+VRT_r_##obj##_status(const struct sess *sp)			\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	return(http->status);					\
+}
+
+VRT_DO_STATUS(obj, sp->obj->http)
+VRT_DO_STATUS(beresp, sp->wrk->beresp)
+VRT_DO_STATUS(resp, sp->wrk->resp)
+
+/*--------------------------------------------------------------------*/
+
+/* XXX: review this */
+/* Add an objecthead to the saintmode list for the (hopefully) relevant
+ * backend. Some double-up asserting here to avoid assert-errors when there
+ * is no object.
+ */
+void
+VRT_l_beresp_saintmode(const struct sess *sp, double a)
+{
+	struct trouble *new;
+	struct trouble *tr;
+	struct trouble *tr2;
+	struct worker *wrk;
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	wrk = sp->wrk;
+	if (!wrk->vbc)
+		return;
+	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
+	if (!wrk->vbc->backend)
+		return;
+	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
+	if (!sp->objcore)
+		return;
+	CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
+
+	/* Setting a negative holdoff period is a mistake. Detecting this
+	 * when compiling the VCL would be better.
+	 */
+	assert(a > 0);
+
+	ALLOC_OBJ(new, TROUBLE_MAGIC);
+	AN(new);
+	new->target = (uintptr_t)(sp->objcore->objhead);
+	new->timeout = sp->t_req + a;
+
+	/* Insert the new item on the list before the first item with a
+	 * timeout at a later date (ie: sort by which entry will time out
+	 * from the list
+	 */
+	Lck_Lock(&wrk->vbc->backend->mtx);
+	VTAILQ_FOREACH_SAFE(tr, &wrk->vbc->backend->troublelist, list, tr2) {
+		if (tr->timeout < new->timeout) {
+			VTAILQ_INSERT_BEFORE(tr, new, list);
+			new = NULL;
+			break;
+		}
+	}
+
+	/* Insert the item at the end if the list is empty or all other
+	 * items have a longer timeout.
+	 */
+	if (new)
+		VTAILQ_INSERT_TAIL(&wrk->vbc->backend->troublelist, new, list);
+
+	Lck_Unlock(&wrk->vbc->backend->mtx);
+}
+
+/*--------------------------------------------------------------------*/
+
+#define VBERESP(dir, type, onm, field)					\
+void									\
+VRT_l_##dir##_##onm(const struct sess *sp, type a)			\
+{									\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
+	sp->wrk->field = a;						\
+}									\
+									\
+type									\
+VRT_r_##dir##_##onm(const struct sess *sp)				\
+{									\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
+	return (sp->wrk->field);					\
+}
+
+VBERESP(beresp, unsigned, do_esi, do_esi)
+VBERESP(beresp, unsigned, do_gzip, do_gzip)
+VBERESP(beresp, unsigned, do_gunzip, do_gunzip)
+VBERESP(beresp, unsigned, do_stream, do_stream)
+
+/*--------------------------------------------------------------------*/
+
+const char * __match_proto__()
+VRT_r_client_identity(struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	if (sp->client_identity != NULL)
+		return (sp->client_identity);
+	else
+		return (sp->addr);
+}
+
+void
+VRT_l_client_identity(struct sess *sp, const char *str, ...)
+{
+	va_list ap;
+	char *b;
+
+	va_start(ap, str);
+	b = VRT_String(sp->http->ws, NULL, str, ap);
+	va_end(ap);
+	sp->client_identity = b;
+}
+
+/*--------------------------------------------------------------------*/
+
+#define BEREQ_TIMEOUT(which)					\
+void __match_proto__()						\
+VRT_l_bereq_##which(struct sess *sp, double num)		\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	sp->wrk->which = (num > 0.0 ? num : 0.0);		\
+}								\
+								\
+double __match_proto__()					\
+VRT_r_bereq_##which(struct sess *sp)				\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	return(sp->wrk->which);					\
+}
+
+BEREQ_TIMEOUT(connect_timeout)
+BEREQ_TIMEOUT(first_byte_timeout)
+BEREQ_TIMEOUT(between_bytes_timeout)
+
+/*--------------------------------------------------------------------*/
+
+const char *
+VRT_r_beresp_backend_name(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
+	return(sp->wrk->vbc->backend->vcl_name);
+}
+
+struct sockaddr_storage *
+VRT_r_beresp_backend_ip(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
+	return(sp->wrk->vbc->addr);
+}
+
+int
+VRT_r_beresp_backend_port(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
+	return (VTCP_port(sp->wrk->vbc->addr));
+}
+
+const char * __match_proto__()
+VRT_r_beresp_storage(struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	if (sp->wrk->storage_hint != NULL)
+		return (sp->wrk->storage_hint);
+	else
+		return (NULL);
+}
+
+void __match_proto__()
+VRT_l_beresp_storage(struct sess *sp, const char *str, ...)
+{
+	va_list ap;
+	char *b;
+
+	va_start(ap, str);
+	b = VRT_String(sp->wrk->ws, NULL, str, ap);
+	va_end(ap);
+	sp->wrk->storage_hint = b;
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_l_req_backend(struct sess *sp, struct director *be)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	sp->director = be;
+}
+
+struct director * __match_proto__()
+VRT_r_req_backend(struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return (sp->director);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+VRT_l_req_esi(struct sess *sp, unsigned process_esi)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	/*
+	 * Only allow you to turn of esi in the main request
+	 * else everything gets confused
+	 */
+	if(sp->esi_level == 0)
+		sp->disable_esi = !process_esi;
+}
+
+unsigned __match_proto__()
+VRT_r_req_esi(struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return (!sp->disable_esi);
+}
+
+int
+VRT_r_req_esi_level(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return(sp->esi_level);
+}
+
+/*--------------------------------------------------------------------*/
+
+unsigned __match_proto__()
+VRT_r_req_can_gzip(struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return (RFC2616_Req_Gzip(sp));
+}
+
+
+/*--------------------------------------------------------------------*/
+
+int
+VRT_r_req_restarts(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	return (sp->restarts);
+}
+
+/*--------------------------------------------------------------------
+ * NB: TTL is relative to when object was created, whereas grace and
+ * keep are relative to ttl.
+ */
+
+#define VRT_DO_EXP(which, exp, fld, offset, extra)		\
+								\
+void __match_proto__()						\
+VRT_l_##which##_##fld(struct sess *sp, double a)		\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	if (a > 0.)						\
+		a += offset;					\
+	EXP_Set_##fld(&exp, a);					\
+	extra;							\
+}								\
+								\
+double __match_proto__()					\
+VRT_r_##which##_##fld(struct sess *sp)				\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	return(EXP_Get_##fld(&exp) - offset);			\
+}
+
+static void
+vrt_wsp_exp(const struct sess *sp, unsigned xid, const struct exp *e)
+{
+	WSP(sp, SLT_TTL, "%u VCL %.0f %.0f %.0f %.0f %.0f",
+	    xid, e->ttl - (sp->t_req - e->entered), e->grace, e->keep,
+	    sp->t_req, e->age + (sp->t_req - e->entered));
+}
+
+VRT_DO_EXP(req, sp->exp, ttl, 0, )
+VRT_DO_EXP(req, sp->exp, grace, 0, )
+VRT_DO_EXP(req, sp->exp, keep, 0, )
+
+VRT_DO_EXP(obj, sp->obj->exp, grace, 0,
+   EXP_Rearm(sp->obj);
+   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
+VRT_DO_EXP(obj, sp->obj->exp, ttl, (sp->t_req - sp->obj->exp.entered),
+   EXP_Rearm(sp->obj);
+   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
+VRT_DO_EXP(obj, sp->obj->exp, keep, 0,
+   EXP_Rearm(sp->obj);
+   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
+
+VRT_DO_EXP(beresp, sp->wrk->exp, grace, 0,
+   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
+VRT_DO_EXP(beresp, sp->wrk->exp, ttl, 0,
+   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
+VRT_DO_EXP(beresp, sp->wrk->exp, keep, 0,
+   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
+
+/*--------------------------------------------------------------------
+ * req.xid
+ */
+
+const char * __match_proto__()
+VRT_r_req_xid(struct sess *sp)
+{
+	char *p;
+	int size;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	size = snprintf(NULL, 0, "%u", sp->xid) + 1;
+	AN(p = WS_Alloc(sp->http->ws, size));
+	assert(snprintf(p, size, "%u", sp->xid) < size);
+	return (p);
+}
+
+/*--------------------------------------------------------------------*/
+
+#define REQ_BOOL(which)						\
+void __match_proto__()						\
+VRT_l_req_##which(struct sess *sp, unsigned val)		\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	sp->which = val ? 1 : 0;				\
+}								\
+								\
+unsigned __match_proto__()					\
+VRT_r_req_##which(struct sess *sp)				\
+{								\
+								\
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
+	return(sp->which);					\
+}
+
+REQ_BOOL(hash_ignore_busy)
+REQ_BOOL(hash_always_miss)
+
+/*--------------------------------------------------------------------*/
+
+struct sockaddr_storage *
+VRT_r_client_ip(struct sess *sp)
+{
+
+	return (&sp->sockaddr);
+}
+
+struct sockaddr_storage *
+VRT_r_server_ip(struct sess *sp)
+{
+	int i;
+
+	if (sp->mysockaddr.ss_family == AF_UNSPEC) {
+		i = getsockname(sp->fd,
+		    (void*)&sp->mysockaddr, &sp->mysockaddrlen);
+		assert(VTCP_Check(i));
+	}
+
+	return (&sp->mysockaddr);
+}
+
+const char*
+VRT_r_server_identity(struct sess *sp)
+{
+	(void)sp;
+
+	if (heritage.identity[0] != '\0')
+		return (heritage.identity);
+	else
+		return (heritage.name);
+}
+
+
+const char*
+VRT_r_server_hostname(struct sess *sp)
+{
+	(void)sp;
+
+	if (vrt_hostname[0] == '\0')
+		AZ(gethostname(vrt_hostname, sizeof(vrt_hostname)));
+
+	return (vrt_hostname);
+}
+
+/*--------------------------------------------------------------------
+ * XXX: This is pessimistically silly
+ */
+
+int
+VRT_r_server_port(struct sess *sp)
+{
+	int i;
+
+	if (sp->mysockaddr.ss_family == AF_UNSPEC) {
+		i = getsockname(sp->fd,
+		    (void*)&sp->mysockaddr, &sp->mysockaddrlen);
+		assert(VTCP_Check(i));
+	}
+	return (VTCP_port(&sp->mysockaddr));
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+VRT_r_obj_hits(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
+	return (sp->obj->hits);
+}
+
+double
+VRT_r_obj_lastuse(const struct sess *sp)
+{
+
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);	/* XXX */
+	return (VTIM_real() - sp->obj->last_use);
+}
+
+unsigned
+VRT_r_req_backend_healthy(const struct sess *sp)
+{
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+	CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
+	return (VDI_Healthy(sp->director, sp));
+}
+
diff --git a/bin/varnishd/cache/cache_vrt_vmod.c b/bin/varnishd/cache/cache_vrt_vmod.c
new file mode 100644
index 0000000..6b3b846
--- /dev/null
+++ b/bin/varnishd/cache/cache_vrt_vmod.c
@@ -0,0 +1,180 @@
+/*-
+ * 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.
+ *
+ * Runtime support for compiled VCL programs
+ */
+
+#include "config.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "vcli_priv.h"
+#include "vrt.h"
+
+/*--------------------------------------------------------------------
+ * Modules stuff
+ */
+
+struct vmod {
+	unsigned		magic;
+#define VMOD_MAGIC		0xb750219c
+
+	VTAILQ_ENTRY(vmod)	list;
+
+	int			ref;
+
+	char			*nm;
+	char			*path;
+	void			*hdl;
+	const void		*funcs;
+	int			funclen;
+	const void		*idptr;
+};
+
+static VTAILQ_HEAD(,vmod)	vmods = VTAILQ_HEAD_INITIALIZER(vmods);
+
+int
+VRT_Vmod_Init(void **hdl, void *ptr, int len, const char *nm,
+    const char *path, struct cli *cli)
+{
+	struct vmod *v;
+	void *x, *y, *z, *w;
+
+	ASSERT_CLI();
+
+	VTAILQ_FOREACH(v, &vmods, list)
+		if (!strcmp(v->nm, nm))	// Also path, len ?
+			break;
+	if (v == NULL) {
+		ALLOC_OBJ(v, VMOD_MAGIC);
+		AN(v);
+
+		v->hdl = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+		if (v->hdl == NULL) {
+			VCLI_Out(cli, "Loading VMOD %s from %s:\n", nm, path);
+			VCLI_Out(cli, "dlopen() failed: %s\n", dlerror());
+			VCLI_Out(cli, "Check child process permissions.\n");
+			FREE_OBJ(v);
+			return (1);
+		}
+
+		x = dlsym(v->hdl, "Vmod_Name");
+		y = dlsym(v->hdl, "Vmod_Len");
+		z = dlsym(v->hdl, "Vmod_Func");
+		w = dlsym(v->hdl, "Vmod_Id");
+		if (x == NULL || y == NULL || z == NULL || w == NULL) {
+			VCLI_Out(cli, "Loading VMOD %s from %s:\n", nm, path);
+			VCLI_Out(cli, "VMOD symbols not found\n");
+			VCLI_Out(cli, "Check relative pathnames.\n");
+			(void)dlclose(v->hdl);
+			FREE_OBJ(v);
+			return (1);
+		}
+		AN(x);
+		AN(y);
+		AN(z);
+		AN(w);
+		if (strcmp(x, nm)) {
+			VCLI_Out(cli, "Loading VMOD %s from %s:\n", nm, path);
+			VCLI_Out(cli, "File contain wrong VMOD (\"%s\")\n", x);
+			VCLI_Out(cli, "Check relative pathnames ?.\n");
+			(void)dlclose(v->hdl);
+			FREE_OBJ(v);
+			return (1);
+		}
+
+		v->funclen = *(const int *)y;
+		v->funcs = z;
+
+		REPLACE(v->nm, nm);
+		REPLACE(v->path, path);
+
+		VSC_C_main->vmods++;
+		VTAILQ_INSERT_TAIL(&vmods, v, list);
+		v->idptr = w;
+	}
+
+	assert(len == v->funclen);
+	memcpy(ptr, v->funcs, v->funclen);
+	v->ref++;
+
+	*hdl = v;
+	return (0);
+}
+
+void
+VRT_Vmod_Fini(void **hdl)
+{
+	struct vmod *v;
+
+	ASSERT_CLI();
+
+	AN(*hdl);
+	CAST_OBJ_NOTNULL(v, *hdl, VMOD_MAGIC);
+	*hdl = NULL;
+	if (--v->ref != 0)
+		return;
+#ifndef DONT_DLCLOSE_VMODS
+	AZ(dlclose(v->hdl));
+#endif
+	free(v->nm);
+	free(v->path);
+	VTAILQ_REMOVE(&vmods, v, list);
+	VSC_C_main->vmods--;
+	FREE_OBJ(v);
+}
+
+/*---------------------------------------------------------------------*/
+
+static void
+ccf_debug_vmod(struct cli *cli, const char * const *av, void *priv)
+{
+	struct vmod *v;
+
+	(void)av;
+	(void)priv;
+	ASSERT_CLI();
+	VTAILQ_FOREACH(v, &vmods, list)
+		VCLI_Out(cli, "%5d %s (%s)\n", v->ref, v->nm, v->path);
+}
+
+static struct cli_proto vcl_cmds[] = {
+	{ "debug.vmod", "debug.vmod", "show loaded vmods", 0, 0,
+		"d", ccf_debug_vmod },
+	{ NULL }
+};
+
+void
+VMOD_Init(void)
+{
+
+	CLI_AddFuncs(vcl_cmds);
+}
diff --git a/bin/varnishd/cache/cache_wrk.c b/bin/varnishd/cache/cache_wrk.c
new file mode 100644
index 0000000..bfee84c
--- /dev/null
+++ b/bin/varnishd/cache/cache_wrk.c
@@ -0,0 +1,204 @@
+/*-
+ * 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.
+ *
+ * Worker thread stuff unrelated to the worker thread pools.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "cache.h"
+
+#include "hash/hash_slinger.h"
+#include "vsha256.h"
+
+static struct lock		wstat_mtx;
+
+/*--------------------------------------------------------------------*/
+
+static void
+wrk_sumstat(struct worker *w)
+{
+
+	Lck_AssertHeld(&wstat_mtx);
+#define L0(n)
+#define L1(n) (VSC_C_main->n += w->stats.n)
+#define VSC_DO_MAIN
+#define VSC_F(n, t, l, f, d, e) L##l(n);
+#include "tbl/vsc_fields.h"
+#undef VSC_F
+#undef VSC_DO_MAIN
+#undef L0
+#undef L1
+	memset(&w->stats, 0, sizeof w->stats);
+}
+
+void
+WRK_SumStat(struct worker *w)
+{
+
+	Lck_Lock(&wstat_mtx);
+	wrk_sumstat(w);
+	Lck_Unlock(&wstat_mtx);
+}
+
+int
+WRK_TrySumStat(struct worker *w)
+{
+	if (Lck_Trylock(&wstat_mtx))
+		return (0);
+	wrk_sumstat(w);
+	Lck_Unlock(&wstat_mtx);
+	return (1);
+}
+
+/*--------------------------------------------------------------------
+ * Create and starte a back-ground thread which as its own worker and
+ * session data structures;
+ */
+
+struct bgthread {
+	unsigned	magic;
+#define BGTHREAD_MAGIC	0x23b5152b
+	const char	*name;
+	bgthread_t	*func;
+	void		*priv;
+};
+
+static void *
+wrk_bgthread(void *arg)
+{
+	struct bgthread *bt;
+	struct worker ww;
+	struct sess *sp;
+	uint32_t logbuf[1024];	/* XXX:  size ? */
+
+	CAST_OBJ_NOTNULL(bt, arg, BGTHREAD_MAGIC);
+	THR_SetName(bt->name);
+	sp = SES_Alloc();
+	XXXAN(sp);
+	memset(&ww, 0, sizeof ww);
+	sp->wrk = &ww;
+	ww.magic = WORKER_MAGIC;
+	ww.wlp = ww.wlb = logbuf;
+	ww.wle = logbuf + (sizeof logbuf) / 4;
+
+	(void)bt->func(sp, bt->priv);
+
+	WRONG("BgThread terminated");
+
+	NEEDLESS_RETURN(NULL);
+}
+
+void
+WRK_BgThread(pthread_t *thr, const char *name, bgthread_t *func, void *priv)
+{
+	struct bgthread *bt;
+
+	ALLOC_OBJ(bt, BGTHREAD_MAGIC);
+	AN(bt);
+
+	bt->name = name;
+	bt->func = func;
+	bt->priv = priv;
+	AZ(pthread_create(thr, NULL, wrk_bgthread, bt));
+}
+
+/*--------------------------------------------------------------------*/
+
+static void *
+wrk_thread_real(void *priv, unsigned shm_workspace, unsigned sess_workspace,
+    uint16_t nhttp, unsigned http_space, unsigned siov)
+{
+	struct worker *w, ww;
+	uint32_t wlog[shm_workspace / 4];
+	/* XXX: can we trust these to be properly aligned ? */
+	unsigned char ws[sess_workspace];
+	unsigned char http0[http_space];
+	unsigned char http1[http_space];
+	unsigned char http2[http_space];
+	struct iovec iov[siov];
+	struct SHA256Context sha256;
+
+	THR_SetName("cache-worker");
+	w = &ww;
+	memset(w, 0, sizeof *w);
+	w->magic = WORKER_MAGIC;
+	w->lastused = NAN;
+	w->wlb = w->wlp = wlog;
+	w->wle = wlog + (sizeof wlog) / 4;
+	w->sha256ctx = &sha256;
+	w->bereq = HTTP_create(http0, nhttp);
+	w->beresp = HTTP_create(http1, nhttp);
+	w->resp = HTTP_create(http2, nhttp);
+	w->wrw.iov = iov;
+	w->wrw.siov = siov;
+	w->wrw.ciov = siov;
+	AZ(pthread_cond_init(&w->cond, NULL));
+
+	WS_Init(w->ws, "wrk", ws, sess_workspace);
+
+	VSL(SLT_WorkThread, 0, "%p start", w);
+
+	Pool_Work_Thread(priv, w);
+	AZ(w->pool);
+
+	VSL(SLT_WorkThread, 0, "%p end", w);
+	if (w->vcl != NULL)
+		VCL_Rel(&w->vcl);
+	AZ(pthread_cond_destroy(&w->cond));
+	HSH_Cleanup(w);
+	WRK_SumStat(w);
+	return (NULL);
+}
+
+void *
+WRK_thread(void *priv)
+{
+	uint16_t nhttp;
+	unsigned siov;
+
+	assert(cache_param->http_max_hdr <= 65535);
+	/* We need to snapshot these two for consistency */
+	nhttp = (uint16_t)cache_param->http_max_hdr;
+	siov = nhttp * 2;
+	if (siov > IOV_MAX)
+		siov = IOV_MAX;
+	return (wrk_thread_real(priv,
+	    cache_param->shm_workspace,
+	    cache_param->wthread_workspace,
+	    nhttp, HTTP_estimate(nhttp), siov));
+}
+
+void
+WRK_Init(void)
+{
+	Lck_New(&wstat_mtx, lck_wstat);
+}
diff --git a/bin/varnishd/cache/cache_wrw.c b/bin/varnishd/cache/cache_wrw.c
new file mode 100644
index 0000000..2160f69
--- /dev/null
+++ b/bin/varnishd/cache/cache_wrw.c
@@ -0,0 +1,344 @@
+/*-
+ * 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.
+ *
+ * Write data to fd
+ * We try to use writev() if possible in order to minimize number of
+ * syscalls made and packets sent.  It also just might allow the worker
+ * thread to complete the request without holding stuff locked.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#ifdef SENDFILE_WORKS
+#  if defined(__FreeBSD__) || defined(__DragonFly__)
+#    include <sys/socket.h>
+#  elif defined(__linux__)
+#    include <sys/sendfile.h>
+#  elif defined(__sun)
+#    include <sys/sendfile.h>
+#  else
+#     error Unknown sendfile() implementation
+#  endif
+#endif /* SENDFILE_WORKS */
+#include <sys/uio.h>
+
+#include <stdio.h>
+
+#include "cache.h"
+#include "vtim.h"
+
+/*--------------------------------------------------------------------
+ */
+
+int
+WRW_Error(const struct worker *w)
+{
+
+	return (w->wrw.werr);
+}
+
+void
+WRW_Reserve(struct worker *w, int *fd)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+	AZ(wrw->wfd);
+	wrw->werr = 0;
+	wrw->liov = 0;
+	wrw->niov = 0;
+	wrw->ciov = wrw->siov;
+	wrw->wfd = fd;
+}
+
+static void
+WRW_Release(struct worker *w)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+	AN(wrw->wfd);
+	wrw->werr = 0;
+	wrw->liov = 0;
+	wrw->niov = 0;
+	wrw->ciov = wrw->siov;
+	wrw->wfd = NULL;
+}
+
+unsigned
+WRW_Flush(struct worker *w)
+{
+	ssize_t i;
+	struct wrw *wrw;
+	char cbuf[32];
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+	AN(wrw->wfd);
+
+	/* For chunked, there must be one slot reserved for the chunked tail */
+	if (wrw->ciov < wrw->siov)
+		assert(wrw->niov < wrw->siov);
+
+	if (*wrw->wfd >= 0 && wrw->liov > 0 && wrw->werr == 0) {
+		if (wrw->ciov < wrw->siov && wrw->cliov > 0) {
+			bprintf(cbuf, "00%jx\r\n", (intmax_t)wrw->cliov);
+			i = strlen(cbuf);
+			wrw->iov[wrw->ciov].iov_base = cbuf;
+			wrw->iov[wrw->ciov].iov_len = i;
+			wrw->liov += i;
+
+			wrw->iov[wrw->niov].iov_base = cbuf + i - 2;
+			wrw->iov[wrw->niov++].iov_len = 2;
+			wrw->liov += 2;
+		} else if (wrw->ciov < wrw->siov) {
+			wrw->iov[wrw->ciov].iov_base = cbuf;
+			wrw->iov[wrw->ciov].iov_len = 0;
+		}
+		i = writev(*wrw->wfd, wrw->iov, wrw->niov);
+		while (i != wrw->liov && i > 0) {
+			/* Remove sent data from start of I/O vector,
+			 * then retry; we hit a timeout, but some data
+			 * was sent.
+
+			 XXX: Add a "minimum sent data per timeout
+			 counter to prevent slowlaris attacks
+			*/
+			size_t used = 0;
+
+			if (VTIM_real() - w->sp->t_resp > cache_param->send_timeout) {
+				WSL(w, SLT_Debug, *wrw->wfd,
+				    "Hit total send timeout, wrote = %ld/%ld; not retrying",
+				    i, wrw->liov);
+				i = -1;
+				break;
+			}
+
+			WSL(w, SLT_Debug, *wrw->wfd,
+			    "Hit send timeout, wrote = %ld/%ld; retrying",
+			    i, wrw->liov);
+
+			for (int j = 0; j < wrw->niov; j++) {
+				if (used + wrw->iov[j].iov_len > i) {
+					/* Cutoff is in this iov */
+					int used_here = i - used;
+					wrw->iov[j].iov_len -= used_here;
+					wrw->iov[j].iov_base = (char*)wrw->iov[j].iov_base + used_here;
+					memmove(wrw->iov, &wrw->iov[j],
+						(wrw->niov - j) * sizeof(struct iovec));
+					wrw->niov -= j;
+					wrw->liov -= i;
+					break;
+				}
+				used += wrw->iov[j].iov_len;
+			}
+			i = writev(*wrw->wfd, wrw->iov, wrw->niov);
+		}
+		if (i <= 0) {
+			wrw->werr++;
+			WSL(w, SLT_Debug, *wrw->wfd,
+			    "Write error, retval = %d, len = %d, errno = %s",
+			    i, wrw->liov, strerror(errno));
+		}
+	}
+	wrw->liov = 0;
+	wrw->cliov = 0;
+	wrw->niov = 0;
+	if (wrw->ciov < wrw->siov)
+		wrw->ciov = wrw->niov++;
+	return (wrw->werr);
+}
+
+unsigned
+WRW_FlushRelease(struct worker *w)
+{
+	unsigned u;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AN(w->wrw.wfd);
+	u = WRW_Flush(w);
+	WRW_Release(w);
+	return (u);
+}
+
+unsigned
+WRW_WriteH(struct worker *w, const txt *hh, const char *suf)
+{
+	unsigned u;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	AN(w->wrw.wfd);
+	AN(w);
+	AN(hh);
+	AN(hh->b);
+	AN(hh->e);
+	u = WRW_Write(w, hh->b, hh->e - hh->b);
+	if (suf != NULL)
+		u += WRW_Write(w, suf, -1);
+	return (u);
+}
+
+unsigned
+WRW_Write(struct worker *w, const void *ptr, int len)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+	AN(wrw->wfd);
+	if (len == 0 || *wrw->wfd < 0)
+		return (0);
+	if (len == -1)
+		len = strlen(ptr);
+	if (wrw->niov >= wrw->siov - (wrw->ciov < wrw->siov ? 1 : 0))
+		(void)WRW_Flush(w);
+	wrw->iov[wrw->niov].iov_base = TRUST_ME(ptr);
+	wrw->iov[wrw->niov].iov_len = len;
+	wrw->liov += len;
+	wrw->niov++;
+	if (wrw->ciov < wrw->siov) {
+		assert(wrw->niov < wrw->siov);
+		wrw->cliov += len;
+	}
+	return (len);
+}
+
+void
+WRW_Chunked(struct worker *w)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+
+	assert(wrw->ciov == wrw->siov);
+	/*
+	 * If there are not space for chunked header, a chunk of data and
+	 * a chunk tail, we might as well flush right away.
+	 */
+	if (wrw->niov + 3 >= wrw->siov)
+		(void)WRW_Flush(w);
+	wrw->ciov = wrw->niov++;
+	wrw->cliov = 0;
+	assert(wrw->ciov < wrw->siov);
+	assert(wrw->niov < wrw->siov);
+}
+
+/*
+ * XXX: It is not worth the complexity to attempt to get the
+ * XXX: end of chunk into the WRW_Flush(), because most of the time
+ * XXX: if not always, that is a no-op anyway, because the calling
+ * XXX: code already called WRW_Flush() to release local storage.
+ */
+
+void
+WRW_EndChunk(struct worker *w)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+
+	assert(wrw->ciov < wrw->siov);
+	(void)WRW_Flush(w);
+	wrw->ciov = wrw->siov;
+	wrw->niov = 0;
+	wrw->cliov = 0;
+	(void)WRW_Write(w, "0\r\n\r\n", -1);
+}
+
+
+#ifdef SENDFILE_WORKS
+void
+WRW_Sendfile(struct worker *w, int fd, off_t off, unsigned len)
+{
+	struct wrw *wrw;
+
+	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
+	wrw = &w->wrw;
+	AN(wrw->wfd);
+	assert(fd >= 0);
+	assert(len > 0);
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+	do {
+		struct sf_hdtr sfh;
+		memset(&sfh, 0, sizeof sfh);
+		if (wrw->niov > 0) {
+			sfh.headers = wrw->iov;
+			sfh.hdr_cnt = wrw->niov;
+		}
+		if (sendfile(fd, *wrw->wfd, off, len, &sfh, NULL, 0) != 0)
+			wrw->werr++;
+		wrw->liov = 0;
+		wrw->niov = 0;
+	} while (0);
+#elif defined(__linux__)
+	do {
+		if (WRW_Flush(w) == 0 &&
+		    sendfile(*wrw->wfd, fd, &off, len) != len)
+			wrw->werr++;
+	} while (0);
+#elif defined(__sun) && defined(HAVE_SENDFILEV)
+	do {
+		sendfilevec_t svvec[cache_param->http_headers * 2 + 1];
+		size_t xferred = 0, expected = 0;
+		int i;
+		for (i = 0; i < wrw->niov; i++) {
+			svvec[i].sfv_fd = SFV_FD_SELF;
+			svvec[i].sfv_flag = 0;
+			svvec[i].sfv_off = (off_t) wrw->iov[i].iov_base;
+			svvec[i].sfv_len = wrw->iov[i].iov_len;
+			expected += svvec[i].sfv_len;
+		}
+		svvec[i].sfv_fd = fd;
+		svvec[i].sfv_flag = 0;
+		svvec[i].sfv_off = off;
+		svvec[i].sfv_len = len;
+		expected += svvec[i].sfv_len;
+		if (sendfilev(*wrw->wfd, svvec, i, &xferred) == -1 ||
+		    xferred != expected)
+			wrw->werr++;
+		wrw->liov = 0;
+		wrw->niov = 0;
+	} while (0);
+#elif defined(__sun) && defined(HAVE_SENDFILE)
+	do {
+		if (WRW_Flush(w) == 0 &&
+		    sendfile(*wrw->wfd, fd, &off, len) != len)
+			wrw->werr++;
+	} while (0);
+#else
+#error Unknown sendfile() implementation
+#endif
+}
+#endif /* SENDFILE_WORKS */
+
diff --git a/bin/varnishd/cache/cache_ws.c b/bin/varnishd/cache/cache_ws.c
new file mode 100644
index 0000000..9fca215
--- /dev/null
+++ b/bin/varnishd/cache/cache_ws.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2010 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 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"
+
+void
+WS_Assert(const struct ws *ws)
+{
+
+	CHECK_OBJ_NOTNULL(ws, WS_MAGIC);
+	DSL(0x02, SLT_Debug, 0, "WS(%p = (%s, %p %u %u %u)",
+	    ws, ws->id, ws->s, pdiff(ws->s, ws->f),
+	    ws->r == NULL ? 0 : pdiff(ws->f, ws->r),
+	    pdiff(ws->s, ws->e));
+	assert(ws->s != NULL);
+	assert(PAOK(ws->s));
+	assert(ws->e != NULL);
+	assert(PAOK(ws->e));
+	assert(ws->s < ws->e);
+	assert(ws->f >= ws->s);
+	assert(ws->f <= ws->e);
+	assert(PAOK(ws->f));
+	if (ws->r) {
+		assert(ws->r > ws->s);
+		assert(ws->r <= ws->e);
+		assert(PAOK(ws->r));
+	}
+}
+
+void
+WS_Init(struct ws *ws, const char *id, void *space, unsigned len)
+{
+
+	DSL(0x02, SLT_Debug, 0,
+	    "WS_Init(%p, \"%s\", %p, %u)", ws, id, space, len);
+	assert(space != NULL);
+	memset(ws, 0, sizeof *ws);
+	ws->magic = WS_MAGIC;
+	ws->s = space;
+	assert(PAOK(space));
+	ws->e = ws->s + len;
+	assert(PAOK(len));
+	ws->f = ws->s;
+	ws->id = id;
+	WS_Assert(ws);
+}
+
+/*
+ * Reset a WS to start or a given pointer, likely from WS_Snapshot
+ */
+
+void
+WS_Reset(struct ws *ws, char *p)
+{
+
+	WS_Assert(ws);
+	DSL(0x02, SLT_Debug, 0, "WS_Reset(%p, %p)", ws, p);
+	assert(ws->r == NULL);
+	if (p == NULL)
+		ws->f = ws->s;
+	else {
+		assert(p >= ws->s);
+		assert(p < ws->e);
+		ws->f = p;
+	}
+	WS_Assert(ws);
+}
+
+char *
+WS_Alloc(struct ws *ws, unsigned bytes)
+{
+	char *r;
+
+	WS_Assert(ws);
+	bytes = PRNDUP(bytes);
+
+	assert(ws->r == NULL);
+	if (ws->f + bytes > ws->e) {
+		ws->overflow++;
+		WS_Assert(ws);
+		return(NULL);
+	}
+	r = ws->f;
+	ws->f += bytes;
+	DSL(0x02, SLT_Debug, 0, "WS_Alloc(%p, %u) = %p", ws, bytes, r);
+	WS_Assert(ws);
+	return (r);
+}
+
+char *
+WS_Dup(struct ws *ws, const char *s)
+{
+	unsigned l;
+	char *p;
+
+	WS_Assert(ws);
+	l = strlen(s) + 1;
+	p = WS_Alloc(ws, l);
+	if (p != NULL)
+		memcpy(p, s, l);
+	DSL(0x02, SLT_Debug, 0, "WS_Dup(%p, \"%s\") = %p", ws, s, p);
+	WS_Assert(ws);
+	return (p);
+}
+
+unsigned
+WS_Free(const struct ws *ws)
+{
+
+	WS_Assert(ws);
+	return(ws->e - ws->f);
+}
+
+char *
+WS_Snapshot(struct ws *ws)
+{
+
+	WS_Assert(ws);
+	assert(ws->r == NULL);
+	DSL(0x02, SLT_Debug, 0, "WS_Snapshot(%p) = %p", ws, ws->f);
+	return (ws->f);
+}
+
+unsigned
+WS_Reserve(struct ws *ws, unsigned bytes)
+{
+	unsigned b2;
+
+	WS_Assert(ws);
+	assert(ws->r == NULL);
+	if (bytes == 0)
+		b2 = ws->e - ws->f;
+	else if (bytes > ws->e - ws->f)
+		b2 = ws->e - ws->f;
+	else
+		b2 = bytes;
+	b2 = PRNDDN(b2);
+	xxxassert(ws->f + b2 <= ws->e);
+	ws->r = ws->f + b2;
+	DSL(0x02, SLT_Debug, 0, "WS_Reserve(%p, %u/%u) = %u",
+	    ws, b2, bytes, pdiff(ws->f, ws->r));
+	WS_Assert(ws);
+	return (pdiff(ws->f, ws->r));
+}
+
+void
+WS_Release(struct ws *ws, unsigned bytes)
+{
+	WS_Assert(ws);
+	bytes = PRNDUP(bytes);
+	assert(bytes <= ws->e - ws->f);
+	DSL(0x02, SLT_Debug, 0, "WS_Release(%p, %u)", ws, bytes);
+	assert(ws->r != NULL);
+	assert(ws->f + bytes <= ws->r);
+	ws->f += bytes;
+	ws->r = NULL;
+	WS_Assert(ws);
+}
+
+void
+WS_ReleaseP(struct ws *ws, char *ptr)
+{
+	WS_Assert(ws);
+	DSL(0x02, SLT_Debug, 0, "WS_ReleaseP(%p, %p)", ws, ptr);
+	assert(ws->r != NULL);
+	assert(ptr >= ws->f);
+	assert(ptr <= ws->r);
+	ws->f += PRNDUP(ptr - ws->f);
+	ws->r = NULL;
+	WS_Assert(ws);
+}
+
+#if 0
+/* XXX: not used anywhere (yet) */
+void
+WS_Return(struct ws *ws, char *s, char *e)
+{
+
+	WS_Assert(ws);
+	if (e == ws->f)
+		ws->f = s;
+}
+#endif
diff --git a/bin/varnishd/cache_acceptor.c b/bin/varnishd/cache_acceptor.c
deleted file mode 100644
index 8c83121..0000000
--- a/bin/varnishd/cache_acceptor.c
+++ /dev/null
@@ -1,430 +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.
- *
- */
-
-#include "config.h"
-
-#include "cache.h"
-#include "common/heritage.h"
-
-#include "vcli.h"
-#include "vcli_priv.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-static pthread_t	VCA_thread;
-static struct timeval	tv_sndtimeo;
-static struct timeval	tv_rcvtimeo;
-
-/*--------------------------------------------------------------------
- * We want to get out of any kind of trouble-hit TCP connections as fast
- * as absolutely possible, so we set them LINGER enabled with zero timeout,
- * so that even if there are outstanding write data on the socket, a close(2)
- * will return immediately.
- */
-static const struct linger linger = {
-	.l_onoff	=	0,
-};
-
-static unsigned char	need_sndtimeo, need_rcvtimeo, need_linger, need_test;
-
-static void
-sock_test(int fd)
-{
-	struct linger lin;
-	struct timeval tv;
-	socklen_t l;
-	int i;
-
-	l = sizeof lin;
-	i = getsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, &l);
-	if (i) {
-		VTCP_Assert(i);
-		return;
-	}
-	assert(l == sizeof lin);
-	if (memcmp(&lin, &linger, l))
-		need_linger = 1;
-
-#ifdef SO_SNDTIMEO_WORKS
-	l = sizeof tv;
-	i = getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &l);
-	if (i) {
-		VTCP_Assert(i);
-		return;
-	}
-	assert(l == sizeof tv);
-	if (memcmp(&tv, &tv_sndtimeo, l))
-		need_sndtimeo = 1;
-#else
-	(void)tv;
-	(void)tv_sndtimeo;
-	(void)need_sndtimeo;
-#endif
-
-#ifdef SO_RCVTIMEO_WORKS
-	l = sizeof tv;
-	i = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &l);
-	if (i) {
-		VTCP_Assert(i);
-		return;
-	}
-	assert(l == sizeof tv);
-	if (memcmp(&tv, &tv_rcvtimeo, l))
-		need_rcvtimeo = 1;
-#else
-	(void)tv;
-	(void)tv_rcvtimeo;
-	(void)need_rcvtimeo;
-#endif
-
-	need_test = 0;
-}
-
-/*--------------------------------------------------------------------
- * Called once the workerthread gets hold of the session, to do setup
- * setup overhead, we don't want to bother the acceptor thread with.
- */
-
-void
-VCA_Prep(struct sess *sp)
-{
-	char addr[VTCP_ADDRBUFSIZE];
-	char port[VTCP_PORTBUFSIZE];
-
-	VTCP_name(&sp->sockaddr, sp->sockaddrlen,
-	    addr, sizeof addr, port, sizeof port);
-	sp->addr = WS_Dup(sp->ws, addr);
-	sp->port = WS_Dup(sp->ws, port);
-	if (cache_param->log_local_addr) {
-		AZ(getsockname(sp->fd, (void*)&sp->mysockaddr, &sp->mysockaddrlen));
-		VTCP_name(&sp->mysockaddr, sp->mysockaddrlen,
-		    addr, sizeof addr, port, sizeof port);
-		WSP(sp, SLT_SessionOpen, "%s %s %s %s",
-		    sp->addr, sp->port, addr, port);
-	} else {
-		WSP(sp, SLT_SessionOpen, "%s %s %s",
-		    sp->addr, sp->port, sp->mylsock->name);
-	}
-	sp->acct_ses.first = sp->t_open;
-	if (need_test)
-		sock_test(sp->fd);
-	if (need_linger)
-		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_LINGER,
-		    &linger, sizeof linger));
-#ifdef SO_SNDTIMEO_WORKS
-	if (need_sndtimeo)
-		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_SNDTIMEO,
-		    &tv_sndtimeo, sizeof tv_sndtimeo));
-#endif
-#ifdef SO_RCVTIMEO_WORKS
-	if (need_rcvtimeo)
-		VTCP_Assert(setsockopt(sp->fd, SOL_SOCKET, SO_RCVTIMEO,
-		    &tv_rcvtimeo, sizeof tv_rcvtimeo));
-#endif
-}
-
-/*--------------------------------------------------------------------
- * If accept(2)'ing fails, we pace ourselves to relive any resource
- * shortage if possible.
- */
-
-static double vca_pace = 0.0;
-static struct lock pace_mtx;
-
-static void
-vca_pace_check(void)
-{
-	double p;
-
-	if (vca_pace == 0.0)
-		return;
-	Lck_Lock(&pace_mtx);
-	p = vca_pace;
-	Lck_Unlock(&pace_mtx);
-	if (p > 0.0)
-		VTIM_sleep(p);
-}
-
-static void
-vca_pace_bad(void)
-{
-
-	Lck_Lock(&pace_mtx);
-	vca_pace += cache_param->acceptor_sleep_incr;
-	if (vca_pace > cache_param->acceptor_sleep_max)
-		vca_pace = cache_param->acceptor_sleep_max;
-	Lck_Unlock(&pace_mtx);
-}
-
-static void
-vca_pace_good(void)
-{
-
-	if (vca_pace == 0.0)
-		return;
-	Lck_Lock(&pace_mtx);
-	vca_pace *= cache_param->acceptor_sleep_decay;
-	if (vca_pace < cache_param->acceptor_sleep_incr)
-		vca_pace = 0.0;
-	Lck_Unlock(&pace_mtx);
-}
-
-/*--------------------------------------------------------------------
- * Accept on a listen socket, and handle error returns.
- */
-
-static int hack_ready;
-
-int
-VCA_Accept(struct listen_sock *ls, struct wrk_accept *wa)
-{
-	int i;
-
-	CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
-	vca_pace_check();
-
-	while(!hack_ready)
-		(void)usleep(100*1000);
-
-	wa->acceptaddrlen = sizeof wa->acceptaddr;
-	i = accept(ls->sock, (void*)&wa->acceptaddr, &wa->acceptaddrlen);
-
-	if (i < 0) {
-		switch (errno) {
-		case ECONNABORTED:
-			break;
-		case EMFILE:
-			VSL(SLT_Debug, ls->sock, "Too many open files");
-			vca_pace_bad();
-			break;
-		default:
-			VSL(SLT_Debug, ls->sock, "Accept failed: %s",
-			    strerror(errno));
-			vca_pace_bad();
-			break;
-		}
-	}
-	wa->acceptlsock = ls;
-	wa->acceptsock = i;
-	return (i);
-}
-
-/*--------------------------------------------------------------------
- * Fail a session
- *
- * This happens if we accept the socket, but cannot get a session
- * structure.
- *
- * We consider this a DoS situation (false positive:  Extremely popular
- * busy objects) and silently close the connection with minimum effort
- * and fuzz, rather than try to send an intelligent message back.
- */
-
-void
-VCA_FailSess(struct worker *w)
-{
-	struct wrk_accept *wa;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CAST_OBJ_NOTNULL(wa, (void*)w->ws->f, WRK_ACCEPT_MAGIC);
-	AZ(w->sp);
-	AZ(close(wa->acceptsock));
-	w->stats.sess_drop++;
-	vca_pace_bad();
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VCA_SetupSess(struct worker *w)
-{
-	struct sess *sp;
-	struct wrk_accept *wa;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CAST_OBJ_NOTNULL(wa, (void*)w->ws->f, WRK_ACCEPT_MAGIC);
-	sp = w->sp;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	sp->fd = wa->acceptsock;
-	sp->vsl_id = wa->acceptsock | VSL_CLIENTMARKER ;
-	wa->acceptsock = -1;
-	sp->t_open = VTIM_real();
-	sp->t_end = sp->t_open;
-	sp->mylsock = wa->acceptlsock;
-	CHECK_OBJ_NOTNULL(sp->mylsock, LISTEN_SOCK_MAGIC);
-	assert(wa->acceptaddrlen <= sp->sockaddrlen);
-	memcpy(&sp->sockaddr, &wa->acceptaddr, wa->acceptaddrlen);
-	sp->sockaddrlen = wa->acceptaddrlen;
-	sp->step = STP_FIRST;
-	vca_pace_good();
-	w->stats.sess_conn++;
-}
-
-/*--------------------------------------------------------------------*/
-
-static void *
-vca_acct(void *arg)
-{
-#ifdef SO_RCVTIMEO_WORKS
-	double sess_timeout = 0;
-#endif
-#ifdef SO_SNDTIMEO_WORKS
-	double send_timeout = 0;
-#endif
-	struct listen_sock *ls;
-	double t0, now;
-
-	THR_SetName("cache-acceptor");
-	(void)arg;
-
-	VTAILQ_FOREACH(ls, &heritage.socks, list) {
-		if (ls->sock < 0)
-			continue;
-		AZ(listen(ls->sock, cache_param->listen_depth));
-		AZ(setsockopt(ls->sock, SOL_SOCKET, SO_LINGER,
-		    &linger, sizeof linger));
-	}
-
-	hack_ready = 1;
-
-	need_test = 1;
-	t0 = VTIM_real();
-	while (1) {
-		(void)sleep(1);
-#ifdef SO_SNDTIMEO_WORKS
-		if (cache_param->idle_send_timeout != send_timeout) {
-			need_test = 1;
-			send_timeout = cache_param->idle_send_timeout;
-			tv_sndtimeo = VTIM_timeval(send_timeout);
-			VTAILQ_FOREACH(ls, &heritage.socks, list) {
-				if (ls->sock < 0)
-					continue;
-				AZ(setsockopt(ls->sock, SOL_SOCKET,
-				    SO_SNDTIMEO,
-				    &tv_sndtimeo, sizeof tv_sndtimeo));
-			}
-		}
-#endif
-#ifdef SO_RCVTIMEO_WORKS
-		if (cache_param->sess_timeout != sess_timeout) {
-			need_test = 1;
-			sess_timeout = cache_param->sess_timeout;
-			tv_rcvtimeo = VTIM_timeval(sess_timeout);
-			VTAILQ_FOREACH(ls, &heritage.socks, list) {
-				if (ls->sock < 0)
-					continue;
-				AZ(setsockopt(ls->sock, SOL_SOCKET,
-				    SO_RCVTIMEO,
-				    &tv_rcvtimeo, sizeof tv_rcvtimeo));
-			}
-		}
-#endif
-		now = VTIM_real();
-		VSC_C_main->uptime = (uint64_t)(now - t0);
-	}
-	NEEDLESS_RETURN(NULL);
-}
-
-
-/*--------------------------------------------------------------------*/
-
-static void
-ccf_start(struct cli *cli, const char * const *av, void *priv)
-{
-
-	(void)cli;
-	(void)av;
-	(void)priv;
-
-	AZ(pthread_create(&VCA_thread, NULL, vca_acct, NULL));
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ccf_listen_address(struct cli *cli, const char * const *av, void *priv)
-{
-	struct listen_sock *ls;
-	char h[32], p[32];
-
-	(void)cli;
-	(void)av;
-	(void)priv;
-
-	/*
-	 * This CLI command is primarily used by varnishtest.  Don't
-	 * respond until liste(2) has been called, in order to avoid
-	 * a race where varnishtest::client would attempt to connect(2)
-	 * before listen(2) has been called.
-	 */
-	while(!hack_ready)
-		(void)usleep(100*1000);
-
-	VTAILQ_FOREACH(ls, &heritage.socks, list) {
-		if (ls->sock < 0)
-			continue;
-		VTCP_myname(ls->sock, h, sizeof h, p, sizeof p);
-		VCLI_Out(cli, "%s %s\n", h, p);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-static struct cli_proto vca_cmds[] = {
-	{ CLI_SERVER_START,	"i", ccf_start },
-	{ "debug.listen_address",
-	    "debug.listen_address",
-	    "Report the actual listen address\n", 0, 0,
-	    "d", ccf_listen_address, NULL },
-	{ NULL }
-};
-
-void
-VCA_Init(void)
-{
-
-	CLI_AddFuncs(vca_cmds);
-	Lck_New(&pace_mtx, lck_vcapace);
-}
-
-void
-VCA_Shutdown(void)
-{
-	struct listen_sock *ls;
-	int i;
-
-	VTAILQ_FOREACH(ls, &heritage.socks, list) {
-		if (ls->sock < 0)
-			continue;
-		i = ls->sock;
-		ls->sock = -1;
-		(void)close(i);
-	}
-}
diff --git a/bin/varnishd/cache_backend.c b/bin/varnishd/cache_backend.c
deleted file mode 100644
index 1f290c3..0000000
--- a/bin/varnishd/cache_backend.c
+++ /dev/null
@@ -1,517 +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.
- *
- * Handle backend connections and backend request structures.
- *
- */
-
-#include "config.h"
-
-#include <poll.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vrt.h"
-#include "vtcp.h"
-
-/*--------------------------------------------------------------------
- * The "simple" director really isn't, since thats where all the actual
- * connections happen.  Nontheless, pretend it is simple by sequestering
- * the directoricity of it under this line.
- */
-
-struct vdi_simple {
-	unsigned		magic;
-#define VDI_SIMPLE_MAGIC	0x476d25b7
-	struct director		dir;
-	struct backend		*backend;
-	const struct vrt_backend *vrt;
-};
-
-/*--------------------------------------------------------------------
- * Create default Host: header for backend request
- */
-void
-VDI_AddHostHeader(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->bereq, HTTP_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->vbc->vdis, VDI_SIMPLE_MAGIC);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->bereq,
-	    "Host: %s", sp->wrk->vbc->vdis->vrt->hosthdr);
-}
-
-/*--------------------------------------------------------------------*/
-
-/* Private interface from backend_cfg.c */
-void
-VBE_ReleaseConn(struct vbc *vc)
-{
-
-	CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
-	assert(vc->backend == NULL);
-	assert(vc->fd < 0);
-
-	vc->addr = NULL;
-	vc->addrlen = 0;
-	vc->recycled = 0;
-	Lck_Lock(&VBE_mtx);
-	VSC_C_main->n_vbc--;
-	Lck_Unlock(&VBE_mtx);
-	FREE_OBJ(vc);
-}
-
-#define FIND_TMO(tmx, dst, sp, be)		\
-	do {					\
-		dst = sp->wrk->tmx;		\
-		if (dst == 0.0)			\
-			dst = be->tmx;		\
-		if (dst == 0.0)			\
-			dst = cache_param->tmx;	\
-	} while (0)
-
-/*--------------------------------------------------------------------
- * Attempt to connect to a given addrinfo entry.
- *
- * Must be called with locked backend, but will release the backend
- * lock during the slow/sleeping stuff, so that other worker threads
- * can have a go, while we ponder.
- *
- */
-
-static int
-vbe_TryConnect(const struct sess *sp, int pf, const struct sockaddr_storage *sa,
-    socklen_t salen, const struct vdi_simple *vs)
-{
-	int s, i, tmo;
-	double tmod;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
-
-	s = socket(pf, SOCK_STREAM, 0);
-	if (s < 0)
-		return (s);
-
-	FIND_TMO(connect_timeout, tmod, sp, vs->vrt);
-
-	tmo = (int)(tmod * 1000.0);
-
-	i = VTCP_connect(s, sa, salen, tmo);
-
-	if (i != 0) {
-		AZ(close(s));
-		return (-1);
-	}
-
-	return (s);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-bes_conn_try(const struct sess *sp, struct vbc *vc, const struct vdi_simple *vs)
-{
-	int s;
-	struct backend *bp = vs->backend;
-	char abuf1[VTCP_ADDRBUFSIZE];
-	char pbuf1[VTCP_PORTBUFSIZE];
-
-	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
-
-	Lck_Lock(&bp->mtx);
-	bp->refcount++;
-	bp->n_conn++;		/* It mostly works */
-	Lck_Unlock(&bp->mtx);
-
-	s = -1;
-	assert(bp->ipv6 != NULL || bp->ipv4 != NULL);
-
-	/* release lock during stuff that can take a long time */
-
-	if (cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
-		s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
-		vc->addr = bp->ipv6;
-		vc->addrlen = bp->ipv6len;
-	}
-	if (s == -1 && bp->ipv4 != NULL) {
-		s = vbe_TryConnect(sp, PF_INET, bp->ipv4, bp->ipv4len, vs);
-		vc->addr = bp->ipv4;
-		vc->addrlen = bp->ipv4len;
-	}
-	if (s == -1 && !cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
-		s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
-		vc->addr = bp->ipv6;
-		vc->addrlen = bp->ipv6len;
-	}
-
-	vc->fd = s;
-	if (s < 0) {
-		Lck_Lock(&bp->mtx);
-		bp->n_conn--;
-		bp->refcount--;		/* Only keep ref on success */
-		Lck_Unlock(&bp->mtx);
-		vc->addr = NULL;
-		vc->addrlen = 0;
-	} else {
-		vc->vsl_id = s | VSL_BACKENDMARKER;
-		VTCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
-		WSL(sp->wrk, SLT_BackendOpen, vc->vsl_id, "%s %s %s ",
-		    vs->backend->display_name, abuf1, pbuf1);
-	}
-
-}
-
-/*--------------------------------------------------------------------
- * Check that there is still something at the far end of a given socket.
- * We poll the fd with instant timeout, if there are any events we can't
- * use it (backends are not allowed to pipeline).
- */
-
-static int
-vbe_CheckFd(int fd)
-{
-	struct pollfd pfd;
-
-	pfd.fd = fd;
-	pfd.events = POLLIN;
-	pfd.revents = 0;
-	return(poll(&pfd, 1, 0) == 0);
-}
-
-/*--------------------------------------------------------------------
- * Manage a pool of vbc structures.
- * XXX: as an experiment, make this caching controled by a parameter
- * XXX: so we can see if it has any effect.
- */
-
-static struct vbc *
-vbe_NewConn(void)
-{
-	struct vbc *vc;
-
-	ALLOC_OBJ(vc, VBC_MAGIC);
-	XXXAN(vc);
-	vc->fd = -1;
-	Lck_Lock(&VBE_mtx);
-	VSC_C_main->n_vbc++;
-	Lck_Unlock(&VBE_mtx);
-	return (vc);
-}
-
-/*--------------------------------------------------------------------
- * It evaluates if a backend is healthy _for_a_specific_object_.
- * That means that it relies on sp->objcore->objhead. This is mainly for
- * saint-mode, but also takes backend->healthy into account. If
- * cache_param->saintmode_threshold is 0, this is basically just a test of
- * backend->healthy.
- *
- * The threshold has to be evaluated _after_ the timeout check, otherwise
- * items would never time out once the threshold is reached.
- */
-
-static unsigned int
-vbe_Healthy(const struct vdi_simple *vs, const struct sess *sp)
-{
-	struct trouble *tr;
-	struct trouble *tr2;
-	struct trouble *old;
-	unsigned i = 0, retval;
-	unsigned int threshold;
-	struct backend *backend;
-	uintptr_t target;
-	double now;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
-	backend = vs->backend;
-	CHECK_OBJ_NOTNULL(backend, BACKEND_MAGIC);
-
-	if (backend->admin_health == ah_probe && !backend->healthy)
-		return (0);
-
-	if (backend->admin_health == ah_sick)
-		return (0);
-
-	/* VRT/VCC sets threshold to UINT_MAX to mark that it's not
-	 * specified by VCL (thus use param).
-	 */
-	if (vs->vrt->saintmode_threshold == UINT_MAX)
-		threshold = cache_param->saintmode_threshold;
-	else
-		threshold = vs->vrt->saintmode_threshold;
-
-	if (backend->admin_health == ah_healthy)
-		threshold = UINT_MAX;
-
-	/* Saintmode is disabled */
-	if (threshold == 0)
-		return (1);
-
-	if (sp->objcore == NULL)
-		return (1);
-
-	now = sp->t_req;
-	target = (uintptr_t)(sp->objcore->objhead);
-
-	old = NULL;
-	retval = 1;
-	Lck_Lock(&backend->mtx);
-	VTAILQ_FOREACH_SAFE(tr, &backend->troublelist, list, tr2) {
-		CHECK_OBJ_NOTNULL(tr, TROUBLE_MAGIC);
-
-		if (tr->timeout < now) {
-			VTAILQ_REMOVE(&backend->troublelist, tr, list);
-			old = tr;
-			retval = 1;
-			break;
-		}
-
-		if (tr->target == target) {
-			retval = 0;
-			break;
-		}
-
-		/* If the threshold is at 1, a single entry on the list
-		 * will disable the backend. Since 0 is disable, ++i
-		 * instead of i++ to allow this behavior.
-		 */
-		if (++i >= threshold) {
-			retval = 0;
-			break;
-		}
-	}
-	Lck_Unlock(&backend->mtx);
-
-	if (old != NULL)
-		FREE_OBJ(old);
-
-	return (retval);
-}
-
-/*--------------------------------------------------------------------
- * Get a connection to a particular backend.
- */
-
-static struct vbc *
-vbe_GetVbe(const struct sess *sp, struct vdi_simple *vs)
-{
-	struct vbc *vc;
-	struct backend *bp;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
-	bp = vs->backend;
-	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
-
-	/* first look for vbc's we can recycle */
-	while (1) {
-		Lck_Lock(&bp->mtx);
-		vc = VTAILQ_FIRST(&bp->connlist);
-		if (vc != NULL) {
-			bp->refcount++;
-			assert(vc->backend == bp);
-			assert(vc->fd >= 0);
-			AN(vc->addr);
-			VTAILQ_REMOVE(&bp->connlist, vc, list);
-		}
-		Lck_Unlock(&bp->mtx);
-		if (vc == NULL)
-			break;
-		if (vbe_CheckFd(vc->fd)) {
-			/* XXX locking of stats */
-			VSC_C_main->backend_reuse += 1;
-			WSP(sp, SLT_Backend, "%d %s %s",
-			    vc->fd, sp->director->vcl_name, bp->display_name);
-			vc->vdis = vs;
-			vc->recycled = 1;
-			return (vc);
-		}
-		VSC_C_main->backend_toolate++;
-		WSL(sp->wrk, SLT_BackendClose, vc->vsl_id, "%s", bp->display_name);
-
-		/* Checkpoint log to flush all info related to this connection
-		   before the OS reuses the FD */
-		WSL_Flush(sp->wrk, 0);
-
-		VTCP_close(&vc->fd);
-		VBE_DropRefConn(bp);
-		vc->backend = NULL;
-		VBE_ReleaseConn(vc);
-	}
-
-	if (!vbe_Healthy(vs, sp)) {
-		VSC_C_main->backend_unhealthy++;
-		return (NULL);
-	}
-
-	if (vs->vrt->max_connections > 0 &&
-	    bp->n_conn >= vs->vrt->max_connections) {
-		VSC_C_main->backend_busy++;
-		return (NULL);
-	}
-
-	vc = vbe_NewConn();
-	assert(vc->fd == -1);
-	AZ(vc->backend);
-	bes_conn_try(sp, vc, vs);
-	if (vc->fd < 0) {
-		VBE_ReleaseConn(vc);
-		VSC_C_main->backend_fail++;
-		return (NULL);
-	}
-	vc->backend = bp;
-	VSC_C_main->backend_conn++;
-	WSP(sp, SLT_Backend, "%d %s %s",
-	    vc->fd, sp->director->vcl_name, bp->display_name);
-	vc->vdis = vs;
-	return (vc);
-}
-
-/*--------------------------------------------------------------------
- * Returns the backend if and only if the this is a simple director.
- * XXX: Needs a better name and possibly needs a better general approach.
- * XXX: This is mainly used by the DNS director to fetch the actual backend
- * XXX: so it can compare DNS lookups with the actual IP.
- */
-
-struct backend *
-vdi_get_backend_if_simple(const struct director *d)
-{
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	struct vdi_simple *vs, *vs2;
-
-	vs2 = d->priv;
-	if (vs2->magic != VDI_SIMPLE_MAGIC)
-		return (NULL);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
-	return (vs->backend);
-}
-
-/*--------------------------------------------------------------------
- *
- */
-
-void
-VBE_UseHealth(const struct director *vdi)
-{
-	struct vdi_simple *vs;
-
-	ASSERT_CLI();
-
-	if (strcmp(vdi->name, "simple"))
-		return;
-	CAST_OBJ_NOTNULL(vs, vdi->priv, VDI_SIMPLE_MAGIC);
-	if (vs->vrt->probe == NULL)
-		return;
-	VBP_Use(vs->backend, vs->vrt->probe);
-}
-
-/*--------------------------------------------------------------------
- *
- */
-
-static struct vbc * __match_proto__(vdi_getfd_f)
-vdi_simple_getfd(const struct director *d, struct sess *sp)
-{
-	struct vdi_simple *vs;
-	struct vbc *vc;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
-	vc = vbe_GetVbe(sp, vs);
-	if (vc != NULL) {
-		FIND_TMO(first_byte_timeout,
-		    vc->first_byte_timeout, sp, vs->vrt);
-		FIND_TMO(between_bytes_timeout,
-		    vc->between_bytes_timeout, sp, vs->vrt);
-	}
-	return (vc);
-}
-
-static unsigned
-vdi_simple_healthy(const struct director *d, const struct sess *sp)
-{
-	struct vdi_simple *vs;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
-	return (vbe_Healthy(vs, sp));
-}
-
-static void
-vdi_simple_fini(const struct director *d)
-{
-	struct vdi_simple *vs;
-
-	ASSERT_CLI();
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
-
-	if (vs->vrt->probe != NULL)
-		VBP_Remove(vs->backend, vs->vrt->probe);
-	VBE_DropRefVcl(vs->backend);
-	free(vs->dir.vcl_name);
-	vs->dir.magic = 0;
-	FREE_OBJ(vs);
-}
-
-void
-VRT_init_dir_simple(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	const struct vrt_backend *t;
-	struct vdi_simple *vs;
-
-	ASSERT_CLI();
-	(void)cli;
-	t = priv;
-
-	ALLOC_OBJ(vs, VDI_SIMPLE_MAGIC);
-	XXXAN(vs);
-	vs->dir.magic = DIRECTOR_MAGIC;
-	vs->dir.priv = vs;
-	vs->dir.name = "simple";
-	REPLACE(vs->dir.vcl_name, t->vcl_name);
-	vs->dir.getfd = vdi_simple_getfd;
-	vs->dir.fini = vdi_simple_fini;
-	vs->dir.healthy = vdi_simple_healthy;
-
-	vs->vrt = t;
-
-	vs->backend = VBE_AddBackend(cli, t);
-	if (vs->vrt->probe != NULL)
-		VBP_Insert(vs->backend, vs->vrt->probe, vs->vrt->hosthdr);
-
-	bp[idx] = &vs->dir;
-}
diff --git a/bin/varnishd/cache_backend.h b/bin/varnishd/cache_backend.h
deleted file mode 100644
index 72a1283..0000000
--- a/bin/varnishd/cache_backend.h
+++ /dev/null
@@ -1,192 +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.
- *
- * This is the central switch-board for backend connections and it is
- * slightly complicated by a number of optimizations.
- *
- * The data structures:
- *
- *    A vrt_backend is a definition of a backend in a VCL program.
- *
- *    A backend is a TCP destination, possibly multi-homed and it has a
- *    number of associated properties and statistics.
- *
- *    A vbc is an open TCP connection to a backend.
- *
- *    A bereq is a memory carrier for handling a HTTP transaction with
- *    a backend over a vbc.
- *
- *    A director is a piece of code that selects which backend to use,
- *    by whatever method or metric it chooses.
- *
- * The relationships:
- *
- *    Backends and directors get instantiated when VCL's are loaded,
- *    and this always happen in the CLI thread.
- *
- *    When a VCL tries to instantiate a backend, any existing backend
- *    with the same identity (== definition in VCL) will be used instead
- *    so that vbc's can be reused across VCL changes.
- *
- *    Directors disapper with the VCL that created them.
- *
- *    Backends disappear when their reference count drop to zero.
- *
- *    Backends have their host/port name looked up to addrinfo structures
- *    when they are instantiated, and we just cache that result and cycle
- *    through the entries (for multihomed backends) on failure only.
- *    XXX: add cli command to redo lookup.
- *
- *    bereq is sort of a step-child here, we just manage the pool of them.
- *
- */
-
-struct vbp_target;
-struct vbc;
-struct vrt_backend_probe;
-
-/*--------------------------------------------------------------------
- * A director is a piece of code which selects one of possibly multiple
- * backends to use.
- */
-
-typedef struct vbc *vdi_getfd_f(const struct director *, struct sess *sp);
-typedef void vdi_fini_f(const struct director *);
-typedef unsigned vdi_healthy(const struct director *, const struct sess *sp);
-
-struct director {
-	unsigned		magic;
-#define DIRECTOR_MAGIC		0x3336351d
-	const char		*name;
-	char			*vcl_name;
-	vdi_getfd_f		*getfd;
-	vdi_fini_f		*fini;
-	vdi_healthy		*healthy;
-	void			*priv;
-};
-
-/*--------------------------------------------------------------------
- * List of objectheads that have recently been rejected by VCL.
- */
-
-struct trouble {
-	unsigned		magic;
-#define TROUBLE_MAGIC		0x4211ab21
-	uintptr_t		target;
-	double			timeout;
-	VTAILQ_ENTRY(trouble)	list;
-};
-
-/*--------------------------------------------------------------------
- * An instance of a backend from a VCL program.
- */
-
-enum admin_health {
-	ah_invalid = 0,
-	ah_healthy,
-	ah_sick,
-	ah_probe
-};
-
-struct backend {
-	unsigned		magic;
-#define BACKEND_MAGIC		0x64c4c7c6
-
-	VTAILQ_ENTRY(backend)	list;
-	int			refcount;
-	struct lock		mtx;
-
-	char			*vcl_name;
-	char			*display_name;
-	char			*ipv4_addr;
-	char			*ipv6_addr;
-	char			*port;
-
-	struct sockaddr_storage	*ipv4;
-	socklen_t		ipv4len;
-	struct sockaddr_storage	*ipv6;
-	socklen_t		ipv6len;
-
-	unsigned		n_conn;
-	VTAILQ_HEAD(, vbc)	connlist;
-
-	struct vbp_target	*probe;
-	unsigned		healthy;
-	enum admin_health	admin_health;
-	VTAILQ_HEAD(, trouble)	troublelist;
-
-	struct VSC_C_vbe	*vsc;
-};
-
-/* -------------------------------------------------------------------*/
-
-/* Backend connection */
-struct vbc {
-	unsigned		magic;
-#define VBC_MAGIC		0x0c5e6592
-	VTAILQ_ENTRY(vbc)	list;
-	struct backend		*backend;
-	struct vdi_simple	*vdis;
-	unsigned		vsl_id;
-	int			fd;
-
-	struct sockaddr_storage	*addr;
-	socklen_t		addrlen;
-
-	uint8_t			recycled;
-
-	/* Timeouts */
-	double			first_byte_timeout;
-	double			between_bytes_timeout;
-};
-
-/* cache_backend.c */
-void VBE_ReleaseConn(struct vbc *vc);
-struct backend *vdi_get_backend_if_simple(const struct director *d);
-
-/* cache_backend_cfg.c */
-extern struct lock VBE_mtx;
-void VBE_DropRefConn(struct backend *);
-void VBE_DropRefVcl(struct backend *);
-void VBE_DropRefLocked(struct backend *b);
-
-/* cache_backend_poll.c */
-void VBP_Insert(struct backend *b, struct vrt_backend_probe const *p, const char *hosthdr);
-void VBP_Remove(struct backend *b, struct vrt_backend_probe const *p);
-void VBP_Use(const struct backend *b, const struct vrt_backend_probe const *p);
-void VBP_Summary(struct cli *cli, const struct vbp_target *vt);
-
-/* Init functions for directors */
-typedef void dir_init_f(struct cli *, struct director **, int , const void*);
-dir_init_f VRT_init_dir_simple;
-dir_init_f VRT_init_dir_dns;
-dir_init_f VRT_init_dir_hash;
-dir_init_f VRT_init_dir_random;
-dir_init_f VRT_init_dir_round_robin;
-dir_init_f VRT_init_dir_fallback;
-dir_init_f VRT_init_dir_client;
diff --git a/bin/varnishd/cache_backend_cfg.c b/bin/varnishd/cache_backend_cfg.c
deleted file mode 100644
index cbb8c85..0000000
--- a/bin/varnishd/cache_backend_cfg.c
+++ /dev/null
@@ -1,507 +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.
- *
- * Handle configuration of backends from VCL programs.
- *
- */
-
-#include "config.h"
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vcli.h"
-#include "vcli_priv.h"
-#include "vrt.h"
-
-struct lock VBE_mtx;
-
-
-/*
- * The list of backends is not locked, it is only ever accessed from
- * the CLI thread, so there is no need.
- */
-static VTAILQ_HEAD(, backend) backends = VTAILQ_HEAD_INITIALIZER(backends);
-
-/*--------------------------------------------------------------------
- */
-
-static void
-VBE_Nuke(struct backend *b)
-{
-
-	ASSERT_CLI();
-	VTAILQ_REMOVE(&backends, b, list);
-	free(b->ipv4);
-	free(b->ipv4_addr);
-	free(b->ipv6);
-	free(b->ipv6_addr);
-	free(b->port);
-	VSM_Free(b->vsc);
-	FREE_OBJ(b);
-	VSC_C_main->n_backend--;
-}
-
-/*--------------------------------------------------------------------
- */
-
-void
-VBE_Poll(void)
-{
-	struct backend *b, *b2;
-
-	ASSERT_CLI();
-	VTAILQ_FOREACH_SAFE(b, &backends, list, b2) {
-		assert(
-			b->admin_health == ah_healthy ||
-			b->admin_health == ah_sick ||
-			b->admin_health == ah_probe
-		);
-		if (b->refcount == 0 && b->probe == NULL)
-			VBE_Nuke(b);
-	}
-}
-
-/*--------------------------------------------------------------------
- * Drop a reference to a backend.
- * The last reference must come from the watcher in the CLI thread,
- * as only that thread is allowed to clean up the backend list.
- */
-
-void
-VBE_DropRefLocked(struct backend *b)
-{
-	int i;
-	struct vbc *vbe, *vbe2;
-
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-	assert(b->refcount > 0);
-
-	i = --b->refcount;
-	Lck_Unlock(&b->mtx);
-	if (i > 0)
-		return;
-
-	ASSERT_CLI();
-	VTAILQ_FOREACH_SAFE(vbe, &b->connlist, list, vbe2) {
-		VTAILQ_REMOVE(&b->connlist, vbe, list);
-		if (vbe->fd >= 0) {
-			AZ(close(vbe->fd));
-			vbe->fd = -1;
-		}
-		vbe->backend = NULL;
-		VBE_ReleaseConn(vbe);
-	}
-	VBE_Nuke(b);
-}
-
-void
-VBE_DropRefVcl(struct backend *b)
-{
-
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-
-	Lck_Lock(&b->mtx);
-	b->vsc->vcls--;
-	VBE_DropRefLocked(b);
-}
-
-void
-VBE_DropRefConn(struct backend *b)
-{
-
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-
-	Lck_Lock(&b->mtx);
-	assert(b->n_conn > 0);
-	b->n_conn--;
-	VBE_DropRefLocked(b);
-}
-
-/*--------------------------------------------------------------------
- * See lib/libvcl/vcc_backend.c::emit_sockaddr()
- */
-
-static void
-copy_sockaddr(struct sockaddr_storage **sa, socklen_t *len,
-    const unsigned char *src)
-{
-
-	assert(*src > 0);
-	*sa = calloc(sizeof **sa, 1);
-	XXXAN(*sa);
-	memcpy(*sa, src + 1, *src);
-	*len = *src;
-}
-
-/*--------------------------------------------------------------------
- * Add a backend/director instance when loading a VCL.
- * If an existing backend is matched, grab a refcount and return.
- * Else create a new backend structure with reference initialized to one.
- */
-
-struct backend *
-VBE_AddBackend(struct cli *cli, const struct vrt_backend *vb)
-{
-	struct backend *b;
-	char buf[128];
-
-	AN(vb->vcl_name);
-	assert(vb->ipv4_sockaddr != NULL || vb->ipv6_sockaddr != NULL);
-	(void)cli;
-	ASSERT_CLI();
-
-	/* Run through the list and see if we already have this backend */
-	VTAILQ_FOREACH(b, &backends, list) {
-		CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-		if (strcmp(b->vcl_name, vb->vcl_name))
-			continue;
-		if (vb->ipv4_sockaddr != NULL && (
-		    b->ipv4len != vb->ipv4_sockaddr[0] ||
-		    memcmp(b->ipv4, vb->ipv4_sockaddr + 1, b->ipv4len)))
-			continue;
-		if (vb->ipv6_sockaddr != NULL && (
-		    b->ipv6len != vb->ipv6_sockaddr[0] ||
-		    memcmp(b->ipv6, vb->ipv6_sockaddr + 1, b->ipv6len)))
-			continue;
-		b->refcount++;
-		b->vsc->vcls++;
-		return (b);
-	}
-
-	/* Create new backend */
-	ALLOC_OBJ(b, BACKEND_MAGIC);
-	XXXAN(b);
-	Lck_New(&b->mtx, lck_backend);
-	b->refcount = 1;
-
-	bprintf(buf, "%s(%s,%s,%s)",
-	    vb->vcl_name,
-	    vb->ipv4_addr == NULL ? "" : vb->ipv4_addr,
-	    vb->ipv6_addr == NULL ? "" : vb->ipv6_addr, vb->port);
-
-	b->vsc = VSM_Alloc(sizeof *b->vsc, VSC_CLASS, VSC_TYPE_VBE, buf);
-	b->vsc->vcls++;
-
-	VTAILQ_INIT(&b->connlist);
-
-	VTAILQ_INIT(&b->troublelist);
-
-	/*
-	 * This backend may live longer than the VCL that instantiated it
-	 * so we cannot simply reference the VCL's copy of things.
-	 */
-	REPLACE(b->vcl_name, vb->vcl_name);
-	REPLACE(b->display_name, buf);
-	REPLACE(b->ipv4_addr, vb->ipv4_addr);
-	REPLACE(b->ipv6_addr, vb->ipv6_addr);
-	REPLACE(b->port, vb->port);
-
-	/*
-	 * Copy over the sockaddrs
-	 */
-	if (vb->ipv4_sockaddr != NULL)
-		copy_sockaddr(&b->ipv4, &b->ipv4len, vb->ipv4_sockaddr);
-	if (vb->ipv6_sockaddr != NULL)
-		copy_sockaddr(&b->ipv6, &b->ipv6len, vb->ipv6_sockaddr);
-
-	assert(b->ipv4 != NULL || b->ipv6 != NULL);
-
-	b->healthy = 1;
-	b->admin_health = ah_probe;
-
-	VTAILQ_INSERT_TAIL(&backends, b, list);
-	VSC_C_main->n_backend++;
-	return (b);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
-    int idx, const void *priv)
-{
-
-	ASSERT_CLI();
-	if (!strcmp(name, "simple"))
-		VRT_init_dir_simple(cli, dir, idx, priv);
-	else if (!strcmp(name, "hash"))
-		VRT_init_dir_hash(cli, dir, idx, priv);
-	else if (!strcmp(name, "random"))
-		VRT_init_dir_random(cli, dir, idx, priv);
-	else if (!strcmp(name, "dns"))
-		VRT_init_dir_dns(cli, dir, idx, priv);
-	else if (!strcmp(name, "round-robin"))
-		VRT_init_dir_round_robin(cli, dir, idx, priv);
-	else if (!strcmp(name, "fallback"))
-		VRT_init_dir_fallback(cli, dir, idx, priv);
-	else if (!strcmp(name, "client"))
-		VRT_init_dir_client(cli, dir, idx, priv);
-	else
-		INCOMPL();
-}
-
-void
-VRT_fini_dir(struct cli *cli, struct director *b)
-{
-
-	(void)cli;
-	ASSERT_CLI();
-	CHECK_OBJ_NOTNULL(b, DIRECTOR_MAGIC);
-	b->fini(b);
-	b->priv = NULL;
-}
-
-/*---------------------------------------------------------------------
- * String to admin_health
- */
-
-static enum admin_health
-vbe_str2adminhealth(const char *wstate)
-{
-
-	if (strcasecmp(wstate, "healthy") == 0)
-		return(ah_healthy);
-	if (strcasecmp(wstate, "sick") == 0)
-		return(ah_sick);
-	if (strcmp(wstate, "auto") == 0)
-		return(ah_probe);
-	return (ah_invalid);
-}
-
-/*---------------------------------------------------------------------
- * A general function for finding backends and doing things with them.
- *
- * Return -1 on match-argument parse errors.
- *
- * If the call-back function returns non-zero, the search is terminated
- * and we relay that return value.
- *
- * Otherwise we return the number of matches.
- */
-
-typedef int bf_func(struct cli *cli, struct backend *b, void *priv);
-
-static int
-backend_find(struct cli *cli, const char *matcher, bf_func *func, void *priv)
-{
-	struct backend *b;
-	const char *s;
-	const char *name_b;
-	ssize_t name_l = 0;
-	const char *ip_b = NULL;
-	ssize_t ip_l = 0;
-	const char *port_b = NULL;
-	ssize_t port_l = 0;
-	int found = 0;
-	int i;
-
-	name_b = matcher;
-	if (matcher != NULL) {
-		s = strchr(matcher,'(');
-
-		if (s != NULL)
-			name_l = s - name_b;
-		else
-			name_l = strlen(name_b);
-
-		if (s != NULL) {
-			s++;
-			while (isspace(*s))
-				s++;
-			ip_b = s;
-			while (*s != '\0' &&
-			    *s != ')' &&
-			    *s != ':' &&
-			    !isspace(*s))
-				s++;
-			ip_l = s - ip_b;
-			while (isspace(*s))
-				s++;
-			if (*s == ':') {
-				s++;
-				while (isspace(*s))
-					s++;
-				port_b = s;
-				while (*s != '\0' && *s != ')' && !isspace(*s))
-					s++;
-				port_l = s - port_b;
-			}
-			while (isspace(*s))
-				s++;
-			if (*s != ')') {
-				VCLI_Out(cli,
-				    "Match string syntax error:"
-				    " ')' not found.");
-				VCLI_SetResult(cli, CLIS_CANT);
-				return (-1);
-			}
-			s++;
-			while (isspace(*s))
-				s++;
-			if (*s != '\0') {
-				VCLI_Out(cli,
-				    "Match string syntax error:"
-				    " junk after ')'");
-				VCLI_SetResult(cli, CLIS_CANT);
-				return (-1);
-			}
-		}
-	}
-	VTAILQ_FOREACH(b, &backends, list) {
-		CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-		if (port_b != NULL && strncmp(b->port, port_b, port_l) != 0)
-			continue;
-		if (name_b != NULL && strncmp(b->vcl_name, name_b, name_l) != 0)
-			continue;
-		if (ip_b != NULL &&
-		    (b->ipv4_addr == NULL ||
-		      strncmp(b->ipv4_addr, ip_b, ip_l)) &&
-		    (b->ipv6_addr == NULL ||
-		      strncmp(b->ipv6_addr, ip_b, ip_l)))
-			continue;
-		found++;
-		i = func(cli, b, priv);
-		if (i)
-			return(i);
-	}
-	return (found);
-}
-
-/*---------------------------------------------------------------------*/
-
-static int __match_proto__()
-do_list(struct cli *cli, struct backend *b, void *priv)
-{
-	int *hdr;
-
-	AN(priv);
-	hdr = priv;
-	if (!*hdr) {
-		VCLI_Out(cli, "%-30s %-6s %-10s %s",
-		    "Backend name", "Refs", "Admin", "Probe");
-		*hdr = 1;
-	}
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-
-	VCLI_Out(cli, "\n%-30s %-6d", b->display_name, b->refcount);
-
-	if (b->admin_health == ah_probe)
-		VCLI_Out(cli, " %-10s", "probe");
-	else if (b->admin_health == ah_sick)
-		VCLI_Out(cli, " %-10s", "sick");
-	else if (b->admin_health == ah_healthy)
-		VCLI_Out(cli, " %-10s", "healthy");
-	else
-		VCLI_Out(cli, " %-10s", "invalid");
-
-	if (b->probe == NULL)
-		VCLI_Out(cli, " %s", "Healthy (no probe)");
-	else {
-		if (b->healthy)
-			VCLI_Out(cli, " %s", "Healthy ");
-		else
-			VCLI_Out(cli, " %s", "Sick ");
-		VBP_Summary(cli, b->probe);
-	}
-
-	return (0);
-}
-
-static void
-cli_backend_list(struct cli *cli, const char * const *av, void *priv)
-{
-	int hdr = 0;
-
-	(void)av;
-	(void)priv;
-	ASSERT_CLI();
-	(void)backend_find(cli, av[2], do_list, &hdr);
-}
-
-/*---------------------------------------------------------------------*/
-
-static int __match_proto__()
-do_set_health(struct cli *cli, struct backend *b, void *priv)
-{
-	enum admin_health state;
-
-	(void)cli;
-	state = *(enum admin_health*)priv;
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-	b->admin_health = state;
-	return (0);
-}
-
-static void
-cli_backend_set_health(struct cli *cli, const char * const *av, void *priv)
-{
-	enum admin_health state;
-	int n;
-
-	(void)av;
-	(void)priv;
-	ASSERT_CLI();
-	AN(av[2]);
-	AN(av[3]);
-	state = vbe_str2adminhealth(av[3]);
-	if (state == ah_invalid) {
-		VCLI_Out(cli, "Invalid state %s", av[3]);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return;
-	}
-	n = backend_find(cli, av[2], do_set_health, &state);
-	if (n == 0) {
-		VCLI_Out(cli, "No Backends matches");
-		VCLI_SetResult(cli, CLIS_PARAM);
-	}
-}
-
-/*---------------------------------------------------------------------*/
-
-static struct cli_proto backend_cmds[] = {
-	{ "backend.list", "backend.list",
-	    "\tList all backends\n", 0, 1, "d", cli_backend_list },
-	{ "backend.set_health", "backend.set_health matcher state",
-	    "\tShow a backend\n", 2, 2, "d", cli_backend_set_health },
-	{ NULL }
-};
-
-/*---------------------------------------------------------------------*/
-
-void
-VBE_Init(void)
-{
-
-	Lck_New(&VBE_mtx, lck_vbe);
-	CLI_AddFuncs(backend_cmds);
-}
diff --git a/bin/varnishd/cache_backend_poll.c b/bin/varnishd/cache_backend_poll.c
deleted file mode 100644
index efd64cb..0000000
--- a/bin/varnishd/cache_backend_poll.c
+++ /dev/null
@@ -1,597 +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.
- *
- * Poll backends for collection of health statistics
- *
- * We co-opt threads from the worker pool for probing the backends,
- * but we want to avoid a potentially messy cleanup operation when we
- * retire the backend, so the thread owns the health information, which
- * the backend references, rather than the other way around.
- *
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vcli_priv.h"
-#include "vrt.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-/* Default averaging rate, we want something pretty responsive */
-#define AVG_RATE			4
-
-struct vbp_vcl {
-	unsigned			magic;
-#define VBP_VCL_MAGIC			0x70829764
-
-	VTAILQ_ENTRY(vbp_vcl)		list;
-	const struct vrt_backend_probe	*probep;
-	struct vrt_backend_probe	probe;
-	const char			*hosthdr;
-};
-
-struct vbp_target {
-	unsigned			magic;
-#define VBP_TARGET_MAGIC		0x6b7cb656
-
-	struct backend			*backend;
-	VTAILQ_HEAD( ,vbp_vcl)		vcls;
-
-	struct vrt_backend_probe	probe;
-	int				stop;
-	struct vsb			*vsb;
-	char				*req;
-	int				req_len;
-
-	char				resp_buf[128];
-	unsigned			good;
-
-	/* Collected statistics */
-#define BITMAP(n, c, t, b)	uint64_t	n;
-#include "tbl/backend_poll.h"
-#undef BITMAP
-
-	double				last;
-	double				avg;
-	double				rate;
-
-	VTAILQ_ENTRY(vbp_target)	list;
-	pthread_t			thread;
-};
-
-static VTAILQ_HEAD(, vbp_target)	vbp_list =
-    VTAILQ_HEAD_INITIALIZER(vbp_list);
-
-static struct lock			vbp_mtx;
-
-/*--------------------------------------------------------------------
- * Poke one backend, once, but possibly at both IPv4 and IPv6 addresses.
- *
- * We do deliberately not use the stuff in cache_backend.c, because we
- * want to measure the backends response without local distractions.
- */
-
-static int
-vbp_connect(int pf, const struct sockaddr_storage *sa, socklen_t salen, int tmo)
-{
-	int s, i;
-
-	s = socket(pf, SOCK_STREAM, 0);
-	if (s < 0)
-		return (s);
-
-	i = VTCP_connect(s, sa, salen, tmo);
-	if (i == 0)
-		return (s);
-	VTCP_close(&s);
-	return (-1);
-}
-
-static void
-vbp_poke(struct vbp_target *vt)
-{
-	int s, tmo, i;
-	double t_start, t_now, t_end;
-	unsigned rlen, resp;
-	struct backend *bp;
-	char buf[8192], *p;
-	struct pollfd pfda[1], *pfd = pfda;
-
-	bp = vt->backend;
-	CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
-
-	t_start = t_now = VTIM_real();
-	t_end = t_start + vt->probe.timeout;
-	tmo = (int)round((t_end - t_now) * 1e3);
-
-	s = -1;
-	if (cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
-		s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
-		t_now = VTIM_real();
-		tmo = (int)round((t_end - t_now) * 1e3);
-		if (s >= 0)
-			vt->good_ipv6 |= 1;
-	}
-	if (tmo > 0 && s < 0 && bp->ipv4 != NULL) {
-		s = vbp_connect(PF_INET, bp->ipv4, bp->ipv4len, tmo);
-		t_now = VTIM_real();
-		tmo = (int)round((t_end - t_now) * 1e3);
-		if (s >= 0)
-			vt->good_ipv4 |= 1;
-	}
-	if (tmo > 0 && s < 0 && bp->ipv6 != NULL) {
-		s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
-		t_now = VTIM_real();
-		tmo = (int)round((t_end - t_now) * 1e3);
-		if (s >= 0)
-			vt->good_ipv6 |= 1;
-	}
-	if (s < 0) {
-		/* Got no connection: failed */
-		return;
-	}
-	if (tmo <= 0) {
-		/* Spent too long time getting it */
-		VTCP_close(&s);
-		return;
-	}
-
-	/* Send the request */
-	i = write(s, vt->req, vt->req_len);
-	if (i != vt->req_len) {
-		if (i < 0)
-			vt->err_xmit |= 1;
-		VTCP_close(&s);
-		return;
-	}
-	vt->good_xmit |= 1;
-
-	pfd->fd = s;
-	rlen = 0;
-	do {
-		pfd->events = POLLIN;
-		pfd->revents = 0;
-		tmo = (int)round((t_end - t_now) * 1e3);
-		if (tmo > 0)
-			i = poll(pfd, 1, tmo);
-		if (i == 0 || tmo <= 0) {
-			VTCP_close(&s);
-			return;
-		}
-		if (rlen < sizeof vt->resp_buf)
-			i = read(s, vt->resp_buf + rlen,
-			    sizeof vt->resp_buf - rlen);
-		else
-			i = read(s, buf, sizeof buf);
-		rlen += i;
-	} while (i > 0);
-
-	VTCP_close(&s);
-
-	if (i < 0) {
-		vt->err_recv |= 1;
-		return;
-	}
-
-	if (rlen == 0)
-		return;
-
-	/* So we have a good receive ... */
-	t_now = VTIM_real();
-	vt->last = t_now - t_start;
-	vt->good_recv |= 1;
-
-	/* Now find out if we like the response */
-	vt->resp_buf[sizeof vt->resp_buf - 1] = '\0';
-	p = strchr(vt->resp_buf, '\r');
-	if (p != NULL)
-		*p = '\0';
-	p = strchr(vt->resp_buf, '\n');
-	if (p != NULL)
-		*p = '\0';
-
-	i = sscanf(vt->resp_buf, "HTTP/%*f %u %s", &resp, buf);
-
-	if (i == 2 && resp == vt->probe.exp_status)
-		vt->happy |= 1;
-}
-
-/*--------------------------------------------------------------------
- * Record pokings...
- */
-
-static void
-vbp_start_poke(struct vbp_target *vt)
-{
-	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
-
-#define BITMAP(n, c, t, b)	vt->n <<= 1;
-#include "tbl/backend_poll.h"
-#undef BITMAP
-
-	vt->last = 0;
-	vt->resp_buf[0] = '\0';
-}
-
-static void
-vbp_has_poked(struct vbp_target *vt)
-{
-	unsigned i, j;
-	uint64_t u;
-	const char *logmsg;
-	char bits[10];
-
-	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
-
-	/* Calculate exponential average */
-	if (vt->happy & 1) {
-		if (vt->rate < AVG_RATE)
-			vt->rate += 1.0;
-		vt->avg += (vt->last - vt->avg) / vt->rate;
-	}
-
-	i = 0;
-#define BITMAP(n, c, t, b)	bits[i++] = (vt->n & 1) ? c : '-';
-#include "tbl/backend_poll.h"
-#undef BITMAP
-	bits[i] = '\0';
-
-	u = vt->happy;
-	for (i = j = 0; i < vt->probe.window; i++) {
-		if (u & 1)
-			j++;
-		u >>= 1;
-	}
-	vt->good = j;
-
-	if (vt->good >= vt->probe.threshold) {
-		if (vt->backend->healthy)
-			logmsg = "Still healthy";
-		else
-			logmsg = "Back healthy";
-		vt->backend->healthy = 1;
-	} else {
-		if (vt->backend->healthy)
-			logmsg = "Went sick";
-		else
-			logmsg = "Still sick";
-		vt->backend->healthy = 0;
-	}
-	VSL(SLT_Backend_health, 0, "%s %s %s %u %u %u %.6f %.6f %s",
-	    vt->backend->vcl_name, logmsg, bits,
-	    vt->good, vt->probe.threshold, vt->probe.window,
-	    vt->last, vt->avg, vt->resp_buf);
-	vt->backend->vsc->happy = vt->happy;
-}
-
-/*--------------------------------------------------------------------
- * Build request from probe spec
- */
-
-static void
-vbp_build_req(struct vsb *vsb, const struct vbp_vcl *vcl)
-{
-
-	XXXAN(vsb);
-	XXXAN(vcl);
-	VSB_clear(vsb);
-	if(vcl->probe.request != NULL) {
-		VSB_cat(vsb, vcl->probe.request);
-	} else {
-		VSB_printf(vsb, "GET %s HTTP/1.1\r\n",
-		    vcl->probe.url != NULL ?  vcl->probe.url : "/");
-		if (vcl->hosthdr != NULL)
-			VSB_printf(vsb, "Host: %s\r\n", vcl->hosthdr);
-		VSB_printf(vsb, "Connection: close\r\n");
-		VSB_printf(vsb, "\r\n");
-	}
-	AZ(VSB_finish(vsb));
-}
-
-/*--------------------------------------------------------------------
- * One thread per backend to be poked.
- */
-
-static void *
-vbp_wrk_poll_backend(void *priv)
-{
-	struct vbp_target *vt;
-	struct vbp_vcl *vcl = NULL;
-
-	THR_SetName("backend poll");
-
-	CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC);
-
-	while (!vt->stop) {
-		Lck_Lock(&vbp_mtx);
-		if (VTAILQ_FIRST(&vt->vcls) != vcl) {
-			vcl = VTAILQ_FIRST(&vt->vcls);
-			vbp_build_req(vt->vsb, vcl);
-			vt->probe = vcl->probe;
-		}
-		Lck_Unlock(&vbp_mtx);
-
-		vt->req = VSB_data(vt->vsb);
-		vt->req_len = VSB_len(vt->vsb);
-
-		vbp_start_poke(vt);
-		vbp_poke(vt);
-		vbp_has_poked(vt);
-
-		if (!vt->stop)
-			VTIM_sleep(vt->probe.interval);
-	}
-	return (NULL);
-}
-
-/*--------------------------------------------------------------------
- * Cli functions
- */
-
-void
-VBP_Summary(struct cli *cli, const struct vbp_target *vt)
-{
-
-	CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
-	VCLI_Out(cli, "%d/%d", vt->good, vt->probe.window);
-}
-
-static void
-vbp_bitmap(struct cli *cli, char c, uint64_t map, const char *lbl)
-{
-	int i;
-	uint64_t u = (1ULL << 63);
-
-	for (i = 0; i < 64; i++) {
-		if (map & u)
-			VCLI_Out(cli, "%c", c);
-		else
-			VCLI_Out(cli, "-");
-		map <<= 1;
-	}
-	VCLI_Out(cli, " %s\n", lbl);
-}
-
-/*lint -e{506} constant value boolean */
-/*lint -e{774} constant value boolean */
-static void
-vbp_health_one(struct cli *cli, const struct vbp_target *vt)
-{
-
-	VCLI_Out(cli, "Backend %s is %s\n",
-	    vt->backend->vcl_name,
-	    vt->backend->healthy ? "Healthy" : "Sick");
-	VCLI_Out(cli, "Current states  good: %2u threshold: %2u window: %2u\n",
-	    vt->good, vt->probe.threshold, vt->probe.window);
-	VCLI_Out(cli, "Average responsetime of good probes: %.6f\n", vt->avg);
-	VCLI_Out(cli,
-	    "Oldest                       "
-	    "                             Newest\n");
-	VCLI_Out(cli,
-	    "============================="
-	    "===================================\n");
-
-#define BITMAP(n, c, t, b)					\
-		if ((vt->n != 0) || (b))			\
-			vbp_bitmap(cli, (c), vt->n, (t));
-#include "tbl/backend_poll.h"
-#undef BITMAP
-}
-
-static void
-vbp_health(struct cli *cli, const char * const *av, void *priv)
-{
-	struct vbp_target *vt;
-
-	ASSERT_CLI();
-	(void)av;
-	(void)priv;
-
-	VTAILQ_FOREACH(vt, &vbp_list, list)
-		vbp_health_one(cli, vt);
-}
-
-static struct cli_proto debug_cmds[] = {
-	{ "debug.health", "debug.health",
-		"\tDump backend health stuff\n",
-		0, 0, "d", vbp_health },
-	{ NULL }
-};
-
-/*--------------------------------------------------------------------
- * A new VCL wants to probe this backend,
- */
-
-static struct vbp_vcl *
-vbp_new_vcl(const struct vrt_backend_probe *p, const char *hosthdr)
-{
-	struct vbp_vcl *vcl;
-
-	ALLOC_OBJ(vcl, VBP_VCL_MAGIC);
-	XXXAN(vcl);
-	vcl->probep = p;
-	vcl->probe = *p;
-	vcl->hosthdr = hosthdr;
-
-	/*
-	 * Sanitize and insert defaults
-	 * XXX: we could make these defaults parameters
-	 */
-	if (vcl->probe.timeout == 0.0)
-		vcl->probe.timeout = 2.0;
-	if (vcl->probe.interval == 0.0)
-		vcl->probe.interval = 5.0;
-	if (vcl->probe.window == 0)
-		vcl->probe.window = 8;
-	if (vcl->probe.threshold == 0)
-		vcl->probe.threshold = 3;
-	if (vcl->probe.exp_status == 0)
-		vcl->probe.exp_status = 200;
-
-	if (vcl->probe.threshold == ~0U)
-		vcl->probe.initial = vcl->probe.threshold - 1;
-
-	if (vcl->probe.initial > vcl->probe.threshold)
-		vcl->probe.initial = vcl->probe.threshold;
-	return (vcl);
-}
-
-/*--------------------------------------------------------------------
- * Insert/Remove/Use called from cache_backend.c
- */
-
-void
-VBP_Insert(struct backend *b, const struct vrt_backend_probe *p, const char *hosthdr)
-{
-	struct vbp_target *vt;
-	struct vbp_vcl *vcl;
-	int startthread = 0;
-	unsigned u;
-
-	ASSERT_CLI();
-	AN(p);
-
-	if (b->probe == NULL) {
-		ALLOC_OBJ(vt, VBP_TARGET_MAGIC);
-		XXXAN(vt);
-		VTAILQ_INIT(&vt->vcls);
-		vt->backend = b;
-		vt->vsb = VSB_new_auto();
-		XXXAN(vt->vsb);
-		b->probe = vt;
-		startthread = 1;
-		VTAILQ_INSERT_TAIL(&vbp_list, vt, list);
-	} else {
-		vt = b->probe;
-	}
-
-	VTAILQ_FOREACH(vcl, &vt->vcls, list)
-		assert (vcl->probep != p);
-
-	vcl = vbp_new_vcl(p, hosthdr);
-	Lck_Lock(&vbp_mtx);
-	VTAILQ_INSERT_TAIL(&vt->vcls, vcl, list);
-	Lck_Unlock(&vbp_mtx);
-
-	if (startthread) {
-		for (u = 0; u < vcl->probe.initial; u++) {
-			vbp_start_poke(vt);
-			vt->happy |= 1;
-			vbp_has_poked(vt);
-		}
-		AZ(pthread_create(&vt->thread, NULL, vbp_wrk_poll_backend, vt));
-	}
-}
-
-void
-VBP_Use(const struct backend *b, const struct vrt_backend_probe *p)
-{
-	struct vbp_target *vt;
-	struct vbp_vcl *vcl;
-
-	ASSERT_CLI();
-	AN(p);
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-	AN(b->probe);
-	vt = b->probe;
-
-	VTAILQ_FOREACH(vcl, &vt->vcls, list) {
-		if (vcl->probep != p)
-			continue;
-
-		Lck_Lock(&vbp_mtx);
-		VTAILQ_REMOVE(&vt->vcls, vcl, list);
-		VTAILQ_INSERT_HEAD(&vt->vcls, vcl, list);
-		Lck_Unlock(&vbp_mtx);
-		return;
-	}
-}
-
-void
-VBP_Remove(struct backend *b, struct vrt_backend_probe const *p)
-{
-	struct vbp_target *vt;
-	struct vbp_vcl *vcl;
-	void *ret;
-
-	ASSERT_CLI();
-	AN(p);
-	CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
-	AN(b->probe);
-	vt = b->probe;
-
-	VTAILQ_FOREACH(vcl, &vt->vcls, list)
-		if (vcl->probep == p)
-			break;
-
-	AN(vcl);
-
-	Lck_Lock(&vbp_mtx);
-	VTAILQ_REMOVE(&vt->vcls, vcl, list);
-	Lck_Unlock(&vbp_mtx);
-
-	FREE_OBJ(vcl);
-
-	if (!VTAILQ_EMPTY(&vt->vcls))
-		return;
-
-	/* No more polling for this backend */
-
-	b->healthy = 1;
-
-	vt->stop = 1;
-	AZ(pthread_cancel(vt->thread));
-	AZ(pthread_join(vt->thread, &ret));
-
-	b->healthy = 1;
-
-	VTAILQ_REMOVE(&vbp_list, vt, list);
-	b->probe = NULL;
-	VSB_delete(vt->vsb);
-	FREE_OBJ(vt);
-}
-
-/*--------------------------------------------------------------------
- * Initialize the backend probe subsystem
- */
-
-void
-VBP_Init(void)
-{
-
-	Lck_New(&vbp_mtx, lck_vbp);
-	CLI_AddFuncs(debug_cmds);
-}
diff --git a/bin/varnishd/cache_ban.c b/bin/varnishd/cache_ban.c
deleted file mode 100644
index 768e631..0000000
--- a/bin/varnishd/cache_ban.c
+++ /dev/null
@@ -1,1108 +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.
- *
- * Ban processing
- *
- * A ban consists of a number of conditions (or tests), all of which must be
- * satisfied.  Here are some potential bans we could support:
- *
- *	req.url == "/foo"
- *	req.url ~ ".iso" && obj.size > 10MB
- *	req.http.host ~ "web1.com" && obj.set-cookie ~ "USER=29293"
- *
- * We make the "&&" mandatory from the start, leaving the syntax space
- * for latter handling of "||" as well.
- *
- * Bans are compiled into bytestrings as follows:
- *	8 bytes	- double: timestamp		XXX: Byteorder ?
- *	4 bytes - be32: length
- *	1 byte - flags: 0x01: BAN_F_REQ
- *	N tests
- * A test have this form:
- *	1 byte - arg (see ban_vars.h col 3 "BAN_ARG_XXX")
- *	(n bytes) - http header name, canonical encoding
- *	lump - comparison arg
- *	1 byte - operation (BAN_OPER_)
- *	(lump) - compiled regexp
- * A lump is:
- *	4 bytes - be32: length
- *	n bytes - content
- *
- * In a perfect world, we should vector through VRE to get to PCRE,
- * but since we rely on PCRE's ability to encode the regexp into a
- * byte string, that would be a little bit artificial, so this is
- * the exception that confirmes the rule.
- *
- */
-
-#include "config.h"
-
-#include <pcre.h>
-#include <stdio.h>
-
-#include "cache.h"
-
-#include "hash/hash_slinger.h"
-#include "vcli.h"
-#include "vcli_priv.h"
-#include "vend.h"
-#include "vtim.h"
-
-struct ban {
-	unsigned		magic;
-#define BAN_MAGIC		0x700b08ea
-	VTAILQ_ENTRY(ban)	list;
-	unsigned		refcount;
-	unsigned		flags;
-#define BAN_F_GONE		(1 << 0)
-#define BAN_F_REQ		(1 << 2)
-#define BAN_F_LURK		(3 << 6)	/* ban-lurker-color */
-	VTAILQ_HEAD(,objcore)	objcore;
-	struct vsb		*vsb;
-	uint8_t			*spec;
-};
-
-#define LURK_SHIFT 6
-
-struct ban_test {
-	uint8_t			arg1;
-	const char		*arg1_spec;
-	uint8_t			oper;
-	const char		*arg2;
-	const void		*arg2_spec;
-};
-
-static VTAILQ_HEAD(banhead_s,ban) ban_head = VTAILQ_HEAD_INITIALIZER(ban_head);
-static struct lock ban_mtx;
-static struct ban *ban_magic;
-static pthread_t ban_thread;
-static struct ban * volatile ban_start;
-static bgthread_t ban_lurker;
-
-/*--------------------------------------------------------------------
- * BAN string magic markers
- */
-
-#define	BAN_OPER_EQ	0x10
-#define	BAN_OPER_NEQ	0x11
-#define	BAN_OPER_MATCH	0x12
-#define	BAN_OPER_NMATCH	0x13
-
-#define BAN_ARG_URL	0x18
-#define BAN_ARG_REQHTTP	0x19
-#define BAN_ARG_OBJHTTP	0x1a
-
-/*--------------------------------------------------------------------
- * Variables we can purge on
- */
-
-static const struct pvar {
-	const char		*name;
-	unsigned		flag;
-	uint8_t			tag;
-} pvars[] = {
-#define PVAR(a, b, c)	{ (a), (b), (c) },
-#include "tbl/ban_vars.h"
-#undef PVAR
-	{ 0, 0, 0}
-};
-
-/*--------------------------------------------------------------------
- * Storage handling of bans
- */
-
-struct ban *
-BAN_New(void)
-{
-	struct ban *b;
-
-	ALLOC_OBJ(b, BAN_MAGIC);
-	if (b == NULL)
-		return (b);
-	b->vsb = VSB_new_auto();
-	if (b->vsb == NULL) {
-		FREE_OBJ(b);
-		return (NULL);
-	}
-	VTAILQ_INIT(&b->objcore);
-	return (b);
-}
-
-void
-BAN_Free(struct ban *b)
-{
-
-	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
-	AZ(b->refcount);
-	assert(VTAILQ_EMPTY(&b->objcore));
-
-	if (b->vsb != NULL)
-		VSB_delete(b->vsb);
-	if (b->spec != NULL)
-		free(b->spec);
-	FREE_OBJ(b);
-}
-
-/*--------------------------------------------------------------------
- * Get & Release a tail reference, used to hold the list stable for
- * traversals etc.
- */
-
-struct ban *
-BAN_TailRef(void)
-{
-	struct ban *b;
-
-	ASSERT_CLI();
-	Lck_Lock(&ban_mtx);
-	b = VTAILQ_LAST(&ban_head, banhead_s);
-	AN(b);
-	b->refcount++;
-	Lck_Unlock(&ban_mtx);
-	return (b);
-}
-
-void
-BAN_TailDeref(struct ban **bb)
-{
-	struct ban *b;
-
-	b = *bb;
-	*bb = NULL;
-	Lck_Lock(&ban_mtx);
-	b->refcount--;
-	Lck_Unlock(&ban_mtx);
-}
-
-/*--------------------------------------------------------------------
- * Extract time and length from ban-spec
- */
-
-static double
-ban_time(const uint8_t *banspec)
-{
-	double t;
-
-	assert(sizeof t == 8);
-	memcpy(&t, banspec, sizeof t);
-	return (t);
-}
-
-static unsigned
-ban_len(const uint8_t *banspec)
-{
-	unsigned u;
-
-	u = vbe32dec(banspec + 8);
-	return (u);
-}
-
-/*--------------------------------------------------------------------
- * Access a lump of bytes in a ban test spec
- */
-
-static void
-ban_add_lump(const struct ban *b, const void *p, uint32_t len)
-{
-	uint8_t buf[sizeof len];
-
-	vbe32enc(buf, len);
-	VSB_bcat(b->vsb, buf, sizeof buf);
-	VSB_bcat(b->vsb, p, len);
-}
-
-static const void *
-ban_get_lump(const uint8_t **bs)
-{
-	const void *r;
-	unsigned ln;
-
-	ln = vbe32dec(*bs);
-	*bs += 4;
-	r = (const void*)*bs;
-	*bs += ln;
-	return (r);
-}
-
-/*--------------------------------------------------------------------
- * Pick a test apart from a spec string
- */
-
-static void
-ban_iter(const uint8_t **bs, struct ban_test *bt)
-{
-
-	memset(bt, 0, sizeof *bt);
-	bt->arg1 = *(*bs)++;
-	if (bt->arg1 == BAN_ARG_REQHTTP || bt->arg1 == BAN_ARG_OBJHTTP) {
-		bt->arg1_spec = (const char *)*bs;
-		(*bs) += (*bs)[0] + 2;
-	}
-	bt->arg2 = ban_get_lump(bs);
-	bt->oper = *(*bs)++;
-	if (bt->oper == BAN_OPER_MATCH || bt->oper == BAN_OPER_NMATCH)
-		bt->arg2_spec = ban_get_lump(bs);
-}
-
-/*--------------------------------------------------------------------
- * Parse and add a http argument specification
- * Output something which HTTP_GetHdr understands
- */
-
-static void
-ban_parse_http(const struct ban *b, const char *a1)
-{
-	int l;
-
-	l = strlen(a1) + 1;
-	assert(l <= 127);
-	VSB_putc(b->vsb, (char)l);
-	VSB_cat(b->vsb, a1);
-	VSB_putc(b->vsb, ':');
-	VSB_putc(b->vsb, '\0');
-}
-
-/*--------------------------------------------------------------------
- * Parse and add a ban test specification
- */
-
-static int
-ban_parse_regexp(struct cli *cli, const struct ban *b, const char *a3)
-{
-	const char *error;
-	int erroroffset, rc;
-	size_t sz;
-	pcre *re;
-
-	re = pcre_compile(a3, 0, &error, &erroroffset, NULL);
-	if (re == NULL) {
-		VSL(SLT_Debug, 0, "REGEX: <%s>", error);
-		VCLI_Out(cli, "%s", error);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return (-1);
-	}
-	rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &sz);
-	AZ(rc);
-	ban_add_lump(b, re, sz);
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Add a (and'ed) test-condition to a ban
- */
-
-int
-BAN_AddTest(struct cli *cli, struct ban *b, const char *a1, const char *a2,
-    const char *a3)
-{
-	const struct pvar *pv;
-	int i;
-
-	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
-
-	for (pv = pvars; pv->name != NULL; pv++)
-		if (!strncmp(a1, pv->name, strlen(pv->name)))
-			break;
-	if (pv->name == NULL) {
-		VCLI_Out(cli, "unknown or unsupported field \"%s\"", a1);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return (-1);
-	}
-
-	if (pv->flag & PVAR_REQ)
-		b->flags |= BAN_F_REQ;
-
-	VSB_putc(b->vsb, pv->tag);
-	if (pv->flag & PVAR_HTTP)
-		ban_parse_http(b, a1 + strlen(pv->name));
-
-	ban_add_lump(b, a3, strlen(a3) + 1);
-	if (!strcmp(a2, "~")) {
-		VSB_putc(b->vsb, BAN_OPER_MATCH);
-		i = ban_parse_regexp(cli, b, a3);
-		if (i)
-			return (i);
-	} else if (!strcmp(a2, "!~")) {
-		VSB_putc(b->vsb, BAN_OPER_NMATCH);
-		i = ban_parse_regexp(cli, b, a3);
-		if (i)
-			return (i);
-	} else if (!strcmp(a2, "==")) {
-		VSB_putc(b->vsb, BAN_OPER_EQ);
-	} else if (!strcmp(a2, "!=")) {
-		VSB_putc(b->vsb, BAN_OPER_NEQ);
-	} else {
-		VCLI_Out(cli,
-		    "expected conditional (~, !~, == or !=) got \"%s\"", a2);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return (-1);
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * We maintain ban_start as a pointer to the first element of the list
- * as a separate variable from the VTAILQ, to avoid depending on the
- * internals of the VTAILQ macros.  We tacitly assume that a pointer
- * write is always atomic in doing so.
- */
-
-void
-BAN_Insert(struct ban *b)
-{
-	struct ban  *bi, *be;
-	ssize_t ln;
-	double t0;
-
-	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
-
-	AZ(VSB_finish(b->vsb));
-	ln = VSB_len(b->vsb);
-	assert(ln >= 0);
-
-	b->spec = malloc(ln + 13L);	/* XXX */
-	XXXAN(b->spec);
-
-	t0 = VTIM_real();
-	memcpy(b->spec, &t0, sizeof t0);
-	b->spec[12] = (b->flags & BAN_F_REQ) ? 1 : 0;
-	memcpy(b->spec + 13, VSB_data(b->vsb), ln);
-	ln += 13;
-	vbe32enc(b->spec + 8, ln);
-
-	VSB_delete(b->vsb);
-	b->vsb = NULL;
-
-	Lck_Lock(&ban_mtx);
-	VTAILQ_INSERT_HEAD(&ban_head, b, list);
-	ban_start = b;
-	VSC_C_main->bans++;
-	VSC_C_main->bans_added++;
-	if (b->flags & BAN_F_REQ)
-		VSC_C_main->bans_req++;
-
-	be = VTAILQ_LAST(&ban_head, banhead_s);
-	if (cache_param->ban_dups && be != b)
-		be->refcount++;
-	else
-		be = NULL;
-
-	SMP_NewBan(b->spec, ln);
-	Lck_Unlock(&ban_mtx);
-
-	if (be == NULL)
-		return;
-
-	/* Hunt down duplicates, and mark them as gone */
-	bi = b;
-	Lck_Lock(&ban_mtx);
-	while(bi != be) {
-		bi = VTAILQ_NEXT(bi, list);
-		if (bi->flags & BAN_F_GONE)
-			continue;
-		/* Safe because the length is part of the fixed size hdr */
-		if (memcmp(b->spec + 8, bi->spec + 8, ln - 8))
-			continue;
-		bi->flags |= BAN_F_GONE;
-		VSC_C_main->bans_gone++;
-		VSC_C_main->bans_dups++;
-	}
-	be->refcount--;
-	Lck_Unlock(&ban_mtx);
-}
-
-/*--------------------------------------------------------------------
- * A new object is created, grab a reference to the newest ban
- */
-
-void
-BAN_NewObjCore(struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AZ(oc->ban);
-	Lck_Lock(&ban_mtx);
-	oc->ban = ban_start;
-	ban_start->refcount++;
-	VTAILQ_INSERT_TAIL(&ban_start->objcore, oc, ban_list);
-	Lck_Unlock(&ban_mtx);
-}
-
-/*--------------------------------------------------------------------
- * An object is destroyed, release its ban reference
- */
-
-void
-BAN_DestroyObj(struct objcore *oc)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	if (oc->ban == NULL)
-		return;
-	CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
-	Lck_Lock(&ban_mtx);
-	assert(oc->ban->refcount > 0);
-	oc->ban->refcount--;
-	VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
-	oc->ban = NULL;
-	Lck_Unlock(&ban_mtx);
-}
-
-/*--------------------------------------------------------------------
- * Find and/or Grab a reference to an objects ban based on timestamp
- * Assume we hold a TailRef, so list traversal is safe.
- */
-
-struct ban *
-BAN_RefBan(struct objcore *oc, double t0, const struct ban *tail)
-{
-	struct ban *b;
-	double t1 = 0;
-
-	VTAILQ_FOREACH(b, &ban_head, list) {
-		t1 = ban_time(b->spec);
-		if (t1 <= t0)
-			break;
-		if (b == tail)
-			break;
-	}
-	AN(b);
-	assert(t1 == t0);
-	Lck_Lock(&ban_mtx);
-	b->refcount++;
-	VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
-	Lck_Unlock(&ban_mtx);
-	return (b);
-}
-
-/*--------------------------------------------------------------------
- * Put a skeleton ban in the list, unless there is an identical,
- * time & condition, ban already in place.
- *
- * If a newer ban has same condition, mark the new ban GONE.
- * mark any older bans, with the same condition, GONE as well.
- */
-
-void
-BAN_Reload(const uint8_t *ban, unsigned len)
-{
-	struct ban *b, *b2;
-	int gone = 0;
-	double t0, t1, t2 = 9e99;
-
-	ASSERT_CLI();
-
-	t0 = ban_time(ban);
-	assert(len == ban_len(ban));
-
-	Lck_Lock(&ban_mtx);
-
-	VTAILQ_FOREACH(b, &ban_head, list) {
-		t1 = ban_time(b->spec);
-		assert (t1 < t2);
-		t2 = t1;
-		if (t1 == t0) {
-			Lck_Unlock(&ban_mtx);
-			return;
-		}
-		if (t1 < t0)
-			break;
-		if (!memcmp(b->spec + 8, ban + 8, len - 8)) {
-			gone |= BAN_F_GONE;
-			VSC_C_main->bans_dups++;
-			VSC_C_main->bans_gone++;
-		}
-	}
-
-	VSC_C_main->bans++;
-	VSC_C_main->bans_added++;
-
-	b2 = BAN_New();
-	AN(b2);
-	b2->spec = malloc(len);
-	AN(b2->spec);
-	memcpy(b2->spec, ban, len);
-	b2->flags |= gone;
-	if (ban[12])
-		b2->flags |= BAN_F_REQ;
-	if (b == NULL)
-		VTAILQ_INSERT_TAIL(&ban_head, b2, list);
-	else
-		VTAILQ_INSERT_BEFORE(b, b2, list);
-
-	/* Hunt down older duplicates */
-	for (b = VTAILQ_NEXT(b2, list); b != NULL; b = VTAILQ_NEXT(b, list)) {
-		if (b->flags & BAN_F_GONE)
-			continue;
-		if (!memcmp(b->spec + 8, ban + 8, len - 8)) {
-			b->flags |= BAN_F_GONE;
-			VSC_C_main->bans_dups++;
-			VSC_C_main->bans_gone++;
-		}
-	}
-	Lck_Unlock(&ban_mtx);
-}
-
-/*--------------------------------------------------------------------
- * Get a bans timestamp
- */
-
-double
-BAN_Time(const struct ban *b)
-{
-
-	if (b == NULL)
-		return (0.0);
-
-	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
-	return (ban_time(b->spec));
-}
-
-/*--------------------------------------------------------------------
- * All silos have read their bans, ready for action
- */
-
-void
-BAN_Compile(void)
-{
-
-	ASSERT_CLI();
-
-	SMP_NewBan(ban_magic->spec, ban_len(ban_magic->spec));
-	ban_start = VTAILQ_FIRST(&ban_head);
-	WRK_BgThread(&ban_thread, "ban-lurker", ban_lurker, NULL);
-}
-
-/*--------------------------------------------------------------------
- * Evaluate ban-spec
- */
-
-static int
-ban_evaluate(const uint8_t *bs, const struct http *objhttp,
-    const struct http *reqhttp, unsigned *tests)
-{
-	struct ban_test bt;
-	const uint8_t *be;
-	char *arg1;
-
-	be = bs + ban_len(bs);
-	bs += 13;
-	while (bs < be) {
-		(*tests)++;
-		ban_iter(&bs, &bt);
-		arg1 = NULL;
-		switch (bt.arg1) {
-		case BAN_ARG_URL:
-			arg1 = reqhttp->hd[HTTP_HDR_URL].b;
-			break;
-		case BAN_ARG_REQHTTP:
-			(void)http_GetHdr(reqhttp, bt.arg1_spec, &arg1);
-			break;
-		case BAN_ARG_OBJHTTP:
-			(void)http_GetHdr(objhttp, bt.arg1_spec, &arg1);
-			break;
-		default:
-			INCOMPL();
-		}
-
-		switch (bt.oper) {
-		case BAN_OPER_EQ:
-			if (arg1 == NULL || strcmp(arg1, bt.arg2))
-				return (0);
-			break;
-		case BAN_OPER_NEQ:
-			if (arg1 != NULL && !strcmp(arg1, bt.arg2))
-				return (0);
-			break;
-		case BAN_OPER_MATCH:
-			if (arg1 == NULL ||
-			    pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
-			    0, 0, NULL, 0) < 0)
-				return (0);
-			break;
-		case BAN_OPER_NMATCH:
-			if (arg1 != NULL &&
-			    pcre_exec(bt.arg2_spec, NULL, arg1, strlen(arg1),
-			    0, 0, NULL, 0) >= 0)
-				return (0);
-			break;
-		default:
-			INCOMPL();
-		}
-	}
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * Check an object against all applicable bans
- *
- * Return:
- *	-1 not all bans checked, but none of the checked matched
- *		Only if !has_req
- *	0 No bans matched, object moved to ban_start.
- *	1 Ban matched, object removed from ban list.
- */
-
-static int
-ban_check_object(struct object *o, const struct sess *sp, int has_req)
-{
-	struct ban *b;
-	struct objcore *oc;
-	struct ban * volatile b0;
-	unsigned tests, skipped;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	oc = o->objcore;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	CHECK_OBJ_NOTNULL(oc->ban, BAN_MAGIC);
-
-	b0 = ban_start;
-	CHECK_OBJ_NOTNULL(b0, BAN_MAGIC);
-
-	if (b0 == oc->ban)
-		return (0);
-
-	/*
-	 * This loop is safe without locks, because we know we hold
-	 * a refcount on a ban somewhere in the list and we do not
-	 * inspect the list past that ban.
-	 */
-	tests = 0;
-	skipped = 0;
-	for (b = b0; b != oc->ban; b = VTAILQ_NEXT(b, list)) {
-		CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
-		if (b->flags & BAN_F_GONE)
-			continue;
-		if ((b->flags & BAN_F_LURK) &&
-		    (b->flags & BAN_F_LURK) == (oc->flags & OC_F_LURK)) {
-			AZ(b->flags & BAN_F_REQ);
-			/* Lurker already tested this */
-			continue;
-		}
-		if (!has_req && (b->flags & BAN_F_REQ)) {
-			/*
-			 * We cannot test this one, but there might
-			 * be other bans that match, so we soldier on
-			 */
-			skipped++;
-		} else if (ban_evaluate(b->spec, o->http, sp->http, &tests))
-			break;
-	}
-
-	Lck_Lock(&ban_mtx);
-	VSC_C_main->bans_tested++;
-	VSC_C_main->bans_tests_tested += tests;
-
-	if (b == oc->ban && skipped > 0) {
-		AZ(has_req);
-		Lck_Unlock(&ban_mtx);
-		/*
-		 * Not banned, but some tests were skipped, so we cannot know
-		 * for certain that it cannot be, so we just have to give up.
-		 */
-		return (-1);
-	}
-
-	oc->ban->refcount--;
-	VTAILQ_REMOVE(&oc->ban->objcore, oc, ban_list);
-	if (b == oc->ban) {	/* not banned */
-		b->flags &= ~BAN_F_LURK;
-		VTAILQ_INSERT_TAIL(&b0->objcore, oc, ban_list);
-		b0->refcount++;
-	}
-	Lck_Unlock(&ban_mtx);
-
-	if (b == oc->ban) {	/* not banned */
-		oc->ban = b0;
-		oc_updatemeta(oc);
-		return (0);
-	} else {
-		EXP_Clr(&o->exp);
-		oc->ban = NULL;
-		oc_updatemeta(oc);
-		/* BAN also changed, but that is not important any more */
-		WSP(sp, SLT_ExpBan, "%u was banned", o->xid);
-		EXP_Rearm(o);
-		return (1);
-	}
-}
-
-int
-BAN_CheckObject(struct object *o, const struct sess *sp)
-{
-
-	return (ban_check_object(o, sp, 1) > 0);
-}
-
-static struct ban *
-ban_CheckLast(void)
-{
-	struct ban *b;
-
-	Lck_AssertHeld(&ban_mtx);
-	b = VTAILQ_LAST(&ban_head, banhead_s);
-	if (b != VTAILQ_FIRST(&ban_head) && b->refcount == 0) {
-		if (b->flags & BAN_F_GONE)
-			VSC_C_main->bans_gone--;
-		if (b->flags & BAN_F_REQ)
-			VSC_C_main->bans_req--;
-		VSC_C_main->bans--;
-		VSC_C_main->bans_deleted++;
-		VTAILQ_REMOVE(&ban_head, b, list);
-	} else {
-		b = NULL;
-	}
-	return (b);
-}
-
-/*--------------------------------------------------------------------
- * Ban lurker thread
- */
-
-static int
-ban_lurker_work(const struct sess *sp, unsigned pass)
-{
-	struct ban *b, *b0, *b2;
-	struct objhead *oh;
-	struct objcore *oc, *oc2;
-	struct object *o;
-	int i;
-
-	AN(pass & BAN_F_LURK);
-	AZ(pass & ~BAN_F_LURK);
-
-	/* First route the last ban(s) */
-	do {
-		Lck_Lock(&ban_mtx);
-		b2 = ban_CheckLast();
-		Lck_Unlock(&ban_mtx);
-		if (b2 != NULL)
-			BAN_Free(b2);
-	} while (b2 != NULL);
-
-	/*
-	 * Find out if we have any bans we can do something about
-	 * If we find any, tag them with our pass number.
-	 */
-	i = 0;
-	b0 = NULL;
-	VTAILQ_FOREACH(b, &ban_head, list) {
-		if (b->flags & BAN_F_GONE)
-			continue;
-		if (b->flags & BAN_F_REQ)
-			continue;
-		if (b == VTAILQ_LAST(&ban_head, banhead_s))
-			continue;
-		if (b0 == NULL)
-			b0 = b;
-		i++;
-		b->flags &= ~BAN_F_LURK;
-		b->flags |= pass;
-	}
-	if (cache_param->diag_bitmap & 0x80000)
-		VSL(SLT_Debug, 0, "lurker: %d actionable bans", i);
-	if (i == 0)
-		return (0);
-
-	VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list) {
-		if (cache_param->diag_bitmap & 0x80000)
-			VSL(SLT_Debug, 0, "lurker doing %f %d",
-			    ban_time(b->spec), b->refcount);
-		while (1) {
-			Lck_Lock(&ban_mtx);
-			oc = VTAILQ_FIRST(&b->objcore);
-			if (oc == NULL)
-				break;
-			CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-			if (cache_param->diag_bitmap & 0x80000)
-				VSL(SLT_Debug, 0, "test: %p %d %d",
-				    oc, oc->flags & OC_F_LURK, pass);
-			if ((oc->flags & OC_F_LURK) == pass)
-				break;
-			oh = oc->objhead;
-			CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-			if (Lck_Trylock(&oh->mtx)) {
-				Lck_Unlock(&ban_mtx);
-				VTIM_sleep(cache_param->ban_lurker_sleep);
-				continue;
-			}
-			/*
-			 * See if the objcore is still on the objhead since
-			 * we race against HSH_Deref() which comes in the
-			 * opposite locking order.
-			 */
-			VTAILQ_FOREACH(oc2, &oh->objcs, list)
-				if (oc == oc2)
-					break;
-			if (oc2 == NULL) {
-				Lck_Unlock(&oh->mtx);
-				Lck_Unlock(&ban_mtx);
-				VTIM_sleep(cache_param->ban_lurker_sleep);
-				continue;
-			}
-			/*
-			 * Grab a reference to the OC and we can let go of
-			 * the BAN mutex
-			 */
-			AN(oc->refcnt);
-			oc->refcnt++;
-			oc->flags &= ~OC_F_LURK;
-			Lck_Unlock(&ban_mtx);
-			/*
-			 * Get the object and check it against all relevant bans
-			 */
-			o = oc_getobj(sp->wrk, oc);
-			i = ban_check_object(o, sp, 0);
-			if (cache_param->diag_bitmap & 0x80000)
-				VSL(SLT_Debug, 0, "lurker got: %p %d",
-				    oc, i);
-			if (i == -1) {
-				/* Not banned, not moved */
-				oc->flags |= pass;
-				Lck_Lock(&ban_mtx);
-				VTAILQ_REMOVE(&b->objcore, oc, ban_list);
-				VTAILQ_INSERT_TAIL(&b->objcore, oc, ban_list);
-				Lck_Unlock(&ban_mtx);
-			}
-			Lck_Unlock(&oh->mtx);
-			if (cache_param->diag_bitmap & 0x80000)
-				VSL(SLT_Debug, 0, "lurker done: %p %d %d",
-				    oc, oc->flags & OC_F_LURK, pass);
-			(void)HSH_Deref(sp->wrk, NULL, &o);
-			VTIM_sleep(cache_param->ban_lurker_sleep);
-		}
-		Lck_AssertHeld(&ban_mtx);
-		if (!(b->flags & BAN_F_REQ)) {
-			if (!(b->flags & BAN_F_GONE)) {
-				b->flags |= BAN_F_GONE;
-				VSC_C_main->bans_gone++;
-			}
-			if (cache_param->diag_bitmap & 0x80000)
-				VSL(SLT_Debug, 0, "lurker BAN %f now gone",
-				    ban_time(b->spec));
-		}
-		Lck_Unlock(&ban_mtx);
-		VTIM_sleep(cache_param->ban_lurker_sleep);
-		if (b == b0)
-			break;
-	}
-	return (1);
-}
-
-static void * __match_proto__(bgthread_t)
-ban_lurker(struct sess *sp, void *priv)
-{
-	struct ban *bf;
-	unsigned pass = (1 << LURK_SHIFT);
-
-	int i = 0;
-	(void)priv;
-	while (1) {
-
-		while (cache_param->ban_lurker_sleep == 0.0) {
-			/*
-			 * Ban-lurker is disabled:
-			 * Clean the last ban, if possible, and sleep
-			 */
-			Lck_Lock(&ban_mtx);
-			bf = ban_CheckLast();
-			Lck_Unlock(&ban_mtx);
-			if (bf != NULL)
-				BAN_Free(bf);
-			else
-				VTIM_sleep(1.0);
-		}
-
-		i = ban_lurker_work(sp, pass);
-		WSL_Flush(sp->wrk, 0);
-		WRK_SumStat(sp->wrk);
-		if (i) {
-			pass += (1 << LURK_SHIFT);
-			pass &= BAN_F_LURK;
-			if (pass == 0)
-				pass += (1 << LURK_SHIFT);
-			VTIM_sleep(cache_param->ban_lurker_sleep);
-		} else {
-			VTIM_sleep(1.0);
-		}
-	}
-	NEEDLESS_RETURN(NULL);
-}
-
-/*--------------------------------------------------------------------
- * CLI functions to add bans
- */
-
-static void
-ccf_ban(struct cli *cli, const char * const *av, void *priv)
-{
-	int narg, i;
-	struct ban *b;
-
-	(void)priv;
-
-	/* First do some cheap checks on the arguments */
-	for (narg = 0; av[narg + 2] != NULL; narg++)
-		continue;
-	if ((narg % 4) != 3) {
-		VCLI_Out(cli, "Wrong number of arguments");
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return;
-	}
-	for (i = 3; i < narg; i += 4) {
-		if (strcmp(av[i + 2], "&&")) {
-			VCLI_Out(cli, "Found \"%s\" expected &&", av[i + 2]);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-	}
-
-	b = BAN_New();
-	if (b == NULL) {
-		VCLI_Out(cli, "Out of Memory");
-		VCLI_SetResult(cli, CLIS_CANT);
-		return;
-	}
-	for (i = 0; i < narg; i += 4)
-		if (BAN_AddTest(cli, b, av[i + 2], av[i + 3], av[i + 4])) {
-			BAN_Free(b);
-			return;
-		}
-	BAN_Insert(b);
-}
-
-static void
-ccf_ban_url(struct cli *cli, const char * const *av, void *priv)
-{
-	const char *aav[6];
-
-	(void)priv;
-	aav[0] = NULL;
-	aav[1] = "ban";
-	aav[2] = "req.url";
-	aav[3] = "~";
-	aav[4] = av[2];
-	aav[5] = NULL;
-	ccf_ban(cli, aav, priv);
-}
-
-static void
-ban_render(struct cli *cli, const uint8_t *bs)
-{
-	struct ban_test bt;
-	const uint8_t *be;
-
-	be = bs + ban_len(bs);
-	bs += 13;
-	while (bs < be) {
-		ban_iter(&bs, &bt);
-		switch (bt.arg1) {
-		case BAN_ARG_URL:
-			VCLI_Out(cli, "req.url");
-			break;
-		case BAN_ARG_REQHTTP:
-			VCLI_Out(cli, "req.http.%.*s",
-			    bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
-			break;
-		case BAN_ARG_OBJHTTP:
-			VCLI_Out(cli, "obj.http.%.*s",
-			    bt.arg1_spec[0] - 1, bt.arg1_spec + 1);
-			break;
-		default:
-			INCOMPL();
-		}
-		switch (bt.oper) {
-		case BAN_OPER_EQ:	VCLI_Out(cli, " == "); break;
-		case BAN_OPER_NEQ:	VCLI_Out(cli, " != "); break;
-		case BAN_OPER_MATCH:	VCLI_Out(cli, " ~ "); break;
-		case BAN_OPER_NMATCH:	VCLI_Out(cli, " !~ "); break;
-		default:
-			INCOMPL();
-		}
-		VCLI_Out(cli, "%s", bt.arg2);
-		if (bs < be)
-			VCLI_Out(cli, " && ");
-	}
-}
-
-static void
-ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
-{
-	struct ban *b, *bl;
-
-	(void)av;
-	(void)priv;
-
-	/* Get a reference so we are safe to traverse the list */
-	bl = BAN_TailRef();
-
-	VCLI_Out(cli, "Present bans:\n");
-	VTAILQ_FOREACH(b, &ban_head, list) {
-		if (b == bl && !(cache_param->diag_bitmap & 0x80000))
-			break;
-		VCLI_Out(cli, "%10.6f %5u%s\t", ban_time(b->spec),
-		    bl == b ? b->refcount - 1 : b->refcount,
-		    b->flags & BAN_F_GONE ? "G" : " ");
-		ban_render(cli, b->spec);
-		VCLI_Out(cli, "\n");
-		if (cache_param->diag_bitmap & 0x80000) {
-			Lck_Lock(&ban_mtx);
-			struct objcore *oc;
-			VTAILQ_FOREACH(oc, &b->objcore, ban_list)
-				VCLI_Out(cli, "     %p\n", oc);
-			Lck_Unlock(&ban_mtx);
-		}
-	}
-
-	BAN_TailDeref(&bl);
-}
-
-static struct cli_proto ban_cmds[] = {
-	{ CLI_BAN_URL,				"", ccf_ban_url },
-	{ CLI_BAN,				"", ccf_ban },
-	{ CLI_BAN_LIST,				"", ccf_ban_list },
-	{ NULL }
-};
-
-void
-BAN_Init(void)
-{
-
-	Lck_New(&ban_mtx, lck_ban);
-	CLI_AddFuncs(ban_cmds);
-	assert(BAN_F_LURK == OC_F_LURK);
-	AN((1 << LURK_SHIFT) & BAN_F_LURK);
-	AN((2 << LURK_SHIFT) & BAN_F_LURK);
-
-	ban_magic = BAN_New();
-	AN(ban_magic);
-	ban_magic->flags |= BAN_F_GONE;
-	VSC_C_main->bans_gone++;
-	BAN_Insert(ban_magic);
-}
diff --git a/bin/varnishd/cache_center.c b/bin/varnishd/cache_center.c
deleted file mode 100644
index e42fac8..0000000
--- a/bin/varnishd/cache_center.c
+++ /dev/null
@@ -1,1691 +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.
- *
- * This file contains the central state machine for pushing requests.
- *
- * We cannot just use direct calls because it is possible to kick a
- * request back to the lookup stage (usually after a rewrite).  The
- * state engine also allows us to break the processing up into some
- * logical chunks which improves readability a little bit.
- *
- * Since the states are rather nasty in detail, I have decided to embedd
- * a dot(1) graph in the source code comments.  So to see the big picture,
- * extract the DOT lines and run though dot(1), for instance with the
- * command:
- *	sed -n '/^DOT/s///p' cache_center.c | dot -Tps > /tmp/_.ps
- */
-
-/*
-DOT digraph vcl_center {
-xDOT	page="8.2,11.5"
-DOT	size="7.2,10.5"
-DOT	margin="0.5"
-DOT	center="1"
-DOT acceptor [
-DOT	shape=hexagon
-DOT	label="Request received"
-DOT ]
-DOT ERROR [shape=plaintext]
-DOT RESTART [shape=plaintext]
-DOT acceptor -> start [style=bold,color=green]
- */
-
-#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 "vcli_priv.h"
-#include "vsha256.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-#ifndef HAVE_SRANDOMDEV
-#include "compat/srandomdev.h"
-#endif
-
-static unsigned xids;
-
-/*--------------------------------------------------------------------
- * WAIT
- * Wait (briefly) until we have a full request in our htc.
- */
-
-static int
-cnt_wait(struct sess *sp)
-{
-	int i;
-	struct pollfd pfd[1];
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	AZ(sp->vcl);
-	AZ(sp->obj);
-	assert(sp->xid == 0);
-
-	i = HTC_Complete(sp->htc);
-	if (i == 0 && cache_param->session_linger > 0) {
-		pfd[0].fd = sp->fd;
-		pfd[0].events = POLLIN;
-		pfd[0].revents = 0;
-		i = poll(pfd, 1, cache_param->session_linger);
-		if (i)
-			i = HTC_Rx(sp->htc);
-	}
-	if (i == 0) {
-		WSP(sp, SLT_Debug, "herding");
-		sp->wrk->stats.sess_herd++;
-		SES_Charge(sp);
-		sp->wrk = NULL;
-		Pool_Wait(sp);
-		return (1);
-	}
-	if (i == 1) {
-		sp->step = STP_START;
-		return (0);
-	}
-	if (i == -2) {
-		SES_Close(sp, "overflow");
-		return (0);
-	}
-	if (i == -1 && Tlen(sp->htc->rxbuf) == 0 &&
-	    (errno == 0 || errno == ECONNRESET))
-		SES_Close(sp, "EOF");
-	else
-		SES_Close(sp, "error");
-	sp->step = STP_DONE;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * We have a refcounted object on the session, now deliver it.
- *
-DOT subgraph xcluster_prepresp {
-DOT	prepresp [
-DOT		shape=ellipse
-DOT		label="Filter obj.->resp."
-DOT	]
-DOT	vcl_deliver [
-DOT		shape=record
-DOT		label="vcl_deliver()|resp."
-DOT	]
-DOT	prepresp -> vcl_deliver [style=bold,color=green]
-DOT	prepresp -> vcl_deliver [style=bold,color=cyan]
-DOT	prepresp -> vcl_deliver [style=bold,color=red]
-DOT	prepresp -> vcl_deliver [style=bold,color=blue,]
-DOT	vcl_deliver -> deliver [style=bold,color=green,label=deliver]
-DOT	vcl_deliver -> deliver [style=bold,color=red]
-DOT	vcl_deliver -> deliver [style=bold,color=blue]
-DOT     vcl_deliver -> errdeliver [label="error"]
-DOT     errdeliver [label="ERROR",shape=plaintext]
-DOT     vcl_deliver -> rstdeliver [label="restart",color=purple]
-DOT     rstdeliver [label="RESTART",shape=plaintext]
-DOT     vcl_deliver -> streambody [style=bold,color=cyan,label="deliver"]
-DOT }
- *
- */
-
-static int
-cnt_prepresp(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	if (sp->wrk->do_stream)
-		AssertObjCorePassOrBusy(sp->obj->objcore);
-
-	sp->wrk->res_mode = 0;
-
-	if ((sp->wrk->h_content_length != NULL || !sp->wrk->do_stream) &&
-	    !sp->wrk->do_gzip && !sp->wrk->do_gunzip)
-		sp->wrk->res_mode |= RES_LEN;
-
-	if (!sp->disable_esi && sp->obj->esidata != NULL) {
-		/* In ESI mode, we don't know the aggregate length */
-		sp->wrk->res_mode &= ~RES_LEN;
-		sp->wrk->res_mode |= RES_ESI;
-	}
-
-	if (sp->esi_level > 0) {
-		sp->wrk->res_mode &= ~RES_LEN;
-		sp->wrk->res_mode |= RES_ESI_CHILD;
-	}
-
-	if (cache_param->http_gzip_support && sp->obj->gziped &&
-	    !RFC2616_Req_Gzip(sp)) {
-		/*
-		 * We don't know what it uncompresses to
-		 * XXX: we could cache that
-		 */
-		sp->wrk->res_mode &= ~RES_LEN;
-		sp->wrk->res_mode |= RES_GUNZIP;
-	}
-
-	if (!(sp->wrk->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
-		if (sp->obj->len == 0 && !sp->wrk->do_stream)
-			/*
-			 * If the object is empty, neither ESI nor GUNZIP
-			 * can make it any different size
-			 */
-			sp->wrk->res_mode |= RES_LEN;
-		else if (!sp->wantbody) {
-			/* Nothing */
-		} else if (sp->http->protover >= 11) {
-			sp->wrk->res_mode |= RES_CHUNKED;
-		} else {
-			sp->wrk->res_mode |= RES_EOF;
-			sp->doclose = "EOF mode";
-		}
-	}
-
-	sp->t_resp = VTIM_real();
-	if (sp->obj->objcore != NULL) {
-		if ((sp->t_resp - sp->obj->last_lru) > cache_param->lru_timeout &&
-		    EXP_Touch(sp->obj->objcore))
-			sp->obj->last_lru = sp->t_resp;
-		sp->obj->last_use = sp->t_resp;	/* XXX: locking ? */
-	}
-	http_Setup(sp->wrk->resp, sp->wrk->ws);
-	RES_BuildHttp(sp);
-	VCL_deliver_method(sp);
-	switch (sp->handling) {
-	case VCL_RET_DELIVER:
-		break;
-	case VCL_RET_RESTART:
-		if (sp->restarts >= cache_param->max_restarts)
-			break;
-		if (sp->wrk->do_stream) {
-			VDI_CloseFd(sp->wrk);
-			HSH_Drop(sp);
-		} else {
-			(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-		}
-		AZ(sp->obj);
-		sp->restarts++;
-		sp->director = NULL;
-		sp->wrk->h_content_length = NULL;
-		http_Setup(sp->wrk->bereq, NULL);
-		http_Setup(sp->wrk->beresp, NULL);
-		http_Setup(sp->wrk->resp, NULL);
-		sp->step = STP_RECV;
-		return (0);
-	default:
-		WRONG("Illegal action in vcl_deliver{}");
-	}
-	if (sp->wrk->do_stream) {
-		AssertObjCorePassOrBusy(sp->obj->objcore);
-		sp->step = STP_STREAMBODY;
-	} else {
-		sp->step = STP_DELIVER;
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Deliver an already stored object
- *
-DOT subgraph xcluster_deliver {
-DOT	deliver [
-DOT		shape=ellipse
-DOT		label="Send body"
-DOT	]
-DOT }
-DOT deliver -> DONE [style=bold,color=green]
-DOT deliver -> DONE [style=bold,color=red]
-DOT deliver -> DONE [style=bold,color=blue]
- *
- */
-
-static int
-cnt_deliver(struct sess *sp)
-{
-
-	sp->director = NULL;
-	sp->restarts = 0;
-
-	RES_WriteObj(sp);
-
-	assert(WRW_IsReleased(sp->wrk));
-	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
-	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-	http_Setup(sp->wrk->resp, NULL);
-	sp->step = STP_DONE;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * This is the final state, figure out if we should close or recycle
- * the client connection
- *
-DOT	DONE [
-DOT		shape=hexagon
-DOT		label="Request completed"
-DOT	]
- */
-
-static int
-cnt_done(struct sess *sp)
-{
-	double dh, dp, da;
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	AZ(sp->obj);
-	AZ(sp->wrk->vbc);
-	sp->director = NULL;
-	sp->restarts = 0;
-
-	sp->wrk->do_esi = 0;
-	sp->wrk->do_gunzip = 0;
-	sp->wrk->do_gzip = 0;
-	sp->wrk->do_stream = 0;
-	sp->wrk->is_gunzip = 0;
-	sp->wrk->is_gzip = 0;
-
-	if (sp->vcl != NULL && sp->esi_level == 0) {
-		if (sp->wrk->vcl != NULL)
-			VCL_Rel(&sp->wrk->vcl);
-		sp->wrk->vcl = sp->vcl;
-		sp->vcl = NULL;
-	}
-
-	SES_Charge(sp);
-
-	sp->t_end = VTIM_real();
-	sp->wrk->lastused = sp->t_end;
-	if (sp->xid == 0) {
-		sp->t_req = sp->t_end;
-		sp->t_resp = sp->t_end;
-	} else if (sp->esi_level == 0) {
-		dp = sp->t_resp - sp->t_req;
-		da = sp->t_end - sp->t_resp;
-		dh = sp->t_req - sp->t_open;
-		/* XXX: Add StatReq == StatSess */
-		/* XXX: Workaround for pipe */
-		if (sp->fd >= 0) {
-			WSP(sp, SLT_Length, "%ju",
-			    (uintmax_t)sp->req_bodybytes);
-		}
-		WSP(sp, SLT_ReqEnd, "%u %.9f %.9f %.9f %.9f %.9f",
-		    sp->xid, sp->t_req, sp->t_end, dh, dp, da);
-	}
-	sp->xid = 0;
-	sp->t_open = sp->t_end;
-	sp->t_resp = NAN;
-	WSL_Flush(sp->wrk, 0);
-
-	/* If we did an ESI include, don't mess up our state */
-	if (sp->esi_level > 0)
-		return (1);
-
-	sp->req_bodybytes = 0;
-
-	sp->t_req = NAN;
-	sp->hash_always_miss = 0;
-	sp->hash_ignore_busy = 0;
-
-	if (sp->fd >= 0 && sp->doclose != NULL) {
-		/*
-		 * This is an orderly close of the connection; ditch nolinger
-		 * before we close, to get queued data transmitted.
-		 */
-		// XXX: not yet (void)VTCP_linger(sp->fd, 0);
-		SES_Close(sp, sp->doclose);
-	}
-
-	if (sp->fd < 0) {
-		sp->wrk->stats.sess_closed++;
-		SES_Delete(sp, NULL);
-		return (1);
-	}
-
-	if (sp->wrk->stats.client_req >= cache_param->wthread_stats_rate)
-		WRK_SumStat(sp->wrk);
-	/* Reset the workspace to the session-watermark */
-	WS_Reset(sp->ws, sp->ws_ses);
-	WS_Reset(sp->wrk->ws, NULL);
-
-	i = HTC_Reinit(sp->htc);
-	if (i == 1) {
-		sp->wrk->stats.sess_pipeline++;
-		sp->step = STP_START;
-		return (0);
-	}
-	if (Tlen(sp->htc->rxbuf)) {
-		sp->wrk->stats.sess_readahead++;
-		sp->step = STP_WAIT;
-		return (0);
-	}
-	if (cache_param->session_linger > 0) {
-		sp->wrk->stats.sess_linger++;
-		sp->step = STP_WAIT;
-		return (0);
-	}
-	sp->wrk->stats.sess_herd++;
-	sp->wrk = NULL;
-	Pool_Wait(sp);
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * Emit an error
- *
-DOT subgraph xcluster_error {
-DOT	vcl_error [
-DOT		shape=record
-DOT		label="vcl_error()|resp."
-DOT	]
-DOT	ERROR -> vcl_error
-DOT	vcl_error-> prepresp [label=deliver]
-DOT }
-DOT vcl_error-> rsterr [label="restart",color=purple]
-DOT rsterr [label="RESTART",shape=plaintext]
- */
-
-static int
-cnt_error(struct sess *sp)
-{
-	struct worker *w;
-	struct http *h;
-	char date[40];
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	sp->wrk->do_esi = 0;
-	sp->wrk->is_gzip = 0;
-	sp->wrk->is_gunzip = 0;
-	sp->wrk->do_gzip = 0;
-	sp->wrk->do_gunzip = 0;
-	sp->wrk->do_stream = 0;
-
-	w = sp->wrk;
-	if (sp->obj == NULL) {
-		HSH_Prealloc(sp);
-		EXP_Clr(&w->exp);
-		sp->obj = STV_NewObject(sp, NULL, cache_param->http_resp_size,
-		     &w->exp, (uint16_t)cache_param->http_max_hdr);
-		if (sp->obj == NULL)
-			sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE,
-			    cache_param->http_resp_size, &w->exp,
-			    (uint16_t)cache_param->http_max_hdr);
-		if (sp->obj == NULL) {
-			sp->doclose = "Out of objects";
-			sp->director = NULL;
-			sp->wrk->h_content_length = NULL;
-			http_Setup(sp->wrk->beresp, NULL);
-			http_Setup(sp->wrk->bereq, NULL);
-			sp->step = STP_DONE;
-			return(0);
-		}
-		AN(sp->obj);
-		sp->obj->xid = sp->xid;
-		sp->obj->exp.entered = sp->t_req;
-	} else {
-		/* XXX: Null the headers ? */
-	}
-	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-	h = sp->obj->http;
-
-	if (sp->err_code < 100 || sp->err_code > 999)
-		sp->err_code = 501;
-
-	http_PutProtocol(w, sp->vsl_id, h, "HTTP/1.1");
-	http_PutStatus(h, sp->err_code);
-	VTIM_format(VTIM_real(), date);
-	http_PrintfHeader(w, sp->vsl_id, h, "Date: %s", date);
-	http_SetHeader(w, sp->vsl_id, h, "Server: Varnish");
-
-	if (sp->err_reason != NULL)
-		http_PutResponse(w, sp->vsl_id, h, sp->err_reason);
-	else
-		http_PutResponse(w, sp->vsl_id, h,
-		    http_StatusMessage(sp->err_code));
-	VCL_error_method(sp);
-
-	if (sp->handling == VCL_RET_RESTART &&
-	    sp->restarts <  cache_param->max_restarts) {
-		HSH_Drop(sp);
-		sp->director = NULL;
-		sp->restarts++;
-		sp->step = STP_RECV;
-		return (0);
-	} else if (sp->handling == VCL_RET_RESTART)
-		sp->handling = VCL_RET_DELIVER;
-
-
-	/* We always close when we take this path */
-	sp->doclose = "error";
-	sp->wantbody = 1;
-
-	assert(sp->handling == VCL_RET_DELIVER);
-	sp->err_code = 0;
-	sp->err_reason = NULL;
-	http_Setup(sp->wrk->bereq, NULL);
-	sp->step = STP_PREPRESP;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Fetch response headers from the backend
- *
-DOT subgraph xcluster_fetch {
-DOT	fetch [
-DOT		shape=ellipse
-DOT		label="fetch hdr\nfrom backend\n(find obj.ttl)"
-DOT	]
-DOT	vcl_fetch [
-DOT		shape=record
-DOT		label="vcl_fetch()|req.\nbereq.\nberesp."
-DOT	]
-DOT	fetch -> vcl_fetch [style=bold,color=blue]
-DOT	fetch -> vcl_fetch [style=bold,color=red]
-DOT	fetch_pass [
-DOT		shape=ellipse
-DOT		label="obj.f.pass=true"
-DOT	]
-DOT	vcl_fetch -> fetch_pass [label="hit_for_pass",style=bold,color=red]
-DOT }
-DOT fetch_pass -> fetchbody [style=bold,color=red]
-DOT vcl_fetch -> fetchbody [label="deliver",style=bold,color=blue]
-DOT vcl_fetch -> rstfetch [label="restart",color=purple]
-DOT rstfetch [label="RESTART",shape=plaintext]
-DOT fetch -> errfetch
-DOT vcl_fetch -> errfetch [label="error"]
-DOT errfetch [label="ERROR",shape=plaintext]
- */
-
-static int
-cnt_fetch(struct sess *sp)
-{
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	AN(sp->director);
-	AZ(sp->wrk->vbc);
-	AZ(sp->wrk->h_content_length);
-	AZ(sp->wrk->do_close);
-	AZ(sp->wrk->storage_hint);
-
-	http_Setup(sp->wrk->beresp, sp->wrk->ws);
-
-	i = FetchHdr(sp);
-	/*
-	 * If we recycle a backend connection, there is a finite chance
-	 * that the backend closed it before we get a request to it.
-	 * Do a single retry in that case.
-	 */
-	if (i == 1) {
-		VSC_C_main->backend_retry++;
-		i = FetchHdr(sp);
-	}
-
-	if (i) {
-		sp->handling = VCL_RET_ERROR;
-		sp->err_code = 503;
-	} else {
-		/*
-		 * These two headers can be spread over multiple actual headers
-		 * and we rely on their content outside of VCL, so collect them
-		 * into one line here.
-		 */
-		http_CollectHdr(sp->wrk->beresp, H_Cache_Control);
-		http_CollectHdr(sp->wrk->beresp, H_Vary);
-
-		/*
-		 * Figure out how the fetch is supposed to happen, before the
-		 * headers are adultered by VCL
-		 * NB: Also sets other sp->wrk variables
-		 */
-		sp->wrk->body_status = RFC2616_Body(sp);
-
-		sp->err_code = http_GetStatus(sp->wrk->beresp);
-
-		/*
-		 * What does RFC2616 think about TTL ?
-		 */
-		EXP_Clr(&sp->wrk->exp);
-		sp->wrk->exp.entered = VTIM_real();
-		RFC2616_Ttl(sp);
-
-		/* pass from vclrecv{} has negative TTL */
-		if (sp->objcore == NULL)
-			sp->wrk->exp.ttl = -1.;
-
-		AZ(sp->wrk->do_esi);
-
-		VCL_fetch_method(sp);
-
-		switch (sp->handling) {
-		case VCL_RET_HIT_FOR_PASS:
-			if (sp->objcore != NULL)
-				sp->objcore->flags |= OC_F_PASS;
-			sp->step = STP_FETCHBODY;
-			return (0);
-		case VCL_RET_DELIVER:
-			AssertObjCorePassOrBusy(sp->objcore);
-			sp->step = STP_FETCHBODY;
-			return (0);
-		default:
-			break;
-		}
-
-		/* We are not going to fetch the body, Close the connection */
-		VDI_CloseFd(sp->wrk);
-	}
-
-	/* Clean up partial fetch */
-	AZ(sp->wrk->vbc);
-
-	if (sp->objcore != NULL) {
-		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
-		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
-		sp->objcore = NULL;
-	}
-	http_Setup(sp->wrk->bereq, NULL);
-	http_Setup(sp->wrk->beresp, NULL);
-	sp->wrk->h_content_length = NULL;
-	sp->director = NULL;
-	sp->wrk->storage_hint = NULL;
-
-	switch (sp->handling) {
-	case VCL_RET_RESTART:
-		sp->restarts++;
-		sp->step = STP_RECV;
-		return (0);
-	case VCL_RET_ERROR:
-		sp->step = STP_ERROR;
-		return (0);
-	default:
-		WRONG("Illegal action in vcl_fetch{}");
-	}
-}
-
-/*--------------------------------------------------------------------
- * Fetch response body from the backend
- *
-DOT subgraph xcluster_body {
-DOT	fetchbody [
-DOT		shape=diamond
-DOT		label="stream ?"
-DOT	]
-DOT	fetchbody2 [
-DOT		shape=ellipse
-DOT		label="fetch body\nfrom backend\n"
-DOT	]
-DOT }
-DOT fetchbody -> fetchbody2 [label=no,style=bold,color=red]
-DOT fetchbody -> fetchbody2 [style=bold,color=blue]
-DOT fetchbody -> prepresp [label=yes,style=bold,color=cyan]
-DOT fetchbody2 -> prepresp [style=bold,color=red]
-DOT fetchbody2 -> prepresp [style=bold,color=blue]
- */
-
-
-static int
-cnt_fetchbody(struct sess *sp)
-{
-	int i;
-	struct http *hp, *hp2;
-	char *b;
-	uint16_t nhttp;
-	unsigned l;
-	struct vsb *vary = NULL;
-	int varyl = 0, pass;
-
-	assert(sp->handling == VCL_RET_HIT_FOR_PASS ||
-	    sp->handling == VCL_RET_DELIVER);
-
-	if (sp->objcore == NULL) {
-		/* This is a pass from vcl_recv */
-		pass = 1;
-		/* VCL may have fiddled this, but that doesn't help */
-		sp->wrk->exp.ttl = -1.;
-	} else if (sp->handling == VCL_RET_HIT_FOR_PASS) {
-		/* pass from vcl_fetch{} -> hit-for-pass */
-		/* XXX: the bereq was not filtered pass... */
-		pass = 1;
-	} else {
-		/* regular object */
-		pass = 0;
-	}
-
-	/*
-	 * The VCL variables beresp.do_g[un]zip tells us how we want the
-	 * object processed before it is stored.
-	 *
-	 * The backend Content-Encoding header tells us what we are going
-	 * to receive, which we classify in the following three classes:
-	 *
-	 *	"Content-Encoding: gzip"	--> object is gzip'ed.
-	 *	no Content-Encoding		--> object is not gzip'ed.
-	 *	anything else			--> do nothing wrt gzip
-	 *
-	 */
-
-	AZ(sp->wrk->vfp);
-
-	/* We do nothing unless the param is set */
-	if (!cache_param->http_gzip_support)
-		sp->wrk->do_gzip = sp->wrk->do_gunzip = 0;
-
-	sp->wrk->is_gzip =
-	    http_HdrIs(sp->wrk->beresp, H_Content_Encoding, "gzip");
-
-	sp->wrk->is_gunzip =
-	    !http_GetHdr(sp->wrk->beresp, H_Content_Encoding, NULL);
-
-	/* It can't be both */
-	assert(sp->wrk->is_gzip == 0 || sp->wrk->is_gunzip == 0);
-
-	/* We won't gunzip unless it is gzip'ed */
-	if (sp->wrk->do_gunzip && !sp->wrk->is_gzip)
-		sp->wrk->do_gunzip = 0;
-
-	/* If we do gunzip, remove the C-E header */
-	if (sp->wrk->do_gunzip)
-		http_Unset(sp->wrk->beresp, H_Content_Encoding);
-
-	/* We wont gzip unless it is ungziped */
-	if (sp->wrk->do_gzip && !sp->wrk->is_gunzip)
-		sp->wrk->do_gzip = 0;
-
-	/* If we do gzip, add the C-E header */
-	if (sp->wrk->do_gzip)
-		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->beresp,
-		    "Content-Encoding: gzip");
-
-	/* But we can't do both at the same time */
-	assert(sp->wrk->do_gzip == 0 || sp->wrk->do_gunzip == 0);
-
-	/* ESI takes precedence and handles gzip/gunzip itself */
-	if (sp->wrk->do_esi)
-		sp->wrk->vfp = &vfp_esi;
-	else if (sp->wrk->do_gunzip)
-		sp->wrk->vfp = &vfp_gunzip;
-	else if (sp->wrk->do_gzip)
-		sp->wrk->vfp = &vfp_gzip;
-	else if (sp->wrk->is_gzip)
-		sp->wrk->vfp = &vfp_testgzip;
-
-	if (sp->wrk->do_esi || sp->esi_level > 0)
-		sp->wrk->do_stream = 0;
-	if (!sp->wantbody)
-		sp->wrk->do_stream = 0;
-
-	l = http_EstimateWS(sp->wrk->beresp,
-	    pass ? HTTPH_R_PASS : HTTPH_A_INS, &nhttp);
-
-	/* Create Vary instructions */
-	if (sp->objcore != NULL) {
-		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
-		vary = VRY_Create(sp, sp->wrk->beresp);
-		if (vary != NULL) {
-			varyl = VSB_len(vary);
-			assert(varyl > 0);
-			l += varyl;
-		}
-	}
-
-	/*
-	 * Space for producing a Content-Length: header including padding
-	 * A billion gigabytes is enough for anybody.
-	 */
-	l += strlen("Content-Length: XxxXxxXxxXxxXxxXxx") + sizeof(void *);
-
-	if (sp->wrk->exp.ttl < cache_param->shortlived || sp->objcore == NULL)
-		sp->wrk->storage_hint = TRANSIENT_STORAGE;
-
-	sp->obj = STV_NewObject(sp, sp->wrk->storage_hint, l,
-	    &sp->wrk->exp, nhttp);
-	if (sp->obj == NULL) {
-		/*
-		 * Try to salvage the transaction by allocating a
-		 * shortlived object on Transient storage.
-		 */
-		sp->obj = STV_NewObject(sp, TRANSIENT_STORAGE, l,
-		    &sp->wrk->exp, nhttp);
-		if (sp->wrk->exp.ttl > cache_param->shortlived)
-			sp->wrk->exp.ttl = cache_param->shortlived;
-		sp->wrk->exp.grace = 0.0;
-		sp->wrk->exp.keep = 0.0;
-	}
-	if (sp->obj == NULL) {
-		sp->err_code = 503;
-		sp->step = STP_ERROR;
-		VDI_CloseFd(sp->wrk);
-		return (0);
-	}
-	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-
-	sp->wrk->storage_hint = NULL;
-
-	if (sp->wrk->do_gzip || (sp->wrk->is_gzip && !sp->wrk->do_gunzip))
-		sp->obj->gziped = 1;
-
-	if (vary != NULL) {
-		sp->obj->vary =
-		    (void *)WS_Alloc(sp->obj->http->ws, varyl);
-		AN(sp->obj->vary);
-		memcpy(sp->obj->vary, VSB_data(vary), varyl);
-		VRY_Validate(sp->obj->vary);
-		VSB_delete(vary);
-	}
-
-	sp->obj->xid = sp->xid;
-	sp->obj->response = sp->err_code;
-	WS_Assert(sp->obj->ws_o);
-
-	/* Filter into object */
-	hp = sp->wrk->beresp;
-	hp2 = sp->obj->http;
-
-	hp2->logtag = HTTP_Obj;
-	http_CopyResp(hp2, hp);
-	http_FilterFields(sp->wrk, sp->vsl_id, hp2, hp,
-	    pass ? HTTPH_R_PASS : HTTPH_A_INS);
-	http_CopyHome(sp->wrk, sp->vsl_id, hp2);
-
-	if (http_GetHdr(hp, H_Last_Modified, &b))
-		sp->obj->last_modified = VTIM_parse(b);
-	else
-		sp->obj->last_modified = floor(sp->wrk->exp.entered);
-
-	assert(WRW_IsReleased(sp->wrk));
-
-	/*
-	 * If we can deliver a 304 reply, we don't bother streaming.
-	 * Notice that vcl_deliver{} could still nuke the headers
-	 * that allow the 304, in which case we return 200 non-stream.
-	 */
-	if (sp->obj->response == 200 &&
-	    sp->http->conds &&
-	    RFC2616_Do_Cond(sp))
-		sp->wrk->do_stream = 0;
-
-	AssertObjCorePassOrBusy(sp->obj->objcore);
-
-	if (sp->wrk->do_stream) {
-		sp->step = STP_PREPRESP;
-		return (0);
-	}
-
-	/* Use unmodified headers*/
-	i = FetchBody(sp->wrk, sp->obj);
-
-	sp->wrk->h_content_length = NULL;
-
-	http_Setup(sp->wrk->bereq, NULL);
-	http_Setup(sp->wrk->beresp, NULL);
-	sp->wrk->vfp = NULL;
-	assert(WRW_IsReleased(sp->wrk));
-	AZ(sp->wrk->vbc);
-	AN(sp->director);
-
-	if (i) {
-		HSH_Drop(sp);
-		AZ(sp->obj);
-		sp->err_code = 503;
-		sp->step = STP_ERROR;
-		return (0);
-	}
-
-	if (sp->obj->objcore != NULL) {
-		EXP_Insert(sp->obj);
-		AN(sp->obj->objcore);
-		AN(sp->obj->objcore->ban);
-		HSH_Unbusy(sp);
-	}
-	sp->wrk->acct_tmp.fetch++;
-	sp->step = STP_PREPRESP;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Stream the body as we fetch it
-DOT subgraph xstreambody {
-DOT	streambody [
-DOT		shape=ellipse
-DOT		label="streaming\nfetch/deliver"
-DOT	]
-DOT }
-DOT streambody -> DONE [style=bold,color=cyan]
- */
-
-static int
-cnt_streambody(struct sess *sp)
-{
-	int i;
-	struct stream_ctx sctx;
-	uint8_t obuf[sp->wrk->res_mode & RES_GUNZIP ?
-	    cache_param->gzip_stack_buffer : 1];
-
-	memset(&sctx, 0, sizeof sctx);
-	sctx.magic = STREAM_CTX_MAGIC;
-	AZ(sp->wrk->sctx);
-	sp->wrk->sctx = &sctx;
-
-	if (sp->wrk->res_mode & RES_GUNZIP) {
-		sctx.vgz = VGZ_NewUngzip(sp->wrk, "U S -");
-		sctx.obuf = obuf;
-		sctx.obuf_len = sizeof (obuf);
-	}
-
-	RES_StreamStart(sp);
-
-	AssertObjCorePassOrBusy(sp->obj->objcore);
-
-	i = FetchBody(sp->wrk, sp->obj);
-
-	sp->wrk->h_content_length = NULL;
-
-	http_Setup(sp->wrk->bereq, NULL);
-	http_Setup(sp->wrk->beresp, NULL);
-	sp->wrk->vfp = NULL;
-	AZ(sp->wrk->vbc);
-	AN(sp->director);
-
-	if (!i && sp->obj->objcore != NULL) {
-		EXP_Insert(sp->obj);
-		AN(sp->obj->objcore);
-		AN(sp->obj->objcore->ban);
-		HSH_Unbusy(sp);
-	} else {
-		sp->doclose = "Stream error";
-	}
-	sp->wrk->acct_tmp.fetch++;
-	sp->director = NULL;
-	sp->restarts = 0;
-
-	RES_StreamEnd(sp);
-	if (sp->wrk->res_mode & RES_GUNZIP)
-		(void)VGZ_Destroy(&sctx.vgz, sp->vsl_id);
-
-	sp->wrk->sctx = NULL;
-	assert(WRW_IsReleased(sp->wrk));
-	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
-	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-	http_Setup(sp->wrk->resp, NULL);
-	sp->step = STP_DONE;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * The very first request
- */
-static int
-cnt_first(struct sess *sp)
-{
-
-	/*
-	 * XXX: If we don't have acceptfilters we are somewhat subject
-	 * XXX: to DoS'ing here.  One remedy would be to set a shorter
-	 * XXX: SO_RCVTIMEO and once we have received something here
-	 * XXX: increase it to the normal value.
-	 */
-
-	assert(sp->xid == 0);
-	assert(sp->restarts == 0);
-	VCA_Prep(sp);
-
-	/* Record the session watermark */
-	sp->ws_ses = WS_Snapshot(sp->ws);
-
-	/* Receive a HTTP protocol request */
-	HTC_Init(sp->htc, sp->ws, sp->fd, sp->vsl_id, cache_param->http_req_size,
-	    cache_param->http_req_hdr_len);
-	sp->wrk->lastused = sp->t_open;
-	sp->wrk->acct_tmp.sess++;
-
-	sp->step = STP_WAIT;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * HIT
- * We had a cache hit.  Ask VCL, then march off as instructed.
- *
-DOT subgraph xcluster_hit {
-DOT	hit [
-DOT		shape=record
-DOT		label="vcl_hit()|req.\nobj."
-DOT	]
-DOT }
-DOT hit -> err_hit [label="error"]
-DOT err_hit [label="ERROR",shape=plaintext]
-DOT hit -> rst_hit [label="restart",color=purple]
-DOT rst_hit [label="RESTART",shape=plaintext]
-DOT hit -> pass [label=pass,style=bold,color=red]
-DOT hit -> prepresp [label="deliver",style=bold,color=green]
- */
-
-static int
-cnt_hit(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	assert(!(sp->obj->objcore->flags & OC_F_PASS));
-
-	AZ(sp->wrk->do_stream);
-
-	VCL_hit_method(sp);
-
-	if (sp->handling == VCL_RET_DELIVER) {
-		/* Dispose of any body part of the request */
-		(void)FetchReqBody(sp);
-		AZ(sp->wrk->bereq->ws);
-		AZ(sp->wrk->beresp->ws);
-		sp->step = STP_PREPRESP;
-		return (0);
-	}
-
-	/* Drop our object, we won't need it */
-	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-	sp->objcore = NULL;
-
-	switch(sp->handling) {
-	case VCL_RET_PASS:
-		sp->step = STP_PASS;
-		return (0);
-	case VCL_RET_ERROR:
-		sp->step = STP_ERROR;
-		return (0);
-	case VCL_RET_RESTART:
-		sp->director = NULL;
-		sp->restarts++;
-		sp->step = STP_RECV;
-		return (0);
-	default:
-		WRONG("Illegal action in vcl_hit{}");
-	}
-}
-
-/*--------------------------------------------------------------------
- * LOOKUP
- * Hash things together and look object up in hash-table.
- *
- * LOOKUP consists of two substates so that we can reenter if we
- * encounter a busy object.
- *
-DOT subgraph xcluster_lookup {
-DOT	hash [
-DOT		shape=record
-DOT		label="vcl_hash()|req."
-DOT	]
-DOT	lookup [
-DOT		shape=diamond
-DOT		label="obj in cache ?\ncreate if not"
-DOT	]
-DOT	lookup2 [
-DOT		shape=diamond
-DOT		label="obj.f.pass ?"
-DOT	]
-DOT	hash -> lookup [label="hash",style=bold,color=green]
-DOT	lookup -> lookup2 [label="yes",style=bold,color=green]
-DOT }
-DOT lookup2 -> hit [label="no", style=bold,color=green]
-DOT lookup2 -> pass [label="yes",style=bold,color=red]
-DOT lookup -> miss [label="no",style=bold,color=blue]
- */
-
-static int
-cnt_lookup(struct sess *sp)
-{
-	struct objcore *oc;
-	struct object *o;
-	struct objhead *oh;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	if (sp->hash_objhead == NULL) {
-		/* Not a waiting list return */
-		AZ(sp->vary_b);
-		AZ(sp->vary_l);
-		AZ(sp->vary_e);
-		(void)WS_Reserve(sp->ws, 0);
-	} else {
-		AN(sp->ws->r);
-	}
-	sp->vary_b = (void*)sp->ws->f;
-	sp->vary_e = (void*)sp->ws->r;
-	sp->vary_b[2] = '\0';
-
-	oc = HSH_Lookup(sp, &oh);
-
-	if (oc == NULL) {
-		/*
-		 * We lost the session to a busy object, disembark the
-		 * worker thread.   The hash code to restart the session,
-		 * still in STP_LOOKUP, later when the busy object isn't.
-		 * NB:  Do not access sp any more !
-		 */
-		return (1);
-	}
-
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-
-	/* If we inserted a new object it's a miss */
-	if (oc->flags & OC_F_BUSY) {
-		sp->wrk->stats.cache_miss++;
-
-		if (sp->vary_l != NULL) {
-			assert(oc->busyobj->vary == sp->vary_b);
-			VRY_Validate(oc->busyobj->vary);
-			WS_ReleaseP(sp->ws, (void*)sp->vary_l);
-		} else {
-			AZ(oc->busyobj->vary);
-			WS_Release(sp->ws, 0);
-		}
-		sp->vary_b = NULL;
-		sp->vary_l = NULL;
-		sp->vary_e = NULL;
-
-		sp->objcore = oc;
-		sp->step = STP_MISS;
-		return (0);
-	}
-
-	o = oc_getobj(sp->wrk, oc);
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	sp->obj = o;
-
-	WS_Release(sp->ws, 0);
-	sp->vary_b = NULL;
-	sp->vary_l = NULL;
-	sp->vary_e = NULL;
-
-	if (oc->flags & OC_F_PASS) {
-		sp->wrk->stats.cache_hitpass++;
-		WSP(sp, SLT_HitPass, "%u", sp->obj->xid);
-		(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-		sp->objcore = NULL;
-		sp->step = STP_PASS;
-		return (0);
-	}
-
-	sp->wrk->stats.cache_hit++;
-	WSP(sp, SLT_Hit, "%u", sp->obj->xid);
-	sp->step = STP_HIT;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * We had a miss, ask VCL, proceed as instructed
- *
-DOT subgraph xcluster_miss {
-DOT	miss [
-DOT		shape=ellipse
-DOT		label="filter req.->bereq."
-DOT	]
-DOT	vcl_miss [
-DOT		shape=record
-DOT		label="vcl_miss()|req.\nbereq."
-DOT	]
-DOT	miss -> vcl_miss [style=bold,color=blue]
-DOT }
-DOT vcl_miss -> rst_miss [label="restart",color=purple]
-DOT rst_miss [label="RESTART",shape=plaintext]
-DOT vcl_miss -> err_miss [label="error"]
-DOT err_miss [label="ERROR",shape=plaintext]
-DOT vcl_miss -> fetch [label="fetch",style=bold,color=blue]
-DOT vcl_miss -> pass [label="pass",style=bold,color=red]
-DOT
- */
-
-static int
-cnt_miss(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	AZ(sp->obj);
-	AN(sp->objcore);
-	WS_Reset(sp->wrk->ws, NULL);
-	http_Setup(sp->wrk->bereq, sp->wrk->ws);
-	http_FilterHeader(sp, HTTPH_R_FETCH);
-	http_ForceGet(sp->wrk->bereq);
-	if (cache_param->http_gzip_support) {
-		/*
-		 * We always ask the backend for gzip, even if the
-		 * client doesn't grok it.  We will uncompress for
-		 * the minority of clients which don't.
-		 */
-		http_Unset(sp->wrk->bereq, H_Accept_Encoding);
-		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->bereq,
-		    "Accept-Encoding: gzip");
-	}
-	sp->wrk->connect_timeout = 0;
-	sp->wrk->first_byte_timeout = 0;
-	sp->wrk->between_bytes_timeout = 0;
-	VCL_miss_method(sp);
-	switch(sp->handling) {
-	case VCL_RET_ERROR:
-		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
-		sp->objcore = NULL;
-		http_Setup(sp->wrk->bereq, NULL);
-		sp->step = STP_ERROR;
-		return (0);
-	case VCL_RET_PASS:
-		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
-		sp->objcore = NULL;
-		sp->step = STP_PASS;
-		return (0);
-	case VCL_RET_FETCH:
-		sp->step = STP_FETCH;
-		return (0);
-	case VCL_RET_RESTART:
-		AZ(HSH_Deref(sp->wrk, sp->objcore, NULL));
-		sp->objcore = NULL;
-		INCOMPL();
-	default:
-		WRONG("Illegal action in vcl_miss{}");
-	}
-}
-
-/*--------------------------------------------------------------------
- * Start pass processing by getting headers from backend, then
- * continue in passbody.
- *
-DOT subgraph xcluster_pass {
-DOT	pass [
-DOT		shape=ellipse
-DOT		label="deref obj."
-DOT	]
-DOT	pass2 [
-DOT		shape=ellipse
-DOT		label="filter req.->bereq."
-DOT	]
-DOT	vcl_pass [
-DOT		shape=record
-DOT		label="vcl_pass()|req.\nbereq."
-DOT	]
-DOT	pass_do [
-DOT		shape=ellipse
-DOT		label="create anon object\n"
-DOT	]
-DOT	pass -> pass2 [style=bold, color=red]
-DOT	pass2 -> vcl_pass [style=bold, color=red]
-DOT	vcl_pass -> pass_do [label="pass"] [style=bold, color=red]
-DOT }
-DOT pass_do -> fetch [style=bold, color=red]
-DOT vcl_pass -> rst_pass [label="restart",color=purple]
-DOT rst_pass [label="RESTART",shape=plaintext]
-DOT vcl_pass -> err_pass [label="error"]
-DOT err_pass [label="ERROR",shape=plaintext]
- */
-
-static int
-cnt_pass(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-	AZ(sp->obj);
-
-	WS_Reset(sp->wrk->ws, NULL);
-	http_Setup(sp->wrk->bereq, sp->wrk->ws);
-	http_FilterHeader(sp, HTTPH_R_PASS);
-
-	sp->wrk->connect_timeout = 0;
-	sp->wrk->first_byte_timeout = 0;
-	sp->wrk->between_bytes_timeout = 0;
-	VCL_pass_method(sp);
-	if (sp->handling == VCL_RET_ERROR) {
-		http_Setup(sp->wrk->bereq, NULL);
-		sp->step = STP_ERROR;
-		return (0);
-	}
-	assert(sp->handling == VCL_RET_PASS);
-	sp->wrk->acct_tmp.pass++;
-	sp->sendbody = 1;
-	sp->step = STP_FETCH;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Ship the request header to the backend unchanged, then pipe
- * until one of the ends close the connection.
- *
-DOT subgraph xcluster_pipe {
-DOT	pipe [
-DOT		shape=ellipse
-DOT		label="Filter req.->bereq."
-DOT	]
-DOT	vcl_pipe [
-DOT		shape=record
-DOT		label="vcl_pipe()|req.\nbereq\."
-DOT	]
-DOT	pipe_do [
-DOT		shape=ellipse
-DOT		label="send bereq.\npipe until close"
-DOT	]
-DOT	vcl_pipe -> pipe_do [label="pipe",style=bold,color=orange]
-DOT	pipe -> vcl_pipe [style=bold,color=orange]
-DOT }
-DOT pipe_do -> DONE [style=bold,color=orange]
-DOT vcl_pipe -> err_pipe [label="error"]
-DOT err_pipe [label="ERROR",shape=plaintext]
- */
-
-static int
-cnt_pipe(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-
-	sp->wrk->acct_tmp.pipe++;
-	WS_Reset(sp->wrk->ws, NULL);
-	http_Setup(sp->wrk->bereq, sp->wrk->ws);
-	http_FilterHeader(sp, HTTPH_R_PIPE);
-
-	VCL_pipe_method(sp);
-
-	if (sp->handling == VCL_RET_ERROR)
-		INCOMPL();
-	assert(sp->handling == VCL_RET_PIPE);
-
-	PipeSession(sp);
-	assert(WRW_IsReleased(sp->wrk));
-	http_Setup(sp->wrk->bereq, NULL);
-	sp->step = STP_DONE;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * RECV
- * We have a complete request, set everything up and start it.
- *
-DOT subgraph xcluster_recv {
-DOT	recv [
-DOT		shape=record
-DOT		label="vcl_recv()|req."
-DOT	]
-DOT }
-DOT RESTART -> recv
-DOT recv -> pipe [label="pipe",style=bold,color=orange]
-DOT recv -> pass2 [label="pass",style=bold,color=red]
-DOT recv -> err_recv [label="error"]
-DOT err_recv [label="ERROR",shape=plaintext]
-DOT recv -> hash [label="lookup",style=bold,color=green]
- */
-
-static int
-cnt_recv(struct sess *sp)
-{
-	unsigned recv_handling;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
-	AZ(sp->obj);
-	assert(sp->wrk->wrw.ciov == sp->wrk->wrw.siov);
-
-	/* By default we use the first backend */
-	AZ(sp->director);
-	sp->director = sp->vcl->director[0];
-	AN(sp->director);
-
-	sp->disable_esi = 0;
-	sp->hash_always_miss = 0;
-	sp->hash_ignore_busy = 0;
-	sp->client_identity = NULL;
-
-	http_CollectHdr(sp->http, H_Cache_Control);
-
-	VCL_recv_method(sp);
-	recv_handling = sp->handling;
-
-	if (sp->restarts >= cache_param->max_restarts) {
-		if (sp->err_code == 0)
-			sp->err_code = 503;
-		sp->step = STP_ERROR;
-		return (0);
-	}
-
-	/* Zap these, in case we came here through restart */
-	sp->wrk->do_esi = 0;
-	sp->wrk->is_gzip = 0;
-	sp->wrk->is_gunzip = 0;
-	sp->wrk->do_gzip = 0;
-	sp->wrk->do_gunzip = 0;
-	sp->wrk->do_stream = 0;
-
-	if (cache_param->http_gzip_support &&
-	     (recv_handling != VCL_RET_PIPE) &&
-	     (recv_handling != VCL_RET_PASS)) {
-		if (RFC2616_Req_Gzip(sp)) {
-			http_Unset(sp->http, H_Accept_Encoding);
-			http_SetHeader(sp->wrk, sp->vsl_id, sp->http,
-			    "Accept-Encoding: gzip");
-		} else {
-			http_Unset(sp->http, H_Accept_Encoding);
-		}
-	}
-
-	SHA256_Init(sp->wrk->sha256ctx);
-	VCL_hash_method(sp);
-	assert(sp->handling == VCL_RET_HASH);
-	SHA256_Final(sp->digest, sp->wrk->sha256ctx);
-
-	if (!strcmp(sp->http->hd[HTTP_HDR_REQ].b, "HEAD"))
-		sp->wantbody = 0;
-	else
-		sp->wantbody = 1;
-
-	sp->sendbody = 0;
-	switch(recv_handling) {
-	case VCL_RET_LOOKUP:
-		/* XXX: discard req body, if any */
-		sp->step = STP_LOOKUP;
-		return (0);
-	case VCL_RET_PIPE:
-		if (sp->esi_level > 0) {
-			/* XXX: VSL something */
-			INCOMPL();
-			/* sp->step = STP_DONE; */
-			return (1);
-		}
-		sp->step = STP_PIPE;
-		return (0);
-	case VCL_RET_PASS:
-		sp->step = STP_PASS;
-		return (0);
-	case VCL_RET_ERROR:
-		/* XXX: discard req body, if any */
-		sp->step = STP_ERROR;
-		return (0);
-	default:
-		WRONG("Illegal action in vcl_recv{}");
-	}
-}
-
-/*--------------------------------------------------------------------
- * START
- * Handle a request, wherever it came from recv/restart.
- *
-DOT start [shape=box,label="Dissect request"]
-DOT start -> recv [style=bold,color=green]
- */
-
-static int
-cnt_start(struct sess *sp)
-{
-	uint16_t done;
-	char *p;
-	const char *r = "HTTP/1.1 100 Continue\r\n\r\n";
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	AZ(sp->restarts);
-	AZ(sp->obj);
-	AZ(sp->vcl);
-
-	/* Update stats of various sorts */
-	sp->wrk->stats.client_req++;
-	sp->t_req = VTIM_real();
-	sp->wrk->lastused = sp->t_req;
-	sp->wrk->acct_tmp.req++;
-
-	/* Assign XID and log */
-	sp->xid = ++xids;				/* XXX not locked */
-	WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port,  sp->xid);
-
-	/* Borrow VCL reference from worker thread */
-	VCL_Refresh(&sp->wrk->vcl);
-	sp->vcl = sp->wrk->vcl;
-	sp->wrk->vcl = NULL;
-
-	http_Setup(sp->http, sp->ws);
-	done = http_DissectRequest(sp);
-
-	/* If we could not even parse the request, just close */
-	if (done == 400) {
-		sp->step = STP_DONE;
-		SES_Close(sp, "junk");
-		return (0);
-	}
-
-	/* Catch request snapshot */
-	sp->ws_req = WS_Snapshot(sp->ws);
-
-	/* Catch original request, before modification */
-	HTTP_Copy(sp->http0, sp->http);
-
-	if (done != 0) {
-		sp->err_code = done;
-		sp->step = STP_ERROR;
-		return (0);
-	}
-
-	sp->doclose = http_DoConnection(sp->http);
-
-	/* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */
-
-	/*
-	 * Handle Expect headers
-	 */
-	if (http_GetHdr(sp->http, H_Expect, &p)) {
-		if (strcasecmp(p, "100-continue")) {
-			sp->err_code = 417;
-			sp->step = STP_ERROR;
-			return (0);
-		}
-
-		/* XXX: Don't bother with write failures for now */
-		(void)write(sp->fd, r, strlen(r));
-		/* XXX: When we do ESI includes, this is not removed
-		 * XXX: because we use http0 as our basis.  Believed
-		 * XXX: safe, but potentially confusing.
-		 */
-		http_Unset(sp->http, H_Expect);
-	}
-
-	sp->step = STP_RECV;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Central state engine dispatcher.
- *
- * Kick the session around until it has had enough.
- *
- */
-
-static void
-cnt_diag(struct sess *sp, const char *state)
-{
-	if (sp->wrk != NULL) {
-		WSP(sp, SLT_Debug, "thr %p STP_%s sp %p obj %p vcl %p",
-		    pthread_self(), state, sp, sp->obj, sp->vcl);
-		WSL_Flush(sp->wrk, 0);
-	} else {
-		VSL(SLT_Debug, sp->vsl_id,
-		    "thr %p STP_%s sp %p obj %p vcl %p",
-		    pthread_self(), state, sp, sp->obj, sp->vcl);
-	}
-}
-
-void
-CNT_Session(struct sess *sp)
-{
-	int done;
-	struct worker *w;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	w = sp->wrk;
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-
-	/*
-	 * Possible entrance states
-	 */
-	assert(
-	    sp->step == STP_FIRST ||
-	    sp->step == STP_START ||
-	    sp->step == STP_LOOKUP ||
-	    sp->step == STP_RECV);
-
-	AZ(w->do_stream);
-	AZ(w->is_gzip);
-	AZ(w->do_gzip);
-	AZ(w->is_gunzip);
-	AZ(w->do_gunzip);
-	AZ(w->do_esi);
-
-	/*
-	 * Whenever we come in from the acceptor or waiter, we need to set
-	 * blocking mode, but there is no point in setting it when we come from
-	 * ESI or when a parked sessions returns.
-	 * 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->step == STP_FIRST || sp->step == STP_START) &&
-	    VTCP_blocking(sp->fd)) {
-		if (errno == ECONNRESET)
-			SES_Close(sp, "remote closed");
-		else
-			SES_Close(sp, "error");
-		sp->step = STP_DONE;
-	}
-
-	/*
-	 * NB: Once done is set, we can no longer touch sp!
-	 */
-	for (done = 0; !done; ) {
-		assert(sp->wrk == w);
-		/*
-		 * This is a good place to be paranoid about the various
-		 * pointers still pointing to the things we expect.
-		 */
-		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-		CHECK_OBJ_ORNULL(sp->obj, OBJECT_MAGIC);
-		CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-		CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
-		WS_Assert(w->ws);
-
-		switch (sp->step) {
-#define STEP(l,u) \
-		    case STP_##u: \
-			if (cache_param->diag_bitmap & 0x01) \
-				cnt_diag(sp, #u); \
-			done = cnt_##l(sp); \
-		        break;
-#include "tbl/steps.h"
-#undef STEP
-		default:
-			WRONG("State engine misfire");
-		}
-		WS_Assert(w->ws);
-		CHECK_OBJ_ORNULL(w->nobjhead, OBJHEAD_MAGIC);
-	}
-	WSL_Flush(w, 0);
-	AZ(w->do_stream);
-	AZ(w->is_gzip);
-	AZ(w->do_gzip);
-	AZ(w->is_gunzip);
-	AZ(w->do_gunzip);
-	AZ(w->do_esi);
-#define ACCT(foo)	AZ(w->acct_tmp.foo);
-#include "tbl/acct_fields.h"
-#undef ACCT
-	assert(WRW_IsReleased(w));
-}
-
-/*
-DOT }
-*/
-
-/*--------------------------------------------------------------------
- * Debugging aids
- */
-
-static void
-cli_debug_xid(struct cli *cli, const char * const *av, void *priv)
-{
-	(void)priv;
-	if (av[2] != NULL)
-		xids = strtoul(av[2], NULL, 0);
-	VCLI_Out(cli, "XID is %u", xids);
-}
-
-/*
- * Default to seed=1, this is the only seed value POSIXl guarantees will
- * result in a reproducible random number sequence.
- */
-static void
-cli_debug_srandom(struct cli *cli, const char * const *av, void *priv)
-{
-	(void)priv;
-	unsigned seed = 1;
-
-	if (av[2] != NULL)
-		seed = strtoul(av[2], NULL, 0);
-	srandom(seed);
-	srand48(random());
-	VCLI_Out(cli, "Random(3) seeded with %lu", seed);
-}
-
-static struct cli_proto debug_cmds[] = {
-	{ "debug.xid", "debug.xid",
-		"\tExamine or set XID\n", 0, 1, "d", cli_debug_xid },
-	{ "debug.srandom", "debug.srandom",
-		"\tSeed the random(3) function\n", 0, 1, "d", cli_debug_srandom },
-	{ NULL }
-};
-
-/*--------------------------------------------------------------------
- *
- */
-
-void
-CNT_Init(void)
-{
-
-	srandomdev();
-	srand48(random());
-	xids = random();
-	CLI_AddFuncs(debug_cmds);
-}
-
-
diff --git a/bin/varnishd/cache_cli.c b/bin/varnishd/cache_cli.c
deleted file mode 100644
index f29f86a..0000000
--- a/bin/varnishd/cache_cli.c
+++ /dev/null
@@ -1,243 +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.
- *
- * Caching process CLI handling.
- *
- * We only have one CLI source, the stdin/stdout pipes from the manager
- * process, but we complicate things by having undocumented commands that
- * we do not want to show in a plain help, and by having commands that the
- * manager has already shown in help before asking us.
- */
-
-#include "config.h"
-
-#include <stddef.h>			// offsetof
-
-#include "cache.h"
-#include "common/heritage.h"
-
-#include "cache_backend.h"		// struct vbc
-#include "hash/hash_slinger.h"		// struct objhead
-#include "vcli.h"
-#include "vcli_common.h"
-#include "vcli_priv.h"
-#include "vcli_serve.h"
-
-pthread_t		cli_thread;
-static struct lock	cli_mtx;
-static int		add_check;
-static struct VCLS	*cls;
-
-/*
- * The CLI commandlist is split in three:
- *  - Commands we get from/share with the manager, we don't show these
- *	in help, as the manager already did that.
- *  - Cache process commands, show in help
- *  - Undocumented debug commands, show in undocumented "help -d"
- */
-
-/*--------------------------------------------------------------------
- * Add CLI functions to the appropriate command set
- */
-
-void
-CLI_AddFuncs(struct cli_proto *p)
-{
-
-	AZ(add_check);
-	Lck_Lock(&cli_mtx);
-	AZ(VCLS_AddFunc(cls, 0, p));
-	Lck_Unlock(&cli_mtx);
-}
-
-static void
-cli_cb_before(const struct cli *cli)
-{
-
-	ASSERT_CLI();
-	VSL(SLT_CLI, 0, "Rd %s", cli->cmd);
-	VCL_Poll();
-	VBE_Poll();
-	Lck_Lock(&cli_mtx);
-}
-
-static void
-cli_cb_after(const struct cli *cli)
-{
-
-	ASSERT_CLI();
-	Lck_Unlock(&cli_mtx);
-	VSL(SLT_CLI, 0, "Wr %03u %u %s",
-	    cli->result, VSB_len(cli->sb), VSB_data(cli->sb));
-}
-
-void
-CLI_Run(void)
-{
-	int i;
-
-	add_check = 1;
-
-	AN(VCLS_AddFd(cls, heritage.cli_in, heritage.cli_out, NULL, NULL));
-
-	do {
-		i = VCLS_Poll(cls, -1);
-	} while(i > 0);
-	VSL(SLT_CLI, 0, "EOF on CLI connection, worker stops");
-	VCA_Shutdown();
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-cli_debug_sizeof(struct cli *cli, const char * const *av, void *priv)
-{
-	(void)av;
-	(void)priv;
-
-#define SZOF(foo)       VCLI_Out(cli, \
-    "sizeof(%s) = %zd = 0x%zx\n", #foo, sizeof(foo), sizeof(foo))
-	SZOF(struct ws);
-	SZOF(struct http);
-	SZOF(struct http_conn);
-	SZOF(struct acct);
-	SZOF(struct worker);
-	SZOF(struct storage);
-	SZOF(struct object);
-	SZOF(struct objcore);
-	SZOF(struct objhead);
-	SZOF(struct sess);
-	SZOF(struct vbc);
-	SZOF(struct VSC_C_main);
-	SZOF(struct lock);
-#if 0
-#define OFOF(foo, bar)	{ foo __foo; VCLI_Out(cli, \
-    "%-30s = 0x%4zx @ 0x%4zx\n", \
-	#foo "." #bar, sizeof(__foo.bar), offsetof(foo, bar)); }
-#if 0
-	OFOF(struct objhead, magic);
-	OFOF(struct objhead, refcnt);
-	OFOF(struct objhead, mtx);
-	OFOF(struct objhead, objcs);
-	OFOF(struct objhead, digest);
-	OFOF(struct objhead, waitinglist);
-	OFOF(struct objhead, _u);
-#endif
-#if 0
-	OFOF(struct http, magic);
-	OFOF(struct http, logtag);
-	OFOF(struct http, ws);
-	OFOF(struct http, hd);
-	OFOF(struct http, hdf);
-	OFOF(struct http, shd);
-	OFOF(struct http, nhd);
-	OFOF(struct http, status);
-	OFOF(struct http, protover);
-	OFOF(struct http, conds);
-#endif
-#if 0
-	OFOF(struct storage, magic);
-	OFOF(struct storage, fd);
-	OFOF(struct storage, where);
-	OFOF(struct storage, list);
-	OFOF(struct storage, stevedore);
-	OFOF(struct storage, priv);
-	OFOF(struct storage, ptr);
-	OFOF(struct storage, len);
-	OFOF(struct storage, space);
-#endif
-#if 0
-	OFOF(struct object, magic);
-	OFOF(struct object, xid);
-	OFOF(struct object, objstore);
-	OFOF(struct object, objcore);
-	OFOF(struct object, ws_o);
-	OFOF(struct object, vary);
-	OFOF(struct object, hits);
-	OFOF(struct object, response);
-	OFOF(struct object, gziped);
-	OFOF(struct object, gzip_start);
-	OFOF(struct object, gzip_last);
-	OFOF(struct object, gzip_stop);
-	OFOF(struct object, len);
-	OFOF(struct object, age);
-	OFOF(struct object, entered);
-	OFOF(struct object, exp);
-	OFOF(struct object, last_modified);
-	OFOF(struct object, last_lru);
-	OFOF(struct object, http);
-	OFOF(struct object, store);
-	OFOF(struct object, esidata);
-	OFOF(struct object, last_use);
-#endif
-#undef OFOF
-#endif
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ccf_panic(struct cli *cli, const char * const *av, void *priv)
-{
-
-	(void)cli;
-	(void)av;
-	(void)priv;
-	assert(!strcmp("", "You asked for it"));
-}
-
-/*--------------------------------------------------------------------*/
-
-static struct cli_proto master_cmds[] = {
-	{ CLI_PING,		"i", VCLS_func_ping },
-	{ CLI_HELP,             "i", VCLS_func_help },
-	{ "debug.sizeof", "debug.sizeof",
-		"\tDump sizeof various data structures\n",
-		0, 0, "d", cli_debug_sizeof },
-	{ "debug.panic.worker", "debug.panic.worker",
-		"\tPanic the worker process.\n",
-		0, 0, "d", ccf_panic },
-	{ NULL }
-};
-
-/*--------------------------------------------------------------------
- * Initialize the CLI subsystem
- */
-
-void
-CLI_Init(void)
-{
-
-	Lck_New(&cli_mtx, lck_cli);
-	cli_thread = pthread_self();
-
-	cls = VCLS_New(cli_cb_before, cli_cb_after, cache_param->cli_buffer);
-	AN(cls);
-
-	CLI_AddFuncs(master_cmds);
-}
diff --git a/bin/varnishd/cache_dir.c b/bin/varnishd/cache_dir.c
deleted file mode 100644
index d4794f9..0000000
--- a/bin/varnishd/cache_dir.c
+++ /dev/null
@@ -1,121 +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.
- *
- * Handle backend connections and backend request structures.
- *
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vtcp.h"
-
-/* Close a connection ------------------------------------------------*/
-
-void
-VDI_CloseFd(struct worker *wrk)
-{
-	struct backend *bp;
-
-	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
-	assert(wrk->vbc->fd >= 0);
-
-	bp = wrk->vbc->backend;
-
-	WSL(wrk, SLT_BackendClose, wrk->vbc->vsl_id, "%s", bp->display_name);
-
-	/* Checkpoint log to flush all info related to this connection
-	   before the OS reuses the FD */
-	WSL_Flush(wrk, 0);
-
-	VTCP_close(&wrk->vbc->fd);
-	VBE_DropRefConn(bp);
-	wrk->vbc->backend = NULL;
-	VBE_ReleaseConn(wrk->vbc);
-	wrk->vbc = NULL;
-	wrk->do_close = 0;
-}
-
-/* Recycle a connection ----------------------------------------------*/
-
-void
-VDI_RecycleFd(struct worker *wrk)
-{
-	struct backend *bp;
-
-	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
-	assert(wrk->vbc->fd >= 0);
-	AZ(wrk->do_close);
-
-	bp = wrk->vbc->backend;
-
-	WSL(wrk, SLT_BackendReuse, wrk->vbc->vsl_id, "%s", bp->display_name);
-	/*
-	 * Flush the shmlog, so that another session reusing this backend
-	 * will log chronologically later than our use of it.
-	 */
-	WSL_Flush(wrk, 0);
-	Lck_Lock(&bp->mtx);
-	VSC_C_main->backend_recycle++;
-	VTAILQ_INSERT_HEAD(&bp->connlist, wrk->vbc, list);
-	wrk->vbc = NULL;
-	VBE_DropRefLocked(bp);
-}
-
-/* Get a connection --------------------------------------------------*/
-
-struct vbc *
-VDI_GetFd(const struct director *d, struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	if (d == NULL)
-		d = sp->director;
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	return (d->getfd(d, sp));
-}
-
-/* Check health ------------------------------------------------------
- *
- * The target is really an objhead pointer, but since it can not be
- * dereferenced during health-checks, we pass it as uintptr_t, which
- * hopefully will make people investigate, before mucking about with it.
- */
-
-int
-VDI_Healthy(const struct director *d, const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	return (d->healthy(d, sp));
-}
diff --git a/bin/varnishd/cache_dir_dns.c b/bin/varnishd/cache_dir_dns.c
deleted file mode 100644
index ae96dbb..0000000
--- a/bin/varnishd/cache_dir_dns.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*-
- * Copyright (c) 2009-2010 Varnish Software AS
- * All rights reserved.
- *
- * Author: Kristian Lyngstol <kristian at redpill-linpro.com>
- *
- * 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 <netinet/in.h>
-
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vrt.h"
-
-/*--------------------------------------------------------------------*/
-
-/* FIXME: Should eventually be a configurable variable. */
-#define VDI_DNS_MAX_CACHE		1024
-#define VDI_DNS_GROUP_MAX_BACKENDS	1024
-
-/* DNS Cache entry
- */
-struct vdi_dns_hostgroup {
-	unsigned			magic;
-#define VDI_DNSDIR_MAGIC		0x1bacab21
-	char				*hostname;
-	struct director			*hosts[VDI_DNS_GROUP_MAX_BACKENDS];
-	unsigned			nhosts;
-	unsigned			next_host; /* Next to use...*/
-	double				ttl;
-	VTAILQ_ENTRY(vdi_dns_hostgroup)	list;
-};
-
-struct vdi_dns {
-	unsigned			magic;
-#define VDI_DNS_MAGIC			0x1337a178
-	struct director			dir;
-	struct director			**hosts;
-	unsigned			nhosts;
-	VTAILQ_HEAD(_cachelist,vdi_dns_hostgroup)	cachelist;
-	unsigned			ncachelist;
-	pthread_rwlock_t		rwlock;
-	const char			*suffix;
-	double			ttl;
-};
-
-/* Compare an IPv4 backend to a IPv4 addr/len */
-static int
-vdi_dns_comp_addrinfo4(const struct backend *bp,
-		       const struct sockaddr_storage *addr,
-		       const socklen_t len)
-{
-	uint32_t u, p;
-	const struct sockaddr_in *bps = (const void *)bp->ipv4;
-	const struct sockaddr_in *bpd = (const void *)addr;
-
-	if (bp->ipv4len != len || len <= 0)
-		return (0);
-
-	u = bpd->sin_addr.s_addr;
-	p = bps->sin_addr.s_addr;
-
-	return (u == p);
-}
-
-/* Compare an IPv6 backend to a IPv6 addr/len */
-static int
-vdi_dns_comp_addrinfo6(const struct backend *bp,
-		       const struct sockaddr_storage *addr,
-		       const socklen_t len)
-{
-	const uint8_t *u, *p;
-	const struct sockaddr_in6 *bps = (const void *)bp->ipv6;
-	const struct sockaddr_in6 *bpd = (const void *)addr;
-
-	if (bp->ipv6len != len || len <= 0)
-		return (0);
-
-	u = bpd->sin6_addr.s6_addr;
-	p = bps->sin6_addr.s6_addr;
-
-	return (!memcmp(u, p, 16));
-}
-
-/* Check if a backends socket is the same as addr */
-static int
-vdi_dns_comp_addrinfo(const struct director *dir,
-		      const struct sockaddr_storage *addr,
-		      const socklen_t len)
-{
-	struct backend *bp;
-
-	bp = vdi_get_backend_if_simple(dir);
-	AN(bp);
-	if (addr->ss_family == PF_INET && bp->ipv4) {
-		return (vdi_dns_comp_addrinfo4(bp, addr, len));
-	} else if (addr->ss_family == PF_INET6 && bp->ipv6) {
-		return (vdi_dns_comp_addrinfo6(bp, addr, len));
-	}
-	return (0);
-}
-
-/* Pick a host from an existing hostgroup.
- * Balance on round-robin if multiple backends are available and only pick
- * healthy ones.
- */
-static struct director *
-vdi_dns_pick_host(const struct sess *sp, struct vdi_dns_hostgroup *group) {
-	int initial, i, nhosts, current;
-	if (group->nhosts == 0)
-		return (NULL); // In case of error.
-	if (group->next_host >= group->nhosts)
-		group->next_host = 0;
-
-	/* Pick a healthy backend */
-	initial = group->next_host;
-	nhosts = group->nhosts;
-	for (i=0; i < nhosts; i++) {
-		if (i + initial >= nhosts)
-			current = i + initial - nhosts;
-		else
-			current = i + initial;
-		if (VDI_Healthy(group->hosts[current], sp)) {
-			group->next_host = current+1;
-			return (group->hosts[current]);
-		}
-	}
-
-	return (NULL);
-}
-
-/* Remove an item from the dns cache.
- * If *group is NULL, the head is popped.
- * Remember locking.
- */
-static void
-vdi_dns_pop_cache(struct vdi_dns *vs,
-		  struct vdi_dns_hostgroup *group)
-{
-	if (group == NULL)
-		group = VTAILQ_LAST( &vs->cachelist, _cachelist );
-	assert(group != NULL);
-	free(group->hostname);
-	VTAILQ_REMOVE(&vs->cachelist, group, list);
-	FREE_OBJ(group);
-	vs->ncachelist--;
-}
-
-/* Dummy in case someone feels like optimizing it? meh...
- */
-static inline int
-vdi_dns_groupmatch(const struct vdi_dns_hostgroup *group, const char *hostname)
-{
-	return (!strcmp(group->hostname, hostname));
-}
-
-/* Search the cache for 'hostname' and put a backend-pointer as necessary,
- * return true for cache hit. This could still be a NULL backend if we did
- * a lookup earlier and didn't find a host (ie: cache failed too)
- *
- * if rwlock is true, the first timed out object found (if any) is popped
- * and freed.
- */
-static int
-vdi_dns_cache_has(const struct sess *sp,
-		  struct vdi_dns *vs,
-		  const char *hostname,
-		  struct director **backend,
-		  int rwlock)
-{
-	struct director *ret;
-	struct vdi_dns_hostgroup *hostgr;
-	struct vdi_dns_hostgroup *hostgr2;
-	VTAILQ_FOREACH_SAFE(hostgr, &vs->cachelist, list, hostgr2) {
-		CHECK_OBJ_NOTNULL(hostgr, VDI_DNSDIR_MAGIC);
-		if (hostgr->ttl <= sp->t_req) {
-			if (rwlock)
-				vdi_dns_pop_cache(vs, hostgr);
-			return (0);
-		}
-		if (vdi_dns_groupmatch(hostgr, hostname)) {
-			ret = (vdi_dns_pick_host(sp, hostgr));
-			*backend = ret;
-			if (*backend != NULL)
-				CHECK_OBJ_NOTNULL(*backend, DIRECTOR_MAGIC);
-			return (1);
-		}
-	}
-	return (0);
-}
-
-/* Add a newly cached item to the dns cache list.
- * (Sorry for the list_add/_add confusion...)
- */
-static void
-vdi_dns_cache_list_add(const struct sess *sp,
-		       struct vdi_dns *vs,
-		       struct vdi_dns_hostgroup *new)
-{
-	if (vs->ncachelist >= VDI_DNS_MAX_CACHE) {
-		VSC_C_main->dir_dns_cache_full++;
-		vdi_dns_pop_cache(vs, NULL);
-	}
-	CHECK_OBJ_NOTNULL(new, VDI_DNSDIR_MAGIC);
-	assert(new->hostname != 0);
-	new->ttl = sp->t_req + vs->ttl;
-	VTAILQ_INSERT_HEAD(&vs->cachelist, new, list);
-	vs->ncachelist++;
-}
-
-/* Add an item to the dns cache.
- * XXX: Might want to factor the getaddrinfo() out of the lock and do the
- * cache_has() afterwards to do multiple dns lookups in parallel...
- */
-static int
-vdi_dns_cache_add(const struct sess *sp,
-		  struct vdi_dns *vs,
-		  const char *hostname,
-		  struct director **backend)
-{
-	int error, i, host = 0;
-	struct addrinfo *res0, *res, hint;
-	struct vdi_dns_hostgroup *new;
-
-	/* Due to possible race while upgrading the lock, we have to
-	 * recheck if the result is already looked up. The overhead for
-	 * this is insignificant unless dns isn't cached properly (all
-	 * unique names or something equally troublesome).
-	 */
-
-	if (vdi_dns_cache_has(sp, vs, hostname, backend, 1))
-		return (1);
-
-	memset(&hint, 0, sizeof hint);
-	hint.ai_family = PF_UNSPEC;
-	hint.ai_socktype = SOCK_STREAM;
-
-	ALLOC_OBJ(new, VDI_DNSDIR_MAGIC);
-	XXXAN(new);
-
-	REPLACE(new->hostname, hostname);
-
-	error = getaddrinfo(hostname, "80", &hint, &res0);
-	VSC_C_main->dir_dns_lookups++;
-	if (error) {
-		vdi_dns_cache_list_add(sp, vs, new);
-		VSC_C_main->dir_dns_failed++;
-		return (0);
-	}
-
-	for (res = res0; res; res = res->ai_next) {
-		if (res->ai_family != PF_INET && res->ai_family != PF_INET6)
-			continue;
-
-		for (i = 0; i < vs->nhosts; i++) {
-			struct sockaddr_storage ss_hack;
-			memcpy(&ss_hack, res->ai_addr, res->ai_addrlen);
-			if (vdi_dns_comp_addrinfo(vs->hosts[i],
-			    &ss_hack, res->ai_addrlen)) {
-				new->hosts[host] = vs->hosts[i];
-				CHECK_OBJ_NOTNULL(new->hosts[host],
-				    DIRECTOR_MAGIC);
-				host++;
-			}
-		}
-	}
-	freeaddrinfo(res0);
-
-	new->nhosts = host;
-	vdi_dns_cache_list_add(sp, vs, new);
-	*backend = vdi_dns_pick_host(sp, new);
-	return (1);
-}
-
-/* Walk through the cached lookups looking for the relevant host, add one
- * if it isn't already cached.
- *
- * Returns a backend or NULL.
- */
-static struct director *
-vdi_dns_walk_cache(const struct sess *sp,
-		   struct vdi_dns *vs,
-		   const char *hostname)
-{
-	struct director *backend = NULL;
-	int ret;
-
-	AZ(pthread_rwlock_rdlock(&vs->rwlock));
-	ret = vdi_dns_cache_has(sp, vs, hostname, &backend, 0);
-	AZ(pthread_rwlock_unlock(&vs->rwlock));
-	if (!ret) {
-		/*
-		 * XXX: Isn't there a race here where another thread
-		 * XXX: could grab the lock and add it before we do ?
-		 * XXX: Should 'ret' be checked for that ?
-		 */
-		AZ(pthread_rwlock_wrlock(&vs->rwlock));
-		ret = vdi_dns_cache_add(sp, vs, hostname, &backend);
-		AZ(pthread_rwlock_unlock(&vs->rwlock));
-	} else
-		VSC_C_main->dir_dns_hit++;
-
-	/* Bank backend == cached a failure, so to speak */
-	if (backend != NULL)
-		CHECK_OBJ_NOTNULL(backend, DIRECTOR_MAGIC);
-	return (backend);
-}
-
-/* Parses the Host:-header and heads out to find a backend.
- */
-static struct director *
-vdi_dns_find_backend(const struct sess *sp, struct vdi_dns *vs)
-{
-	struct director *ret;
-	struct http *hp;
-	char *p, *q;
-	char hostname[NI_MAXHOST];
-
-	/* bereq is only present after recv et. al, otherwise use req (ie:
-	 * use req for health checks in vcl_recv and such).
-	 */
-	if (sp->wrk->bereq)
-		hp = sp->wrk->bereq;
-	else
-		hp = sp->http;
-
-
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	if (http_GetHdr(hp, H_Host, &p) == 0)
-		return (NULL);
-
-	q = strchr(p, ':');
-	if (q == NULL)
-		q = strchr(p, '\0');
-	AN(q);
-
-	bprintf(hostname, "%.*s%s", (int)(q - p), p,
-	    vs->suffix ? vs->suffix : "");
-
-	ret = vdi_dns_walk_cache(sp, vs, hostname);
-	return (ret);
-}
-
-static struct vbc *
-vdi_dns_getfd(const struct director *director, struct sess *sp)
-{
-	struct vdi_dns *vs;
-	struct director *dir;
-	struct vbc *vbe;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(director, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, director->priv, VDI_DNS_MAGIC);
-
-	dir = vdi_dns_find_backend(sp, vs);
-	if (!dir || !VDI_Healthy(dir, sp))
-		return (NULL);
-
-	vbe = VDI_GetFd(dir, sp);
-	return (vbe);
-}
-
-static unsigned
-vdi_dns_healthy(const struct director *dir, const struct sess *sp)
-{
-	/* XXX: Fooling -Werror for a bit until it's actually implemented.
-	 */
-	(void)dir;
-	(void)sp;
-	return (1);
-
-	/*
-	struct vdi_dns *vs;
-	struct director *dir;
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_DNS_MAGIC);
-
-	dir = vdi_dns_find_backend(sp, vs);
-
-	if (dir)
-		return (1);
-	return (0);
-	*/
-}
-
-static void
-vdi_dns_fini(const struct director *d)
-{
-	struct vdi_dns *vs;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_DNS_MAGIC);
-
-	free(vs->hosts);
-	free(vs->dir.vcl_name);
-	vs->dir.magic = 0;
-	/* FIXME: Free the cache */
-	AZ(pthread_rwlock_destroy(&vs->rwlock));
-	FREE_OBJ(vs);
-}
-
-void
-VRT_init_dir_dns(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	const struct vrt_dir_dns *t;
-	struct vdi_dns *vs;
-	const struct vrt_dir_dns_entry *te;
-	int i;
-
-	ASSERT_CLI();
-	(void)cli;
-	t = priv;
-	ALLOC_OBJ(vs, VDI_DNS_MAGIC);
-	XXXAN(vs);
-	vs->hosts = calloc(sizeof(struct director *), t->nmember);
-	XXXAN(vs->hosts);
-
-	vs->dir.magic = DIRECTOR_MAGIC;
-	vs->dir.priv = vs;
-	vs->dir.name = "dns";
-	REPLACE(vs->dir.vcl_name, t->name);
-	vs->dir.getfd = vdi_dns_getfd;
-	vs->dir.fini = vdi_dns_fini;
-	vs->dir.healthy = vdi_dns_healthy;
-
-	vs->suffix = t->suffix;
-	vs->ttl = t->ttl;
-
-	te = t->members;
-	for (i = 0; i < t->nmember; i++, te++)
-		vs->hosts[i] = bp[te->host];
-	vs->nhosts = t->nmember;
-	vs->ttl = t->ttl;
-	VTAILQ_INIT(&vs->cachelist);
-	AZ(pthread_rwlock_init(&vs->rwlock, NULL));
-	bp[idx] = &vs->dir;
-}
diff --git a/bin/varnishd/cache_dir_random.c b/bin/varnishd/cache_dir_random.c
deleted file mode 100644
index d6570ed..0000000
--- a/bin/varnishd/cache_dir_random.c
+++ /dev/null
@@ -1,285 +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.
- *
- * This code is shared between the random, client and hash directors, because
- * they share the same properties and most of the same selection logic.
- *
- * The random director picks a backend on random.
- *
- * The hash director picks based on the hash from vcl_hash{}
- *
- * The client director picks based on client identity or IP-address
- *
- * In all cases, the choice is by weight of the healthy subset of
- * configured backends.
- *
- * Failures to get a connection are retried, here all three policies
- * fall back to a deterministically random choice, by weight in the
- * healthy subset.
- *
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vend.h"
-#include "vrt.h"
-#include "vsha256.h"
-
-/*--------------------------------------------------------------------*/
-
-struct vdi_random_host {
-	struct director		*backend;
-	double			weight;
-};
-
-enum crit_e {c_random, c_hash, c_client};
-
-struct vdi_random {
-	unsigned		magic;
-#define VDI_RANDOM_MAGIC	0x3771ae23
-	struct director		dir;
-
-	enum crit_e		criteria;
-	unsigned		retries;
-	double			tot_weight;
-	struct vdi_random_host	*hosts;
-	unsigned		nhosts;
-};
-
-/*
- * Applies sha256 using the given context and input/length, and returns
- * a double in the range [0...1[ based on the hash.
- */
-static double
-vdi_random_sha(const char *input, ssize_t len)
-{
-	struct SHA256Context ctx;
-	uint8_t sign[SHA256_LEN];
-
-	AN(input);
-	SHA256_Init(&ctx);
-	SHA256_Update(&ctx, input, len);
-	SHA256_Final(sign, &ctx);
-	return (scalbn(vle32dec(sign), -32));
-}
-
-/*
- * Sets up the initial seed for picking a backend according to policy.
- */
-static double
-vdi_random_init_seed(const struct vdi_random *vs, const struct sess *sp)
-{
-	const char *p;
-	double retval;
-
-	switch (vs->criteria) {
-	case c_client:
-		if (sp->client_identity != NULL)
-			p = sp->client_identity;
-		else
-			p = sp->addr;
-		retval = vdi_random_sha(p, strlen(p));
-		break;
-	case c_hash:
-		AN(sp->digest);
-		retval = scalbn(vle32dec(sp->digest), -32);
-		break;
-	case c_random:
-	default:
-		retval = scalbn(random(), -31);
-		break;
-	}
-	return (retval);
-}
-
-/*
- * Find the healthy backend corresponding to the weight r [0...1[
- */
-static struct vbc *
-vdi_random_pick_one(struct sess *sp, const struct vdi_random *vs, double r)
-{
-	double w[vs->nhosts];
-	int i;
-	double s1;
-
-	assert(r >= 0.0 && r < 1.0);
-
-	memset(w, 0, sizeof w);
-	/* Sum up the weights of healty backends */
-	s1 = 0.0;
-	for (i = 0; i < vs->nhosts; i++) {
-		if (VDI_Healthy(vs->hosts[i].backend, sp))
-			w[i] = vs->hosts[i].weight;
-		s1 += w[i];
-	}
-
-	if (s1 == 0.0)
-		return (NULL);
-
-	r *= s1;
-	s1 = 0.0;
-	for (i = 0; i < vs->nhosts; i++)  {
-		s1 += w[i];
-		if (r < s1)
-			return(VDI_GetFd(vs->hosts[i].backend, sp));
-	}
-	return (NULL);
-}
-
-/*
- * Try the specified number of times to get a backend.
- * First one according to policy, after that, deterministically
- * random by rehashing the key.
- */
-static struct vbc *
-vdi_random_getfd(const struct director *d, struct sess *sp)
-{
-	int k;
-	struct vdi_random *vs;
-	double r;
-	struct vbc *vbe;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
-
-	r = vdi_random_init_seed(vs, sp);
-
-	for (k = 0; k < vs->retries; k++) {
-		vbe = vdi_random_pick_one(sp, vs, r);
-		if (vbe != NULL)
-			return (vbe);
-		r = vdi_random_sha((void *)&r, sizeof(r));
-	}
-	return (NULL);
-}
-
-/*
- * Healthy if just a single backend is...
- */
-static unsigned
-vdi_random_healthy(const struct director *d, const struct sess *sp)
-{
-	struct vdi_random *vs;
-	int i;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
-
-	for (i = 0; i < vs->nhosts; i++) {
-		if (VDI_Healthy(vs->hosts[i].backend, sp))
-			return (1);
-	}
-	return (0);
-}
-
-static void
-vdi_random_fini(const struct director *d)
-{
-	struct vdi_random *vs;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
-
-	free(vs->hosts);
-	free(vs->dir.vcl_name);
-	vs->dir.magic = 0;
-	FREE_OBJ(vs);
-}
-
-static void
-vrt_init(struct cli *cli, struct director **bp, int idx,
-    const void *priv, enum crit_e criteria)
-{
-	const struct vrt_dir_random *t;
-	struct vdi_random *vs;
-	const struct vrt_dir_random_entry *te;
-	struct vdi_random_host *vh;
-	int i;
-
-	ASSERT_CLI();
-	(void)cli;
-	t = priv;
-
-	ALLOC_OBJ(vs, VDI_RANDOM_MAGIC);
-	XXXAN(vs);
-	vs->hosts = calloc(sizeof *vh, t->nmember);
-	XXXAN(vs->hosts);
-
-	vs->dir.magic = DIRECTOR_MAGIC;
-	vs->dir.priv = vs;
-	vs->dir.name = "random";
-	REPLACE(vs->dir.vcl_name, t->name);
-	vs->dir.getfd = vdi_random_getfd;
-	vs->dir.fini = vdi_random_fini;
-	vs->dir.healthy = vdi_random_healthy;
-
-	vs->criteria = criteria;
-	vs->retries = t->retries;
-	if (vs->retries == 0)
-		vs->retries = t->nmember;
-	vh = vs->hosts;
-	te = t->members;
-	vs->tot_weight = 0.;
-	for (i = 0; i < t->nmember; i++, vh++, te++) {
-		assert(te->weight > 0.0);
-		vh->weight = te->weight;
-		vs->tot_weight += vh->weight;
-		vh->backend = bp[te->host];
-		AN(vh->backend);
-	}
-	vs->nhosts = t->nmember;
-	bp[idx] = &vs->dir;
-}
-
-void
-VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	vrt_init(cli, bp, idx, priv, c_random);
-}
-
-void
-VRT_init_dir_hash(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	vrt_init(cli, bp, idx, priv, c_hash);
-}
-
-void
-VRT_init_dir_client(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	vrt_init(cli, bp, idx, priv, c_client);
-}
diff --git a/bin/varnishd/cache_dir_round_robin.c b/bin/varnishd/cache_dir_round_robin.c
deleted file mode 100644
index 7d75473..0000000
--- a/bin/varnishd/cache_dir_round_robin.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*-
- * Copyright (c) 2008-2011 Varnish Software AS
- * All rights reserved.
- *
- * Author: Petter Knudsen <petter at linpro.no>
- *
- * 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 <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vrt.h"
-
-/*--------------------------------------------------------------------*/
-
-struct vdi_round_robin_host {
-	struct director			*backend;
-};
-
-enum mode_e { m_round_robin, m_fallback };
-
-struct vdi_round_robin {
-	unsigned			magic;
-#define VDI_ROUND_ROBIN_MAGIC		0x2114a178
-	struct director			dir;
-	enum mode_e			mode;
-	struct vdi_round_robin_host	*hosts;
-	unsigned			nhosts;
-	unsigned			next_host;
-};
-
-static struct vbc *
-vdi_round_robin_getfd(const struct director *d, struct sess *sp)
-{
-	int i;
-	struct vdi_round_robin *vs;
-	struct director *backend;
-	struct vbc *vbe;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
-
-	/*
-	 * In fallback mode we ignore the next_host and always grab the
-	 * first healthy backend we can find.
-	 */
-	for (i = 0; i < vs->nhosts; i++) {
-		if (vs->mode == m_round_robin) {
-			backend = vs->hosts[vs->next_host].backend;
-			vs->next_host = (vs->next_host + 1) % vs->nhosts;
-		} else /* m_fallback */ {
-			backend = vs->hosts[i].backend;
-		}
-		if (!VDI_Healthy(backend, sp))
-			continue;
-		vbe = VDI_GetFd(backend, sp);
-		if (vbe != NULL)
-			return (vbe);
-	}
-
-	return (NULL);
-}
-
-static unsigned
-vdi_round_robin_healthy(const struct director *d, const struct sess *sp)
-{
-	struct vdi_round_robin *vs;
-	struct director *backend;
-	int i;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
-
-	for (i = 0; i < vs->nhosts; i++) {
-		backend = vs->hosts[i].backend;
-		if (VDI_Healthy(backend, sp))
-			return (1);
-	}
-	return (0);
-}
-
-static void
-vdi_round_robin_fini(const struct director *d)
-{
-	struct vdi_round_robin *vs;
-
-	CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
-	CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
-
-	free(vs->hosts);
-	free(vs->dir.vcl_name);
-	vs->dir.magic = 0;
-	vs->next_host = 0;
-	FREE_OBJ(vs);
-}
-
-static void
-vrt_init_dir(struct cli *cli, struct director **bp, int idx,
-    const void *priv, enum mode_e mode)
-{
-	const struct vrt_dir_round_robin *t;
-	struct vdi_round_robin *vs;
-	const struct vrt_dir_round_robin_entry *te;
-	struct vdi_round_robin_host *vh;
-	int i;
-
-	ASSERT_CLI();
-	(void)cli;
-	t = priv;
-
-	ALLOC_OBJ(vs, VDI_ROUND_ROBIN_MAGIC);
-	XXXAN(vs);
-	vs->hosts = calloc(sizeof *vh, t->nmember);
-	XXXAN(vs->hosts);
-
-	vs->dir.magic = DIRECTOR_MAGIC;
-	vs->dir.priv = vs;
-	vs->dir.name = "round_robin";
-	REPLACE(vs->dir.vcl_name, t->name);
-	vs->dir.getfd = vdi_round_robin_getfd;
-	vs->dir.fini = vdi_round_robin_fini;
-	vs->dir.healthy = vdi_round_robin_healthy;
-
-	vs->mode = mode;
-	vh = vs->hosts;
-	te = t->members;
-	for (i = 0; i < t->nmember; i++, vh++, te++) {
-		vh->backend = bp[te->host];
-		AN (vh->backend);
-	}
-	vs->nhosts = t->nmember;
-	vs->next_host = 0;
-
-	bp[idx] = &vs->dir;
-}
-
-void
-VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	vrt_init_dir(cli, bp, idx, priv, m_round_robin);
-}
-
-void
-VRT_init_dir_fallback(struct cli *cli, struct director **bp, int idx,
-    const void *priv)
-{
-	vrt_init_dir(cli, bp, idx, priv, m_fallback);
-}
-
diff --git a/bin/varnishd/cache_esi.h b/bin/varnishd/cache_esi.h
deleted file mode 100644
index ff84d41..0000000
--- a/bin/varnishd/cache_esi.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*-
- * Copyright (c) 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.
- *
- */
-
-#define	VEC_GZ	(0x21)
-#define	VEC_V1	(0x40 + 1)
-#define	VEC_V2	(0x40 + 2)
-#define	VEC_V8	(0x40 + 8)
-#define	VEC_C1	(0x50 + 1)
-#define	VEC_C2	(0x50 + 2)
-#define	VEC_C8	(0x50 + 8)
-#define	VEC_S1	(0x60 + 1)
-#define	VEC_S2	(0x60 + 2)
-#define	VEC_S8	(0x60 + 8)
-#define	VEC_INCL	'I'
-
-typedef ssize_t vep_callback_t(struct worker *w, ssize_t l, enum vgz_flag flg);
-
-void VEP_Init(struct worker *w, vep_callback_t *cb);
-void VEP_Parse(const struct worker *w, const char *p, size_t l);
-struct vsb *VEP_Finish(struct worker *w);
-
-
diff --git a/bin/varnishd/cache_esi_deliver.c b/bin/varnishd/cache_esi_deliver.c
deleted file mode 100644
index 4051027..0000000
--- a/bin/varnishd/cache_esi_deliver.c
+++ /dev/null
@@ -1,570 +0,0 @@
-/*-
- * Copyright (c) 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.
- *
- * VED - Varnish Esi Delivery
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_esi.h"
-#include "vend.h"
-#include "vgz.h"
-
-/*--------------------------------------------------------------------*/
-
-static void
-ved_include(struct sess *sp, const char *src, const char *host)
-{
-	struct object *obj;
-	struct worker *w;
-	char *sp_ws_wm;
-	char *wrk_ws_wm;
-	unsigned sxid, res_mode;
-
-	w = sp->wrk;
-
-	if (sp->esi_level >= cache_param->max_esi_depth)
-		return;
-	sp->esi_level++;
-
-	(void)WRW_FlushRelease(w);
-
-	obj = sp->obj;
-	sp->obj = NULL;
-	res_mode = sp->wrk->res_mode;
-
-	/* Reset request to status before we started messing with it */
-	HTTP_Copy(sp->http, sp->http0);
-
-	/* Take a workspace snapshot */
-	sp_ws_wm = WS_Snapshot(sp->ws);
-	wrk_ws_wm = WS_Snapshot(w->ws);
-
-	http_SetH(sp->http, HTTP_HDR_URL, src);
-	if (host != NULL && *host != '\0')  {
-		http_Unset(sp->http, H_Host);
-		http_Unset(sp->http, H_If_Modified_Since);
-		http_SetHeader(w, sp->vsl_id, sp->http, host);
-	}
-	/*
-	 * XXX: We should decide if we should cache the director
-	 * XXX: or not (for session/backend coupling).  Until then
-	 * XXX: make sure we don't trip up the check in vcl_recv.
-	 */
-	sp->director = NULL;
-	sp->step = STP_RECV;
-	http_ForceGet(sp->http);
-
-	/* Don't do conditionals */
-	sp->http->conds = 0;
-	http_Unset(sp->http, H_If_Modified_Since);
-
-	/* Client content already taken care of */
-	http_Unset(sp->http, H_Content_Length);
-
-	sp->wrk->do_esi = 0;
-	sp->wrk->is_gzip = 0;
-	sp->wrk->is_gunzip = 0;
-	sp->wrk->do_gzip = 0;
-	sp->wrk->do_gunzip = 0;
-	sp->wrk->do_stream = 0;
-
-	sxid = sp->xid;
-	while (1) {
-		sp->wrk = w;
-		CNT_Session(sp);
-		if (sp->step == STP_DONE)
-			break;
-		AZ(sp->wrk);
-		WSL_Flush(w, 0);
-		DSL(0x20, SLT_Debug, sp->vsl_id, "loop waiting for ESI");
-		(void)usleep(10000);
-	}
-	sp->xid = sxid;
-	AN(sp->wrk);
-	assert(sp->step == STP_DONE);
-	sp->esi_level--;
-	sp->obj = obj;
-	sp->wrk->res_mode = res_mode;
-
-	/* Reset the workspace */
-	WS_Reset(sp->ws, sp_ws_wm);
-	WS_Reset(w->ws, wrk_ws_wm);
-
-	WRW_Reserve(sp->wrk, &sp->fd);
-	if (sp->wrk->res_mode & RES_CHUNKED)
-		WRW_Chunked(sp->wrk);
-}
-
-/*--------------------------------------------------------------------*/
-
-
-//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
-#define Debug(fmt, ...) /**/
-
-static ssize_t
-ved_decode_len(uint8_t **pp)
-{
-	uint8_t *p;
-	ssize_t l;
-
-	p = *pp;
-	switch (*p & 15) {
-	case 1:
-		l = p[1];
-		p += 2;
-		break;
-	case 2:
-		l = vbe16dec(p + 1);
-		p += 3;
-		break;
-	case 8:
-		l = vbe64dec(p + 1);
-		p += 9;
-		break;
-	default:
-		printf("Illegal Length %d %d\n", *p, (*p & 15));
-		INCOMPL();
-	}
-	*pp = p;
-	assert(l > 0);
-	return (l);
-}
-
-/*---------------------------------------------------------------------
- * If a gzip'ed ESI object includes a ungzip'ed object, we need to make
- * it looked like a gzip'ed data stream.  The official way to do so would
- * be to fire up libvgz and gzip it, but we don't, we fake it.
- *
- * First, we cannot know if it is ungzip'ed on purpose, the admin may
- * know something we don't.
- *
- * What do you mean "BS ?"
- *
- * All right then...
- *
- * The matter of the fact is that we simply will not fire up a gzip in
- * the output path because it costs too much memory and CPU, so we simply
- * wrap the data in very convenient "gzip copy-blocks" and send it down
- * the stream with a bit more overhead.
- */
-
-static void
-ved_pretend_gzip(const struct sess *sp, const uint8_t *p, ssize_t l)
-{
-	uint8_t buf1[5], buf2[5];
-	uint16_t lx;
-
-	lx = 65535;
-	buf1[0] = 0;
-	vle16enc(buf1 + 1, lx);
-	vle16enc(buf1 + 3, ~lx);
-
-	while (l > 0) {
-		if (l >= 65535) {
-			lx = 65535;
-			(void)WRW_Write(sp->wrk, buf1, sizeof buf1);
-		} else {
-			lx = (uint16_t)l;
-			buf2[0] = 0;
-			vle16enc(buf2 + 1, lx);
-			vle16enc(buf2 + 3, ~lx);
-			(void)WRW_Write(sp->wrk, buf2, sizeof buf2);
-		}
-		(void)WRW_Write(sp->wrk, p, lx);
-		sp->wrk->crc = crc32(sp->wrk->crc, p, lx);
-		sp->wrk->l_crc += lx;
-		l -= lx;
-		p += lx;
-	}
-	/* buf2 is local, have to flush */
-	(void)WRW_Flush(sp->wrk);
-}
-
-/*---------------------------------------------------------------------
- */
-
-static const uint8_t gzip_hdr[] = {
-	0x1f, 0x8b, 0x08,
-	0x00, 0x00, 0x00, 0x00,
-	0x00,
-	0x02, 0x03
-};
-
-void
-ESI_Deliver(struct sess *sp)
-{
-	struct storage *st;
-	uint8_t *p, *e, *q, *r;
-	unsigned off;
-	ssize_t l, l2, l_icrc = 0;
-	uint32_t icrc = 0;
-	uint8_t tailbuf[8 + 5];
-	int isgzip;
-	struct vgz *vgz = NULL;
-	char obuf[cache_param->gzip_stack_buffer];
-	ssize_t obufl = 0;
-	size_t dl;
-	const void *dp;
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	st = sp->obj->esidata;
-	AN(st);
-	assert(sizeof obuf >= 1024);
-
-	obuf[0] = 0;	/* For flexelint */
-
-	p = st->ptr;
-	e = st->ptr + st->len;
-
-	if (*p == VEC_GZ) {
-		isgzip = 1;
-		p++;
-	} else {
-		isgzip = 0;
-	}
-
-	if (sp->esi_level == 0) {
-		/*
-		 * Only the top level document gets to decide this.
-		 */
-		sp->wrk->gzip_resp = 0;
-		if (isgzip && !(sp->wrk->res_mode & RES_GUNZIP)) {
-			assert(sizeof gzip_hdr == 10);
-			/* Send out the gzip header */
-			(void)WRW_Write(sp->wrk, gzip_hdr, 10);
-			sp->wrk->l_crc = 0;
-			sp->wrk->gzip_resp = 1;
-			sp->wrk->crc = crc32(0L, Z_NULL, 0);
-		}
-	}
-
-	if (isgzip && !sp->wrk->gzip_resp) {
-		vgz = VGZ_NewUngzip(sp->wrk, "U D E");
-
-		/* Feed a gzip header to gunzip to make it happy */
-		VGZ_Ibuf(vgz, gzip_hdr, sizeof gzip_hdr);
-		VGZ_Obuf(vgz, obuf, sizeof obuf);
-		i = VGZ_Gunzip(vgz, &dp, &dl);
-		assert(i == VGZ_OK);
-		assert(VGZ_IbufEmpty(vgz));
-		assert(dl == 0);
-
-		obufl = 0;
-	}
-
-	st = VTAILQ_FIRST(&sp->obj->store);
-	off = 0;
-
-	while (p < e) {
-		switch (*p) {
-		case VEC_V1:
-		case VEC_V2:
-		case VEC_V8:
-			l = ved_decode_len(&p);
-			if (isgzip) {
-				assert(*p == VEC_C1 || *p == VEC_C2 ||
-				    *p == VEC_C8);
-				l_icrc = ved_decode_len(&p);
-				icrc = vbe32dec(p);
-				p += 4;
-				if (sp->wrk->gzip_resp) {
-					sp->wrk->crc = crc32_combine(
-					    sp->wrk->crc, icrc, l_icrc);
-					sp->wrk->l_crc += l_icrc;
-				}
-			}
-			/*
-			 * There is no guarantee that the 'l' bytes are all
-			 * in the same storage segment, so loop over storage
-			 * until we have processed them all.
-			 */
-			while (l > 0) {
-				l2 = l;
-				if (l2 > st->len - off)
-					l2 = st->len - off;
-				l -= l2;
-
-				if (sp->wrk->gzip_resp && isgzip) {
-					/*
-					 * We have a gzip'ed VEC and delivers
-					 * a gzip'ed ESI response.
-					 */
-					(void)WRW_Write(sp->wrk, st->ptr + off, l2);
-				} else if (sp->wrk->gzip_resp) {
-					/*
-					 * A gzip'ed ESI response, but the VEC
-					 * was not gzip'ed.
-					 */
-					ved_pretend_gzip(sp, st->ptr + off, l2);
-				} else if (isgzip) {
-					/*
-					 * A gzip'ed VEC, but ungzip'ed ESI
-					 * response
-					 */
-					AN(vgz);
-					i = VGZ_WrwGunzip(sp->wrk, vgz,
-						st->ptr + off, l2,
-						obuf, sizeof obuf, &obufl);
-					if (WRW_Error(sp->wrk)) {
-						SES_Close(sp, "remote closed");
-						p = e;
-						break;
-					}
-					assert (i == VGZ_OK || i == VGZ_END);
-				} else {
-					/*
-					 * Ungzip'ed VEC, ungzip'ed ESI response
-					 */
-					(void)WRW_Write(sp->wrk, st->ptr + off, l2);
-				}
-				off += l2;
-				if (off == st->len) {
-					st = VTAILQ_NEXT(st, list);
-					off = 0;
-				}
-			}
-			break;
-		case VEC_S1:
-		case VEC_S2:
-		case VEC_S8:
-			l = ved_decode_len(&p);
-			Debug("SKIP1(%d)\n", (int)l);
-			/*
-			 * There is no guarantee that the 'l' bytes are all
-			 * in the same storage segment, so loop over storage
-			 * until we have processed them all.
-			 */
-			while (l > 0) {
-				l2 = l;
-				if (l2 > st->len - off)
-					l2 = st->len - off;
-				l -= l2;
-				off += l2;
-				if (off == st->len) {
-					st = VTAILQ_NEXT(st, list);
-					off = 0;
-				}
-			}
-			break;
-		case VEC_INCL:
-			p++;
-			q = (void*)strchr((const char*)p, '\0');
-			AN(q);
-			q++;
-			r = (void*)strchr((const char*)q, '\0');
-			AN(r);
-			if (obufl > 0) {
-				(void)WRW_Write(sp->wrk, obuf, obufl);
-				obufl = 0;
-			}
-			if (WRW_Flush(sp->wrk)) {
-				SES_Close(sp, "remote closed");
-				p = e;
-				break;
-			}
-			Debug("INCL [%s][%s] BEGIN\n", q, p);
-			ved_include(sp, (const char*)q, (const char*)p);
-			Debug("INCL [%s][%s] END\n", q, p);
-			p = r + 1;
-			break;
-		default:
-			printf("XXXX 0x%02x [%s]\n", *p, p);
-			INCOMPL();
-		}
-	}
-	if (vgz != NULL) {
-		if (obufl > 0)
-			(void)WRW_Write(sp->wrk, obuf, obufl);
-		(void)VGZ_Destroy(&vgz, sp->vsl_id);
-	}
-	if (sp->wrk->gzip_resp && sp->esi_level == 0) {
-		/* Emit a gzip literal block with finish bit set */
-		tailbuf[0] = 0x01;
-		tailbuf[1] = 0x00;
-		tailbuf[2] = 0x00;
-		tailbuf[3] = 0xff;
-		tailbuf[4] = 0xff;
-
-		/* Emit CRC32 */
-		vle32enc(tailbuf + 5, sp->wrk->crc);
-
-		/* MOD(2^32) length */
-		vle32enc(tailbuf + 9, sp->wrk->l_crc);
-
-		(void)WRW_Write(sp->wrk, tailbuf, 13);
-	}
-	(void)WRW_Flush(sp->wrk);
-}
-
-/*---------------------------------------------------------------------
- * Include an object in a gzip'ed ESI object delivery
- */
-
-static uint8_t
-ved_deliver_byterange(const struct sess *sp, ssize_t low, ssize_t high)
-{
-	struct storage *st;
-	ssize_t l, lx;
-	u_char *p;
-
-//printf("BR %jd %jd\n", low, high);
-	lx = 0;
-	VTAILQ_FOREACH(st, &sp->obj->store, list) {
-		p = st->ptr;
-		l = st->len;
-//printf("[0-] %jd %jd\n", lx, lx + l);
-		if (lx + l < low) {
-			lx += l;
-			continue;
-		}
-		if (lx == high)
-			return (p[0]);
-		assert(lx < high);
-		if (lx < low) {
-			p += (low - lx);
-			l -= (low - lx);
-			lx = low;
-		}
-//printf("[1-] %jd %jd\n", lx, lx + l);
-		if (lx + l >= high)
-			l = high - lx;
-//printf("[2-] %jd %jd\n", lx, lx + l);
-		assert(lx >= low && lx + l <= high);
-		if (l != 0)
-			(void)WRW_Write(sp->wrk, p, l);
-		if (lx + st->len > high)
-			return(p[l]);
-		lx += st->len;
-	}
-	INCOMPL();
-}
-
-void
-ESI_DeliverChild(const struct sess *sp)
-{
-	struct storage *st;
-	struct object *obj;
-	ssize_t start, last, stop, lpad;
-	u_char *p, cc;
-	uint32_t icrc;
-	uint32_t ilen;
-	uint8_t *dbits;
-
-	if (!sp->obj->gziped) {
-		VTAILQ_FOREACH(st, &sp->obj->store, list)
-			ved_pretend_gzip(sp, st->ptr, st->len);
-		return;
-	}
-	/*
-	 * This is the interesting case: Deliver all the deflate
-	 * blocks, stripping the "LAST" bit of the last one and
-	 * padding it, as necessary, to a byte boundary.
-	 */
-
-	dbits = (void*)WS_Alloc(sp->wrk->ws, 8);
-	AN(dbits);
-	obj = sp->obj;
-	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
-	start = obj->gzip_start;
-	last = obj->gzip_last;
-	stop = obj->gzip_stop;
-	assert(start > 0 && start < obj->len * 8);
-	assert(last > 0 && last < obj->len * 8);
-	assert(stop > 0 && stop < obj->len * 8);
-	assert(last >= start);
-	assert(last < stop);
-
-	/* The start bit must be byte aligned. */
-	AZ(start & 7);
-
-	/*
-	 * XXX: optimize for the case where the 'last'
-	 * XXX: bit is in a empty copy block
-	 */
-	*dbits = ved_deliver_byterange(sp, start/8, last/8);
-	*dbits &= ~(1U << (last & 7));
-	(void)WRW_Write(sp->wrk, dbits, 1);
-	cc = ved_deliver_byterange(sp, 1 + last/8, stop/8);
-	switch((int)(stop & 7)) {
-	case 0: /* xxxxxxxx */
-		/* I think we have an off by one here, but that's OK */
-		lpad = 0;
-		break;
-	case 1: /* x000.... 00000000 00000000 11111111 11111111 */
-	case 3: /* xxx000.. 00000000 00000000 11111111 11111111 */
-	case 5: /* xxxxx000 00000000 00000000 11111111 11111111 */
-		dbits[1] = cc | 0x00;
-		dbits[2] = 0x00; dbits[3] = 0x00;
-	        dbits[4] = 0xff; dbits[5] = 0xff;
-		lpad = 5;
-		break;
-	case 2: /* xx010000 00000100 00000001 00000000 */
-		dbits[1] = cc | 0x08;
-		dbits[2] = 0x20;
-		dbits[3] = 0x80;
-		dbits[4] = 0x00;
-		lpad = 4;
-		break;
-	case 4: /* xxxx0100 00000001 00000000 */
-		dbits[1] = cc | 0x20;
-		dbits[2] = 0x80;
-		dbits[3] = 0x00;
-		lpad = 3;
-		break;
-	case 6: /* xxxxxx01 00000000 */
-		dbits[1] = cc | 0x80;
-		dbits[2] = 0x00;
-		lpad = 2;
-		break;
-	case 7:	/* xxxxxxx0 00...... 00000000 00000000 11111111 11111111 */
-		dbits[1] = cc | 0x00;
-		dbits[2] = 0x00;
-		dbits[3] = 0x00; dbits[4] = 0x00;
-	        dbits[5] = 0xff; dbits[6] = 0xff;
-		lpad = 6;
-		break;
-	default:
-		INCOMPL();
-	}
-	if (lpad > 0)
-		(void)WRW_Write(sp->wrk, dbits + 1, lpad);
-	st = VTAILQ_LAST(&sp->obj->store, storagehead);
-	assert(st->len > 8);
-
-	p = st->ptr + st->len - 8;
-	icrc = vle32dec(p);
-	ilen = vle32dec(p + 4);
-	sp->wrk->crc = crc32_combine(sp->wrk->crc, icrc, ilen);
-	sp->wrk->l_crc += ilen;
-}
diff --git a/bin/varnishd/cache_esi_fetch.c b/bin/varnishd/cache_esi_fetch.c
deleted file mode 100644
index 5ec8f6b..0000000
--- a/bin/varnishd/cache_esi_fetch.c
+++ /dev/null
@@ -1,405 +0,0 @@
-/*-
- * Copyright (c) 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.
- *
- * VEF Varnish Esi Fetching
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_esi.h"
-
-/*---------------------------------------------------------------------
- * Read some bytes.
- *
- * If the esi_syntax&8 bit is set, we read only a couple of bytes at
- * a time, in order to stress the parse/pending/callback code.
- */
-
-static ssize_t
-vef_read(struct worker *w, struct http_conn *htc, void *buf, ssize_t buflen,
-    ssize_t bytes)
-{
-	ssize_t d;
-
-	if (buflen < bytes)
-		bytes = buflen;
-	if (cache_param->esi_syntax & 0x8) {
-		d = (random() & 3) + 1;
-		if (d < bytes)
-			bytes = d;
-	}
-	return (HTC_Read(w, htc, buf, bytes));
-}
-
-/*---------------------------------------------------------------------
- * We receive a ungzip'ed object, and want to store it ungzip'ed.
- */
-
-static int
-vfp_esi_bytes_uu(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	ssize_t wl;
-	struct storage *st;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-
-	while (bytes > 0) {
-		st = FetchStorage(w, 0);
-		if (st == NULL)
-			return (-1);
-		wl = vef_read(w, htc,
-		    st->ptr + st->len, st->space - st->len, bytes);
-		if (wl <= 0)
-			return (wl);
-		VEP_Parse(w, (const char *)st->ptr + st->len, wl);
-		st->len += wl;
-		w->fetch_obj->len += wl;
-		bytes -= wl;
-	}
-	return (1);
-}
-
-/*---------------------------------------------------------------------
- * We receive a gzip'ed object, and want to store it ungzip'ed.
- */
-
-static int
-vfp_esi_bytes_gu(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	struct vgz *vg;
-	ssize_t wl;
-	uint8_t	ibuf[cache_param->gzip_stack_buffer];
-	int i;
-	size_t dl;
-	const void *dp;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vg = w->vgz_rx;
-
-	while (bytes > 0) {
-		if (VGZ_IbufEmpty(vg) && bytes > 0) {
-			wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
-			if (wl <= 0)
-				return (wl);
-			VGZ_Ibuf(vg, ibuf, wl);
-			bytes -= wl;
-		}
-		if (VGZ_ObufStorage(w, vg))
-			return(-1);
-		i = VGZ_Gunzip(vg, &dp, &dl);
-		xxxassert(i == VGZ_OK || i == VGZ_END);
-		VEP_Parse(w, dp, dl);
-		w->fetch_obj->len += dl;
-	}
-	return (1);
-}
-
-/*---------------------------------------------------------------------
- */
-
-struct vef_priv {
-	unsigned		magic;
-#define VEF_MAGIC		0xf104b51f
-	struct vgz		*vgz;
-
-	char			*bufp;
-	ssize_t			tot;
-	int			error;
-	char			pending[20];
-	ssize_t			npend;
-};
-
-/*---------------------------------------------------------------------
- * We receive a [un]gzip'ed object, and want to store it gzip'ed.
- */
-
-static ssize_t
-vfp_vep_callback(struct worker *w, ssize_t l, enum vgz_flag flg)
-{
-	struct vef_priv *vef;
-	size_t dl, px;
-	const void *dp;
-	int i;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vef = w->vef_priv;
-	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
-	assert(l >= 0);
-
-	if (vef->error) {
-		vef->tot += l;
-		return (vef->tot);
-	}
-
-	/*
-	 * l == 0 is valid when 'flg' calls for action, but in the
-	 * normal case we can just ignore a l==0 request.
-	 * (It would cause Z_BUF_ERROR anyway)
-	 */
-	if (l == 0 && flg == VGZ_NORMAL)
-		return (vef->tot);
-
-	do {
-		px = vef->npend;
-		if (l < px)
-			px = l;
-		if (px != 0) {
-			VGZ_Ibuf(vef->vgz, vef->pending, px);
-			l -= px;
-		} else {
-			VGZ_Ibuf(vef->vgz, vef->bufp, l);
-			vef->bufp += l;
-			l = 0;
-		}
-		do {
-			if (VGZ_ObufStorage(w, vef->vgz)) {
-				vef->error = ENOMEM;
-				vef->tot += l;
-				return (vef->tot);
-			}
-			i = VGZ_Gzip(vef->vgz, &dp, &dl, flg);
-			vef->tot += dl;
-			w->fetch_obj->len += dl;
-		} while (!VGZ_IbufEmpty(vef->vgz) ||
-		    (flg != VGZ_NORMAL && VGZ_ObufFull(vef->vgz)));
-		if (px != 0) {
-			memmove(vef->pending, vef->pending + px,
-			    vef->npend - px);
-			vef->npend -= px;
-		}
-	} while (l > 0);
-	if (flg == VGZ_FINISH)
-		assert(i == 1);			/* XXX */
-	else
-		assert(i == 0);			/* XXX */
-	return (vef->tot);
-}
-
-static int
-vfp_esi_bytes_ug(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	ssize_t wl;
-	char ibuf[cache_param->gzip_stack_buffer];
-	struct vef_priv *vef;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vef = w->vef_priv;
-	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
-
-	while (bytes > 0) {
-		wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
-		if (wl <= 0)
-			return (wl);
-		bytes -= wl;
-		vef->bufp = ibuf;
-		VEP_Parse(w, ibuf, wl);
-		assert(vef->bufp >= ibuf && vef->bufp <= ibuf + wl);
-		if (vef->error) {
-			errno = vef->error;
-			return (-1);
-		}
-		if (vef->bufp < ibuf + wl) {
-			wl = (ibuf + wl) - vef->bufp;
-			assert(wl + vef->npend < sizeof vef->pending);
-			memmove(vef->pending + vef->npend, vef->bufp, wl);
-			vef->npend += wl;
-		}
-	}
-	return (1);
-}
-
-/*---------------------------------------------------------------------
- * We receive a gzip'ed object, and want to store it gzip'ed.
- */
-
-static int
-vfp_esi_bytes_gg(struct worker *w, struct http_conn *htc, size_t bytes)
-{
-	ssize_t wl;
-	char ibuf[cache_param->gzip_stack_buffer];
-	char ibuf2[cache_param->gzip_stack_buffer];
-	struct vef_priv *vef;
-	size_t dl;
-	const void *dp;
-	int i;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vef = w->vef_priv;
-	CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
-	assert(sizeof ibuf >= 1024);
-	ibuf2[0] = 0; /* For Flexelint */
-
-	while (bytes > 0) {
-		wl = vef_read(w, htc, ibuf, sizeof ibuf, bytes);
-		if (wl <= 0)
-			return (wl);
-		bytes -= wl;
-
-		vef->bufp = ibuf;
-		VGZ_Ibuf(w->vgz_rx, ibuf, wl);
-		do {
-			VGZ_Obuf(w->vgz_rx, ibuf2, sizeof ibuf2);
-			i = VGZ_Gunzip(w->vgz_rx, &dp, &dl);
-			/* XXX: check i */
-			assert(i >= VGZ_OK);
-			vef->bufp = ibuf2;
-			if (dl > 0)
-				VEP_Parse(w, ibuf2, dl);
-			if (vef->error) {
-				errno = vef->error;
-				return (-1);
-			}
-			if (vef->bufp < ibuf2 + dl) {
-				dl = (ibuf2 + dl) - vef->bufp;
-				assert(dl + vef->npend < sizeof vef->pending);
-				memmove(vef->pending + vef->npend,
-				    vef->bufp, dl);
-				vef->npend += dl;
-			}
-		} while (!VGZ_IbufEmpty(w->vgz_rx));
-	}
-	return (1);
-}
-
-
-/*---------------------------------------------------------------------*/
-
-static void __match_proto__()
-vfp_esi_begin(struct worker *w, size_t estimate)
-{
-	struct vef_priv *vef;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-
-	AZ(w->vgz_rx);
-	if (w->is_gzip && w->do_gunzip) {
-		w->vgz_rx = VGZ_NewUngzip(w, "U F E");
-		VEP_Init(w, NULL);
-	} else if (w->is_gunzip && w->do_gzip) {
-		ALLOC_OBJ(vef, VEF_MAGIC);
-		AN(vef);
-		vef->vgz = VGZ_NewGzip(w, "G F E");
-		AZ(w->vef_priv);
-		w->vef_priv = vef;
-		VEP_Init(w, vfp_vep_callback);
-	} else if (w->is_gzip) {
-		w->vgz_rx = VGZ_NewUngzip(w, "U F E");
-		ALLOC_OBJ(vef, VEF_MAGIC);
-		AN(vef);
-		vef->vgz = VGZ_NewGzip(w, "G F E");
-		AZ(w->vef_priv);
-		w->vef_priv = vef;
-		VEP_Init(w, vfp_vep_callback);
-	} else {
-		AZ(w->vef_priv);
-		VEP_Init(w, NULL);
-	}
-
-	(void)estimate;
-	AN(w->vep);
-}
-
-static int __match_proto__()
-vfp_esi_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	int i;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	AZ(w->fetch_failed);
-	AN(w->vep);
-	assert(w->htc == htc);
-	if (w->is_gzip && w->do_gunzip)
-		i = vfp_esi_bytes_gu(w, htc, bytes);
-	else if (w->is_gunzip && w->do_gzip)
-		i = vfp_esi_bytes_ug(w, htc, bytes);
-	else if (w->is_gzip)
-		i = vfp_esi_bytes_gg(w, htc, bytes);
-	else
-		i = vfp_esi_bytes_uu(w, htc, bytes);
-	AN(w->vep);
-	return (i);
-}
-
-static int __match_proto__()
-vfp_esi_end(struct worker *w)
-{
-	struct vsb *vsb;
-	struct vef_priv *vef;
-	ssize_t l;
-	int retval;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	AN(w->vep);
-
-	retval = w->fetch_failed;
-
-	if (w->vgz_rx != NULL && VGZ_Destroy(&w->vgz_rx, -1) != VGZ_END)
-		retval = FetchError(w,
-		    "Gunzip+ESI Failed at the very end");
-
-	vsb = VEP_Finish(w);
-
-	if (vsb != NULL) {
-		if (!retval) {
-			l = VSB_len(vsb);
-			assert(l > 0);
-			/* XXX: This is a huge waste of storage... */
-			w->fetch_obj->esidata = STV_alloc(w, l);
-			if (w->fetch_obj->esidata != NULL) {
-				memcpy(w->fetch_obj->esidata->ptr,
-				    VSB_data(vsb), l);
-				w->fetch_obj->esidata->len = l;
-			} else {
-				retval = FetchError(w,
-				    "Could not allocate storage for esidata");
-			}
-		}
-		VSB_delete(vsb);
-	}
-
-	if (w->vef_priv != NULL) {
-		vef = w->vef_priv;
-		CHECK_OBJ_NOTNULL(vef, VEF_MAGIC);
-		w->vef_priv = NULL;
-		VGZ_UpdateObj(vef->vgz, w->fetch_obj);
-		if (VGZ_Destroy(&vef->vgz,  -1) != VGZ_END)
-			retval = FetchError(w,
-			    "ESI+Gzip Failed at the very end");
-		FREE_OBJ(vef);
-	}
-	return (retval);
-}
-
-struct vfp vfp_esi = {
-        .begin  =       vfp_esi_begin,
-        .bytes  =       vfp_esi_bytes,
-        .end    =       vfp_esi_end,
-};
diff --git a/bin/varnishd/cache_esi_parse.c b/bin/varnishd/cache_esi_parse.c
deleted file mode 100644
index 9e2b4f6..0000000
--- a/bin/varnishd/cache_esi_parse.c
+++ /dev/null
@@ -1,1189 +0,0 @@
-/*-
- * Copyright (c) 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.
- *
- * VEP Varnish Esi Parsing
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_esi.h"
-#include "vct.h"
-#include "vend.h"
-#include "vgz.h"
-
-//#define Debug(fmt, ...) printf(fmt, __VA_ARGS__)
-#define Debug(fmt, ...) /**/
-
-struct vep_state;
-
-enum dowhat {DO_ATTR, DO_TAG};
-typedef void dostuff_f(struct vep_state *, enum dowhat);
-
-struct vep_match {
-	const char	*match;
-	const char	* const *state;
-};
-
-enum vep_mark { VERBATIM = 0, SKIP };
-
-struct vep_state {
-	unsigned		magic;
-#define VEP_MAGIC		0x55cb9b82
-	struct vsb		*vsb;
-
-	struct worker		*wrk;
-	int			dogzip;
-	vep_callback_t		*cb;
-
-	/* Internal Counter for default call-back function */
-	ssize_t			cb_x;
-
-	/* parser state */
-	const char		*state;
-	unsigned		startup;
-	unsigned		esi_found;
-
-	unsigned		endtag;
-	unsigned		emptytag;
-	unsigned		canattr;
-
-	unsigned		remove;
-
-	ssize_t			o_wait;
-	ssize_t			o_pending;
-	ssize_t			o_total;
-	uint32_t		crc;
-	ssize_t			o_crc;
-	uint32_t		crcp;
-	ssize_t			o_last;
-
-const char		*hack_p;
-	const char		*ver_p;
-
-	const char		*until;
-	const char		*until_p;
-	const char		*until_s;
-
-	int			in_esi_tag;
-
-	const char		*esicmt;
-	const char		*esicmt_p;
-
-	struct vep_match	*attr;
-	struct vsb		*attr_vsb;
-	int			attr_delim;
-
-	struct vep_match	*match;
-	struct vep_match	*match_hit;
-
-	char			tag[10];
-	int			tag_i;
-
-	dostuff_f		*dostuff;
-
-	struct vsb		*include_src;
-
-	unsigned		nm_skip;
-	unsigned		nm_verbatim;
-	unsigned		nm_pending;
-	enum vep_mark		last_mark;
-};
-
-/*---------------------------------------------------------------------*/
-
-static const char * const VEP_START =		"[Start]";
-static const char * const VEP_TESTXML =		"[TestXml]";
-static const char * const VEP_NOTXML =		"[NotXml]";
-
-static const char * const VEP_NEXTTAG =		"[NxtTag]";
-static const char * const VEP_NOTMYTAG =	"[NotMyTag]";
-
-static const char * const VEP_STARTTAG =	"[StartTag]";
-static const char * const VEP_COMMENT =		"[Comment]";
-static const char * const VEP_CDATA =		"[CDATA]";
-static const char * const VEP_ESITAG =		"[ESITag]";
-
-static const char * const VEP_ESIREMOVE =	"[ESI:Remove]";
-static const char * const VEP_ESIINCLUDE =	"[ESI:Include]";
-static const char * const VEP_ESICOMMENT =	"[ESI:Comment]";
-static const char * const VEP_ESIBOGON =	"[ESI:Bogon]";
-
-static const char * const VEP_INTAG =		"[InTag]";
-static const char * const VEP_TAGERROR =	"[TagError]";
-
-static const char * const VEP_ATTR =		"[Attribute]";
-static const char * const VEP_SKIPATTR =	"[SkipAttribute]";
-static const char * const VEP_ATTRDELIM =	"[AttrDelim]";
-static const char * const VEP_ATTRGETVAL =	"[AttrGetValue]";
-static const char * const VEP_ATTRVAL =		"[AttrValue]";
-
-static const char * const VEP_UNTIL =		"[Until]";
-static const char * const VEP_MATCHBUF =	"[MatchBuf]";
-static const char * const VEP_MATCH =		"[Match]";
-
-/*---------------------------------------------------------------------*/
-
-static struct vep_match vep_match_starttag[] = {
-	{ "!--",	&VEP_COMMENT },
-	{ "esi:",	&VEP_ESITAG },
-	{ "![CDATA[",	&VEP_CDATA },
-	{ NULL,		&VEP_NOTMYTAG }
-};
-
-/*---------------------------------------------------------------------*/
-
-static struct vep_match vep_match_esi[] = {
-	{ "include",	&VEP_ESIINCLUDE },
-	{ "remove",	&VEP_ESIREMOVE },
-	{ "comment",	&VEP_ESICOMMENT },
-	{ NULL,		&VEP_ESIBOGON }
-};
-
-/*---------------------------------------------------------------------*/
-
-static struct vep_match vep_match_attr_include[] = {
-	{ "src=",	&VEP_ATTRGETVAL },
-	{ NULL,		&VEP_SKIPATTR }
-};
-
-/*--------------------------------------------------------------------
- * Report a parsing error
- */
-
-static void
-vep_error(const struct vep_state *vep, const char *p)
-{
-	intmax_t l;
-
-	VSC_C_main->esi_errors++;
-	l = (intmax_t)(vep->ver_p - vep->hack_p);
-	WSLB(vep->wrk, SLT_ESI_xmlerror, "ERR at %jd %s", l, p);
-
-}
-
-/*--------------------------------------------------------------------
- * Report a parsing warning
- */
-
-static void
-vep_warn(const struct vep_state *vep, const char *p)
-{
-	intmax_t l;
-
-	VSC_C_main->esi_warnings++;
-	l = (intmax_t)(vep->ver_p - vep->hack_p);
-	printf("WARNING at %jd %s\n", l, p);
-	WSLB(vep->wrk, SLT_ESI_xmlerror, "WARN at %jd %s", l, p);
-
-}
-
-/*---------------------------------------------------------------------
- * return match or NULL if more input needed.
- */
-
-static struct vep_match *
-vep_match(struct vep_state *vep, const char *b, const char *e)
-{
-	struct vep_match *vm;
-	const char *q, *r;
-	ssize_t l;
-
-	for (vm = vep->match; vm->match; vm++) {
-		r = b;
-		for (q = vm->match; *q && r < e; q++, r++)
-			if (*q != *r)
-				break;
-		if (*q != '\0' && r == e) {
-			if (b != vep->tag) {
-				l = e - b;
-				assert(l < sizeof vep->tag);
-				memmove(vep->tag, b, l);
-				vep->tag_i = l;
-			}
-			return (NULL);
-		}
-		if (*q == '\0')
-			return (vm);
-	}
-	return (vm);
-}
-
-/*---------------------------------------------------------------------
- *
- */
-
-static void
-vep_emit_len(const struct vep_state *vep, ssize_t l, int m8, int m16, int m64)
-{
-	uint8_t buf[9];
-
-	assert(l > 0);
-	if (l < 256) {
-		buf[0] = (uint8_t)m8;
-		buf[1] = (uint8_t)l;
-		assert((ssize_t)buf[1] == l);
-		VSB_bcat(vep->vsb, buf, 2);
-	} else if (l < 65536) {
-		buf[0] = (uint8_t)m16;
-		vbe16enc(buf + 1, (uint16_t)l);
-		assert((ssize_t)vbe16dec(buf + 1) == l);
-		VSB_bcat(vep->vsb, buf, 3);
-	} else {
-		buf[0] = (uint8_t)m64;
-		vbe64enc(buf + 1, l);
-		assert((ssize_t)vbe64dec(buf + 1) == l);
-		VSB_bcat(vep->vsb, buf, 9);
-	}
-}
-
-static void
-vep_emit_skip(const struct vep_state *vep, ssize_t l)
-{
-
-	if (cache_param->esi_syntax & 0x20) {
-		Debug("---> SKIP(%jd)\n", (intmax_t)l);
-	}
-	vep_emit_len(vep, l, VEC_S1, VEC_S2, VEC_S8);
-}
-
-static void
-vep_emit_verbatim(const struct vep_state *vep, ssize_t l, ssize_t l_crc)
-{
-	uint8_t buf[4];
-
-	if (cache_param->esi_syntax & 0x20) {
-		Debug("---> VERBATIM(%jd)\n", (intmax_t)l);
-	}
-	vep_emit_len(vep, l, VEC_V1, VEC_V2, VEC_V8);
-	if (vep->dogzip) {
-		vep_emit_len(vep, l_crc, VEC_C1, VEC_C2, VEC_C8);
-		vbe32enc(buf, vep->crc);
-		VSB_bcat(vep->vsb, buf, sizeof buf);
-	}
-}
-
-static void
-vep_emit_common(struct vep_state *vep, ssize_t l, enum vep_mark mark)
-{
-
-	assert(l > 0);
-	assert(mark == SKIP || mark == VERBATIM);
-	if (mark == SKIP)
-		vep_emit_skip(vep, l);
-	else
-		vep_emit_verbatim(vep, l, vep->o_crc);
-
-	vep->crc = crc32(0L, Z_NULL, 0);
-	vep->o_crc = 0;
-	vep->o_total += l;
-}
-
-/*---------------------------------------------------------------------
- *
- */
-
-static void
-vep_mark_common(struct vep_state *vep, const char *p, enum vep_mark mark)
-{
-	ssize_t l, lcb;
-
-	assert(mark == SKIP || mark == VERBATIM);
-
-	/* The NO-OP case, no data, no pending data & no change of mode */
-	if (vep->last_mark == mark && p == vep->ver_p && vep->o_pending == 0)
-		return;
-
-	/*
-	 * If we changed mode, emit whatever the opposite mode
-	 * assembled before the pending bytes.
-	 */
-
-	if (vep->last_mark != mark && (vep->o_wait > 0 || vep->startup)) {
-		lcb = vep->cb(vep->wrk, 0,
-		    mark == VERBATIM ? VGZ_RESET : VGZ_ALIGN);
-		if (lcb - vep->o_last > 0)
-			vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
-		vep->o_last = lcb;
-		vep->o_wait = 0;
-	}
-
-	/* Transfer pending bytes CRC into active mode CRC */
-	if (vep->o_pending) {
-		(void)vep->cb(vep->wrk, vep->o_pending, VGZ_NORMAL);
-		if (vep->o_crc == 0) {
-			vep->crc = vep->crcp;
-			vep->o_crc = vep->o_pending;
-		} else {
-			vep->crc = crc32_combine(vep->crc,
-			    vep->crcp, vep->o_pending);
-			vep->o_crc += vep->o_pending;
-		}
-		vep->crcp = crc32(0L, Z_NULL, 0);
-		vep->o_wait += vep->o_pending;
-		vep->o_pending = 0;
-	}
-
-	/* * Process this bit of input */
-	AN(vep->ver_p);
-	l = p - vep->ver_p;
-	assert(l >= 0);
-	vep->crc = crc32(vep->crc, (const void*)vep->ver_p, l);
-	vep->o_crc += l;
-	vep->ver_p = p;
-
-	vep->o_wait += l;
-	vep->last_mark = mark;
-	(void)vep->cb(vep->wrk, l, VGZ_NORMAL);
-}
-
-static void
-vep_mark_verbatim(struct vep_state *vep, const char *p)
-{
-
-	vep_mark_common(vep, p, VERBATIM);
-	vep->nm_verbatim++;
-}
-
-static void
-vep_mark_skip(struct vep_state *vep, const char *p)
-{
-
-	vep_mark_common(vep, p, SKIP);
-	vep->nm_skip++;
-}
-
-static void
-vep_mark_pending(struct vep_state *vep, const char *p)
-{
-	ssize_t l;
-
-	AN(vep->ver_p);
-	l = p - vep->ver_p;
-	assert(l > 0);
-	assert(l >= 0);
-	vep->crcp = crc32(vep->crcp, (const void *)vep->ver_p, l);
-	vep->ver_p = p;
-
-	vep->o_pending += l;
-	vep->nm_pending++;
-}
-
-/*---------------------------------------------------------------------
- */
-
-static void __match_proto__()
-vep_do_comment(struct vep_state *vep, enum dowhat what)
-{
-	Debug("DO_COMMENT(%d)\n", what);
-	assert(what == DO_TAG);
-	if (!vep->emptytag)
-		vep_error(vep, "ESI 1.0 <esi:comment> needs final '/'");
-}
-
-/*---------------------------------------------------------------------
- */
-
-static void __match_proto__()
-vep_do_remove(struct vep_state *vep, enum dowhat what)
-{
-	Debug("DO_REMOVE(%d, end %d empty %d remove %d)\n",
-	    what, vep->endtag, vep->emptytag, vep->remove);
-	assert(what == DO_TAG);
-	if (vep->emptytag) {
-		vep_error(vep,
-		    "ESI 1.0 <esi:remove/> not legal");
-	} else {
-		if (vep->remove && !vep->endtag)
-			vep_error(vep,
-			    "ESI 1.0 <esi:remove> already open");
-		else if (!vep->remove && vep->endtag)
-			vep_error(vep,
-			    "ESI 1.0 <esi:remove> not open");
-		else
-			vep->remove = !vep->endtag;
-	}
-}
-
-/*---------------------------------------------------------------------
- */
-
-static void __match_proto__()
-vep_do_include(struct vep_state *vep, enum dowhat what)
-{
-	char *p, *q, *h;
-	ssize_t l;
-	txt url;
-
-	Debug("DO_INCLUDE(%d)\n", what);
-	if (what == DO_ATTR) {
-		Debug("ATTR (%s) (%s)\n", vep->match_hit->match,
-			VSB_data(vep->attr_vsb));
-		if (vep->include_src != NULL) {
-			vep_error(vep,
-			    "ESI 1.0 <esi:include> "
-			    "has multiple src= attributes");
-			vep->state = VEP_TAGERROR;
-			VSB_delete(vep->attr_vsb);
-			VSB_delete(vep->include_src);
-			vep->attr_vsb = NULL;
-			vep->include_src = NULL;
-			return;
-		}
-		XXXAZ(vep->include_src);	/* multiple src= */
-		vep->include_src = vep->attr_vsb;
-		return;
-	}
-	assert(what == DO_TAG);
-	if (!vep->emptytag)
-		vep_warn(vep,
-		    "ESI 1.0 <esi:include> lacks final '/'");
-	if (vep->include_src == NULL) {
-		vep_error(vep,
-		    "ESI 1.0 <esi:include> lacks src attr");
-		return;
-	}
-
-	/*
-	 * Strictly speaking, we ought to spit out any piled up skip before
-	 * emitting the VEC for the include, but objectively that makes no
-	 * difference and robs us of a chance to collapse another skip into
-	 * this on so we don't do that.
-	 * However, we cannot tolerate any verbatim stuff piling up.
-	 * The mark_skip() before calling dostuff should have taken
-	 * care of that.  Make sure.
-	 */
-	assert(vep->o_wait == 0 || vep->last_mark == SKIP);
-	/* XXX: what if it contains NUL bytes ?? */
-	p = VSB_data(vep->include_src);
-	l = VSB_len(vep->include_src);
-	h = 0;
-
-	VSB_printf(vep->vsb, "%c", VEC_INCL);
-	if (l > 7 && !memcmp(p, "http://", 7)) {
-		h = p + 7;
-		p = strchr(h, '/');
-		AN(p);
-		Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
-		VSB_printf(vep->vsb, "Host: %.*s%c",
-		    (int)(p-h), h, 0);
-	} else if (*p == '/') {
-		VSB_printf(vep->vsb, "%c", 0);
-	} else {
-		VSB_printf(vep->vsb, "%c", 0);
-		url = vep->wrk->bereq->hd[HTTP_HDR_URL];
-		/* Look for the last / before a '?' */
-		h = NULL;
-		for (q = url.b; q < url.e && *q != '?'; q++)
-			if (*q == '/')
-				h = q;
-		if (h == NULL)
-			h = q + 1;
-
-		Debug("INCL:: [%.*s]/[%s]\n",
-		    (int)(h - url.b), url.b, p);
-		VSB_printf(vep->vsb, "%.*s/", (int)(h - url.b), url.b);
-	}
-	l -= (p - VSB_data(vep->include_src));
-	for (q = p; *q != '\0'; ) {
-		if (*q == '&') {
-#define R(w,f,r)							\
-			if (q + w <= p + l && !memcmp(q, f, w)) { \
-				VSB_printf(vep->vsb, "%c", r);	\
-				q += w;				\
-				continue;			\
-			}
-			R(6, "'", '\'');
-			R(6, """, '"');
-			R(4, "<", '<');
-			R(4, ">", '>');
-			R(5, "&", '&');
-		}
-		VSB_printf(vep->vsb, "%c", *q++);
-	}
-#undef R
-	VSB_printf(vep->vsb, "%c", 0);
-
-	VSB_delete(vep->include_src);
-	vep->include_src = NULL;
-}
-
-/*---------------------------------------------------------------------
- * Lex/Parse object for ESI instructions
- *
- * This function is called with the input object piecemal so do not
- * assume that we have more than one char available at at time, but
- * optimize for getting huge chunks.
- *
- * NB: At the bottom of this source-file, there is a dot-diagram matching
- * NB: the state-machine.  Please maintain it along with the code.
- */
-
-void
-VEP_Parse(const struct worker *w, const char *p, size_t l)
-{
-	struct vep_state *vep;
-	const char *e;
-	struct vep_match *vm;
-	int i;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vep = w->vep;
-	CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
-	assert(l > 0);
-
-	/* XXX: Really need to fix this */
-	if (vep->hack_p == NULL)
-		vep->hack_p = p;
-
-	vep->ver_p = p;
-
-	e = p + l;
-
-	while (p < e) {
-		AN(vep->state);
-		i = e - p;
-		if (i > 10)
-			i = 10;
-		Debug("EP %s %d (%.*s) [%.*s]\n",
-		    vep->state,
-		    vep->remove,
-		    vep->tag_i, vep->tag,
-		    i, p);
-		assert(p >= vep->ver_p);
-
-		/******************************************************
-		 * SECTION A
-		 */
-
-		if (vep->state == VEP_START) {
-			if (cache_param->esi_syntax & 0x1)
-				vep->state = VEP_NEXTTAG;
-			else
-				vep->state = VEP_TESTXML;
-		} else if (vep->state == VEP_TESTXML) {
-			/*
-			 * If the first non-whitespace char is different
-			 * from '<' we assume this is not XML.
-			 */
-			while (p < e && vct_islws(*p))
-				p++;
-			vep_mark_verbatim(vep, p);
-			if (p < e && *p == '<') {
-				p++;
-				vep->state = VEP_STARTTAG;
-			} else if (p < e) {
-				WSLB(vep->wrk, SLT_ESI_xmlerror,
-				    "No ESI processing, first char not '<'");
-				vep->state = VEP_NOTXML;
-			}
-		} else if (vep->state == VEP_NOTXML) {
-			/*
-			 * This is not recognized as XML, just skip thru
-			 * vfp_esi_end() will handle the rest
-			 */
-			p = e;
-			vep_mark_verbatim(vep, p);
-
-		/******************************************************
-		 * SECTION B
-		 */
-
-		} else if (vep->state == VEP_NOTMYTAG) {
-			if (cache_param->esi_syntax & 0x2) {
-				p++;
-				vep->state = VEP_NEXTTAG;
-			} else {
-				vep->tag_i = 0;
-				while (p < e) {
-					if (*p++ == '>') {
-						vep->state = VEP_NEXTTAG;
-						break;
-					}
-				}
-			}
-			if (p == e && !vep->remove)
-				vep_mark_verbatim(vep, p);
-		} else if (vep->state == VEP_NEXTTAG) {
-			/*
-			 * Hunt for start of next tag and keep an eye
-			 * out for end of EsiCmt if armed.
-			 */
-			vep->emptytag = 0;
-			vep->endtag = 0;
-			vep->attr = NULL;
-			vep->dostuff = NULL;
-			while (p < e && *p != '<') {
-				if (vep->esicmt_p == NULL) {
-					p++;
-					continue;
-				}
-				if (*p != *vep->esicmt_p) {
-					p++;
-					vep->esicmt_p = vep->esicmt;
-					continue;
-				}
-				if (!vep->remove &&
-				    vep->esicmt_p == vep->esicmt)
-					vep_mark_verbatim(vep, p);
-				p++;
-				if (*++vep->esicmt_p == '\0') {
-					vep->esi_found = 1;
-					vep->esicmt = NULL;
-					vep->esicmt_p = NULL;
-					/*
-					 * The end of the esicmt
-					 * should not be emitted.
-					 * But the stuff before should
-					 */
-					vep_mark_skip(vep, p);
-				}
-			}
-			if (p < e) {
-				if (!vep->remove)
-					vep_mark_verbatim(vep, p);
-				assert(*p == '<');
-				p++;
-				vep->state = VEP_STARTTAG;
-			} else if (vep->esicmt_p == vep->esicmt && !vep->remove)
-				vep_mark_verbatim(vep, p);
-
-		/******************************************************
-		 * SECTION C
-		 */
-
-		} else if (vep->state == VEP_STARTTAG) {
-			/*
-			 * Start of tag, set up match table
-			 */
-			if (p < e) {
-				if (*p == '/') {
-					vep->endtag = 1;
-					p++;
-				}
-				vep->match = vep_match_starttag;
-				vep->state = VEP_MATCH;
-			}
-		} else if (vep->state == VEP_COMMENT) {
-			/*
-			 * We are in a comment, find out if it is an
-			 * ESI comment or a regular comment
-			 */
-			if (vep->esicmt == NULL)
-				vep->esicmt_p = vep->esicmt = "esi";
-			while (p < e) {
-				if (*p != *vep->esicmt_p) {
-					vep->esicmt_p = vep->esicmt = NULL;
-					vep->until_p = vep->until = "-->";
-					vep->until_s = VEP_NEXTTAG;
-					vep->state = VEP_UNTIL;
-					vep_mark_verbatim(vep, p);
-					break;
-				}
-				p++;
-				if (*++vep->esicmt_p != '\0')
-					continue;
-				if (vep->remove)
-					vep_error(vep,
-					    "ESI 1.0 Nested <!--esi"
-					    " element in <esi:remove>");
-				vep->esicmt_p = vep->esicmt = "-->";
-				vep->state = VEP_NEXTTAG;
-				vep_mark_skip(vep, p);
-				break;
-			}
-		} else if (vep->state == VEP_CDATA) {
-			/*
-			 * Easy: just look for the end of CDATA
-			 */
-			vep->until_p = vep->until = "]]>";
-			vep->until_s = VEP_NEXTTAG;
-			vep->state = VEP_UNTIL;
-		} else if (vep->state == VEP_ESITAG) {
-			vep->in_esi_tag = 1;
-			vep->esi_found = 1;
-			vep_mark_skip(vep, p);
-			vep->match = vep_match_esi;
-			vep->state = VEP_MATCH;
-		} else if (vep->state == VEP_ESIINCLUDE) {
-			if (vep->remove) {
-				vep_error(vep,
-				    "ESI 1.0 <esi:include> element"
-				    " nested in <esi:remove>");
-				vep->state = VEP_TAGERROR;
-			} else if (vep->endtag) {
-				vep_error(vep,
-				    "ESI 1.0 </esi:include> illegal end-tag");
-				vep->state = VEP_TAGERROR;
-			} else {
-				vep->dostuff = vep_do_include;
-				vep->state = VEP_INTAG;
-				vep->attr = vep_match_attr_include;
-			}
-		} else if (vep->state == VEP_ESIREMOVE) {
-			vep->dostuff = vep_do_remove;
-			vep->state = VEP_INTAG;
-		} else if (vep->state == VEP_ESICOMMENT) {
-			if (vep->remove) {
-				vep_error(vep,
-				    "ESI 1.0 <esi:comment> element"
-				    " nested in <esi:remove>");
-				vep->state = VEP_TAGERROR;
-			} else if (vep->endtag) {
-				vep_error(vep,
-				    "ESI 1.0 </esi:comment> illegal end-tag");
-				vep->state = VEP_TAGERROR;
-			} else {
-				vep->dostuff = vep_do_comment;
-				vep->state = VEP_INTAG;
-			}
-		} else if (vep->state == VEP_ESIBOGON) {
-			vep_error(vep,
-			    "ESI 1.0 <esi:bogus> element");
-			vep->state = VEP_TAGERROR;
-
-		/******************************************************
-		 * SECTION D
-		 */
-
-		} else if (vep->state == VEP_INTAG) {
-			vep->tag_i = 0;
-			while (p < e && vct_islws(*p) && !vep->emptytag) {
-				p++;
-				vep->canattr = 1;
-			}
-			if (p < e && *p == '/' && !vep->emptytag) {
-				p++;
-				vep->emptytag = 1;
-				vep->canattr = 0;
-			}
-			if (p < e && *p == '>') {
-				p++;
-				AN(vep->dostuff);
-				vep_mark_skip(vep, p);
-				vep->dostuff(vep, DO_TAG);
-				vep->in_esi_tag = 0;
-				vep->state = VEP_NEXTTAG;
-			} else if (p < e && vep->emptytag) {
-				vep_error(vep,
-				    "XML 1.0 '>' does not follow '/' in tag");
-				vep->state = VEP_TAGERROR;
-			} else if (p < e && vep->canattr &&
-			    vct_isxmlnamestart(*p)) {
-				vep->state = VEP_ATTR;
-			} else if (p < e) {
-				vep_error(vep,
-				    "XML 1.0 Illegal attribute start char");
-				vep->state = VEP_TAGERROR;
-			}
-		} else if (vep->state == VEP_TAGERROR) {
-			while (p < e && *p != '>')
-				p++;
-			if (p < e) {
-				p++;
-				vep_mark_skip(vep, p);
-				vep->in_esi_tag = 0;
-				vep->state = VEP_NEXTTAG;
-			}
-
-		/******************************************************
-		 * SECTION E
-		 */
-
-		} else if (vep->state == VEP_ATTR) {
-			AZ(vep->attr_delim);
-			if (vep->attr == NULL) {
-				p++;
-				AZ(vep->attr_vsb);
-				vep->state = VEP_SKIPATTR;
-			} else {
-				vep->match = vep->attr;
-				vep->state = VEP_MATCH;
-			}
-		} else if (vep->state == VEP_SKIPATTR) {
-			while (p < e && vct_isxmlname(*p))
-				p++;
-			if (p < e && *p == '=') {
-				p++;
-				vep->state = VEP_ATTRDELIM;
-			} else if (p < e && *p == '>') {
-				vep->state = VEP_INTAG;
-			} else if (p < e && *p == '/') {
-				vep->state = VEP_INTAG;
-			} else if (p < e && vct_issp(*p)) {
-				vep->state = VEP_INTAG;
-			} else if (p < e) {
-				vep_error(vep,
-				    "XML 1.0 Illegal attr char");
-				vep->state = VEP_TAGERROR;
-			}
-		} else if (vep->state == VEP_ATTRGETVAL) {
-			vep->attr_vsb = VSB_new_auto();
-			vep->state = VEP_ATTRDELIM;
-		} else if (vep->state == VEP_ATTRDELIM) {
-			AZ(vep->attr_delim);
-			if (*p == '"' || *p == '\'') {
-				vep->attr_delim = *p++;
-				vep->state = VEP_ATTRVAL;
-			} else if (!vct_issp(*p)) {
-				vep->attr_delim = ' ';
-				vep->state = VEP_ATTRVAL;
-			} else {
-				vep_error(vep,
-				    "XML 1.0 Illegal attribute delimiter");
-				vep->state = VEP_TAGERROR;
-			}
-
-		} else if (vep->state == VEP_ATTRVAL) {
-			while (p < e && *p != '>' && *p != vep->attr_delim &&
-			   (vep->attr_delim != ' ' || !vct_issp(*p))) {
-				if (vep->attr_vsb != NULL)
-					VSB_bcat(vep->attr_vsb, p, 1);
-				p++;
-			}
-			if (p < e && *p == '>') {
-				vep_error(vep,
-				    "XML 1.0 Missing end attribute delimiter");
-				vep->state = VEP_TAGERROR;
-				vep->attr_delim = 0;
-				if (vep->attr_vsb != NULL) {
-					AZ(VSB_finish(vep->attr_vsb));
-					VSB_delete(vep->attr_vsb);
-					vep->attr_vsb = NULL;
-				}
-			} else if (p < e) {
-				vep->attr_delim = 0;
-				p++;
-				vep->state = VEP_INTAG;
-				if (vep->attr_vsb != NULL) {
-					AZ(VSB_finish(vep->attr_vsb));
-					AN(vep->dostuff);
-					vep->dostuff(vep, DO_ATTR);
-					vep->attr_vsb = NULL;
-				}
-			}
-
-		/******************************************************
-		 * Utility Section
-		 */
-
-		} else if (vep->state == VEP_MATCH) {
-			/*
-			 * Match against a table
-			 */
-			vm = vep_match(vep, p, e);
-			vep->match_hit = vm;
-			if (vm != NULL) {
-				if (vm->match != NULL)
-					p += strlen(vm->match);
-				vep->state = *vm->state;
-				vep->match = NULL;
-				vep->tag_i = 0;
-			} else {
-				memcpy(vep->tag, p, e - p);
-				vep->tag_i = e - p;
-				vep->state = VEP_MATCHBUF;
-				p = e;
-			}
-		} else if (vep->state == VEP_MATCHBUF) {
-			/*
-			 * Match against a table while split over input
-			 * sections.
-			 */
-			do {
-				if (*p == '>') {
-					for (vm = vep->match;
-					    vm->match != NULL; vm++)
-						continue;
-					AZ(vm->match);
-				} else {
-					vep->tag[vep->tag_i++] = *p++;
-					vm = vep_match(vep,
-					    vep->tag, vep->tag + vep->tag_i);
-					if (vm && vm->match == NULL) {
-						vep->tag_i--;
-						p--;
-					}
-				}
-			} while (vm == NULL && p < e);
-			vep->match_hit = vm;
-			if (vm == NULL) {
-				assert(p == e);
-			} else {
-				vep->state = *vm->state;
-				vep->match = NULL;
-			}
-		} else if (vep->state == VEP_UNTIL) {
-			/*
-			 * Skip until we see magic string
-			 */
-			while (p < e) {
-				if (*p++ != *vep->until_p++) {
-					vep->until_p = vep->until;
-				} else if (*vep->until_p == '\0') {
-					vep->state = vep->until_s;
-					break;
-				}
-			}
-			if (p == e && !vep->remove)
-				vep_mark_verbatim(vep, p);
-		} else {
-			Debug("*** Unknown state %s\n", vep->state);
-			INCOMPL();
-		}
-	}
-	/*
-	 * We must always mark up the storage we got, try to do so
-	 * in the most efficient way, in particular with respect to
-	 * minimizing and limiting use of pending.
-	 */
-	if (p == vep->ver_p)
-		;
-	else if (vep->in_esi_tag)
-		vep_mark_skip(vep, p);
-	else if (vep->remove)
-		vep_mark_skip(vep, p);
-	else
-		vep_mark_pending(vep, p);
-}
-
-/*---------------------------------------------------------------------
- */
-
-static ssize_t __match_proto__()
-vep_default_cb(struct worker *w, ssize_t l, enum vgz_flag flg)
-{
-
-	(void)flg;
-	AN(w->vep);
-	w->vep->cb_x += l;
-Debug("CB(%jd,%d) = %jd\n", (intmax_t)l, flg, (intmax_t)w->vep->cb_x);
-	return (w->vep->cb_x);
-}
-
-/*---------------------------------------------------------------------
- */
-
-void
-VEP_Init(struct worker *w, vep_callback_t *cb)
-{
-	struct vep_state *vep;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	AZ(w->vep);
-	w->vep = (void*)WS_Alloc(w->ws, sizeof *vep);
-	AN(w->vep);
-
-	vep = w->vep;
-	memset(vep, 0, sizeof *vep);
-	vep->magic = VEP_MAGIC;
-	vep->wrk = w;
-	vep->vsb = VSB_new_auto();
-	AN(vep->vsb);
-
-	if (cb != NULL) {
-		vep->dogzip = 1;
-		/* XXX */
-		VSB_printf(vep->vsb, "%c", VEC_GZ);
-		vep->cb = cb;
-	} else {
-		vep->cb = vep_default_cb;
-	}
-
-	vep->state = VEP_START;
-	vep->crc = crc32(0L, Z_NULL, 0);
-	vep->crcp = crc32(0L, Z_NULL, 0);
-
-	/*
-	 * We must force the GZIP header out as a SKIP string, otherwise
-	 * an object starting with <esi:include would have its GZIP header
-	 * appear after the included object (e000026.vtc)
-	 */
-	vep->startup = 1;
-	vep->ver_p = "";
-	vep->last_mark = SKIP;
-	vep_mark_common(vep, vep->ver_p, VERBATIM);
-	vep->startup = 0;
-}
-
-/*---------------------------------------------------------------------
- */
-
-struct vsb *
-VEP_Finish(struct worker *w)
-{
-	struct vep_state *vep;
-	ssize_t l, lcb;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	vep = w->vep;
-	CHECK_OBJ_NOTNULL(vep, VEP_MAGIC);
-
-	if (vep->o_pending)
-		vep_mark_common(vep, vep->ver_p, vep->last_mark);
-	if (vep->o_wait > 0) {
-		lcb = vep->cb(vep->wrk, 0, VGZ_ALIGN);
-		vep_emit_common(vep, lcb - vep->o_last, vep->last_mark);
-	}
-	(void)vep->cb(vep->wrk, 0, VGZ_FINISH);
-
-	w->vep = NULL;
-
-	AZ(VSB_finish(vep->vsb));
-	l = VSB_len(vep->vsb);
-	if (vep->esi_found && l > 0)
-		return (vep->vsb);
-	VSB_delete(vep->vsb);
-	return (NULL);
-}
-
-#if 0
-
-digraph xml {
-	rankdir="LR"
-	size="7,10"
-#################################################################
-# SECTION A
-#
-
-START		[shape=ellipse]
-TESTXML		[shape=ellipse]
-NOTXML		[shape=ellipse]
-NEXTTAGa	[shape=hexagon, label="NEXTTAG"]
-STARTTAGa	[shape=hexagon, label="STARTTAG"]
-START		-> TESTXML
-START		-> NEXTTAGa	[style=dotted, label="syntax:1"]
-TESTXML		-> TESTXML	[label="lws"]
-TESTXML		-> NOTXML
-TESTXML		-> STARTTAGa	[label="'<'"]
-
-#################################################################
-# SECTION B
-
-NOTMYTAG	[shape=ellipse]
-NEXTTAG		[shape=ellipse]
-NOTMYTAG	-> NEXTTAG	[style=dotted, label="syntax:2"]
-STARTTAGb	[shape=hexagon, label="STARTTAG"]
-NOTMYTAG	-> NEXTTAG	[label="'>'"]
-NOTMYTAG	-> NOTMYTAG	[label="*"]
-NEXTTAG		-> NEXTTAG	[label="'-->'"]
-NEXTTAG		-> NEXTTAG	[label="*"]
-NEXTTAG		-> STARTTAGb	[label="'<'"]
-
-#################################################################
-# SECTION C
-
-STARTTAG	[shape=ellipse]
-COMMENT		[shape=ellipse]
-CDATA		[shape=ellipse]
-ESITAG		[shape=ellipse]
-ESIETAG		[shape=ellipse]
-ESIINCLUDE	[shape=ellipse]
-ESIREMOVE	[shape=ellipse]
-ESICOMMENT	[shape=ellipse]
-ESIBOGON	[shape=ellipse]
-INTAGc		[shape=hexagon, label="INTAG"]
-NOTMYTAGc	[shape=hexagon, label="NOTMYTAG"]
-NEXTTAGc	[shape=hexagon, label="NEXTTAG"]
-TAGERRORc	[shape=hexagon, label="TAGERROR"]
-C1		[shape=circle,label=""]
-STARTTAG	-> COMMENT	[label="'<!--'"]
-STARTTAG	-> ESITAG	[label="'<esi'"]
-STARTTAG	-> CDATA	[label="'<![CDATA['"]
-STARTTAG	-> NOTMYTAGc	[label="'*'"]
-COMMENT		-> NEXTTAGc	[label="'esi'"]
-COMMENT		-> C1		[label="*"]
-C1		-> C1		[label="*"]
-C1		-> NEXTTAGc	[label="-->"]
-CDATA		-> CDATA	[label="*"]
-CDATA		-> NEXTTAGc	[label="]]>"]
-ESITAG		-> ESIINCLUDE	[label="'include'"]
-ESITAG		-> ESIREMOVE	[label="'remove'"]
-ESITAG		-> ESICOMMENT	[label="'comment'"]
-ESITAG		-> ESIBOGON	[label="*"]
-ESICOMMENT	-> INTAGc
-ESICOMMENT	-> TAGERRORc
-ESICOMMENT	-> TAGERRORc	[style=dotted, label="nested\nin\nremove"]
-ESIREMOVE	-> INTAGc
-ESIREMOVE	-> TAGERRORc
-ESIINCLUDE	-> INTAGc
-ESIINCLUDE	-> TAGERRORc
-ESIINCLUDE	-> TAGERRORc	[style=dotted, label="nested\nin\nremove"]
-ESIBOGON	-> TAGERRORc
-
-#################################################################
-# SECTION D
-
-INTAG		[shape=ellipse]
-TAGERROR	[shape=ellipse]
-NEXTTAGd	[shape=hexagon, label="NEXTTAG"]
-ATTRd		[shape=hexagon, label="ATTR"]
-D1		[shape=circle, label=""]
-D2		[shape=circle, label=""]
-INTAG		-> D1		[label="lws"]
-D1		-> D2		[label="/"]
-INTAG		-> D2		[label="/"]
-INTAG		-> NEXTTAGd	[label=">"]
-D1		-> NEXTTAGd	[label=">"]
-D2		-> NEXTTAGd	[label=">"]
-D1		-> ATTRd	[label="XMLstartchar"]
-D1		-> TAGERROR	[label="*"]
-D2		-> TAGERROR	[label="*"]
-TAGERROR	-> TAGERROR	[label="*"]
-TAGERROR	-> NEXTTAGd	[label="'>'"]
-
-#################################################################
-# SECTION E
-
-ATTR		[shape=ellipse]
-SKIPATTR	[shape=ellipse]
-ATTRGETVAL	[shape=ellipse]
-ATTRDELIM	[shape=ellipse]
-ATTRVAL		[shape=ellipse]
-TAGERRORe	[shape=hexagon, label="TAGERROR"]
-INTAGe		[shape=hexagon, label="INTAG"]
-ATTR		-> SKIPATTR	[label="*"]
-ATTR		-> ATTRGETVAL	[label="wanted attr"]
-SKIPATTR	-> SKIPATTR	[label="XMLname"]
-SKIPATTR	-> ATTRDELIM	[label="'='"]
-SKIPATTR	-> TAGERRORe	[label="*"]
-ATTRGETVAL	-> ATTRDELIM
-ATTRDELIM	-> ATTRVAL	[label="\""]
-ATTRDELIM	-> ATTRVAL	[label="\'"]
-ATTRDELIM	-> ATTRVAL	[label="*"]
-ATTRDELIM	-> TAGERRORe	[label="lws"]
-ATTRVAL		-> TAGERRORe	[label="'>'"]
-ATTRVAL		-> INTAGe	[label="delim"]
-ATTRVAL		-> ATTRVAL	[label="*"]
-
-}
-
-#endif
diff --git a/bin/varnishd/cache_expire.c b/bin/varnishd/cache_expire.c
deleted file mode 100644
index 23e3fc6..0000000
--- a/bin/varnishd/cache_expire.c
+++ /dev/null
@@ -1,490 +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.
- *
- * LRU and object timer handling.
- *
- * We have two data structures, a LRU-list and a binary heap for the timers
- * and two ways to kill objects: TTL-timeouts and LRU cleanups.
- *
- * Any object on the LRU is also on the binheap and vice versa.
- *
- * We hold a single object reference for both data structures.
- *
- * An attempted overview:
- *
- *	                        EXP_Ttl()      EXP_Grace()   EXP_Keep()
- *				   |                |            |
- *      entered                    v                v            |
- *         |                       +--------------->+            |
- *         v                       |      grace                  |
- *         +---------------------->+                             |
- *                  ttl            |                             v
- *                                 +---------------------------->+
- *                                     keep
- *
- */
-
-#include "config.h"
-
-#include <math.h>
-
-#include "cache.h"
-
-#include "binary_heap.h"
-#include "hash/hash_slinger.h"
-#include "vtim.h"
-
-static pthread_t exp_thread;
-static struct binheap *exp_heap;
-static struct lock exp_mtx;
-
-/*--------------------------------------------------------------------
- * struct exp manipulations
- *
- * The Get/Set functions encapsulate the mutual magic between the
- * fields in one single place.
- */
-
-void
-EXP_Clr(struct exp *e)
-{
-
-	e->ttl = -1;
-	e->grace = -1;
-	e->keep = -1;
-	e->age = 0;
-	e->entered = 0;
-}
-
-#define EXP_ACCESS(fld, low_val, extra)				\
-	double							\
-	EXP_Get_##fld(const struct exp *e)			\
-	{							\
-		return (e->fld > 0. ? e->fld : low_val);	\
-	}							\
-								\
-	void							\
-	EXP_Set_##fld(struct exp *e, double v)			\
-	{							\
-		if (v > 0.)					\
-			e->fld = v;				\
-		else {						\
-			e->fld = -1.;				\
-			extra;					\
-		}						\
-	}							\
-
-EXP_ACCESS(ttl, -1., (e->grace = e->keep = -1.))
-EXP_ACCESS(grace, 0., )
-EXP_ACCESS(keep, 0.,)
-
-/*--------------------------------------------------------------------
- * Calculate an objects effective keep, grace or ttl time, suitably
- * adjusted for defaults and by per-session limits.
- */
-
-static double
-EXP_Keep(const struct sess *sp, const struct object *o)
-{
-	double r;
-
-	r = (double)cache_param->default_keep;
-	if (o->exp.keep > 0.)
-		r = o->exp.keep;
-	if (sp != NULL && sp->exp.keep > 0. && sp->exp.keep < r)
-		r = sp->exp.keep;
-	return (EXP_Ttl(sp, o) + r);
-}
-
-double
-EXP_Grace(const struct sess *sp, const struct object *o)
-{
-	double r;
-
-	r = (double)cache_param->default_grace;
-	if (o->exp.grace >= 0.)
-		r = o->exp.grace;
-	if (sp != NULL && sp->exp.grace > 0. && sp->exp.grace < r)
-		r = sp->exp.grace;
-	return (EXP_Ttl(sp, o) + r);
-}
-
-double
-EXP_Ttl(const struct sess *sp, const struct object *o)
-{
-	double r;
-
-	r = o->exp.ttl;
-	if (sp != NULL && sp->exp.ttl > 0. && sp->exp.ttl < r)
-		r = sp->exp.ttl;
-	return (o->exp.entered + r);
-}
-
-/*--------------------------------------------------------------------
- * When & why does the timer fire for this object ?
- */
-
-static int
-update_object_when(const struct object *o)
-{
-	struct objcore *oc;
-	double when, w2;
-
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	oc = o->objcore;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	Lck_AssertHeld(&exp_mtx);
-
-	when = EXP_Keep(NULL, o);
-	w2 = EXP_Grace(NULL, o);
-	if (w2 > when)
-		when = w2;
-	assert(!isnan(when));
-	if (when == oc->timer_when)
-		return (0);
-	oc->timer_when = when;
-	return (1);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-exp_insert(struct objcore *oc, struct lru *lru)
-{
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
-
-	Lck_AssertHeld(&lru->mtx);
-	Lck_AssertHeld(&exp_mtx);
-	assert(oc->timer_idx == BINHEAP_NOIDX);
-	binheap_insert(exp_heap, oc);
-	assert(oc->timer_idx != BINHEAP_NOIDX);
-	VTAILQ_INSERT_TAIL(&lru->lru_head, oc, lru_list);
-}
-
-/*--------------------------------------------------------------------
- * Object has been added to cache, record in lru & binheap.
- *
- * The objcore comes with a reference, which we inherit.
- */
-
-void
-EXP_Inject(struct objcore *oc, struct lru *lru, double when)
-{
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
-
-	Lck_Lock(&lru->mtx);
-	Lck_Lock(&exp_mtx);
-	oc->timer_when = when;
-	exp_insert(oc, lru);
-	Lck_Unlock(&exp_mtx);
-	Lck_Unlock(&lru->mtx);
-}
-
-/*--------------------------------------------------------------------
- * Object has been added to cache, record in lru & binheap.
- *
- * We grab a reference to the object, which will keep it around until
- * we decide its time to let it go.
- */
-
-void
-EXP_Insert(struct object *o)
-{
-	struct objcore *oc;
-	struct lru *lru;
-
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	oc = o->objcore;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AssertObjBusy(o);
-	HSH_Ref(oc);
-
-	assert(o->exp.entered != 0 && !isnan(o->exp.entered));
-	o->last_lru = o->exp.entered;
-
-	lru = oc_getlru(oc);
-	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
-	Lck_Lock(&lru->mtx);
-	Lck_Lock(&exp_mtx);
-	(void)update_object_when(o);
-	exp_insert(oc, lru);
-	Lck_Unlock(&exp_mtx);
-	Lck_Unlock(&lru->mtx);
-	oc_updatemeta(oc);
-}
-
-/*--------------------------------------------------------------------
- * Object was used, move to tail of LRU list.
- *
- * To avoid the exp_mtx becoming a hotspot, we only attempt to move
- * objects if they have not been moved recently and if the lock is available.
- * This optimization obviously leaves the LRU list imperfectly sorted.
- */
-
-int
-EXP_Touch(struct objcore *oc)
-{
-	struct lru *lru;
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-
-	/*
-	 * For -spersistent we don't move objects on the lru list.  Each
-	 * segment has its own LRU list, and the order on it is not material
-	 * for anything.  The code below would move the objects to the
-	 * LRU list of the currently open segment, which would prevent
-	 * the cleaner from doing its job.
-	 */
-	if (oc->flags & OC_F_LRUDONTMOVE)
-		return (0);
-
-	lru = oc_getlru(oc);
-	CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
-
-	/*
-	 * We only need the LRU lock here.  The locking order is LRU->EXP
-	 * so we can trust the content of the oc->timer_idx without the
-	 * EXP lock.   Since each lru list has its own lock, this should
-	 * reduce contention a fair bit
-	 */
-	if (Lck_Trylock(&lru->mtx))
-		return (0);
-
-	if (oc->timer_idx != BINHEAP_NOIDX) {
-		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
-		VTAILQ_INSERT_TAIL(&lru->lru_head, oc, lru_list);
-		VSC_C_main->n_lru_moved++;
-	}
-	Lck_Unlock(&lru->mtx);
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * We have changed one or more of the object timers, shuffle it
- * accordingly in the binheap
- *
- * The VCL code can send us here on a non-cached object, just return.
- *
- * XXX: special case check for ttl = 0 ?
- */
-
-void
-EXP_Rearm(const struct object *o)
-{
-	struct objcore *oc;
-	struct lru *lru;
-
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	oc = o->objcore;
-	if (oc == NULL)
-		return;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	lru = oc_getlru(oc);
-	Lck_Lock(&lru->mtx);
-	Lck_Lock(&exp_mtx);
-	/*
-	 * The hang-man might have this object of the binheap while
-	 * tending to a timer.  If so, we do not muck with it here.
-	 */
-	if (oc->timer_idx != BINHEAP_NOIDX && update_object_when(o)) {
-		assert(oc->timer_idx != BINHEAP_NOIDX);
-		binheap_reorder(exp_heap, oc->timer_idx);
-		assert(oc->timer_idx != BINHEAP_NOIDX);
-	}
-	Lck_Unlock(&exp_mtx);
-	Lck_Unlock(&lru->mtx);
-	oc_updatemeta(oc);
-}
-
-/*--------------------------------------------------------------------
- * This thread monitors the root of the binary heap and whenever an
- * object expires, accounting also for graceability, it is killed.
- */
-
-static void * __match_proto__(void *start_routine(void *))
-exp_timer(struct sess *sp, void *priv)
-{
-	struct objcore *oc;
-	struct lru *lru;
-	double t;
-	struct object *o;
-
-	(void)priv;
-	t = VTIM_real();
-	oc = NULL;
-	while (1) {
-		if (oc == NULL) {
-			WSL_Flush(sp->wrk, 0);
-			WRK_SumStat(sp->wrk);
-			VTIM_sleep(cache_param->expiry_sleep);
-			t = VTIM_real();
-		}
-
-		Lck_Lock(&exp_mtx);
-		oc = binheap_root(exp_heap);
-		if (oc == NULL) {
-			Lck_Unlock(&exp_mtx);
-			continue;
-		}
-		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-
-		/*
-		 * We may have expired so many objects that our timestamp
-		 * got out of date, refresh it and check again.
-		 */
-		if (oc->timer_when > t)
-			t = VTIM_real();
-		if (oc->timer_when > t) {
-			Lck_Unlock(&exp_mtx);
-			oc = NULL;
-			continue;
-		}
-
-		/*
-		 * It's time...
-		 * Technically we should drop the exp_mtx, get the lru->mtx
-		 * get the exp_mtx again and then check that the oc is still
-		 * on the binheap.  We take the shorter route and try to
-		 * get the lru->mtx and punt if we fail.
-		 */
-
-		lru = oc_getlru(oc);
-		CHECK_OBJ_NOTNULL(lru, LRU_MAGIC);
-		if (Lck_Trylock(&lru->mtx)) {
-			Lck_Unlock(&exp_mtx);
-			oc = NULL;
-			continue;
-		}
-
-		/* Remove from binheap */
-		assert(oc->timer_idx != BINHEAP_NOIDX);
-		binheap_delete(exp_heap, oc->timer_idx);
-		assert(oc->timer_idx == BINHEAP_NOIDX);
-
-		/* And from LRU */
-		lru = oc_getlru(oc);
-		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
-
-		Lck_Unlock(&exp_mtx);
-		Lck_Unlock(&lru->mtx);
-
-		VSC_C_main->n_expired++;
-
-		CHECK_OBJ_NOTNULL(oc->objhead, OBJHEAD_MAGIC);
-		o = oc_getobj(sp->wrk, oc);
-		WSL(sp->wrk, SLT_ExpKill, 0, "%u %.0f",
-		    o->xid, EXP_Ttl(NULL, o) - t);
-		(void)HSH_Deref(sp->wrk, oc, NULL);
-	}
-	NEEDLESS_RETURN(NULL);
-}
-
-/*--------------------------------------------------------------------
- * Attempt to make space by nuking the oldest object on the LRU list
- * which isn't in use.
- * Returns: 1: did, 0: didn't, -1: can't
- */
-
-int
-EXP_NukeOne(struct worker *w, struct lru *lru)
-{
-	struct objcore *oc;
-	struct object *o;
-
-	/* Find the first currently unused object on the LRU.  */
-	Lck_Lock(&lru->mtx);
-	Lck_Lock(&exp_mtx);
-	VTAILQ_FOREACH(oc, &lru->lru_head, lru_list) {
-		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-		assert (oc->timer_idx != BINHEAP_NOIDX);
-		/*
-		 * It wont release any space if we cannot release the last
-		 * reference, besides, if somebody else has a reference,
-		 * it's a bad idea to nuke this object anyway.
-		 */
-		if (oc->refcnt == 1)
-			break;
-	}
-	if (oc != NULL) {
-		VTAILQ_REMOVE(&lru->lru_head, oc, lru_list);
-		binheap_delete(exp_heap, oc->timer_idx);
-		assert(oc->timer_idx == BINHEAP_NOIDX);
-		VSC_C_main->n_lru_nuked++;
-	}
-	Lck_Unlock(&exp_mtx);
-	Lck_Unlock(&lru->mtx);
-
-	if (oc == NULL)
-		return (-1);
-
-	/* XXX: bad idea for -spersistent */
-	o = oc_getobj(w, oc);
-	WSL(w, SLT_ExpKill, 0, "%u LRU", o->xid);
-	(void)HSH_Deref(w, NULL, &o);
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * BinHeap helper functions for objcore.
- */
-
-static int
-object_cmp(void *priv, void *a, void *b)
-{
-	struct objcore *aa, *bb;
-
-	(void)priv;
-	CAST_OBJ_NOTNULL(aa, a, OBJCORE_MAGIC);
-	CAST_OBJ_NOTNULL(bb, b, OBJCORE_MAGIC);
-	return (aa->timer_when < bb->timer_when);
-}
-
-static void
-object_update(void *priv, void *p, unsigned u)
-{
-	struct objcore *oc;
-
-	(void)priv;
-	CAST_OBJ_NOTNULL(oc, p, OBJCORE_MAGIC);
-	oc->timer_idx = u;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-EXP_Init(void)
-{
-
-	Lck_New(&exp_mtx, lck_exp);
-	exp_heap = binheap_new(NULL, object_cmp, object_update);
-	XXXAN(exp_heap);
-	WRK_BgThread(&exp_thread, "cache-timeout", exp_timer, NULL);
-}
diff --git a/bin/varnishd/cache_fetch.c b/bin/varnishd/cache_fetch.c
deleted file mode 100644
index a678dcc..0000000
--- a/bin/varnishd/cache_fetch.c
+++ /dev/null
@@ -1,645 +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.
- */
-
-#include "config.h"
-
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "vcli_priv.h"
-#include "vct.h"
-#include "vtcp.h"
-
-static unsigned fetchfrag;
-
-/*--------------------------------------------------------------------
- * We want to issue the first error we encounter on fetching and
- * supress the rest.  This function does that.
- *
- * Other code is allowed to look at w->fetch_failed to bail out
- *
- * For convenience, always return -1
- */
-
-int
-FetchError2(struct worker *w, const char *error, const char *more)
-{
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	if (!w->fetch_failed) {
-		if (more == NULL)
-			WSLB(w, SLT_FetchError, "%s", error);
-		else
-			WSLB(w, SLT_FetchError, "%s: %s", error, more);
-	}
-	w->fetch_failed = 1;
-	return (-1);
-}
-
-int
-FetchError(struct worker *w, const char *error)
-{
-	return(FetchError2(w, error, NULL));
-}
-
-/*--------------------------------------------------------------------
- * VFP_NOP
- *
- * This fetch-processor does nothing but store the object.
- * It also documents the API
- */
-
-/*--------------------------------------------------------------------
- * VFP_BEGIN
- *
- * Called to set up stuff.
- *
- * 'estimate' is the estimate of the number of bytes we expect to receive,
- * as seen on the socket, or zero if unknown.
- */
-static void __match_proto__()
-vfp_nop_begin(struct worker *w, size_t estimate)
-{
-
-	if (estimate > 0)
-		(void)FetchStorage(w, estimate);
-}
-
-/*--------------------------------------------------------------------
- * VFP_BYTES
- *
- * Process (up to) 'bytes' from the socket.
- *
- * Return -1 on error, issue FetchError()
- *	will not be called again, once error happens.
- * Return 0 on EOF on socket even if bytes not reached.
- * Return 1 when 'bytes' have been processed.
- */
-
-static int __match_proto__()
-vfp_nop_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	ssize_t l, wl;
-	struct storage *st;
-
-	AZ(w->fetch_failed);
-	while (bytes > 0) {
-		st = FetchStorage(w, 0);
-		if (st == NULL)
-			return(-1);
-		l = st->space - st->len;
-		if (l > bytes)
-			l = bytes;
-		wl = HTC_Read(w, htc, st->ptr + st->len, l);
-		if (wl <= 0)
-			return (wl);
-		st->len += wl;
-		w->fetch_obj->len += wl;
-		bytes -= wl;
-		if (w->do_stream)
-			RES_StreamPoll(w);
-	}
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * VFP_END
- *
- * Finish & cleanup
- *
- * Return -1 for error
- * Return 0 for OK
- */
-
-static int __match_proto__()
-vfp_nop_end(struct worker *w)
-{
-	struct storage *st;
-
-	st = VTAILQ_LAST(&w->fetch_obj->store, storagehead);
-	if (st == NULL)
-		return (0);
-
-	if (st->len == 0) {
-		VTAILQ_REMOVE(&w->fetch_obj->store, st, list);
-		STV_free(st);
-		return (0);
-	}
-	if (st->len < st->space)
-		STV_trim(st, st->len);
-	return (0);
-}
-
-static struct vfp vfp_nop = {
-	.begin	=	vfp_nop_begin,
-	.bytes	=	vfp_nop_bytes,
-	.end	=	vfp_nop_end,
-};
-
-/*--------------------------------------------------------------------
- * Fetch Storage to put object into.
- *
- */
-
-struct storage *
-FetchStorage(struct worker *w, ssize_t sz)
-{
-	ssize_t l;
-	struct storage *st;
-	struct object *obj;
-
-	obj = w->fetch_obj;
-	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
-	st = VTAILQ_LAST(&obj->store, storagehead);
-	if (st != NULL && st->len < st->space)
-		return (st);
-
-	l = fetchfrag;
-	if (l == 0)
-		l = sz;
-	if (l == 0)
-		l = cache_param->fetch_chunksize * 1024LL;
-	st = STV_alloc(w, l);
-	if (st == NULL) {
-		(void)FetchError(w, "Could not get storage");
-		return (NULL);
-	}
-	AZ(st->len);
-	VTAILQ_INSERT_TAIL(&obj->store, st, list);
-	return (st);
-}
-
-/*--------------------------------------------------------------------
- * Convert a string to a size_t safely
- */
-
-static ssize_t
-fetch_number(const char *nbr, int radix)
-{
-	uintmax_t cll;
-	ssize_t cl;
-	char *q;
-
-	if (*nbr == '\0')
-		return (-1);
-	cll = strtoumax(nbr, &q, radix);
-	if (q == NULL || *q != '\0')
-		return (-1);
-
-	cl = (ssize_t)cll;
-	if((uintmax_t)cl != cll) /* Protect against bogusly large values */
-		return (-1);
-	return (cl);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-fetch_straight(struct worker *w, struct http_conn *htc, ssize_t cl)
-{
-	int i;
-
-	assert(w->body_status == BS_LENGTH);
-
-	if (cl < 0) {
-		return (FetchError(w, "straight length field bogus"));
-	} else if (cl == 0)
-		return (0);
-
-	i = w->vfp->bytes(w, htc, cl);
-	if (i <= 0)
-		return (FetchError(w, "straight insufficient bytes"));
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Read a chunked HTTP object.
- *
- * XXX: Reading one byte at a time is pretty pessimal.
- */
-
-static int
-fetch_chunked(struct worker *w, struct http_conn *htc)
-{
-	int i;
-	char buf[20];		/* XXX: 20 is arbitrary */
-	unsigned u;
-	ssize_t cl;
-
-	assert(w->body_status == BS_CHUNKED);
-	do {
-		/* Skip leading whitespace */
-		do {
-			if (HTC_Read(w, htc, buf, 1) <= 0)
-				return (-1);
-		} while (vct_islws(buf[0]));
-
-		if (!vct_ishex(buf[0]))
-			return (FetchError(w,"chunked header non-hex"));
-
-		/* Collect hex digits, skipping leading zeros */
-		for (u = 1; u < sizeof buf; u++) {
-			do {
-				if (HTC_Read(w, htc, buf + u, 1) <= 0)
-					return (-1);
-			} while (u == 1 && buf[0] == '0' && buf[u] == '0');
-			if (!vct_ishex(buf[u]))
-				break;
-		}
-
-		if (u >= sizeof buf)
-			return (FetchError(w,"chunked header too long"));
-
-		/* Skip trailing white space */
-		while(vct_islws(buf[u]) && buf[u] != '\n')
-			if (HTC_Read(w, htc, buf + u, 1) <= 0)
-				return (-1);
-
-		if (buf[u] != '\n')
-			return (FetchError(w,"chunked header no NL"));
-
-		buf[u] = '\0';
-		cl = fetch_number(buf, 16);
-		if (cl < 0)
-			return (FetchError(w,"chunked header number syntax"));
-
-		if (cl > 0 && w->vfp->bytes(w, htc, cl) <= 0)
-			return (-1);
-
-		i = HTC_Read(w, htc, buf, 1);
-		if (i <= 0)
-			return (-1);
-		if (buf[0] == '\r' && HTC_Read(w, htc, buf, 1) <= 0)
-			return (-1);
-		if (buf[0] != '\n')
-			return (FetchError(w,"chunked tail no NL"));
-	} while (cl > 0);
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-fetch_eof(struct worker *w, struct http_conn *htc)
-{
-	int i;
-
-	assert(w->body_status == BS_EOF);
-	i = w->vfp->bytes(w, htc, SSIZE_MAX);
-	if (i < 0)
-		return (-1);
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Fetch any body attached to the incoming request, and either write it
- * to the backend (if we pass) or discard it (anything else).
- * This is mainly a separate function to isolate the stack buffer and
- * to contain the complexity when we start handling chunked encoding.
- */
-
-int
-FetchReqBody(struct sess *sp)
-{
-	unsigned long content_length;
-	char buf[8192];
-	char *ptr, *endp;
-	int rdcnt;
-
-	if (http_GetHdr(sp->http, H_Content_Length, &ptr)) {
-
-		content_length = strtoul(ptr, &endp, 10);
-		/* XXX should check result of conversion */
-		while (content_length) {
-			if (content_length > sizeof buf)
-				rdcnt = sizeof buf;
-			else
-				rdcnt = content_length;
-			rdcnt = HTC_Read(sp->wrk, sp->htc, buf, rdcnt);
-			if (rdcnt <= 0)
-				return (1);
-			content_length -= rdcnt;
-			if (!sp->sendbody)
-				continue;
-			(void)WRW_Write(sp->wrk, buf, rdcnt); /* XXX: stats ? */
-			if (WRW_Flush(sp->wrk))
-				return (2);
-		}
-	}
-	if (http_GetHdr(sp->http, H_Transfer_Encoding, NULL)) {
-		/* XXX: Handle chunked encoding. */
-		WSP(sp, SLT_Debug, "Transfer-Encoding in request");
-		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
-FetchHdr(struct sess *sp)
-{
-	struct vbc *vc;
-	struct worker *w;
-	char *b;
-	struct http *hp;
-	int retry = -1;
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-	w = sp->wrk;
-
-	AN(sp->director);
-	AZ(sp->obj);
-
-	if (sp->objcore != NULL) {		/* pass has no objcore */
-		CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
-		AN(sp->objcore->flags & OC_F_BUSY);
-	}
-
-	hp = w->bereq;
-
-	sp->wrk->vbc = VDI_GetFd(NULL, sp);
-	if (sp->wrk->vbc == NULL) {
-		WSP(sp, SLT_FetchError, "no backend connection");
-		return (-1);
-	}
-	vc = sp->wrk->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(hp, H_Host, &b))
-		VDI_AddHostHeader(sp);
-
-	(void)VTCP_blocking(vc->fd);	/* XXX: we should timeout instead */
-	WRW_Reserve(w, &vc->fd);
-	(void)http_Write(w, vc->vsl_id, hp, 0);	/* XXX: stats ? */
-
-	/* Deal with any message-body the request might have */
-	i = FetchReqBody(sp);
-	if (WRW_FlushRelease(w) || i > 0) {
-		WSP(sp, SLT_FetchError, "backend write error: %d (%s)",
-		    errno, strerror(errno));
-		VDI_CloseFd(sp->wrk);
-		/* XXX: other cleanup ? */
-		return (retry);
-	}
-
-	/* Checkpoint the vsl.here */
-	WSL_Flush(w, 0);
-
-	/* XXX is this the right place? */
-	VSC_C_main->backend_req++;
-
-	/* Receive response */
-
-	HTC_Init(w->htc, w->ws, vc->fd, vc->vsl_id, cache_param->http_resp_size,
-	    cache_param->http_resp_hdr_len);
-
-	VTCP_set_read_timeout(vc->fd, vc->first_byte_timeout);
-
-	i = HTC_Rx(w->htc);
-
-	if (i < 0) {
-		WSP(sp, SLT_FetchError, "http first read error: %d %d (%s)",
-		    i, errno, strerror(errno));
-		VDI_CloseFd(sp->wrk);
-		/* XXX: other cleanup ? */
-		/* Retryable if we never received anything */
-		return (i == -1 ? retry : -1);
-	}
-
-	VTCP_set_read_timeout(vc->fd, vc->between_bytes_timeout);
-
-	while (i == 0) {
-		i = HTC_Rx(w->htc);
-		if (i < 0) {
-			WSP(sp, SLT_FetchError,
-			    "http first read error: %d %d (%s)",
-			    i, errno, strerror(errno));
-			VDI_CloseFd(sp->wrk);
-			/* XXX: other cleanup ? */
-			return (-1);
-		}
-	}
-
-	hp = w->beresp;
-
-	if (http_DissectResponse(w, w->htc, hp)) {
-		WSP(sp, SLT_FetchError, "http format error");
-		VDI_CloseFd(sp->wrk);
-		/* XXX: other cleanup ? */
-		return (-1);
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-FetchBody(struct worker *w, struct object *obj)
-{
-	int cls;
-	struct storage *st;
-	int mklen;
-	ssize_t cl;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	AZ(w->fetch_obj);
-	CHECK_OBJ_NOTNULL(w->vbc, VBC_MAGIC);
-	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
-	CHECK_OBJ_NOTNULL(obj->http, HTTP_MAGIC);
-
-	if (w->vfp == NULL)
-		w->vfp = &vfp_nop;
-
-	AssertObjCorePassOrBusy(obj->objcore);
-
-	AZ(w->vgz_rx);
-	AZ(VTAILQ_FIRST(&obj->store));
-
-	w->fetch_obj = obj;
-	w->fetch_failed = 0;
-
-	/* XXX: pick up estimate from objdr ? */
-	cl = 0;
-	switch (w->body_status) {
-	case BS_NONE:
-		cls = 0;
-		mklen = 0;
-		break;
-	case BS_ZERO:
-		cls = 0;
-		mklen = 1;
-		break;
-	case BS_LENGTH:
-		cl = fetch_number( w->h_content_length, 10);
-		w->vfp->begin(w, cl > 0 ? cl : 0);
-		cls = fetch_straight(w, w->htc, cl);
-		mklen = 1;
-		if (w->vfp->end(w))
-			cls = -1;
-		break;
-	case BS_CHUNKED:
-		w->vfp->begin(w, cl);
-		cls = fetch_chunked(w, w->htc);
-		mklen = 1;
-		if (w->vfp->end(w))
-			cls = -1;
-		break;
-	case BS_EOF:
-		w->vfp->begin(w, cl);
-		cls = fetch_eof(w, w->htc);
-		mklen = 1;
-		if (w->vfp->end(w))
-			cls = -1;
-		break;
-	case BS_ERROR:
-		cls = 1;
-		mklen = 0;
-		break;
-	default:
-		cls = 0;
-		mklen = 0;
-		INCOMPL();
-	}
-	AZ(w->vgz_rx);
-
-	/*
-	 * It is OK for ->end to just leave the last storage segment
-	 * sitting on w->storage, we will always call vfp_nop_end()
-	 * to get it trimmed or thrown out if empty.
-	 */
-	AZ(vfp_nop_end(w));
-
-	w->fetch_obj = NULL;
-
-	WSLB(w, SLT_Fetch_Body, "%u(%s) cls %d mklen %u",
-	    w->body_status, body_status(w->body_status),
-	    cls, mklen);
-
-	if (w->body_status == BS_ERROR) {
-		VDI_CloseFd(w);
-		return (__LINE__);
-	}
-
-	if (cls < 0) {
-		w->stats.fetch_failed++;
-		/* XXX: Wouldn't this store automatically be released ? */
-		while (!VTAILQ_EMPTY(&obj->store)) {
-			st = VTAILQ_FIRST(&obj->store);
-			VTAILQ_REMOVE(&obj->store, st, list);
-			STV_free(st);
-		}
-		VDI_CloseFd(w);
-		obj->len = 0;
-		return (__LINE__);
-	}
-	AZ(w->fetch_failed);
-
-	if (cls == 0 && w->do_close)
-		cls = 1;
-
-	WSLB(w, SLT_Length, "%u", obj->len);
-
-	{
-	/* Sanity check fetch methods accounting */
-		ssize_t uu;
-
-		uu = 0;
-		VTAILQ_FOREACH(st, &obj->store, list)
-			uu += st->len;
-		if (w->do_stream)
-			/* Streaming might have started freeing stuff */
-			assert (uu <= obj->len);
-
-		else
-			assert(uu == obj->len);
-	}
-
-	if (mklen > 0) {
-		http_Unset(obj->http, H_Content_Length);
-		http_PrintfHeader(w, w->vbc->vsl_id, obj->http,
-		    "Content-Length: %jd", (intmax_t)obj->len);
-	}
-
-	if (cls)
-		VDI_CloseFd(w);
-	else
-		VDI_RecycleFd(w);
-
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Debugging aids
- */
-
-static void
-debug_fragfetch(struct cli *cli, const char * const *av, void *priv)
-{
-	(void)priv;
-	(void)cli;
-	fetchfrag = strtoul(av[2], NULL, 0);
-}
-
-static struct cli_proto debug_cmds[] = {
-	{ "debug.fragfetch", "debug.fragfetch",
-		"\tEnable fetch fragmentation\n", 1, 1, "d", debug_fragfetch },
-	{ NULL }
-};
-
-/*--------------------------------------------------------------------
- *
- */
-
-void
-Fetch_Init(void)
-{
-
-	CLI_AddFuncs(debug_cmds);
-}
diff --git a/bin/varnishd/cache_gzip.c b/bin/varnishd/cache_gzip.c
deleted file mode 100644
index 32a7413..0000000
--- a/bin/varnishd/cache_gzip.c
+++ /dev/null
@@ -1,697 +0,0 @@
-/*-
- * Copyright (c) 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.
- *
- * Interaction with the linvgz (zlib) library.
- *
- * The zlib library pollutes namespace a LOT when you include the "vgz.h"
- * (aka (zlib.h") file so we contain the damage by vectoring all access
- * to libz through this source file.
- *
- * The API defined by this file, will also insulate the rest of the code,
- * should we find a better gzip library at a later date.
- *
- * The absolutely worst case gzip processing path, once we have pipe-lining,
- * will be the following, so we need to be a bit careful with the scratch
- * space we use:
- *
- *	Backend		Tmp	Input	Output
- *         |		----------------------
- *	   v
- *	 gunzip		wrk	stack	?
- *         |
- *	   v
- *	  esi
- *         |
- *	   v
- *	  gzip		wrk	?	storage
- *         |
- *	   v
- *	  cache
- *         |
- *	   v
- *	 gunzip		wrk	storage	stack
- *         |
- *	   v
- *	 client
- *
- * XXXX: The two '?' are obviously the same memory, but I have yet to decide
- * where it goes.   As usual we try to avoid the session->ws if we can but
- * I may have to use that.
- *
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "vgz.h"
-
-struct vgz {
-	unsigned		magic;
-#define VGZ_MAGIC		0x162df0cb
-	enum {VGZ_GZ,VGZ_UN}	dir;
-	struct worker		*wrk;
-	const char		*id;
-	struct ws		*tmp;
-	char			*tmp_snapshot;
-	int			last_i;
-
-	struct storage		*obuf;
-
-	z_stream		vz;
-};
-
-/*--------------------------------------------------------------------*/
-
-static voidpf
-vgz_alloc(voidpf opaque, uInt items, uInt size)
-{
-	struct vgz *vg;
-
-	CAST_OBJ_NOTNULL(vg, opaque, VGZ_MAGIC);
-
-	return (WS_Alloc(vg->tmp, items * size));
-}
-
-static void
-vgz_free(voidpf opaque, voidpf address)
-{
-	struct vgz *vg;
-
-	CAST_OBJ_NOTNULL(vg, opaque, VGZ_MAGIC);
-	(void)address;
-}
-
-/*--------------------------------------------------------------------
- * Set up a gunzip instance
- */
-
-static struct vgz *
-vgz_alloc_vgz(struct worker *wrk, const char *id)
-{
-	struct vgz *vg;
-	struct ws *ws;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	ws = wrk->ws;
-	WS_Assert(ws);
-	// XXX: we restore workspace in esi:include
-	// vg = (void*)WS_Alloc(ws, sizeof *vg);
-	ALLOC_OBJ(vg, VGZ_MAGIC);
-	AN(vg);
-	memset(vg, 0, sizeof *vg);
-	vg->magic = VGZ_MAGIC;
-	vg->wrk = wrk;
-	vg->id = id;
-
-	switch (cache_param->gzip_tmp_space) {
-	case 0:
-	case 1:
-		/* malloc, the default */
-		break;
-	case 2:
-		vg->tmp = wrk->ws;
-		vg->tmp_snapshot = WS_Snapshot(vg->tmp);
-		vg->vz.zalloc = vgz_alloc;
-		vg->vz.zfree = vgz_free;
-		vg->vz.opaque = vg;
-		break;
-	default:
-		assert(0 == __LINE__);
-	}
-	return (vg);
-}
-
-struct vgz *
-VGZ_NewUngzip(struct worker *wrk, const char *id)
-{
-	struct vgz *vg;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	vg = vgz_alloc_vgz(wrk, id);
-	vg->dir = VGZ_UN;
-	VSC_C_main->n_gunzip++;
-
-	/*
-	 * Max memory usage according to zonf.h:
-	 *	mem_needed = "a few kb" + (1 << (windowBits))
-	 * Since we don't control windowBits, we have to assume
-	 * it is 15, so 34-35KB or so.
-	 */
-	assert(Z_OK == inflateInit2(&vg->vz, 31));
-	return (vg);
-}
-
-struct vgz *
-VGZ_NewGzip(struct worker *wrk, const char *id)
-{
-	struct vgz *vg;
-	int i;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	vg = vgz_alloc_vgz(wrk, id);
-	vg->dir = VGZ_GZ;
-	VSC_C_main->n_gzip++;
-
-	/*
-	 * From zconf.h:
-	 *
-	 *	mem_needed = "a few kb"
-	 *		+ (1 << (windowBits+2))
-	 *		+  (1 << (memLevel+9))
-	 *
-	 * windowBits [8..15] (-> 1K..128K)
-	 * memLevel [1..9] (-> 1K->256K)
-	 *
-	 * XXX: They probably needs to be params...
-	 *
-	 * XXX: It may be more efficent to malloc them, rather than have
-	 * XXX: too many worker threads grow the stacks.
-	 */
-	i = deflateInit2(&vg->vz,
-	    cache_param->gzip_level,		/* Level */
-	    Z_DEFLATED,			/* Method */
-	    16 + cache_param->gzip_window,	/* Window bits (16=gzip + 15) */
-	    cache_param->gzip_memlevel,	/* memLevel */
-	    Z_DEFAULT_STRATEGY);
-	assert(Z_OK == i);
-	return (vg);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
-{
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-
-	AZ(vg->vz.avail_in);
-	vg->vz.next_in = TRUST_ME(ptr);
-	vg->vz.avail_in = len;
-}
-
-int
-VGZ_IbufEmpty(const struct vgz *vg)
-{
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	return (vg->vz.avail_in == 0);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
-{
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-
-	vg->vz.next_out = TRUST_ME(ptr);
-	vg->vz.avail_out = len;
-}
-
-int
-VGZ_ObufFull(const struct vgz *vg)
-{
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	return (vg->vz.avail_out == 0);
-}
-
-/*--------------------------------------------------------------------
- * Keep the outbuffer supplied with storage and file it under the
- * sp->obj as it fills.
- */
-
-int
-VGZ_ObufStorage(struct worker *w, struct vgz *vg)
-{
-	struct storage *st;
-
-	st = FetchStorage(w, 0);
-	if (st == NULL)
-		return (-1);
-
-	vg->obuf = st;
-	VGZ_Obuf(vg, st->ptr + st->len, st->space - st->len);
-
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-VGZ_Gunzip(struct vgz *vg, const void **pptr, size_t *plen)
-{
-	int i;
-	ssize_t l;
-	const uint8_t *before;
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-
-	*pptr = NULL;
-	*plen = 0;
-	AN(vg->vz.next_out);
-	AN(vg->vz.avail_out);
-	before = vg->vz.next_out;
-	i = inflate(&vg->vz, 0);
-	if (i == Z_OK || i == Z_STREAM_END) {
-		*pptr = before;
-		l = (const uint8_t *)vg->vz.next_out - before;
-		*plen = l;
-		if (vg->obuf != NULL)
-			vg->obuf->len += l;
-	}
-	vg->last_i = i;
-	if (i == Z_OK)
-		return (VGZ_OK);
-	if (i == Z_STREAM_END)
-		return (VGZ_END);
-	if (i == Z_BUF_ERROR)
-		return (VGZ_STUCK);
-	VSL(SLT_Debug, 0, "Unknown INFLATE=%d (%s)\n", i, vg->vz.msg);
-	return (VGZ_ERROR);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-VGZ_Gzip(struct vgz *vg, const void **pptr, size_t *plen, enum vgz_flag flags)
-{
-	int i;
-	int zflg;
-	ssize_t l;
-	const uint8_t *before;
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-
-	*pptr = NULL;
-	*plen = 0;
-	AN(vg->vz.next_out);
-	AN(vg->vz.avail_out);
-	before = vg->vz.next_out;
-	switch(flags) {
-	case VGZ_NORMAL:	zflg = Z_NO_FLUSH; break;
-	case VGZ_ALIGN:		zflg = Z_SYNC_FLUSH; break;
-	case VGZ_RESET:		zflg = Z_FULL_FLUSH; break;
-	case VGZ_FINISH:	zflg = Z_FINISH; break;
-	default:		INCOMPL();
-	}
-	i = deflate(&vg->vz, zflg);
-	if (i == Z_OK || i == Z_STREAM_END) {
-		*pptr = before;
-		l = (const uint8_t *)vg->vz.next_out - before;
-		*plen = l;
-		if (vg->obuf != NULL)
-			vg->obuf->len += l;
-	}
-	vg->last_i = i;
-	if (i == Z_OK)
-		return (0);
-	if (i == Z_STREAM_END)
-		return (1);
-	if (i == Z_BUF_ERROR)
-		return (2);
-	return (-1);
-}
-
-/*--------------------------------------------------------------------
- * Gunzip ibuf into outb, if it runs full, emit it with WRW.
- * Leave flushing to caller, more data may be coming.
- */
-
-int
-VGZ_WrwGunzip(struct worker *w, struct vgz *vg, const void *ibuf,
-    ssize_t ibufl, char *obuf, ssize_t obufl, ssize_t *obufp)
-{
-	int i;
-	size_t dl;
-	const void *dp;
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	assert(obufl > 16);
-	VGZ_Ibuf(vg, ibuf, ibufl);
-	if (ibufl == 0)
-		return (VGZ_OK);
-	VGZ_Obuf(vg, obuf + *obufp, obufl - *obufp);
-	do {
-		if (obufl == *obufp)
-			i = VGZ_STUCK;
-		else {
-			i = VGZ_Gunzip(vg, &dp, &dl);
-			*obufp += dl;
-		}
-		if (i < VGZ_OK) {
-			/* XXX: VSL ? */
-			return (-1);
-		}
-		if (obufl == *obufp || i == VGZ_STUCK) {
-			w->acct_tmp.bodybytes += *obufp;
-			(void)WRW_Write(w, obuf, *obufp);
-			(void)WRW_Flush(w);
-			*obufp = 0;
-			VGZ_Obuf(vg, obuf + *obufp, obufl - *obufp);
-		}
-	} while (!VGZ_IbufEmpty(vg));
-	if (i == VGZ_STUCK)
-		i = VGZ_OK;
-	return (i);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VGZ_UpdateObj(const struct vgz *vg, struct object *obj)
-{
-
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
-	obj->gzip_start	= vg->vz.start_bit;
-	obj->gzip_last	= vg->vz.last_bit;
-	obj->gzip_stop	= vg->vz.stop_bit;
-}
-
-/*--------------------------------------------------------------------
- * Passing a vsl_id of -1 means "use w->vbc->vsl_id"
- */
-
-int
-VGZ_Destroy(struct vgz **vgp, int vsl_id)
-{
-	struct vgz *vg;
-	int i;
-
-	vg = *vgp;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	*vgp = NULL;
-
-	if (vsl_id < 0)
-		WSLB(vg->wrk, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
-		    vg->id,
-		    (intmax_t)vg->vz.total_in,
-		    (intmax_t)vg->vz.total_out,
-		    (intmax_t)vg->vz.start_bit,
-		    (intmax_t)vg->vz.last_bit,
-		    (intmax_t)vg->vz.stop_bit);
-	else
-		WSL(vg->wrk, SLT_Gzip, vsl_id, "%s %jd %jd %jd %jd %jd",
-		    vg->id,
-		    (intmax_t)vg->vz.total_in,
-		    (intmax_t)vg->vz.total_out,
-		    (intmax_t)vg->vz.start_bit,
-		    (intmax_t)vg->vz.last_bit,
-		    (intmax_t)vg->vz.stop_bit);
-	if (vg->tmp != NULL)
-		WS_Reset(vg->tmp, vg->tmp_snapshot);
-	if (vg->dir == VGZ_GZ)
-		i = deflateEnd(&vg->vz);
-	else
-		i = inflateEnd(&vg->vz);
-	if (vg->last_i == Z_STREAM_END && i == Z_OK)
-		i = Z_STREAM_END;
-	FREE_OBJ(vg);
-	if (i == Z_OK)
-		return (VGZ_OK);
-	if (i == Z_STREAM_END)
-		return (VGZ_END);
-	if (i == Z_BUF_ERROR)
-		return (VGZ_STUCK);
-	return (VGZ_ERROR);
-}
-
-/*--------------------------------------------------------------------
- * VFP_GUNZIP
- *
- * A VFP for gunzip'ing an object as we receive it from the backend
- */
-
-static void __match_proto__()
-vfp_gunzip_begin(struct worker *w, size_t estimate)
-{
-	(void)estimate;
-	AZ(w->vgz_rx);
-	w->vgz_rx = VGZ_NewUngzip(w, "U F -");
-}
-
-static int __match_proto__()
-vfp_gunzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	struct vgz *vg;
-	ssize_t l, wl;
-	int i = -100;
-	uint8_t	ibuf[cache_param->gzip_stack_buffer];
-	size_t dl;
-	const void *dp;
-
-	AZ(w->fetch_failed);
-	vg = w->vgz_rx;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	AZ(vg->vz.avail_in);
-	while (bytes > 0 || vg->vz.avail_in > 0) {
-		if (vg->vz.avail_in == 0 && bytes > 0) {
-			l = sizeof ibuf;
-			if (l > bytes)
-				l = bytes;
-			wl = HTC_Read(w, htc, ibuf, l);
-			if (wl <= 0)
-				return (wl);
-			VGZ_Ibuf(vg, ibuf, wl);
-			bytes -= wl;
-		}
-
-		if (VGZ_ObufStorage(w, vg))
-			return(-1);
-		i = VGZ_Gunzip(vg, &dp, &dl);
-		if (i != VGZ_OK && i != VGZ_END)
-			return(FetchError(w, "Gunzip data error"));
-		w->fetch_obj->len += dl;
-		if (w->do_stream)
-			RES_StreamPoll(w);
-	}
-	assert(i == Z_OK || i == Z_STREAM_END);
-	return (1);
-}
-
-static int __match_proto__()
-vfp_gunzip_end(struct worker *w)
-{
-	struct vgz *vg;
-
-	vg = w->vgz_rx;
-	w->vgz_rx = NULL;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	if (w->fetch_failed) {
-		(void)VGZ_Destroy(&vg, -1);
-		return(0);
-	}
-	if (VGZ_Destroy(&vg, -1) != VGZ_END)
-		return(FetchError(w, "Gunzip error at the very end"));
-	return (0);
-}
-
-struct vfp vfp_gunzip = {
-        .begin  =       vfp_gunzip_begin,
-        .bytes  =       vfp_gunzip_bytes,
-        .end    =       vfp_gunzip_end,
-};
-
-
-/*--------------------------------------------------------------------
- * VFP_GZIP
- *
- * A VFP for gzip'ing an object as we receive it from the backend
- */
-
-static void __match_proto__()
-vfp_gzip_begin(struct worker *w, size_t estimate)
-{
-	(void)estimate;
-
-	AZ(w->vgz_rx);
-	w->vgz_rx = VGZ_NewGzip(w, "G F -");
-}
-
-static int __match_proto__()
-vfp_gzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	struct vgz *vg;
-	ssize_t l, wl;
-	int i = -100;
-	uint8_t ibuf[cache_param->gzip_stack_buffer];
-	size_t dl;
-	const void *dp;
-
-	AZ(w->fetch_failed);
-	vg = w->vgz_rx;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	AZ(vg->vz.avail_in);
-	while (bytes > 0 || !VGZ_IbufEmpty(vg)) {
-		if (VGZ_IbufEmpty(vg) && bytes > 0) {
-			l = sizeof ibuf;
-			if (l > bytes)
-				l = bytes;
-			wl = HTC_Read(w, htc, ibuf, l);
-			if (wl <= 0)
-				return (wl);
-			VGZ_Ibuf(vg, ibuf, wl);
-			bytes -= wl;
-		}
-		if (VGZ_ObufStorage(w, vg))
-			return(-1);
-		i = VGZ_Gzip(vg, &dp, &dl, VGZ_NORMAL);
-		assert(i == Z_OK);
-		w->fetch_obj->len += dl;
-		if (w->do_stream)
-			RES_StreamPoll(w);
-	}
-	return (1);
-}
-
-static int __match_proto__()
-vfp_gzip_end(struct worker *w)
-{
-	struct vgz *vg;
-	size_t dl;
-	const void *dp;
-	int i;
-
-	vg = w->vgz_rx;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	w->vgz_rx = NULL;
-	if (w->fetch_failed) {
-		(void)VGZ_Destroy(&vg, -1);
-		return(0);
-	}
-	do {
-		VGZ_Ibuf(vg, "", 0);
-		if (VGZ_ObufStorage(w, vg))
-			return(-1);
-		i = VGZ_Gzip(vg, &dp, &dl, VGZ_FINISH);
-		w->fetch_obj->len += dl;
-	} while (i != Z_STREAM_END);
-	if (w->do_stream)
-		RES_StreamPoll(w);
-	VGZ_UpdateObj(vg, w->fetch_obj);
-	if (VGZ_Destroy(&vg, -1) != VGZ_END)
-		return(FetchError(w, "Gzip error at the very end"));
-	return (0);
-}
-
-struct vfp vfp_gzip = {
-        .begin  =       vfp_gzip_begin,
-        .bytes  =       vfp_gzip_bytes,
-        .end    =       vfp_gzip_end,
-};
-
-/*--------------------------------------------------------------------
- * VFP_TESTGZIP
- *
- * A VFP for testing that received gzip data is valid, and for
- * collecting the magic bits while we're at it.
- */
-
-static void __match_proto__()
-vfp_testgzip_begin(struct worker *w, size_t estimate)
-{
-	(void)estimate;
-	w->vgz_rx = VGZ_NewUngzip(w, "u F -");
-	CHECK_OBJ_NOTNULL(w->vgz_rx, VGZ_MAGIC);
-}
-
-static int __match_proto__()
-vfp_testgzip_bytes(struct worker *w, struct http_conn *htc, ssize_t bytes)
-{
-	struct vgz *vg;
-	ssize_t l, wl;
-	int i = -100;
-	uint8_t	obuf[cache_param->gzip_stack_buffer];
-	size_t dl;
-	const void *dp;
-	struct storage *st;
-
-	AZ(w->fetch_failed);
-	vg = w->vgz_rx;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	AZ(vg->vz.avail_in);
-	while (bytes > 0) {
-		st = FetchStorage(w, 0);
-		if (st == NULL)
-			return(-1);
-		l = st->space - st->len;
-		if (l > bytes)
-			l = bytes;
-		wl = HTC_Read(w, htc, st->ptr + st->len, l);
-		if (wl <= 0)
-			return (wl);
-		bytes -= wl;
-		VGZ_Ibuf(vg, st->ptr + st->len, wl);
-		st->len += wl;
-		w->fetch_obj->len += wl;
-		if (w->do_stream)
-			RES_StreamPoll(w);
-
-		while (!VGZ_IbufEmpty(vg)) {
-			VGZ_Obuf(vg, obuf, sizeof obuf);
-			i = VGZ_Gunzip(vg, &dp, &dl);
-			if (i == VGZ_END && !VGZ_IbufEmpty(vg))
-				return(FetchError(w, "Junk after gzip data"));
-			if (i != VGZ_OK && i != VGZ_END)
-				return(FetchError2(w,
-				    "Invalid Gzip data", vg->vz.msg));
-		}
-	}
-	assert(i == VGZ_OK || i == VGZ_END);
-	return (1);
-}
-
-static int __match_proto__()
-vfp_testgzip_end(struct worker *w)
-{
-	struct vgz *vg;
-
-	vg = w->vgz_rx;
-	w->vgz_rx = NULL;
-	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
-	if (w->fetch_failed) {
-		(void)VGZ_Destroy(&vg, -1);
-		return(0);
-	}
-	VGZ_UpdateObj(vg, w->fetch_obj);
-	if (VGZ_Destroy(&vg, -1) != VGZ_END)
-		return(FetchError(w, "TestGunzip error at the very end"));
-	return (0);
-}
-
-struct vfp vfp_testgzip = {
-        .begin  =       vfp_testgzip_begin,
-        .bytes  =       vfp_testgzip_bytes,
-        .end    =       vfp_testgzip_end,
-};
diff --git a/bin/varnishd/cache_hash.c b/bin/varnishd/cache_hash.c
deleted file mode 100644
index db865de..0000000
--- a/bin/varnishd/cache_hash.c
+++ /dev/null
@@ -1,752 +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.
- *
- * This is the central hash-table code, it relies on a chosen hash
- * implementation only for the actual hashing, all the housekeeping
- * happens here.
- *
- * We have two kinds of structures, objecthead and object.  An objecthead
- * corresponds to a given (Host:, URL) tupple, and the objects hung from
- * the objecthead may represent various variations (ie: Vary: header,
- * different TTL etc) instances of that web-entity.
- *
- * Each objecthead has a mutex which locks both its own fields, the
- * list of objects and fields in the objects.
- *
- * The hash implementation must supply a reference count facility on
- * the objecthead, and return with a reference held after a lookup.
- *
- * Lookups in the hash implementation returns with a ref held and each
- * object hung from the objhead holds a ref as well.
- *
- * Objects have refcounts which are locked by the objecthead mutex.
- *
- * New objects are always marked busy, and they can go from busy to
- * not busy only once.
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "hash/hash_slinger.h"
-#include "vsha256.h"
-
-static const struct hash_slinger *hash;
-
-/*---------------------------------------------------------------------*/
-/* Precreate an objhead and object for later use */
-void
-HSH_Prealloc(const struct sess *sp)
-{
-	struct worker *w;
-	struct objhead *oh;
-	struct objcore *oc;
-	struct waitinglist *wl;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-	w = sp->wrk;
-
-	if (w->nobjcore == NULL) {
-		ALLOC_OBJ(oc, OBJCORE_MAGIC);
-		XXXAN(oc);
-		w->nobjcore = oc;
-		w->stats.n_objectcore++;
-		oc->flags |= OC_F_BUSY;
-	}
-	CHECK_OBJ_NOTNULL(w->nobjcore, OBJCORE_MAGIC);
-
-	if (w->nobjhead == NULL) {
-		ALLOC_OBJ(oh, OBJHEAD_MAGIC);
-		XXXAN(oh);
-		oh->refcnt = 1;
-		VTAILQ_INIT(&oh->objcs);
-		Lck_New(&oh->mtx, lck_objhdr);
-		w->nobjhead = oh;
-		w->stats.n_objecthead++;
-	}
-	CHECK_OBJ_NOTNULL(w->nobjhead, OBJHEAD_MAGIC);
-
-	if (w->nwaitinglist == NULL) {
-		ALLOC_OBJ(wl, WAITINGLIST_MAGIC);
-		XXXAN(wl);
-		VTAILQ_INIT(&wl->list);
-		w->nwaitinglist = wl;
-		w->stats.n_waitinglist++;
-	}
-	CHECK_OBJ_NOTNULL(w->nwaitinglist, WAITINGLIST_MAGIC);
-
-	if (w->nbusyobj == NULL) {
-		ALLOC_OBJ(w->nbusyobj, BUSYOBJ_MAGIC);
-		XXXAN(w->nbusyobj);
-	}
-
-	if (hash->prep != NULL)
-		hash->prep(sp);
-}
-
-void
-HSH_Cleanup(struct worker *w)
-{
-
-	if (w->nobjcore != NULL) {
-		FREE_OBJ(w->nobjcore);
-		w->stats.n_objectcore--;
-		w->nobjcore = NULL;
-	}
-	if (w->nobjhead != NULL) {
-		Lck_Delete(&w->nobjhead->mtx);
-		FREE_OBJ(w->nobjhead);
-		w->nobjhead = NULL;
-		w->stats.n_objecthead--;
-	}
-	if (w->nwaitinglist != NULL) {
-		FREE_OBJ(w->nwaitinglist);
-		w->nwaitinglist = NULL;
-	}
-	if (w->nhashpriv != NULL) {
-		/* XXX: If needed, add slinger method for this */
-		free(w->nhashpriv);
-		w->nhashpriv = NULL;
-	}
-	if (w->nbusyobj != NULL) {
-		FREE_OBJ(w->nbusyobj);
-		w->nbusyobj = NULL;
-	}
-}
-
-void
-HSH_DeleteObjHead(struct worker *w, struct objhead *oh)
-{
-
-	AZ(oh->refcnt);
-	assert(VTAILQ_EMPTY(&oh->objcs));
-	Lck_Delete(&oh->mtx);
-	w->stats.n_objecthead--;
-	FREE_OBJ(oh);
-}
-
-void
-HSH_AddString(const struct sess *sp, const char *str)
-{
-	int l;
-
-	if (str == NULL)
-		str = "";
-	l = strlen(str);
-
-	SHA256_Update(sp->wrk->sha256ctx, str, l);
-	SHA256_Update(sp->wrk->sha256ctx, "#", 1);
-
-	if (cache_param->log_hash)
-		WSP(sp, SLT_Hash, "%s", str);
-}
-
-/*---------------------------------------------------------------------
- * This is a debugging hack to enable testing of boundary conditions
- * in the hash algorithm.
- * We trap the first 9 different digests and translate them to different
- * digests with edge bit conditions
- */
-
-static struct hsh_magiclist {
-	unsigned char was[SHA256_LEN];
-	unsigned char now[SHA256_LEN];
-} hsh_magiclist[] = {
-	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
-	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } },
-	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 } },
-	{ .now = {	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 } },
-	{ .now = {	0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-	{ .now = {	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-	{ .now = {	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-	{ .now = {	0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
-};
-
-#define HSH_NMAGIC (sizeof hsh_magiclist / sizeof hsh_magiclist[0])
-
-static void
-hsh_testmagic(void *result)
-{
-	int i, j;
-	static int nused = 0;
-
-	for (i = 0; i < nused; i++)
-		if (!memcmp(hsh_magiclist[i].was, result, SHA256_LEN))
-			break;
-	if (i == nused && i < HSH_NMAGIC)
-		memcpy(hsh_magiclist[nused++].was, result, SHA256_LEN);
-	if (i == nused)
-		return;
-	assert(i < HSH_NMAGIC);
-	fprintf(stderr, "HASHMAGIC: <");
-	for (j = 0; j < SHA256_LEN; j++)
-		fprintf(stderr, "%02x", ((unsigned char*)result)[j]);
-	fprintf(stderr, "> -> <");
-	memcpy(result, hsh_magiclist[i].now, SHA256_LEN);
-	for (j = 0; j < SHA256_LEN; j++)
-		fprintf(stderr, "%02x", ((unsigned char*)result)[j]);
-	fprintf(stderr, ">\n");
-}
-
-/*---------------------------------------------------------------------
- * Insert an object which magically appears out of nowhere or, more likely,
- * comes off some persistent storage device.
- * Return it with a reference held.
- */
-
-struct objcore *
-HSH_Insert(const struct sess *sp)
-{
-	struct worker *w;
-	struct objhead *oh;
-	struct objcore *oc;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-	AN(hash);
-	w = sp->wrk;
-
-	HSH_Prealloc(sp);
-	if (cache_param->diag_bitmap & 0x80000000)
-		hsh_testmagic(sp->wrk->nobjhead->digest);
-
-	AZ(sp->hash_objhead);
-	AN(w->nobjhead);
-	oh = hash->lookup(sp, w->nobjhead);
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-	if (oh == w->nobjhead)
-		w->nobjhead = NULL;
-	Lck_Lock(&oh->mtx);
-	assert(oh->refcnt > 0);
-
-	/* Insert (precreated) objcore in objecthead */
-	oc = w->nobjcore;
-	w->nobjcore = NULL;
-	oc->refcnt = 1;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	AZ(oc->flags & OC_F_BUSY);
-
-	VTAILQ_INSERT_HEAD(&oh->objcs, oc, list);
-	/* NB: do not deref objhead the new object inherits our reference */
-	oc->objhead = oh;
-	Lck_Unlock(&oh->mtx);
-	sp->wrk->stats.n_vampireobject++;
-	return (oc);
-}
-
-/*---------------------------------------------------------------------
- */
-
-struct objcore *
-HSH_Lookup(struct sess *sp, struct objhead **poh)
-{
-	struct worker *w;
-	struct objhead *oh;
-	struct objcore *oc;
-	struct objcore *busy_oc, *grace_oc;
-	struct object *o;
-	double grace_ttl;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->http, HTTP_MAGIC);
-	AN(sp->director);
-	AN(hash);
-	w = sp->wrk;
-
-	HSH_Prealloc(sp);
-	memcpy(sp->wrk->nobjhead->digest, sp->digest, sizeof sp->digest);
-	if (cache_param->diag_bitmap & 0x80000000)
-		hsh_testmagic(sp->wrk->nobjhead->digest);
-
-	if (sp->hash_objhead != NULL) {
-		/*
-		 * This sess came off the waiting list, and brings a
-		 * oh refcnt with it.
-		 */
-		CHECK_OBJ_NOTNULL(sp->hash_objhead, OBJHEAD_MAGIC);
-		oh = sp->hash_objhead;
-		sp->hash_objhead = NULL;
-	} else {
-		AN(w->nobjhead);
-		oh = hash->lookup(sp, w->nobjhead);
-		if (oh == w->nobjhead)
-			w->nobjhead = NULL;
-	}
-
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-	Lck_Lock(&oh->mtx);
-	assert(oh->refcnt > 0);
-	busy_oc = NULL;
-	grace_oc = NULL;
-	grace_ttl = NAN;
-	VTAILQ_FOREACH(oc, &oh->objcs, list) {
-		/* Must be at least our own ref + the objcore we examine */
-		assert(oh->refcnt > 1);
-		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-		assert(oc->objhead == oh);
-
-		if (oc->flags & OC_F_BUSY) {
-			CHECK_OBJ_NOTNULL(oc->busyobj, BUSYOBJ_MAGIC);
-			if (sp->hash_ignore_busy)
-				continue;
-
-			if (oc->busyobj->vary != NULL &&
-			    !VRY_Match(sp, oc->busyobj->vary))
-				continue;
-
-			busy_oc = oc;
-			continue;
-		}
-
-		o = oc_getobj(sp->wrk, oc);
-		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-
-		if (o->exp.ttl <= 0.)
-			continue;
-		if (BAN_CheckObject(o, sp))
-			continue;
-		if (o->vary != NULL && !VRY_Match(sp, o->vary))
-			continue;
-
-		/* If still valid, use it */
-		if (EXP_Ttl(sp, o) >= sp->t_req)
-			break;
-
-		/*
-		 * Remember any matching objects inside their grace period
-		 * and if there are several, use the least expired one.
-		 */
-		if (EXP_Grace(sp, o) >= sp->t_req) {
-			if (grace_oc == NULL ||
-			    grace_ttl < o->exp.entered + o->exp.ttl) {
-				grace_oc = oc;
-				grace_ttl = o->exp.entered + o->exp.ttl;
-			}
-		}
-	}
-
-	/*
-	 * If we have seen a busy object or the backend is unhealthy, and
-	 * we have an object in grace, use it, if req.grace is also
-	 * satisified.
-	 * XXX: Interesting footnote:  The busy object might be for a
-	 * XXX: different "Vary:" than we sought.  We have no way of knowing
-	 * XXX: this until the object is unbusy'ed, so in practice we
-	 * XXX: serialize fetch of all Vary's if grace is possible.
-	 */
-
-	AZ(sp->objcore);
-	sp->objcore = grace_oc;		/* XXX: Hack-ish */
-	if (oc == NULL			/* We found no live object */
-	    && grace_oc != NULL		/* There is a grace candidate */
-	    && (busy_oc != NULL		/* Somebody else is already busy */
-	    || !VDI_Healthy(sp->director, sp))) {
-					/* Or it is impossible to fetch */
-		o = oc_getobj(sp->wrk, grace_oc);
-		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-		oc = grace_oc;
-	}
-	sp->objcore = NULL;
-
-	if (oc != NULL && !sp->hash_always_miss) {
-		o = oc_getobj(sp->wrk, oc);
-		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-		assert(oc->objhead == oh);
-
-		/* We found an object we like */
-		oc->refcnt++;
-		if (o->hits < INT_MAX)
-			o->hits++;
-		assert(oh->refcnt > 1);
-		Lck_Unlock(&oh->mtx);
-		assert(hash->deref(oh));
-		*poh = oh;
-		return (oc);
-	}
-
-	if (busy_oc != NULL) {
-		/* There are one or more busy objects, wait for them */
-		if (sp->esi_level == 0) {
-			CHECK_OBJ_NOTNULL(sp->wrk->nwaitinglist,
-			    WAITINGLIST_MAGIC);
-			if (oh->waitinglist == NULL) {
-				oh->waitinglist = sp->wrk->nwaitinglist;
-				sp->wrk->nwaitinglist = NULL;
-			}
-			VTAILQ_INSERT_TAIL(&oh->waitinglist->list, sp, list);
-		}
-		if (cache_param->diag_bitmap & 0x20)
-			WSP(sp, SLT_Debug,
-				"on waiting list <%p>", oh);
-		SES_Charge(sp);
-		/*
-		 * The objhead reference transfers to the sess, we get it
-		 * back when the sess comes off the waiting list and
-		 * calls us again
-		 */
-		sp->hash_objhead = oh;
-		sp->wrk = NULL;
-		Lck_Unlock(&oh->mtx);
-		return (NULL);
-	}
-
-	/* Insert (precreated) objcore in objecthead */
-	oc = w->nobjcore;
-	w->nobjcore = NULL;
-	AN(oc->flags & OC_F_BUSY);
-	oc->refcnt = 1;
-
-	/* XXX: clear w->nbusyobj before use */
-	VRY_Validate(sp->vary_b);
-	if (sp->vary_l != NULL)
-		w->nbusyobj->vary = sp->vary_b;
-	else
-		w->nbusyobj->vary = NULL;
-	oc->busyobj = w->nbusyobj;
-	w->nbusyobj = NULL;
-
-	/*
-	 * Busy objects go on the tail, so they will not trip up searches.
-	 * HSH_Unbusy() will move them to the front.
-	 */
-	VTAILQ_INSERT_TAIL(&oh->objcs, oc, list);
-	oc->objhead = oh;
-	/* NB: do not deref objhead the new object inherits our reference */
-	Lck_Unlock(&oh->mtx);
-	*poh = oh;
-	return (oc);
-}
-
-/*---------------------------------------------------------------------
- */
-
-static void
-hsh_rush(struct objhead *oh)
-{
-	unsigned u;
-	struct sess *sp;
-	struct waitinglist *wl;
-
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-	Lck_AssertHeld(&oh->mtx);
-	wl = oh->waitinglist;
-	CHECK_OBJ_NOTNULL(wl, WAITINGLIST_MAGIC);
-	for (u = 0; u < cache_param->rush_exponent; u++) {
-		sp = VTAILQ_FIRST(&wl->list);
-		if (sp == NULL)
-			break;
-		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-		AZ(sp->wrk);
-		VTAILQ_REMOVE(&wl->list, sp, list);
-		DSL(0x20, SLT_Debug, sp->vsl_id, "off waiting list");
-		if (SES_Schedule(sp)) {
-			/*
-			 * We could not schedule the session, leave the
-			 * rest on the busy list.
-			 */
-			break;
-		}
-	}
-	if (VTAILQ_EMPTY(&wl->list)) {
-		oh->waitinglist = NULL;
-		FREE_OBJ(wl);
-	}
-}
-
-/*---------------------------------------------------------------------
- * Purge an entire objhead
- */
-
-void
-HSH_Purge(const struct sess *sp, struct objhead *oh, double ttl, double grace)
-{
-	struct objcore *oc, **ocp;
-	unsigned spc, nobj, n;
-	struct object *o;
-
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-	spc = WS_Reserve(sp->wrk->ws, 0);
-	ocp = (void*)sp->wrk->ws->f;
-	Lck_Lock(&oh->mtx);
-	assert(oh->refcnt > 0);
-	nobj = 0;
-	VTAILQ_FOREACH(oc, &oh->objcs, list) {
-		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-		assert(oc->objhead == oh);
-		if (oc->flags & OC_F_BUSY) {
-			/*
-			 * We cannot purge busy objects here, because their
-			 * owners have special rights to them, and may nuke
-			 * them without concern for the refcount, which by
-			 * definition always must be one, so they don't check.
-			 */
-			continue;
-		}
-
-		(void)oc_getobj(sp->wrk, oc); /* XXX: still needed ? */
-
-		xxxassert(spc >= sizeof *ocp);
-		oc->refcnt++;
-		spc -= sizeof *ocp;
-		ocp[nobj++] = oc;
-	}
-	Lck_Unlock(&oh->mtx);
-
-	/* NB: inverse test to catch NAN also */
-	if (!(ttl > 0.))
-		ttl = -1.;
-	if (!(grace > 0.))
-		grace = -1.;
-	for (n = 0; n < nobj; n++) {
-		oc = ocp[n];
-		CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-		o = oc_getobj(sp->wrk, oc);
-		if (o == NULL)
-			continue;
-		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-		o->exp.ttl = ttl;
-		o->exp.grace = grace;
-		EXP_Rearm(o);
-		(void)HSH_Deref(sp->wrk, NULL, &o);
-	}
-	WS_Release(sp->wrk->ws, 0);
-}
-
-
-/*---------------------------------------------------------------------
- * Kill a busy object we don't need anyway.
- * There may be sessions on the waiting list, so we cannot just blow
- * it out of the water.
- */
-
-void
-HSH_Drop(struct sess *sp)
-{
-	struct object *o;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	o = sp->obj;
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	AssertObjCorePassOrBusy(o->objcore);
-	o->exp.ttl = -1.;
-	if (o->objcore != NULL)		/* Pass has no objcore */
-		HSH_Unbusy(sp);
-	(void)HSH_Deref(sp->wrk, NULL, &sp->obj);
-}
-
-void
-HSH_Unbusy(const struct sess *sp)
-{
-	struct object *o;
-	struct objhead *oh;
-	struct objcore *oc;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	o = sp->obj;
-	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-	oc = o->objcore;
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	oh = oc->objhead;
-	CHECK_OBJ(oh, OBJHEAD_MAGIC);
-
-	AssertObjBusy(o);
-	AN(oc->ban);
-	assert(oc->refcnt > 0);
-	assert(oh->refcnt > 0);
-	if (o->ws_o->overflow)
-		sp->wrk->stats.n_objoverflow++;
-	if (cache_param->diag_bitmap & 0x40)
-		WSP(sp, SLT_Debug,
-		    "Object %u workspace free %u", o->xid, WS_Free(o->ws_o));
-
-	/* XXX: pretouch neighbors on oh->objcs to prevent page-on under mtx */
-	Lck_Lock(&oh->mtx);
-	assert(oh->refcnt > 0);
-	/* XXX: strictly speaking, we should sort in Date: order. */
-	VTAILQ_REMOVE(&oh->objcs, oc, list);
-	VTAILQ_INSERT_HEAD(&oh->objcs, oc, list);
-	oc->flags &= ~OC_F_BUSY;
-	AZ(sp->wrk->nbusyobj);
-	sp->wrk->nbusyobj = oc->busyobj;
-	oc->busyobj = NULL;
-	if (oh->waitinglist != NULL)
-		hsh_rush(oh);
-	AN(oc->ban);
-	Lck_Unlock(&oh->mtx);
-	assert(oc_getobj(sp->wrk, oc) == o);
-}
-
-void
-HSH_Ref(struct objcore *oc)
-{
-	struct objhead *oh;
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-	oh = oc->objhead;
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-	Lck_Lock(&oh->mtx);
-	assert(oc->refcnt > 0);
-	oc->refcnt++;
-	Lck_Unlock(&oh->mtx);
-}
-
-/*--------------------------------------------------------------------
- * Dereference objcore and or object
- *
- * Can deal with:
- *	bare objcore (incomplete fetch)
- *	bare object (pass)
- *	object with objcore
- *	XXX later:  objcore with object (?)
- *
- * But you can only supply one of the two arguments at a time.
- *
- * Returns zero if target was destroyed.
- */
-
-int
-HSH_Deref(struct worker *w, struct objcore *oc, struct object **oo)
-{
-	struct object *o = NULL;
-	struct objhead *oh;
-	unsigned r;
-
-	/* Only one arg at a time */
-	assert(oc == NULL || oo == NULL);
-
-	if (oo != NULL) {
-		o = *oo;
-		*oo = NULL;
-		CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
-		oc = o->objcore;
-	}
-
-	if (o != NULL && oc == NULL) {
-		/*
-		 * A pass object with neither objcore nor objhdr reference.
-		 * -> simply free the (Transient) storage
-		 */
-		STV_Freestore(o);
-		STV_free(o->objstore);
-		w->stats.n_object--;
-		return (0);
-	}
-
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
-
-	oh = oc->objhead;
-	CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
-
-	Lck_Lock(&oh->mtx);
-	assert(oh->refcnt > 0);
-	assert(oc->refcnt > 0);
-	r = --oc->refcnt;
-	if (!r)
-		VTAILQ_REMOVE(&oh->objcs, oc, list);
-	else {
-		/* Must have an object */
-		AN(oc->methods);
-	}
-	if (oh->waitinglist != NULL)
-		hsh_rush(oh);
-	Lck_Unlock(&oh->mtx);
-	if (r != 0)
-		return (r);
-
-	BAN_DestroyObj(oc);
-	AZ(oc->ban);
-
-	if (oc->flags & OC_F_BUSY) {
-		CHECK_OBJ_NOTNULL(oc->busyobj, BUSYOBJ_MAGIC);
-		if (w->nbusyobj == NULL)
-			w->nbusyobj = oc->busyobj;
-		else
-			FREE_OBJ(oc->busyobj);
-		oc->busyobj = NULL;
-	}
-	AZ(oc->busyobj);
-
-	if (oc->methods != NULL) {
-		oc_freeobj(oc);
-		w->stats.n_object--;
-	}
-	FREE_OBJ(oc);
-
-	w->stats.n_objectcore--;
-	/* Drop our ref on the objhead */
-	assert(oh->refcnt > 0);
-	if (hash->deref(oh))
-		return (0);
-	HSH_DeleteObjHead(w, oh);
-	return (0);
-}
-
-void
-HSH_Init(const struct hash_slinger *slinger)
-{
-
-	assert(DIGEST_LEN == SHA256_LEN);	/* avoid #include pollution */
-	hash = slinger;
-	if (hash->start != NULL)
-		hash->start();
-}
diff --git a/bin/varnishd/cache_http.c b/bin/varnishd/cache_http.c
deleted file mode 100644
index 784eb28..0000000
--- a/bin/varnishd/cache_http.c
+++ /dev/null
@@ -1,1119 +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.
- *
- * HTTP request storage and manipulation
- */
-
-#include "config.h"
-
-#include <stdio.h>
-
-#include "cache.h"
-
-#include "vct.h"
-
-#define HTTPH(a, b, c, d, e, f, g) char b[] = "*" a ":";
-#include "tbl/http_headers.h"
-#undef HTTPH
-
-/*lint -save -e773 not () */
-#define LOGMTX2(ax, bx, cx)	[bx] = SLT_##ax##cx
-
-#define LOGMTX1(ax) {					\
-	LOGMTX2(ax, HTTP_HDR_REQ,	Request),	\
-	LOGMTX2(ax, HTTP_HDR_RESPONSE,	Response),	\
-	LOGMTX2(ax, HTTP_HDR_STATUS,	Status),	\
-	LOGMTX2(ax, HTTP_HDR_URL,	URL),		\
-	LOGMTX2(ax, HTTP_HDR_PROTO,	Protocol),	\
-	LOGMTX2(ax, HTTP_HDR_FIRST,	Header),	\
-	}
-
-static const enum VSL_tag_e logmtx[][HTTP_HDR_FIRST + 1] = {
-	[HTTP_Rx] = LOGMTX1(Rx),
-	[HTTP_Tx] = LOGMTX1(Tx),
-	[HTTP_Obj] = LOGMTX1(Obj)
-};
-/*lint -restore */
-
-static enum VSL_tag_e
-http2shmlog(const struct http *hp, int t)
-{
-
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	if (t > HTTP_HDR_FIRST)
-		t = HTTP_HDR_FIRST;
-	assert(hp->logtag >= HTTP_Rx && hp->logtag <= HTTP_Obj); /*lint !e685*/
-	assert(t >= HTTP_HDR_REQ && t <= HTTP_HDR_FIRST);
-	return (logmtx[hp->logtag][t]);
-}
-
-static void
-WSLH(struct worker *w, unsigned vsl_id, const struct http *hp, unsigned hdr)
-{
-
-	AN(vsl_id & (VSL_CLIENTMARKER|VSL_BACKENDMARKER));
-	WSLR(w, http2shmlog(hp, hdr), vsl_id, hp->hd[hdr]);
-}
-
-/*--------------------------------------------------------------------*/
-/* List of canonical HTTP response code names from RFC2616 */
-
-static struct http_msg {
-	unsigned	nbr;
-	const char	*txt;
-} http_msg[] = {
-#define HTTP_RESP(n, t)	{ n, t},
-#include "tbl/http_response.h"
-	{ 0, NULL }
-};
-
-const char *
-http_StatusMessage(unsigned status)
-{
-	struct http_msg *mp;
-
-	assert(status >= 100 && status <= 999);
-	for (mp = http_msg; mp->nbr != 0 && mp->nbr <= status; mp++)
-		if (mp->nbr == status)
-			return (mp->txt);
-	return ("Unknown Error");
-}
-
-/*--------------------------------------------------------------------*/
-
-unsigned
-HTTP_estimate(unsigned nhttp)
-{
-
-	/* XXX: We trust the structs to size-aligned as necessary */
-	return (sizeof (struct http) + (sizeof (txt) + 1) * nhttp);
-}
-
-struct http *
-HTTP_create(void *p, uint16_t nhttp)
-{
-	struct http *hp;
-
-	hp = p;
-	hp->magic = HTTP_MAGIC;
-	hp->hd = (void*)(hp + 1);
-	hp->shd = nhttp;
-	hp->hdf = (void*)(hp->hd + nhttp);
-	return (hp);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_Setup(struct http *hp, struct ws *ws)
-{
-	uint16_t shd;
-	txt *hd;
-	unsigned char *hdf;
-
-	/* XXX: This is not elegant, is it efficient ? */
-	shd = hp->shd;
-	hd = hp->hd;
-	hdf = hp->hdf;
-	memset(hp, 0, sizeof *hp);
-	memset(hd, 0, sizeof *hd * shd);
-	memset(hdf, 0, sizeof *hdf * shd);
-	hp->magic = HTTP_MAGIC;
-	hp->ws = ws;
-	hp->nhd = HTTP_HDR_FIRST;
-	hp->shd = shd;
-	hp->hd = hd;
-	hp->hdf = hdf;
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-http_IsHdr(const txt *hh, const char *hdr)
-{
-	unsigned l;
-
-	Tcheck(*hh);
-	AN(hdr);
-	l = hdr[0];
-	assert(l == strlen(hdr + 1));
-	assert(hdr[l] == ':');
-	hdr++;
-	return (!strncasecmp(hdr, hh->b, l));
-}
-
-/*--------------------------------------------------------------------
- * This function collapses multiple headerlines of the same name.
- * The lines are joined with a comma, according to [rfc2616, 4.2bot, p32]
- */
-
-void
-http_CollectHdr(struct http *hp, const char *hdr)
-{
-	unsigned u, v, ml, f = 0, x;
-	char *b = NULL, *e = NULL;
-
-	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
-		while (u < hp->nhd && http_IsHdr(&hp->hd[u], hdr)) {
-			Tcheck(hp->hd[u]);
-			if (f == 0) {
-				/* Found first header, just record the fact */
-				f = u;
-				break;
-			}
-			if (b == NULL) {
-				/* Found second header, start our collection */
-				ml = WS_Reserve(hp->ws, 0);
-				b = hp->ws->f;
-				e = b + ml;
-				x = Tlen(hp->hd[f]);
-				if (b + x < e) {
-					memcpy(b, hp->hd[f].b, x);
-					b += x;
-				} else
-					b = e;
-			}
-
-			AN(b);
-			AN(e);
-
-			/* Append the Nth header we found */
-			if (b < e)
-				*b++ = ',';
-			x = Tlen(hp->hd[u]) - *hdr;
-			if (b + x < e) {
-				memcpy(b, hp->hd[u].b + *hdr, x);
-				b += x;
-			} else
-				b = e;
-
-			/* Shift remaining headers up one slot */
-			for (v = u; v < hp->nhd - 1; v++)
-				hp->hd[v] = hp->hd[v + 1];
-			hp->nhd--;
-		}
-
-	}
-	if (b == NULL)
-		return;
-	AN(e);
-	if (b >= e) {
-		WS_Release(hp->ws, 0);
-		return;
-	}
-	*b = '\0';
-	hp->hd[f].b = hp->ws->f;
-	hp->hd[f].e = b;
-	WS_ReleaseP(hp->ws, b + 1);
-}
-
-
-/*--------------------------------------------------------------------*/
-
-static unsigned
-http_findhdr(const struct http *hp, unsigned l, const char *hdr)
-{
-	unsigned u;
-
-	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
-		Tcheck(hp->hd[u]);
-		if (hp->hd[u].e < hp->hd[u].b + l + 1)
-			continue;
-		if (hp->hd[u].b[l] != ':')
-			continue;
-		if (strncasecmp(hdr, hp->hd[u].b, l))
-			continue;
-		return (u);
-	}
-	return (0);
-}
-
-int
-http_GetHdr(const struct http *hp, const char *hdr, char **ptr)
-{
-	unsigned u, l;
-	char *p;
-
-	l = hdr[0];
-	diagnostic(l == strlen(hdr + 1));
-	assert(hdr[l] == ':');
-	hdr++;
-	u = http_findhdr(hp, l - 1, hdr);
-	if (u == 0) {
-		if (ptr != NULL)
-			*ptr = NULL;
-		return (0);
-	}
-	if (ptr != NULL) {
-		p = hp->hd[u].b + l;
-		while (vct_issp(*p))
-			p++;
-		*ptr = p;
-	}
-	return (1);
-}
-
-
-/*--------------------------------------------------------------------
- * Find a given data element in a header according to RFC2616's #rule
- * (section 2.1, p15)
- */
-
-int
-http_GetHdrData(const struct http *hp, const char *hdr,
-    const char *field, char **ptr)
-{
-	char *h, *e;
-	unsigned fl;
-
-	if (ptr != NULL)
-		*ptr = NULL;
-	if (!http_GetHdr(hp, hdr, &h))
-		return (0);
-	AN(h);
-	e = strchr(h, '\0');
-	fl = strlen(field);
-	while (h + fl <= e) {
-		/* Skip leading whitespace and commas */
-		if (vct_islws(*h) || *h == ',') {
-			h++;
-			continue;
-		}
-		/* Check for substrings before memcmp() */
-		if ((h + fl == e || vct_issepctl(h[fl])) &&
-		    !memcmp(h, field, fl)) {
-			if (ptr != NULL) {
-				h += fl;
-				while (vct_islws(*h))
-					h++;
-				*ptr = h;
-			}
-			return (1);
-		}
-		/* Skip until end of header or comma */
-		while (*h && *h != ',')
-			h++;
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Find a given headerfields Q value.
- */
-
-double
-http_GetHdrQ(const struct http *hp, const char *hdr, const char *field)
-{
-	char *h;
-	int i;
-	double a, b;
-
-	h = NULL;
-	i = http_GetHdrData(hp, hdr, field, &h);
-	if (!i)
-		return (0.);
-
-	if (h == NULL)
-		return (1.);
-	/* Skip whitespace, looking for '=' */
-	while (*h && vct_issp(*h))
-		h++;
-	if (*h++ != ';')
-		return (1.);
-	while (*h && vct_issp(*h))
-		h++;
-	if (*h++ != 'q')
-		return (1.);
-	while (*h && vct_issp(*h))
-		h++;
-	if (*h++ != '=')
-		return (1.);
-	while (*h && vct_issp(*h))
-		h++;
-	a = 0.;
-	while (vct_isdigit(*h)) {
-		a *= 10.;
-		a += *h - '0';
-		h++;
-	}
-	if (*h++ != '.')
-		return (a);
-	b = .1;
-	while (vct_isdigit(*h)) {
-		a += b * (*h - '0');
-		b *= .1;
-		h++;
-	}
-	return (a);
-}
-
-/*--------------------------------------------------------------------
- * Find a given headerfields value.
- */
-
-int
-http_GetHdrField(const struct http *hp, const char *hdr,
-    const char *field, char **ptr)
-{
-	char *h;
-	int i;
-
-	if (ptr != NULL)
-		*ptr = NULL;
-
-	h = NULL;
-	i = http_GetHdrData(hp, hdr, field, &h);
-	if (!i)
-		return (i);
-
-	if (ptr != NULL && h != NULL) {
-		/* Skip whitespace, looking for '=' */
-		while (*h && vct_issp(*h))
-			h++;
-		if (*h == '=') {
-			h++;
-			while (*h && vct_issp(*h))
-				h++;
-			*ptr = h;
-		}
-	}
-	return (i);
-}
-
-/*--------------------------------------------------------------------
- * XXX: redo with http_GetHdrField() ?
- */
-
-const char *
-http_DoConnection(const struct http *hp)
-{
-	char *p, *q;
-	const char *ret;
-	unsigned u;
-
-	if (!http_GetHdr(hp, H_Connection, &p)) {
-		if (hp->protover < 11)
-			return ("not HTTP/1.1");
-		return (NULL);
-	}
-	ret = NULL;
-	AN(p);
-	for (; *p; p++) {
-		if (vct_issp(*p))
-			continue;
-		if (*p == ',')
-			continue;
-		for (q = p + 1; *q; q++)
-			if (*q == ',' || vct_issp(*q))
-				break;
-		u = pdiff(p, q);
-		if (u == 5 && !strncasecmp(p, "close", u))
-			ret = "Connection: close";
-		u = http_findhdr(hp, u, p);
-		if (u != 0)
-			hp->hdf[u] |= HDF_FILTER;
-		if (!*q)
-			break;
-		p = q;
-	}
-	return (ret);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-http_HdrIs(const struct http *hp, const char *hdr, const char *val)
-{
-	char *p;
-
-	if (!http_GetHdr(hp, hdr, &p))
-		return (0);
-	AN(p);
-	if (!strcasecmp(p, val))
-		return (1);
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-uint16_t
-http_GetStatus(const struct http *hp)
-{
-
-	return (hp->status);
-}
-
-const char *
-http_GetReq(const struct http *hp)
-{
-
-	Tcheck(hp->hd[HTTP_HDR_REQ]);
-	return (hp->hd[HTTP_HDR_REQ].b);
-}
-
-/*--------------------------------------------------------------------
- * Dissect the headers of the HTTP protocol message.
- * Detect conditionals (headers which start with '^[Ii][Ff]-')
- */
-
-static uint16_t
-http_dissect_hdrs(struct worker *w, struct http *hp, unsigned vsl_id, char *p,
-    const struct http_conn *htc)
-{
-	char *q, *r;
-	txt t = htc->rxbuf;
-
-	if (*p == '\r')
-		p++;
-
-	hp->nhd = HTTP_HDR_FIRST;
-	hp->conds = 0;
-	r = NULL;		/* For FlexeLint */
-	for (; p < t.e; p = r) {
-
-		/* Find end of next header */
-		q = r = p;
-		while (r < t.e) {
-			if (!vct_iscrlf(*r)) {
-				r++;
-				continue;
-			}
-			q = r;
-			assert(r < t.e);
-			r += vct_skipcrlf(r);
-			if (r >= t.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) {
-			VSC_C_main->losthdr++;
-			WSL(w, SLT_LostHeader, vsl_id, "%.*s",
-			    q - p > 20 ? 20 : q - p, p);
-			return (413);
-		}
-
-		/* Empty header = end of headers */
-		if (p == q)
-			break;
-
-		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 (hp->nhd < hp->shd) {
-			hp->hdf[hp->nhd] = 0;
-			hp->hd[hp->nhd].b = p;
-			hp->hd[hp->nhd].e = q;
-			WSLH(w, vsl_id, hp, hp->nhd);
-			hp->nhd++;
-		} else {
-			VSC_C_main->losthdr++;
-			WSL(w, SLT_LostHeader, vsl_id, "%.*s",
-			    q - p > 20 ? 20 : q - p, p);
-			return (413);
-		}
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Deal with first line of HTTP protocol message.
- */
-
-static uint16_t
-http_splitline(struct worker *w, unsigned vsl_id, struct http *hp,
-    const struct http_conn *htc, int h1, int h2, int h3)
-{
-	char *p, *q;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-
-	/* XXX: Assert a NUL at rx.e ? */
-	Tcheck(htc->rxbuf);
-
-	/* Skip leading LWS */
-	for (p = htc->rxbuf.b ; vct_islws(*p); p++)
-		continue;
-
-	/* First field cannot contain SP, CRLF or CTL */
-	q = p;
-	for (; !vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[h1].b = q;
-	hp->hd[h1].e = p;
-
-	/* Skip SP */
-	for (; vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-
-	/* Second field cannot contain LWS or CTL */
-	q = p;
-	for (; !vct_islws(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-	hp->hd[h2].b = q;
-	hp->hd[h2].e = p;
-
-	if (!Tlen(hp->hd[h2]))
-		return (413);
-
-	/* Skip SP */
-	for (; vct_issp(*p); p++) {
-		if (vct_isctl(*p))
-			return (400);
-	}
-
-	/* Third field is optional and cannot contain CTL */
-	q = p;
-	if (!vct_iscrlf(*p)) {
-		for (; !vct_iscrlf(*p); p++)
-			if (!vct_issep(*p) && vct_isctl(*p))
-				return (400);
-	}
-	hp->hd[h3].b = q;
-	hp->hd[h3].e = p;
-
-	/* Skip CRLF */
-	p += vct_skipcrlf(p);
-
-	*hp->hd[h1].e = '\0';
-	WSLH(w, vsl_id, hp, h1);
-
-	*hp->hd[h2].e = '\0';
-	WSLH(w, vsl_id, hp, h2);
-
-	if (hp->hd[h3].e != NULL) {
-		*hp->hd[h3].e = '\0';
-		WSLH(w, vsl_id, hp, h3);
-	}
-
-	return (http_dissect_hdrs(w, hp, vsl_id, p, htc));
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-http_ProtoVer(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
-http_DissectRequest(struct sess *sp)
-{
-	struct http_conn *htc;
-	struct http *hp;
-	uint16_t retval;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	htc = sp->htc;
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	hp = sp->http;
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-
-	hp->logtag = HTTP_Rx;
-
-	retval = http_splitline(sp->wrk, sp->vsl_id, hp, htc,
-	    HTTP_HDR_REQ, HTTP_HDR_URL, HTTP_HDR_PROTO);
-	if (retval != 0) {
-		WSPR(sp, SLT_HttpGarbage, htc->rxbuf);
-		return (retval);
-	}
-	http_ProtoVer(hp);
-	return (retval);
-}
-
-/*--------------------------------------------------------------------*/
-
-uint16_t
-http_DissectResponse(struct worker *w, const struct http_conn *htc,
-    struct http *hp)
-{
-	int j;
-	uint16_t retval = 0;
-	char *p;
-
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	hp->logtag = HTTP_Rx;
-
-	if (http_splitline(w, htc->vsl_id, hp, htc,
-	    HTTP_HDR_PROTO, HTTP_HDR_STATUS, HTTP_HDR_RESPONSE))
-		retval = 503;
-
-	if (retval == 0 && memcmp(hp->hd[HTTP_HDR_PROTO].b, "HTTP/1.", 7))
-		retval = 503;
-
-	if (retval == 0 && Tlen(hp->hd[HTTP_HDR_STATUS]) != 3)
-		retval = 503;
-
-	if (retval == 0) {
-		hp->status = 0;
-		p = hp->hd[HTTP_HDR_STATUS].b;
-		for (j = 100; j != 0; j /= 10) {
-			if (!vct_isdigit(*p)) {
-				retval = 503;
-				break;
-			}
-			hp->status += (uint16_t)(j * (*p - '0'));
-			p++;
-		}
-		if (*p != '\0')
-			retval = 503;
-	}
-
-	if (retval != 0) {
-		WSLR(w, SLT_HttpGarbage, htc->vsl_id, htc->rxbuf);
-		assert(retval >= 100 && retval <= 999);
-		hp->status = retval;
-	} else {
-		http_ProtoVer(hp);
-	}
-
-	if (hp->hd[HTTP_HDR_RESPONSE].b == NULL ||
-	    !Tlen(hp->hd[HTTP_HDR_RESPONSE])) {
-		/* Backend didn't send a response string, use the standard */
-		hp->hd[HTTP_HDR_RESPONSE].b =
-		    TRUST_ME(http_StatusMessage(hp->status));
-		hp->hd[HTTP_HDR_RESPONSE].e =
-		    strchr(hp->hd[HTTP_HDR_RESPONSE].b, '\0');
-	}
-	return (retval);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_SetH(const struct http *to, unsigned n, const char *fm)
-{
-
-	assert(n < to->shd);
-	AN(fm);
-	to->hd[n].b = TRUST_ME(fm);
-	to->hd[n].e = strchr(to->hd[n].b, '\0');
-	to->hdf[n] = 0;
-}
-
-static void
-http_copyh(const struct http *to, const struct http *fm, unsigned n)
-{
-
-	assert(n < HTTP_HDR_FIRST);
-	Tcheck(fm->hd[n]);
-	to->hd[n] = fm->hd[n];
-	to->hdf[n] = fm->hdf[n];
-}
-
-void
-http_ForceGet(const struct http *to)
-{
-	if (strcmp(http_GetReq(to), "GET"))
-		http_SetH(to, HTTP_HDR_REQ, "GET");
-}
-
-void
-http_CopyResp(struct http *to, const struct http *fm)
-{
-
-	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	http_SetH(to, HTTP_HDR_PROTO, "HTTP/1.1");
-	to->status = fm->status;
-	http_copyh(to, fm, HTTP_HDR_RESPONSE);
-}
-
-void
-http_SetResp(struct http *to, const char *proto, uint16_t status,
-    const char *response)
-{
-
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	http_SetH(to, HTTP_HDR_PROTO, proto);
-	assert(status >= 100 && status <= 999);
-	to->status = status;
-	http_SetH(to, HTTP_HDR_RESPONSE, response);
-}
-
-static void
-http_copyheader(struct worker *w, unsigned vsl_id, struct http *to,
-    const struct http *fm, unsigned n)
-{
-
-	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	assert(n < fm->shd);
-	Tcheck(fm->hd[n]);
-	if (to->nhd < to->shd) {
-		to->hd[to->nhd] = fm->hd[n];
-		to->hdf[to->nhd] = 0;
-		to->nhd++;
-	} else  {
-		VSC_C_main->losthdr++;
-		WSLR(w, SLT_LostHeader, vsl_id, fm->hd[n]);
-	}
-}
-
-/*--------------------------------------------------------------------
- * Estimate how much workspace we need to Filter this header according
- * to 'how'.
- */
-
-unsigned
-http_EstimateWS(const struct http *fm, unsigned how, uint16_t *nhd)
-{
-	unsigned u, l;
-
-	l = 0;
-	*nhd = HTTP_HDR_FIRST;
-	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
-	for (u = 0; u < fm->nhd; u++) {
-		if (fm->hd[u].b == NULL)
-			continue;
-		if (fm->hdf[u] & HDF_FILTER)
-			continue;
-#define HTTPH(a, b, c, d, e, f, g) \
-		if (((e) & how) && http_IsHdr(&fm->hd[u], (b))) \
-			continue;
-#include "tbl/http_headers.h"
-#undef HTTPH
-		l += PRNDUP(Tlen(fm->hd[u]) + 1);
-		(*nhd)++;
-		// fm->hdf[u] |= HDF_COPY;
-	}
-	return (l);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_FilterFields(struct worker *w, unsigned vsl_id, struct http *to,
-    const struct http *fm, unsigned how)
-{
-	unsigned u;
-
-	CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	to->nhd = HTTP_HDR_FIRST;
-	to->status = fm->status;
-	for (u = HTTP_HDR_FIRST; u < fm->nhd; u++) {
-		if (fm->hd[u].b == NULL)
-			continue;
-		if (fm->hdf[u] & HDF_FILTER)
-			continue;
-#define HTTPH(a, b, c, d, e, f, g) \
-		if (((e) & how) && http_IsHdr(&fm->hd[u], (b))) \
-			continue;
-#include "tbl/http_headers.h"
-#undef HTTPH
-		http_copyheader(w, vsl_id, to, fm, u);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_FilterHeader(const struct sess *sp, unsigned how)
-{
-	struct http *hp;
-
-	hp = sp->wrk->bereq;
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	hp->logtag = HTTP_Tx;
-
-	http_copyh(hp, sp->http, HTTP_HDR_REQ);
-	http_copyh(hp, sp->http, HTTP_HDR_URL);
-	if (how == HTTPH_R_FETCH)
-		http_SetH(hp, HTTP_HDR_PROTO, "HTTP/1.1");
-	else
-		http_copyh(hp, sp->http, HTTP_HDR_PROTO);
-	http_FilterFields(sp->wrk, sp->vsl_id, hp, sp->http, how);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, hp, "X-Varnish: %u", sp->xid);
-}
-
-/*--------------------------------------------------------------------
- * This function copies any header fields which reference foreign
- * storage into our own WS.
- */
-
-void
-http_CopyHome(struct worker *w, unsigned vsl_id, const struct http *hp)
-{
-	unsigned u, l;
-	char *p;
-
-	for (u = 0; u < hp->nhd; u++) {
-		if (hp->hd[u].b == NULL)
-			continue;
-		if (hp->hd[u].b >= hp->ws->s && hp->hd[u].e <= hp->ws->e) {
-			WSLH(w, vsl_id, hp, u);
-			continue;
-		}
-		l = Tlen(hp->hd[u]);
-		p = WS_Alloc(hp->ws, l + 1);
-		if (p != NULL) {
-			WSLH(w, vsl_id, hp, u);
-			memcpy(p, hp->hd[u].b, l + 1L);
-			hp->hd[u].b = p;
-			hp->hd[u].e = p + l;
-		} else {
-			/* XXX This leaves a slot empty */
-			VSC_C_main->losthdr++;
-			WSLR(w, SLT_LostHeader, vsl_id, hp->hd[u]);
-			hp->hd[u].b = NULL;
-			hp->hd[u].e = NULL;
-		}
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_ClrHeader(struct http *to)
-{
-
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	to->nhd = HTTP_HDR_FIRST;
-	to->status = 0;
-	to->protover = 0;
-	to->conds = 0;
-	memset(to->hd, 0, sizeof *to->hd * to->shd);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-http_SetHeader(struct worker *w, unsigned vsl_id, struct http *to,
-    const char *hdr)
-{
-
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	if (to->nhd >= to->shd) {
-		VSC_C_main->losthdr++;
-		WSL(w, SLT_LostHeader, vsl_id, "%s", hdr);
-		return;
-	}
-	http_SetH(to, to->nhd++, hdr);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-http_PutField(struct worker *w, unsigned vsl_id, const struct http *to,
-    int field, const char *string)
-{
-	char *p;
-	unsigned l;
-
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	l = strlen(string);
-	p = WS_Alloc(to->ws, l + 1);
-	if (p == NULL) {
-		WSL(w, SLT_LostHeader, vsl_id, "%s", string);
-		to->hd[field].b = NULL;
-		to->hd[field].e = NULL;
-		to->hdf[field] = 0;
-	} else {
-		memcpy(p, string, l + 1L);
-		to->hd[field].b = p;
-		to->hd[field].e = p + l;
-		to->hdf[field] = 0;
-	}
-}
-
-void
-http_PutProtocol(struct worker *w, unsigned vsl_id, const struct http *to,
-    const char *protocol)
-{
-
-	http_PutField(w, vsl_id, to, HTTP_HDR_PROTO, protocol);
-	if (to->hd[HTTP_HDR_PROTO].b == NULL)
-		http_SetH(to, HTTP_HDR_PROTO, "HTTP/1.1");
-	Tcheck(to->hd[HTTP_HDR_PROTO]);
-}
-
-void
-http_PutStatus(struct http *to, uint16_t status)
-{
-
-	assert(status >= 100 && status <= 999);
-	to->status = status;
-}
-
-void
-http_PutResponse(struct worker *w, unsigned vsl_id, const struct http *to,
-    const char *response)
-{
-
-	http_PutField(w, vsl_id, to, HTTP_HDR_RESPONSE, response);
-	if (to->hd[HTTP_HDR_RESPONSE].b == NULL)
-		http_SetH(to, HTTP_HDR_RESPONSE, "Lost Response");
-	Tcheck(to->hd[HTTP_HDR_RESPONSE]);
-}
-
-void
-http_PrintfHeader(struct worker *w, unsigned vsl_id, struct http *to,
-    const char *fmt, ...)
-{
-	va_list ap;
-	unsigned l, n;
-
-	CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
-	l = WS_Reserve(to->ws, 0);
-	va_start(ap, fmt);
-	n = vsnprintf(to->ws->f, l, fmt, ap);
-	va_end(ap);
-	if (n + 1 >= l || to->nhd >= to->shd) {
-		VSC_C_main->losthdr++;
-		WSL(w, SLT_LostHeader, vsl_id, "%s", to->ws->f);
-		WS_Release(to->ws, 0);
-	} else {
-		to->hd[to->nhd].b = to->ws->f;
-		to->hd[to->nhd].e = to->ws->f + n;
-		to->hdf[to->nhd] = 0;
-		WS_Release(to->ws, n + 1);
-		to->nhd++;
-	}
-}
-/*--------------------------------------------------------------------*/
-
-void
-http_Unset(struct http *hp, const char *hdr)
-{
-	uint16_t u, v;
-
-	for (v = u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
-		if (hp->hd[u].b == NULL)
-			continue;
-		if (http_IsHdr(&hp->hd[u], hdr))
-			continue;
-		if (v != u) {
-			memcpy(&hp->hd[v], &hp->hd[u], sizeof *hp->hd);
-			memcpy(&hp->hdf[v], &hp->hdf[u], sizeof *hp->hdf);
-		}
-		v++;
-	}
-	hp->nhd = v;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-HTTP_Copy(struct http *to, const struct http * const fm)
-{
-
-	to->conds = fm->conds;
-	to->logtag = fm->logtag;
-	to->status = fm->status;
-	to->protover = fm->protover;
-	to->nhd = fm->nhd;
-	assert(fm->nhd <= to->shd);
-	memcpy(to->hd, fm->hd, fm->nhd * sizeof *to->hd);
-	memcpy(to->hdf, fm->hdf, fm->nhd * sizeof *to->hdf);
-}
-
-/*--------------------------------------------------------------------*/
-
-unsigned
-http_Write(struct worker *w, unsigned vsl_id, const struct http *hp, int resp)
-{
-	unsigned u, l;
-
-	if (resp) {
-		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], " ");
-		WSLH(w, vsl_id, hp, HTTP_HDR_PROTO);
-
-		hp->hd[HTTP_HDR_STATUS].b = WS_Alloc(w->ws, 4);
-		AN(hp->hd[HTTP_HDR_STATUS].b);
-
-		sprintf(hp->hd[HTTP_HDR_STATUS].b, "%3d", hp->status);
-		hp->hd[HTTP_HDR_STATUS].e = hp->hd[HTTP_HDR_STATUS].b + 3;
-
-		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_STATUS], " ");
-		WSLH(w, vsl_id, hp, HTTP_HDR_STATUS);
-
-		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_RESPONSE], "\r\n");
-		WSLH(w, vsl_id, hp, HTTP_HDR_RESPONSE);
-	} else {
-		AN(hp->hd[HTTP_HDR_URL].b);
-		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_REQ], " ");
-		WSLH(w, vsl_id, hp, HTTP_HDR_REQ);
-		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_URL], " ");
-		WSLH(w, vsl_id, hp, HTTP_HDR_URL);
-		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], "\r\n");
-		WSLH(w, vsl_id, hp, HTTP_HDR_PROTO);
-	}
-	for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
-		if (hp->hd[u].b == NULL)
-			continue;
-		AN(hp->hd[u].b);
-		AN(hp->hd[u].e);
-		l += WRW_WriteH(w, &hp->hd[u], "\r\n");
-		WSLH(w, vsl_id, hp, u);
-	}
-	l += WRW_Write(w, "\r\n", -1);
-	return (l);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-HTTP_Init(void)
-{
-
-#define HTTPH(a, b, c, d, e, f, g) b[0] = (char)strlen(b + 1);
-#include "tbl/http_headers.h"
-#undef HTTPH
-}
diff --git a/bin/varnishd/cache_httpconn.c b/bin/varnishd/cache_httpconn.c
deleted file mode 100644
index 9e0a052..0000000
--- a/bin/varnishd/cache_httpconn.c
+++ /dev/null
@@ -1,229 +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.
- *
- * 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.
- *
- * We use this both for client and backend connections.
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "vct.h"
-
-/*--------------------------------------------------------------------
- * Check if we have a complete HTTP request or response yet
- *
- * Return values:
- *	 0  No, keep trying
- *	>0  Yes, it is this many bytes long.
- */
-
-static int
-htc_header_complete(txt *t)
-{
-	const char *p;
-
-	Tcheck(*t);
-	assert(*t->e == '\0');
-	/* Skip any leading white space */
-	for (p = t->b ; vct_issp(*p); p++)
-		continue;
-	if (p == t->e) {
-		/* All white space */
-		t->e = t->b;
-		*t->e = '\0';
-		return (0);
-	}
-	while (1) {
-		p = strchr(p, '\n');
-		if (p == NULL)
-			return (0);
-		p++;
-		if (*p == '\r')
-			p++;
-		if (*p == '\n')
-			break;
-	}
-	p++;
-	return (p - t->b);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-HTC_Init(struct http_conn *htc, struct ws *ws, int fd, unsigned vsl_id,
-    unsigned maxbytes, unsigned maxhdr)
-{
-
-	htc->magic = HTTP_CONN_MAGIC;
-	htc->ws = ws;
-	htc->fd = fd;
-	htc->vsl_id = vsl_id;
-	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.
- */
-
-int
-HTC_Reinit(struct http_conn *htc)
-{
-	unsigned 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 = Tlen(htc->pipeline);
-		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 (HTC_Complete(htc));
-}
-
-/*--------------------------------------------------------------------
- * Return 1 if we have a complete HTTP procol header
- */
-
-int
-HTC_Complete(struct http_conn *htc)
-{
-	int i;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	i = htc_header_complete(&htc->rxbuf);
-	assert(i >= 0);
-	if (i == 0)
-		return (0);
-	WS_ReleaseP(htc->ws, htc->rxbuf.e);
-	AZ(htc->pipeline.b);
-	AZ(htc->pipeline.e);
-	if (htc->rxbuf.b + i < htc->rxbuf.e) {
-		htc->pipeline.b = htc->rxbuf.b + i;
-		htc->pipeline.e = htc->rxbuf.e;
-		htc->rxbuf.e = htc->pipeline.b;
-	}
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * Receive more HTTP protocol bytes
- * Returns:
- *	-2 overflow
- *	-1 error
- *	 0 more needed
- *	 1 got complete HTTP header
- */
-
-int
-HTC_Rx(struct http_conn *htc)
-{
-	int i;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	AN(htc->ws->r);
-	i = (htc->ws->r - htc->rxbuf.e) - 1;	/* space for NUL */
-	if (i <= 0) {
-		WS_ReleaseP(htc->ws, htc->rxbuf.b);
-		return (-2);
-	}
-	i = read(htc->fd, htc->rxbuf.e, i);
-	if (i <= 0) {
-		/*
-		 * We wouldn't come here if we had a complete HTTP header
-		 * so consequently an EOF can not be OK
-		 */
-		WS_ReleaseP(htc->ws, htc->rxbuf.b);
-		return (-1);
-	}
-	htc->rxbuf.e += i;
-	*htc->rxbuf.e = '\0';
-	return (HTC_Complete(htc));
-}
-
-/*--------------------------------------------------------------------
- * Read up to len bytes, returning pipelined data first.
- */
-
-ssize_t
-HTC_Read(struct worker *w, struct http_conn *htc, void *d, size_t len)
-{
-	size_t l;
-	unsigned char *p;
-	ssize_t i;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	l = 0;
-	p = d;
-	if (htc->pipeline.b) {
-		l = Tlen(htc->pipeline);
-		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)
-		return (l);
-	i = read(htc->fd, p, len);
-	if (i < 0) {
-		WSL(w, SLT_FetchError, htc->vsl_id, "%s", strerror(errno));
-		return (i);
-	}
-	return (i + l);
-}
diff --git a/bin/varnishd/cache_lck.c b/bin/varnishd/cache_lck.c
deleted file mode 100644
index 2aef6dc..0000000
--- a/bin/varnishd/cache_lck.c
+++ /dev/null
@@ -1,210 +0,0 @@
-/*-
- * Copyright (c) 2008-2010 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.
- *
- * The geniuses who came up with pthreads did not think operations like
- * pthread_assert_mutex_held() were important enough to include them in
- * the API.
- *
- * Build our own locks on top of pthread mutexes and hope that the next
- * civilization is better at such crucial details than this one.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "cache.h"
-
-/*The constability of lck depends on platform pthreads implementation */
-
-struct ilck {
-	unsigned		magic;
-#define ILCK_MAGIC		0x7b86c8a5
-	pthread_mutex_t		mtx;
-	int			held;
-	pthread_t		owner;
-	VTAILQ_ENTRY(ilck)	list;
-	const char		*w;
-	struct VSC_C_lck	*stat;
-};
-
-static VTAILQ_HEAD(, ilck)	ilck_head =
-    VTAILQ_HEAD_INITIALIZER(ilck_head);
-
-static pthread_mutex_t		lck_mtx;
-
-void __match_proto__()
-Lck__Lock(struct lock *lck, const char *p, const char *f, int l)
-{
-	struct ilck *ilck;
-	int r;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	if (!(cache_param->diag_bitmap & 0x18)) {
-		AZ(pthread_mutex_lock(&ilck->mtx));
-		AZ(ilck->held);
-		ilck->stat->locks++;
-		ilck->owner = pthread_self();
-		ilck->held = 1;
-		return;
-	}
-	r = pthread_mutex_trylock(&ilck->mtx);
-	assert(r == 0 || r == EBUSY);
-	if (r) {
-		ilck->stat->colls++;
-		if (cache_param->diag_bitmap & 0x8)
-			VSL(SLT_Debug, 0, "MTX_CONTEST(%s,%s,%d,%s)",
-			    p, f, l, ilck->w);
-		AZ(pthread_mutex_lock(&ilck->mtx));
-	} else if (cache_param->diag_bitmap & 0x8) {
-		VSL(SLT_Debug, 0, "MTX_LOCK(%s,%s,%d,%s)", p, f, l, ilck->w);
-	}
-	AZ(ilck->held);
-	ilck->stat->locks++;
-	ilck->owner = pthread_self();
-	ilck->held = 1;
-}
-
-void __match_proto__()
-Lck__Unlock(struct lock *lck, const char *p, const char *f, int l)
-{
-	struct ilck *ilck;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	assert(pthread_equal(ilck->owner, pthread_self()));
-	AN(ilck->held);
-	ilck->held = 0;
-	AZ(pthread_mutex_unlock(&ilck->mtx));
-	if (cache_param->diag_bitmap & 0x8)
-		VSL(SLT_Debug, 0, "MTX_UNLOCK(%s,%s,%d,%s)", p, f, l, ilck->w);
-}
-
-int __match_proto__()
-Lck__Trylock(struct lock *lck, const char *p, const char *f, int l)
-{
-	struct ilck *ilck;
-	int r;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	r = pthread_mutex_trylock(&ilck->mtx);
-	assert(r == 0 || r == EBUSY);
-	if (cache_param->diag_bitmap & 0x8)
-		VSL(SLT_Debug, 0,
-		    "MTX_TRYLOCK(%s,%s,%d,%s) = %d", p, f, l, ilck->w);
-	if (r == 0) {
-		AZ(ilck->held);
-		ilck->held = 1;
-		ilck->owner = pthread_self();
-	}
-	return (r);
-}
-
-void
-Lck__Assert(const struct lock *lck, int held)
-{
-	struct ilck *ilck;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	if (held)
-		assert(ilck->held &&
-		    pthread_equal(ilck->owner, pthread_self()));
-	else
-		assert(!ilck->held ||
-		    !pthread_equal(ilck->owner, pthread_self()));
-}
-
-int __match_proto__()
-Lck_CondWait(pthread_cond_t *cond, struct lock *lck, struct timespec *ts)
-{
-	struct ilck *ilck;
-	int retval = 0;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	AN(ilck->held);
-	assert(pthread_equal(ilck->owner, pthread_self()));
-	ilck->held = 0;
-	if (ts == NULL) {
-		AZ(pthread_cond_wait(cond, &ilck->mtx));
-	} else {
-		retval = pthread_cond_timedwait(cond, &ilck->mtx, ts);
-		assert(retval == 0 || retval == ETIMEDOUT);
-	}
-	AZ(ilck->held);
-	ilck->held = 1;
-	ilck->owner = pthread_self();
-	return (retval);
-}
-
-void
-Lck__New(struct lock *lck, struct VSC_C_lck *st, const char *w)
-{
-	struct ilck *ilck;
-
-	AN(st);
-	AZ(lck->priv);
-	ALLOC_OBJ(ilck, ILCK_MAGIC);
-	AN(ilck);
-	ilck->w = w;
-	ilck->stat = st;
-	ilck->stat->creat++;
-	AZ(pthread_mutex_init(&ilck->mtx, NULL));
-	AZ(pthread_mutex_lock(&lck_mtx));
-	VTAILQ_INSERT_TAIL(&ilck_head, ilck, list);
-	AZ(pthread_mutex_unlock(&lck_mtx));
-	lck->priv = ilck;
-}
-
-void
-Lck_Delete(struct lock *lck)
-{
-	struct ilck *ilck;
-
-	CAST_OBJ_NOTNULL(ilck, lck->priv, ILCK_MAGIC);
-	ilck->stat->destroy++;
-	lck->priv = NULL;
-	AZ(pthread_mutex_lock(&lck_mtx));
-	VTAILQ_REMOVE(&ilck_head, ilck, list);
-	AZ(pthread_mutex_unlock(&lck_mtx));
-	AZ(pthread_mutex_destroy(&ilck->mtx));
-	FREE_OBJ(ilck);
-}
-
-#define LOCK(nam) struct VSC_C_lck *lck_##nam;
-#include "tbl/locks.h"
-#undef LOCK
-
-void
-LCK_Init(void)
-{
-
-	AZ(pthread_mutex_init(&lck_mtx, NULL));
-#define LOCK(nam)						\
-	lck_##nam = VSM_Alloc(sizeof(struct VSC_C_lck),		\
-	   VSC_CLASS, VSC_TYPE_LCK, #nam);
-#include "tbl/locks.h"
-#undef LOCK
-}
diff --git a/bin/varnishd/cache_main.c b/bin/varnishd/cache_main.c
deleted file mode 100644
index eb3fa1d..0000000
--- a/bin/varnishd/cache_main.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2009 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 <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-#include "common/heritage.h"
-
-#include "waiter/cache_waiter.h"
-#include "hash/hash_slinger.h"
-
-volatile struct params	*cache_param;
-
-/*--------------------------------------------------------------------
- * Per thread storage for the session currently being processed by
- * the thread.  This is used for panic messages.
- */
-
-static pthread_key_t sp_key;
-
-void
-THR_SetSession(const struct sess *sp)
-{
-
-	AZ(pthread_setspecific(sp_key, sp));
-}
-
-const struct sess *
-THR_GetSession(void)
-{
-
-	return (pthread_getspecific(sp_key));
-}
-
-/*--------------------------------------------------------------------
- * Name threads if our pthreads implementation supports it.
- */
-
-static pthread_key_t name_key;
-
-void
-THR_SetName(const char *name)
-{
-
-	AZ(pthread_setspecific(name_key, name));
-#ifdef HAVE_PTHREAD_SET_NAME_NP
-	pthread_set_name_np(pthread_self(), name);
-#endif
-}
-
-const char *
-THR_GetName(void)
-{
-
-	return (pthread_getspecific(name_key));
-}
-
-/*--------------------------------------------------------------------
- * XXX: Think more about which order we start things
- */
-
-void
-child_main(void)
-{
-
-	setbuf(stdout, NULL);
-	setbuf(stderr, NULL);
-	printf("Child starts\n");
-
-	AZ(pthread_key_create(&sp_key, NULL));
-	AZ(pthread_key_create(&name_key, NULL));
-
-	THR_SetName("cache-main");
-
-	VSL_Init();	/* First, LCK needs it. */
-
-	LCK_Init();	/* Second, locking */
-
-	WAIT_Init();
-	PAN_Init();
-	CLI_Init();
-	Fetch_Init();
-
-	CNT_Init();
-	VCL_Init();
-
-	HTTP_Init();
-
-	VBE_Init();
-	VBP_Init();
-	WRK_Init();
-	Pool_Init();
-
-	EXP_Init();
-	HSH_Init(heritage.hash);
-	BAN_Init();
-
-	VCA_Init();
-
-	SMS_Init();
-	SMP_Init();
-	STV_open();
-
-	VMOD_Init();
-
-	BAN_Compile();
-
-	/* Wait for persistent storage to load if asked to */
-	if (cache_param->diag_bitmap & 0x00020000)
-		SMP_Ready();
-
-	CLI_Run();
-
-	STV_close();
-
-	printf("Child dies\n");
-}
diff --git a/bin/varnishd/cache_panic.c b/bin/varnishd/cache_panic.c
deleted file mode 100644
index c626ae3..0000000
--- a/bin/varnishd/cache_panic.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2011 Varnish Software AS
- * All rights reserved.
- *
- * Author: Dag-Erling Smørgrav <des at des.no>
- *
- * 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"
-
-#ifndef HAVE_EXECINFO_H
-#include "compat/execinfo.h"
-#else
-#include <execinfo.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "vapi/vsm_int.h"
-
-#include "cache_backend.h"
-#include "waiter/cache_waiter.h"
-#include "vcl.h"
-
-/*
- * The panic string is constructed in memory, then copied to the
- * shared memory.
- *
- * It can be extracted post-mortem from a core dump using gdb:
- *
- * (gdb) printf "%s", panicstr
- */
-
-static struct vsb vsps, *vsp;
-static pthread_mutex_t panicstr_mtx = PTHREAD_MUTEX_INITIALIZER;
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_ws(const struct ws *ws, int indent)
-{
-
-	VSB_printf(vsp, "%*sws = %p { %s\n", indent, "",
-	    ws, ws->overflow ? "overflow" : "");
-	VSB_printf(vsp, "%*sid = \"%s\",\n", indent + 2, "", ws->id);
-	VSB_printf(vsp, "%*s{s,f,r,e} = {%p", indent + 2, "", ws->s);
-	if (ws->f > ws->s)
-		VSB_printf(vsp, ",+%ld", (long) (ws->f - ws->s));
-	else
-		VSB_printf(vsp, ",%p", ws->f);
-	if (ws->r > ws->s)
-		VSB_printf(vsp, ",+%ld", (long) (ws->r - ws->s));
-	else
-		VSB_printf(vsp, ",%p", ws->r);
-	if (ws->e > ws->s)
-		VSB_printf(vsp, ",+%ld", (long) (ws->e - ws->s));
-	else
-		VSB_printf(vsp, ",%p", ws->e);
-	VSB_printf(vsp, "},\n");
-	VSB_printf(vsp, "%*s},\n", indent, "" );
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_vbc(const struct vbc *vbc)
-{
-
-	struct backend *be;
-
-	be = vbc->backend;
-
-	VSB_printf(vsp, "  backend = %p fd = %d {\n", be, vbc->fd);
-	VSB_printf(vsp, "    display_name = \"%s\",\n", be->display_name);
-	VSB_printf(vsp, "  },\n");
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_storage(const struct storage *st)
-{
-	int i, j;
-
-#define MAX_BYTES (4*16)
-#define show(ch) (((ch) > 31 && (ch) < 127) ? (ch) : '.')
-
-	VSB_printf(vsp, "      %u {\n", st->len);
-	for (i = 0; i < MAX_BYTES && i < st->len; i += 16) {
-		VSB_printf(vsp, "        ");
-		for (j = 0; j < 16; ++j) {
-			if (i + j < st->len)
-				VSB_printf(vsp, "%02x ", st->ptr[i + j]);
-			else
-				VSB_printf(vsp, "   ");
-		}
-		VSB_printf(vsp, "|");
-		for (j = 0; j < 16; ++j)
-			if (i + j < st->len)
-				VSB_printf(vsp, "%c", show(st->ptr[i + j]));
-		VSB_printf(vsp, "|\n");
-	}
-	if (st->len > MAX_BYTES)
-		VSB_printf(vsp, "        [%u more]\n", st->len - MAX_BYTES);
-	VSB_printf(vsp, "      },\n");
-
-#undef show
-#undef MAX_BYTES
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_http(const char *id, const struct http *h, int indent)
-{
-	int i;
-
-	VSB_printf(vsp, "%*shttp[%s] = {\n", indent, "", id);
-	VSB_printf(vsp, "%*sws = %p[%s]\n", indent + 2, "",
-	    h->ws, h->ws ? h->ws->id : "");
-	for (i = 0; i < h->nhd; ++i) {
-		if (h->hd[i].b == NULL && h->hd[i].e == NULL)
-			continue;
-		VSB_printf(vsp, "%*s\"%.*s\",\n", indent + 4, "",
-		    (int)(h->hd[i].e - h->hd[i].b),
-		    h->hd[i].b);
-	}
-	VSB_printf(vsp, "%*s},\n", indent, "");
-}
-
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_object(const struct object *o)
-{
-	const struct storage *st;
-
-	VSB_printf(vsp, "  obj = %p {\n", o);
-	VSB_printf(vsp, "    xid = %u,\n", o->xid);
-	pan_ws(o->ws_o, 4);
-	pan_http("obj", o->http, 4);
-	VSB_printf(vsp, "    len = %jd,\n", (intmax_t)o->len);
-	VSB_printf(vsp, "    store = {\n");
-	VTAILQ_FOREACH(st, &o->store, list)
-		pan_storage(st);
-	VSB_printf(vsp, "    },\n");
-	VSB_printf(vsp, "  },\n");
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_vcl(const struct VCL_conf *vcl)
-{
-	int i;
-
-	VSB_printf(vsp, "    vcl = {\n");
-	VSB_printf(vsp, "      srcname = {\n");
-	for (i = 0; i < vcl->nsrc; ++i)
-		VSB_printf(vsp, "        \"%s\",\n", vcl->srcname[i]);
-	VSB_printf(vsp, "      },\n");
-	VSB_printf(vsp, "    },\n");
-}
-
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_wrk(const struct worker *wrk)
-{
-
-	VSB_printf(vsp, "  worker = %p {\n", wrk);
-	pan_ws(wrk->ws, 4);
-	if (wrk->bereq->ws != NULL)
-		pan_http("bereq", wrk->bereq, 4);
-	if (wrk->beresp->ws != NULL)
-		pan_http("beresp", wrk->beresp, 4);
-	if (wrk->resp->ws != NULL)
-		pan_http("resp", wrk->resp, 4);
-	VSB_printf(vsp, "    },\n");
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_sess(const struct sess *sp)
-{
-	const char *stp, *hand;
-
-	VSB_printf(vsp, "sp = %p {\n", sp);
-	VSB_printf(vsp,
-	    "  fd = %d, id = %d, xid = %u,\n",
-	    sp->fd, sp->vsl_id & VSL_IDENTMASK, sp->xid);
-	VSB_printf(vsp, "  client = %s %s,\n",
-	    sp->addr ? sp->addr : "?.?.?.?",
-	    sp->port ? sp->port : "?");
-	switch (sp->step) {
-#define STEP(l, u) case STP_##u: stp = "STP_" #u; break;
-#include "tbl/steps.h"
-#undef STEP
-		default: stp = NULL;
-	}
-	hand = VCL_Return_Name(sp->handling);
-	if (stp != NULL)
-		VSB_printf(vsp, "  step = %s,\n", stp);
-	else
-		VSB_printf(vsp, "  step = 0x%x,\n", sp->step);
-	if (hand != NULL)
-		VSB_printf(vsp, "  handling = %s,\n", hand);
-	else
-		VSB_printf(vsp, "  handling = 0x%x,\n", sp->handling);
-	if (sp->err_code)
-		VSB_printf(vsp,
-		    "  err_code = %d, err_reason = %s,\n", sp->err_code,
-		    sp->err_reason ? sp->err_reason : "(null)");
-
-	VSB_printf(vsp, "  restarts = %d, esi_level = %d\n",
-	    sp->restarts, sp->esi_level);
-
-	VSB_printf(vsp, "  flags = ");
-	if (sp->wrk->do_stream)	VSB_printf(vsp, " do_stream");
-	if (sp->wrk->do_gzip)	VSB_printf(vsp, " do_gzip");
-	if (sp->wrk->do_gunzip)	VSB_printf(vsp, " do_gunzip");
-	if (sp->wrk->do_esi)	VSB_printf(vsp, " do_esi");
-	if (sp->wrk->do_close)	VSB_printf(vsp, " do_close");
-	if (sp->wrk->is_gzip)	VSB_printf(vsp, " is_gzip");
-	if (sp->wrk->is_gunzip)	VSB_printf(vsp, " is_gunzip");
-	VSB_printf(vsp, "\n");
-	VSB_printf(vsp, "  bodystatus = %d\n", sp->wrk->body_status);
-
-	pan_ws(sp->ws, 2);
-	pan_http("req", sp->http, 2);
-
-	if (sp->wrk != NULL)
-		pan_wrk(sp->wrk);
-
-	if (VALID_OBJ(sp->vcl, VCL_CONF_MAGIC))
-		pan_vcl(sp->vcl);
-
-	if (VALID_OBJ(sp->wrk->vbc, BACKEND_MAGIC))
-		pan_vbc(sp->wrk->vbc);
-
-	if (VALID_OBJ(sp->obj, OBJECT_MAGIC))
-		pan_object(sp->obj);
-
-	VSB_printf(vsp, "},\n");
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_backtrace(void)
-{
-	void *array[10];
-	size_t size;
-	size_t i;
-
-	size = backtrace (array, 10);
-	if (size == 0)
-		return;
-	VSB_printf(vsp, "Backtrace:\n");
-	for (i = 0; i < size; i++) {
-		VSB_printf (vsp, "  ");
-		if (Symbol_Lookup(vsp, array[i]) < 0) {
-			char **strings;
-			strings = backtrace_symbols(&array[i], 1);
-			if (strings != NULL && strings[0] != NULL)
-				VSB_printf(vsp, "%p: %s", array[i], strings[0]);
-			else
-				VSB_printf(vsp, "%p: (?)", array[i]);
-		}
-		VSB_printf (vsp, "\n");
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-pan_ic(const char *func, const char *file, int line, const char *cond,
-    int err, int xxx)
-{
-	const char *q;
-	const struct sess *sp;
-
-	AZ(pthread_mutex_lock(&panicstr_mtx)); /* Won't be released,
-						  we're going to die
-						  anyway */
-	switch(xxx) {
-	case 3:
-		VSB_printf(vsp,
-		    "Wrong turn at %s:%d:\n%s\n", file, line, cond);
-		break;
-	case 2:
-		VSB_printf(vsp,
-		    "Panic from VCL:\n  %s\n", cond);
-		break;
-	case 1:
-		VSB_printf(vsp,
-		    "Missing errorhandling code in %s(), %s line %d:\n"
-		    "  Condition(%s) not true.",
-		    func, file, line, cond);
-		break;
-	default:
-	case 0:
-		VSB_printf(vsp,
-		    "Assert error in %s(), %s line %d:\n"
-		    "  Condition(%s) not true.\n",
-		    func, file, line, cond);
-		break;
-	}
-	if (err)
-		VSB_printf(vsp, "errno = %d (%s)\n", err, strerror(err));
-
-	q = THR_GetName();
-	if (q != NULL)
-		VSB_printf(vsp, "thread = (%s)\n", q);
-
-	VSB_printf(vsp, "ident = %s,%s\n",
-	    VSB_data(vident) + 1, WAIT_GetName());
-
-	pan_backtrace();
-
-	if (!(cache_param->diag_bitmap & 0x2000)) {
-		sp = THR_GetSession();
-		if (sp != NULL)
-			pan_sess(sp);
-	}
-	VSB_printf(vsp, "\n");
-	VSB_bcat(vsp, "", 1);	/* NUL termination */
-
-	if (cache_param->diag_bitmap & 0x4000)
-		(void)fputs(VSM_head->panicstr, stderr);
-
-#ifdef HAVE_ABORT2
-	if (cache_param->diag_bitmap & 0x8000) {
-		void *arg[1];
-		char *p;
-
-		for (p = VSM_head->panicstr; *p; p++)
-			if (*p == '\n')
-				*p = ' ';
-		arg[0] = VSM_head->panicstr;
-		abort2(VSM_head->panicstr, 1, arg);
-	}
-#endif
-	if (cache_param->diag_bitmap & 0x1000)
-		exit(4);
-	else
-		abort();
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-PAN_Init(void)
-{
-
-	VAS_Fail = pan_ic;
-	vsp = &vsps;
-	AN(VSB_new(vsp, VSM_head->panicstr, sizeof VSM_head->panicstr,
-	    VSB_FIXEDLEN));
-}
diff --git a/bin/varnishd/cache_pipe.c b/bin/varnishd/cache_pipe.c
deleted file mode 100644
index 4180d39..0000000
--- a/bin/varnishd/cache_pipe.c
+++ /dev/null
@@ -1,133 +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 "vtcp.h"
-#include "vtim.h"
-
-static int
-rdf(int fd0, int fd1)
-{
-	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);
-		if (i != j)
-			(void)usleep(100000);		/* XXX hack */
-	}
-	return (0);
-}
-
-void
-PipeSession(struct sess *sp)
-{
-	struct vbc *vc;
-	struct worker *w;
-	struct pollfd fds[2];
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC);
-	w = sp->wrk;
-
-	sp->wrk->vbc = VDI_GetFd(NULL, sp);
-	if (sp->wrk->vbc == NULL)
-		return;
-	vc = sp->wrk->vbc;
-	(void)VTCP_blocking(vc->fd);
-
-	WRW_Reserve(w, &vc->fd);
-	sp->wrk->acct_tmp.hdrbytes +=
-	    http_Write(w, sp->vsl_id, sp->wrk->bereq, 0);
-
-	if (sp->htc->pipeline.b != NULL)
-		sp->wrk->acct_tmp.bodybytes +=
-		    WRW_Write(w, sp->htc->pipeline.b, Tlen(sp->htc->pipeline));
-
-	i = WRW_FlushRelease(w);
-
-	if (i) {
-		SES_Close(sp, "pipe");
-		VDI_CloseFd(sp->wrk);
-		return;
-	}
-
-	sp->t_resp = VTIM_real();
-
-	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(sp->fd, 0);
-	fds[1].fd = 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, cache_param->pipe_timeout * 1000);
-		if (i < 1)
-			break;
-		if (fds[0].revents && rdf(vc->fd, sp->fd)) {
-			if (fds[1].fd == -1)
-				break;
-			(void)shutdown(vc->fd, SHUT_RD);
-			(void)shutdown(sp->fd, SHUT_WR);
-			fds[0].events = 0;
-			fds[0].fd = -1;
-		}
-		if (fds[1].revents && rdf(sp->fd, vc->fd)) {
-			if (fds[0].fd == -1)
-				break;
-			(void)shutdown(sp->fd, SHUT_RD);
-			(void)shutdown(vc->fd, SHUT_WR);
-			fds[1].events = 0;
-			fds[1].fd = -1;
-		}
-	}
-	SES_Close(sp, "pipe");
-	VDI_CloseFd(sp->wrk);
-}
diff --git a/bin/varnishd/cache_pool.c b/bin/varnishd/cache_pool.c
deleted file mode 100644
index 79a5fcd..0000000
--- a/bin/varnishd/cache_pool.c
+++ /dev/null
@@ -1,594 +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.
- *
- * We maintain a number of worker thread pools, to spread lock contention.
- *
- * Pools can be added on the fly, as a means to mitigate lock contention,
- * but can only be removed again by a restart. (XXX: we could fix that)
- *
- * Two threads herd the pools, one eliminates idle threads and aggregates
- * statistics for all the pools, the other thread creates new threads
- * on demand, subject to various numerical constraints.
- *
- * The algorithm for when to create threads needs to be reactive enough
- * to handle startup spikes, but sufficiently attenuated to not cause
- * thread pileups.  This remains subject for improvement.
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "cache.h"
-#include "common/heritage.h"
-
-#include "waiter/cache_waiter.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-/*--------------------------------------------------------------------
- * MAC OS/X is incredibly moronic when it comes to time and such...
- */
-
-#ifndef CLOCK_MONOTONIC
-#define CLOCK_MONOTONIC	0
-
-#include <sys/time.h>
-
-static int
-clock_gettime(int foo, struct timespec *ts)
-{
-	struct timeval tv;
-
-	(void)foo;
-	gettimeofday(&tv, NULL);
-	ts->tv_sec = tv.tv_sec;
-	ts->tv_nsec = tv.tv_usec * 1000;
-	return (0);
-}
-
-static int
-pthread_condattr_setclock(pthread_condattr_t *attr, int foo)
-{
-	(void)attr;
-	(void)foo;
-	return (0);
-}
-#endif /* !CLOCK_MONOTONIC */
-
-static void *waiter_priv;
-
-VTAILQ_HEAD(workerhead, worker);
-
-struct poolsock {
-	unsigned			magic;
-#define POOLSOCK_MAGIC			0x1b0a2d38
-	VTAILQ_ENTRY(poolsock)		list;
-	struct listen_sock		*lsock;
-};
-
-/* Number of work requests queued in excess of worker threads available */
-
-struct pool {
-	unsigned		magic;
-#define POOL_MAGIC		0x606658fa
-	VTAILQ_ENTRY(pool)	list;
-
-	pthread_cond_t		herder_cond;
-	struct lock		herder_mtx;
-	pthread_t		herder_thr;
-
-	struct lock		mtx;
-	struct workerhead	idle;
-	VTAILQ_HEAD(, sess)	queue;
-	VTAILQ_HEAD(, poolsock)	socks;
-	unsigned		nthr;
-	unsigned		lqueue;
-	unsigned		last_lqueue;
-	uintmax_t		ndropped;
-	uintmax_t		nqueued;
-	struct sesspool		*sesspool;
-};
-
-static struct lock		pool_mtx;
-static pthread_t		thr_pool_herder;
-
-/*--------------------------------------------------------------------
- * Nobody is accepting on this socket, so we do.
- *
- * As long as we can stick the accepted connection to another thread
- * we do so, otherwise we return and handle it ourselves.
- *
- * Notice calling convention:  Called locked and returns locked, but
- * works lock in the meantime.
- *
- * We store data about the accept in reserved workspace, it is only used
- * for a brief moment and it takes up around 144 bytes.
- */
-
-static int
-pool_accept(struct pool *pp, struct worker *w, const struct poolsock *ps)
-{
-	struct worker *w2;
-	struct wrk_accept *wa, *wa2;
-
-	CHECK_OBJ_NOTNULL(pp, POOL_MAGIC);
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(ps, POOLSOCK_MAGIC);
-
-	CHECK_OBJ_NOTNULL(ps->lsock, LISTEN_SOCK_MAGIC);
-	Lck_AssertHeld(&pp->mtx);
-	Lck_Unlock(&pp->mtx);
-	assert(sizeof *wa == WS_Reserve(w->ws, sizeof *wa));
-	wa = (void*)w->ws->f;
-	while (1) {
-		memset(wa, 0, sizeof *wa);
-		wa->magic = WRK_ACCEPT_MAGIC;
-
-		if (ps->lsock->sock < 0) {
-			/* Socket Shutdown */
-			Lck_Lock(&pp->mtx);
-			return (-1);
-		}
-		if (VCA_Accept(ps->lsock, wa) < 0) {
-			w->stats.sess_fail++;
-			/* We're going to pace in vca anyway... */
-			(void)WRK_TrySumStat(w);
-			continue;
-		}
-
-		Lck_Lock(&pp->mtx);
-		if (VTAILQ_EMPTY(&pp->idle))
-			return (0);
-		w2 = VTAILQ_FIRST(&pp->idle);
-		VTAILQ_REMOVE(&pp->idle, w2, list);
-		Lck_Unlock(&pp->mtx);
-		assert(sizeof *wa2 == WS_Reserve(w2->ws, sizeof *wa2));
-		wa2 = (void*)w2->ws->f;
-		memcpy(wa2, wa, sizeof *wa);
-		AZ(pthread_cond_signal(&w2->cond));
-	}
-}
-
-/*--------------------------------------------------------------------
- * This is the work function for worker threads in the pool.
- */
-
-void
-Pool_Work_Thread(void *priv, struct worker *w)
-{
-	struct pool *pp;
-	int stats_clean, i;
-	struct poolsock *ps;
-
-	CAST_OBJ_NOTNULL(pp, priv, POOL_MAGIC);
-	w->pool = pp;
-	Lck_Lock(&pp->mtx);
-	stats_clean = 1;
-	while (1) {
-
-		Lck_AssertHeld(&pp->mtx);
-
-		CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-		CHECK_OBJ_NOTNULL(w->bereq, HTTP_MAGIC);
-		CHECK_OBJ_NOTNULL(w->beresp, HTTP_MAGIC);
-		CHECK_OBJ_NOTNULL(w->resp, HTTP_MAGIC);
-
-		WS_Reset(w->ws, NULL);
-
-		w->sp = VTAILQ_FIRST(&pp->queue);
-		if (w->sp != NULL) {
-			/* Process queued requests, if any */
-			assert(pp->lqueue > 0);
-			VTAILQ_REMOVE(&pp->queue, w->sp, poollist);
-			pp->lqueue--;
-		} else if (!VTAILQ_EMPTY(&pp->socks)) {
-			/* Accept on a socket */
-			ps = VTAILQ_FIRST(&pp->socks);
-			VTAILQ_REMOVE(&pp->socks, ps, list);
-			i = pool_accept(pp, w, ps);
-			Lck_AssertHeld(&pp->mtx);
-			if (i < 0) {
-				/* Socket Shutdown */
-				FREE_OBJ(ps);
-				WS_Release(w->ws, 0);
-				continue;
-			}
-			VTAILQ_INSERT_TAIL(&pp->socks, ps, list);
-		} else if (VTAILQ_EMPTY(&pp->socks)) {
-			/* Nothing to do: To sleep, perchance to dream ... */
-			if (isnan(w->lastused))
-				w->lastused = VTIM_real();
-			VTAILQ_INSERT_HEAD(&pp->idle, w, list);
-			if (!stats_clean)
-				WRK_SumStat(w);
-			(void)Lck_CondWait(&w->cond, &pp->mtx, NULL);
-		}
-
-		/*
-		 * If we got neither session or accepted a socket, we were
-		 * woken up to die to cull the herd.
-		 */
-		if (w->sp == NULL && w->ws->r == NULL)
-			break;
-
-		Lck_Unlock(&pp->mtx);
-
-		if (w->sp == NULL) {
-			/* Turn accepted socket into a session */
-			assert(w->ws->r != NULL);
-			w->sp = SES_New(w, pp->sesspool);
-			if (w->sp == NULL)
-				VCA_FailSess(w);
-			else
-				VCA_SetupSess(w);
-			WS_Release(w->ws, 0);
-		}
-		assert(w->ws->r == NULL);
-
-		if (w->sp != NULL) {
-			CHECK_OBJ_NOTNULL(w->sp, SESS_MAGIC);
-
-			stats_clean = 0;
-			w->lastused = NAN;
-			w->storage_hint = NULL;
-
-			AZ(w->sp->wrk);
-			THR_SetSession(w->sp);
-			w->sp->wrk = w;
-			CNT_Session(w->sp);
-			THR_SetSession(NULL);
-			w->sp = NULL;
-
-			WS_Assert(w->ws);
-			AZ(w->bereq->ws);
-			AZ(w->beresp->ws);
-			AZ(w->resp->ws);
-			AZ(w->wrw.wfd);
-			AZ(w->storage_hint);
-			assert(w->wlp == w->wlb);
-			if (cache_param->diag_bitmap & 0x00040000) {
-				if (w->vcl != NULL)
-					VCL_Rel(&w->vcl);
-			}
-		}
-		stats_clean = WRK_TrySumStat(w);
-		Lck_Lock(&pp->mtx);
-	}
-	Lck_Unlock(&pp->mtx);
-	w->pool = NULL;
-}
-
-/*--------------------------------------------------------------------
- * Queue a workrequest if possible.
- *
- * Return zero if the request was queued, negative if it wasn't.
- */
-
-static int
-pool_queue(struct pool *pp, struct sess *sp)
-{
-	struct worker *w;
-
-	Lck_Lock(&pp->mtx);
-
-	/* If there are idle threads, we tickle the first one into action */
-	w = VTAILQ_FIRST(&pp->idle);
-	if (w != NULL) {
-		VTAILQ_REMOVE(&pp->idle, w, list);
-		Lck_Unlock(&pp->mtx);
-		w->sp = sp;
-		AZ(pthread_cond_signal(&w->cond));
-		return (0);
-	}
-
-	/* If we have too much in the queue already, refuse. */
-	if (pp->lqueue > (cache_param->queue_max * pp->nthr) / 100) {
-		pp->ndropped++;
-		Lck_Unlock(&pp->mtx);
-		return (-1);
-	}
-
-	VTAILQ_INSERT_TAIL(&pp->queue, sp, poollist);
-	pp->nqueued++;
-	pp->lqueue++;
-	Lck_Unlock(&pp->mtx);
-	AZ(pthread_cond_signal(&pp->herder_cond));
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-Pool_Schedule(struct pool *pp, struct sess *sp)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	AZ(sp->wrk);
-	if (pool_queue(pp, sp) == 0)
-		return(0);
-
-	VSC_C_main->client_drop_late++;
-
-	/*
-	 * Couldn't queue it -- kill it.
-	 *
-	 * XXX: a notice might be polite, but would potentially
-	 * XXX: sleep whichever thread got us here
-	 */
-	sp->t_end = VTIM_real();
-	if (sp->vcl != NULL) {
-		/*
-		 * A session parked on a busy object can come here
-		 * after it wakes up.  Loose the VCL reference.
-		 */
-		VCL_Rel(&sp->vcl);
-	}
-	return (1);
-}
-
-/*--------------------------------------------------------------------
- * Wait for another request
- */
-
-void
-Pool_Wait(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	AZ(sp->obj);
-	AZ(sp->vcl);
-	assert(sp->fd >= 0);
-	/*
-	 * Set nonblocking in the worker-thread, before passing to the
-	 * acceptor thread, to reduce syscall density of the latter.
-	 */
-	if (VTCP_nonblocking(sp->fd))
-		SES_Close(sp, "remote closed");
-	waiter->pass(waiter_priv, sp);
-}
-
-/*--------------------------------------------------------------------
- * Create another thread, if necessary & possible
- */
-
-static void
-pool_breed(struct pool *qp, const pthread_attr_t *tp_attr)
-{
-	pthread_t tp;
-
-	/*
-	 * If we need more threads, and have space, create
-	 * one more thread.
-	 */
-	if (qp->nthr < cache_param->wthread_min ||	/* Not enough threads yet */
-	    (qp->lqueue > cache_param->wthread_add_threshold && /* more needed */
-	    qp->lqueue > qp->last_lqueue)) {	/* not getting better since last */
-		if (qp->nthr > cache_param->wthread_max) {
-			Lck_Lock(&pool_mtx);
-			VSC_C_main->threads_limited++;
-			Lck_Unlock(&pool_mtx);
-		} else if (pthread_create(&tp, tp_attr, WRK_thread, qp)) {
-			VSL(SLT_Debug, 0, "Create worker thread failed %d %s",
-			    errno, strerror(errno));
-			Lck_Lock(&pool_mtx);
-			VSC_C_main->threads_limited++;
-			Lck_Unlock(&pool_mtx);
-			VTIM_sleep(cache_param->wthread_fail_delay * 1e-3);
-		} else {
-			AZ(pthread_detach(tp));
-			VTIM_sleep(cache_param->wthread_add_delay * 1e-3);
-			qp->nthr++;
-			Lck_Lock(&pool_mtx);
-			VSC_C_main->threads++;
-			VSC_C_main->threads_created++;
-			Lck_Unlock(&pool_mtx);
-		}
-	}
-	qp->last_lqueue = qp->lqueue;
-}
-
-/*--------------------------------------------------------------------
- * Herd a single pool
- *
- * This thread wakes up whenever a pool queues.
- *
- * The trick here is to not be too aggressive about creating threads.
- * We do this by only examining one pool at a time, and by sleeping
- * a short while whenever we create a thread and a little while longer
- * whenever we fail to, hopefully missing a lot of cond_signals in
- * the meantime.
- *
- * XXX: probably need a lot more work.
- *
- */
-
-static void*
-pool_herder(void *priv)
-{
-	struct pool *pp;
-	pthread_attr_t tp_attr;
-	struct timespec ts;
-	double t_idle;
-	struct worker *w;
-	int i;
-
-	CAST_OBJ_NOTNULL(pp, priv, POOL_MAGIC);
-	AZ(pthread_attr_init(&tp_attr));
-
-	while (1) {
-		/* Set the stacksize for worker threads we create */
-		if (cache_param->wthread_stacksize != UINT_MAX)
-			AZ(pthread_attr_setstacksize(&tp_attr,
-			    cache_param->wthread_stacksize));
-		else {
-			AZ(pthread_attr_destroy(&tp_attr));
-			AZ(pthread_attr_init(&tp_attr));
-		}
-
-		pool_breed(pp, &tp_attr);
-
-		if (pp->nthr < cache_param->wthread_min)
-			continue;
-
-		AZ(clock_gettime(CLOCK_MONOTONIC, &ts));
-		ts.tv_sec += cache_param->wthread_purge_delay / 1000;
-		ts.tv_nsec +=
-		    (cache_param->wthread_purge_delay % 1000) * 1000000;
-		if (ts.tv_nsec >= 1000000000) {
-			ts.tv_sec++;
-			ts.tv_nsec -= 1000000000;
-		}
-
-		Lck_Lock(&pp->herder_mtx);
-		i = Lck_CondWait(&pp->herder_cond, &pp->herder_mtx, &ts);
-		Lck_Unlock(&pp->herder_mtx);
-		if (!i)
-			continue;
-
-		if (pp->nthr <= cache_param->wthread_min)
-			continue;
-
-		t_idle = VTIM_real() - cache_param->wthread_timeout;
-
-		Lck_Lock(&pp->mtx);
-		VSC_C_main->sess_queued += pp->nqueued;
-		VSC_C_main->sess_dropped += pp->ndropped;
-		pp->nqueued = pp->ndropped = 0;
-		w = VTAILQ_LAST(&pp->idle, workerhead);
-		if (w != NULL &&
-		    (w->lastused < t_idle || pp->nthr > cache_param->wthread_max)) {
-			VTAILQ_REMOVE(&pp->idle, w, list);
-		} else
-			w = NULL;
-		Lck_Unlock(&pp->mtx);
-
-		/* And give it a kiss on the cheek... */
-		if (w != NULL) {
-			pp->nthr--;
-			Lck_Lock(&pool_mtx);
-			VSC_C_main->threads--;
-			VSC_C_main->threads_destroyed++;
-			Lck_Unlock(&pool_mtx);
-			AZ(w->sp);
-			AZ(pthread_cond_signal(&w->cond));
-		}
-	}
-	NEEDLESS_RETURN(NULL);
-}
-
-/*--------------------------------------------------------------------
- * Add a thread pool
- */
-
-static struct pool *
-pool_mkpool(void)
-{
-	struct pool *pp;
-	struct listen_sock *ls;
-	struct poolsock *ps;
-	pthread_condattr_t cv_attr;
-
-	ALLOC_OBJ(pp, POOL_MAGIC);
-	XXXAN(pp);
-	Lck_New(&pp->mtx, lck_wq);
-
-	VTAILQ_INIT(&pp->queue);
-	VTAILQ_INIT(&pp->idle);
-	VTAILQ_INIT(&pp->socks);
-	pp->sesspool = SES_NewPool(pp);
-	AN(pp->sesspool);
-
-	VTAILQ_FOREACH(ls, &heritage.socks, list) {
-		if (ls->sock < 0)
-			continue;
-		ALLOC_OBJ(ps, POOLSOCK_MAGIC);
-		XXXAN(ps);
-		ps->lsock = ls;
-		VTAILQ_INSERT_TAIL(&pp->socks, ps, list);
-	}
-
-	AZ(pthread_condattr_init(&cv_attr));
-	AZ(pthread_condattr_setclock(&cv_attr, CLOCK_MONOTONIC));
-	AZ(pthread_cond_init(&pp->herder_cond, &cv_attr));
-	AZ(pthread_condattr_destroy(&cv_attr));
-	Lck_New(&pp->herder_mtx, lck_herder);
-	AZ(pthread_create(&pp->herder_thr, NULL, pool_herder, pp));
-
-	return (pp);
-}
-
-/*--------------------------------------------------------------------
- * This thread adjusts the number of pools to match the parameter.
- *
- */
-
-static void *
-pool_poolherder(void *priv)
-{
-	unsigned nwq;
-	VTAILQ_HEAD(,pool)	pools = VTAILQ_HEAD_INITIALIZER(pools);
-	struct pool *pp;
-	uint64_t u;
-
-	THR_SetName("pool_herder");
-	(void)priv;
-
-	nwq = 0;
-	while (1) {
-		if (nwq < cache_param->wthread_pools) {
-			pp = pool_mkpool();
-			if (pp != NULL) {
-				VTAILQ_INSERT_TAIL(&pools, pp, list);
-				VSC_C_main->pools++;
-				nwq++;
-				continue;
-			}
-		}
-		/* XXX: remove pools */
-		if (0)
-			SES_DeletePool(NULL, NULL);
-		(void)sleep(1);
-		u = 0;
-		VTAILQ_FOREACH(pp, &pools, list)
-			u += pp->lqueue;
-		VSC_C_main->thread_queue_len = u;
-	}
-	NEEDLESS_RETURN(NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-Pool_Init(void)
-{
-
-	waiter_priv = waiter->init();
-	Lck_New(&pool_mtx, lck_wq);
-	AZ(pthread_create(&thr_pool_herder, NULL, pool_poolherder, NULL));
-}
diff --git a/bin/varnishd/cache_response.c b/bin/varnishd/cache_response.c
deleted file mode 100644
index 487a514..0000000
--- a/bin/varnishd/cache_response.c
+++ /dev/null
@@ -1,427 +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.
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "vct.h"
-#include "vtim.h"
-
-/*--------------------------------------------------------------------*/
-
-static void
-res_dorange(const struct sess *sp, const char *r, ssize_t *plow, ssize_t *phigh)
-{
-	ssize_t low, high, has_low;
-
-	assert(sp->obj->response == 200);
-	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 >= sp->obj->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 = sp->obj->len - high;
-			high = sp->obj->len - 1;
-		}
-	} else
-		high = sp->obj->len - 1;
-	if (*r != '\0')
-		return;
-
-	if (high >= sp->obj->len)
-		high = sp->obj->len - 1;
-
-	if (low > high)
-		return;
-
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-	    "Content-Range: bytes %jd-%jd/%jd",
-	    (intmax_t)low, (intmax_t)high, (intmax_t)sp->obj->len);
-	http_Unset(sp->wrk->resp, H_Content_Length);
-	assert(sp->wrk->res_mode & RES_LEN);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-	    "Content-Length: %jd", (intmax_t)(1 + high - low));
-	http_SetResp(sp->wrk->resp, "HTTP/1.1", 206, "Partial Content");
-
-	*plow = low;
-	*phigh = high;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-RES_BuildHttp(const struct sess *sp)
-{
-	char time_str[30];
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-
-	http_ClrHeader(sp->wrk->resp);
-	sp->wrk->resp->logtag = HTTP_Tx;
-	http_CopyResp(sp->wrk->resp, sp->obj->http);
-	http_FilterFields(sp->wrk, sp->vsl_id, sp->wrk->resp, sp->obj->http,
-	    HTTPH_A_DELIVER);
-
-	if (!(sp->wrk->res_mode & RES_LEN)) {
-		http_Unset(sp->wrk->resp, H_Content_Length);
-	} else if (cache_param->http_range_support) {
-		/* We only accept ranges if we know the length */
-		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-		    "Accept-Ranges: bytes");
-	}
-
-	if (sp->wrk->res_mode & RES_CHUNKED)
-		http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-		    "Transfer-Encoding: chunked");
-
-	VTIM_format(VTIM_real(), time_str);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Date: %s", time_str);
-
-	if (sp->xid != sp->obj->xid)
-		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-		    "X-Varnish: %u %u", sp->xid, sp->obj->xid);
-	else
-		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-		    "X-Varnish: %u", sp->xid);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Age: %.0f",
-	    sp->obj->exp.age + sp->t_resp - sp->obj->exp.entered);
-	http_SetHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Via: 1.1 varnish");
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp, "Connection: %s",
-	    sp->doclose ? "close" : "keep-alive");
-}
-
-/*--------------------------------------------------------------------
- * We have a gzip'ed object and need to ungzip it for a client which
- * does not understand gzip.
- * XXX: handle invalid gzip data better (how ?)
- */
-
-static void
-res_WriteGunzipObj(const struct sess *sp)
-{
-	struct storage *st;
-	unsigned u = 0;
-	struct vgz *vg;
-	char obuf[cache_param->gzip_stack_buffer];
-	ssize_t obufl = 0;
-	int i;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	vg = VGZ_NewUngzip(sp->wrk, "U D -");
-
-	VGZ_Obuf(vg, obuf, sizeof obuf);
-	VTAILQ_FOREACH(st, &sp->obj->store, list) {
-		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
-		u += st->len;
-
-		VSC_C_main->n_objwrite++;
-
-		i = VGZ_WrwGunzip(sp->wrk, vg,
-		    st->ptr, st->len,
-		    obuf, sizeof obuf, &obufl);
-		/* XXX: error check */
-		(void)i;
-	}
-	if (obufl) {
-		(void)WRW_Write(sp->wrk, obuf, obufl);
-		(void)WRW_Flush(sp->wrk);
-	}
-	(void)VGZ_Destroy(&vg, sp->vsl_id);
-	assert(u == sp->obj->len);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-res_WriteDirObj(const struct sess *sp, ssize_t low, ssize_t high)
-{
-	ssize_t u = 0;
-	size_t ptr, off, len;
-	struct storage *st;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	ptr = 0;
-	VTAILQ_FOREACH(st, &sp->obj->store, list) {
-		CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
-		u += st->len;
-		len = st->len;
-		off = 0;
-		if (ptr + len <= low) {
-			/* This segment is too early */
-			ptr += len;
-			continue;
-		}
-		if (ptr < low) {
-			/* Chop front of segment off */
-			off += (low - ptr);
-			len -= (low - ptr);
-			ptr += (low - ptr);
-		}
-		if (ptr + len > high)
-			/* Chop tail of segment off */
-			len = 1 + high - ptr;
-
-		ptr += len;
-
-		sp->wrk->acct_tmp.bodybytes += len;
-#ifdef SENDFILE_WORKS
-		/*
-		 * XXX: the overhead of setting up sendfile is not
-		 * XXX: epsilon and maybe not even delta, so avoid
-		 * XXX: engaging sendfile for small objects.
-		 * XXX: Should use getpagesize() ?
-		 */
-		if (st->fd >= 0 &&
-		    st->len >= cache_param->sendfile_threshold) {
-			VSC_C_main->n_objsendfile++;
-			WRW_Sendfile(sp->wrk, st->fd, st->where + off, len);
-			continue;
-		}
-#endif /* SENDFILE_WORKS */
-		VSC_C_main->n_objwrite++;
-		(void)WRW_Write(sp->wrk, st->ptr + off, len);
-	}
-	assert(u == sp->obj->len);
-}
-
-/*--------------------------------------------------------------------
- * Deliver an object.
- * Attempt optimizations like 304 and 206 here.
- */
-
-void
-RES_WriteObj(struct sess *sp)
-{
-	char *r;
-	ssize_t low, high;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	WRW_Reserve(sp->wrk, &sp->fd);
-
-	if (sp->obj->response == 200 &&
-	    sp->http->conds &&
-	    RFC2616_Do_Cond(sp)) {
-		sp->wantbody = 0;
-		http_SetResp(sp->wrk->resp, "HTTP/1.1", 304, "Not Modified");
-		http_Unset(sp->wrk->resp, H_Content_Length);
-		http_Unset(sp->wrk->resp, H_Transfer_Encoding);
-	}
-
-	/*
-	 * If nothing special planned, we can attempt Range support
-	 */
-	low = 0;
-	high = sp->obj->len - 1;
-	if (
-	    sp->wantbody &&
-	    (sp->wrk->res_mode & RES_LEN) &&
-	    !(sp->wrk->res_mode & (RES_ESI|RES_ESI_CHILD|RES_GUNZIP)) &&
-	    cache_param->http_range_support &&
-	    sp->obj->response == 200 &&
-	    http_GetHdr(sp->http, H_Range, &r))
-		res_dorange(sp, r, &low, &high);
-
-	/*
-	 * Always remove C-E if client don't grok it
-	 */
-	if (sp->wrk->res_mode & RES_GUNZIP)
-		http_Unset(sp->wrk->resp, H_Content_Encoding);
-
-	/*
-	 * Send HTTP protocol header, unless interior ESI object
-	 */
-	if (!(sp->wrk->res_mode & RES_ESI_CHILD))
-		sp->wrk->acct_tmp.hdrbytes +=
-		    http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
-
-	if (!sp->wantbody)
-		sp->wrk->res_mode &= ~RES_CHUNKED;
-
-	if (sp->wrk->res_mode & RES_CHUNKED)
-		WRW_Chunked(sp->wrk);
-
-	if (!sp->wantbody) {
-		/* This was a HEAD or conditional request */
-	} else if (sp->obj->len == 0) {
-		/* Nothing to do here */
-	} else if (sp->wrk->res_mode & RES_ESI) {
-		ESI_Deliver(sp);
-	} else if (sp->wrk->res_mode & RES_ESI_CHILD && sp->wrk->gzip_resp) {
-		ESI_DeliverChild(sp);
-	} else if (sp->wrk->res_mode & RES_ESI_CHILD &&
-	    !sp->wrk->gzip_resp && sp->obj->gziped) {
-		res_WriteGunzipObj(sp);
-	} else if (sp->wrk->res_mode & RES_GUNZIP) {
-		res_WriteGunzipObj(sp);
-	} else {
-		res_WriteDirObj(sp, low, high);
-	}
-
-	if (sp->wrk->res_mode & RES_CHUNKED &&
-	    !(sp->wrk->res_mode & RES_ESI_CHILD))
-		WRW_EndChunk(sp->wrk);
-
-	if (WRW_FlushRelease(sp->wrk) && sp->fd >= 0)
-		SES_Close(sp, "remote closed");
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-RES_StreamStart(struct sess *sp)
-{
-	struct stream_ctx *sctx;
-
-	sctx = sp->wrk->sctx;
-	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
-
-	AZ(sp->wrk->res_mode & RES_ESI_CHILD);
-	AN(sp->wantbody);
-
-	WRW_Reserve(sp->wrk, &sp->fd);
-	/*
-	 * Always remove C-E if client don't grok it
-	 */
-	if (sp->wrk->res_mode & RES_GUNZIP)
-		http_Unset(sp->wrk->resp, H_Content_Encoding);
-
-	if (!(sp->wrk->res_mode & RES_CHUNKED) &&
-	    sp->wrk->h_content_length != NULL)
-		http_PrintfHeader(sp->wrk, sp->vsl_id, sp->wrk->resp,
-		    "Content-Length: %s", sp->wrk->h_content_length);
-
-	sp->wrk->acct_tmp.hdrbytes +=
-	    http_Write(sp->wrk, sp->vsl_id, sp->wrk->resp, 1);
-
-	if (sp->wrk->res_mode & RES_CHUNKED)
-		WRW_Chunked(sp->wrk);
-}
-
-void
-RES_StreamPoll(struct worker *w)
-{
-	struct stream_ctx *sctx;
-	struct storage *st;
-	ssize_t l, l2;
-	void *ptr;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(w->fetch_obj, OBJECT_MAGIC);
-	sctx = w->sctx;
-	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
-	if (w->fetch_obj->len == sctx->stream_next)
-		return;
-	assert(w->fetch_obj->len > sctx->stream_next);
-	l = sctx->stream_front;
-	VTAILQ_FOREACH(st, &w->fetch_obj->store, list) {
-		if (st->len + l <= sctx->stream_next) {
-			l += st->len;
-			continue;
-		}
-		l2 = st->len + l - sctx->stream_next;
-		ptr = st->ptr + (sctx->stream_next - l);
-		if (w->res_mode & RES_GUNZIP) {
-			(void)VGZ_WrwGunzip(w, sctx->vgz, ptr, l2,
-			    sctx->obuf, sctx->obuf_len, &sctx->obuf_ptr);
-		} else {
-			(void)WRW_Write(w, ptr, l2);
-		}
-		l += st->len;
-		sctx->stream_next += l2;
-	}
-	if (!(w->res_mode & RES_GUNZIP))
-		(void)WRW_Flush(w);
-
-	if (w->fetch_obj->objcore == NULL ||
-	    (w->fetch_obj->objcore->flags & OC_F_PASS)) {
-		/*
-		 * This is a pass object, release storage as soon as we
-		 * have delivered it.
-		 */
-		while (1) {
-			st = VTAILQ_FIRST(&w->fetch_obj->store);
-			if (st == NULL ||
-			    sctx->stream_front + st->len > sctx->stream_next)
-				break;
-			VTAILQ_REMOVE(&w->fetch_obj->store, st, list);
-			sctx->stream_front += st->len;
-			STV_free(st);
-		}
-	}
-}
-
-void
-RES_StreamEnd(struct sess *sp)
-{
-	struct stream_ctx *sctx;
-
-	sctx = sp->wrk->sctx;
-	CHECK_OBJ_NOTNULL(sctx, STREAM_CTX_MAGIC);
-
-	if (sp->wrk->res_mode & RES_GUNZIP && sctx->obuf_ptr > 0)
-		(void)WRW_Write(sp->wrk, sctx->obuf, sctx->obuf_ptr);
-	if (sp->wrk->res_mode & RES_CHUNKED &&
-	    !(sp->wrk->res_mode & RES_ESI_CHILD))
-		WRW_EndChunk(sp->wrk);
-	if (WRW_FlushRelease(sp->wrk))
-		SES_Close(sp, "remote closed");
-}
diff --git a/bin/varnishd/cache_rfc2616.c b/bin/varnishd/cache_rfc2616.c
deleted file mode 100644
index 4041f45..0000000
--- a/bin/varnishd/cache_rfc2616.c
+++ /dev/null
@@ -1,336 +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.
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "vtim.h"
-
-/*--------------------------------------------------------------------
- * TTL and Age calculation in Varnish
- *
- * RFC2616 has a lot to say about how caches should calculate the TTL
- * and expiry times of objects, but it sort of misses the case that
- * applies to Varnish:  the server-side cache.
- *
- * A normal cache, shared or single-client, has no symbiotic relationship
- * with the server, and therefore must take a very defensive attitude
- * if the Data/Expiry/Age/max-age data does not make sense.  Overall
- * the policy described in section 13 of RFC 2616 results in no caching
- * happening on the first little sign of trouble.
- *
- * Varnish on the other hand tries to offload as many transactions from
- * the backend as possible, and therefore just passing through everything
- * if there is a clock-skew between backend and Varnish is not a workable
- * choice.
- *
- * Varnish implements a policy which is RFC2616 compliant when there
- * is no clockskew, and falls as gracefully as possible otherwise.
- * Our "clockless cache" model is syntehsized from the bits of RFC2616
- * that talks about how a cache should react to a clockless origin server,
- * and more or less uses the inverse logic for the opposite relationship.
- *
- */
-
-void
-RFC2616_Ttl(const struct sess *sp)
-{
-	unsigned max_age, age;
-	double h_date, h_expires;
-	char *p;
-	const struct http *hp;
-
-	hp = sp->wrk->beresp;
-
-	assert(sp->wrk->exp.entered != 0.0 && !isnan(sp->wrk->exp.entered));
-	/* If all else fails, cache using default ttl */
-	sp->wrk->exp.ttl = cache_param->default_ttl;
-
-	max_age = age = 0;
-	h_expires = 0;
-	h_date = 0;
-
-	/*
-	 * Initial cacheability determination per [RFC2616, 13.4]
-	 * We do not support ranges yet, so 206 is out.
-	 */
-
-	if (http_GetHdr(hp, H_Age, &p)) {
-		age = strtoul(p, NULL, 0);
-		sp->wrk->exp.age = age;
-	}
-	if (http_GetHdr(hp, H_Expires, &p))
-		h_expires = VTIM_parse(p);
-
-	if (http_GetHdr(hp, H_Date, &p))
-		h_date = VTIM_parse(p);
-
-	switch (sp->err_code) {
-	default:
-		sp->wrk->exp.ttl = -1.;
-		break;
-	case 200: /* OK */
-	case 203: /* Non-Authoritative Information */
-	case 300: /* Multiple Choices */
-	case 301: /* Moved Permanently */
-	case 302: /* Moved Temporarily */
-	case 307: /* Temporary Redirect */
-	case 410: /* Gone */
-	case 404: /* Not Found */
-		/*
-		 * First find any relative specification from the backend
-		 * These take precedence according to RFC2616, 13.2.4
-		 */
-
-		if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
-		    http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
-		    p != NULL) {
-
-			if (*p == '-')
-				max_age = 0;
-			else
-				max_age = strtoul(p, NULL, 0);
-
-			if (age > max_age)
-				sp->wrk->exp.ttl = 0;
-			else
-				sp->wrk->exp.ttl = max_age - age;
-			break;
-		}
-
-		/* No expire header, fall back to default */
-		if (h_expires == 0)
-			break;
-
-
-		/* If backend told us it is expired already, don't cache. */
-		if (h_expires < h_date) {
-			sp->wrk->exp.ttl = 0;
-			break;
-		}
-
-		if (h_date == 0 ||
-		    fabs(h_date - sp->wrk->exp.entered) < cache_param->clock_skew) {
-			/*
-			 * If we have no Date: header or if it is
-			 * sufficiently close to our clock we will
-			 * trust Expires: relative to our own clock.
-			 */
-			if (h_expires < sp->wrk->exp.entered)
-				sp->wrk->exp.ttl = 0;
-			else
-				sp->wrk->exp.ttl = h_expires -
-				    sp->wrk->exp.entered;
-			break;
-		} else {
-			/*
-			 * But even if the clocks are out of whack we can still
-			 * derive a relative time from the two headers.
-			 * (the negative ttl case is caught above)
-			 */
-			sp->wrk->exp.ttl = (int)(h_expires - h_date);
-		}
-
-	}
-
-	/* calculated TTL, Our time, Date, Expires, max-age, age */
-	WSP(sp, SLT_TTL,
-	    "%u RFC %.0f %.0f %.0f %.0f %.0f %.0f %.0f %u",
-	    sp->xid, sp->wrk->exp.ttl, -1., -1., sp->wrk->exp.entered,
-	    sp->wrk->exp.age, h_date, h_expires, max_age);
-}
-
-/*--------------------------------------------------------------------
- * Body existence, fetch method and close policy.
- */
-
-enum body_status
-RFC2616_Body(const struct sess *sp)
-{
-	struct http *hp;
-	char *b;
-
-	hp = sp->wrk->beresp;
-
-	if (hp->protover < 11 && !http_HdrIs(hp, H_Connection, "keep-alive"))
-		sp->wrk->do_close = 1;
-	else if (http_HdrIs(hp, H_Connection, "close"))
-		sp->wrk->do_close = 1;
-	else
-		sp->wrk->do_close = 0;
-
-	if (!strcasecmp(http_GetReq(sp->wrk->bereq), "head")) {
-		/*
-		 * A HEAD request can never have a body in the reply,
-		 * no matter what the headers might say.
-		 * [RFC2516 4.3 p33]
-		 */
-		sp->wrk->stats.fetch_head++;
-		return (BS_NONE);
-	}
-
-	if (hp->status <= 199) {
-		/*
-		 * 1xx responses never have a body.
-		 * [RFC2616 4.3 p33]
-		 */
-		sp->wrk->stats.fetch_1xx++;
-		return (BS_NONE);
-	}
-
-	if (hp->status == 204) {
-		/*
-		 * 204 is "No Content", obviously don't expect a body.
-		 * [RFC2616 10.2.5 p60]
-		 */
-		sp->wrk->stats.fetch_204++;
-		return (BS_NONE);
-	}
-
-	if (hp->status == 304) {
-		/*
-		 * 304 is "Not Modified" it has no body.
-		 * [RFC2616 10.3.5 p63]
-		 */
-		sp->wrk->stats.fetch_304++;
-		return (BS_NONE);
-	}
-
-	if (http_HdrIs(hp, H_Transfer_Encoding, "chunked")) {
-		 sp->wrk->stats.fetch_chunked++;
-		return (BS_CHUNKED);
-	}
-
-	if (http_GetHdr(hp, H_Transfer_Encoding, &b)) {
-		sp->wrk->stats.fetch_bad++;
-		return (BS_ERROR);
-	}
-
-	if (http_GetHdr(hp, H_Content_Length, &sp->wrk->h_content_length)) {
-		sp->wrk->stats.fetch_length++;
-		return (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.
-		 */
-		sp->wrk->stats.fetch_zero++;
-		return (BS_ZERO);
-	}
-
-	if (http_HdrIs(hp, H_Connection, "close")) {
-		/*
-		 * In this case, it is safe to just read what comes.
-		 */
-		sp->wrk->stats.fetch_close++;
-		return (BS_EOF);
-	}
-
-	if (hp->protover < 11) {
-		/*
-		 * With no Connection header, assume EOF.
-		 */
-		sp->wrk->stats.fetch_oldhttp++;
-		return (BS_EOF);
-	}
-
-	/*
-	 * Fall back to EOF transfer.
-	 */
-	sp->wrk->stats.fetch_eof++;
-	return (BS_EOF);
-}
-
-/*--------------------------------------------------------------------
- * Find out if the request can receive a gzip'ed response
- */
-
-unsigned
-RFC2616_Req_Gzip(const struct sess *sp)
-{
-
-
-	/*
-	 * "x-gzip" is for http/1.0 backwards compat, final note in 14.3
-	 * p104 says to not do q values for x-gzip, so we just test
-	 * for its existence.
-	 */
-	if (http_GetHdrData(sp->http, H_Accept_Encoding, "x-gzip", NULL))
-		return (1);
-
-	/*
-	 * "gzip" is the real thing, but the 'q' value must be nonzero.
-	 * We do not care a hoot if the client prefers some other
-	 * compression more than gzip: Varnish only does gzip.
-	 */
-	if (http_GetHdrQ(sp->http, H_Accept_Encoding, "gzip") > 0.)
-		return (1);
-
-	/* Bad client, no gzip. */
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-RFC2616_Do_Cond(const struct sess *sp)
-{
-	char *p, *e;
-	double ims;
-	int do_cond = 0;
-
-	/* RFC 2616 13.3.4 states we need to match both ETag
-	   and If-Modified-Since if present*/
-
-	if (http_GetHdr(sp->http, H_If_Modified_Since, &p) ) {
-		if (!sp->obj->last_modified)
-			return (0);
-		ims = VTIM_parse(p);
-		if (ims > sp->t_req)	/* [RFC2616 14.25] */
-			return (0);
-		if (sp->obj->last_modified > ims)
-			return (0);
-		do_cond = 1;
-	}
-
-	if (http_GetHdr(sp->http, H_If_None_Match, &p) &&
-	    http_GetHdr(sp->obj->http, H_ETag, &e)) {
-		if (strcmp(p,e) != 0)
-			return (0);
-		do_cond = 1;
-	}
-
-	return (do_cond);
-}
diff --git a/bin/varnishd/cache_session.c b/bin/varnishd/cache_session.c
deleted file mode 100644
index 7befbcc..0000000
--- a/bin/varnishd/cache_session.c
+++ /dev/null
@@ -1,419 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2010 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.
- *
- * Session management
- *
- * This is a little bit of a mixed back, containing both memory management
- * and various state-change functions.
- *
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "waiter/cache_waiter.h"
-
-/*--------------------------------------------------------------------*/
-
-struct sessmem {
-	unsigned		magic;
-#define SESSMEM_MAGIC		0x555859c5
-
-	struct sesspool		*pool;
-
-	unsigned		workspace;
-	uint16_t		nhttp;
-	void			*wsp;
-	struct http		*http[2];
-	VTAILQ_ENTRY(sessmem)	list;
-
-	struct sess		sess;
-};
-
-struct sesspool {
-	unsigned		magic;
-#define SESSPOOL_MAGIC		0xd916e202
-	struct pool		*pool;
-	VTAILQ_HEAD(,sessmem)	freelist;
-	struct lock		mtx;
-	unsigned		nsess;
-	unsigned		dly_free_cnt;
-};
-
-/*--------------------------------------------------------------------
- * Charge statistics from worker to request and session.
- */
-
-void
-SES_Charge(struct sess *sp)
-{
-	struct acct *a = &sp->wrk->acct_tmp;
-
-	sp->req_bodybytes += a->bodybytes;
-
-#define ACCT(foo)				\
-	sp->wrk->stats.s_##foo += a->foo;	\
-	sp->acct_ses.foo += a->foo;		\
-	a->foo = 0;
-#include "tbl/acct_fields.h"
-#undef ACCT
-}
-
-/*--------------------------------------------------------------------
- * This function allocates a session + assorted peripheral data
- * structures in one single malloc operation.
- */
-
-static struct sessmem *
-ses_sm_alloc(void)
-{
-	struct sessmem *sm;
-	unsigned char *p, *q;
-	unsigned nws;
-	uint16_t nhttp;
-	unsigned l, hl;
-
-	/*
-	 * It is not necessary to lock these, but we need to
-	 * cache them locally, to make sure we get a consistent
-	 * view of the value.
-	 */
-	nws = cache_param->sess_workspace;
-	nhttp = (uint16_t)cache_param->http_max_hdr;
-
-	hl = HTTP_estimate(nhttp);
-	l = sizeof *sm + nws + 2 * hl;
-	VSC_C_main->sessmem_size = l;
-	p = malloc(l);
-	if (p == NULL)
-		return (NULL);
-	q = p + l;
-
-	/* Don't waste time zeroing the workspace */
-	memset(p, 0, l - nws);
-
-	sm = (void*)p;
-	p += sizeof *sm;
-
-	sm->magic = SESSMEM_MAGIC;
-	sm->workspace = nws;
-	sm->nhttp = nhttp;
-
-	sm->http[0] = HTTP_create(p, nhttp);
-	p += hl;
-
-	sm->http[1] = HTTP_create(p, nhttp);
-	p += hl;
-
-	sm->wsp = p;
-	p += nws;
-
-	assert(p == q);
-
-	return (sm);
-}
-
-/*--------------------------------------------------------------------
- * This prepares a session for use, based on its sessmem structure.
- */
-
-static void
-ses_setup(struct sessmem *sm)
-{
-	struct sess *sp;
-
-	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
-	sp = &sm->sess;
-	memset(sp, 0, sizeof *sp);
-
-	/* We assume that the sess has been zeroed by the time we get here */
-	AZ(sp->magic);
-
-	sp->magic = SESS_MAGIC;
-	sp->mem = sm;
-	sp->sockaddrlen = sizeof(sp->sockaddr);
-	sp->mysockaddrlen = sizeof(sp->mysockaddr);
-	sp->sockaddr.ss_family = sp->mysockaddr.ss_family = PF_UNSPEC;
-	sp->t_open = NAN;
-	sp->t_req = NAN;
-	sp->t_resp = NAN;
-	sp->t_end = NAN;
-	EXP_Clr(&sp->exp);
-
-	WS_Init(sp->ws, "sess", sm->wsp, sm->workspace);
-	sp->http = sm->http[0];
-	sp->http0 = sm->http[1];
-}
-
-/*--------------------------------------------------------------------
- * Get a new session, preferably by recycling an already ready one
- */
-
-struct sess *
-SES_New(struct worker *wrk, struct sesspool *pp)
-{
-	struct sessmem *sm;
-	struct sess *sp;
-	int do_alloc;
-
-	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
-
-	do_alloc = 0;
-	Lck_Lock(&pp->mtx);
-	sm = VTAILQ_FIRST(&pp->freelist);
-	if (sm != NULL) {
-		VTAILQ_REMOVE(&pp->freelist, sm, list);
-	} else if (pp->nsess < cache_param->max_sess) {
-		pp->nsess++;
-		do_alloc = 1;
-	}
-	wrk->stats.sessmem_free += pp->dly_free_cnt;
-	pp->dly_free_cnt = 0;
-	Lck_Unlock(&pp->mtx);
-	if (do_alloc) {
-		sm = ses_sm_alloc();
-		if (sm != NULL) {
-			wrk->stats.sessmem_alloc++;
-			sm->pool = pp;
-			ses_setup(sm);
-		} else {
-			wrk->stats.sessmem_fail++;
-		}
-	} else if (sm == NULL) {
-		wrk->stats.sessmem_limit++;
-	}
-	if (sm == NULL)
-		return (NULL);
-	sp = &sm->sess;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return (sp);
-}
-
-/*--------------------------------------------------------------------
- * Allocate a session for use by background threads.
- */
-
-struct sess *
-SES_Alloc(void)
-{
-	struct sess *sp;
-	struct sessmem *sm;
-
-	sm = ses_sm_alloc();
-	AN(sm);
-	ses_setup(sm);
-	sp = &sm->sess;
-	sp->sockaddrlen = 0;
-	return (sp);
-}
-
-/*--------------------------------------------------------------------
- * Schedule a session back on a work-thread from its pool
- */
-
-int
-SES_Schedule(struct sess *sp)
-{
-	struct sessmem *sm;
-	struct sesspool *pp;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	AZ(sp->wrk);
-
-	sm = sp->mem;
-	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
-
-	pp = sm->pool;
-	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
-
-	AN(pp->pool);
-
-	if (Pool_Schedule(pp->pool, sp)) {
-		SES_Delete(sp, "dropped");
-		return (1);
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * Handle a session (from waiter)
- *
- * Status: see HTC_Rx()
- */
-
-void
-SES_Handle(struct sess *sp, int status)
-{
-
-	switch (status) {
-	case -2:
-		SES_Delete(sp, "blast");
-		break;
-	case -1:
-		SES_Delete(sp, "no request");
-		break;
-	case 1:
-		sp->step = STP_START;
-		(void)SES_Schedule(sp);
-		break;
-	default:
-		WRONG("Unexpected return from HTC_Rx()");
-	}
-}
-
-/*--------------------------------------------------------------------
- * Close a sessions connection.
- */
-
-void
-SES_Close(struct sess *sp, const char *reason)
-{
-	int i;
-
-	assert(sp->fd >= 0);
-	VSL(SLT_SessionClose, sp->vsl_id, "%s", reason);
-	i = close(sp->fd);
-	assert(i == 0 || errno != EBADF); /* XXX EINVAL seen */
-	sp->fd = -1;
-}
-
-/*--------------------------------------------------------------------
- * (Close &) Free or Recycle a session.
- *
- * If the workspace has changed, deleted it, otherwise wash it, and put
- * it up for adoption.
- *
- * XXX: We should also check nhttp
- */
-
-void
-SES_Delete(struct sess *sp, const char *reason)
-{
-	struct acct *b;
-	struct sessmem *sm;
-	static char noaddr[] = "-";
-	struct worker *wrk;
-	struct sesspool *pp;
-
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	sm = sp->mem;
-	CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
-	pp = sm->pool;
-	CHECK_OBJ_NOTNULL(pp, SESSPOOL_MAGIC);
-	wrk = sp->wrk;
-	CHECK_OBJ_ORNULL(wrk, WORKER_MAGIC);
-
-
-	if (reason != NULL)
-		SES_Close(sp, reason);
-	assert(sp->fd < 0);
-
-	AZ(sp->obj);
-	AZ(sp->vcl);
-	if (sp->addr == NULL)
-		sp->addr = noaddr;
-	if (sp->port == NULL)
-		sp->port = noaddr;
-
-	b = &sp->acct_ses;
-	assert(!isnan(b->first));
-	assert(!isnan(sp->t_end));
-
-	VSL(SLT_StatSess, sp->vsl_id, "%s %s %.0f %ju %ju %ju %ju %ju %ju %ju",
-	    sp->addr, sp->port, sp->t_end - b->first,
-	    b->sess, b->req, b->pipe, b->pass,
-	    b->fetch, b->hdrbytes, b->bodybytes);
-
-	if (sm->workspace != cache_param->sess_workspace ||
-	    sm->nhttp != (uint16_t)cache_param->http_max_hdr ||
-	    pp->nsess > cache_param->max_sess) {
-		free(sm);
-		Lck_Lock(&pp->mtx);
-		if (wrk != NULL)
-			wrk->stats.sessmem_free++;
-		else
-			pp->dly_free_cnt++;
-		pp->nsess--;
-		Lck_Unlock(&pp->mtx);
-	} else {
-		/* Clean and prepare for reuse */
-		ses_setup(sm);
-		Lck_Lock(&pp->mtx);
-		if (wrk != NULL) {
-			wrk->stats.sessmem_free += pp->dly_free_cnt;
-			pp->dly_free_cnt = 0;
-		}
-		VTAILQ_INSERT_HEAD(&pp->freelist, sm, list);
-		Lck_Unlock(&pp->mtx);
-	}
-}
-
-/*--------------------------------------------------------------------
- * Create and delete pools
- */
-
-struct sesspool *
-SES_NewPool(struct pool *pp)
-{
-	struct sesspool *sp;
-
-	ALLOC_OBJ(sp, SESSPOOL_MAGIC);
-	AN(sp);
-	sp->pool = pp;
-	VTAILQ_INIT(&sp->freelist);
-	Lck_New(&sp->mtx, lck_sessmem);
-	return (sp);
-}
-
-void
-SES_DeletePool(struct sesspool *sp, struct worker *wrk)
-{
-	struct sessmem *sm;
-
-	CHECK_OBJ_NOTNULL(sp, SESSPOOL_MAGIC);
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	Lck_Lock(&sp->mtx);
-	while (!VTAILQ_EMPTY(&sp->freelist)) {
-		sm = VTAILQ_FIRST(&sp->freelist);
-		CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
-		VTAILQ_REMOVE(&sp->freelist, sm, list);
-		FREE_OBJ(sm);
-		wrk->stats.sessmem_free++;
-		sp->nsess--;
-	}
-	AZ(sp->nsess);
-	Lck_Unlock(&sp->mtx);
-	Lck_Delete(&sp->mtx);
-	FREE_OBJ(sp);
-}
diff --git a/bin/varnishd/cache_shmlog.c b/bin/varnishd/cache_shmlog.c
deleted file mode 100644
index 1252fa3..0000000
--- a/bin/varnishd/cache_shmlog.c
+++ /dev/null
@@ -1,346 +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.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"	// For w->vbc
-
-#include "vapi/vsm_int.h"
-#include "vmb.h"
-#include "vtim.h"
-
-/* These cannot be struct lock, which depends on vsm/vsl working */
-static pthread_mutex_t vsl_mtx;
-static pthread_mutex_t vsm_mtx;
-
-static uint32_t			*vsl_start;
-static const uint32_t		*vsl_end;
-static uint32_t			*vsl_ptr;
-
-static inline uint32_t
-vsl_w0(uint32_t type, uint32_t length)
-{
-
-	assert(length < 0x10000);
-        return (((type & 0xff) << 24) | length);
-}
-
-/*--------------------------------------------------------------------*/
-
-static inline void
-vsl_hdr(enum VSL_tag_e tag, uint32_t *p, unsigned len, unsigned id)
-{
-
-	assert(((uintptr_t)p & 0x3) == 0);
-
-	p[1] = id;
-	VMB();
-	p[0] = vsl_w0(tag, len);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-vsl_wrap(void)
-{
-
-	assert(vsl_ptr >= vsl_start + 1);
-	assert(vsl_ptr < vsl_end);
-	vsl_start[1] = VSL_ENDMARKER;
-	do
-		vsl_start[0]++;
-	while (vsl_start[0] == 0);
-	VWMB();
-	if (vsl_ptr != vsl_start + 1) {
-		*vsl_ptr = VSL_WRAPMARKER;
-		vsl_ptr = vsl_start + 1;
-	}
-	VSC_C_main->shm_cycles++;
-}
-
-/*--------------------------------------------------------------------
- * Reserve bytes for a record, wrap if necessary
- */
-
-static uint32_t *
-vsl_get(unsigned len, unsigned records, unsigned flushes)
-{
-	uint32_t *p;
-
-	if (pthread_mutex_trylock(&vsl_mtx)) {
-		AZ(pthread_mutex_lock(&vsl_mtx));
-		VSC_C_main->shm_cont++;
-	}
-	assert(vsl_ptr < vsl_end);
-	assert(((uintptr_t)vsl_ptr & 0x3) == 0);
-
-	VSC_C_main->shm_writes++;
-	VSC_C_main->shm_flushes += flushes;
-	VSC_C_main->shm_records += records;
-
-	/* Wrap if necessary */
-	if (VSL_END(vsl_ptr, len) >= vsl_end)
-		vsl_wrap();
-
-	p = vsl_ptr;
-	vsl_ptr = VSL_END(vsl_ptr, len);
-
-	*vsl_ptr = VSL_ENDMARKER;
-
-	assert(vsl_ptr < vsl_end);
-	assert(((uintptr_t)vsl_ptr & 0x3) == 0);
-	AZ(pthread_mutex_unlock(&vsl_mtx));
-
-	return (p);
-}
-
-/*--------------------------------------------------------------------
- * This variant copies a byte-range directly to the log, without
- * taking the detour over sprintf()
- */
-
-static void
-VSLR(enum VSL_tag_e tag, int id, const char *b, unsigned len)
-{
-	uint32_t *p;
-	unsigned mlen;
-
-	mlen = cache_param->shm_reclen;
-
-	/* Truncate */
-	if (len > mlen)
-		len = mlen;
-
-	p = vsl_get(len, 1, 0);
-
-	memcpy(p + 2, b, len);
-	vsl_hdr(tag, p, len, id);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VSL(enum VSL_tag_e tag, int id, const char *fmt, ...)
-{
-	va_list ap;
-	unsigned n, mlen = cache_param->shm_reclen;
-	char buf[mlen];
-
-	/*
-	 * XXX: consider formatting into a stack buffer then move into
-	 * XXX: shmlog with VSLR().
-	 */
-	AN(fmt);
-	va_start(ap, fmt);
-
-	if (strchr(fmt, '%') == NULL) {
-		VSLR(tag, id, fmt, strlen(fmt));
-	} else {
-		n = vsnprintf(buf, mlen, fmt, ap);
-		if (n > mlen)
-			n = mlen;
-		VSLR(tag, id, buf, n);
-	}
-	va_end(ap);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-WSL_Flush(struct worker *w, int overflow)
-{
-	uint32_t *p;
-	unsigned l;
-
-	l = pdiff(w->wlb, w->wlp);
-	if (l == 0)
-		return;
-
-	assert(l >= 8);
-
-	p = vsl_get(l - 8, w->wlr, overflow);
-
-	memcpy(p + 1, w->wlb + 1, l - 4);
-	VWMB();
-	p[0] = w->wlb[0];
-	w->wlp = w->wlb;
-	w->wlr = 0;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-WSLR(struct worker *w, enum VSL_tag_e tag, int id, txt t)
-{
-	unsigned l, mlen;
-
-	Tcheck(t);
-	mlen = cache_param->shm_reclen;
-
-	/* Truncate */
-	l = Tlen(t);
-	if (l > mlen) {
-		l = mlen;
-		t.e = t.b + l;
-	}
-
-	assert(w->wlp < w->wle);
-
-	/* Wrap if necessary */
-	if (VSL_END(w->wlp, l) >= w->wle)
-		WSL_Flush(w, 1);
-	assert (VSL_END(w->wlp, l) < w->wle);
-	memcpy(VSL_DATA(w->wlp), t.b, l);
-	vsl_hdr(tag, w->wlp, l, id);
-	w->wlp = VSL_END(w->wlp, l);
-	assert(w->wlp < w->wle);
-	w->wlr++;
-	if (cache_param->diag_bitmap & 0x10000)
-		WSL_Flush(w, 0);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-wsl(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, va_list ap)
-{
-	char *p;
-	unsigned n, mlen;
-	txt t;
-
-	AN(fmt);
-	mlen = cache_param->shm_reclen;
-
-	if (strchr(fmt, '%') == NULL) {
-		t.b = TRUST_ME(fmt);
-		t.e = strchr(t.b, '\0');
-		WSLR(w, tag, id, t);
-	} else {
-		assert(w->wlp < w->wle);
-
-		/* Wrap if we cannot fit a full size record */
-		if (VSL_END(w->wlp, mlen) >= w->wle)
-			WSL_Flush(w, 1);
-
-		p = VSL_DATA(w->wlp);
-		n = vsnprintf(p, mlen, fmt, ap);
-		if (n > mlen)
-			n = mlen;	/* we truncate long fields */
-		vsl_hdr(tag, w->wlp, n, id);
-		w->wlp = VSL_END(w->wlp, n);
-		assert(w->wlp < w->wle);
-		w->wlr++;
-	}
-	if (cache_param->diag_bitmap & 0x10000)
-		WSL_Flush(w, 0);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-WSL(struct worker *w, enum VSL_tag_e tag, int id, const char *fmt, ...)
-{
-	va_list ap;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	AN(fmt);
-	va_start(ap, fmt);
-	wsl(w, tag, id, fmt, ap);
-	va_end(ap);
-}
-
-
-/*--------------------------------------------------------------------*/
-
-void
-WSLB(struct worker *w, enum VSL_tag_e tag, const char *fmt, ...)
-{
-	va_list ap;
-
-	CHECK_OBJ_NOTNULL(w, WORKER_MAGIC);
-	CHECK_OBJ_NOTNULL(w->vbc, VBC_MAGIC);
-	AN(fmt);
-	va_start(ap, fmt);
-	wsl(w, tag, w->vbc->vsl_id, fmt, ap);
-	va_end(ap);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VSL_Init(void)
-{
-	struct VSM_chunk *vsc;
-
-	AZ(pthread_mutex_init(&vsl_mtx, NULL));
-	AZ(pthread_mutex_init(&vsm_mtx, NULL));
-
-	VSM__Clean();
-
-	VSM_ITER(vsc)
-		if (!strcmp(vsc->class, VSL_CLASS))
-			break;
-	AN(vsc);
-	vsl_start = VSM_PTR(vsc);
-	vsl_end = VSM_NEXT(vsc);
-	vsl_ptr = vsl_start + 1;
-
-	vsl_wrap();
-	VSM_head->starttime = (intmax_t)VTIM_real();
-	memset(VSM_head->panicstr, '\0', sizeof *VSM_head->panicstr);
-	memset(VSC_C_main, 0, sizeof *VSC_C_main);
-	VSM_head->child_pid = getpid();
-}
-
-/*--------------------------------------------------------------------*/
-
-void *
-VSM_Alloc(unsigned size, const char *class, const char *type,
-    const char *ident)
-{
-	void *p;
-
-	AZ(pthread_mutex_lock(&vsm_mtx));
-	p = VSM__Alloc(size, class, type, ident);
-	AZ(pthread_mutex_unlock(&vsm_mtx));
-	return (p);
-}
-
-void
-VSM_Free(const void *ptr)
-{
-
-	AZ(pthread_mutex_lock(&vsm_mtx));
-	VSM__Free(ptr);
-	AZ(pthread_mutex_unlock(&vsm_mtx));
-}
diff --git a/bin/varnishd/cache_vary.c b/bin/varnishd/cache_vary.c
deleted file mode 100644
index 026f937..0000000
--- a/bin/varnishd/cache_vary.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*-
- * 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.
- *
- * Do Vary processing.
- *
- * When we insert an object into the cache which has a Vary: header,
- * we encode a vary matching string containing the headers mentioned
- * and their value.
- *
- * When we match an object in the cache, we check the present request
- * against the vary matching string.
- *
- * The only kind of header-munging we do is leading & trailing space
- * removal.  All the potential "q=foo" gymnastics is not worth the
- * effort.
- *
- * The vary matching string has the following format:
- *
- * Sequence of: {
- *	<msb>			\   Length of header contents.
- *	<lsb>			/
- *	<length of header + 1>	\
- *	<header>		 \  Same format as argument to http_GetHdr()
- *	':'			 /
- *	'\0'			/
- *      <header>		>   Only present if length != 0xffff
- * }
- *      '\0'
- */
-
-#include "config.h"
-
-#include "cache.h"
-
-#include "vct.h"
-#include "vend.h"
-
-struct vsb *
-VRY_Create(const struct sess *sp, const struct http *hp)
-{
-	char *v, *p, *q, *h, *e;
-	struct vsb *sb, *sbh;
-	int l;
-
-	/* No Vary: header, no worries */
-	if (!http_GetHdr(hp, H_Vary, &v))
-		return (NULL);
-
-	/* For vary matching string */
-	sb = VSB_new_auto();
-	AN(sb);
-
-	/* For header matching strings */
-	sbh = VSB_new_auto();
-	AN(sbh);
-
-	if (*v == ':') {
-		WSP(sp, SLT_Error, "Vary header had extra ':', fix backend");
-		v++;
-	}
-	for (p = v; *p; p++) {
-
-		/* Find next header-name */
-		if (vct_issp(*p))
-			continue;
-		for (q = p; *q && !vct_issp(*q) && *q != ','; q++)
-			continue;
-
-		/* Build a header-matching string out of it */
-		VSB_clear(sbh);
-		VSB_printf(sbh, "%c%.*s:%c",
-		    (char)(1 + (q - p)), (int)(q - p), p, 0);
-		AZ(VSB_finish(sbh));
-
-		if (http_GetHdr(sp->http, VSB_data(sbh), &h)) {
-			AZ(vct_issp(*h));
-			/* Trim trailing space */
-			e = strchr(h, '\0');
-			while (e > h && vct_issp(e[-1]))
-				e--;
-			/* Encode two byte length and contents */
-			l = e - h;
-			assert(!(l & ~0xffff));
-		} else {
-			e = h;
-			l = 0xffff;
-		}
-		VSB_printf(sb, "%c%c", (unsigned)l >> 8, l & 0xff);
-		/* Append to vary matching string */
-		VSB_bcat(sb, VSB_data(sbh), VSB_len(sbh));
-		if (e != h)
-			VSB_bcat(sb, h, e - h);
-
-		while (vct_issp(*q))
-			q++;
-		if (*q == '\0')
-			break;
-		xxxassert(*q == ',');
-		p = q;
-	}
-	/* Terminate vary matching string */
-	VSB_printf(sb, "%c%c%c", 0xff, 0xff, 0);
-
-	VSB_delete(sbh);
-	AZ(VSB_finish(sb));
-	return(sb);
-}
-
-/*
- * Find length of a vary entry
- */
-static unsigned
-vry_len(const uint8_t *p)
-{
-	unsigned l = vbe16dec(p);
-
-	return (2 + p[2] + 2 + (l == 0xffff ? 0 : l));
-}
-
-/*
- * Compare two vary entries
- */
-static int
-vry_cmp(const uint8_t * const *v1, uint8_t * const *v2)
-{
-	unsigned retval = 0;
-
-	if (!memcmp(*v1, *v2, vry_len(*v1))) {
-		/* Same same */
-		retval = 0;
-	} else if (memcmp((*v1) + 2, (*v2) + 2, (*v1)[2] + 2)) {
-		/* Different header */
-		retval = 1;
-	} else if (cache_param->http_gzip_support &&
-	    !strcasecmp(H_Accept_Encoding, (const char*)((*v1)+2))) {
-		/*
-		 * If we do gzip processing, we do not vary on Accept-Encoding,
-		 * because we want everybody to get the gzip'ed object, and
-		 * varnish will gunzip as necessary.  We implement the skip at
-		 * check time, rather than create time, so that object in
-		 * persistent storage can be used with either setting of
-		 * http_gzip_support.
-		 */
-		retval = 0;
-	} else {
-		/* Same header, different content */
-		retval = 2;
-	}
-	return (retval);
-}
-
-int
-VRY_Match(struct sess *sp, const uint8_t *vary)
-{
-	uint8_t *vsp = sp->vary_b;
-	char *h, *e;
-	unsigned lh, ln;
-	int i, retval = 1, oflo = 0;
-
-	AN(vsp);
-	while (vary[2]) {
-		i = vry_cmp(&vary, &vsp);
-		if (i == 1) {
-			/* Build a new entry */
-
-			i = http_GetHdr(sp->http, (const char*)(vary+2), &h);
-			if (i) {
-				/* Trim trailing space */
-				e = strchr(h, '\0');
-				while (e > h && vct_issp(e[-1]))
-					e--;
-				lh = e - h;
-				assert(lh < 0xffff);
-			} else {
-				e = h = NULL;
-				lh = 0xffff;
-			}
-
-			/* Length of the entire new vary entry */
-			ln = 2 + vary[2] + 2 + (lh == 0xffff ? 0 : lh);
-			if (vsp + ln >= sp->vary_e) {
-				vsp = sp->vary_b;
-				oflo = 1;
-			}
-
-			/*
-			 * We MUST have space for one entry and the end marker
-			 * after it, which prevents old junk from confusing us
-			 */
-			assert(vsp + ln + 2 < sp->vary_e);
-
-			vbe16enc(vsp, (uint16_t)lh);
-			memcpy(vsp + 2, vary + 2, vary[2] + 2);
-			if (h != NULL && e != NULL) {
-				memcpy(vsp + 2 + vsp[2] + 2, h, e - h);
-				vsp[2 + vary[2] + 2 + (e - h) + 2] = '\0';
-			} else
-				vsp[2 + vary[2] + 2 + 2] = '\0';
-
-			i = vry_cmp(&vary, &vsp);
-			assert(i != 1);	/* hdr must be the same now */
-		}
-		if (i != 0)
-			retval = 0;
-		vsp += vry_len(vsp);
-		vary += vry_len(vary);
-	}
-	if (vsp + 3 > sp->vary_e)
-		oflo = 1;
-
-	if (oflo) {
-		/* XXX: Should log this */
-		vsp = sp->vary_b;
-	}
-	vsp[0] = 0xff;
-	vsp[1] = 0xff;
-	vsp[2] = 0;
-	if (oflo)
-		sp->vary_l = NULL;
-	else
-		sp->vary_l = vsp + 3;
-	return (retval);
-}
-
-void
-VRY_Validate(const uint8_t *vary)
-{
-
-	while (vary[2] != 0) {
-		assert(strlen((const char*)vary+3) == vary[2]);
-		vary += vry_len(vary);
-	}
-}
diff --git a/bin/varnishd/cache_vcl.c b/bin/varnishd/cache_vcl.c
deleted file mode 100644
index 068b482..0000000
--- a/bin/varnishd/cache_vcl.c
+++ /dev/null
@@ -1,365 +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.
- *
- * Interface *to* compiled VCL code:  Loading, unloading, calling into etc.
- *
- * The interface *from* the compiled VCL code is in cache_vrt.c.
- */
-
-#include "config.h"
-
-#include <dlfcn.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "vcl.h"
-#include "vcli.h"
-#include "vcli_priv.h"
-
-struct vcls {
-	unsigned		magic;
-#define VVCLS_MAGIC		0x214188f2
-	VTAILQ_ENTRY(vcls)	list;
-	char			*name;
-	void			*dlh;
-	struct VCL_conf		conf[1];
-};
-
-/*
- * XXX: Presently all modifications to this list happen from the
- * CLI event-engine, so no locking is necessary
- */
-static VTAILQ_HEAD(, vcls)	vcl_head =
-    VTAILQ_HEAD_INITIALIZER(vcl_head);
-
-
-static struct lock		vcl_mtx;
-static struct vcls		*vcl_active; /* protected by vcl_mtx */
-
-/*--------------------------------------------------------------------*/
-
-const char *
-VCL_Return_Name(unsigned method)
-{
-
-	switch (method) {
-#define VCL_RET_MAC(l, U, B) case VCL_RET_##U: return(#l);
-#include "tbl/vcl_returns.h"
-#undef VCL_RET_MAC
-	default:
-		return (NULL);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-VCL_Get(struct VCL_conf **vcc)
-{
-	static int once = 0;
-
-	while (!once && vcl_active == NULL) {
-		(void)sleep(1);
-	}
-	once = 1;
-
-	Lck_Lock(&vcl_mtx);
-	AN(vcl_active);
-	*vcc = vcl_active->conf;
-	AN(*vcc);
-	AZ((*vcc)->discard);
-	(*vcc)->busy++;
-	Lck_Unlock(&vcl_mtx);
-}
-
-void
-VCL_Refresh(struct VCL_conf **vcc)
-{
-	if (*vcc == vcl_active->conf)
-		return;
-	if (*vcc != NULL)
-		VCL_Rel(vcc);	/* XXX: optimize locking */
-	VCL_Get(vcc);
-}
-
-void
-VCL_Rel(struct VCL_conf **vcc)
-{
-	struct VCL_conf *vc;
-
-	AN(*vcc);
-	vc = *vcc;
-	*vcc = NULL;
-
-	Lck_Lock(&vcl_mtx);
-	assert(vc->busy > 0);
-	vc->busy--;
-	/*
-	 * We do not garbage collect discarded VCL's here, that happens
-	 * in VCL_Poll() which is called from the CLI thread.
-	 */
-	Lck_Unlock(&vcl_mtx);
-}
-
-/*--------------------------------------------------------------------*/
-
-static struct vcls *
-vcl_find(const char *name)
-{
-	struct vcls *vcl;
-
-	ASSERT_CLI();
-	VTAILQ_FOREACH(vcl, &vcl_head, list) {
-		if (vcl->conf->discard)
-			continue;
-		if (!strcmp(vcl->name, name))
-			return (vcl);
-	}
-	return (NULL);
-}
-
-static int
-VCL_Load(const char *fn, const char *name, struct cli *cli)
-{
-	struct vcls *vcl;
-	struct VCL_conf const *cnf;
-
-	ASSERT_CLI();
-	vcl = vcl_find(name);
-	if (vcl != NULL) {
-		VCLI_Out(cli, "Config '%s' already loaded", name);
-		return (1);
-	}
-
-	ALLOC_OBJ(vcl, VVCLS_MAGIC);
-	XXXAN(vcl);
-
-	vcl->dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
-
-	if (vcl->dlh == NULL) {
-		VCLI_Out(cli, "dlopen(%s): %s\n", fn, dlerror());
-		FREE_OBJ(vcl);
-		return (1);
-	}
-	cnf = dlsym(vcl->dlh, "VCL_conf");
-	if (cnf == NULL) {
-		VCLI_Out(cli, "Internal error: No VCL_conf symbol\n");
-		(void)dlclose(vcl->dlh);
-		FREE_OBJ(vcl);
-		return (1);
-	}
-	memcpy(vcl->conf, cnf, sizeof *cnf);
-
-	if (vcl->conf->magic != VCL_CONF_MAGIC) {
-		VCLI_Out(cli, "Wrong VCL_CONF_MAGIC\n");
-		(void)dlclose(vcl->dlh);
-		FREE_OBJ(vcl);
-		return (1);
-	}
-	if (vcl->conf->init_vcl(cli)) {
-		VCLI_Out(cli, "VCL \"%s\" Failed to initialize", name);
-		(void)dlclose(vcl->dlh);
-		FREE_OBJ(vcl);
-		return (1);
-	}
-	REPLACE(vcl->name, name);
-	VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
-	VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
-	(void)vcl->conf->init_func(NULL);
-	Lck_Lock(&vcl_mtx);
-	if (vcl_active == NULL)
-		vcl_active = vcl;
-	Lck_Unlock(&vcl_mtx);
-	VSC_C_main->n_vcl++;
-	VSC_C_main->n_vcl_avail++;
-	return (0);
-}
-
-/*--------------------------------------------------------------------
- * This function is polled from the CLI thread to dispose of any non-busy
- * VCLs which have been discarded.
- */
-
-static void
-VCL_Nuke(struct vcls *vcl)
-{
-
-	ASSERT_CLI();
-	assert(vcl != vcl_active);
-	assert(vcl->conf->discard);
-	assert(vcl->conf->busy == 0);
-	VTAILQ_REMOVE(&vcl_head, vcl, list);
-	(void)vcl->conf->fini_func(NULL);
-	vcl->conf->fini_vcl(NULL);
-	free(vcl->name);
-	(void)dlclose(vcl->dlh);
-	FREE_OBJ(vcl);
-	VSC_C_main->n_vcl--;
-	VSC_C_main->n_vcl_discard--;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VCL_Poll(void)
-{
-	struct vcls *vcl, *vcl2;
-
-	ASSERT_CLI();
-	VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2)
-		if (vcl->conf->discard && vcl->conf->busy == 0)
-			VCL_Nuke(vcl);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ccf_config_list(struct cli *cli, const char * const *av, void *priv)
-{
-	struct vcls *vcl;
-	const char *flg;
-
-	(void)av;
-	(void)priv;
-	ASSERT_CLI();
-	VTAILQ_FOREACH(vcl, &vcl_head, list) {
-		if (vcl == vcl_active) {
-			flg = "active";
-		} else if (vcl->conf->discard) {
-			flg = "discarded";
-		} else
-			flg = "available";
-		VCLI_Out(cli, "%-10s %6u %s\n",
-		    flg,
-		    vcl->conf->busy,
-		    vcl->name);
-	}
-}
-
-static void
-ccf_config_load(struct cli *cli, const char * const *av, void *priv)
-{
-
-	(void)av;
-	(void)priv;
-	ASSERT_CLI();
-	if (VCL_Load(av[3], av[2], cli))
-		VCLI_SetResult(cli, CLIS_PARAM);
-	return;
-}
-
-static void
-ccf_config_discard(struct cli *cli, const char * const *av, void *priv)
-{
-	struct vcls *vcl;
-
-	ASSERT_CLI();
-	(void)av;
-	(void)priv;
-	vcl = vcl_find(av[2]);
-	if (vcl == NULL) {
-		VCLI_SetResult(cli, CLIS_PARAM);
-		VCLI_Out(cli, "VCL '%s' unknown", av[2]);
-		return;
-	}
-	Lck_Lock(&vcl_mtx);
-	if (vcl == vcl_active) {
-		Lck_Unlock(&vcl_mtx);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		VCLI_Out(cli, "VCL %s is the active VCL", av[2]);
-		return;
-	}
-	VSC_C_main->n_vcl_discard++;
-	VSC_C_main->n_vcl_avail--;
-	vcl->conf->discard = 1;
-	Lck_Unlock(&vcl_mtx);
-	if (vcl->conf->busy == 0)
-		VCL_Nuke(vcl);
-}
-
-static void
-ccf_config_use(struct cli *cli, const char * const *av, void *priv)
-{
-	struct vcls *vcl;
-	int i;
-
-	(void)av;
-	(void)priv;
-	vcl = vcl_find(av[2]);
-	if (vcl == NULL) {
-		VCLI_Out(cli, "No VCL named '%s'", av[2]);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return;
-	}
-	Lck_Lock(&vcl_mtx);
-	vcl_active = vcl;
-	Lck_Unlock(&vcl_mtx);
-
-	/* Tickle this VCL's backends to take over health polling */
-	for(i = 1; i < vcl->conf->ndirector; i++)
-		VBE_UseHealth(vcl->conf->director[i]);
-}
-
-/*--------------------------------------------------------------------*/
-
-#define VCL_MET_MAC(func, upper, bitmap)				\
-void									\
-VCL_##func##_method(struct sess *sp)					\
-{									\
-									\
-	sp->handling = 0;						\
-	sp->cur_method = VCL_MET_ ## upper;				\
-	WSP(sp, SLT_VCL_call, "%s", #func);				\
-	(void)sp->vcl->func##_func(sp);					\
-	WSP(sp, SLT_VCL_return, "%s", VCL_Return_Name(sp->handling));	\
-	sp->cur_method = 0;						\
-	assert((1U << sp->handling) & bitmap);				\
-	assert(!((1U << sp->handling) & ~bitmap));			\
-}
-
-#include "tbl/vcl_returns.h"
-#undef VCL_MET_MAC
-
-/*--------------------------------------------------------------------*/
-
-static struct cli_proto vcl_cmds[] = {
-	{ CLI_VCL_LOAD,         "i", ccf_config_load },
-	{ CLI_VCL_LIST,         "i", ccf_config_list },
-	{ CLI_VCL_DISCARD,      "i", ccf_config_discard },
-	{ CLI_VCL_USE,          "i", ccf_config_use },
-	{ NULL }
-};
-
-void
-VCL_Init()
-{
-
-	CLI_AddFuncs(vcl_cmds);
-	Lck_New(&vcl_mtx, lck_vcl);
-}
diff --git a/bin/varnishd/cache_vrt.c b/bin/varnishd/cache_vrt.c
deleted file mode 100644
index c20b552..0000000
--- a/bin/varnishd/cache_vrt.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2010 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.
- *
- * Runtime support for compiled VCL programs
- */
-
-#include "config.h"
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-
-#include "cache_backend.h"
-#include "hash/hash_slinger.h"
-#include "vav.h"
-#include "vcl.h"
-#include "vrt.h"
-#include "vrt_obj.h"
-#include "vtim.h"
-
-const void * const vrt_magic_string_end = &vrt_magic_string_end;
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_error(struct sess *sp, unsigned code, const char *reason)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	WSL(sp->wrk, SLT_Debug, 0, "VCL_error(%u, %s)", code, reason ?
-	    reason : "(null)");
-	if (code < 100 || code > 999)
-		code = 503;
-	sp->err_code = (uint16_t)code;
-	sp->err_reason = reason ? reason : http_StatusMessage(sp->err_code);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_count(const struct sess *sp, unsigned u)
-{
-
-	if (sp == NULL)
-		return;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	if (cache_param->vcl_trace)
-		WSP(sp, SLT_VCL_trace, "%u %d.%d", u,
-		    sp->vcl->ref[u].line, sp->vcl->ref[u].pos);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_acl_log(const struct sess *sp, const char *msg)
-{
-	WSP(sp, SLT_VCL_acl, msg);
-}
-
-/*--------------------------------------------------------------------*/
-
-static struct http *
-vrt_selecthttp(const struct sess *sp, enum gethdr_e where)
-{
-	struct http *hp;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	switch (where) {
-	case HDR_REQ:
-		hp = sp->http;
-		break;
-	case HDR_BEREQ:
-		hp = sp->wrk->bereq;
-		break;
-	case HDR_BERESP:
-		hp = sp->wrk->beresp;
-		break;
-	case HDR_RESP:
-		hp = sp->wrk->resp;
-		break;
-	case HDR_OBJ:
-		CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-		hp = sp->obj->http;
-		break;
-	default:
-		INCOMPL();
-	}
-	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
-	return (hp);
-}
-
-char *
-VRT_GetHdr(const struct sess *sp, enum gethdr_e where, const char *n)
-{
-	char *p;
-	struct http *hp;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	hp = vrt_selecthttp(sp, where);
-	if (!http_GetHdr(hp, n, &p))
-		return (NULL);
-	return (p);
-}
-
-/*--------------------------------------------------------------------
- * XXX: Optimize the single element case ?
- */
-
-char *
-VRT_StringList(char *d, unsigned dl, const char *p, va_list ap)
-{
-	char *b, *e;
-	unsigned x;
-
-	b = d;
-	e = b + dl;
-	while (p != vrt_magic_string_end && b < e) {
-		if (p != NULL) {
-			x = strlen(p);
-			if (b + x < e)
-				memcpy(b, p, x);
-			b += x;
-		}
-		p = va_arg(ap, const char *);
-	}
-	if (b >= e)
-		return (NULL);
-	*b++ = '\0';
-	return (b);
-}
-
-/*--------------------------------------------------------------------
- * XXX: Optimize the single element case ?
- */
-
-char *
-VRT_String(struct ws *ws, const char *h, const char *p, va_list ap)
-{
-	char *b, *e;
-	unsigned u, x;
-
-	u = WS_Reserve(ws, 0);
-	e = b = ws->f;
-	e += u;
-	if (h != NULL) {
-		x = strlen(h);
-		if (b + x < e)
-			memcpy(b, h, x);
-		b += x;
-		if (b < e)
-			*b = ' ';
-		b++;
-	}
-	b = VRT_StringList(b, e > b ? e - b : 0, p, ap);
-	if (b == NULL || b == e) {
-		WS_Release(ws, 0);
-		return (NULL);
-	}
-	e = b;
-	b = ws->f;
-	WS_Release(ws, e - b);
-	return (b);
-}
-
-/*--------------------------------------------------------------------
- * Build a string on the worker threads workspace
- */
-
-const char *
-VRT_WrkString(const struct sess *sp, const char *p, ...)
-{
-	va_list ap;
-	char *b;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	va_start(ap, p);
-	b = VRT_String(sp->wrk->ws, NULL, p, ap);
-	va_end(ap);
-	return (b);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_SetHdr(const struct sess *sp , enum gethdr_e where, const char *hdr,
-    const char *p, ...)
-{
-	struct http *hp;
-	va_list ap;
-	char *b;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	hp = vrt_selecthttp(sp, where);
-	va_start(ap, p);
-	if (p == NULL) {
-		http_Unset(hp, hdr);
-	} else {
-		b = VRT_String(hp->ws, hdr + 1, p, ap);
-		if (b == NULL) {
-			WSP(sp, SLT_LostHeader, "%s", hdr + 1);
-		} else {
-			http_Unset(hp, hdr);
-			http_SetHeader(sp->wrk, sp->vsl_id, hp, b);
-		}
-	}
-	va_end(ap);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_handling(struct sess *sp, unsigned hand)
-{
-
-	if (sp == NULL) {
-		assert(hand == VCL_RET_OK);
-		return;
-	}
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	assert(hand < VCL_RET_MAX);
-	sp->handling = hand;
-}
-
-/*--------------------------------------------------------------------
- * Add an element to the array/list of hash bits.
- */
-
-void
-VRT_hashdata(const struct sess *sp, const char *str, ...)
-{
-	va_list ap;
-	const char *p;
-
-	HSH_AddString(sp, str);
-	va_start(ap, str);
-	while (1) {
-		p = va_arg(ap, const char *);
-		if (p == vrt_magic_string_end)
-			break;
-		HSH_AddString(sp, p);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-double
-VRT_r_now(const struct sess *sp)
-{
-
-	(void)sp;
-	return (VTIM_real());
-}
-
-/*--------------------------------------------------------------------*/
-
-char *
-VRT_IP_string(const struct sess *sp, const struct sockaddr_storage *sa)
-{
-	char *p;
-	const struct sockaddr_in *si4;
-	const struct sockaddr_in6 *si6;
-	const void *addr;
-	int len;
-
-	switch (sa->ss_family) {
-	case AF_INET:
-		len = INET_ADDRSTRLEN;
-		si4 = (const void *)sa;
-		addr = &(si4->sin_addr);
-		break;
-	case AF_INET6:
-		len = INET6_ADDRSTRLEN;
-		si6 = (const void *)sa;
-		addr = &(si6->sin6_addr);
-		break;
-	default:
-		INCOMPL();
-	}
-	XXXAN(len);
-	AN(p = WS_Alloc(sp->http->ws, len));
-	AN(inet_ntop(sa->ss_family, addr, p, len));
-	return (p);
-}
-
-char *
-VRT_int_string(const struct sess *sp, int num)
-{
-	char *p;
-	int size;
-
-	size = snprintf(NULL, 0, "%d", num) + 1;
-	AN(p = WS_Alloc(sp->http->ws, size));
-	assert(snprintf(p, size, "%d", num) < size);
-	return (p);
-}
-
-char *
-VRT_double_string(const struct sess *sp, double num)
-{
-	char *p;
-	int size;
-
-	size = snprintf(NULL, 0, "%.3f", num) + 1;
-	AN(p = WS_Alloc(sp->http->ws, size));
-	assert(snprintf(p, size, "%.3f", num) < size);
-	return (p);
-}
-
-char *
-VRT_time_string(const struct sess *sp, double t)
-{
-	char *p;
-
-	AN(p = WS_Alloc(sp->http->ws, VTIM_FORMAT_SIZE));
-	VTIM_format(t, p);
-	return (p);
-}
-
-const char *
-VRT_backend_string(const struct sess *sp, const struct director *d)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	if (d == NULL)
-		d = sp->director;
-	if (d == NULL)
-		return (NULL);
-	return (d->vcl_name);
-}
-
-const char *
-VRT_bool_string(const struct sess *sp, unsigned val)
-{
-
-	(void)sp;
-	return (val ? "true" : "false");
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_Rollback(struct sess *sp)
-{
-
-	HTTP_Copy(sp->http, sp->http0);
-	WS_Reset(sp->ws, sp->ws_req);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_panic(const struct sess *sp, const char *str, ...)
-{
-	va_list ap;
-	char *b;
-
-	va_start(ap, str);
-	b = VRT_String(sp->http->ws, "PANIC: ", str, ap);
-	va_end(ap);
-	VAS_Fail("VCL", "", 0, b, 0, 2);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_synth_page(const struct sess *sp, unsigned flags, const char *str, ...)
-{
-	va_list ap;
-	const char *p;
-	struct vsb *vsb;
-
-	(void)flags;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
-	vsb = SMS_Makesynth(sp->obj);
-	AN(vsb);
-
-	VSB_cat(vsb, str);
-	va_start(ap, str);
-	p = va_arg(ap, const char *);
-	while (p != vrt_magic_string_end) {
-		if (p == NULL)
-			p = "(null)";
-		VSB_cat(vsb, p);
-		p = va_arg(ap, const char *);
-	}
-	va_end(ap);
-	SMS_Finish(sp->obj);
-	http_Unset(sp->obj->http, H_Content_Length);
-	http_PrintfHeader(sp->wrk, sp->vsl_id, sp->obj->http,
-	    "Content-Length: %d", sp->obj->len);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_ban(struct sess *sp, char *cmds, ...)
-{
-	char *a1, *a2, *a3;
-	va_list ap;
-	struct ban *b;
-	int good;
-
-	(void)sp;
-	b = BAN_New();
-	va_start(ap, cmds);
-	a1 = cmds;
-	good = 0;
-	while (a1 != NULL) {
-		good = 0;
-		a2 = va_arg(ap, char *);
-		if (a2 == NULL)
-			break;
-		a3 = va_arg(ap, char *);
-		if (a3 == NULL)
-			break;
-		if (BAN_AddTest(NULL, b, a1, a2, a3))
-			break;
-		a1 = va_arg(ap, char *);
-		good = 1;
-	}
-	if (!good)
-		/* XXX: report error how ? */
-		BAN_Free(b);
-	else
-		BAN_Insert(b);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_ban_string(struct sess *sp, const char *str)
-{
-	char *a1, *a2, *a3;
-	char **av;
-	struct ban *b;
-	int good;
-	int i;
-
-	(void)sp;
-	av = VAV_Parse(str, NULL, ARGV_NOESC);
-	if (av[0] != NULL) {
-		/* XXX: report error how ? */
-		VAV_Free(av);
-		return;
-	}
-	b = BAN_New();
-	good = 0;
-	for (i = 1; ;) {
-		a1 = av[i++];
-		if (a1 == NULL)
-			break;
-		good = 0;
-		a2 = av[i++];
-		if (a2 == NULL)
-			break;
-		a3 = av[i++];
-		if (a3 == NULL)
-			break;
-		if (BAN_AddTest(NULL, b, a1, a2, a3))
-			break;
-		good = 1;
-		if (av[i] == NULL)
-			break;
-		good = 0;
-		if (strcmp(av[i++], "&&"))
-			break;
-	}
-	if (!good)
-		/* XXX: report error how ? */
-		BAN_Free(b);
-	else
-		BAN_Insert(b);
-	VAV_Free(av);
-}
-
-/*--------------------------------------------------------------------
- * "real" purges
- */
-
-void
-VRT_purge(const struct sess *sp, double ttl, double grace)
-{
-	if (sp->cur_method == VCL_MET_HIT)
-		HSH_Purge(sp, sp->obj->objcore->objhead, ttl, grace);
-	else if (sp->cur_method == VCL_MET_MISS)
-		HSH_Purge(sp, sp->objcore->objhead, ttl, grace);
-}
-
-/*--------------------------------------------------------------------
- * Simple stuff
- */
-
-int
-VRT_strcmp(const char *s1, const char *s2)
-{
-	if (s1 == NULL || s2 == NULL)
-		return(1);
-	return (strcmp(s1, s2));
-}
-
-void
-VRT_memmove(void *dst, const void *src, unsigned len)
-{
-
-	(void)memmove(dst, src, len);
-}
diff --git a/bin/varnishd/cache_vrt_re.c b/bin/varnishd/cache_vrt_re.c
deleted file mode 100644
index 7759d0a..0000000
--- a/bin/varnishd/cache_vrt_re.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2009 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.
- *
- * Runtime support for compiled VCL programs, regexps
- */
-
-#include "config.h"
-
-#include <ctype.h>
-
-#include "cache.h"
-
-#include "vre.h"
-#include "vrt.h"
-
-void
-VRT_re_init(void **rep, const char *re)
-{
-	vre_t *t;
-	const char *error;
-	int erroroffset;
-
-	/* This was already check-compiled by the VCL compiler */
-	t = VRE_compile(re, 0, &error, &erroroffset);
-	AN(t);
-	*rep = t;
-}
-
-void
-VRT_re_fini(void *rep)
-{
-	vre_t *vv;
-
-	vv = rep;
-	if (rep != NULL)
-		VRE_free(&vv);
-}
-
-int
-VRT_re_match(const struct sess *sp, const char *s, void *re)
-{
-	vre_t *t;
-	int i;
-
-	if (s == NULL)
-		s = "";
-	AN(re);
-	t = re;
-	i = VRE_exec(t, s, strlen(s), 0, 0, NULL, 0, &cache_param->vre_limits);
-	if (i >= 0)
-		return (1);
-	if (i < VRE_ERROR_NOMATCH )
-		WSP(sp, SLT_VCL_Error, "Regexp matching returned %d", i);
-	return (0);
-}
-
-const char *
-VRT_regsub(const struct sess *sp, int all, const char *str, void *re,
-    const char *sub)
-{
-	int ovector[30];
-	vre_t *t;
-	int i, l;
-	txt res;
-	char *b0;
-	const char *s;
-	unsigned u, x;
-	int options = 0;
-	size_t len;
-
-	AN(re);
-	if (str == NULL)
-		str = "";
-	t = re;
-	memset(ovector, 0, sizeof(ovector));
-	len = strlen(str);
-	i = VRE_exec(t, str, len, 0, options, ovector, 30,
-	    &cache_param->vre_limits);
-
-	/* If it didn't match, we can return the original string */
-	if (i == VRE_ERROR_NOMATCH)
-		return(str);
-	if (i < VRE_ERROR_NOMATCH ) {
-		WSP(sp, SLT_VCL_Error, "Regexp matching returned %d", i);
-		return(str);
-	}
-
-	u = WS_Reserve(sp->http->ws, 0);
-	res.e = res.b = b0 = sp->http->ws->f;
-	res.e += u;
-
-	do {
-		/* Copy prefix to match */
-		Tadd(&res, str, ovector[0]);
-		for (s = sub ; *s != '\0'; s++ ) {
-			if (*s != '\\' || s[1] == '\0') {
-				if (res.b < res.e)
-					*res.b++ = *s;
-				continue;
-			}
-			s++;
-			if (isdigit(*s)) {
-				x = *s - '0';
-				l = ovector[2*x+1] - ovector[2*x];
-				Tadd(&res, str + ovector[2*x], l);
-				continue;
-			} else {
-				if (res.b < res.e)
-					*res.b++ = *s;
-			}
-		}
-		str += ovector[1];
-		len -= ovector[1];
-		if (!all)
-			break;
-		memset(&ovector, 0, sizeof(ovector));
-		options |= VRE_NOTEMPTY_ATSTART;
-		i = VRE_exec(t, str, len, 0, options, ovector, 30,
-		    &cache_param->vre_limits);
-		if (i < VRE_ERROR_NOMATCH ) {
-			WS_Release(sp->http->ws, 0);
-			WSP(sp, SLT_VCL_Error,
-			    "Regexp matching returned %d", i);
-			return(str);
-		}
-	} while (i != VRE_ERROR_NOMATCH);
-
-	/* Copy suffix to match */
-	Tadd(&res, str, len+1);
-	if (res.b >= res.e) {
-		WS_Release(sp->http->ws, 0);
-		return (str);
-	}
-	Tcheck(res);
-	WS_ReleaseP(sp->http->ws, res.b);
-	return (b0);
-}
diff --git a/bin/varnishd/cache_vrt_var.c b/bin/varnishd/cache_vrt_var.c
deleted file mode 100644
index 860c7aa..0000000
--- a/bin/varnishd/cache_vrt_var.c
+++ /dev/null
@@ -1,550 +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.
- *
- * Runtime support for compiled VCL programs
- */
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "cache.h"
-#include "common/heritage.h"
-
-#include "cache_backend.h"
-#include "vrt_obj.h"
-#include "vtcp.h"
-#include "vtim.h"
-
-static char vrt_hostname[255] = "";
-
-/*--------------------------------------------------------------------*/
-
-static void
-vrt_do_string(struct worker *w, int fd, const struct http *hp, int fld,
-    const char *err, const char *p, va_list ap)
-{
-	char *b;
-
-	// AN(p);
-	AN(hp);
-	b = VRT_String(hp->ws, NULL, p, ap);
-	if (b == NULL || *b == '\0') {
-		WSL(w, SLT_LostHeader, fd, err);
-	} else {
-		http_SetH(hp, fld, b);
-	}
-	va_end(ap);
-}
-
-#define VRT_DO_HDR(obj, hdr, http, fld)				\
-void								\
-VRT_l_##obj##_##hdr(const struct sess *sp, const char *p, ...)	\
-{								\
-	va_list ap;						\
-								\
-	va_start(ap, p);					\
-	vrt_do_string(sp->wrk, sp->fd,				\
-	    http, fld, #obj "." #hdr, p, ap);			\
-	va_end(ap);						\
-}								\
-								\
-const char *							\
-VRT_r_##obj##_##hdr(const struct sess *sp)			\
-{								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	CHECK_OBJ_NOTNULL(http, HTTP_MAGIC);			\
-	return (http->hd[fld].b);				\
-}
-
-VRT_DO_HDR(req,   request,	sp->http,		HTTP_HDR_REQ)
-VRT_DO_HDR(req,   url,		sp->http,		HTTP_HDR_URL)
-VRT_DO_HDR(req,   proto,	sp->http,		HTTP_HDR_PROTO)
-VRT_DO_HDR(bereq, request,	sp->wrk->bereq,		HTTP_HDR_REQ)
-VRT_DO_HDR(bereq, url,		sp->wrk->bereq,		HTTP_HDR_URL)
-VRT_DO_HDR(bereq, proto,	sp->wrk->bereq,		HTTP_HDR_PROTO)
-VRT_DO_HDR(obj,   proto,	sp->obj->http,		HTTP_HDR_PROTO)
-VRT_DO_HDR(obj,   response,	sp->obj->http,		HTTP_HDR_RESPONSE)
-VRT_DO_HDR(resp,  proto,	sp->wrk->resp,		HTTP_HDR_PROTO)
-VRT_DO_HDR(resp,  response,	sp->wrk->resp,		HTTP_HDR_RESPONSE)
-VRT_DO_HDR(beresp,  proto,	sp->wrk->beresp,	HTTP_HDR_PROTO)
-VRT_DO_HDR(beresp,  response,	sp->wrk->beresp,	HTTP_HDR_RESPONSE)
-
-/*--------------------------------------------------------------------*/
-
-#define VRT_DO_STATUS(obj, http)				\
-void								\
-VRT_l_##obj##_status(const struct sess *sp, int num)		\
-{								\
-								\
-	assert(num >= 100 && num <= 999);			\
-	http->status = (uint16_t)num;				\
-}								\
-								\
-int								\
-VRT_r_##obj##_status(const struct sess *sp)			\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	return(http->status);					\
-}
-
-VRT_DO_STATUS(obj, sp->obj->http)
-VRT_DO_STATUS(beresp, sp->wrk->beresp)
-VRT_DO_STATUS(resp, sp->wrk->resp)
-
-/*--------------------------------------------------------------------*/
-
-/* XXX: review this */
-/* Add an objecthead to the saintmode list for the (hopefully) relevant
- * backend. Some double-up asserting here to avoid assert-errors when there
- * is no object.
- */
-void
-VRT_l_beresp_saintmode(const struct sess *sp, double a)
-{
-	struct trouble *new;
-	struct trouble *tr;
-	struct trouble *tr2;
-	struct worker *wrk;
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	wrk = sp->wrk;
-	if (!wrk->vbc)
-		return;
-	CHECK_OBJ_NOTNULL(wrk->vbc, VBC_MAGIC);
-	if (!wrk->vbc->backend)
-		return;
-	CHECK_OBJ_NOTNULL(wrk->vbc->backend, BACKEND_MAGIC);
-	if (!sp->objcore)
-		return;
-	CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
-
-	/* Setting a negative holdoff period is a mistake. Detecting this
-	 * when compiling the VCL would be better.
-	 */
-	assert(a > 0);
-
-	ALLOC_OBJ(new, TROUBLE_MAGIC);
-	AN(new);
-	new->target = (uintptr_t)(sp->objcore->objhead);
-	new->timeout = sp->t_req + a;
-
-	/* Insert the new item on the list before the first item with a
-	 * timeout at a later date (ie: sort by which entry will time out
-	 * from the list
-	 */
-	Lck_Lock(&wrk->vbc->backend->mtx);
-	VTAILQ_FOREACH_SAFE(tr, &wrk->vbc->backend->troublelist, list, tr2) {
-		if (tr->timeout < new->timeout) {
-			VTAILQ_INSERT_BEFORE(tr, new, list);
-			new = NULL;
-			break;
-		}
-	}
-
-	/* Insert the item at the end if the list is empty or all other
-	 * items have a longer timeout.
-	 */
-	if (new)
-		VTAILQ_INSERT_TAIL(&wrk->vbc->backend->troublelist, new, list);
-
-	Lck_Unlock(&wrk->vbc->backend->mtx);
-}
-
-/*--------------------------------------------------------------------*/
-
-#define VBERESP(dir, type, onm, field)					\
-void									\
-VRT_l_##dir##_##onm(const struct sess *sp, type a)			\
-{									\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
-	sp->wrk->field = a;						\
-}									\
-									\
-type									\
-VRT_r_##dir##_##onm(const struct sess *sp)				\
-{									\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);				\
-	return (sp->wrk->field);					\
-}
-
-VBERESP(beresp, unsigned, do_esi, do_esi)
-VBERESP(beresp, unsigned, do_gzip, do_gzip)
-VBERESP(beresp, unsigned, do_gunzip, do_gunzip)
-VBERESP(beresp, unsigned, do_stream, do_stream)
-
-/*--------------------------------------------------------------------*/
-
-const char * __match_proto__()
-VRT_r_client_identity(struct sess *sp)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	if (sp->client_identity != NULL)
-		return (sp->client_identity);
-	else
-		return (sp->addr);
-}
-
-void
-VRT_l_client_identity(struct sess *sp, const char *str, ...)
-{
-	va_list ap;
-	char *b;
-
-	va_start(ap, str);
-	b = VRT_String(sp->http->ws, NULL, str, ap);
-	va_end(ap);
-	sp->client_identity = b;
-}
-
-/*--------------------------------------------------------------------*/
-
-#define BEREQ_TIMEOUT(which)					\
-void __match_proto__()						\
-VRT_l_bereq_##which(struct sess *sp, double num)		\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	sp->wrk->which = (num > 0.0 ? num : 0.0);		\
-}								\
-								\
-double __match_proto__()					\
-VRT_r_bereq_##which(struct sess *sp)				\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	return(sp->wrk->which);					\
-}
-
-BEREQ_TIMEOUT(connect_timeout)
-BEREQ_TIMEOUT(first_byte_timeout)
-BEREQ_TIMEOUT(between_bytes_timeout)
-
-/*--------------------------------------------------------------------*/
-
-const char *
-VRT_r_beresp_backend_name(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
-	return(sp->wrk->vbc->backend->vcl_name);
-}
-
-struct sockaddr_storage *
-VRT_r_beresp_backend_ip(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
-	return(sp->wrk->vbc->addr);
-}
-
-int
-VRT_r_beresp_backend_port(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	CHECK_OBJ_NOTNULL(sp->wrk->vbc, VBC_MAGIC);
-	return (VTCP_port(sp->wrk->vbc->addr));
-}
-
-const char * __match_proto__()
-VRT_r_beresp_storage(struct sess *sp)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	if (sp->wrk->storage_hint != NULL)
-		return (sp->wrk->storage_hint);
-	else
-		return (NULL);
-}
-
-void __match_proto__()
-VRT_l_beresp_storage(struct sess *sp, const char *str, ...)
-{
-	va_list ap;
-	char *b;
-
-	va_start(ap, str);
-	b = VRT_String(sp->wrk->ws, NULL, str, ap);
-	va_end(ap);
-	sp->wrk->storage_hint = b;
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_l_req_backend(struct sess *sp, struct director *be)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	sp->director = be;
-}
-
-struct director * __match_proto__()
-VRT_r_req_backend(struct sess *sp)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return (sp->director);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_l_req_esi(struct sess *sp, unsigned process_esi)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	/*
-	 * Only allow you to turn of esi in the main request
-	 * else everything gets confused
-	 */
-	if(sp->esi_level == 0)
-		sp->disable_esi = !process_esi;
-}
-
-unsigned __match_proto__()
-VRT_r_req_esi(struct sess *sp)
-{
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return (!sp->disable_esi);
-}
-
-int
-VRT_r_req_esi_level(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return(sp->esi_level);
-}
-
-/*--------------------------------------------------------------------*/
-
-unsigned __match_proto__()
-VRT_r_req_can_gzip(struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return (RFC2616_Req_Gzip(sp));
-}
-
-
-/*--------------------------------------------------------------------*/
-
-int
-VRT_r_req_restarts(const struct sess *sp)
-{
-
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-	return (sp->restarts);
-}
-
-/*--------------------------------------------------------------------
- * NB: TTL is relative to when object was created, whereas grace and
- * keep are relative to ttl.
- */
-
-#define VRT_DO_EXP(which, exp, fld, offset, extra)		\
-								\
-void __match_proto__()						\
-VRT_l_##which##_##fld(struct sess *sp, double a)		\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	if (a > 0.)						\
-		a += offset;					\
-	EXP_Set_##fld(&exp, a);					\
-	extra;							\
-}								\
-								\
-double __match_proto__()					\
-VRT_r_##which##_##fld(struct sess *sp)				\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	return(EXP_Get_##fld(&exp) - offset);			\
-}
-
-static void
-vrt_wsp_exp(const struct sess *sp, unsigned xid, const struct exp *e)
-{
-	WSP(sp, SLT_TTL, "%u VCL %.0f %.0f %.0f %.0f %.0f",
-	    xid, e->ttl - (sp->t_req - e->entered), e->grace, e->keep,
-	    sp->t_req, e->age + (sp->t_req - e->entered));
-}
-
-VRT_DO_EXP(req, sp->exp, ttl, 0, )
-VRT_DO_EXP(req, sp->exp, grace, 0, )
-VRT_DO_EXP(req, sp->exp, keep, 0, )
-
-VRT_DO_EXP(obj, sp->obj->exp, grace, 0,
-   EXP_Rearm(sp->obj);
-   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
-VRT_DO_EXP(obj, sp->obj->exp, ttl, (sp->t_req - sp->obj->exp.entered),
-   EXP_Rearm(sp->obj);
-   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
-VRT_DO_EXP(obj, sp->obj->exp, keep, 0,
-   EXP_Rearm(sp->obj);
-   vrt_wsp_exp(sp, sp->obj->xid, &sp->obj->exp);)
-
-VRT_DO_EXP(beresp, sp->wrk->exp, grace, 0,
-   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
-VRT_DO_EXP(beresp, sp->wrk->exp, ttl, 0,
-   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
-VRT_DO_EXP(beresp, sp->wrk->exp, keep, 0,
-   vrt_wsp_exp(sp, sp->xid, &sp->wrk->exp);)
-
-/*--------------------------------------------------------------------
- * req.xid
- */
-
-const char * __match_proto__()
-VRT_r_req_xid(struct sess *sp)
-{
-	char *p;
-	int size;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	size = snprintf(NULL, 0, "%u", sp->xid) + 1;
-	AN(p = WS_Alloc(sp->http->ws, size));
-	assert(snprintf(p, size, "%u", sp->xid) < size);
-	return (p);
-}
-
-/*--------------------------------------------------------------------*/
-
-#define REQ_BOOL(which)						\
-void __match_proto__()						\
-VRT_l_req_##which(struct sess *sp, unsigned val)		\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	sp->which = val ? 1 : 0;				\
-}								\
-								\
-unsigned __match_proto__()					\
-VRT_r_req_##which(struct sess *sp)				\
-{								\
-								\
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);			\
-	return(sp->which);					\
-}
-
-REQ_BOOL(hash_ignore_busy)
-REQ_BOOL(hash_always_miss)
-
-/*--------------------------------------------------------------------*/
-
-struct sockaddr_storage *
-VRT_r_client_ip(struct sess *sp)
-{
-
-	return (&sp->sockaddr);
-}
-
-struct sockaddr_storage *
-VRT_r_server_ip(struct sess *sp)
-{
-	int i;
-
-	if (sp->mysockaddr.ss_family == AF_UNSPEC) {
-		i = getsockname(sp->fd,
-		    (void*)&sp->mysockaddr, &sp->mysockaddrlen);
-		assert(VTCP_Check(i));
-	}
-
-	return (&sp->mysockaddr);
-}
-
-const char*
-VRT_r_server_identity(struct sess *sp)
-{
-	(void)sp;
-
-	if (heritage.identity[0] != '\0')
-		return (heritage.identity);
-	else
-		return (heritage.name);
-}
-
-
-const char*
-VRT_r_server_hostname(struct sess *sp)
-{
-	(void)sp;
-
-	if (vrt_hostname[0] == '\0')
-		AZ(gethostname(vrt_hostname, sizeof(vrt_hostname)));
-
-	return (vrt_hostname);
-}
-
-/*--------------------------------------------------------------------
- * XXX: This is pessimistically silly
- */
-
-int
-VRT_r_server_port(struct sess *sp)
-{
-	int i;
-
-	if (sp->mysockaddr.ss_family == AF_UNSPEC) {
-		i = getsockname(sp->fd,
-		    (void*)&sp->mysockaddr, &sp->mysockaddrlen);
-		assert(VTCP_Check(i));
-	}
-	return (VTCP_port(&sp->mysockaddr));
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-VRT_r_obj_hits(const struct sess *sp)