src/server/test/io.c

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

mercurial