| 29 #include "io.h" |
29 #include "io.h" |
| 30 |
30 |
| 31 #include "testutils.h" |
31 #include "testutils.h" |
| 32 |
32 |
| 33 |
33 |
| 34 UCX_TEST(test_io_http_stream_parse_chunk_header_hdronly_first) { |
34 CX_TEST(test_io_http_stream_parse_chunk_header_hdronly_first) { |
| 35 char *str = strdup("100\r\n"); |
35 char *str = strdup("100\r\n"); |
| 36 size_t len = strlen(str); |
36 size_t len = strlen(str); |
| 37 char *str2 = strdup("12345\r\n"); |
37 char *str2 = strdup("12345\r\n"); |
| 38 size_t len2 = strlen(str2); |
38 size_t len2 = strlen(str2); |
| 39 char *str3 = strdup("FF\r\n"); |
39 char *str3 = strdup("FF\r\n"); |
| 40 size_t len3 = strlen(str3); |
40 size_t len3 = strlen(str3); |
| 41 |
41 |
| 42 UCX_TEST_BEGIN; |
42 CX_TEST_DO { |
| 43 |
43 |
| 44 int64_t chunklen; |
44 int64_t chunklen; |
| 45 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
45 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
| 46 UCX_TEST_ASSERT(ret == 5, "ret != 5"); |
46 CX_TEST_ASSERT(ret == 5); |
| 47 UCX_TEST_ASSERT(chunklen == 0x100, "wrong chunk length"); |
47 CX_TEST_ASSERT(chunklen == 0x100); |
| 48 |
48 |
| 49 // test 2 |
49 // test 2 |
| 50 ret = http_stream_parse_chunk_header(str2, len2, TRUE, &chunklen); |
50 ret = http_stream_parse_chunk_header(str2, len2, TRUE, &chunklen); |
| 51 UCX_TEST_ASSERT(ret == 7, "ret != 7 (test 2)"); |
51 CX_TEST_ASSERT(ret == 7); |
| 52 UCX_TEST_ASSERT(chunklen == 0x12345, "wrong chunk length (test 2)"); |
52 CX_TEST_ASSERT(chunklen == 0x12345); |
| 53 |
53 |
| 54 // test 3: hex test |
54 // test 3: hex test |
| 55 ret = http_stream_parse_chunk_header(str3, len3, TRUE, &chunklen); |
55 ret = http_stream_parse_chunk_header(str3, len3, TRUE, &chunklen); |
| 56 UCX_TEST_ASSERT(ret == 4, "ret != 7 (test 3)"); |
56 CX_TEST_ASSERT(ret == 4); |
| 57 UCX_TEST_ASSERT(chunklen == 0xFF, "wrong chunk length (test 3)"); |
57 CX_TEST_ASSERT(chunklen == 0xFF); |
| 58 |
58 |
| 59 UCX_TEST_END; |
59 } |
| 60 free(str); |
60 free(str); |
| 61 free(str2); |
61 free(str2); |
| 62 } |
62 } |
| 63 |
63 |
| 64 UCX_TEST(test_io_http_stream_parse_chunk_header_hdronly) { |
64 CX_TEST(test_io_http_stream_parse_chunk_header_hdronly) { |
| 65 char *str = strdup("\r\n100\r\n"); |
65 char *str = strdup("\r\n100\r\n"); |
| 66 size_t len = strlen(str); |
66 size_t len = strlen(str); |
| 67 char *str2 = strdup("\nab\n"); |
67 char *str2 = strdup("\nab\n"); |
| 68 size_t len2 = strlen(str2); |
68 size_t len2 = strlen(str2); |
| 69 |
69 |
| 70 UCX_TEST_BEGIN; |
70 CX_TEST_DO { |
| 71 |
71 |
| 72 int64_t chunklen; |
72 int64_t chunklen; |
| 73 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
73 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
| 74 UCX_TEST_ASSERT(ret == 7, "ret != 7"); |
74 CX_TEST_ASSERT(ret == 7); |
| 75 UCX_TEST_ASSERT(chunklen == 0x100, "wrong chunk length"); |
75 CX_TEST_ASSERT(chunklen == 0x100); |
| 76 |
76 |
| 77 // test 2 with just \n as line break |
77 // test 2 with just \n as line break |
| 78 ret = http_stream_parse_chunk_header(str2, len2, FALSE, &chunklen); |
78 ret = http_stream_parse_chunk_header(str2, len2, FALSE, &chunklen); |
| 79 UCX_TEST_ASSERT(ret == 4, "ret != 5 (test 2)"); |
79 CX_TEST_ASSERT(ret == 4); |
| 80 UCX_TEST_ASSERT(chunklen == 0xab, "wrong chunk length (test 2)"); |
80 CX_TEST_ASSERT(chunklen == 0xab); |
| 81 |
81 |
| 82 UCX_TEST_END; |
82 } |
| 83 free(str); |
83 free(str); |
| 84 free(str2); |
84 free(str2); |
| 85 } |
85 } |
| 86 |
86 |
| 87 UCX_TEST(test_io_http_stream_parse_chunk_header_hdronly_seq_fail) { |
87 CX_TEST(test_io_http_stream_parse_chunk_header_hdronly_seq_fail) { |
| 88 // test: after the first chunk header, \r\n is required before any new header |
88 // test: after the first chunk header, \r\n is required before any new header |
| 89 char *str = strdup("ff\r\n"); |
89 char *str = strdup("ff\r\n"); |
| 90 size_t len = strlen(str); |
90 size_t len = strlen(str); |
| 91 |
91 |
| 92 UCX_TEST_BEGIN; |
92 CX_TEST_DO { |
| 93 |
93 |
| 94 int64_t chunklen; |
94 int64_t chunklen; |
| 95 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
95 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
| 96 UCX_TEST_ASSERT(ret == -1, "ret != -1"); |
96 CX_TEST_ASSERT(ret == -1); |
| 97 |
97 |
| 98 UCX_TEST_END; |
98 } |
| 99 free(str); |
99 free(str); |
| 100 } |
100 } |
| 101 |
101 |
| 102 UCX_TEST(test_io_http_stream_parse_chunk_header_hdr_data) { |
102 CX_TEST(test_io_http_stream_parse_chunk_header_hdr_data) { |
| 103 char *str = strdup("\r\nb\r\nhello world\r\n"); |
103 char *str = strdup("\r\nb\r\nhello world\r\n"); |
| 104 size_t len = strlen(str); |
104 size_t len = strlen(str); |
| 105 |
105 |
| 106 UCX_TEST_BEGIN; |
106 CX_TEST_DO { |
| 107 |
107 |
| 108 int64_t chunklen; |
108 int64_t chunklen; |
| 109 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
109 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
| 110 UCX_TEST_ASSERT(ret == 5, "ret != 5"); |
110 CX_TEST_ASSERT(ret == 5); |
| 111 |
111 |
| 112 UCX_TEST_END; |
112 } |
| 113 free(str); |
113 free(str); |
| 114 } |
114 } |
| 115 |
115 |
| 116 UCX_TEST(test_io_http_stream_parse_chunk_header_empty) { |
116 CX_TEST(test_io_http_stream_parse_chunk_header_empty) { |
| 117 char *str = ""; |
117 char *str = ""; |
| 118 size_t len = strlen(str); |
118 size_t len = strlen(str); |
| 119 |
119 |
| 120 UCX_TEST_BEGIN; |
120 CX_TEST_DO { |
| 121 |
121 |
| 122 int64_t chunklen; |
122 int64_t chunklen; |
| 123 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
123 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
| 124 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
124 CX_TEST_ASSERT(ret == 0); |
| 125 |
125 |
| 126 ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
126 ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
| 127 UCX_TEST_ASSERT(ret == 0, "ret != 0 (test 2)"); |
127 CX_TEST_ASSERT(ret == 0); |
| 128 |
128 |
| 129 UCX_TEST_END; |
129 } |
| 130 } |
130 } |
| 131 |
131 |
| 132 UCX_TEST(test_io_http_stream_parse_chunk_header_partial_first) { |
132 CX_TEST(test_io_http_stream_parse_chunk_header_partial_first) { |
| 133 char *str = strdup("123"); |
133 char *str = strdup("123"); |
| 134 size_t len = strlen(str); |
134 size_t len = strlen(str); |
| 135 |
135 |
| 136 UCX_TEST_BEGIN; |
136 CX_TEST_DO { |
| 137 |
137 |
| 138 int64_t chunklen; |
138 int64_t chunklen; |
| 139 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
139 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
| 140 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
140 CX_TEST_ASSERT(ret == 0); |
| 141 |
141 |
| 142 UCX_TEST_END; |
142 } |
| 143 free(str); |
143 free(str); |
| 144 } |
144 } |
| 145 |
145 |
| 146 UCX_TEST(test_io_http_stream_parse_chunk_header_partial) { |
146 CX_TEST(test_io_http_stream_parse_chunk_header_partial) { |
| 147 char *str = strdup("123"); |
147 char *str = strdup("123"); |
| 148 size_t len = strlen(str); |
148 size_t len = strlen(str); |
| 149 char *str2 = strdup("\r\n"); |
149 char *str2 = strdup("\r\n"); |
| 150 size_t len2 = strlen(str2); |
150 size_t len2 = strlen(str2); |
| 151 char *str3 = strdup("\r"); |
151 char *str3 = strdup("\r"); |
| 152 size_t len3 = strlen(str3); |
152 size_t len3 = strlen(str3); |
| 153 char *str4 = strdup("\r\n123"); |
153 char *str4 = strdup("\r\n123"); |
| 154 size_t len4 = strlen(str4); |
154 size_t len4 = strlen(str4); |
| 155 |
155 |
| 156 UCX_TEST_BEGIN; |
156 CX_TEST_DO { |
| 157 |
157 |
| 158 int64_t chunklen; |
158 int64_t chunklen; |
| 159 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
159 int ret = http_stream_parse_chunk_header(str, len, TRUE, &chunklen); |
| 160 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
160 CX_TEST_ASSERT(ret == 0); |
| 161 ret = http_stream_parse_chunk_header(str2, len2, FALSE, &chunklen); |
161 ret = http_stream_parse_chunk_header(str2, len2, FALSE, &chunklen); |
| 162 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
162 CX_TEST_ASSERT(ret == 0); |
| 163 ret = http_stream_parse_chunk_header(str3, len3, FALSE, &chunklen); |
163 ret = http_stream_parse_chunk_header(str3, len3, FALSE, &chunklen); |
| 164 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
164 CX_TEST_ASSERT(ret == 0); |
| 165 ret = http_stream_parse_chunk_header(str4, len4, FALSE, &chunklen); |
165 ret = http_stream_parse_chunk_header(str4, len4, FALSE, &chunklen); |
| 166 UCX_TEST_ASSERT(ret == 0, "ret != 0"); |
166 CX_TEST_ASSERT(ret == 0); |
| 167 |
167 |
| 168 UCX_TEST_END; |
168 } |
| 169 free(str); |
169 free(str); |
| 170 free(str2); |
170 free(str2); |
| 171 free(str3); |
171 free(str3); |
| 172 free(str4); |
172 free(str4); |
| 173 } |
173 } |
| 174 |
174 |
| 175 UCX_TEST(test_io_http_stream_parse_chunk_header_invalid) { |
175 CX_TEST(test_io_http_stream_parse_chunk_header_invalid) { |
| 176 char *str = strdup("hello\r\n"); |
176 char *str = strdup("hello\r\n"); |
| 177 size_t len = strlen(str); |
177 size_t len = strlen(str); |
| 178 char *str2 = strdup("x4\r\n\r\n123\r\n"); |
178 char *str2 = strdup("x4\r\n\r\n123\r\n"); |
| 179 size_t len2 = strlen(str2); |
179 size_t len2 = strlen(str2); |
| 180 char *str3 = strdup("\r\n\r\n123\r\n"); |
180 char *str3 = strdup("\r\n\r\n123\r\n"); |
| 241 size_t len3 = strlen(str3); |
240 size_t len3 = strlen(str3); |
| 242 char *str4 = strdup("\r\n0"); |
241 char *str4 = strdup("\r\n0"); |
| 243 size_t len4 = strlen(str4); |
242 size_t len4 = strlen(str4); |
| 244 |
243 |
| 245 |
244 |
| 246 UCX_TEST_BEGIN; |
245 CX_TEST_DO { |
| 247 |
246 |
| 248 int64_t chunklen = -1; |
247 int64_t chunklen = -1; |
| 249 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
248 int ret = http_stream_parse_chunk_header(str, len, FALSE, &chunklen); |
| 250 UCX_TEST_ASSERT(ret == 7, "ret != 7"); |
249 CX_TEST_ASSERT(ret == 7); |
| 251 UCX_TEST_ASSERT(chunklen == 0, "chunklen != 0"); |
250 CX_TEST_ASSERT(chunklen == 0); |
| 252 |
251 |
| 253 chunklen = -1; |
252 chunklen = -1; |
| 254 ret = http_stream_parse_chunk_header(str2, len2, TRUE, &chunklen); |
253 ret = http_stream_parse_chunk_header(str2, len2, TRUE, &chunklen); |
| 255 UCX_TEST_ASSERT(ret == 5, "ret != 5 (test 2)"); |
254 CX_TEST_ASSERT(ret == 5); |
| 256 UCX_TEST_ASSERT(chunklen == 0, "chunklen != 0 (test 2)"); |
255 CX_TEST_ASSERT(chunklen == 0); |
| 257 |
256 |
| 258 // expect 0 (incomplete) |
257 // expect 0 (incomplete) |
| 259 ret = http_stream_parse_chunk_header(str3, len3, FALSE, &chunklen); |
258 ret = http_stream_parse_chunk_header(str3, len3, FALSE, &chunklen); |
| 260 UCX_TEST_ASSERT(ret == 0, "ret != 3 (test 3)"); |
259 CX_TEST_ASSERT(ret == 0); |
| 261 |
260 |
| 262 ret = http_stream_parse_chunk_header(str4, len4, FALSE, &chunklen); |
261 ret = http_stream_parse_chunk_header(str4, len4, FALSE, &chunklen); |
| 263 UCX_TEST_ASSERT(ret == 0, "ret != 3 (test 4)"); |
262 CX_TEST_ASSERT(ret == 0); |
| 264 |
263 |
| 265 UCX_TEST_END; |
264 } |
| 266 free(str); |
265 free(str); |
| 267 free(str2); |
266 free(str2); |
| 268 free(str3); |
267 free(str3); |
| 269 free(str4); |
268 free(str4); |
| 270 } |
269 } |
| 271 |
270 |
| 272 |
271 |
| 273 UCX_TEST(test_io_httpstream_write) { |
272 CX_TEST(test_io_httpstream_write) { |
| 274 Session *sn = testutil_session(); |
273 Session *sn = testutil_session(); |
| 275 |
274 |
| 276 TestIOStream *st = testutil_iostream(2048, TRUE); |
275 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 277 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
276 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 278 |
277 |
| 279 UCX_TEST_BEGIN; |
278 CX_TEST_DO { |
| |
279 |
| |
280 char *msg = "hello world!"; |
| |
281 size_t msglen = strlen(msg); |
| |
282 |
| |
283 ssize_t w = net_write(http, msg, msglen); |
| |
284 |
| |
285 CX_TEST_ASSERT(w == msglen); |
| |
286 CX_TEST_ASSERT(st->buf->size == msglen); |
| |
287 CX_TEST_ASSERT(!memcmp(st->buf->space, msg, msglen)); |
| |
288 |
| |
289 // test again, make sure the second message is written directly after the wirst one |
| |
290 char *msg2 = "test"; |
| |
291 size_t msglen2 = strlen(msg2); |
| |
292 |
| |
293 w = net_write(http, msg2, msglen2); |
| |
294 |
| |
295 CX_TEST_ASSERT(w == msglen2); |
| |
296 CX_TEST_ASSERT(st->buf->size == msglen+msglen2); |
| |
297 CX_TEST_ASSERT(!memcmp(st->buf->space + msglen, msg2, msglen2)); |
| |
298 |
| |
299 } |
| |
300 |
| |
301 testutil_destroy_session(sn); |
| |
302 } |
| |
303 |
| |
304 CX_TEST(test_io_httpstream_chunked_write) { |
| |
305 Session *sn = testutil_session(); |
| |
306 |
| |
307 TestIOStream *st = testutil_iostream(2048, TRUE); |
| |
308 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| |
309 httpstream_enable_chunked_write(http); |
| |
310 |
| |
311 CX_TEST_DO { |
| |
312 |
| |
313 char *msg = "hello world!"; |
| |
314 size_t msglen = strlen(msg); |
| |
315 |
| |
316 char *bufmsg = "c\r\nhello world!\r\n"; |
| |
317 size_t bufmsglen = strlen(bufmsg); |
| |
318 |
| |
319 ssize_t w = net_write(http, msg, msglen); |
| |
320 |
| |
321 cxstring s1 = cx_strn(st->buf->space, st->buf->size); |
| |
322 cxstring s2 = cx_strn(bufmsg, bufmsglen); |
| |
323 |
| |
324 CX_TEST_ASSERT(w == msglen); |
| |
325 CX_TEST_ASSERT(st->buf->size == bufmsglen); |
| |
326 CX_TEST_ASSERT(!cx_strcasecmp(s1, s2)); |
| |
327 |
| |
328 // write again |
| |
329 w = net_write(http, msg, msglen); |
| |
330 CX_TEST_ASSERT(w == msglen); |
| |
331 CX_TEST_ASSERT(st->buf->size == 2*bufmsglen); |
| |
332 |
| |
333 cxstring s3 = cx_strn(st->buf->space+bufmsglen, bufmsglen); |
| |
334 CX_TEST_ASSERT(!cx_strcasecmp(s2, s3)); |
| |
335 |
| |
336 } |
| |
337 } |
| |
338 |
| |
339 CX_TEST(test_io_httpstream_chunked_write_end) { |
| |
340 Session *sn = testutil_session(); |
| |
341 |
| |
342 TestIOStream *st = testutil_iostream(2048, TRUE); |
| |
343 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| |
344 httpstream_enable_chunked_write(http); |
| |
345 |
| |
346 CX_TEST_DO { |
| |
347 |
| |
348 char *msg = "hello world!"; |
| |
349 size_t msglen = strlen(msg); |
| |
350 |
| |
351 char *bufmsg = "c\r\nhello world!\r\n0\r\n\r\n"; |
| |
352 size_t bufmsglen = strlen(bufmsg); |
| |
353 |
| |
354 ssize_t w = net_write(http, msg, msglen); |
| |
355 net_finish(http); |
| |
356 |
| |
357 cxstring s1 = cx_strn(st->buf->space, st->buf->size); |
| |
358 cxstring s2 = cx_strn(bufmsg, bufmsglen); |
| |
359 |
| |
360 CX_TEST_ASSERT(w == msglen); |
| |
361 CX_TEST_ASSERT(st->buf->size == bufmsglen); |
| |
362 CX_TEST_ASSERT(!cx_strcasecmp(s1, s2)); |
| |
363 |
| |
364 } |
| |
365 } |
| |
366 |
| |
367 CX_TEST(test_io_httpstream_chunked_write_xx) { |
| |
368 // This test creates a giant buffer and writes it with |
| |
369 // chunked transfer encoding to the http stream with varying chunk length |
| |
370 |
| |
371 Session *sn = testutil_session(); |
| |
372 |
| |
373 TestIOStream *st = testutil_iostream(2048, TRUE); |
| |
374 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| |
375 httpstream_enable_chunked_write(http); |
| |
376 |
| |
377 CX_TEST_DO { |
| |
378 |
| |
379 // create test data |
| |
380 CxBuffer *testdata = cxBufferCreate(NULL, 1024*1024*4, cxDefaultAllocator, 0); |
| |
381 for(size_t i=0;i<testdata->capacity;i++) { |
| |
382 cxBufferPut(testdata, 35+(i%91)); |
| |
383 } |
| |
384 |
| |
385 // write chunks, start with single diget chunk length and increase |
| |
386 // chunk size with each step |
| |
387 size_t pos = 0; |
| |
388 int j = 0; |
| |
389 ssize_t i=15; |
| |
390 while(pos<testdata->size) { |
| |
391 char *buf = testdata->space + pos; |
| |
392 size_t remaining = testdata->size - pos; |
| |
393 ssize_t len = pos + i < remaining ? i : remaining; |
| |
394 pos += len; |
| |
395 |
| |
396 ssize_t w = net_write(http, buf, len); |
| |
397 |
| |
398 CX_TEST_ASSERT(w == len); |
| |
399 i+=100; // increase chunk size |
| |
400 j++; // debug |
| |
401 } |
| |
402 |
| |
403 // terminate chunk |
| |
404 net_finish(http); |
| |
405 |
| |
406 // code below also used in test_io_httpstream_chunked_write_xx_limit |
| |
407 |
| |
408 // make sure the output is correctly encoded |
| |
409 // extract chunks from st->buf by using http_stream_parse_chunk_header |
| |
410 // (which should be well-tested) |
| |
411 WSBool first_chunk = TRUE; |
| |
412 int64_t chunklen = 0; |
| |
413 |
| |
414 char *buf = st->buf->space; |
| |
415 size_t bufsize = st->buf->size; |
| |
416 |
| |
417 pos = 0; // st->buf position |
| |
418 size_t srcpos = 0; // testdata position |
| |
419 int debug_counter = 0; |
| |
420 while(pos < bufsize) { |
| |
421 ssize_t remaining = bufsize - pos; |
| |
422 ssize_t src_remaining = testdata->size - srcpos; |
| |
423 |
| |
424 int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen); |
| |
425 first_chunk = FALSE; |
| |
426 |
| |
427 // ret must always be > 0 (0: incomplete chunk header, -1: invalid syntax) |
| |
428 CX_TEST_ASSERT(ret > 0); |
| |
429 if(chunklen == 0) { |
| |
430 CX_TEST_ASSERT(src_remaining == 0); |
| |
431 break; |
| |
432 } |
| |
433 |
| |
434 CX_TEST_ASSERT(chunklen <= src_remaining); |
| |
435 |
| |
436 char *src_chunk = testdata->space+srcpos; |
| |
437 char *buf_chunk = buf+pos+ret; |
| |
438 |
| |
439 CX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen)); |
| |
440 |
| |
441 pos += ret + chunklen; |
| |
442 srcpos += chunklen; |
| |
443 |
| |
444 debug_counter++; |
| |
445 } |
| |
446 |
| |
447 cxBufferFree(testdata); |
| |
448 testutil_destroy_session(sn); |
| |
449 testutil_iostream_destroy(st); |
| |
450 |
| |
451 } |
| |
452 } |
| |
453 |
| |
454 CX_TEST(test_io_httpstream_chunked_write_partial_header) { |
| |
455 Session *sn = testutil_session(); |
| |
456 |
| |
457 TestIOStream *st = testutil_iostream(2048, TRUE); |
| |
458 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| |
459 httpstream_enable_chunked_write(http); |
| |
460 |
| |
461 CX_TEST_DO { |
| |
462 |
| |
463 memset(st->buf->space, 0, st->buf->capacity); |
| 280 |
464 |
| 281 char *msg = "hello world!"; |
465 char *msg = "hello world!"; |
| 282 size_t msglen = strlen(msg); |
466 size_t msglen = strlen(msg); |
| 283 |
467 |
| |
468 st->max_write = 1; // limit the test stream max write size |
| |
469 io_set_max_writes(1); |
| |
470 |
| |
471 // only 1 byte of the header is written, 0 bytes of msg |
| 284 ssize_t w = net_write(http, msg, msglen); |
472 ssize_t w = net_write(http, msg, msglen); |
| 285 |
473 CX_TEST_ASSERT(w == 0); |
| 286 UCX_TEST_ASSERT(w == msglen, "wrong size returned by net_write"); |
474 CX_TEST_ASSERT(st->buf->size == 1); |
| 287 UCX_TEST_ASSERT(st->buf->size == msglen, "wrong buffer size"); |
475 CX_TEST_ASSERT(tolower(st->buf->space[0]) == 'c'); |
| 288 UCX_TEST_ASSERT(!memcmp(st->buf->space, msg, msglen), "wrong buffer content"); |
476 |
| 289 |
477 // next header byte: '\r' |
| 290 // test again, make sure the second message is written directly after the wirst one |
|
| 291 char *msg2 = "test"; |
|
| 292 size_t msglen2 = strlen(msg2); |
|
| 293 |
|
| 294 w = net_write(http, msg2, msglen2); |
|
| 295 |
|
| 296 UCX_TEST_ASSERT(w == msglen2, "wrong size returned by net_write (2)"); |
|
| 297 UCX_TEST_ASSERT(st->buf->size == msglen+msglen2, "wrong buffer size (2)"); |
|
| 298 UCX_TEST_ASSERT(!memcmp(st->buf->space + msglen, msg2, msglen2), "wrong buffer content (2)"); |
|
| 299 |
|
| 300 UCX_TEST_END; |
|
| 301 |
|
| 302 testutil_destroy_session(sn); |
|
| 303 } |
|
| 304 |
|
| 305 UCX_TEST(test_io_httpstream_chunked_write) { |
|
| 306 Session *sn = testutil_session(); |
|
| 307 |
|
| 308 TestIOStream *st = testutil_iostream(2048, TRUE); |
|
| 309 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
|
| 310 httpstream_enable_chunked_write(http); |
|
| 311 |
|
| 312 UCX_TEST_BEGIN; |
|
| 313 |
|
| 314 char *msg = "hello world!"; |
|
| 315 size_t msglen = strlen(msg); |
|
| 316 |
|
| 317 char *bufmsg = "c\r\nhello world!\r\n"; |
|
| 318 size_t bufmsglen = strlen(bufmsg); |
|
| 319 |
|
| 320 ssize_t w = net_write(http, msg, msglen); |
|
| 321 |
|
| 322 cxstring s1 = cx_strn(st->buf->space, st->buf->size); |
|
| 323 cxstring s2 = cx_strn(bufmsg, bufmsglen); |
|
| 324 |
|
| 325 UCX_TEST_ASSERT(w == msglen, "wrong size returned by net_write"); |
|
| 326 UCX_TEST_ASSERT(st->buf->size == bufmsglen, "wrong buffer size"); |
|
| 327 UCX_TEST_ASSERT(!cx_strcasecmp(s1, s2), "wrong buffer content"); |
|
| 328 |
|
| 329 // write again |
|
| 330 w = net_write(http, msg, msglen); |
478 w = net_write(http, msg, msglen); |
| 331 UCX_TEST_ASSERT(w == msglen, "write 2: wrong return value"); |
479 CX_TEST_ASSERT(w == 0); |
| 332 UCX_TEST_ASSERT(st->buf->size == 2*bufmsglen, "write 2: wrong buf size"); |
480 CX_TEST_ASSERT(st->buf->size == 2); |
| 333 |
481 CX_TEST_ASSERT(st->buf->space[1] == '\r'); |
| 334 cxstring s3 = cx_strn(st->buf->space+bufmsglen, bufmsglen); |
482 |
| 335 UCX_TEST_ASSERT(!cx_strcasecmp(s2, s3), "write 2: wrong buf content"); |
483 // next header byte: '\n' |
| 336 |
484 w = net_write(http, msg, msglen); |
| 337 |
485 CX_TEST_ASSERT(w == 0); |
| 338 UCX_TEST_END; |
486 CX_TEST_ASSERT(st->buf->size == 3); |
| 339 } |
487 CX_TEST_ASSERT(st->buf->space[2] == '\n'); |
| 340 |
488 |
| 341 UCX_TEST(test_io_httpstream_chunked_write_end) { |
489 // next: content |
| 342 Session *sn = testutil_session(); |
490 w = net_write(http, msg, msglen); |
| 343 |
491 CX_TEST_ASSERT(w == 1); |
| 344 TestIOStream *st = testutil_iostream(2048, TRUE); |
492 CX_TEST_ASSERT(st->buf->size == 4); |
| 345 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
493 CX_TEST_ASSERT(st->buf->space[3] == msg[0]); |
| 346 httpstream_enable_chunked_write(http); |
494 |
| 347 |
|
| 348 UCX_TEST_BEGIN; |
|
| 349 |
|
| 350 char *msg = "hello world!"; |
|
| 351 size_t msglen = strlen(msg); |
|
| 352 |
|
| 353 char *bufmsg = "c\r\nhello world!\r\n0\r\n\r\n"; |
|
| 354 size_t bufmsglen = strlen(bufmsg); |
|
| 355 |
|
| 356 ssize_t w = net_write(http, msg, msglen); |
|
| 357 net_finish(http); |
|
| 358 |
|
| 359 cxstring s1 = cx_strn(st->buf->space, st->buf->size); |
|
| 360 cxstring s2 = cx_strn(bufmsg, bufmsglen); |
|
| 361 |
|
| 362 UCX_TEST_ASSERT(w == msglen, "wrong size returned by net_write"); |
|
| 363 UCX_TEST_ASSERT(st->buf->size == bufmsglen, "wrong buffer size"); |
|
| 364 UCX_TEST_ASSERT(!cx_strcasecmp(s1, s2), "wrong buffer content"); |
|
| 365 |
|
| 366 |
|
| 367 UCX_TEST_END; |
|
| 368 } |
|
| 369 |
|
| 370 UCX_TEST(test_io_httpstream_chunked_write_xx) { |
|
| 371 // This test creates a giant buffer and writes it with |
|
| 372 // chunked transfer encoding to the http stream with varying chunk length |
|
| 373 |
|
| 374 Session *sn = testutil_session(); |
|
| 375 |
|
| 376 TestIOStream *st = testutil_iostream(2048, TRUE); |
|
| 377 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
|
| 378 httpstream_enable_chunked_write(http); |
|
| 379 |
|
| 380 UCX_TEST_BEGIN; |
|
| 381 |
|
| 382 // create test data |
|
| 383 CxBuffer *testdata = cxBufferCreate(NULL, 1024*1024*4, cxDefaultAllocator, 0); |
|
| 384 for(size_t i=0;i<testdata->capacity;i++) { |
|
| 385 cxBufferPut(testdata, 35+(i%91)); |
|
| 386 } |
|
| 387 |
|
| 388 // write chunks, start with single diget chunk length and increase |
|
| 389 // chunk size with each step |
|
| 390 size_t pos = 0; |
|
| 391 int j = 0; |
|
| 392 ssize_t i=15; |
|
| 393 while(pos<testdata->size) { |
|
| 394 char *buf = testdata->space + pos; |
|
| 395 size_t remaining = testdata->size - pos; |
|
| 396 ssize_t len = pos + i < remaining ? i : remaining; |
|
| 397 pos += len; |
|
| 398 |
|
| 399 ssize_t w = net_write(http, buf, len); |
|
| 400 |
|
| 401 UCX_TEST_ASSERT(w == len, "wrong size returned by net_write"); |
|
| 402 i+=100; // increase chunk size |
|
| 403 j++; // debug |
|
| 404 } |
|
| 405 |
|
| 406 // terminate chunk |
|
| 407 net_finish(http); |
|
| 408 |
|
| 409 // code below also used in test_io_httpstream_chunked_write_xx_limit |
|
| 410 |
|
| 411 // make sure the output is correctly encoded |
|
| 412 // extract chunks from st->buf by using http_stream_parse_chunk_header |
|
| 413 // (which should be well-tested) |
|
| 414 WSBool first_chunk = TRUE; |
|
| 415 int64_t chunklen = 0; |
|
| 416 |
|
| 417 char *buf = st->buf->space; |
|
| 418 size_t bufsize = st->buf->size; |
|
| 419 |
|
| 420 pos = 0; // st->buf position |
|
| 421 size_t srcpos = 0; // testdata position |
|
| 422 int debug_counter = 0; |
|
| 423 while(pos < bufsize) { |
|
| 424 ssize_t remaining = bufsize - pos; |
|
| 425 ssize_t src_remaining = testdata->size - srcpos; |
|
| 426 |
|
| 427 int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen); |
|
| 428 first_chunk = FALSE; |
|
| 429 |
|
| 430 // ret must always be > 0 (0: incomplete chunk header, -1: invalid syntax) |
|
| 431 UCX_TEST_ASSERT(ret > 0, "http_stream_parse_chunk_header ret <= 0"); |
|
| 432 if(chunklen == 0) { |
|
| 433 UCX_TEST_ASSERT(src_remaining == 0, "stream end reached but src_remaining > 0"); |
|
| 434 break; |
|
| 435 } |
|
| 436 |
|
| 437 UCX_TEST_ASSERT(chunklen <= src_remaining, "chunklen > src_remaining"); |
|
| 438 |
|
| 439 char *src_chunk = testdata->space+srcpos; |
|
| 440 char *buf_chunk = buf+pos+ret; |
|
| 441 |
|
| 442 UCX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen), "memcmp failed"); |
|
| 443 |
|
| 444 pos += ret + chunklen; |
|
| 445 srcpos += chunklen; |
|
| 446 |
|
| 447 debug_counter++; |
|
| 448 } |
|
| 449 |
|
| 450 cxBufferFree(testdata); |
|
| 451 testutil_destroy_session(sn); |
495 testutil_destroy_session(sn); |
| 452 testutil_iostream_destroy(st); |
496 testutil_iostream_destroy(st); |
| 453 |
497 |
| 454 UCX_TEST_END; |
498 } |
| 455 } |
499 } |
| 456 |
500 |
| 457 UCX_TEST(test_io_httpstream_chunked_write_partial_header) { |
501 CX_TEST(test_io_httpstream_chunked_write_partial_data) { |
| 458 Session *sn = testutil_session(); |
502 Session *sn = testutil_session(); |
| 459 |
503 |
| 460 TestIOStream *st = testutil_iostream(2048, TRUE); |
504 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 461 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
505 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 462 httpstream_enable_chunked_write(http); |
506 httpstream_enable_chunked_write(http); |
| 463 |
507 |
| 464 UCX_TEST_BEGIN; |
508 CX_TEST_DO { |
| 465 |
509 |
| 466 memset(st->buf->space, 0, st->buf->capacity); |
510 memset(st->buf->space, 0, st->buf->capacity); |
| 467 |
511 |
| 468 char *msg = "hello world!"; |
512 char *msg = "hello world!"; |
| 469 size_t msglen = strlen(msg); |
513 size_t msglen = strlen(msg); |
| 470 |
514 size_t msglen_orig = msglen; |
| 471 st->max_write = 1; // limit the test stream max write size |
515 |
| 472 io_set_max_writes(1); |
516 // limit first write to 3 to only write the header |
| 473 |
517 st->max_write = 3; |
| 474 // only 1 byte of the header is written, 0 bytes of msg |
518 io_set_max_writes(1); |
| 475 ssize_t w = net_write(http, msg, msglen); |
519 |
| 476 UCX_TEST_ASSERT(w == 0, "write 1: wrong return value"); |
520 ssize_t w = net_write(http, msg, msglen); |
| 477 UCX_TEST_ASSERT(st->buf->size == 1, "write 1: wrong buf size"); |
521 |
| 478 UCX_TEST_ASSERT(tolower(st->buf->space[0]) == 'c', "write 1: wrong buf content"); |
522 CX_TEST_ASSERT(w == 0); |
| 479 |
523 CX_TEST_ASSERT(st->buf->size == 3); |
| 480 // next header byte: '\r' |
524 CX_TEST_ASSERT(st->buf->space[0] == 'c'); |
| 481 w = net_write(http, msg, msglen); |
525 CX_TEST_ASSERT(st->buf->space[2] == '\n'); |
| 482 UCX_TEST_ASSERT(w == 0, "write 2: wrong return value"); |
526 |
| 483 UCX_TEST_ASSERT(st->buf->size == 2, "write 2: wrong buf size"); |
527 w = net_write(http, msg, msglen); |
| 484 UCX_TEST_ASSERT(st->buf->space[1] == '\r', "write 2: wrong content"); |
528 CX_TEST_ASSERT(w == 3); |
| 485 |
529 CX_TEST_ASSERT(st->buf->size == 6); |
| 486 // next header byte: '\n' |
530 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhel\0", 7)); |
| 487 w = net_write(http, msg, msglen); |
531 |
| 488 UCX_TEST_ASSERT(w == 0, "write 3: wrong return value"); |
532 msg += w; |
| 489 UCX_TEST_ASSERT(st->buf->size == 3, "write 3: wrong buf size"); |
533 msglen -= w; |
| 490 UCX_TEST_ASSERT(st->buf->space[2] == '\n', "write 3: wrong content"); |
534 |
| 491 |
535 w = net_write(http, msg, msglen); |
| 492 // next: content |
536 CX_TEST_ASSERT(w == 3); |
| 493 w = net_write(http, msg, msglen); |
537 CX_TEST_ASSERT(st->buf->size == 9); |
| 494 UCX_TEST_ASSERT(w == 1, "write 4: wrong return value"); |
538 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello \0", 10)); |
| 495 UCX_TEST_ASSERT(st->buf->size == 4, "write 3: wrong buf size"); |
539 |
| 496 UCX_TEST_ASSERT(st->buf->space[3] == msg[0], "write 3: wrong content"); |
540 st->max_write = 1024; |
| 497 |
541 msg += w; |
| 498 testutil_destroy_session(sn); |
542 msglen -= w; |
| 499 testutil_iostream_destroy(st); |
543 |
| 500 |
544 w = net_write(http, msg, msglen); |
| 501 UCX_TEST_END; |
545 CX_TEST_ASSERT(w == msglen); |
| 502 } |
546 CX_TEST_ASSERT(st->buf->size == 3 + msglen_orig + 2); |
| 503 |
547 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n", st->buf->size)); |
| 504 UCX_TEST(test_io_httpstream_chunked_write_partial_data) { |
548 |
| 505 Session *sn = testutil_session(); |
549 testutil_destroy_session(sn); |
| 506 |
550 testutil_iostream_destroy(st); |
| 507 TestIOStream *st = testutil_iostream(2048, TRUE); |
551 |
| 508 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
552 } |
| 509 httpstream_enable_chunked_write(http); |
553 } |
| 510 |
554 |
| 511 UCX_TEST_BEGIN; |
555 CX_TEST(test_io_httpstream_chunked_write_partial_trailer) { |
| 512 |
|
| 513 memset(st->buf->space, 0, st->buf->capacity); |
|
| 514 |
|
| 515 char *msg = "hello world!"; |
|
| 516 size_t msglen = strlen(msg); |
|
| 517 size_t msglen_orig = msglen; |
|
| 518 |
|
| 519 // limit first write to 3 to only write the header |
|
| 520 st->max_write = 3; |
|
| 521 io_set_max_writes(1); |
|
| 522 |
|
| 523 ssize_t w = net_write(http, msg, msglen); |
|
| 524 |
|
| 525 UCX_TEST_ASSERT(w == 0, "write 1: wrong return value"); |
|
| 526 UCX_TEST_ASSERT(st->buf->size == 3, "write 1: wrong buf size"); |
|
| 527 UCX_TEST_ASSERT(st->buf->space[0] == 'c', "write 1: wrong buf content"); |
|
| 528 UCX_TEST_ASSERT(st->buf->space[2] == '\n', "write 1: wrong buf content"); |
|
| 529 |
|
| 530 w = net_write(http, msg, msglen); |
|
| 531 UCX_TEST_ASSERT(w == 3, "write 2: wrong return value"); |
|
| 532 UCX_TEST_ASSERT(st->buf->size == 6, "write 2: wrong buf size"); |
|
| 533 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhel\0", 7), "write 2: wrong buf content"); |
|
| 534 |
|
| 535 msg += w; |
|
| 536 msglen -= w; |
|
| 537 |
|
| 538 w = net_write(http, msg, msglen); |
|
| 539 UCX_TEST_ASSERT(w == 3, "write 3: wrong return value"); |
|
| 540 UCX_TEST_ASSERT(st->buf->size == 9, "write 3: wrong buf size"); |
|
| 541 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello \0", 10), "write 3: wrong buf content"); |
|
| 542 |
|
| 543 st->max_write = 1024; |
|
| 544 msg += w; |
|
| 545 msglen -= w; |
|
| 546 |
|
| 547 w = net_write(http, msg, msglen); |
|
| 548 UCX_TEST_ASSERT(w == msglen, "write 4: wrong return value"); |
|
| 549 UCX_TEST_ASSERT(st->buf->size == 3 + msglen_orig + 2, "write 4: wrong buf size"); |
|
| 550 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n", st->buf->size), "write 4: wrong buf content"); |
|
| 551 |
|
| 552 testutil_destroy_session(sn); |
|
| 553 testutil_iostream_destroy(st); |
|
| 554 |
|
| 555 UCX_TEST_END; |
|
| 556 } |
|
| 557 |
|
| 558 UCX_TEST(test_io_httpstream_chunked_write_partial_trailer) { |
|
| 559 Session *sn = testutil_session(); |
556 Session *sn = testutil_session(); |
| 560 |
557 |
| 561 TestIOStream *st = testutil_iostream(2048, TRUE); |
558 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 562 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
559 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 563 httpstream_enable_chunked_write(http); |
560 httpstream_enable_chunked_write(http); |
| 564 io_set_max_writes(1); |
561 io_set_max_writes(1); |
| 565 |
562 |
| 566 UCX_TEST_BEGIN; |
563 CX_TEST_DO { |
| 567 |
564 |
| 568 memset(st->buf->space, 0, st->buf->capacity); |
565 memset(st->buf->space, 0, st->buf->capacity); |
| 569 |
566 |
| 570 char *msg = "hello world!"; |
567 char *msg = "hello world!"; |
| 571 size_t msglen = strlen(msg); |
568 size_t msglen = strlen(msg); |
| 572 |
569 |
| 573 char *msg2 = "newmsg"; |
570 char *msg2 = "newmsg"; |
| 574 size_t msglen2 = strlen(msg2); |
571 size_t msglen2 = strlen(msg2); |
| 575 |
572 |
| 576 char *msg3 = "msg3"; |
573 char *msg3 = "msg3"; |
| 577 size_t msglen3 = strlen(msg3); |
574 size_t msglen3 = strlen(msg3); |
| 578 |
575 |
| 579 st->max_write = 3 + msglen; // header + msg, but without trailer |
576 st->max_write = 3 + msglen; // header + msg, but without trailer |
| 580 |
577 |
| 581 ssize_t w = net_write(http, msg, msglen); |
578 ssize_t w = net_write(http, msg, msglen); |
| 582 |
579 |
| 583 UCX_TEST_ASSERT(w == msglen, "write 1: wrong return value"); |
580 CX_TEST_ASSERT(w == msglen); |
| 584 UCX_TEST_ASSERT(st->buf->size == 3 + msglen, "write 1: wrong buf size"); |
581 CX_TEST_ASSERT(st->buf->size == 3 + msglen); |
| 585 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\0", st->buf->size + 1), "write 1: wrong buf content"); |
582 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\0", st->buf->size + 1)); |
| 586 |
583 |
| 587 st->max_write = 2 + 3 + msglen2; // trailer + new header + new msg, without new trailer |
584 st->max_write = 2 + 3 + msglen2; // trailer + new header + new msg, without new trailer |
| 588 |
585 |
| 589 w = net_write(http, msg2, msglen2); |
586 w = net_write(http, msg2, msglen2); |
| 590 UCX_TEST_ASSERT(w == msglen2, "write 2: wrong return value"); |
587 CX_TEST_ASSERT(w == msglen2); |
| 591 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2, "write 2: wrong buf size"); |
588 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2); |
| 592 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\0", st->buf->size + 1), "write 2: wrong buf content"); |
589 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\0", st->buf->size + 1)); |
| 593 |
590 |
| 594 // limit write to 1 byte: two writes required for trailer, net_write should return 0 |
591 // limit write to 1 byte: two writes required for trailer, net_write should return 0 |
| 595 st->max_write = 1; |
592 st->max_write = 1; |
| 596 |
593 |
| 597 w = net_write(http, "dummymsg", 8); |
594 w = net_write(http, "dummymsg", 8); |
| 598 UCX_TEST_ASSERT(w == 0, "write 3: wrong return value"); |
595 CX_TEST_ASSERT(w == 0); |
| 599 |
596 |
| 600 w = net_write(http, "dummymsg", 8); |
597 w = net_write(http, "dummymsg", 8); |
| 601 UCX_TEST_ASSERT(w == 0, "write 4: wrong return value"); |
598 CX_TEST_ASSERT(w == 0); |
| 602 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2, "write 4: wrong buf size"); |
599 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2); |
| 603 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n\0", st->buf->size + 1), "write 4: wrong buf content"); |
600 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n\0", st->buf->size + 1)); |
| 604 |
601 |
| 605 st->max_write = 1024; |
602 st->max_write = 1024; |
| 606 w = net_write(http, msg3, msglen3); |
603 w = net_write(http, msg3, msglen3); |
| 607 |
604 |
| 608 UCX_TEST_ASSERT(w == msglen3, "write 5: wrong return value"); |
605 CX_TEST_ASSERT(w == msglen3); |
| 609 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2 + 3 + msglen3 + 2, "write 5: wrong buf size"); |
606 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2 + 3 + msglen3 + 2); |
| 610 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n4\r\nmsg3\r\n", st->buf->size + 1), "write 5: wrong buf content"); |
607 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)); |
| 611 |
608 |
| 612 |
609 |
| 613 testutil_destroy_session(sn); |
610 testutil_destroy_session(sn); |
| 614 testutil_iostream_destroy(st); |
611 testutil_iostream_destroy(st); |
| 615 |
612 |
| 616 UCX_TEST_END; |
613 } |
| 617 } |
614 } |
| 618 |
615 |
| 619 UCX_TEST(test_io_httpstream_chunked_write_partial_trailer_partial_header) { |
616 CX_TEST(test_io_httpstream_chunked_write_partial_trailer_partial_header) { |
| 620 Session *sn = testutil_session(); |
617 Session *sn = testutil_session(); |
| 621 |
618 |
| 622 TestIOStream *st = testutil_iostream(2048, TRUE); |
619 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 623 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
620 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 624 httpstream_enable_chunked_write(http); |
621 httpstream_enable_chunked_write(http); |
| 625 io_set_max_writes(1); |
622 io_set_max_writes(1); |
| 626 |
623 |
| 627 UCX_TEST_BEGIN; |
624 CX_TEST_DO { |
| 628 |
625 |
| 629 memset(st->buf->space, 0, st->buf->capacity); |
626 memset(st->buf->space, 0, st->buf->capacity); |
| 630 |
627 |
| 631 char *msg = "hello world!"; |
628 char *msg = "hello world!"; |
| 632 size_t msglen = strlen(msg); |
629 size_t msglen = strlen(msg); |
| 633 |
630 |
| 634 char *msg2 = "newmsg"; |
631 char *msg2 = "newmsg"; |
| 635 size_t msglen2 = strlen(msg2); |
632 size_t msglen2 = strlen(msg2); |
| 636 |
633 |
| 637 // Test: write partial trailer followed by partial header write |
634 // Test: write partial trailer followed by partial header write |
| 638 |
635 |
| 639 st->max_write = 3 + msglen + 1; |
636 st->max_write = 3 + msglen + 1; |
| 640 |
637 |
| 641 ssize_t w = net_write(http, msg, msglen); |
638 ssize_t w = net_write(http, msg, msglen); |
| 642 |
639 |
| 643 UCX_TEST_ASSERT(w == msglen, "write 1: wrong return value"); |
640 CX_TEST_ASSERT(w == msglen); |
| 644 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 1, "write 1: wrong buf size"); |
641 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 1); |
| 645 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\0", st->buf->size + 1), "write 1: wrong buf content"); |
642 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\0", st->buf->size + 1)); |
| 646 |
643 |
| 647 st->max_write = 2; // write 1 trailer byte and 1 header byte |
644 st->max_write = 2; // write 1 trailer byte and 1 header byte |
| 648 |
645 |
| 649 w = net_write(http, msg2, msglen2); |
646 w = net_write(http, msg2, msglen2); |
| 650 |
647 |
| 651 UCX_TEST_ASSERT(w == 0, "write 2: wrong return value"); |
648 CX_TEST_ASSERT(w == 0); |
| 652 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 1, "write 2: wrong buf size"); |
649 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 1); |
| 653 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\0", st->buf->size + 1), "write 2: wrong buf content"); |
650 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\0", st->buf->size + 1)); |
| 654 |
651 |
| 655 // force partial header write again |
652 // force partial header write again |
| 656 st->max_write = 1; |
653 st->max_write = 1; |
| 657 |
654 |
| 658 w = net_write(http, msg2, msglen2); |
655 w = net_write(http, msg2, msglen2); |
| 659 |
656 |
| 660 UCX_TEST_ASSERT(w == 0, "write 3: wrong return value"); |
657 CX_TEST_ASSERT(w == 0); |
| 661 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 2, "write 3: wrong buf size"); |
658 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 2); |
| 662 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\0", st->buf->size + 1), "write 3: wrong buf content"); |
659 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\0", st->buf->size + 1)); |
| 663 |
660 |
| 664 st->max_write = 1024; |
661 st->max_write = 1024; |
| 665 |
662 |
| 666 w = net_write(http, msg2, msglen2); |
663 w = net_write(http, msg2, msglen2); |
| 667 |
664 |
| 668 UCX_TEST_ASSERT(w ==msglen2, "write 4: wrong return value"); |
665 CX_TEST_ASSERT(w ==msglen2); |
| 669 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2, "write 4: wrong buf size"); |
666 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2); |
| 670 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size + 1), "write 4: wrong buf content"); |
667 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size + 1)); |
| 671 |
668 |
| 672 |
669 |
| 673 testutil_destroy_session(sn); |
670 testutil_destroy_session(sn); |
| 674 testutil_iostream_destroy(st); |
671 testutil_iostream_destroy(st); |
| 675 |
672 |
| 676 UCX_TEST_END; |
673 } |
| 677 } |
674 } |
| 678 |
675 |
| 679 UCX_TEST(test_io_httpstream_chunked_write_data_2x) { |
676 CX_TEST(test_io_httpstream_chunked_write_data_2x) { |
| 680 Session *sn = testutil_session(); |
677 Session *sn = testutil_session(); |
| 681 |
678 |
| 682 TestIOStream *st = testutil_iostream(2048, TRUE); |
679 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 683 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
680 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 684 httpstream_enable_chunked_write(http); |
681 httpstream_enable_chunked_write(http); |
| 685 io_set_max_writes(1); |
682 io_set_max_writes(1); |
| 686 |
683 |
| 687 UCX_TEST_BEGIN; |
684 CX_TEST_DO { |
| 688 |
685 |
| 689 memset(st->buf->space, 0, st->buf->capacity); |
686 memset(st->buf->space, 0, st->buf->capacity); |
| 690 |
687 |
| 691 // Test: First write a partial header, which forces a chunk with a specific |
688 // Test: First write a partial header, which forces a chunk with a specific |
| 692 // size. After that, write a message, that is bigger than the first |
689 // size. After that, write a message, that is bigger than the first |
| 693 // chunk, forcing a start of a second chunk, in one big writev op. |
690 // chunk, forcing a start of a second chunk, in one big writev op. |
| 694 |
691 |
| 695 char *msg = "hello world!"; |
692 char *msg = "hello world!"; |
| 696 size_t msglen = strlen(msg); |
693 size_t msglen = strlen(msg); |
| 697 |
694 |
| 698 char *msg2 = "newmsg"; |
695 char *msg2 = "newmsg"; |
| 699 size_t msglen2 = strlen(msg2); |
696 size_t msglen2 = strlen(msg2); |
| 700 |
697 |
| 701 char *msg_big = "hello world!newmsg"; |
698 char *msg_big = "hello world!newmsg"; |
| 702 size_t msglen_big = strlen(msg_big); |
699 size_t msglen_big = strlen(msg_big); |
| 703 |
700 |
| 704 st->max_write = 1; |
701 st->max_write = 1; |
| 705 |
702 |
| 706 ssize_t w = net_write(http, msg, msglen); // first chunk: msg |
703 ssize_t w = net_write(http, msg, msglen); // first chunk: msg |
| 707 |
704 |
| 708 UCX_TEST_ASSERT(w == 0, "write 1: wrong return value"); |
705 CX_TEST_ASSERT(w == 0); |
| 709 UCX_TEST_ASSERT(st->buf->size == 1, "write 1: wrong buf size"); |
706 CX_TEST_ASSERT(st->buf->size == 1); |
| 710 |
707 |
| 711 st->max_write = 1024; |
708 st->max_write = 1024; |
| 712 |
709 |
| 713 w = net_write(http, msg_big, msglen_big); // first chunk + new chunk |
710 w = net_write(http, msg_big, msglen_big); // first chunk + new chunk |
| 714 |
711 |
| 715 UCX_TEST_ASSERT(w == msglen_big, "write 2: wrong return value"); |
712 CX_TEST_ASSERT(w == msglen_big); |
| 716 UCX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2, "write 2: wrong buf size"); |
713 CX_TEST_ASSERT(st->buf->size == 3 + msglen + 2 + 3 + msglen2 + 2); |
| 717 UCX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size + 1), "write 2: wrong buf content"); |
714 CX_TEST_ASSERT(!memcmp(st->buf->space, "c\r\nhello world!\r\n6\r\nnewmsg\r\n", st->buf->size + 1)); |
| 718 |
715 |
| 719 |
716 |
| 720 testutil_destroy_session(sn); |
717 testutil_destroy_session(sn); |
| 721 testutil_iostream_destroy(st); |
718 testutil_iostream_destroy(st); |
| 722 |
719 |
| 723 UCX_TEST_END; |
720 } |
| 724 } |
721 } |
| 725 |
722 |
| 726 UCX_TEST(test_io_httpstream_chunked_write_xx_limit) { |
723 CX_TEST(test_io_httpstream_chunked_write_xx_limit) { |
| 727 Session *sn = testutil_session(); |
724 Session *sn = testutil_session(); |
| 728 |
725 |
| 729 TestIOStream *st = testutil_iostream(2048, TRUE); |
726 TestIOStream *st = testutil_iostream(2048, TRUE); |
| 730 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
727 IOStream *http = httpstream_new(sn->pool, (IOStream*)st); |
| 731 httpstream_enable_chunked_write(http); |
728 httpstream_enable_chunked_write(http); |
| 732 io_set_max_writes(1); |
729 io_set_max_writes(1); |
| 733 |
730 |
| 734 UCX_TEST_BEGIN; |
731 CX_TEST_DO { |
| 735 |
732 |
| 736 // Test: create testdata and write it in varying chunk sizes, but |
733 // Test: create testdata and write it in varying chunk sizes, but |
| 737 // limit TestIOStream to 1 to 3 byte writes |
734 // limit TestIOStream to 1 to 3 byte writes |
| 738 |
735 |
| 739 // create test data |
736 // create test data |
| 740 CxBuffer *testdata = cxBufferCreate(NULL, 1024*16, cxDefaultAllocator, 0); |
737 CxBuffer *testdata = cxBufferCreate(NULL, 1024*16, cxDefaultAllocator, 0); |
| 741 for(size_t i=0;i<testdata->capacity;i++) { |
738 for(size_t i=0;i<testdata->capacity;i++) { |
| 742 cxBufferPut(testdata, 35+(i%91)); |
739 cxBufferPut(testdata, 35+(i%91)); |
| 743 } |
|
| 744 |
|
| 745 st->max_write = 1; |
|
| 746 |
|
| 747 size_t pos = 0; |
|
| 748 int chunksize = 1; |
|
| 749 while(pos < testdata->size) { |
|
| 750 size_t available = testdata->size - pos; |
|
| 751 |
|
| 752 char *chunk = testdata->space + pos; |
|
| 753 size_t chunklen = chunksize > available ? available : chunksize; |
|
| 754 |
|
| 755 // write chunk |
|
| 756 size_t chunkpos = 0; |
|
| 757 int max_writes = chunklen + 24; // max number of write attempts |
|
| 758 int writes = 0; |
|
| 759 while(chunkpos < chunklen) { |
|
| 760 ssize_t w = net_write(http, chunk+chunkpos, chunklen-chunkpos); |
|
| 761 UCX_TEST_ASSERT(w >= 0, "net_write failed"); |
|
| 762 chunkpos += w; |
|
| 763 |
|
| 764 writes++; |
|
| 765 UCX_TEST_ASSERT(writes < max_writes, "max writes attempts reached"); |
|
| 766 } |
740 } |
| 767 |
741 |
| 768 pos += chunklen; |
742 st->max_write = 1; |
| 769 chunksize += 5; |
743 |
| 770 |
744 size_t pos = 0; |
| 771 // increase max write size at some point |
745 int chunksize = 1; |
| 772 if(pos + chunksize >= testdata->size) { |
746 while(pos < testdata->size) { |
| 773 st->max_write = INT_MAX; |
747 size_t available = testdata->size - pos; |
| 774 } else if(pos > 1024*2) { |
748 |
| 775 if(pos < 1024*8) { |
749 char *chunk = testdata->space + pos; |
| 776 st->max_write = 2; |
750 size_t chunklen = chunksize > available ? available : chunksize; |
| 777 } else { |
751 |
| 778 st->max_write = 3; |
752 // write chunk |
| |
753 size_t chunkpos = 0; |
| |
754 int max_writes = chunklen + 24; // max number of write attempts |
| |
755 int writes = 0; |
| |
756 while(chunkpos < chunklen) { |
| |
757 ssize_t w = net_write(http, chunk+chunkpos, chunklen-chunkpos); |
| |
758 CX_TEST_ASSERT(w >= 0); |
| |
759 chunkpos += w; |
| |
760 |
| |
761 writes++; |
| |
762 CX_TEST_ASSERT(writes < max_writes); |
| |
763 } |
| |
764 |
| |
765 pos += chunklen; |
| |
766 chunksize += 5; |
| |
767 |
| |
768 // increase max write size at some point |
| |
769 if(pos + chunksize >= testdata->size) { |
| |
770 st->max_write = INT_MAX; |
| |
771 } else if(pos > 1024*2) { |
| |
772 if(pos < 1024*8) { |
| |
773 st->max_write = 2; |
| |
774 } else { |
| |
775 st->max_write = 3; |
| |
776 } |
| 779 } |
777 } |
| 780 } |
778 } |
| 781 } |
779 |
| 782 |
780 // terminate chunk |
| 783 // terminate chunk |
781 net_finish(http); |
| 784 net_finish(http); |
782 |
| 785 |
783 |
| 786 |
784 // same code as test_io_httpstream_chunked_write_xx |
| 787 // same code as test_io_httpstream_chunked_write_xx |
785 |
| 788 |
786 // make sure the output is correctly encoded |
| 789 // make sure the output is correctly encoded |
787 // extract chunks from st->buf by using http_stream_parse_chunk_header |
| 790 // extract chunks from st->buf by using http_stream_parse_chunk_header |
788 // (which should be well-tested) |
| 791 // (which should be well-tested) |
789 |
| 792 |
790 WSBool first_chunk = TRUE; |
| 793 WSBool first_chunk = TRUE; |
791 int64_t chunklen = 0; |
| 794 int64_t chunklen = 0; |
792 |
| 795 |
793 char *buf = st->buf->space; |
| 796 char *buf = st->buf->space; |
794 size_t bufsize = st->buf->size; |
| 797 size_t bufsize = st->buf->size; |
795 |
| 798 |
796 pos = 0; // st->buf position |
| 799 pos = 0; // st->buf position |
797 size_t srcpos = 0; // testdata position |
| 800 size_t srcpos = 0; // testdata position |
798 int debug_counter = 0; |
| 801 int debug_counter = 0; |
799 while(pos < bufsize) { |
| 802 while(pos < bufsize) { |
800 ssize_t remaining = bufsize - pos; |
| 803 ssize_t remaining = bufsize - pos; |
801 ssize_t src_remaining = testdata->size - srcpos; |
| 804 ssize_t src_remaining = testdata->size - srcpos; |
802 |
| 805 |
803 int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen); |
| 806 int ret = http_stream_parse_chunk_header(buf+pos, remaining, first_chunk, &chunklen); |
804 first_chunk = FALSE; |
| 807 first_chunk = FALSE; |
805 |
| 808 |
806 // ret must always be > 0 (0: incomplete chunk header, -1: invalid syntax) |
| 809 // ret must always be > 0 (0: incomplete chunk header, -1: invalid syntax) |
807 CX_TEST_ASSERT(ret > 0); |
| 810 UCX_TEST_ASSERT(ret > 0, "http_stream_parse_chunk_header ret <= 0"); |
808 if(chunklen == 0) { |
| 811 if(chunklen == 0) { |
809 CX_TEST_ASSERT(src_remaining == 0); |
| 812 UCX_TEST_ASSERT(src_remaining == 0, "stream end reached but src_remaining > 0"); |
810 break; |
| 813 break; |
811 } |
| |
812 |
| |
813 CX_TEST_ASSERT(chunklen <= src_remaining); |
| |
814 |
| |
815 char *src_chunk = testdata->space+srcpos; |
| |
816 char *buf_chunk = buf+pos+ret; |
| |
817 |
| |
818 CX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen)); |
| |
819 |
| |
820 pos += ret + chunklen; |
| |
821 srcpos += chunklen; |
| |
822 |
| |
823 debug_counter++; |
| 814 } |
824 } |
| 815 |
825 |
| 816 UCX_TEST_ASSERT(chunklen <= src_remaining, "chunklen > src_remaining"); |
826 |
| 817 |
827 testutil_destroy_session(sn); |
| 818 char *src_chunk = testdata->space+srcpos; |
828 testutil_iostream_destroy(st); |
| 819 char *buf_chunk = buf+pos+ret; |
829 cxBufferFree(testdata); |
| 820 |
830 |
| 821 UCX_TEST_ASSERT(!memcmp(buf_chunk, src_chunk, chunklen), "memcmp failed"); |
831 } |
| 822 |
832 } |
| 823 pos += ret + chunklen; |
|
| 824 srcpos += chunklen; |
|
| 825 |
|
| 826 debug_counter++; |
|
| 827 } |
|
| 828 |
|
| 829 |
|
| 830 testutil_destroy_session(sn); |
|
| 831 testutil_iostream_destroy(st); |
|
| 832 cxBufferFree(testdata); |
|
| 833 |
|
| 834 UCX_TEST_END; |
|
| 835 } |
|