mirror of
https://github.com/cesanta/mongoose.git
synced 2024-11-24 02:59:01 +08:00
Fixes to chunked support when pipelining
This commit is contained in:
parent
4bee440a56
commit
779c825e92
2
Makefile
2
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
|
||||
|
146
mongoose.c
146
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',
|
||||
|
@ -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
|
||||
};
|
||||
|
141
src/http.c
141
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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user