mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-22 11:43:21 +08:00
Fix processing of keep-alive requests when chunks are consumed
PUBLISHED_FROM=136ce2ed4f143d7ee246e5fd1078c431e205bb3a
This commit is contained in:
parent
5867c67da2
commit
95fcb261eb
@ -29,6 +29,12 @@ signature: |
|
|||||||
/* Headers */
|
/* Headers */
|
||||||
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
||||||
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Value of the Content-Length header if present,
|
||||||
|
* otherwise MG_HTTP_CONTENT_LENGTH_UNKNOWN.
|
||||||
|
*/
|
||||||
|
size_t content_length;
|
||||||
};
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
|
109
mongoose.c
109
mongoose.c
@ -2913,13 +2913,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
|
|||||||
} else {
|
} else {
|
||||||
mbuf_append(&nc->recv_mbuf, buf, n);
|
mbuf_append(&nc->recv_mbuf, buf, n);
|
||||||
}
|
}
|
||||||
mbuf_trim(&lc->recv_mbuf);
|
|
||||||
lc->last_io_time = nc->last_io_time = (time_t) mg_time();
|
lc->last_io_time = nc->last_io_time = (time_t) mg_time();
|
||||||
#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
|
#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
|
||||||
if (nc->mgr && nc->mgr->hexdump_file != NULL) {
|
if (nc->mgr && nc->mgr->hexdump_file != NULL) {
|
||||||
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
mbuf_trim(&lc->recv_mbuf);
|
||||||
if (n != 0) {
|
if (n != 0) {
|
||||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||||
}
|
}
|
||||||
@ -5820,7 +5820,10 @@ struct mg_http_proto_data {
|
|||||||
struct mg_http_endpoint *endpoints;
|
struct mg_http_endpoint *endpoints;
|
||||||
mg_event_handler_t endpoint_handler;
|
mg_event_handler_t endpoint_handler;
|
||||||
struct mg_reverse_proxy_data reverse_proxy_data;
|
struct mg_reverse_proxy_data reverse_proxy_data;
|
||||||
size_t rcvd; /* How many bytes we have received. */
|
size_t rcvd; /* How many bytes we have received. */
|
||||||
|
size_t body_rcvd; /* How many bytes of body we have received. */
|
||||||
|
size_t body_processed; /* How many bytes of body we have processed. */
|
||||||
|
int finished;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mg_http_proto_data_destructor(void *proto_data);
|
static void mg_http_proto_data_destructor(void *proto_data);
|
||||||
@ -6051,6 +6054,7 @@ static int mg_http_get_request_len(const char *s, int buf_len) {
|
|||||||
static const char *mg_http_parse_headers(const char *s, const char *end,
|
static const char *mg_http_parse_headers(const char *s, const char *end,
|
||||||
int len, struct http_message *req) {
|
int len, struct http_message *req) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
req->content_length = MG_HTTP_CONTENT_LENGTH_UNKNOWN;
|
||||||
while (i < (int) ARRAY_SIZE(req->header_names) - 1) {
|
while (i < (int) ARRAY_SIZE(req->header_names) - 1) {
|
||||||
struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
|
struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
|
||||||
|
|
||||||
@ -6076,9 +6080,10 @@ static const char *mg_http_parse_headers(const char *s, const char *end,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mg_ncasecmp(k->p, "Content-Length", 14)) {
|
if (mg_ncasecmp(k->p, "Content-Length", 14) == 0) {
|
||||||
req->body.len = (size_t) to64(v->p);
|
req->body.len = (size_t) to64(v->p);
|
||||||
req->message.len = len + req->body.len;
|
req->message.len = len + req->body.len;
|
||||||
|
req->content_length = req->body.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
@ -6194,6 +6199,7 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
pd->file.keepalive));
|
pd->file.keepalive));
|
||||||
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
|
pd->finished = 1;
|
||||||
}
|
}
|
||||||
} else if (pd->file.type == DATA_PUT) {
|
} else if (pd->file.type == DATA_PUT) {
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
@ -6206,6 +6212,7 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
if (n == 0 || pd->file.sent >= pd->file.cl) {
|
if (n == 0 || pd->file.sent >= pd->file.cl) {
|
||||||
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
|
pd->finished = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
@ -6361,13 +6368,26 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
|
|||||||
struct http_message *hm);
|
struct http_message *hm);
|
||||||
|
|
||||||
static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
|
static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
|
||||||
int req_len) {
|
struct mg_http_proto_data *pd, int req_len) {
|
||||||
/* Incomplete message received. Send MG_EV_HTTP_CHUNK event */
|
/* Incomplete message received. Send MG_EV_HTTP_CHUNK event */
|
||||||
hm->body.len = c->recv_mbuf.len - req_len;
|
hm->body.len = c->recv_mbuf.len - req_len;
|
||||||
|
if (hm->content_length != MG_HTTP_CONTENT_LENGTH_UNKNOWN) {
|
||||||
|
size_t body_remain = hm->content_length - pd->body_processed;
|
||||||
|
if (hm->body.len > body_remain) {
|
||||||
|
hm->body.len = body_remain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pd != NULL) {
|
||||||
|
pd->body_rcvd = pd->body_processed + hm->body.len;
|
||||||
|
}
|
||||||
c->flags &= ~MG_F_DELETE_CHUNK;
|
c->flags &= ~MG_F_DELETE_CHUNK;
|
||||||
mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm);
|
mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm);
|
||||||
/* Delete processed data if user set MG_F_DELETE_CHUNK flag */
|
/* Delete processed data if user set MG_F_DELETE_CHUNK flag */
|
||||||
if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len;
|
if (c->flags & MG_F_DELETE_CHUNK) {
|
||||||
|
pd->body_processed += hm->body.len;
|
||||||
|
c->recv_mbuf.len = req_len;
|
||||||
|
hm->body.len = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -6438,7 +6458,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||||
hm->message.len = io->len;
|
hm->message.len = io->len;
|
||||||
hm->body.len = io->buf + io->len - hm->body.p;
|
hm->body.len = io->buf + io->len - hm->body.p;
|
||||||
deliver_chunk(nc, hm, req_len);
|
deliver_chunk(nc, hm, pd, req_len);
|
||||||
mg_http_call_endpoint_handler(nc, ev2, hm);
|
mg_http_call_endpoint_handler(nc, ev2, hm);
|
||||||
}
|
}
|
||||||
if (pd != NULL && pd->endpoint_handler != NULL &&
|
if (pd != NULL && pd->endpoint_handler != NULL &&
|
||||||
@ -6450,6 +6470,8 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
if (pd != NULL && pd->file.fp != NULL) {
|
if (pd != NULL && pd->file.fp != NULL) {
|
||||||
mg_http_transfer_file_data(nc);
|
mg_http_transfer_file_data(nc);
|
||||||
|
if (pd->finished) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -6474,8 +6496,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
|
|
||||||
again:
|
again:
|
||||||
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
||||||
|
if (req_len > 0 && (pd == NULL || pd->finished)) {
|
||||||
if (req_len > 0) {
|
|
||||||
/* New request - new proto data */
|
/* New request - new proto data */
|
||||||
pd = mg_http_create_proto_data(nc);
|
pd = mg_http_create_proto_data(nc);
|
||||||
pd->rcvd = io->len;
|
pd->rcvd = io->len;
|
||||||
@ -6557,42 +6578,50 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
|
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
|
||||||
else if (hm->message.len > pd->rcvd) {
|
else {
|
||||||
/* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */
|
deliver_chunk(nc, hm, pd, req_len);
|
||||||
deliver_chunk(nc, hm, req_len);
|
if (hm->message.len > pd->rcvd &&
|
||||||
if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
|
(hm->content_length == MG_HTTP_CONTENT_LENGTH_UNKNOWN ||
|
||||||
LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
|
pd->body_rcvd < hm->content_length)) {
|
||||||
"%lu bytes, and not drained, closing",
|
/* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */
|
||||||
nc, (unsigned long) nc->recv_mbuf.len,
|
if (nc->recv_mbuf_limit > 0 &&
|
||||||
(unsigned long) nc->recv_mbuf_limit));
|
nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
|
||||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
|
||||||
}
|
"%lu bytes, and not drained, closing",
|
||||||
} else {
|
nc, (unsigned long) nc->recv_mbuf.len,
|
||||||
/* We did receive all HTTP body. */
|
(unsigned long) nc->recv_mbuf_limit));
|
||||||
int request_done = 1;
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
}
|
||||||
char addr[32];
|
} else {
|
||||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
/* We did receive all HTTP body. */
|
||||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
int request_done = 1;
|
||||||
DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p,
|
int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||||
(int) hm->uri.len, hm->uri.p));
|
char addr[32];
|
||||||
deliver_chunk(nc, hm, req_len);
|
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||||
/* Whole HTTP message is fully buffered, call event handler */
|
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||||
mg_http_call_endpoint_handler(nc, trigger_ev, hm);
|
DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p,
|
||||||
mbuf_remove(io, hm->message.len);
|
(int) hm->uri.len, hm->uri.p));
|
||||||
pd->rcvd -= hm->message.len;
|
/* Whole HTTP message is fully buffered, call event handler */
|
||||||
|
mg_http_call_endpoint_handler(nc, trigger_ev, hm);
|
||||||
|
mbuf_remove(io, req_len + hm->body.len);
|
||||||
|
pd->rcvd -= hm->message.len;
|
||||||
|
pd->body_rcvd = 0;
|
||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
/* We don't have a generic mechanism of communicating that we are done
|
/* We don't have a generic mechanism of communicating that we are done
|
||||||
* responding to a request (should probably add one). But if we are
|
* responding to a request (should probably add one). But if we are
|
||||||
* serving
|
* serving
|
||||||
* a file, we are definitely not done. */
|
* a file, we are definitely not done. */
|
||||||
if (pd->file.fp != NULL) request_done = 0;
|
if (pd->file.fp != NULL) request_done = 0;
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
/* If this is a CGI request, we are not done either. */
|
/* If this is a CGI request, we are not done either. */
|
||||||
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
||||||
#endif
|
#endif
|
||||||
if (request_done && io->len > 0) goto again;
|
pd->finished = request_done;
|
||||||
|
DBG(("%p finished %d ml %d bl %d", nc, pd->finished,
|
||||||
|
(int) hm->message.len, (int) hm->body.len));
|
||||||
|
if (request_done && io->len > 0) goto again;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
mongoose.h
10
mongoose.h
@ -23,7 +23,7 @@
|
|||||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||||
|
|
||||||
#define MG_VERSION "6.17"
|
#define MG_VERSION "6.18"
|
||||||
|
|
||||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||||
#ifdef MG_LOCALS
|
#ifdef MG_LOCALS
|
||||||
@ -4420,8 +4420,16 @@ struct http_message {
|
|||||||
/* Headers */
|
/* Headers */
|
||||||
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
||||||
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Value of the Content-Length header if present,
|
||||||
|
* otherwise MG_HTTP_CONTENT_LENGTH_UNKNOWN.
|
||||||
|
*/
|
||||||
|
size_t content_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MG_HTTP_CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||||
|
|
||||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||||
/* WebSocket message */
|
/* WebSocket message */
|
||||||
struct websocket_message {
|
struct websocket_message {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
#ifndef CS_MONGOOSE_SRC_COMMON_H_
|
||||||
#define CS_MONGOOSE_SRC_COMMON_H_
|
#define CS_MONGOOSE_SRC_COMMON_H_
|
||||||
|
|
||||||
#define MG_VERSION "6.17"
|
#define MG_VERSION "6.18"
|
||||||
|
|
||||||
/* Local tweaks, applied before any of Mongoose's own headers. */
|
/* Local tweaks, applied before any of Mongoose's own headers. */
|
||||||
#ifdef MG_LOCALS
|
#ifdef MG_LOCALS
|
||||||
|
107
src/mg_http.c
107
src/mg_http.c
@ -174,7 +174,10 @@ struct mg_http_proto_data {
|
|||||||
struct mg_http_endpoint *endpoints;
|
struct mg_http_endpoint *endpoints;
|
||||||
mg_event_handler_t endpoint_handler;
|
mg_event_handler_t endpoint_handler;
|
||||||
struct mg_reverse_proxy_data reverse_proxy_data;
|
struct mg_reverse_proxy_data reverse_proxy_data;
|
||||||
size_t rcvd; /* How many bytes we have received. */
|
size_t rcvd; /* How many bytes we have received. */
|
||||||
|
size_t body_rcvd; /* How many bytes of body we have received. */
|
||||||
|
size_t body_processed; /* How many bytes of body we have processed. */
|
||||||
|
int finished;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void mg_http_proto_data_destructor(void *proto_data);
|
static void mg_http_proto_data_destructor(void *proto_data);
|
||||||
@ -405,6 +408,7 @@ static int mg_http_get_request_len(const char *s, int buf_len) {
|
|||||||
static const char *mg_http_parse_headers(const char *s, const char *end,
|
static const char *mg_http_parse_headers(const char *s, const char *end,
|
||||||
int len, struct http_message *req) {
|
int len, struct http_message *req) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
req->content_length = MG_HTTP_CONTENT_LENGTH_UNKNOWN;
|
||||||
while (i < (int) ARRAY_SIZE(req->header_names) - 1) {
|
while (i < (int) ARRAY_SIZE(req->header_names) - 1) {
|
||||||
struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
|
struct mg_str *k = &req->header_names[i], *v = &req->header_values[i];
|
||||||
|
|
||||||
@ -430,9 +434,10 @@ static const char *mg_http_parse_headers(const char *s, const char *end,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mg_ncasecmp(k->p, "Content-Length", 14)) {
|
if (mg_ncasecmp(k->p, "Content-Length", 14) == 0) {
|
||||||
req->body.len = (size_t) to64(v->p);
|
req->body.len = (size_t) to64(v->p);
|
||||||
req->message.len = len + req->body.len;
|
req->message.len = len + req->body.len;
|
||||||
|
req->content_length = req->body.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
@ -548,6 +553,7 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
pd->file.keepalive));
|
pd->file.keepalive));
|
||||||
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
|
pd->finished = 1;
|
||||||
}
|
}
|
||||||
} else if (pd->file.type == DATA_PUT) {
|
} else if (pd->file.type == DATA_PUT) {
|
||||||
struct mbuf *io = &nc->recv_mbuf;
|
struct mbuf *io = &nc->recv_mbuf;
|
||||||
@ -560,6 +566,7 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) {
|
|||||||
if (n == 0 || pd->file.sent >= pd->file.cl) {
|
if (n == 0 || pd->file.sent >= pd->file.cl) {
|
||||||
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
mg_http_free_proto_data_file(&pd->file);
|
mg_http_free_proto_data_file(&pd->file);
|
||||||
|
pd->finished = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
@ -715,13 +722,26 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
|
|||||||
struct http_message *hm);
|
struct http_message *hm);
|
||||||
|
|
||||||
static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
|
static void deliver_chunk(struct mg_connection *c, struct http_message *hm,
|
||||||
int req_len) {
|
struct mg_http_proto_data *pd, int req_len) {
|
||||||
/* Incomplete message received. Send MG_EV_HTTP_CHUNK event */
|
/* Incomplete message received. Send MG_EV_HTTP_CHUNK event */
|
||||||
hm->body.len = c->recv_mbuf.len - req_len;
|
hm->body.len = c->recv_mbuf.len - req_len;
|
||||||
|
if (hm->content_length != MG_HTTP_CONTENT_LENGTH_UNKNOWN) {
|
||||||
|
size_t body_remain = hm->content_length - pd->body_processed;
|
||||||
|
if (hm->body.len > body_remain) {
|
||||||
|
hm->body.len = body_remain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pd != NULL) {
|
||||||
|
pd->body_rcvd = pd->body_processed + hm->body.len;
|
||||||
|
}
|
||||||
c->flags &= ~MG_F_DELETE_CHUNK;
|
c->flags &= ~MG_F_DELETE_CHUNK;
|
||||||
mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm);
|
mg_call(c, c->handler, c->user_data, MG_EV_HTTP_CHUNK, hm);
|
||||||
/* Delete processed data if user set MG_F_DELETE_CHUNK flag */
|
/* Delete processed data if user set MG_F_DELETE_CHUNK flag */
|
||||||
if (c->flags & MG_F_DELETE_CHUNK) c->recv_mbuf.len = req_len;
|
if (c->flags & MG_F_DELETE_CHUNK) {
|
||||||
|
pd->body_processed += hm->body.len;
|
||||||
|
c->recv_mbuf.len = req_len;
|
||||||
|
hm->body.len = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -792,7 +812,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
int ev2 = is_req ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||||
hm->message.len = io->len;
|
hm->message.len = io->len;
|
||||||
hm->body.len = io->buf + io->len - hm->body.p;
|
hm->body.len = io->buf + io->len - hm->body.p;
|
||||||
deliver_chunk(nc, hm, req_len);
|
deliver_chunk(nc, hm, pd, req_len);
|
||||||
mg_http_call_endpoint_handler(nc, ev2, hm);
|
mg_http_call_endpoint_handler(nc, ev2, hm);
|
||||||
}
|
}
|
||||||
if (pd != NULL && pd->endpoint_handler != NULL &&
|
if (pd != NULL && pd->endpoint_handler != NULL &&
|
||||||
@ -804,6 +824,8 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
if (pd != NULL && pd->file.fp != NULL) {
|
if (pd != NULL && pd->file.fp != NULL) {
|
||||||
mg_http_transfer_file_data(nc);
|
mg_http_transfer_file_data(nc);
|
||||||
|
if (pd->finished) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -828,8 +850,7 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
|
|
||||||
again:
|
again:
|
||||||
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
req_len = mg_parse_http(io->buf, io->len, hm, is_req);
|
||||||
|
if (req_len > 0 && (pd == NULL || pd->finished)) {
|
||||||
if (req_len > 0) {
|
|
||||||
/* New request - new proto data */
|
/* New request - new proto data */
|
||||||
pd = mg_http_create_proto_data(nc);
|
pd = mg_http_create_proto_data(nc);
|
||||||
pd->rcvd = io->len;
|
pd->rcvd = io->len;
|
||||||
@ -911,42 +932,50 @@ void mg_http_handler(struct mg_connection *nc, int ev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
|
#endif /* MG_ENABLE_HTTP_WEBSOCKET */
|
||||||
else if (hm->message.len > pd->rcvd) {
|
else {
|
||||||
/* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */
|
deliver_chunk(nc, hm, pd, req_len);
|
||||||
deliver_chunk(nc, hm, req_len);
|
if (hm->message.len > pd->rcvd &&
|
||||||
if (nc->recv_mbuf_limit > 0 && nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
|
(hm->content_length == MG_HTTP_CONTENT_LENGTH_UNKNOWN ||
|
||||||
LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
|
pd->body_rcvd < hm->content_length)) {
|
||||||
"%lu bytes, and not drained, closing",
|
/* Not yet received all HTTP body, deliver MG_EV_HTTP_CHUNK */
|
||||||
nc, (unsigned long) nc->recv_mbuf.len,
|
if (nc->recv_mbuf_limit > 0 &&
|
||||||
(unsigned long) nc->recv_mbuf_limit));
|
nc->recv_mbuf.len >= nc->recv_mbuf_limit) {
|
||||||
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
LOG(LL_ERROR, ("%p recv buffer (%lu bytes) exceeds the limit "
|
||||||
}
|
"%lu bytes, and not drained, closing",
|
||||||
} else {
|
nc, (unsigned long) nc->recv_mbuf.len,
|
||||||
/* We did receive all HTTP body. */
|
(unsigned long) nc->recv_mbuf_limit));
|
||||||
int request_done = 1;
|
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
|
||||||
int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
}
|
||||||
char addr[32];
|
} else {
|
||||||
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
/* We did receive all HTTP body. */
|
||||||
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
int request_done = 1;
|
||||||
DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p,
|
int trigger_ev = nc->listener ? MG_EV_HTTP_REQUEST : MG_EV_HTTP_REPLY;
|
||||||
(int) hm->uri.len, hm->uri.p));
|
char addr[32];
|
||||||
deliver_chunk(nc, hm, req_len);
|
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
|
||||||
/* Whole HTTP message is fully buffered, call event handler */
|
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
||||||
mg_http_call_endpoint_handler(nc, trigger_ev, hm);
|
DBG(("%p %s %.*s %.*s", nc, addr, (int) hm->method.len, hm->method.p,
|
||||||
mbuf_remove(io, hm->message.len);
|
(int) hm->uri.len, hm->uri.p));
|
||||||
pd->rcvd -= hm->message.len;
|
/* Whole HTTP message is fully buffered, call event handler */
|
||||||
|
mg_http_call_endpoint_handler(nc, trigger_ev, hm);
|
||||||
|
mbuf_remove(io, req_len + hm->body.len);
|
||||||
|
pd->rcvd -= hm->message.len;
|
||||||
|
pd->body_rcvd = 0;
|
||||||
#if MG_ENABLE_FILESYSTEM
|
#if MG_ENABLE_FILESYSTEM
|
||||||
/* We don't have a generic mechanism of communicating that we are done
|
/* We don't have a generic mechanism of communicating that we are done
|
||||||
* responding to a request (should probably add one). But if we are
|
* responding to a request (should probably add one). But if we are
|
||||||
* serving
|
* serving
|
||||||
* a file, we are definitely not done. */
|
* a file, we are definitely not done. */
|
||||||
if (pd->file.fp != NULL) request_done = 0;
|
if (pd->file.fp != NULL) request_done = 0;
|
||||||
#endif
|
#endif
|
||||||
#if MG_ENABLE_HTTP_CGI
|
#if MG_ENABLE_HTTP_CGI
|
||||||
/* If this is a CGI request, we are not done either. */
|
/* If this is a CGI request, we are not done either. */
|
||||||
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
if (pd->cgi.cgi_nc != NULL) request_done = 0;
|
||||||
#endif
|
#endif
|
||||||
if (request_done && io->len > 0) goto again;
|
pd->finished = request_done;
|
||||||
|
DBG(("%p finished %d ml %d bl %d", nc, pd->finished,
|
||||||
|
(int) hm->message.len, (int) hm->body.len));
|
||||||
|
if (request_done && io->len > 0) goto again;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,16 @@ struct http_message {
|
|||||||
/* Headers */
|
/* Headers */
|
||||||
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_names[MG_MAX_HTTP_HEADERS];
|
||||||
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
struct mg_str header_values[MG_MAX_HTTP_HEADERS];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Value of the Content-Length header if present,
|
||||||
|
* otherwise MG_HTTP_CONTENT_LENGTH_UNKNOWN.
|
||||||
|
*/
|
||||||
|
size_t content_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MG_HTTP_CONTENT_LENGTH_UNKNOWN ((size_t) -1)
|
||||||
|
|
||||||
#if MG_ENABLE_HTTP_WEBSOCKET
|
#if MG_ENABLE_HTTP_WEBSOCKET
|
||||||
/* WebSocket message */
|
/* WebSocket message */
|
||||||
struct websocket_message {
|
struct websocket_message {
|
||||||
|
@ -707,13 +707,13 @@ static int mg_recv_udp(struct mg_connection *nc, char *buf, size_t len) {
|
|||||||
} else {
|
} else {
|
||||||
mbuf_append(&nc->recv_mbuf, buf, n);
|
mbuf_append(&nc->recv_mbuf, buf, n);
|
||||||
}
|
}
|
||||||
mbuf_trim(&lc->recv_mbuf);
|
|
||||||
lc->last_io_time = nc->last_io_time = (time_t) mg_time();
|
lc->last_io_time = nc->last_io_time = (time_t) mg_time();
|
||||||
#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
|
#if !defined(NO_LIBC) && MG_ENABLE_HEXDUMP
|
||||||
if (nc->mgr && nc->mgr->hexdump_file != NULL) {
|
if (nc->mgr && nc->mgr->hexdump_file != NULL) {
|
||||||
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
mg_hexdump_connection(nc, nc->mgr->hexdump_file, buf, n, MG_EV_RECV);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
mbuf_trim(&lc->recv_mbuf);
|
||||||
if (n != 0) {
|
if (n != 0) {
|
||||||
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
mg_call(nc, NULL, nc->user_data, MG_EV_RECV, &n);
|
||||||
}
|
}
|
||||||
|
143
test/unit_test.c
143
test/unit_test.c
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "unit_test.h"
|
#include "unit_test.h"
|
||||||
|
|
||||||
#include "common/cs_md5.h"
|
#include "common/cs_md5.h"
|
||||||
#include "common/test_main.h"
|
#include "common/test_main.h"
|
||||||
#include "common/test_util.h"
|
#include "common/test_util.h"
|
||||||
@ -197,20 +198,17 @@ static const char *test_mgr_with_ssl(int use_ssl) {
|
|||||||
struct mg_connection *nc;
|
struct mg_connection *nc;
|
||||||
struct mg_bind_opts bopts;
|
struct mg_bind_opts bopts;
|
||||||
int port, port2;
|
int port, port2;
|
||||||
#if !MG_ENABLE_SSL
|
|
||||||
(void) use_ssl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
/* mgr.hexdump_file = "-"; */
|
/* mgr.hexdump_file = "-"; */
|
||||||
|
|
||||||
memset(&bopts, 0, sizeof(bopts));
|
memset(&bopts, 0, sizeof(bopts));
|
||||||
#if MG_ENABLE_SSL
|
|
||||||
if (use_ssl) {
|
if (use_ssl) {
|
||||||
|
#if MG_ENABLE_SSL
|
||||||
bopts.ssl_cert = S_PEM;
|
bopts.ssl_cert = S_PEM;
|
||||||
bopts.ssl_ca_cert = CA_PEM;
|
bopts.ssl_ca_cert = CA_PEM;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
ASSERT((nc = mg_bind_opt(&mgr, addr, eh1, bopts)) != NULL);
|
ASSERT((nc = mg_bind_opt(&mgr, addr, eh1, bopts)) != NULL);
|
||||||
port2 = htons(nc->sa.sin.sin_port);
|
port2 = htons(nc->sa.sin.sin_port);
|
||||||
ASSERT(port2 > 0);
|
ASSERT(port2 > 0);
|
||||||
@ -781,9 +779,16 @@ static const char *test_parse_address(void) {
|
|||||||
};
|
};
|
||||||
static const char *need_lookup[] = {"udp://a.com:53", "locl_host:12", NULL};
|
static const char *need_lookup[] = {"udp://a.com:53", "locl_host:12", NULL};
|
||||||
static const char *invalid[] = {
|
static const char *invalid[] = {
|
||||||
"99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", "blah://12", ":123x",
|
"99999",
|
||||||
|
"1k",
|
||||||
|
"1.2.3",
|
||||||
|
"1.2.3.4:",
|
||||||
|
"1.2.3.4:2p",
|
||||||
|
"blah://12",
|
||||||
|
":123x",
|
||||||
"veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery.long:12345",
|
"veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery.long:12345",
|
||||||
"udp://missingport", NULL};
|
"udp://missingport",
|
||||||
|
NULL};
|
||||||
char host[50];
|
char host[50];
|
||||||
union socket_address sa;
|
union socket_address sa;
|
||||||
int i, proto;
|
int i, proto;
|
||||||
@ -1492,7 +1497,8 @@ static const char *test_parse_http_message(void) {
|
|||||||
static const char *a = "GET / HTTP/1.0\n\n";
|
static const char *a = "GET / HTTP/1.0\n\n";
|
||||||
static const char *b = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n";
|
static const char *b = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n";
|
||||||
static const char *c = "get b c\nz: k \nb: t\nvvv\n\n xx";
|
static const char *c = "get b c\nz: k \nb: t\nvvv\n\n xx";
|
||||||
static const char *d = "a b c\nContent-Length: 21 \nb: t\nvvv\n\n";
|
static const char *d =
|
||||||
|
"a b c\r\nContent-Length: 21 \r\nb: t\r\nvvv\r\n\r\nabc";
|
||||||
static const char *e = "GET /foo?a=b&c=d HTTP/1.0\n\n";
|
static const char *e = "GET /foo?a=b&c=d HTTP/1.0\n\n";
|
||||||
static const char *f = "POST /x HTTP/1.0\n\n";
|
static const char *f = "POST /x HTTP/1.0\n\n";
|
||||||
static const char *g = "WOHOO /x HTTP/1.0\n\n";
|
static const char *g = "WOHOO /x HTTP/1.0\n\n";
|
||||||
@ -1537,14 +1543,19 @@ static const char *test_parse_http_message(void) {
|
|||||||
ASSERT_EQ(memcmp(req.header_values[1].p, "t", 1), 0);
|
ASSERT_EQ(memcmp(req.header_values[1].p, "t", 1), 0);
|
||||||
ASSERT_EQ(req.header_names[1].len, 1);
|
ASSERT_EQ(req.header_names[1].len, 1);
|
||||||
ASSERT_EQ(req.body.len, 0);
|
ASSERT_EQ(req.body.len, 0);
|
||||||
|
ASSERT_EQ64(req.content_length, MG_HTTP_CONTENT_LENGTH_UNKNOWN);
|
||||||
|
|
||||||
ASSERT_EQ(mg_parse_http(d, strlen(d), &req, 1), (int) strlen(d));
|
ASSERT_EQ(mg_parse_http(d, strlen(d), &req, 1), (int) strlen(d) - 3);
|
||||||
ASSERT_EQ(req.body.len, 21);
|
ASSERT_EQ(req.body.len, 21);
|
||||||
ASSERT_EQ(req.message.len, 21 + strlen(d));
|
ASSERT_EQ(req.content_length, 21);
|
||||||
|
ASSERT_EQ(req.message.len, 21 + strlen(d) - 3);
|
||||||
ASSERT(mg_get_http_header(&req, "foo") == NULL);
|
ASSERT(mg_get_http_header(&req, "foo") == NULL);
|
||||||
ASSERT((v = mg_get_http_header(&req, "contENT-Length")) != NULL);
|
ASSERT((v = mg_get_http_header(&req, "contENT-Length")) != NULL);
|
||||||
ASSERT_EQ(v->len, 2);
|
ASSERT_EQ(v->len, 2);
|
||||||
ASSERT_STREQ_NZ(v->p, "21");
|
ASSERT_STREQ_NZ(v->p, "21");
|
||||||
|
ASSERT((v = mg_get_http_header(&req, "B")) != NULL);
|
||||||
|
ASSERT_EQ(v->len, 1);
|
||||||
|
ASSERT_STREQ_NZ(v->p, "t");
|
||||||
|
|
||||||
ASSERT_EQ(mg_parse_http(e, strlen(e), &req, 1), (int) strlen(e));
|
ASSERT_EQ(mg_parse_http(e, strlen(e), &req, 1), (int) strlen(e));
|
||||||
ASSERT_EQ(mg_vcmp(&req.uri, "/foo"), 0);
|
ASSERT_EQ(mg_vcmp(&req.uri, "/foo"), 0);
|
||||||
@ -1830,7 +1841,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
"0123456789")) != NULL);
|
"0123456789")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 1 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[/ 10] 25");
|
ASSERT_STREQ(buf, "[/ 10] 25");
|
||||||
|
|
||||||
@ -1838,7 +1849,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
"0123456789")) != NULL);
|
"0123456789")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 2 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[/ 10] 25");
|
ASSERT_STREQ(buf, "[/ 10] 25");
|
||||||
|
|
||||||
@ -1846,7 +1857,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
"foo")) != NULL);
|
"foo")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 3 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[/foo?a=b 3] 31");
|
ASSERT_STREQ(buf, "[/foo?a=b 3] 31");
|
||||||
|
|
||||||
@ -1854,7 +1865,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
"0123456789")) != NULL);
|
"0123456789")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 4 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[/foo 10] 28");
|
ASSERT_STREQ(buf, "[/foo 10] 28");
|
||||||
|
|
||||||
@ -1862,7 +1873,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
"0123456789")) != NULL);
|
"0123456789")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 5 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[I am Hello1] 32");
|
ASSERT_STREQ(buf, "[I am Hello1] 32");
|
||||||
|
|
||||||
@ -1870,7 +1881,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
NULL, "0123456789")) != NULL);
|
NULL, "0123456789")) != NULL);
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
DBG(("== WAIT =="));
|
DBG(("== WAIT 6 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[I am Hello two] 35");
|
ASSERT_STREQ(buf, "[I am Hello two] 35");
|
||||||
|
|
||||||
@ -1879,6 +1890,7 @@ static const char *test_http_endpoints(void) {
|
|||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
nc->user_data = buf;
|
nc->user_data = buf;
|
||||||
|
|
||||||
|
DBG(("== WAIT 7 =="));
|
||||||
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
poll_until(&mgr, 1, c_str_ne, buf, (void *) "");
|
||||||
ASSERT_STREQ(buf, "[I am Hello again] 37");
|
ASSERT_STREQ(buf, "[I am Hello again] 37");
|
||||||
|
|
||||||
@ -1894,6 +1906,7 @@ static const char *serve_file_verify(struct http_message *hm) {
|
|||||||
char *data = read_file("unit_test.c", &size);
|
char *data = read_file("unit_test.c", &size);
|
||||||
if (data == NULL || size != hm->body.len ||
|
if (data == NULL || size != hm->body.len ||
|
||||||
memcmp(hm->body.p, data, size) != 0) {
|
memcmp(hm->body.p, data, size) != 0) {
|
||||||
|
DBG(("%d %d", (int) size, (int) hm->body.len));
|
||||||
return "wrong data";
|
return "wrong data";
|
||||||
}
|
}
|
||||||
free(data);
|
free(data);
|
||||||
@ -1946,35 +1959,59 @@ static const char *test_http_serve_file(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void srv1(struct mg_connection *c, int ev, void *ev_data) {
|
static void file_srv(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_REQUEST) {
|
if (ev == MG_EV_HTTP_REQUEST) {
|
||||||
mg_http_serve_file(c, (struct http_message *) ev_data, "unit_test.c",
|
mg_http_serve_file(c, (struct http_message *) ev_data, "unit_test.c",
|
||||||
mg_mk_str("text/plain"), mg_mk_str(""));
|
mg_mk_str("text/plain"), mg_mk_str(""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void srv2(struct mg_connection *c, int ev, void *ev_data) {
|
struct chunk_clt_ctx {
|
||||||
|
int seq;
|
||||||
|
int status;
|
||||||
|
size_t num_chunks;
|
||||||
|
size_t chunks_rcvd;
|
||||||
|
size_t content_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void chunk_clt(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
static cs_md5_ctx c1, c2;
|
static cs_md5_ctx c1, c2;
|
||||||
struct http_message *hm = (struct http_message *) ev_data;
|
struct http_message *hm = (struct http_message *) ev_data;
|
||||||
|
struct chunk_clt_ctx *ctx = (struct chunk_clt_ctx *) c->user_data;
|
||||||
|
|
||||||
switch (ev) {
|
switch (ev) {
|
||||||
case MG_EV_CONNECT:
|
case MG_EV_CONNECT:
|
||||||
cs_md5_init(&c1);
|
cs_md5_init(&c1);
|
||||||
cs_md5_init(&c2);
|
|
||||||
break;
|
break;
|
||||||
case MG_EV_HTTP_CHUNK:
|
case MG_EV_HTTP_CHUNK:
|
||||||
cs_md5_update(&c1, (const unsigned char *) hm->body.p, hm->body.len);
|
cs_md5_update(&c1, (const unsigned char *) hm->body.p, hm->body.len);
|
||||||
|
ctx->chunks_rcvd += hm->body.len;
|
||||||
|
DBG(("%p = seq %d, chunk %d bytes, %d", c, ctx->seq, (int) hm->body.len,
|
||||||
|
(int) ctx->chunks_rcvd));
|
||||||
|
ctx->num_chunks++;
|
||||||
|
ctx->content_length = hm->content_length;
|
||||||
c->flags |= MG_F_DELETE_CHUNK;
|
c->flags |= MG_F_DELETE_CHUNK;
|
||||||
break;
|
break;
|
||||||
case MG_EV_HTTP_REPLY: {
|
case MG_EV_HTTP_REPLY: {
|
||||||
unsigned char sig1[16], sig2[sizeof(sig1)];
|
unsigned char sig1[16], sig2[sizeof(sig1)];
|
||||||
size_t size;
|
size_t size;
|
||||||
char *data = read_file("unit_test.c", &size);
|
char *data = read_file("unit_test.c", &size);
|
||||||
|
cs_md5_final(sig1, &c1);
|
||||||
|
cs_md5_init(&c2);
|
||||||
if (data != NULL) cs_md5_update(&c2, (const unsigned char *) data, size);
|
if (data != NULL) cs_md5_update(&c2, (const unsigned char *) data, size);
|
||||||
free(data);
|
free(data);
|
||||||
cs_md5_final(sig1, &c1);
|
|
||||||
cs_md5_final(sig2, &c2);
|
cs_md5_final(sig2, &c2);
|
||||||
*(int *) c->user_data = (memcmp(sig1, sig2, sizeof(sig1)) == 0) ? 1 : 2;
|
DBG(("%p = seq %d, reply", c, ctx->seq));
|
||||||
|
if (size != ctx->content_length) {
|
||||||
|
DBG(("%p: want %d, got %d", c, (int) size, (int) ctx->content_length));
|
||||||
|
ctx->status = 3;
|
||||||
|
} else if (size != ctx->chunks_rcvd) {
|
||||||
|
DBG(("%p: want %d, got %d", c, (int) size, (int) ctx->chunks_rcvd));
|
||||||
|
ctx->status = 4;
|
||||||
|
} else {
|
||||||
|
ctx->status = (memcmp(sig1, sig2, sizeof(sig1)) == 0) ? 1 : 2;
|
||||||
|
}
|
||||||
|
cs_md5_init(&c1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1984,16 +2021,27 @@ static const char *test_http_serve_file_streaming(void) {
|
|||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
struct mg_connection *lc, *nc;
|
struct mg_connection *lc, *nc;
|
||||||
const char *local_addr = "127.0.0.1:7732";
|
const char *local_addr = "127.0.0.1:7732";
|
||||||
int status = 0;
|
struct chunk_clt_ctx ctx = {0, 0, 0, 0, 0};
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
ASSERT((lc = mg_bind(&mgr, local_addr, srv1)) != NULL);
|
ASSERT((lc = mg_bind(&mgr, local_addr, file_srv)) != NULL);
|
||||||
mg_set_protocol_http_websocket(lc);
|
mg_set_protocol_http_websocket(lc);
|
||||||
ASSERT((nc = mg_connect(&mgr, local_addr, srv2)) != NULL);
|
ASSERT((nc = mg_connect(&mgr, local_addr, chunk_clt)) != NULL);
|
||||||
mg_set_protocol_http_websocket(nc);
|
mg_set_protocol_http_websocket(nc);
|
||||||
nc->user_data = &status;
|
nc->user_data = &ctx;
|
||||||
mg_printf(nc, "GET / HTTP/1.0\r\n\r\n");
|
mg_printf(nc,
|
||||||
poll_until(&mgr, 30, c_int_ne, &status, (void *) 0);
|
"GET / HTTP/1.0\r\n"
|
||||||
ASSERT_EQ(status, 1);
|
"Content-Length: 0\r\n"
|
||||||
|
"Connection: keep-alive\r\n"
|
||||||
|
"\r\n");
|
||||||
|
poll_until(&mgr, 5, c_int_ne, &ctx.status, (void *) 0);
|
||||||
|
ASSERT_EQ(ctx.status, 1);
|
||||||
|
ASSERT_EQ(nc->flags & (MG_F_CLOSE_IMMEDIATELY | MG_F_SEND_AND_CLOSE), 0);
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
ctx.seq = 1;
|
||||||
|
mg_printf(nc, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n");
|
||||||
|
poll_until(&mgr, 5, c_int_ne, &ctx.status, (void *) 0);
|
||||||
|
ASSERT_EQ(ctx.status, 1);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -2005,9 +2053,11 @@ static const char *test_http(void) {
|
|||||||
char buf[50] = "", status[100] = "", mime1[20] = "", mime2[100] = "";
|
char buf[50] = "", status[100] = "", mime1[20] = "", mime2[100] = "";
|
||||||
char opt_buf[1024] = "";
|
char opt_buf[1024] = "";
|
||||||
const char *opt_answer =
|
const char *opt_answer =
|
||||||
"HTTP/1.1 200 OK\r\nServer: Mongoose/" MG_VERSION
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, "
|
"Server: Mongoose/" MG_VERSION
|
||||||
"PUT, DELETE, PROPFIND, MOVE";
|
"\r\n"
|
||||||
|
"Allow: GET, POST, HEAD, CONNECT, OPTIONS, MKCOL, PUT, DELETE, PROPFIND, "
|
||||||
|
"MOVE";
|
||||||
char url[1000];
|
char url[1000];
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
@ -2085,6 +2135,7 @@ static void http_pipeline_handler(struct mg_connection *c, int ev,
|
|||||||
"Content-Type: text/plain\r\nContent-Length: 5\r\n");
|
"Content-Type: text/plain\r\nContent-Length: 5\r\n");
|
||||||
mg_printf(c, "Hello");
|
mg_printf(c, "Hello");
|
||||||
*status = *status + 1;
|
*status = *status + 1;
|
||||||
|
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
} else if (ev == MG_EV_HTTP_REPLY) {
|
} else if (ev == MG_EV_HTTP_REPLY) {
|
||||||
/* Client reply handler */
|
/* Client reply handler */
|
||||||
*status = *status + 10;
|
*status = *status + 10;
|
||||||
@ -2102,7 +2153,12 @@ static const char *test_http_pipeline(void) {
|
|||||||
mg_set_protocol_http_websocket(lc);
|
mg_set_protocol_http_websocket(lc);
|
||||||
ASSERT(nc1 = mg_connect(&mgr, local_addr, http_pipeline_handler));
|
ASSERT(nc1 = mg_connect(&mgr, local_addr, http_pipeline_handler));
|
||||||
mg_set_protocol_http_websocket(nc1);
|
mg_set_protocol_http_websocket(nc1);
|
||||||
mg_printf(nc1, "GET / HTTP/1.1\r\n\r\nGET / HTTP/1.1\r\n\r\n");
|
mg_printf(nc1,
|
||||||
|
"GET /foo HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 0\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"GET /bar HTTP/1.1\r\n"
|
||||||
|
"\r\n");
|
||||||
poll_until(&mgr, 1, c_int_eq, &status, (void *) 22);
|
poll_until(&mgr, 1, c_int_eq, &status, (void *) 22);
|
||||||
ASSERT_EQ(status, 22);
|
ASSERT_EQ(status, 22);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
@ -4225,6 +4281,7 @@ static const char *test_http_multipart2(void) {
|
|||||||
mbuf_init(&mpd.res, 0);
|
mbuf_init(&mpd.res, 0);
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
/* mgr.hexdump_file = "-"; */
|
||||||
nc_listen = mg_bind(&mgr, "8765", cb_mp_srv);
|
nc_listen = mg_bind(&mgr, "8765", cb_mp_srv);
|
||||||
nc_listen->user_data = &mpd;
|
nc_listen->user_data = &mpd;
|
||||||
|
|
||||||
@ -4276,10 +4333,12 @@ static const char *test_http_multipart2(void) {
|
|||||||
mbuf_init(&mpd.res, 0);
|
mbuf_init(&mpd.res, 0);
|
||||||
|
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"POST /test HTTP/1.1\r\nHost: 127.0.0.1:8766\r\nContent-Length: "
|
"POST /test HTTP/1.1\r\n"
|
||||||
"%" SIZE_T_FMT
|
"Host: 127.0.0.1:8766\r\n"
|
||||||
"\r\nConnection: keep-alive\r\nContent-Type: multipart/form-data; "
|
"Content-Length: %" SIZE_T_FMT
|
||||||
"boundary=Asrf456BGe4h\r\n%s",
|
"\r\n"
|
||||||
|
"Connection: keep-alive\r\n"
|
||||||
|
"Content-Type: multipart/form-data; boundary=Asrf456BGe4h\r\n%s",
|
||||||
strlen(multi_part_req), multi_part_req);
|
strlen(multi_part_req), multi_part_req);
|
||||||
|
|
||||||
poll_until(&mgr, 3, c_int_eq, &mpd.request_end, (void *) 1);
|
poll_until(&mgr, 3, c_int_eq, &mpd.request_end, (void *) 1);
|
||||||
@ -4594,7 +4653,13 @@ static const char *test_dns_encode_name(void) {
|
|||||||
struct mbuf mb;
|
struct mbuf mb;
|
||||||
mbuf_init(&mb, 0);
|
mbuf_init(&mb, 0);
|
||||||
ASSERT_EQ(mg_dns_encode_name(&mb, "www.cesanta.com.net.org", 15), 17);
|
ASSERT_EQ(mg_dns_encode_name(&mb, "www.cesanta.com.net.org", 15), 17);
|
||||||
ASSERT_STREQ_NZ(mb.buf, "\x03" "www" "\x07" "cesanta" "\x03" "com");
|
ASSERT_STREQ_NZ(mb.buf,
|
||||||
|
"\x03"
|
||||||
|
"www"
|
||||||
|
"\x07"
|
||||||
|
"cesanta"
|
||||||
|
"\x03"
|
||||||
|
"com");
|
||||||
mbuf_free(&mb);
|
mbuf_free(&mb);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -5670,9 +5735,7 @@ static const char *test_socks(void) {
|
|||||||
char status[100] = "";
|
char status[100] = "";
|
||||||
|
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
#if 0
|
/* mgr.hexdump_file = "-"; */
|
||||||
mgr.hexdump_file = "-";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
cs_log_set_level(LL_INFO);
|
cs_log_set_level(LL_INFO);
|
||||||
|
Loading…
Reference in New Issue
Block a user