Browse Source

Implement li_filter_chunked_decode + one unit-test for it

personal/stbuehler/wip
Stefan Bühler 13 years ago
parent
commit
c6741f7716
  1. 10
      include/lighttpd/filter_chunked.h
  2. 1
      src/CMakeLists.txt
  3. 2
      src/main/connection.c
  4. 155
      src/main/filter_chunked.c
  5. 57
      src/unittests/test-chunk.c

10
include/lighttpd/filter_chunked.h

@ -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

1
src/CMakeLists.txt

@ -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)

2
src/main/connection.c

@ -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);
}

155
src/main/filter_chunked.c

@ -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;
}

57
src/unittests/test-chunk.c

@ -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…
Cancel
Save