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