From a55511ea75003f5ca79a25f6e64eadf91b6dfe9f Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Fri, 30 Nov 2012 15:10:36 +0100 Subject: waitress: Fix chunked decoder Closes #322. --- src/libwaitress/waitress.c | 95 ++++++++++++++++++++++++---------------------- src/libwaitress/waitress.h | 1 + 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/libwaitress/waitress.c b/src/libwaitress/waitress.c index ce2a254..8a8315f 100644 --- a/src/libwaitress/waitress.c +++ b/src/libwaitress/waitress.c @@ -628,64 +628,69 @@ static WaitressHandlerReturn_t WaitressHandleIdentity (void *data, char *buf, } } -/* chunked encoding handler. buf must be \0-terminated, size does not include - * trailing \0. +/* chunked encoding handler */ static WaitressHandlerReturn_t WaitressHandleChunked (void *data, char *buf, const size_t size) { assert (data != NULL); assert (buf != NULL); - WaitressHandle_t *waith = data; - char *content = buf, *nextContent; - - assert (waith != NULL); - assert (buf != NULL); - - while (1) { - if (waith->request.chunkSize > 0) { - const size_t remaining = size-(content-buf); - - if (remaining >= waith->request.chunkSize) { - if (WaitressHandleIdentity (waith, content, - waith->request.chunkSize) == WAITRESS_HANDLER_ABORTED) { - return WAITRESS_HANDLER_ABORTED; - } - - content += waith->request.chunkSize; - if (content[0] == '\r' && content[1] == '\n') { - content += 2; + WaitressHandle_t * const waith = data; + size_t pos = 0; + + while (pos < size) { + switch (waith->request.chunkedState) { + case CHUNKSIZE: + /* Poor man’s hex to integer. This avoids another buffer that + * fills until the terminating \r\n is received. */ + if (buf[pos] >= '0' && buf[pos] <= '9') { + waith->request.chunkSize <<= 4; + waith->request.chunkSize |= buf[pos] & 0xf; + } else if (buf[pos] >= 'a' && buf[pos] <= 'f') { + waith->request.chunkSize <<= 4; + waith->request.chunkSize |= (buf[pos]+9) & 0xf; + } else if (buf[pos] == '\r') { + /* ignore */ + } else if (buf[pos] == '\n') { + waith->request.chunkedState = DATA; + /* last chunk has size 0 */ + if (waith->request.chunkSize == 0) { + return WAITRESS_HANDLER_DONE; + } } else { + /* everything else is a protocol violation */ return WAITRESS_HANDLER_ERR; } - waith->request.chunkSize = 0; - } else { - if (WaitressHandleIdentity (waith, content, remaining) == - WAITRESS_HANDLER_ABORTED) { - return WAITRESS_HANDLER_ABORTED; - } - waith->request.chunkSize -= remaining; - return WAITRESS_HANDLER_CONTINUE; - } - } + ++pos; + break; - if ((nextContent = WaitressGetline (content)) != NULL) { - const long int chunkSize = strtol (content, NULL, 16); - if (chunkSize == 0) { - return WAITRESS_HANDLER_DONE; - } else if (chunkSize < 0) { - return WAITRESS_HANDLER_ERR; - } else { - waith->request.chunkSize = chunkSize; - content = nextContent; - } - } else { - return WAITRESS_HANDLER_ERR; + case DATA: + if (waith->request.chunkSize > 0) { + assert (size >= pos); + size_t payloadSize = size - pos; + + if (payloadSize > waith->request.chunkSize) { + payloadSize = waith->request.chunkSize; + } + if (WaitressHandleIdentity (waith, &buf[pos], + payloadSize) == WAITRESS_HANDLER_ABORTED) { + return WAITRESS_HANDLER_ABORTED; + } + pos += payloadSize; + assert (waith->request.chunkSize >= payloadSize); + waith->request.chunkSize -= payloadSize; + } else { + /* next chunk size starts in the next line */ + if (buf[pos] == '\n') { + waith->request.chunkedState = CHUNKSIZE; + } + ++pos; + } + break; } } - assert (0); - return WAITRESS_HANDLER_ERR; + return WAITRESS_HANDLER_CONTINUE; } /* handle http header diff --git a/src/libwaitress/waitress.h b/src/libwaitress/waitress.h index 0d69f0e..da516bb 100644 --- a/src/libwaitress/waitress.h +++ b/src/libwaitress/waitress.h @@ -107,6 +107,7 @@ typedef struct { WaitressReturn_t readWriteRet; size_t contentLength, contentReceived, chunkSize; + enum {CHUNKSIZE = 0, DATA = 1} chunkedState; char *buf; /* first argument is WaitressHandle_t, but that's not defined yet */ -- cgit v1.2.3