mirror of
https://github.com/cesanta/mongoose.git
synced 2025-06-11 12:14:41 +08:00
mg_file_upload_handler: Support multiple files
curl -F file1 -F file2 ... Add a unit test and fix a minor memory leak when returning an error. CL: mg_file_upload_handler: Support multiple files PUBLISHED_FROM=5c4bf2be676346fb782e80f50f79df6a6721ac88
This commit is contained in:
parent
9ba6eb716d
commit
339bbee0df
23
mongoose.c
23
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: {
|
case MG_EV_HTTP_PART_BEGIN: {
|
||||||
struct mg_http_multipart_part *mp =
|
struct mg_http_multipart_part *mp =
|
||||||
(struct mg_http_multipart_part *) ev_data;
|
(struct mg_http_multipart_part *) ev_data;
|
||||||
struct file_upload_state *fus =
|
struct file_upload_state *fus;
|
||||||
(struct file_upload_state *) MG_CALLOC(1, sizeof(*fus));
|
|
||||||
struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name));
|
struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name));
|
||||||
mp->user_data = NULL;
|
mp->user_data = NULL;
|
||||||
if (lfn.p == NULL || lfn.len == 0) {
|
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;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
return;
|
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);
|
fus->lfn = (char *) MG_MALLOC(lfn.len + 1);
|
||||||
memcpy(fus->lfn, lfn.p, lfn.len);
|
memcpy(fus->lfn, lfn.p, lfn.len);
|
||||||
fus->lfn[lfn.len] = '\0';
|
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) {
|
if (mp->status >= 0 && fus->fp != NULL) {
|
||||||
LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name,
|
LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name,
|
||||||
fus->lfn, (int) fus->num_recd));
|
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 {
|
} else {
|
||||||
LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn));
|
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->lfn);
|
||||||
MG_FREE(fus);
|
MG_FREE(fus);
|
||||||
mp->user_data = NULL;
|
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;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2713,8 +2713,7 @@ void mg_file_upload_handler(struct mg_connection *nc, int ev, void *ev_data,
|
|||||||
case MG_EV_HTTP_PART_BEGIN: {
|
case MG_EV_HTTP_PART_BEGIN: {
|
||||||
struct mg_http_multipart_part *mp =
|
struct mg_http_multipart_part *mp =
|
||||||
(struct mg_http_multipart_part *) ev_data;
|
(struct mg_http_multipart_part *) ev_data;
|
||||||
struct file_upload_state *fus =
|
struct file_upload_state *fus;
|
||||||
(struct file_upload_state *) MG_CALLOC(1, sizeof(*fus));
|
|
||||||
struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name));
|
struct mg_str lfn = local_name_fn(nc, mg_mk_str(mp->file_name));
|
||||||
mp->user_data = NULL;
|
mp->user_data = NULL;
|
||||||
if (lfn.p == NULL || lfn.len == 0) {
|
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;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
return;
|
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);
|
fus->lfn = (char *) MG_MALLOC(lfn.len + 1);
|
||||||
memcpy(fus->lfn, lfn.p, lfn.len);
|
memcpy(fus->lfn, lfn.p, lfn.len);
|
||||||
fus->lfn[lfn.len] = '\0';
|
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) {
|
if (mp->status >= 0 && fus->fp != NULL) {
|
||||||
LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name,
|
LOG(LL_DEBUG, ("%p Uploaded %s (%s), %d bytes", nc, mp->file_name,
|
||||||
fus->lfn, (int) fus->num_recd));
|
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 {
|
} else {
|
||||||
LOG(LL_ERROR, ("Failed to store %s (%s)", mp->file_name, fus->lfn));
|
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->lfn);
|
||||||
MG_FREE(fus);
|
MG_FREE(fus);
|
||||||
mp->user_data = NULL;
|
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;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
# OSX clang doesn't build ASAN. Use brew:
|
# OSX clang doesn't build ASAN. Use brew:
|
||||||
# $ brew tap homebrew/versions
|
# $ brew tap homebrew/versions
|
||||||
# $ brew install llvm36 --with-clang --with-asan
|
# $ 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)
|
PEDANTIC=$(shell gcc --version 2>/dev/null | grep -q clang && echo -pedantic)
|
||||||
|
|
||||||
|
@ -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 void cb_mp_send_one_byte(struct mg_connection *nc, int ev, void *p) {
|
||||||
static int i;
|
static int i = -1;
|
||||||
(void) p;
|
(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++];
|
char ch = ((char *) nc->user_data)[i++];
|
||||||
int l = strlen((char *) nc->user_data);
|
int l = strlen((char *) nc->user_data);
|
||||||
if (ch != '\0') {
|
if (ch != '\0') {
|
||||||
mg_send(nc, &ch, 1);
|
mg_send(nc, &ch, 1);
|
||||||
DBG(("%p sent %d of %d", (void *) nc, i, l));
|
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;
|
if ((r = test_http_multipart_check_res(&mpd.res)) != NULL) return r;
|
||||||
|
|
||||||
c->flags |= MG_F_CLOSE_IMMEDIATELY;
|
|
||||||
mbuf_free(&mpd.res);
|
mbuf_free(&mpd.res);
|
||||||
memset(&mpd, 0, sizeof(mpd));
|
memset(&mpd, 0, sizeof(mpd));
|
||||||
mbuf_init(&mpd.res, 0);
|
mbuf_init(&mpd.res, 0);
|
||||||
@ -4316,6 +4320,89 @@ static const char *test_http_multipart2(void) {
|
|||||||
|
|
||||||
return NULL;
|
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 */
|
#endif /* MG_ENABLE_HTTP_STREAMING_MULTIPART */
|
||||||
|
|
||||||
static const char *test_http_multipart(void) {
|
static const char *test_http_multipart(void) {
|
||||||
@ -5545,6 +5632,7 @@ const char *tests_run(const char *filter) {
|
|||||||
RUN_TEST(test_http_multipart);
|
RUN_TEST(test_http_multipart);
|
||||||
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
#if MG_ENABLE_HTTP_STREAMING_MULTIPART
|
||||||
RUN_TEST(test_http_multipart2);
|
RUN_TEST(test_http_multipart2);
|
||||||
|
RUN_TEST(test_http_multipart_upload);
|
||||||
#endif
|
#endif
|
||||||
RUN_TEST(test_parse_date_string);
|
RUN_TEST(test_parse_date_string);
|
||||||
RUN_TEST(test_websocket);
|
RUN_TEST(test_websocket);
|
||||||
|
Loading…
Reference in New Issue
Block a user