From 779c825e92a9c99895d9eeab627dfcb3d157efe5 Mon Sep 17 00:00:00 2001 From: cpq Date: Fri, 5 Aug 2022 19:18:06 +0100 Subject: [PATCH] Fixes to chunked support when pipelining --- Makefile | 2 +- mongoose.c | 146 ++++++++++++++++++++++++++--------------------- mongoose.h | 1 + src/http.c | 141 ++++++++++++++++++++++++--------------------- src/net.h | 1 + src/sock.c | 5 ++ test/unit_test.c | 75 +++++++++++++----------- 7 files changed, 208 insertions(+), 163 deletions(-) diff --git a/Makefile b/Makefile index 96b93c9c..bb8688f8 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ INCS ?= -Isrc -I. SSL ?= MBEDTLS CWD ?= $(realpath $(CURDIR)) ENV ?= -e Tmp=. -e WINEDEBUG=-all -DOCKER ?= docker run --rm $(ENV) -v $(CWD):$(CWD) -w $(CWD) +DOCKER ?= docker run --platform linux/amd64 --rm $(ENV) -v $(CWD):$(CWD) -w $(CWD) VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS) IPV6 ?= 1 ASAN ?= -fsanitize=address,undefined -fno-sanitize-recover=all diff --git a/mongoose.c b/mongoose.c index a0ab331e..2bb9178a 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1699,6 +1699,7 @@ static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, if (c->send.len >= len + 10) { mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10); c->send.buf[len + 8] = '\r'; + if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker } mg_send(c, "\r\n", 2); } @@ -1714,6 +1715,7 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len); mg_send(c, "\r\n", 2); + if (len == 0) c->is_resp = 0; } // clang-format off @@ -1752,8 +1754,10 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers, if (c->send.len > 15) { mg_snprintf((char *) &c->send.buf[len - 14], 11, "%010lu", (unsigned long) (c->send.len - len)); + c->is_resp = 0; c->send.buf[len - 4] = '\r'; // Change ending 0 to space } + c->is_resp = 0; } static void http_cb(struct mg_connection *, int, void *, void *); @@ -1761,6 +1765,7 @@ static void restore_http_cb(struct mg_connection *c) { mg_fs_close((struct mg_fd *) c->pfn_data); c->pfn_data = NULL; c->pfn = http_cb; + c->is_resp = 0; } char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime); @@ -2277,85 +2282,93 @@ void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) { c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK); } +static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen, + struct mg_http_message *hm, bool *next) { + // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... + // +------------------+--------------------------+---- + // | hlen | chunk1 | ...... + char *buf = (char *) &c->recv.buf[hlen], *p = buf; + size_t len = c->recv.len - hlen; + size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; + size_t mark, pl, dl, del = 0, ofs = 0; + bool last = false; + if (processed <= len) len -= processed, buf += processed; + while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { + size_t saved = c->recv.len; + memmove(p + processed, buf + ofs + pl, dl); + // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); + hm->chunk = mg_str_n(p + processed, dl); + mg_call(c, MG_EV_HTTP_CHUNK, hm); + ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix + processed += dl; + if (c->recv.len != saved) processed -= dl, buf -= dl; + mg_hexdump(c->recv.buf, hlen + processed); + last = (dl == 0); + } + mg_iobuf_del(&c->recv, hlen + processed, del); + mark = ((size_t) c->pfn_data) & MG_DMARK; + c->pfn_data = (void *) (processed | mark); + if (last) { + hm->body.len = processed; + hm->message.len = hlen + processed; + c->pfn_data = NULL; + if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true; + // MG_INFO(("LAST, mark: %lx", mark)); + // mg_hexdump(c->recv.buf, c->recv.len); + } +} + +static void deliver_normal_chunks(struct mg_connection *c, size_t hlen, + struct mg_http_message *hm, bool *next) { + size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK; + bool deleted = ((size_t) c->pfn_data) & MG_DMARK; + hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen); + if (processed <= hm->chunk.len && !deleted) { + hm->chunk.len -= processed; + hm->chunk.ptr += processed; + } + left = hm->body.len < processed ? 0 : hm->body.len - processed; + if (hm->chunk.len > left) hm->chunk.len = left; + if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm); + processed += hm->chunk.len; + if (processed >= hm->body.len) { // Last, 0-len chunk + hm->chunk.len = 0; // Reset length + mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler + c->pfn_data = NULL; // Reset processed counter + if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true; + } else { + size_t del = ((size_t) c->pfn_data) & MG_DMARK; // Keep deletion marker + c->pfn_data = (void *) (processed | del); // if it is set + } +} + static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { struct mg_http_message hm; + // mg_hexdump(c->recv.buf, c->recv.len); while (c->recv.buf != NULL && c->recv.len > 0) { + bool next = false; int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); if (hlen < 0) { mg_error(c, "HTTP parse:\n%.*s", (int) c->recv.len, c->recv.buf); break; } - if (hlen == 0) break; // Request is not buffered yet - if (ev == MG_EV_CLOSE) { - hm.message.len = c->recv.len; + if (c->is_resp) break; // Response is still generated + if (hlen == 0) break; // Request is not buffered yet + if (ev == MG_EV_CLOSE) { // If client did not set Content-Length + hm.message.len = c->recv.len; // and closes now, deliver a MSG hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr); } - - // Deliver MG_EV_HTTP_CHUNK if (mg_is_chunked(&hm)) { - // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... - // +------------------+--------------------------+---- - // | hlen | chunk1 | ...... - char *buf = (char *) &c->recv.buf[hlen], *p = buf; - size_t len = c->recv.len - (size_t) hlen; - size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; - size_t mark, pl, dl, del = 0, ofs = 0; - bool last = false; - if (processed <= len) len -= processed, buf += processed; - while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { - size_t saved = c->recv.len; - memmove(p + processed, buf + ofs + pl, dl); - // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); - hm.chunk = mg_str_n(p + processed, dl); - mg_call(c, MG_EV_HTTP_CHUNK, &hm); - ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix - processed += dl; - if (c->recv.len != saved) processed -= dl, buf -= dl; - // mg_hexdump(c->recv.buf, (size_t) hlen + processed); - last = (dl == 0); - } - mg_iobuf_del(&c->recv, (size_t) hlen + processed, del); - mark = ((size_t) c->pfn_data) & MG_DMARK; - c->pfn_data = (void *) (processed | mark); - if (last) { - hm.body.len = processed; - hm.message.len = (size_t) hlen + processed; - c->pfn_data = NULL; - if (mark) mg_iobuf_del(&c->recv, 0, (size_t) hlen); - // MG_INFO(("LAST")); - // mg_hexdump(c->recv.buf, c->recv.len); - } + deliver_chunked_chunks(c, (size_t) hlen, &hm, &next); } else { - size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; - bool deleted = ((size_t) c->pfn_data) & MG_DMARK; - // if (processed > hm.body.len) processed = hm.body.len; - hm.chunk.ptr = (char *) &c->recv.buf[hlen]; - hm.chunk.len = c->recv.len - (size_t) hlen; - if (processed <= hm.chunk.len && !deleted) { - hm.chunk.ptr += processed; - hm.chunk.len -= processed; - if (hm.chunk.len + processed > hm.body.len) { - hm.chunk.len = hm.body.len - processed; - } - } - // MG_INFO(("NCL->%d %d", (int) processed, (int) hm.chunk.len)); - if (hm.chunk.len) mg_call(c, MG_EV_HTTP_CHUNK, &hm); - processed += hm.chunk.len; - if (processed >= hm.body.len) { // Last, 0-len chunk - hm.chunk.len = 0; - mg_call(c, MG_EV_HTTP_CHUNK, &hm); - if (processed && deleted) mg_iobuf_del(&c->recv, 0, (size_t) hlen); - c->pfn_data = NULL; - } else { - // MG_EV_HTTP_CHUNK handler may have set MG_DMARK: do not override - size_t del = ((size_t) c->pfn_data) & MG_DMARK; - c->pfn_data = (void *) (processed | del); - } + deliver_normal_chunks(c, (size_t) hlen, &hm, &next); } - // Chunk events are delivered. If we have full body, deliver MSG + if (next) continue; // Chunks & request were deleted + // Chunk events are delivered. If we have full body, deliver MSG if (c->recv.len < hm.message.len) break; - mg_call(c, MG_EV_HTTP_MSG, &hm); + if (c->is_accepted) c->is_resp = 1; // Start generating response + mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp mg_iobuf_del(&c->recv, 0, hm.message.len); } } @@ -4613,8 +4626,13 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { mg_timer_poll(&mgr->timers, now); for (c = mgr->conns; c != NULL; c = tmp) { + bool is_resp = c->is_resp; tmp = c->next; mg_call(c, MG_EV_POLL, &now); + if (is_resp && !c->is_resp) { + struct mg_str fake = mg_str_n("", 0); + mg_call(c, MG_EV_READ, &fake); + } MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', diff --git a/mongoose.h b/mongoose.h index 35becd90..c198c8e5 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1077,6 +1077,7 @@ struct mg_connection { unsigned is_draining : 1; // Send remaining data, then close and free unsigned is_closing : 1; // Close and free the connection immediately unsigned is_full : 1; // Stop reads, until cleared + unsigned is_resp : 1; // Response is still being generated unsigned is_readable : 1; // Connection is ready to read unsigned is_writable : 1; // Connection is ready to write }; diff --git a/src/http.c b/src/http.c index df86c5d9..2d3a4401 100644 --- a/src/http.c +++ b/src/http.c @@ -277,6 +277,7 @@ static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, if (c->send.len >= len + 10) { mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10); c->send.buf[len + 8] = '\r'; + if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker } mg_send(c, "\r\n", 2); } @@ -292,6 +293,7 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { mg_printf(c, "%lx\r\n", (unsigned long) len); mg_send(c, buf, len); mg_send(c, "\r\n", 2); + if (len == 0) c->is_resp = 0; } // clang-format off @@ -330,8 +332,10 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers, if (c->send.len > 15) { mg_snprintf((char *) &c->send.buf[len - 14], 11, "%010lu", (unsigned long) (c->send.len - len)); + c->is_resp = 0; c->send.buf[len - 4] = '\r'; // Change ending 0 to space } + c->is_resp = 0; } static void http_cb(struct mg_connection *, int, void *, void *); @@ -339,6 +343,7 @@ static void restore_http_cb(struct mg_connection *c) { mg_fs_close((struct mg_fd *) c->pfn_data); c->pfn_data = NULL; c->pfn = http_cb; + c->is_resp = 0; } char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime); @@ -855,85 +860,93 @@ void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) { c->pfn_data = (void *) ((size_t) c->pfn_data | MG_DMARK); } +static void deliver_chunked_chunks(struct mg_connection *c, size_t hlen, + struct mg_http_message *hm, bool *next) { + // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... + // +------------------+--------------------------+---- + // | hlen | chunk1 | ...... + char *buf = (char *) &c->recv.buf[hlen], *p = buf; + size_t len = c->recv.len - hlen; + size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; + size_t mark, pl, dl, del = 0, ofs = 0; + bool last = false; + if (processed <= len) len -= processed, buf += processed; + while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { + size_t saved = c->recv.len; + memmove(p + processed, buf + ofs + pl, dl); + // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); + hm->chunk = mg_str_n(p + processed, dl); + mg_call(c, MG_EV_HTTP_CHUNK, hm); + ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix + processed += dl; + if (c->recv.len != saved) processed -= dl, buf -= dl; + mg_hexdump(c->recv.buf, hlen + processed); + last = (dl == 0); + } + mg_iobuf_del(&c->recv, hlen + processed, del); + mark = ((size_t) c->pfn_data) & MG_DMARK; + c->pfn_data = (void *) (processed | mark); + if (last) { + hm->body.len = processed; + hm->message.len = hlen + processed; + c->pfn_data = NULL; + if (mark) mg_iobuf_del(&c->recv, 0, hlen), *next = true; + // MG_INFO(("LAST, mark: %lx", mark)); + // mg_hexdump(c->recv.buf, c->recv.len); + } +} + +static void deliver_normal_chunks(struct mg_connection *c, size_t hlen, + struct mg_http_message *hm, bool *next) { + size_t left, processed = ((size_t) c->pfn_data) & ~MG_DMARK; + bool deleted = ((size_t) c->pfn_data) & MG_DMARK; + hm->chunk = mg_str_n((char *) &c->recv.buf[hlen], c->recv.len - hlen); + if (processed <= hm->chunk.len && !deleted) { + hm->chunk.len -= processed; + hm->chunk.ptr += processed; + } + left = hm->body.len < processed ? 0 : hm->body.len - processed; + if (hm->chunk.len > left) hm->chunk.len = left; + if (hm->chunk.len > 0) mg_call(c, MG_EV_HTTP_CHUNK, hm); + processed += hm->chunk.len; + if (processed >= hm->body.len) { // Last, 0-len chunk + hm->chunk.len = 0; // Reset length + mg_call(c, MG_EV_HTTP_CHUNK, hm); // Call user handler + c->pfn_data = NULL; // Reset processed counter + if (processed && deleted) mg_iobuf_del(&c->recv, 0, hlen), *next = true; + } else { + size_t del = ((size_t) c->pfn_data) & MG_DMARK; // Keep deletion marker + c->pfn_data = (void *) (processed | del); // if it is set + } +} + static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { struct mg_http_message hm; + // mg_hexdump(c->recv.buf, c->recv.len); while (c->recv.buf != NULL && c->recv.len > 0) { + bool next = false; int hlen = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); if (hlen < 0) { mg_error(c, "HTTP parse:\n%.*s", (int) c->recv.len, c->recv.buf); break; } - if (hlen == 0) break; // Request is not buffered yet - if (ev == MG_EV_CLOSE) { - hm.message.len = c->recv.len; + if (c->is_resp) break; // Response is still generated + if (hlen == 0) break; // Request is not buffered yet + if (ev == MG_EV_CLOSE) { // If client did not set Content-Length + hm.message.len = c->recv.len; // and closes now, deliver a MSG hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr); } - - // Deliver MG_EV_HTTP_CHUNK if (mg_is_chunked(&hm)) { - // | ... headers ... | HEXNUM\r\n ..data.. \r\n | ...... - // +------------------+--------------------------+---- - // | hlen | chunk1 | ...... - char *buf = (char *) &c->recv.buf[hlen], *p = buf; - size_t len = c->recv.len - (size_t) hlen; - size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; - size_t mark, pl, dl, del = 0, ofs = 0; - bool last = false; - if (processed <= len) len -= processed, buf += processed; - while (!last && getchunk(mg_str_n(buf + ofs, len - ofs), &pl, &dl)) { - size_t saved = c->recv.len; - memmove(p + processed, buf + ofs + pl, dl); - // MG_INFO(("P2 [%.*s]", (int) (processed + dl), p)); - hm.chunk = mg_str_n(p + processed, dl); - mg_call(c, MG_EV_HTTP_CHUNK, &hm); - ofs += pl + dl + 2, del += pl + 2; // 2 is for \r\n suffix - processed += dl; - if (c->recv.len != saved) processed -= dl, buf -= dl; - // mg_hexdump(c->recv.buf, (size_t) hlen + processed); - last = (dl == 0); - } - mg_iobuf_del(&c->recv, (size_t) hlen + processed, del); - mark = ((size_t) c->pfn_data) & MG_DMARK; - c->pfn_data = (void *) (processed | mark); - if (last) { - hm.body.len = processed; - hm.message.len = (size_t) hlen + processed; - c->pfn_data = NULL; - if (mark) mg_iobuf_del(&c->recv, 0, (size_t) hlen); - // MG_INFO(("LAST")); - // mg_hexdump(c->recv.buf, c->recv.len); - } + deliver_chunked_chunks(c, (size_t) hlen, &hm, &next); } else { - size_t processed = ((size_t) c->pfn_data) & ~MG_DMARK; - bool deleted = ((size_t) c->pfn_data) & MG_DMARK; - // if (processed > hm.body.len) processed = hm.body.len; - hm.chunk.ptr = (char *) &c->recv.buf[hlen]; - hm.chunk.len = c->recv.len - (size_t) hlen; - if (processed <= hm.chunk.len && !deleted) { - hm.chunk.ptr += processed; - hm.chunk.len -= processed; - if (hm.chunk.len + processed > hm.body.len) { - hm.chunk.len = hm.body.len - processed; - } - } - // MG_INFO(("NCL->%d %d", (int) processed, (int) hm.chunk.len)); - if (hm.chunk.len) mg_call(c, MG_EV_HTTP_CHUNK, &hm); - processed += hm.chunk.len; - if (processed >= hm.body.len) { // Last, 0-len chunk - hm.chunk.len = 0; - mg_call(c, MG_EV_HTTP_CHUNK, &hm); - if (processed && deleted) mg_iobuf_del(&c->recv, 0, (size_t) hlen); - c->pfn_data = NULL; - } else { - // MG_EV_HTTP_CHUNK handler may have set MG_DMARK: do not override - size_t del = ((size_t) c->pfn_data) & MG_DMARK; - c->pfn_data = (void *) (processed | del); - } + deliver_normal_chunks(c, (size_t) hlen, &hm, &next); } - // Chunk events are delivered. If we have full body, deliver MSG + if (next) continue; // Chunks & request were deleted + // Chunk events are delivered. If we have full body, deliver MSG if (c->recv.len < hm.message.len) break; - mg_call(c, MG_EV_HTTP_MSG, &hm); + if (c->is_accepted) c->is_resp = 1; // Start generating response + mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp mg_iobuf_del(&c->recv, 0, hm.message.len); } } diff --git a/src/net.h b/src/net.h index aab62dd1..24d52666 100644 --- a/src/net.h +++ b/src/net.h @@ -67,6 +67,7 @@ struct mg_connection { unsigned is_draining : 1; // Send remaining data, then close and free unsigned is_closing : 1; // Close and free the connection immediately unsigned is_full : 1; // Stop reads, until cleared + unsigned is_resp : 1; // Response is still being generated unsigned is_readable : 1; // Connection is ready to read unsigned is_writable : 1; // Connection is ready to write }; diff --git a/src/sock.c b/src/sock.c index b1664144..925d142c 100644 --- a/src/sock.c +++ b/src/sock.c @@ -650,8 +650,13 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) { mg_timer_poll(&mgr->timers, now); for (c = mgr->conns; c != NULL; c = tmp) { + bool is_resp = c->is_resp; tmp = c->next; mg_call(c, MG_EV_POLL, &now); + if (is_resp && !c->is_resp) { + struct mg_str fake = mg_str_n("", 0); + mg_call(c, MG_EV_READ, &fake); + } MG_VERBOSE(("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', diff --git a/test/unit_test.c b/test/unit_test.c index 848cd792..6e536e78 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -290,8 +290,17 @@ static void test_iobuf(void) { static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) { if (ev == MG_EV_SNTP_TIME) { - *(int64_t *) fnd = *(int64_t *) evd; - MG_DEBUG(("got time: %lld", *(int64_t *) evd)); + int64_t received = *(int64_t *) evd; + *(int64_t *) fnd = received; + MG_DEBUG(("got time: %lld", received)); +#if MG_ARCH == MG_ARCH_UNIX + struct timeval tv = {0, 0}; + gettimeofday(&tv, 0); + int64_t ms = (int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; + int64_t diff = ms > received ? ms - received : received - ms; + MG_DEBUG(("diff: %lld", diff)); + // ASSERT(diff < 100); // The diff should be less than 300 ms +#endif } else if (ev == MG_EV_OPEN) { c->is_hexdumping = 1; } @@ -1041,7 +1050,7 @@ static void test_http_no_content_length(void) { static void f5(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; - mg_printf(c, "HTTP/1.0 200 OK\n\n%.*s", (int) hm->uri.len, hm->uri.ptr); + mg_http_reply(c, 200, "", "%.*s", (int) hm->uri.len, hm->uri.ptr); (*(int *) fn_data)++; } } @@ -1895,75 +1904,73 @@ static void eY(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { c->label[0]++; if (c->label[0] == 10) mg_send(c, "a", 1); if (c->label[0] == 12) mg_send(c, "bc", 2); - if (c->label[0] == 30) mg_send(c, "d", 1), c->label[0] = 0; + if (c->label[0] == 30) mg_send(c, "d", 1), c->is_resp = 0, c->label[0] = 0; } (void) ev_data, (void) fn_data; } // Do not delete chunks as they arrive static void eh4(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - uint32_t *crc = (uint32_t *) c->label; - if (ev == MG_EV_CONNECT) { - mg_printf(c, "GET / HTTP/1.0\n\n"); - } else if (ev == MG_EV_HTTP_CHUNK) { + uint32_t *crc = (uint32_t *) fn_data; + if (ev == MG_EV_HTTP_CHUNK) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; *crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len); *crc = mg_crc32(*crc, "x", 1); - MG_INFO(("C [%.*s]", (int) hm->chunk.len, hm->chunk.ptr)); + MG_INFO(("%lu C [%.*s]", c->id, (int) hm->chunk.len, hm->chunk.ptr)); } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; - *(uint32_t *) fn_data = mg_crc32(*crc, hm->body.ptr, hm->body.len); - MG_INFO(("M [%.*s]", (int) hm->body.len, hm->body.ptr)); - c->is_closing = 1; + *crc = mg_crc32(*crc, hm->body.ptr, hm->body.len); + MG_INFO(("%lu M [%.*s]", c->id, (int) hm->body.len, hm->body.ptr)); } } // Streaming client event handler. Delete chunks as they arrive static void eh5(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - uint32_t *crc = (uint32_t *) c->label; - if (ev == MG_EV_CONNECT) { - mg_printf(c, "GET / HTTP/1.0\n\n"); - } else if (ev == MG_EV_HTTP_CHUNK) { + uint32_t *crc = (uint32_t *) fn_data; + if (ev == MG_EV_HTTP_CHUNK) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; *crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len); *crc = mg_crc32(*crc, "x", 1); - MG_INFO(("XC [%.*s]", (int) hm->chunk.len, hm->chunk.ptr)); + MG_INFO(("%lu DELC [%.*s]", c->id, (int) hm->chunk.len, hm->chunk.ptr)); mg_http_delete_chunk(c, hm); - if (hm->chunk.len == 0) { - c->is_closing = 1; - *(uint32_t *) fn_data = *crc; - } } else if (ev == MG_EV_HTTP_MSG) { - *(uint32_t *) fn_data = 77U; // Must not be here - ASSERT(0); + ASSERT(0); // Must not be here, MSG must not be fired: chunks deleted! } } static void test_http_chunked_case(mg_event_handler_t s, mg_event_handler_t c, - const char *expected) { + int req_count, const char *expected) { struct mg_mgr mgr; const char *url = "http://127.0.0.1:12344"; - uint32_t i, done = 0; + uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected)); + struct mg_connection *conn; mg_mgr_init(&mgr); mg_http_listen(&mgr, url, s, NULL); - mg_http_connect(&mgr, url, c, &done); - for (i = 0; i < 50 && done == 0; i++) { + conn = mg_http_connect(&mgr, url, c, &crc); + while (conn != NULL && req_count-- > 0) { + mg_printf(conn, "GET / HTTP/1.0\n\n"); + } + for (i = 0; i < 100 && crc != expected_crc; i++) { mg_mgr_poll(&mgr, 1); } - ASSERT(i < 50); - ASSERT(done == mg_crc32(0, expected, strlen(expected))); + ASSERT(i < 100); + ASSERT(crc == expected_crc); mg_mgr_free(&mgr); ASSERT(mgr.conns == NULL); } static void test_http_chunked(void) { // Non-chunked encoding - test_http_chunked_case(eY, eh4, "axbcxdxxabcd"); // Chunks not deleted - test_http_chunked_case(eY, eh5, "axbcxdxx"); // Chunks deleted + test_http_chunked_case(eY, eh4, 1, "axbcxdxxabcd"); // Chunks not deleted + test_http_chunked_case(eY, eh5, 1, "axbcxdxx"); // Chunks deleted + test_http_chunked_case(eY, eh4, 2, "axbcxdxxabcdaxbcxdxxabcd"); + test_http_chunked_case(eY, eh5, 2, "axbcxdxxaxbcxdxx"); // Chunked encoding - test_http_chunked_case(eX, eh4, "axbxcxdxxabcd"); // Chunks not deleted - test_http_chunked_case(eX, eh5, "axbxcxdxx"); // Chunks deleted + test_http_chunked_case(eX, eh4, 1, "axbxcxdxxabcd"); // Chunks not deleted + test_http_chunked_case(eX, eh5, 1, "axbxcxdxx"); // Chunks deleted + test_http_chunked_case(eX, eh4, 2, "axbxcxdxxabcdaxbxcxdxxabcd"); + test_http_chunked_case(eX, eh5, 2, "axbxcxdxxaxbxcxdxx"); } static void test_invalid_listen_addr(void) { @@ -2517,7 +2524,6 @@ int main(void) { test_http_stream_buffer(); test_http_parse(); test_util(); - test_sntp(); test_dns(); test_timer(); test_url(); @@ -2534,6 +2540,7 @@ int main(void) { test_http_no_content_length(); test_http_pipeline(); test_http_range(); + test_sntp(); test_mqtt(); printf("SUCCESS. Total tests: %d\n", s_num_tests); return EXIT_SUCCESS;