Commit affd96d4 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Generalize v1f_pull_chunked() into HTTP1_Chunked() which can also be

used on requests.
parent b61c2305
......@@ -859,6 +859,14 @@ 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];
enum http1_chunked_ret {
H1CR_ERROR,
H1CR_MORE,
H1CR_END,
};
enum http1_chunked_ret
HTTP1_Chunked(struct http_conn *htc, intptr_t *priv, const char **error,
int64_t *statp, void *ptr, ssize_t *lp);
/* cache_http1_deliver.c */
unsigned V1D_FlushReleaseAcct(struct req *req);
......
......@@ -40,7 +40,6 @@
#include "cache_backend.h"
#include "vcli_priv.h"
#include "vct.h"
#include "vtcp.h"
#include "vtim.h"
......@@ -110,10 +109,7 @@ v1f_pull_straight(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv)
static enum vfp_status __match_proto__(vfp_pull_f)
v1f_pull_chunked(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv)
{
int i;
char buf[20]; /* XXX: 20 is arbitrary */
unsigned u;
ssize_t cl, l, lr;
const char *err;
CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
if (p == vfp_init)
......@@ -123,77 +119,18 @@ v1f_pull_chunked(struct busyobj *bo, void *p, ssize_t *lp, intptr_t *priv)
AN(p);
AN(lp);
AN(priv);
l = *lp;
*lp = 0;
if (*priv == -1) {
/* Skip leading whitespace */
do {
lr = HTTP1_Read(&bo->htc, buf, 1);
if (lr <= 0)
return (VFP_Error(bo, "chunked read err"));
bo->acct.beresp_bodybytes += lr;
} while (vct_islws(buf[0]));
if (!vct_ishex(buf[0]))
return (VFP_Error(bo, "chunked header non-hex"));
/* Collect hex digits, skipping leading zeros */
for (u = 1; u < sizeof buf; u++) {
do {
lr = HTTP1_Read(&bo->htc, buf + u, 1);
if (lr <= 0)
return (VFP_Error(bo,
"chunked read err"));
bo->acct.beresp_bodybytes += lr;
} while (u == 1 && buf[0] == '0' && buf[u] == '0');
if (!vct_ishex(buf[u]))
break;
}
if (u >= sizeof buf)
return (VFP_Error(bo,"chunked header too long"));
/* Skip trailing white space */
while(vct_islws(buf[u]) && buf[u] != '\n') {
lr = HTTP1_Read(&bo->htc, buf + u, 1);
if (lr <= 0)
return (VFP_Error(bo, "chunked read err"));
bo->acct.beresp_bodybytes += lr;
}
if (buf[u] != '\n')
return (VFP_Error(bo,"chunked header no NL"));
buf[u] = '\0';
cl = vbf_fetch_number(buf, 16);
if (cl < 0)
return (VFP_Error(bo,"chunked header number syntax"));
*priv = cl;
}
if (*priv > 0) {
if (*priv < l)
l = *priv;
lr = HTTP1_Read(&bo->htc, p, l);
if (lr <= 0)
return (VFP_Error(bo, "straight insufficient bytes"));
bo->acct.beresp_bodybytes += lr;
*lp = lr;
*priv -= lr;
if (*priv == 0)
*priv = -1;
switch (HTTP1_Chunked(&bo->htc, priv, &err,
&bo->acct.beresp_bodybytes, p, lp)) {
case H1CR_ERROR:
return (VFP_Error(bo, "%s", err));
case H1CR_MORE:
return (VFP_OK);
case H1CR_END:
return (VFP_END);
default:
WRONG("invalid HTTP1_Chunked return");
}
AZ(*priv);
i = HTTP1_Read(&bo->htc, buf, 1);
if (i <= 0)
return (VFP_Error(bo, "chunked read err"));
bo->acct.beresp_bodybytes += i;
if (buf[0] == '\r' && HTTP1_Read(&bo->htc, buf, 1) <= 0)
return (VFP_Error(bo, "chunked read err"));
if (buf[0] != '\n')
return (VFP_Error(bo,"chunked tail no NL"));
return (VFP_END);
}
/*--------------------------------------------------------------------*/
......
......@@ -45,6 +45,8 @@
#include "config.h"
#include <inttypes.h>
#include "cache.h"
#include "vct.h"
......@@ -524,3 +526,106 @@ HTTP1_Write(const struct worker *w, const struct http *hp, const int *hf)
l += WRW_Write(w, "\r\n", -1);
return (l);
}
/*--------------------------------------------------------------------
* Read a chunked body.
*
* XXX: Reading one byte at a time is pretty pessimal.
*/
enum http1_chunked_ret
HTTP1_Chunked(struct http_conn *htc, intptr_t *priv, const char **error,
int64_t *statp, void *ptr, ssize_t *lp)
{
int i;
char buf[20]; /* XXX: 20 is arbitrary */
char *q;
unsigned u;
uintmax_t cll;
ssize_t cl, l, lr;
#define ERR(x) do { *error = (x); return (H1CR_ERROR); } while (0)
CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
AN(priv);
AN(error);
AN(statp);
AN(ptr);
AN(lp);
l = *lp;
*lp = 0;
if (*priv == -1) {
/* Skip leading whitespace */
do {
lr = HTTP1_Read(htc, buf, 1);
if (lr <= 0)
ERR("chunked read err");
*statp += lr;
} while (vct_islws(buf[0]));
if (!vct_ishex(buf[0]))
ERR("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)
ERR("chunked read err");
*statp += lr;
} while (u == 1 && buf[0] == '0' && buf[u] == '0');
if (!vct_ishex(buf[u]))
break;
}
if (u >= sizeof buf)
ERR("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)
ERR("chunked read err");
*statp += lr;
}
if (buf[u] != '\n')
ERR("chunked header no NL");
buf[u] = '\0';
cll = strtoumax(buf, &q, 16);
if (q == NULL || *q != '\0')
ERR("chunked header number syntax");
cl = (ssize_t)cll;
if((uintmax_t)cl != cll)
ERR("bogusly large chunk size");
*priv = cl;
}
if (*priv > 0) {
if (*priv < l)
l = *priv;
lr = HTTP1_Read(htc, ptr, l);
if (lr <= 0)
ERR("straight insufficient bytes");
*statp += lr;
*lp = lr;
*priv -= lr;
if (*priv == 0)
*priv = -1;
return (H1CR_MORE);
}
AZ(*priv);
i = HTTP1_Read(htc, buf, 1);
if (i <= 0)
ERR("chunked read err");
*statp += i;
if (buf[0] == '\r' && HTTP1_Read(htc, buf, 1) <= 0)
ERR("chunked read err");
if (buf[0] != '\n')
ERR("chunked tail no NL");
return (H1CR_END);
#undef ERR
}
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