#include "io.h"
#include "testutils.h"
CX_TEST(test_io_http_stream_parse_chunk_header_hdronly_first) {
char *str = strdup(
"100\r\n");
size_t len = strlen(str);
char *str2 = strdup(
"12345\r\n");
size_t len2 = strlen(str2);
char *str3 = strdup(
"FF\r\n");
size_t len3 = strlen(str3);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
5);
CX_TEST_ASSERT(chunklen ==
0x100);
ret = http_stream_parse_chunk_header(str2, len2,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
7);
CX_TEST_ASSERT(chunklen ==
0x12345);
ret = http_stream_parse_chunk_header(str3, len3,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
4);
CX_TEST_ASSERT(chunklen ==
0xFF);
}
free(str);
free(str2);
}
CX_TEST(test_io_http_stream_parse_chunk_header_hdronly) {
char *str = strdup(
"\r\n100\r\n");
size_t len = strlen(str);
char *str2 = strdup(
"\nab\n");
size_t len2 = strlen(str2);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
7);
CX_TEST_ASSERT(chunklen ==
0x100);
ret = http_stream_parse_chunk_header(str2, len2,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
4);
CX_TEST_ASSERT(chunklen ==
0xab);
}
free(str);
free(str2);
}
CX_TEST(test_io_http_stream_parse_chunk_header_hdronly_seq_fail) {
char *str = strdup(
"ff\r\n");
size_t len = strlen(str);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
}
free(str);
}
CX_TEST(test_io_http_stream_parse_chunk_header_hdr_data) {
char *str = strdup(
"\r\nb\r\nhello world\r\n");
size_t len = strlen(str);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
5);
}
free(str);
}
CX_TEST(test_io_http_stream_parse_chunk_header_empty) {
char *str =
"";
size_t len = strlen(str);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
ret = http_stream_parse_chunk_header(str, len,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
}
}
CX_TEST(test_io_http_stream_parse_chunk_header_partial_first) {
char *str = strdup(
"123");
size_t len = strlen(str);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
}
free(str);
}
CX_TEST(test_io_http_stream_parse_chunk_header_partial) {
char *str = strdup(
"123");
size_t len = strlen(str);
char *str2 = strdup(
"\r\n");
size_t len2 = strlen(str2);
char *str3 = strdup(
"\r");
size_t len3 = strlen(str3);
char *str4 = strdup(
"\r\n123");
size_t len4 = strlen(str4);
CX_TEST_DO {
int64_t chunklen;
int ret = http_stream_parse_chunk_header(str, len,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
ret = http_stream_parse_chunk_header(str2, len2,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
ret = http_stream_parse_chunk_header(str3, len3,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
ret = http_stream_parse_chunk_header(str4, len4,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
}
free(str);
free(str2);
free(str3);
free(str4);
}
CX_TEST(test_io_http_stream_parse_chunk_header_invalid) {
char *str = strdup(
"hello\r\n");
size_t len = strlen(str);
char *str2 = strdup(
"x4\r\n\r\n123\r\n");
size_t len2 = strlen(str2);
char *str3 = strdup(
"\r\n\r\n123\r\n");
size_t len3 = strlen(str3);
char *str4 = strdup(
"\r\n\r\nx123\r\n");
size_t len4 = strlen(str3);
char *str5 = strdup(
"\r\n\r\n1 2 3\r\n");
size_t len5 = strlen(str3);
char *str6 = strdup(
"\r\n\r\n1 23\r\n");
size_t len6 = strlen(str3);
CX_TEST_DO {
int64_t chunklen;
int ret;
ret = http_stream_parse_chunk_header(str, len,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str2, len2,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str2, len2,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str3, len3,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str3, len3,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str4, len4,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str4, len4,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str5, len5,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str5, len5,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str6, len6,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
ret = http_stream_parse_chunk_header(str6, len6,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
-1);
}
free(str);
free(str2);
free(str3);
free(str4);
free(str5);
free(str6);
}
CX_TEST(test_io_http_stream_parse_chunk_header_zero) {
char *str = strdup(
"\r\n0\r\n\r\n");
size_t len = strlen(str);
char *str2 = strdup(
"0\r\n\r\n");
size_t len2 = strlen(str2);
char *str3 = strdup(
"\r\n0\r\n");
size_t len3 = strlen(str3);
char *str4 = strdup(
"\r\n0");
size_t len4 = strlen(str4);
CX_TEST_DO {
int64_t chunklen =
-1;
int ret = http_stream_parse_chunk_header(str, len,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
7);
CX_TEST_ASSERT(chunklen ==
0);
chunklen =
-1;
ret = http_stream_parse_chunk_header(str2, len2,
TRUE, &chunklen);
CX_TEST_ASSERT(ret ==
5);
CX_TEST_ASSERT(chunklen ==
0);
ret = http_stream_parse_chunk_header(str3, len3,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
ret = http_stream_parse_chunk_header(str4, len4,
FALSE, &chunklen);
CX_TEST_ASSERT(ret ==
0);
}
free(str);
free(str2);
free(str3);
free(str4);
}
CX_TEST(test_io_httpstream_write) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
CX_TEST_DO {
char *msg =
"hello world!";
size_t msglen = strlen(msg);
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size == msglen);
CX_TEST_ASSERT(!memcmp(st->buf->space, msg, msglen));
char *msg2 =
"test";
size_t msglen2 = strlen(msg2);
w = net_write(http, msg2, msglen2);
CX_TEST_ASSERT(w == msglen2);
CX_TEST_ASSERT(st->buf->size == msglen+msglen2);
CX_TEST_ASSERT(!memcmp(st->buf->space + msglen, msg2, msglen2));
}
testutil_destroy_session(sn);
}
CX_TEST(test_io_httpstream_chunked_write) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
CX_TEST_DO {
char *msg =
"hello world!";
size_t msglen = strlen(msg);
char *bufmsg =
"c\r\nhello world!\r\n";
size_t bufmsglen = strlen(bufmsg);
ssize_t w = net_write(http, msg, msglen);
cxstring s1 = cx_strn(st->buf->space, st->buf->size);
cxstring s2 = cx_strn(bufmsg, bufmsglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size == bufmsglen);
CX_TEST_ASSERT(!cx_strcasecmp(s1, s2));
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size ==
2*bufmsglen);
cxstring s3 = cx_strn(st->buf->space+bufmsglen, bufmsglen);
CX_TEST_ASSERT(!cx_strcasecmp(s2, s3));
}
}
CX_TEST(test_io_httpstream_chunked_write_end) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
CX_TEST_DO {
char *msg =
"hello world!";
size_t msglen = strlen(msg);
char *bufmsg =
"c\r\nhello world!\r\n0\r\n\r\n";
size_t bufmsglen = strlen(bufmsg);
ssize_t w = net_write(http, msg, msglen);
net_finish(http);
cxstring s1 = cx_strn(st->buf->space, st->buf->size);
cxstring s2 = cx_strn(bufmsg, bufmsglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size == bufmsglen);
CX_TEST_ASSERT(!cx_strcasecmp(s1, s2));
}
}
CX_TEST(test_io_httpstream_chunked_write_xx) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
CX_TEST_DO {
CxBuffer *testdata = cxBufferCreate(
NULL,
1024*
1024*
4, cxDefaultAllocator,
0);
for(
size_t i=
0;i<testdata->capacity;i++) {
cxBufferPut(testdata,
35+(i%
91));
}
size_t pos =
0;
int j =
0;
ssize_t i=
15;
while(pos<testdata->size) {
char *buf = testdata->space + pos;
size_t remaining = testdata->size - pos;
ssize_t len = pos + i < remaining ? i : remaining;
pos += len;
ssize_t w = net_write(http, buf, len);
CX_TEST_ASSERT(w == len);
i+=
100;
j++;
}
net_finish(http);
WSBool first_chunk =
TRUE;
int64_t chunklen =
0;
char *buf = st->buf->space;
size_t bufsize = st->buf->size;
pos =
0;
size_t srcpos =
0;
int debug_counter =
0;
while(pos < bufsize) {
ssize_t remaining = bufsize - pos;
ssize_t src_remaining = testdata->size - srcpos;
int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen);
first_chunk =
FALSE;
CX_TEST_ASSERT(ret >
0);
if(chunklen ==
0) {
CX_TEST_ASSERT(src_remaining ==
0);
break;
}
CX_TEST_ASSERT(chunklen <= src_remaining);
char *src_chunk = testdata->space+srcpos;
char *buf_chunk = buf+pos+ret;
CX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen));
pos += ret + chunklen;
srcpos += chunklen;
debug_counter++;
}
cxBufferFree(testdata);
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_partial_header) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
CX_TEST_DO {
memset(st->buf->space,
0, st->buf->capacity);
char *msg =
"hello world!";
size_t msglen = strlen(msg);
st->max_write =
1;
io_set_max_writes(
1);
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
1);
CX_TEST_ASSERT(tolower(st->buf->space[
0]) ==
'c');
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
2);
CX_TEST_ASSERT(st->buf->space[
1] ==
'\r');
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
3);
CX_TEST_ASSERT(st->buf->space[
2] ==
'\n');
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
1);
CX_TEST_ASSERT(st->buf->size ==
4);
CX_TEST_ASSERT(st->buf->space[
3] == msg[
0]);
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_partial_data) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
CX_TEST_DO {
memset(st->buf->space,
0, st->buf->capacity);
char *msg =
"hello world!";
size_t msglen = strlen(msg);
size_t msglen_orig = msglen;
st->max_write =
3;
io_set_max_writes(
1);
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
3);
CX_TEST_ASSERT(st->buf->space[
0] ==
'c');
CX_TEST_ASSERT(st->buf->space[
2] ==
'\n');
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
3);
CX_TEST_ASSERT(st->buf->size ==
6);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhel\0",
7));
msg += w;
msglen -= w;
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
3);
CX_TEST_ASSERT(st->buf->size ==
9);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello \0",
10));
st->max_write =
1024;
msg += w;
msglen -= w;
w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen_orig +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n", st->buf->size));
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_partial_trailer) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
io_set_max_writes(
1);
CX_TEST_DO {
memset(st->buf->space,
0, st->buf->capacity);
char *msg =
"hello world!";
size_t msglen = strlen(msg);
char *msg2 =
"newmsg";
size_t msglen2 = strlen(msg2);
char *msg3 =
"msg3";
size_t msglen3 = strlen(msg3);
st->max_write =
3 + msglen;
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\0", st->buf->size +
1));
st->max_write =
2 +
3 + msglen2;
w = net_write(http, msg2, msglen2);
CX_TEST_ASSERT(w == msglen2);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
3 + msglen2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\nnewmsg\0", st->buf->size +
1));
st->max_write =
1;
w = net_write(http,
"dummymsg",
8);
CX_TEST_ASSERT(w ==
0);
w = net_write(http,
"dummymsg",
8);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
3 + msglen2 +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\nnewmsg\r\n\0", st->buf->size +
1));
st->max_write =
1024;
w = net_write(http, msg3, msglen3);
CX_TEST_ASSERT(w == msglen3);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
3 + msglen2 +
2 +
3 + msglen3 +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\nnewmsg\r\n4\r\nmsg3\r\n", st->buf->size +
1));
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_partial_trailer_partial_header) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
io_set_max_writes(
1);
CX_TEST_DO {
memset(st->buf->space,
0, st->buf->capacity);
char *msg =
"hello world!";
size_t msglen = strlen(msg);
char *msg2 =
"newmsg";
size_t msglen2 = strlen(msg2);
st->max_write =
3 + msglen +
1;
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w == msglen);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
1);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\0", st->buf->size +
1));
st->max_write =
2;
w = net_write(http, msg2, msglen2);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
1);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\0", st->buf->size +
1));
st->max_write =
1;
w = net_write(http, msg2, msglen2);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\0", st->buf->size +
1));
st->max_write =
1024;
w = net_write(http, msg2, msglen2);
CX_TEST_ASSERT(w ==msglen2);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
3 + msglen2 +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size +
1));
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_data_2x) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
io_set_max_writes(
1);
CX_TEST_DO {
memset(st->buf->space,
0, st->buf->capacity);
char *msg =
"hello world!";
size_t msglen = strlen(msg);
char *msg2 =
"newmsg";
size_t msglen2 = strlen(msg2);
char *msg_big =
"hello world!newmsg";
size_t msglen_big = strlen(msg_big);
st->max_write =
1;
ssize_t w = net_write(http, msg, msglen);
CX_TEST_ASSERT(w ==
0);
CX_TEST_ASSERT(st->buf->size ==
1);
st->max_write =
1024;
w = net_write(http, msg_big, msglen_big);
CX_TEST_ASSERT(w == msglen_big);
CX_TEST_ASSERT(st->buf->size ==
3 + msglen +
2 +
3 + msglen2 +
2);
CX_TEST_ASSERT(!memcmp(st->buf->space,
"c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size +
1));
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
}
}
CX_TEST(test_io_httpstream_chunked_write_xx_limit) {
Session *sn = testutil_session();
TestIOStream *st = testutil_iostream(
2048,
TRUE);
IOStream *http = httpstream_new(sn->pool, (IOStream*)st);
httpstream_enable_chunked_write(http);
io_set_max_writes(
1);
CX_TEST_DO {
CxBuffer *testdata = cxBufferCreate(
NULL,
1024*
16, cxDefaultAllocator,
0);
for(
size_t i=
0;i<testdata->capacity;i++) {
cxBufferPut(testdata,
35+(i%
91));
}
st->max_write =
1;
size_t pos =
0;
int chunksize =
1;
while(pos < testdata->size) {
size_t available = testdata->size - pos;
char *chunk = testdata->space + pos;
size_t chunklen = chunksize > available ? available : chunksize;
size_t chunkpos =
0;
int max_writes = chunklen +
24;
int writes =
0;
while(chunkpos < chunklen) {
ssize_t w = net_write(http, chunk+chunkpos, chunklen-chunkpos);
CX_TEST_ASSERT(w >=
0);
chunkpos += w;
writes++;
CX_TEST_ASSERT(writes < max_writes);
}
pos += chunklen;
chunksize +=
5;
if(pos + chunksize >= testdata->size) {
st->max_write =
INT_MAX;
}
else if(pos >
1024*
2) {
if(pos <
1024*
8) {
st->max_write =
2;
}
else {
st->max_write =
3;
}
}
}
net_finish(http);
WSBool first_chunk =
TRUE;
int64_t chunklen =
0;
char *buf = st->buf->space;
size_t bufsize = st->buf->size;
pos =
0;
size_t srcpos =
0;
int debug_counter =
0;
while(pos < bufsize) {
ssize_t remaining = bufsize - pos;
ssize_t src_remaining = testdata->size - srcpos;
int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen);
first_chunk =
FALSE;
CX_TEST_ASSERT(ret >
0);
if(chunklen ==
0) {
CX_TEST_ASSERT(src_remaining ==
0);
break;
}
CX_TEST_ASSERT(chunklen <= src_remaining);
char *src_chunk = testdata->space+srcpos;
char *buf_chunk = buf+pos+ret;
CX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen));
pos += ret + chunklen;
srcpos += chunklen;
debug_counter++;
}
testutil_destroy_session(sn);
testutil_iostream_destroy(st);
cxBufferFree(testdata);
}
}