[master] f3a414f Stuff all of the worker process into a cache/ subdirectory.
Poul-Henning Kamp
phk at varnish-cache.org
Sun Nov 13 11:56:49 CET 2011
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)
-{
-
- 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 *