diff --git a/mongoose.c b/mongoose.c index f19033d864a8a8dbe38ca464deec597a01548b98..a5ad3e7ff48e86bf70e5a169d1d35e168d5fa225 100644 --- a/mongoose.c +++ b/mongoose.c @@ -8279,8 +8279,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, case MG_EV_HTTP_PART_BEGIN: { struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) ev_data; - struct file_upload_state *fus = - (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + struct file_upload_state *fus; struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); mp->user_data = NULL; if (lfn.p == NULL || lfn.len == 0) { @@ -8294,6 +8293,11 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, nc->flags |= MG_F_SEND_AND_CLOSE; return; } + fus = (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + if (fus == NULL) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } fus->lfn = (char *) MG_MALLOC(lfn.len + 1); memcpy(fus->lfn, lfn.p, lfn.len); fus->lfn[lfn.len] = '\0'; @@ -8365,12 +8369,6 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, if (mp->status >= 0 && fus->fp != NULL) { LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, fus->lfn, (int) fus->num_recd)); - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n" - "Ok, %s - %d bytes.\r\n", - mp->file_name, (int) fus->num_recd); } else { LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); /* @@ -8382,6 +8380,15 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, MG_FREE(fus->lfn); MG_FREE(fus); mp->user_data = NULL; + /* Don't close the connection yet, there may be more files to come. */ + break; + } + case MG_EV_HTTP_MULTIPART_REQUEST_END: { + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Ok.\r\n"); nc->flags |= MG_F_SEND_AND_CLOSE; break; } diff --git a/src/mg_http.c b/src/mg_http.c index e7151acaf50f7685e0ca2555d8a2e0fa9c181d7b..8c9fd5f17a1369ccae35ecbca714fcae3bb0affc 100644 --- a/src/mg_http.c +++ b/src/mg_http.c @@ -2713,8 +2713,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, case MG_EV_HTTP_PART_BEGIN: { struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) ev_data; - struct file_upload_state *fus = - (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + struct file_upload_state *fus; struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name)); mp->user_data = NULL; if (lfn.p == NULL || lfn.len == 0) { @@ -2728,6 +2727,11 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, nc->flags |= MG_F_SEND_AND_CLOSE; return; } + fus = (struct file_upload_state *) MG_CALLOC(1, sizeof(*fus)); + if (fus == NULL) { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + return; + } fus->lfn = (char *) MG_MALLOC(lfn.len + 1); memcpy(fus->lfn, lfn.p, lfn.len); fus->lfn[lfn.len] = '\0'; @@ -2799,12 +2803,6 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, if (mp->status >= 0 && fus->fp != NULL) { LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name, fus->lfn, (int) fus->num_recd)); - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n\r\n" - "Ok, %s - %d bytes.\r\n", - mp->file_name, (int) fus->num_recd); } else { LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn)); /* @@ -2816,6 +2814,15 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data, MG_FREE(fus->lfn); MG_FREE(fus); mp->user_data = NULL; + /* Don't close the connection yet, there may be more files to come. */ + break; + } + case MG_EV_HTTP_MULTIPART_REQUEST_END: { + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n\r\n" + "Ok.\r\n"); nc->flags |= MG_F_SEND_AND_CLOSE; break; } diff --git a/test/test.mk b/test/test.mk index 23ad747dfa222a48d38b6282b5b4795a92e0f243..36df546e411b8b21cf251b80a60805f0455e44e4 100644 --- a/test/test.mk +++ b/test/test.mk @@ -28,7 +28,7 @@ # OSX clang doesn't build ASAN. Use brew: # $ brew tap homebrew/versions # $ brew install llvm36 --with-clang --with-asan -CLANG:=clang-3.6 +CLANG:=clang PEDANTIC=$(shell gcc --version 2>/dev/null | grep -q clang && echo -pedantic) diff --git a/test/unit_test.c b/test/unit_test.c index 485403595d0c3df66c6d68c875aa93f70a40852b..763b683bde3831f1a2a2464d571d6db0ee5d53cf 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -4076,14 +4076,19 @@ static void cb_mp_srv(struct mg_connection *nc, int ev, void *p) { } static void cb_mp_send_one_byte(struct mg_connection *nc, int ev, void *p) { - static int i; + static int i = -1; (void) p; - if (ev == MG_EV_POLL) { + if (ev == MG_EV_CONNECT) { + i = 0; + } else if (i >= 0 && ev == MG_EV_POLL) { char ch = ((char *) nc->user_data)[i++]; int l = strlen((char *) nc->user_data); if (ch != '\0') { mg_send(nc, &ch, 1); DBG(("%p sent %d of %d", (void *) nc, i, l)); + } else { + nc->flags |= MG_F_SEND_AND_CLOSE; + i = -1; } } } @@ -4209,7 +4214,6 @@ static const char *test_http_multipart2(void) { if ((r = test_http_multipart_check_res(&mpd.res)) != NULL) return r; - c->flags |= MG_F_CLOSE_IMMEDIATELY; mbuf_free(&mpd.res); memset(&mpd, 0, sizeof(mpd)); mbuf_init(&mpd.res, 0); @@ -4316,6 +4320,89 @@ static const char *test_http_multipart2(void) { return NULL; } + +static struct mg_str upload_lfn_same(struct mg_connection *nc, + struct mg_str fn) { + if (fn.len == 0) { + fn = mg_strdup(mg_mk_str("bar")); + } + (void) nc; + return fn; +} + +static void cb_mp_srv_upload(struct mg_connection *nc, int ev, void *p) { + mg_file_upload_handler(nc, ev, p, upload_lfn_same); + if (ev == MG_EV_CLOSE && nc->listener != NULL) { + *((int *) nc->listener->user_data) = 1; + } + (void) p; +} + +static const char *test_http_multipart_upload(void) { + struct mg_mgr mgr; + const char req_fmt[] = + "%s" + "Content-Disposition: form-data; name=\"a\"; filename=\"foo\"\r\n" + "\r\n" + "%s" + "\r\n--Asrf456BGe4h\r\n" + "Content-Disposition: form-data; name=\"b\"\r\n" + "\r\n" + "%s" + "\r\n--Asrf456BGe4h\r\n" + "Content-Disposition: form-data; name=\"c\"; filename=\"baz\"\r\n" + "\r\n" + "%s" + "\r\n--Asrf456BGe4h--\r\n" + "\r\n"; + + char req[1024 * 5], *data; + struct mg_connection *c, *lc; + size_t size; + int done = 0; + + mg_mgr_init(&mgr, NULL); + lc = mg_bind(&mgr, "8766", cb_mp_srv_upload); + mg_set_protocol_http_websocket(lc); + lc->user_data = &done; + + (void) remove("foo"); + (void) remove("bar"); + (void) remove("baz"); + + snprintf(req, sizeof(req), req_fmt, "", b1, b2, b4); + + ASSERT((c = mg_connect_http(&mgr, cb_mp_send_one_byte, + "http://127.0.0.1:8766/test", + "Content-Type: " + "multipart/form-data;boundary=Asrf456BGe4h", + "\r\n--Asrf456BGe4h\r\n")) != NULL); + c->user_data = req; + + poll_until(&mgr, 5, c_int_eq, &done, (void *) 1); + + data = read_file("foo", &size); + ASSERT_PTRNE(data, NULL); + ASSERT_STREQ_NZ(data, b1); + (void) remove("foo"); + free(data); + + data = read_file("bar", &size); + ASSERT_PTRNE(data, NULL); + ASSERT_STREQ_NZ(data, b2); + (void) remove("bar"); + free(data); + + data = read_file("baz", &size); + ASSERT_PTRNE(data, NULL); + ASSERT_STREQ_NZ(data, b4); + (void) remove("baz"); + free(data); + + mg_mgr_free(&mgr); + return NULL; +} + #endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */ static const char *test_http_multipart(void) { @@ -5545,6 +5632,7 @@ const char *tests_run(const char *filter) { RUN_TEST(test_http_multipart); #if MG_ENABLE_HTTP_STREAMING_MULTIPART RUN_TEST(test_http_multipart2); + RUN_TEST(test_http_multipart_upload); #endif RUN_TEST(test_parse_date_string); RUN_TEST(test_websocket);