diff --git a/mongoose.c b/mongoose.c index 3bf8a8d643f140e20a6bf4cba4c515bea3b64a99..f3f1bf2d5070a83f6249a27e5c3c218d56e21e02 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4204,6 +4204,19 @@ struct mg_http_proto_data_chuncked { int64_t body_len; /* How many bytes of chunked body was reassembled. */ }; +struct mg_http_endpoint { + struct mg_http_endpoint *next; + const char *name; + size_t name_len; + mg_event_handler_t handler; +}; + +struct mg_http_multipart_stream { + const char *boundary; + const char *var_name; + const char *file_name; +}; + struct mg_http_proto_data { #ifndef MG_DISABLE_FILESYSTEM struct mg_http_proto_data_file file; @@ -4212,10 +4225,10 @@ struct mg_http_proto_data { struct mg_http_proto_data_cgi cgi; #endif #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART - struct mbuf strm_state; /* Used by multi-part streaming */ + struct mg_http_multipart_stream mp_stream; #endif struct mg_http_proto_data_chuncked chunk; - struct mbuf endpoints; /* Used by mg_register_http_endpoint */ + struct mg_http_endpoint *endpoints; mg_event_handler_t endpoint_handler; }; @@ -4231,6 +4244,18 @@ static struct mg_http_proto_data *mg_http_get_proto_data( return (struct mg_http_proto_data *) c->proto_data; } +#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART +static void mg_http_free_proto_data_mp_stream( + struct mg_http_multipart_stream *mp) { + free((void *) mp->boundary); + mp->boundary = NULL; + free((void *) mp->var_name); + mp->var_name = NULL; + free((void *) mp->file_name); + mp->file_name = NULL; +} +#endif + #ifndef MG_DISABLE_FILESYSTEM static void mg_http_free_proto_data_file(struct mg_http_proto_data_file *d) { if (d != NULL) { @@ -4251,6 +4276,19 @@ static void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { } #endif +static void mg_http_free_proto_data_endpoints(struct mg_http_endpoint **ep) { + struct mg_http_endpoint *current = *ep; + + while (current != NULL) { + struct mg_http_endpoint *tmp = current->next; + free((void *) current->name); + free(current); + current = tmp; + } + + ep = NULL; +} + static void mg_http_conn_destructor(void *proto_data) { struct mg_http_proto_data *pd = (struct mg_http_proto_data *) proto_data; #ifndef MG_DISABLE_FILESYSTEM @@ -4260,9 +4298,9 @@ static void mg_http_conn_destructor(void *proto_data) { mg_http_free_proto_data_cgi(&pd->cgi); #endif #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART - mbuf_free(&pd->strm_state); + mg_http_free_proto_data_mp_stream(&pd->mp_stream); #endif - mbuf_free(&pd->endpoints); + mg_http_free_proto_data_endpoints(&pd->endpoints); free(proto_data); } @@ -4948,9 +4986,9 @@ MG_INTERNAL size_t mg_handle_chunked(struct mg_connection *nc, static mg_event_handler_t mg_http_get_endpoint_handler( struct mg_connection *nc, struct mg_str *uri_path) { struct mg_http_proto_data *pd; - size_t pos = 0; mg_event_handler_t ret = NULL; int matched, matched_max = 0; + struct mg_http_endpoint *ep; if (nc == NULL) { return NULL; @@ -4958,81 +4996,23 @@ static mg_event_handler_t mg_http_get_endpoint_handler( pd = mg_http_get_proto_data(nc); - while (pos < pd->endpoints.len) { - size_t name_len; - memcpy(&name_len, pd->endpoints.buf + pos, sizeof(name_len)); - if ((matched = mg_match_prefix_n(pd->endpoints.buf + pos + sizeof(size_t), - name_len, uri_path->p, uri_path->len)) != - -1) { + ep = pd->endpoints; + while (ep != NULL) { + if ((matched = mg_match_prefix_n(ep->name, ep->name_len, uri_path->p, + uri_path->len)) != -1) { if (matched > matched_max) { /* Looking for the longest suitable handler */ - memcpy(&ret, - pd->endpoints.buf + pos + sizeof(name_len) + (name_len + 1), - sizeof(ret)); + ret = ep->handler; matched_max = matched; } } - pos += sizeof(name_len) + (name_len + 1) + sizeof(ret); + ep = ep->next; } return ret; } -#ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART -struct mg_http_stream_info { - struct mg_str endpoint; - struct mg_str boundary; - struct mg_str var_name; - struct mg_str file_name; -}; - -/* - * Save/restore state into buf is convinient due to lack of - * protocol/connection parameters in mongoose - * once mongoose will have way to store connection/protocol - * related data these function can be replaced with usual structs - * TODO(alashkin): replace once those way will be implemented - */ -static void mg_http_parse_stream_info(struct mbuf *buf, - struct mg_http_stream_info *si) { - const char *ptr = buf->buf; - memcpy(&si->endpoint.len, ptr, sizeof(si->endpoint.len)); - ptr += sizeof(si->endpoint.len); - si->endpoint.p = ptr; - ptr += si->endpoint.len; - memcpy(&si->boundary.len, ptr, sizeof(si->boundary.len)); - ptr += sizeof(si->boundary.len); - si->boundary.p = ptr; - ptr += si->boundary.len + 1; /* Explicitly zero-terminated */ - memcpy(&si->var_name.len, ptr, sizeof(si->var_name.len)); - ptr += sizeof(si->var_name.len); - si->var_name.p = ptr; - ptr += si->var_name.len + 1; - memcpy(&si->file_name.len, ptr, sizeof(si->file_name.len)); - ptr += sizeof(si->file_name.len); - si->file_name.p = ptr; - ptr += si->file_name.len + 1; -} - -static void mg_http_store_stream_info(struct mbuf *buf, - struct mg_http_stream_info *si) { - char zero = 0; - mbuf_remove(buf, buf->len); - mbuf_append(buf, &si->endpoint.len, sizeof(si->endpoint.len)); - mbuf_append(buf, si->endpoint.p, si->endpoint.len); - mbuf_append(buf, &si->boundary.len, sizeof(si->boundary.len)); - mbuf_append(buf, si->boundary.p, si->boundary.len); - mbuf_append(buf, &zero, 1); /* Make boundary zero terminated */ - mbuf_append(buf, &si->var_name.len, sizeof(si->var_name.len)); - mbuf_append(buf, si->var_name.p, si->var_name.len); - mbuf_append(buf, &zero, 1); - mbuf_append(buf, &si->file_name.len, sizeof(si->file_name.len)); - mbuf_append(buf, si->file_name.p, si->file_name.len); - mbuf_append(buf, &zero, 1); -} -#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ - static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev, struct http_message *hm) { struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); @@ -5087,23 +5067,17 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { #endif if (ev == MG_EV_CLOSE) { #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->strm_state.len != 0) { + if (pd->mp_stream.boundary != NULL) { /* * Multipart message is in progress, but we get close * MG_EV_HTTP_PART_END with error flag */ - struct mg_http_stream_info si; struct mg_http_multipart_part mp; - mg_event_handler_t handler; - memset(&mp, 0, sizeof(mp)); - - mg_http_parse_stream_info(&pd->strm_state, &si); - handler = mg_http_get_endpoint_handler(nc->listener, &si.endpoint); - mp.status = -1; - mp.var_name = si.var_name.p; - mp.file_name = si.file_name.p; - mg_call(nc, (handler ? handler : nc->handler), MG_EV_HTTP_PART_END, &mp); + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + mg_call(nc, (pd->endpoint_handler ? pd->endpoint_handler : nc->handler), + MG_EV_HTTP_PART_END, &mp); } else #endif if (io->len > 0 && mg_parse_http(io->buf, io->len, hm, is_req) > 0) { @@ -5130,7 +5104,7 @@ void mg_http_handler(struct mg_connection *nc, int ev, void *ev_data) { struct mg_str *s; #ifdef MG_ENABLE_HTTP_STREAMING_MULTIPART - if (pd->strm_state.len != 0) { + if (pd->mp_stream.boundary != NULL) { mg_http_multipart_continue(nc, io, ev, ev_data); return; } @@ -5250,8 +5224,6 @@ static void mg_http_multipart_begin(struct mg_connection *nc, const char multipart[] = "multipart"; char boundary[100]; int boundary_len; - struct mg_http_stream_info si; - mg_event_handler_t handler; if (nc->listener == NULL) { /* No streaming for replies now */ @@ -5285,24 +5257,22 @@ static void mg_http_multipart_begin(struct mg_connection *nc, /* If we reach this place - that is multipart request */ - if (pd->strm_state.len != 0) { + if (pd->mp_stream.boundary != NULL) { /* * Another streaming request was in progress, * looks like protocol error */ nc->flags |= MG_F_CLOSE_IMMEDIATELY; - mbuf_free(&pd->strm_state); } else { - si.endpoint = hm->uri; - si.boundary.p = boundary; - si.boundary.len = boundary_len; - si.var_name.p = si.file_name.p = NULL; - si.var_name.len = si.file_name.len = 0; + pd->mp_stream.boundary = strdup(boundary); + pd->mp_stream.var_name = pd->mp_stream.file_name = NULL; - mg_http_store_stream_info(&pd->strm_state, &si); - handler = mg_http_get_endpoint_handler(nc->listener, &si.endpoint); - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_MULTIPART_REQUEST, - hm); + pd->endpoint_handler = mg_http_get_endpoint_handler(nc->listener, &hm->uri); + if (pd->endpoint_handler == NULL) { + pd->endpoint_handler = nc->handler; + } + + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_MULTIPART_REQUEST, hm); mbuf_remove(io, req_len); } @@ -5313,32 +5283,27 @@ exit_mp: static void mg_http_multipart_continue(struct mg_connection *nc, struct mbuf *io, int ev, void *ev_data) { /* Continue to stream multipart */ - struct mg_http_stream_info si; - mg_event_handler_t handler; struct mg_http_multipart_part mp; const char *boundary; int req_len; struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - mg_http_parse_stream_info(&pd->strm_state, &si); - handler = mg_http_get_endpoint_handler(nc->listener, &si.endpoint); - memset(&mp, 0, sizeof(mp)); - - mp.var_name = si.var_name.p; - mp.file_name = si.file_name.p; - boundary = c_strnstr(io->buf, si.boundary.p, io->len); + mp.var_name = pd->mp_stream.var_name; + mp.file_name = pd->mp_stream.file_name; + boundary = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); if (boundary == NULL) { mp.data.p = io->buf; mp.data.len = io->len; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_DATA, &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_DATA, &mp); mbuf_remove(io, io->len); } else { - int has_prefix = 0, has_suffix = 0; + int has_prefix = 0, has_suffix = 0, + boundary_len = strlen(pd->mp_stream.boundary); if (boundary - 2 >= io->buf) { has_prefix = (strncmp(boundary - 2, "--", 2) == 0); } - if (boundary + si.boundary.len <= io->buf + io->len) { - has_suffix = (strncmp(boundary + si.boundary.len, "--", 2) == 0); + if (boundary + boundary_len <= io->buf + io->len) { + has_suffix = (strncmp(boundary + boundary_len, "--", 2) == 0); } if (has_prefix && !has_suffix) { /* No suffix - not last boundary */ @@ -5350,9 +5315,9 @@ static void mg_http_multipart_continue(struct mg_connection *nc, if (num_left > 2) { /* \r\n */ mp.data.p = io->buf; mp.data.len = num_left - 2; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_DATA, &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_DATA, &mp); mp.data.len = 0; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_END, &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_END, &mp); mbuf_remove(io, num_left); } @@ -5362,18 +5327,16 @@ static void mg_http_multipart_continue(struct mg_connection *nc, mp.file_name = filename; if ((req_len = mg_http_get_request_len(io->buf, io->len)) > 0) { const char *tmp; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_BEGIN, - &mp); - si.var_name.p = mp.var_name; - si.var_name.len = strlen(mp.var_name); - si.file_name.p = mp.file_name; - si.file_name.len = strlen(mp.file_name); - mg_http_store_stream_info(&pd->strm_state, &si); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_BEGIN, &mp); + free((void *) pd->mp_stream.var_name); + pd->mp_stream.var_name = strdup(mp.var_name); + free((void *) pd->mp_stream.file_name); + pd->mp_stream.file_name = strdup(mp.file_name); mbuf_remove(io, req_len); mp.data.p = io->buf; - tmp = c_strnstr(io->buf, si.boundary.p, io->len); + tmp = c_strnstr(io->buf, pd->mp_stream.boundary, io->len); if (tmp == NULL) { mp.data.len = io->len; } else { @@ -5382,12 +5345,10 @@ static void mg_http_multipart_continue(struct mg_connection *nc, if (mp.data.len != 0) { size_t data_len = mp.data.len; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_DATA, - &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_DATA, &mp); if (data_len != io->len) { mp.data.len = 0; - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_END, - &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_END, &mp); } mbuf_remove(io, data_len); } @@ -5401,14 +5362,14 @@ static void mg_http_multipart_continue(struct mg_connection *nc, mp.data.p = io->buf; mp.data.len = boundary - io->buf - 4; if (mp.data.len != 0) { - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_DATA, &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_DATA, &mp); } - mg_call(nc, handler ? handler : nc->handler, MG_EV_HTTP_PART_END, &mp); + mg_call(nc, pd->endpoint_handler, MG_EV_HTTP_PART_END, &mp); /* Skip epilogue (if any) */ mbuf_remove(io, io->len); - mbuf_free(&pd->strm_state); + mg_http_free_proto_data_mp_stream(&pd->mp_stream); } else { /* Malformed request */ nc->flags |= MG_F_CLOSE_IMMEDIATELY; @@ -7610,10 +7571,13 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name, void mg_register_http_endpoint(struct mg_connection *nc, const char *uri_path, mg_event_handler_t handler) { struct mg_http_proto_data *pd = mg_http_get_proto_data(nc); - size_t len = strlen(uri_path); - mbuf_append(&pd->endpoints, &len, sizeof(len)); - mbuf_append(&pd->endpoints, uri_path, len + 1); - mbuf_append(&pd->endpoints, &handler, sizeof(handler)); + struct mg_http_endpoint *new_ep = + (struct mg_http_endpoint *) calloc(1, sizeof(*new_ep)); + new_ep->name = strdup(uri_path); + new_ep->name_len = strlen(new_ep->name); + new_ep->handler = handler; + new_ep->next = pd->endpoints; + pd->endpoints = new_ep; } #endif /* MG_DISABLE_HTTP */