Get rid of MG_EV_HTTP_CHUNK

This commit is contained in:
cpq committed 2023-09-26 20:45:24 +01:00
1 parent dde699ffda
commit 7884a2f3c7
10 files changed
+224 -373

No files matched your search

+19 -20
View File
@@ -7,24 +7,23 @@ void mg_call(struct mg_connection *c, int ev, void *ev_data);
void mg_error(struct mg_connection *c, const char *fmt, ...);
enum {
MG_EV_ERROR, // Error char *error_message
MG_EV_OPEN, // Connection created NULL
MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_TLS_HS, // TLS handshake succeeded NULL
MG_EV_READ, // Data received from socket long *bytes_read
MG_EV_WRITE, // Data written to socket long *bytes_written
MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
MG_EV_USER // Starting ID for user events
MG_EV_ERROR, // Error char *error_message
MG_EV_OPEN, // Connection created NULL
MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_TLS_HS, // TLS handshake succeeded NULL
MG_EV_READ, // Data received from socket long *bytes_read
MG_EV_WRITE, // Data written to socket long *bytes_written
MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
MG_EV_USER // Starting ID for user events
};
+53 -110
View File
@@ -245,8 +245,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
hm->message.ptr = hm->head.ptr = s;
hm->body.ptr = end;
hm->head.len = (size_t) req_len;
hm->chunk.ptr = end;
hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite
hm->message.len = hm->body.len = (size_t) -1; // Set body length to infinite
// Parse request line
hm->method.ptr = s;
@@ -951,131 +950,75 @@ static bool is_hex_digit(int c) {
(c >= 'A' && c <= 'F');
}
// If a server sends data to the client using chunked encoding, Mongoose strips
// off the chunking prefix (hex length and \r\n) and suffix (\r\n), appends the
// stripped data to the body, and fires the MG_EV_HTTP_CHUNK event. When zero
// chunk is received, we fire MG_EV_HTTP_MSG, and the body already has all
// chunking prefixes/suffixes stripped.
//
// If a server sends data without chunked encoding, we also fire a series of
// MG_EV_HTTP_CHUNK events for every received piece of data, and then we fire
// MG_EV_HTTP_MSG event in the end.
//
// We track total processed length in the c->pfn_data, which is a void *
// pointer: we store a size_t value there.
static bool getchunk(struct mg_str s, size_t *prefixlen, size_t *datalen) {
size_t i = 0, n;
while (i < s.len && is_hex_digit(s.ptr[i])) i++;
n = mg_unhexn(s.ptr, i);
// MG_INFO(("%d %d", (int) (i + n + 4), (int) s.len));
if (s.len < i + n + 4) return false; // Chunk not yet fully buffered
if (s.ptr[i] != '\r' || s.ptr[i + 1] != '\n') return false;
if (s.ptr[i + n + 2] != '\r' || s.ptr[i + n + 3] != '\n') return false;
*prefixlen = i + 2;
*datalen = n;
return true;
static int skip_chunk(const char *buf, int len, int *pl, int *dl) {
int i = 0, n = 0;
if (len < 3) return 0;
while (i < len && is_hex_digit(buf[i])) i++;
if (len < i + 1 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error
n = (int) mg_unhexn(buf, (size_t) i); // Decode hex length
if (n < 0) return -1; // Error
if (len < i + n + 4) return 0; // Chunk not yet fully buffered
if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error
*pl = i + 2, *dl = n;
return i + 2 + n + 2;
}
static bool mg_is_chunked(struct mg_http_message *hm) {
static bool is_chunked(struct mg_http_message *hm) {
const char *needle = "chunked";
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
return te != NULL && mg_vcasecmp(te, needle) == 0;
}
void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
size_t ofs = (size_t) (hm->chunk.ptr - (char *) c->recv.buf);
mg_iobuf_del(&c->recv, ofs, hm->chunk.len);
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;
size_t 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;
deleted = ((size_t) c->pfn_data) & MG_DMARK; // Re-evaluate after user call
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 {
c->pfn_data = (void *) (processed | deleted); // 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;
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;
size_t ofs = 0; // Parsing offset
while (c->is_resp == 0 && ofs < c->recv.len) {
const char *buf = (char *) c->recv.buf + ofs;
int n = mg_http_parse(buf, c->recv.len - ofs, &hm);
if (n < 0) {
mg_error(c, "HTTP parse");
return;
}
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
if (n == 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 - ofs; // and closes now, deliver MSG
hm.body.len = hm.message.len - (size_t) (hm.body.ptr - hm.message.ptr);
}
if (mg_is_chunked(&hm)) {
deliver_chunked_chunks(c, (size_t) hlen, &hm, &next);
} else {
deliver_normal_chunks(c, (size_t) hlen, &hm, &next);
if (is_chunked(&hm)) {
// For chunked data, strip off prefixes and suffixes from chunks
// and relocate them right after the headers, then report a message
char *s = (char *) c->recv.buf + ofs + n;
int o = 0, pl, dl, cl, len = (int) (c->recv.len - ofs - (size_t) n);
// Find zero-length chunk (the end of the body)
while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0 && dl) o += cl;
if (cl == 0) break; // No zero-len chunk, buffer more data
if (cl < 0) {
mg_error(c, "Invalid chunk");
break;
}
// Zero chunk found. Second pass: strip + relocate
o = 0, hm.body.len = 0, hm.message.len = (size_t) n;
while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0) {
memmove(s + hm.body.len, s + o + pl, (size_t) dl);
o += cl, hm.body.len += (size_t) dl, hm.message.len += (size_t) dl;
if (dl == 0) break;
}
ofs += (size_t) (n + o);
} else { // Normal, non-chunked data
size_t len = c->recv.len - ofs - (size_t) n;
if (hm.body.len > len) break; // Buffer more data
ofs += (size_t) n + hm.body.len;
}
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;
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);
}
if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data
}
(void) evd, (void) fnd;
}
-1
View File
@@ -16,7 +16,6 @@ struct mg_http_message {
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
struct mg_str body; // Body
struct mg_str head; // Request + headers
struct mg_str chunk; // Chunk for chunked encoding, or partial body
struct mg_str message; // Request + headers + body
};