Implement li_filter_chunked_decode + one unit-test for it
parent
680d3fcec5
commit
c6741f7716
|
@ -3,7 +3,13 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
LI_API liHandlerResult li_filter_chunked_encode(liConnection *con, liChunkQueue *out, liChunkQueue *in);
|
||||
LI_API liHandlerResult li_filter_chunked_decode(liConnection *con, liChunkQueue *out, liChunkQueue *in);
|
||||
/* initialize with zero */
|
||||
typedef struct {
|
||||
int parse_state;
|
||||
goffset cur_chunklen;
|
||||
} liFilterDecodeState;
|
||||
|
||||
LI_API liHandlerResult li_filter_chunked_encode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in);
|
||||
LI_API liHandlerResult li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, liFilterDecodeState *state);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -388,5 +388,6 @@ IF(BUILD_UNIT_TESTS)
|
|||
ENDMACRO(ADD_TEST_BINARY)
|
||||
|
||||
ADD_TEST_BINARY(Utils-UnitTest test-utils unittests/test-utils.c)
|
||||
ADD_TEST_BINARY(Chunk-UnitTest test-chunk unittests/test-chunk.c)
|
||||
|
||||
ENDIF(BUILD_UNIT_TESTS)
|
|
@ -43,7 +43,7 @@ static void forward_response_body(liConnection *con) {
|
|||
}
|
||||
|
||||
if (vr->response.transfer_encoding & LI_HTTP_TRANSFER_ENCODING_CHUNKED) {
|
||||
li_filter_chunked_encode(con, con->raw_out, con->out);
|
||||
li_filter_chunked_encode(vr, con->raw_out, con->out);
|
||||
} else {
|
||||
li_chunkqueue_steal_all(con->raw_out, con->out);
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ static void http_chunk_append_len(liChunkQueue *cq, size_t len) {
|
|||
}
|
||||
|
||||
|
||||
liHandlerResult li_filter_chunked_encode(liConnection *con, liChunkQueue *out, liChunkQueue *in) {
|
||||
UNUSED(con);
|
||||
liHandlerResult li_filter_chunked_encode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in) {
|
||||
UNUSED(vr);
|
||||
|
||||
if (in->length > 0) {
|
||||
http_chunk_append_len(out, in->length);
|
||||
|
@ -43,9 +43,150 @@ liHandlerResult li_filter_chunked_encode(liConnection *con, liChunkQueue *out, l
|
|||
return LI_HANDLER_GO_ON;
|
||||
}
|
||||
|
||||
liHandlerResult li_filter_chunked_decode(liConnection *con, liChunkQueue *out, liChunkQueue *in) {
|
||||
UNUSED(con);
|
||||
UNUSED(out);
|
||||
UNUSED(in);
|
||||
return LI_HANDLER_ERROR;
|
||||
#define read_char(c) do { \
|
||||
while (!p || p >= pe) { \
|
||||
res = li_chunk_parser_next(vr, &ctx, &p, &pe); \
|
||||
if (res == LI_HANDLER_WAIT_FOR_EVENT && in->is_closed) { \
|
||||
res = LI_HANDLER_ERROR; \
|
||||
} \
|
||||
if (res != LI_HANDLER_GO_ON) goto leave; \
|
||||
} \
|
||||
c = *p++; \
|
||||
} while(0);
|
||||
|
||||
|
||||
liHandlerResult li_filter_chunked_decode(liVRequest *vr, liChunkQueue *out, liChunkQueue *in, liFilterDecodeState *state) {
|
||||
liHandlerResult res = LI_HANDLER_GO_ON;
|
||||
liChunkParserCtx ctx;
|
||||
gchar *p = NULL, *pe = NULL;
|
||||
gchar c;
|
||||
int digit;
|
||||
|
||||
li_chunk_parser_init(&ctx, in);
|
||||
li_chunk_parser_prepare(&ctx);
|
||||
|
||||
|
||||
for (;;) {
|
||||
/* 0: start new chunklen, 1: reading chunklen, 2: found \r, 3: copying content, 4: found \r,
|
||||
* 10: wait for \r\n\r\n, 11: wait for \n\r\n, 12: wait for \r\n, 13: wait for \n, 14: eof,
|
||||
* 20: error
|
||||
*/
|
||||
switch (state->parse_state) {
|
||||
case 0:
|
||||
state->cur_chunklen = -1;
|
||||
li_chunk_parser_prepare(&ctx);
|
||||
state->parse_state = 1;
|
||||
break;
|
||||
case 1:
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
digit = -1;
|
||||
if (c >= '0' && c <= '9') {
|
||||
digit = c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
digit = c - 'a' + 10;
|
||||
} else if (c >= 'A' && c >= 'F') {
|
||||
digit = c - 'A' + 10;
|
||||
} else if (c == '\r') {
|
||||
if (state->cur_chunklen == -1) {
|
||||
state->parse_state = 20;
|
||||
} else {
|
||||
state->parse_state = 2;
|
||||
}
|
||||
} else {
|
||||
state->parse_state = 20;
|
||||
}
|
||||
if (digit >= 0) {
|
||||
if (state->cur_chunklen < 0) {
|
||||
state->cur_chunklen = digit;
|
||||
} else {
|
||||
if ((G_MAXINT64 - digit) / 16 < state->cur_chunklen) {
|
||||
state->parse_state = 20; /* overflow */
|
||||
} else {
|
||||
state->cur_chunklen = 16 * state->cur_chunklen + digit;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
if (c == '\n') {
|
||||
li_chunkqueue_skip(in, ctx.bytes_in);
|
||||
li_chunk_parser_reset(&ctx); p = NULL;
|
||||
if (state->cur_chunklen > 0) {
|
||||
state->parse_state = 3;
|
||||
} else {
|
||||
li_chunk_parser_prepare(&ctx);
|
||||
state->parse_state = 12;
|
||||
}
|
||||
} else {
|
||||
state->parse_state = 20;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (state->cur_chunklen != 0) {
|
||||
state->cur_chunklen -= li_chunkqueue_steal_len(out, in, state->cur_chunklen);
|
||||
}
|
||||
if (state->cur_chunklen == 0) {
|
||||
li_chunk_parser_prepare(&ctx);
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
if (c == '\r') {
|
||||
state->parse_state = 4;
|
||||
} else {
|
||||
state->parse_state = 20;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
if (c == '\n') {
|
||||
li_chunkqueue_skip(in, ctx.bytes_in);
|
||||
li_chunk_parser_reset(&ctx); p = NULL;
|
||||
state->parse_state = 0;
|
||||
} else {
|
||||
state->parse_state = 20;
|
||||
}
|
||||
break;
|
||||
case 10: /* \r\n\r\n */
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
state->parse_state = (c == '\r') ? 11 : 10;
|
||||
break;
|
||||
case 11: /* \n\r\n */
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
state->parse_state = (c == '\n') ? 12 : 10;
|
||||
break;
|
||||
case 12: /* \r\n */
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
state->parse_state = (c == '\r') ? 13 : 10;
|
||||
break;
|
||||
case 13: /* \n */
|
||||
read_char(c);
|
||||
li_chunk_parser_done(&ctx, 1);
|
||||
state->parse_state = (c == '\n') ? 14 : 10;
|
||||
break;
|
||||
case 14:
|
||||
out->is_closed = TRUE;
|
||||
res = LI_HANDLER_GO_ON;
|
||||
goto leave;
|
||||
case 20:
|
||||
res = LI_HANDLER_ERROR;
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
if (res == LI_HANDLER_ERROR) {
|
||||
out->is_closed = TRUE;
|
||||
in->is_closed = TRUE;
|
||||
li_chunkqueue_skip_all(in);
|
||||
state->parse_state = 20;
|
||||
}
|
||||
li_chunkqueue_skip(in, ctx.bytes_in);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
#define perror(msg) g_error("(%s:%i) %s failed: %s", __FILE__, __LINE__, msg, g_strerror(errno))
|
||||
|
||||
static liChunkQueue* cq_from_str(const gchar *s, size_t len) {
|
||||
liChunkQueue *cq = li_chunkqueue_new();
|
||||
li_chunkqueue_append_mem(cq, s, len);
|
||||
return cq;
|
||||
}
|
||||
|
||||
static void cq_load_str(liChunkQueue *cq, const gchar *s, size_t len) {
|
||||
li_chunkqueue_reset(cq);
|
||||
li_chunkqueue_append_mem(cq, s, len);
|
||||
}
|
||||
|
||||
static void cq_assert_eq(liChunkQueue *cq, const gchar *s, size_t len) {
|
||||
GString *buf = g_string_sized_new(cq->length);
|
||||
g_assert(li_chunkqueue_extract_to(NULL, cq, cq->length, buf));
|
||||
g_assert(0 == memcmp(s, buf->str, len));
|
||||
g_string_free(buf, TRUE);
|
||||
}
|
||||
|
||||
|
||||
static void test_filter_chunked_decode(void) {
|
||||
liChunkQueue *cq = li_chunkqueue_new(), *cq2 = li_chunkqueue_new();
|
||||
liFilterDecodeState decode_state;
|
||||
|
||||
cq_load_str(cq, CONST_STR_LEN(
|
||||
"14\r\n"
|
||||
"01234567890123456789" "\r\n"
|
||||
"0\r\nrandom foo: xx\r\n\r\n"
|
||||
"xxx" /* next message */
|
||||
));
|
||||
cq->is_closed = TRUE;
|
||||
memset(&decode_state, 0, sizeof(decode_state));
|
||||
li_chunkqueue_reset(cq2);
|
||||
g_assert(LI_HANDLER_GO_ON == li_filter_chunked_decode(NULL, cq2, cq, &decode_state));
|
||||
cq_assert_eq(cq2, CONST_STR_LEN(
|
||||
"01234567890123456789"
|
||||
));
|
||||
g_assert(cq2->is_closed);
|
||||
cq_assert_eq(cq, CONST_STR_LEN(
|
||||
"xxx"
|
||||
));
|
||||
|
||||
li_chunkqueue_free(cq);
|
||||
li_chunkqueue_free(cq2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func("/chunk/filter_chunked_decode", test_filter_chunked_decode);
|
||||
|
||||
return g_test_run();
|
||||
}
|
Loading…
Reference in New Issue