Commit 036bd79f authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Move the req.body stuff to its own source-file, its not HTTP1 specific.

parent 2f057a32
......@@ -37,6 +37,7 @@ varnishd_SOURCES = \
cache/cache_panic.c \
cache/cache_pipe.c \
cache/cache_pool.c \
cache/cache_req_body.c \
cache/cache_req_fsm.c \
cache/cache_http1_deliver.c \
cache/cache_rfc2616.c \
......
......@@ -812,9 +812,6 @@ void V1F_Setup_Fetch(struct vfp_ctx *vfc, struct http_conn *htc);
/* cache_http1_fsm.c [HTTP1] */
typedef int (req_body_iter_f)(struct req *, void *priv, void *ptr, size_t);
void HTTP1_Session(struct worker *, struct req *);
int HTTP1_DiscardReqBody(struct req *req);
int HTTP1_CacheReqBody(struct req *req, ssize_t maxsize);
int HTTP1_IterateReqBody(struct req *req, req_body_iter_f *func, void *priv);
extern const int HTTP1_Req[3];
extern const int HTTP1_Resp[3];
......@@ -823,6 +820,11 @@ unsigned V1D_FlushReleaseAcct(struct req *req);
void V1D_Deliver(struct req *, struct busyobj *);
void V1D_Deliver_Synth(struct req *req);
/* cache_req_body.c */
int VRB_Ignore(struct req *req);
int VRB_Cache(struct req *req, ssize_t maxsize);
int VRB_Iterate(struct req *req, req_body_iter_f *func, void *priv);
void VRB_Free(struct req *req);
static inline int
VDP_bytes(struct req *req, enum vdp_action act, const void *ptr, ssize_t len)
......
......@@ -129,7 +129,7 @@ V1F_fetch_hdr(struct worker *wrk, struct busyobj *bo, struct req *req)
if (req != NULL) {
if (do_chunked)
WRW_Chunked(wrk);
i = HTTP1_IterateReqBody(req, vbf_iter_req_body, wrk);
i = VRB_Iterate(req, vbf_iter_req_body, wrk);
if (req->req_body_status == REQ_BODY_TAKEN) {
retry = -1;
......
......@@ -431,235 +431,3 @@ HTTP1_Session(struct worker *wrk, struct req *req)
}
}
}
/*----------------------------------------------------------------------
* Iterate over the req.body.
*
* This can be done exactly once if uncached, and multiple times if the
* req.body is cached.
*/
int
HTTP1_IterateReqBody(struct req *req, req_body_iter_f *func, void *priv)
{
char buf[8192];
struct storage *st;
ssize_t l;
int i;
struct vfp_ctx *vfc;
enum vfp_status vfps = VFP_ERROR;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
AN(func);
switch(req->req_body_status) {
case REQ_BODY_CACHED:
VTAILQ_FOREACH(st, &req->body->list, list) {
i = func(req, priv, st->ptr, st->len);
if (i)
return (i);
}
return (0);
case REQ_BODY_NONE:
return (0);
case REQ_BODY_WITH_LEN:
case REQ_BODY_WITHOUT_LEN:
break;
case REQ_BODY_TAKEN:
VSLb(req->vsl, SLT_VCL_Error,
"Uncached req.body can only be consumed once.");
return (-1);
case REQ_BODY_FAIL:
VSLb(req->vsl, SLT_FetchError,
"Had failed reading req.body before.");
return (-1);
default:
WRONG("Wrong req_body_status in HTTP1_IterateReqBody()");
}
Lck_Lock(&req->sp->mtx);
if (req->req_body_status == REQ_BODY_WITH_LEN ||
req->req_body_status == REQ_BODY_WITHOUT_LEN) {
req->req_body_status = REQ_BODY_TAKEN;
i = 0;
} else
i = -1;
Lck_Unlock(&req->sp->mtx);
if (i) {
VSLb(req->vsl, SLT_VCL_Error,
"Multiple attempts to access non-cached req.body");
return (i);
}
CHECK_OBJ_NOTNULL(req->htc, HTTP_CONN_MAGIC);
vfc = req->htc->vfc;
VFP_Setup(vfc);
vfc->http = req->http;
vfc->vsl = req->vsl;
V1F_Setup_Fetch(vfc, req->htc);
if (VFP_Open(vfc) < 0) {
VSLb(req->vsl, SLT_FetchError, "Could not open Fetch Pipeline");
return (-1);
}
do {
l = sizeof buf;
vfps = VFP_Suck(vfc, buf, &l);
if (vfps == VFP_ERROR) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
} else if (l > 0) {
req->req_bodybytes += l;
req->acct.req_bodybytes += l;
l = func(req, priv, buf, l);
if (l) {
req->req_body_status = REQ_BODY_FAIL;
break;
}
}
} while (vfps == VFP_OK);
VFP_Close(vfc);
VSLb_ts_req(req, "ReqBody", VTIM_real());
return (l);
}
/*----------------------------------------------------------------------
* DiscardReqBody() is a dedicated function, because we might
* be able to disuade or terminate its transmission in some protocols.
* For HTTP1 we have no such luck, and we just iterate it into oblivion.
*/
static int __match_proto__(req_body_iter_f)
httpq_req_body_discard(struct req *req, void *priv, void *ptr, size_t len)
{
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
(void)priv;
(void)ptr;
(void)len;
return (0);
}
int
HTTP1_DiscardReqBody(struct req *req)
{
if (req->req_body_status == REQ_BODY_WITH_LEN ||
req->req_body_status == REQ_BODY_WITHOUT_LEN)
(void)HTTP1_IterateReqBody(req, httpq_req_body_discard, NULL);
return(0);
}
/*----------------------------------------------------------------------
* Cache the req.body if it is smaller than the given size
*
* This function must be called before any backend fetches are kicked
* off to prevent parallelism.
*/
int
HTTP1_CacheReqBody(struct req *req, ssize_t maxsize)
{
struct storage *st;
ssize_t l, yet;
struct vfp_ctx *vfc;
enum vfp_status vfps = VFP_ERROR;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
assert (req->req_step == R_STP_RECV);
switch(req->req_body_status) {
case REQ_BODY_CACHED:
return (0);
case REQ_BODY_FAIL:
return (-1);
case REQ_BODY_NONE:
return (0);
case REQ_BODY_WITHOUT_LEN:
case REQ_BODY_WITH_LEN:
break;
default:
WRONG("Wrong req_body_status in HTTP1_CacheReqBody()");
}
CHECK_OBJ_NOTNULL(req->htc, HTTP_CONN_MAGIC);
vfc = req->htc->vfc;
if (req->htc->content_length > maxsize) {
req->req_body_status = REQ_BODY_FAIL;
(void)VFP_Error(vfc, "Request body too big to cache");
return (-1);
}
VFP_Setup(vfc);
vfc->http = req->http;
vfc->vsl = req->vsl;
V1F_Setup_Fetch(vfc, req->htc);
if (VFP_Open(vfc) < 0) {
req->req_body_status = REQ_BODY_FAIL;
return (-1);
}
yet = req->htc->content_length;
if (yet < 0)
yet = 0;
st = NULL;
do {
if (st == NULL) {
st = STV_alloc_transient(
yet ? yet : cache_param->fetch_chunksize);
if (st == NULL) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
} else {
VTAILQ_INSERT_TAIL(&req->body->list, st, list);
}
}
l = st->space - st->len;
vfps = VFP_Suck(vfc, st->ptr + st->len, &l);
if (vfps == VFP_ERROR) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
}
if (l > 0) {
req->req_bodybytes += l;
req->acct.req_bodybytes += l;
if (yet > 0)
yet -= l;
st->len += l;
if (st->space == st->len)
st = NULL;
l = 0;
}
if (req->req_bodybytes > maxsize) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
}
} while (vfps == VFP_OK);
VFP_Close(vfc);
if (l == 0) {
if (req->req_bodybytes != req->htc->content_length) {
/* We must update also the "pristine" req.* copy */
http_Unset(req->http0, H_Content_Length);
http_Unset(req->http0, H_Transfer_Encoding);
http_PrintfHeader(req->http0, "Content-Length: %ju",
(uintmax_t)req->req_bodybytes);
http_Unset(req->http, H_Content_Length);
http_Unset(req->http, H_Transfer_Encoding);
http_PrintfHeader(req->http, "Content-Length: %ju",
(uintmax_t)req->req_bodybytes);
}
req->req_body_status = REQ_BODY_CACHED;
}
VSLb_ts_req(req, "ReqBody", VTIM_real());
return (l);
}
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2014 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "cache.h"
#include "vtim.h"
/*----------------------------------------------------------------------
* Iterate over the req.body.
*
* This can be done exactly once if uncached, and multiple times if the
* req.body is cached.
*/
int
VRB_Iterate(struct req *req, req_body_iter_f *func, void *priv)
{
char buf[8192];
struct storage *st;
ssize_t l;
int i;
struct vfp_ctx *vfc;
enum vfp_status vfps = VFP_ERROR;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
AN(func);
switch(req->req_body_status) {
case REQ_BODY_CACHED:
VTAILQ_FOREACH(st, &req->body->list, list) {
i = func(req, priv, st->ptr, st->len);
if (i)
return (i);
}
return (0);
case REQ_BODY_NONE:
return (0);
case REQ_BODY_WITH_LEN:
case REQ_BODY_WITHOUT_LEN:
break;
case REQ_BODY_TAKEN:
VSLb(req->vsl, SLT_VCL_Error,
"Uncached req.body can only be consumed once.");
return (-1);
case REQ_BODY_FAIL:
VSLb(req->vsl, SLT_FetchError,
"Had failed reading req.body before.");
return (-1);
default:
WRONG("Wrong req_body_status in VRB_IterateReqBody()");
}
Lck_Lock(&req->sp->mtx);
if (req->req_body_status == REQ_BODY_WITH_LEN ||
req->req_body_status == REQ_BODY_WITHOUT_LEN) {
req->req_body_status = REQ_BODY_TAKEN;
i = 0;
} else
i = -1;
Lck_Unlock(&req->sp->mtx);
if (i) {
VSLb(req->vsl, SLT_VCL_Error,
"Multiple attempts to access non-cached req.body");
return (i);
}
CHECK_OBJ_NOTNULL(req->htc, HTTP_CONN_MAGIC);
vfc = req->htc->vfc;
VFP_Setup(vfc);
vfc->http = req->http;
vfc->vsl = req->vsl;
V1F_Setup_Fetch(vfc, req->htc);
if (VFP_Open(vfc) < 0) {
VSLb(req->vsl, SLT_FetchError, "Could not open Fetch Pipeline");
return (-1);
}
do {
l = sizeof buf;
vfps = VFP_Suck(vfc, buf, &l);
if (vfps == VFP_ERROR) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
} else if (l > 0) {
req->req_bodybytes += l;
req->acct.req_bodybytes += l;
l = func(req, priv, buf, l);
if (l) {
req->req_body_status = REQ_BODY_FAIL;
break;
}
}
} while (vfps == VFP_OK);
VFP_Close(vfc);
VSLb_ts_req(req, "ReqBody", VTIM_real());
return (l);
}
/*----------------------------------------------------------------------
* DiscardReqBody() is a dedicated function, because we might
* be able to disuade or terminate its transmission in some protocols.
* For HTTP1 we have no such luck, and we just iterate it into oblivion.
*/
static int __match_proto__(req_body_iter_f)
httpq_req_body_discard(struct req *req, void *priv, void *ptr, size_t len)
{
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
(void)priv;
(void)ptr;
(void)len;
return (0);
}
int
VRB_Ignore(struct req *req)
{
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
if (req->req_body_status == REQ_BODY_WITH_LEN ||
req->req_body_status == REQ_BODY_WITHOUT_LEN)
(void)VRB_Iterate(req, httpq_req_body_discard, NULL);
return(0);
}
/*----------------------------------------------------------------------
*/
void
VRB_Free(struct req *req)
{
struct storage *st;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
while (!VTAILQ_EMPTY(&req->body->list)) {
st = VTAILQ_FIRST(&req->body->list);
VTAILQ_REMOVE(&req->body->list, st, list);
STV_free(st);
}
}
/*----------------------------------------------------------------------
* Cache the req.body if it is smaller than the given size
*
* This function must be called before any backend fetches are kicked
* off to prevent parallelism.
*/
int
VRB_Cache(struct req *req, ssize_t maxsize)
{
struct storage *st;
ssize_t l, yet;
struct vfp_ctx *vfc;
enum vfp_status vfps = VFP_ERROR;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
assert (req->req_step == R_STP_RECV);
switch(req->req_body_status) {
case REQ_BODY_CACHED:
return (0);
case REQ_BODY_FAIL:
return (-1);
case REQ_BODY_NONE:
return (0);
case REQ_BODY_WITHOUT_LEN:
case REQ_BODY_WITH_LEN:
break;
default:
WRONG("Wrong req_body_status in VRB_CacheReqBody()");
}
CHECK_OBJ_NOTNULL(req->htc, HTTP_CONN_MAGIC);
vfc = req->htc->vfc;
if (req->htc->content_length > maxsize) {
req->req_body_status = REQ_BODY_FAIL;
(void)VFP_Error(vfc, "Request body too big to cache");
return (-1);
}
VFP_Setup(vfc);
vfc->http = req->http;
vfc->vsl = req->vsl;
V1F_Setup_Fetch(vfc, req->htc);
if (VFP_Open(vfc) < 0) {
req->req_body_status = REQ_BODY_FAIL;
return (-1);
}
yet = req->htc->content_length;
if (yet < 0)
yet = 0;
st = NULL;
do {
if (st == NULL) {
st = STV_alloc_transient(
yet ? yet : cache_param->fetch_chunksize);
if (st == NULL) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
} else {
VTAILQ_INSERT_TAIL(&req->body->list, st, list);
}
}
l = st->space - st->len;
vfps = VFP_Suck(vfc, st->ptr + st->len, &l);
if (vfps == VFP_ERROR) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
}
if (l > 0) {
req->req_bodybytes += l;
req->acct.req_bodybytes += l;
if (yet > 0)
yet -= l;
st->len += l;
if (st->space == st->len)
st = NULL;
l = 0;
}
if (req->req_bodybytes > maxsize) {
req->req_body_status = REQ_BODY_FAIL;
l = -1;
break;
}
} while (vfps == VFP_OK);
VFP_Close(vfc);
if (l == 0) {
if (req->req_bodybytes != req->htc->content_length) {
/* We must update also the "pristine" req.* copy */
http_Unset(req->http0, H_Content_Length);
http_Unset(req->http0, H_Transfer_Encoding);
http_PrintfHeader(req->http0, "Content-Length: %ju",
(uintmax_t)req->req_bodybytes);
http_Unset(req->http, H_Content_Length);
http_Unset(req->http, H_Transfer_Encoding);
http_PrintfHeader(req->http, "Content-Length: %ju",
(uintmax_t)req->req_bodybytes);
}
req->req_body_status = REQ_BODY_CACHED;
}
VSLb_ts_req(req, "ReqBody", VTIM_real());
return (l);
}
......@@ -259,7 +259,7 @@ cnt_synth(struct worker *wrk, struct req *req)
req->doclose = SC_RESP_CLOSE;
/* Discard any lingering request body before delivery */
(void)HTTP1_DiscardReqBody(req);
(void)VRB_Ignore(req);
V1D_Deliver_Synth(req);
......@@ -296,7 +296,7 @@ cnt_fetch(struct worker *wrk, struct req *req)
CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
wrk->stats.s_fetch++;
(void)HTTP1_DiscardReqBody(req);
(void)VRB_Ignore(req);
if (req->objcore->flags & OC_F_FAILED) {
req->err_code = 503;
......@@ -420,7 +420,7 @@ cnt_lookup(struct worker *wrk, struct req *req)
AZ(boc->busyobj);
VBF_Fetch(wrk, req, boc, oc, VBF_BACKGROUND);
} else {
(void)HTTP1_DiscardReqBody(req);// XXX: handle err
(void)VRB_Ignore(req);// XXX: handle err
}
wrk->stats.cache_hit++;
req->req_step = R_STP_DELIVER;
......@@ -866,7 +866,6 @@ enum req_fsm_nxt
CNT_Request(struct worker *wrk, struct req *req)
{
enum req_fsm_nxt nxt;
struct storage *st;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
......@@ -920,11 +919,7 @@ CNT_Request(struct worker *wrk, struct req *req)
VSLb(req->vsl, SLT_ESI_BodyBytes, "%ju",
(uintmax_t)req->resp_bodybytes);
while (!VTAILQ_EMPTY(&req->body->list)) {
st = VTAILQ_FIRST(&req->body->list);
VTAILQ_REMOVE(&req->body->list, st, list);
STV_free(st);
}
VRB_Free(req);
req->wrk = NULL;
}
assert(WRW_IsReleased(wrk));
......
......@@ -488,7 +488,7 @@ VRT_CacheReqBody(const struct vrt_ctx *ctx, long long maxsize)
"req.body can only be cached in vcl_recv{}");
return (0);
}
return (HTTP1_CacheReqBody(ctx->req, maxsize));
return (VRB_Cache(ctx->req, maxsize));
}
/*--------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment