From e5910da300613f0b37fb73f66e5f8c7b96cbd6b9 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Tue, 19 Mar 2019 12:59:13 +0000 Subject: [PATCH] Deliver EV_CLOSE to the last endpoint handler CL: mg: Deliver EV_CLOSE to the last endpoint handler PUBLISHED_FROM=02ee2c6627ed9ee1d35022244f71dfe1877ce2c0 --- mongoose.c | 68 ++++++++++++++++++++++++++--------------------- src/mg_http.c | 65 +++++++++++++++++++++++--------------------- src/mg_internal.h | 3 +++ test/unit_test.c | 21 +++++++++------ 4 files changed, 89 insertions(+), 68 deletions(-) diff --git a/mongoose.c b/mongoose.c index 3381d280..f56d6966 100644 --- a/mongoose.c +++ b/mongoose.c @@ -88,6 +88,9 @@ extern void *(*test_calloc)(size_t count, size_t size); #if MG_ENABLE_HTTP struct mg_serve_http_opts; +MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data( + struct mg_connection *c); + /* * Reassemble the content of the buffer (buf, blen) which should be * in the HTTP chunked encoding, by collapsing data chunks to the @@ -6008,20 +6011,29 @@ struct mg_http_proto_data { size_t rcvd; /* How many bytes we have received. */ }; -static void mg_http_conn_destructor(void *proto_data); +static void mg_http_proto_data_destructor(void *proto_data); + struct mg_connection *mg_connect_http_base( struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), struct mg_connect_opts opts, const char *scheme1, const char *scheme2, const char *scheme_ssl1, const char *scheme_ssl2, const char *url, struct mg_str *path, struct mg_str *user_info, struct mg_str *host); +MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data( + struct mg_connection *c) { + /* If we have proto data from previous connection, flush it. */ + if (c->proto_data != NULL) { + void *pd = c->proto_data; + c->proto_data = NULL; + mg_http_proto_data_destructor(pd); + } + c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); + c->proto_data_destructor = mg_http_proto_data_destructor; + return (struct mg_http_proto_data *) c->proto_data; +} + static struct mg_http_proto_data *mg_http_get_proto_data( struct mg_connection *c) { - if (c->proto_data == NULL) { - c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); - c->proto_data_destructor = mg_http_conn_destructor; - } - return (struct mg_http_proto_data *) c->proto_data; } @@ -6077,7 +6089,7 @@ static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { } } -static void mg_http_conn_destructor(void *proto_data) { +static void mg_http_proto_data_destructor(void *proto_data) { struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; #if MG_ENABLE_FILESYSTEM mg_http_free_proto_data_file(&pd->file); @@ -6350,7 +6362,8 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) { /* Rate-limited */ } if (pd->file.sent >= pd->file.cl) { - LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); + LOG(LL_DEBUG, ("%p done, %d bytes, ka %d", nc, (int) pd->file.sent, + pd->file.keepalive)); if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; mg_http_free_proto_data_file(&pd->file); } @@ -6486,12 +6499,12 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, int matched, matched_max = 0; struct mg_http_endpoint *ep; - if (nc == NULL) { - return NULL; - } + if (nc == NULL) return NULL; pd = mg_http_get_proto_data(nc); + if (pd == NULL) return NULL; + ep = pd->endpoints; while (ep != NULL) { if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { @@ -6563,13 +6576,13 @@ void mg_http_handler(struct mg_connection *nc, int ev, if (ev == MG_EV_CLOSE) { #if MG_ENABLE_HTTP_CGI /* Close associated CGI forwarder connection */ - if (pd->cgi.cgi_nc != NULL) { + if (pd != NULL && pd->cgi.cgi_nc != NULL) { pd->cgi.cgi_nc->user_data = NULL; pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; } #endif #if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL) { + if (pd != NULL && pd->mp_stream.boundary != NULL) { /* * Multipart message is in progress, but connection is closed. * Finish part and request with an error flag. @@ -6599,14 +6612,14 @@ void mg_http_handler(struct mg_connection *nc, int ev, deliver_chunk(nc, hm, req_len); mg_http_call_endpoint_handler(nc, ev2, hm); } - pd->rcvd = 0; - if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) { + if (pd != NULL && pd->endpoint_handler != NULL && + pd->endpoint_handler != nc->handler) { mg_call(nc, pd->endpoint_handler, nc->user_data, ev, NULL); } } #if MG_ENABLE_FILESYSTEM - if (pd->file.fp != NULL) { + if (pd != NULL && pd->file.fp != NULL) { mg_http_transfer_file_data(nc); } #endif @@ -6614,7 +6627,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, mg_call(nc, nc->handler, nc->user_data, ev, ev_data); #if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL && + if (pd != NULL && pd->mp_stream.boundary != NULL && (ev == MG_EV_RECV || ev == MG_EV_POLL)) { if (ev == MG_EV_RECV) { pd->rcvd += *(int *) ev_data; @@ -6629,11 +6642,16 @@ void mg_http_handler(struct mg_connection *nc, int ev, if (ev == MG_EV_RECV) { struct mg_str *s; - pd->rcvd += *(int *) ev_data; again: req_len = mg_parse_http(io->buf, io->len, hm, is_req); + if (req_len > 0) { + /* New request - new proto data */ + pd = mg_http_create_proto_data(nc); + pd->rcvd = io->len; + } + if (req_len > 0 && (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && mg_vcasecmp(s, "chunked") == 0) { @@ -6745,18 +6763,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, /* If this is a CGI request, we are not done either. */ if (pd->cgi.cgi_nc != NULL) request_done = 0; #endif - if (request_done) { - /* This request is done but we may receive another on this connection. - */ - mg_http_conn_destructor(pd); - nc->proto_data = NULL; - if (io->len > 0) { - /* We already have data for the next one, restart parsing. */ - pd = mg_http_get_proto_data(nc); - pd->rcvd = io->len; - goto again; - } - } + if (request_done && io->len > 0) goto again; } } } @@ -8896,6 +8903,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc, if (new_ep == NULL) return; pd = mg_http_get_proto_data(nc); + if (pd == NULL) pd = mg_http_create_proto_data(nc); new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); if (opts.auth_domain != NULL && opts.auth_file != NULL) { new_ep->auth_domain = strdup(opts.auth_domain); diff --git a/src/mg_http.c b/src/mg_http.c index 5cd6f623..ef546c37 100644 --- a/src/mg_http.c +++ b/src/mg_http.c @@ -177,20 +177,29 @@ struct mg_http_proto_data { size_t rcvd; /* How many bytes we have received. */ }; -static void mg_http_conn_destructor(void *proto_data); +static void mg_http_proto_data_destructor(void *proto_data); + struct mg_connection *mg_connect_http_base( struct mg_mgr *mgr, MG_CB(mg_event_handler_t ev_handler, void *user_data), struct mg_connect_opts opts, const char *scheme1, const char *scheme2, const char *scheme_ssl1, const char *scheme_ssl2, const char *url, struct mg_str *path, struct mg_str *user_info, struct mg_str *host); +MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data( + struct mg_connection *c) { + /* If we have proto data from previous connection, flush it. */ + if (c->proto_data != NULL) { + void *pd = c->proto_data; + c->proto_data = NULL; + mg_http_proto_data_destructor(pd); + } + c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); + c->proto_data_destructor = mg_http_proto_data_destructor; + return (struct mg_http_proto_data *) c->proto_data; +} + static struct mg_http_proto_data *mg_http_get_proto_data( struct mg_connection *c) { - if (c->proto_data == NULL) { - c->proto_data = MG_CALLOC(1, sizeof(struct mg_http_proto_data)); - c->proto_data_destructor = mg_http_conn_destructor; - } - return (struct mg_http_proto_data *) c->proto_data; } @@ -246,7 +255,7 @@ static void mg_http_free_reverse_proxy_data(struct mg_reverse_proxy_data *rpd) { } } -static void mg_http_conn_destructor(void *proto_data) { +static void mg_http_proto_data_destructor(void *proto_data) { struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; #if MG_ENABLE_FILESYSTEM mg_http_free_proto_data_file(&pd->file); @@ -519,7 +528,8 @@ static void mg_http_transfer_file_data(struct mg_connection *nc) { /* Rate-limited */ } if (pd->file.sent >= pd->file.cl) { - LOG(LL_DEBUG, ("%p done, %d bytes", nc, (int) pd->file.sent)); + LOG(LL_DEBUG, ("%p done, %d bytes, ka %d", nc, (int) pd->file.sent, + pd->file.keepalive)); if (!pd->file.keepalive) nc->flags |= MG_F_SEND_AND_CLOSE; mg_http_free_proto_data_file(&pd->file); } @@ -655,12 +665,12 @@ struct mg_http_endpoint *mg_http_get_endpoint_handler(struct mg_connection *nc, int matched, matched_max = 0; struct mg_http_endpoint *ep; - if (nc == NULL) { - return NULL; - } + if (nc == NULL) return NULL; pd = mg_http_get_proto_data(nc); + if (pd == NULL) return NULL; + ep = pd->endpoints; while (ep != NULL) { if ((matched = mg_match_prefix_n(ep->uri_pattern, *uri_path)) > 0) { @@ -732,13 +742,13 @@ void mg_http_handler(struct mg_connection *nc, int ev, if (ev == MG_EV_CLOSE) { #if MG_ENABLE_HTTP_CGI /* Close associated CGI forwarder connection */ - if (pd->cgi.cgi_nc != NULL) { + if (pd != NULL && pd->cgi.cgi_nc != NULL) { pd->cgi.cgi_nc->user_data = NULL; pd->cgi.cgi_nc->flags |= MG_F_CLOSE_IMMEDIATELY; } #endif #if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL) { + if (pd != NULL && pd->mp_stream.boundary != NULL) { /* * Multipart message is in progress, but connection is closed. * Finish part and request with an error flag. @@ -768,14 +778,14 @@ void mg_http_handler(struct mg_connection *nc, int ev, deliver_chunk(nc, hm, req_len); mg_http_call_endpoint_handler(nc, ev2, hm); } - pd->rcvd = 0; - if (pd->endpoint_handler != NULL && pd->endpoint_handler != nc->handler) { + if (pd != NULL && pd->endpoint_handler != NULL && + pd->endpoint_handler != nc->handler) { mg_call(nc, pd->endpoint_handler, nc->user_data, ev, NULL); } } #if MG_ENABLE_FILESYSTEM - if (pd->file.fp != NULL) { + if (pd != NULL && pd->file.fp != NULL) { mg_http_transfer_file_data(nc); } #endif @@ -783,7 +793,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, mg_call(nc, nc->handler, nc->user_data, ev, ev_data); #if MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->mp_stream.boundary != NULL && + if (pd != NULL && pd->mp_stream.boundary != NULL && (ev == MG_EV_RECV || ev == MG_EV_POLL)) { if (ev == MG_EV_RECV) { pd->rcvd += *(int *) ev_data; @@ -798,11 +808,16 @@ void mg_http_handler(struct mg_connection *nc, int ev, if (ev == MG_EV_RECV) { struct mg_str *s; - pd->rcvd += *(int *) ev_data; again: req_len = mg_parse_http(io->buf, io->len, hm, is_req); + if (req_len > 0) { + /* New request - new proto data */ + pd = mg_http_create_proto_data(nc); + pd->rcvd = io->len; + } + if (req_len > 0 && (s = mg_get_http_header(hm, "Transfer-Encoding")) != NULL && mg_vcasecmp(s, "chunked") == 0) { @@ -914,18 +929,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, /* If this is a CGI request, we are not done either. */ if (pd->cgi.cgi_nc != NULL) request_done = 0; #endif - if (request_done) { - /* This request is done but we may receive another on this connection. - */ - mg_http_conn_destructor(pd); - nc->proto_data = NULL; - if (io->len > 0) { - /* We already have data for the next one, restart parsing. */ - pd = mg_http_get_proto_data(nc); - pd->rcvd = io->len; - goto again; - } - } + if (request_done && io->len > 0) goto again; } } } @@ -3065,6 +3069,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc, if (new_ep == NULL) return; pd = mg_http_get_proto_data(nc); + if (pd == NULL) pd = mg_http_create_proto_data(nc); new_ep->uri_pattern = mg_strdup(mg_mk_str(uri_path)); if (opts.auth_domain != NULL && opts.auth_file != NULL) { new_ep->auth_domain = strdup(opts.auth_domain); diff --git a/src/mg_internal.h b/src/mg_internal.h index 0e62448e..250b6fe1 100644 --- a/src/mg_internal.h +++ b/src/mg_internal.h @@ -84,6 +84,9 @@ extern void *(*test_calloc)(size_t count, size_t size); #if MG_ENABLE_HTTP struct mg_serve_http_opts; +MG_INTERNAL struct mg_http_proto_data *mg_http_create_proto_data( + struct mg_connection *c); + /* * Reassemble the content of the buffer (buf, blen) which should be * in the HTTP chunked encoding, by collapsing data chunks to the diff --git a/test/unit_test.c b/test/unit_test.c index 23ced16c..5eb919ca 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -1700,7 +1700,7 @@ static void cb10(struct mg_connection *nc, int ev, void *ev_data) { } } -static void endpoint_handler(struct mg_connection *nc, int ev, void *ev_data) { +static void default_handler(struct mg_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; (void) ev_data; @@ -1714,7 +1714,8 @@ static void endpoint_handler(struct mg_connection *nc, int ev, void *ev_data) { } } else if (ev == MG_EV_CLOSE) { if (nc->listener != NULL) { - (*(int *) nc->listener->user_data) += 100; + (*(int *) nc->listener->user_data) += 1; + DBG(("%p == default close", nc)); } } } @@ -1727,7 +1728,8 @@ static void handle_hello1(struct mg_connection *nc, int ev, void *ev_data) { nc->flags |= MG_F_SEND_AND_CLOSE; break; case MG_EV_CLOSE: - (*(int *) nc->listener->user_data)++; + DBG(("%p == hello1 close", nc)); + (*(int *) nc->listener->user_data) += 10; break; } } @@ -1740,7 +1742,8 @@ static void handle_hello2(struct mg_connection *nc, int ev, void *ev_data) { nc->flags |= MG_F_SEND_AND_CLOSE; break; case MG_EV_CLOSE: - (*(int *) nc->listener->user_data)++; + DBG(("%p == hello2 close", nc)); + (*(int *) nc->listener->user_data) += 100; break; } } @@ -1753,7 +1756,8 @@ static void handle_hello5(struct mg_connection *nc, int ev, void *ev_data) { nc->flags |= MG_F_SEND_AND_CLOSE; break; case MG_EV_CLOSE: - (*(int *) nc->listener->user_data)++; + DBG(("%p == hello5 close", nc)); + (*(int *) nc->listener->user_data) += 1000; break; } } @@ -1813,7 +1817,7 @@ static const char *test_http_endpoints(void) { mg_mgr_init(&mgr, NULL); /* mgr.hexdump_file = "-"; */ - ASSERT((nc = mg_bind(&mgr, local_addr, endpoint_handler)) != NULL); + ASSERT((nc = mg_bind(&mgr, local_addr, default_handler)) != NULL); mg_register_http_endpoint(nc, "/hello1", handle_hello1 MG_UD_ARG(NULL)); mg_register_http_endpoint(nc, "/hello1/hello2", handle_hello2 MG_UD_ARG(NULL)); @@ -1878,7 +1882,7 @@ static const char *test_http_endpoints(void) { poll_until(&mgr, 1, c_str_ne, buf, (void *) ""); ASSERT_STREQ(buf, "[I am Hello again] 37"); - ASSERT_EQ(close_count, 700); + ASSERT_EQ(close_count, 1117); mg_mgr_free(&mgr); @@ -1987,7 +1991,7 @@ static const char *test_http_serve_file_streaming(void) { ASSERT((nc = mg_connect(&mgr, local_addr, srv2)) != NULL); mg_set_protocol_http_websocket(nc); nc->user_data = &status; - mg_printf(nc, "GET / HTTP/1.1\r\n\r\n"); + mg_printf(nc, "GET / HTTP/1.0\r\n\r\n"); poll_until(&mgr, 30, c_int_ne, &status, (void *) 0); ASSERT_EQ(status, 1); mg_mgr_free(&mgr); @@ -3993,6 +3997,7 @@ static const char *test_http_chunk2(void) { nc.mgr = &mgr; nc.sock = INVALID_SOCKET; nc.handler = eh_chunk2; + mg_http_create_proto_data(&nc); hm.message.len = hm.body.len = ~0; s_handle_chunk_event = 0;