text: /*-
   * Copyright (c) 2006 Verdens Gang AS
   * Copyright (c) 2006-2011 Varnish Software AS
   * All rights reserved.
   *
   * Author: Poul-Henning Kamp <phk@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 <errno.h>
  #include <unistd.h>
  #include <stdarg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "vct.h"
  #include "cache.h"
  
  #define HTTPH(a, b, c, d, e, f, g) char b[] = "*" a ":";
  #include "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, int fd, const struct http *hp, unsigned hdr)
  {
  
  	WSLR(w, http2shmlog(hp, hdr), fd, 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 "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;
  }
  
  /*--------------------------------------------------------------------*/
  
  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(struct http *hp, uint16_t *p_err_code)
  {
  	char *p, *q;
  	const char *ret;
  	unsigned u;
  
  	AN(p_err_code);
  
  	http_CollectHdr(hp, H_Connection);
  	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";
  
  		/* Refuse removal of well-known-headers if they would pass. */
  /*lint -save -e506 */
  #define HTTPH(a,b,c,d,e,f,g)			\
  		if (!((e) & HTTPH_R_PASS) &&	\
  		    strlen(a) == u && !strncasecmp(a, p, u)) {	\
  			*p_err_code = 400;			\
  			return ("Bad request");			\
  		}
  #include "http_headers.h"
  #undef HTTPH
  /*lint -restore */
  
  		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, int fd, 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, fd, "%.*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, fd, hp, hp->nhd);
  			hp->nhd++;
  		} else {
  			VSC_C_main->losthdr++;
  			WSL(w, SLT_LostHeader, fd, "%.*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, int fd, 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 (400);
  
  	/* 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, fd, hp, h1);
  
  	*hp->hd[h2].e = '\0';
  	WSLH(w, fd, hp, h2);
  
  	if (hp->hd[h3].e != NULL) {
  		*hp->hd[h3].e = '\0';
  		WSLH(w, fd, hp, h3);
  	}
  
  	return (http_dissect_hdrs(w, hp, fd, p, htc));
  }
  
  /*--------------------------------------------------------------------*/
  
  static int
  htc_request_check_hdrs(struct sess *sp, struct http *hp)
  {
  	int u;
  	int seen_host = 0;
  	int seen_cl = 0;
  
  	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);
  		if (http_IsHdr(&hp->hd[u], H_Host)) {
  			if (seen_host) {
  				WSP(sp, SLT_Error, "Duplicated Host header");
  				return (400);
  			}
  			seen_host = 1;
  		}
  		if (http_IsHdr(&hp->hd[u], H_Content_Length)) {
  			if (seen_cl) {
  				WSP(sp, SLT_Error,
  				    "Duplicated Content-Length header");
  				return (400);
  			}
  			seen_cl = 1;
  		}
  	}
  	return (0);
  }
  
  /*--------------------------------------------------------------------*/
  
  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->fd, 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);
  
  	retval = htc_request_check_hdrs(sp, 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->fd, 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->fd, 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, int fd, 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, fd, 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 "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, int fd, 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 "http_headers.h"
  #undef HTTPH
  		http_copyheader(w, fd, 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->fd, hp, sp->http, how);
  	http_PrintfHeader(sp->wrk, sp->fd, 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, int fd, 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, fd, hp, u);
  			continue;
  		}
  		l = Tlen(hp->hd[u]);
  		p = WS_Alloc(hp->ws, l + 1);
  		if (p != NULL) {
  			WSLH(w, fd, 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, fd, 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, int fd, 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, fd, "%s", hdr);
  		return;
  	}
  	http_SetH(to, to->nhd++, hdr);
  }
  
  /*--------------------------------------------------------------------*/
  
  static void
  http_PutField(struct worker *w, int fd, 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, fd, "%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, int fd, const struct http *to,
      const char *protocol)
  {
  
  	http_PutField(w, fd, 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, int fd, const struct http *to,
      const char *response)
  {
  
  	http_PutField(w, fd, 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, int fd, 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, fd, "%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, const struct http *hp, int resp)
  {
  	unsigned u, l;
  	int fd = *(w->wrw.wfd);
  
  	if (resp) {
  		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], " ");
  		WSLH(w, fd, hp, HTTP_HDR_PROTO);
  
  		hp->hd[HTTP_HDR_STATUS].b = WS_Alloc(hp->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, fd, hp, HTTP_HDR_STATUS);
  
  		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_RESPONSE], "\r\n");
  		WSLH(w, fd, hp, HTTP_HDR_RESPONSE);
  	} else {
  		AN(hp->hd[HTTP_HDR_URL].b);
  		l = WRW_WriteH(w, &hp->hd[HTTP_HDR_REQ], " ");
  		WSLH(w, fd, hp, HTTP_HDR_REQ);
  		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_URL], " ");
  		WSLH(w, fd, hp, HTTP_HDR_URL);
  		l += WRW_WriteH(w, &hp->hd[HTTP_HDR_PROTO], "\r\n");
  		WSLH(w, fd, 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, fd, 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 "http_headers.h"
  #undef HTTPH
  }
  