diff --git a/docs/README.md b/docs/README.md
index 530d2c46..72895e22 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -846,28 +846,8 @@ Parse the multipart chunk in the `body` at a given `offset`. An initial
`offset` should be 0. Fill up parameters in the provided `part`, which could be
NULL. Return offset to the next chunk, or 0 if there are no more chunks.
-Usage example:
+See `form-upload` example for a usage example.
-```c
-static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- if (mg_http_match_uri(hm, "/upload")) {
- struct mg_http_part part;
- size_t ofs = 0;
- while ((ofs = mg_http_next_multipart(ev->body, ofs, &part)) > 0) {
- LOG(LL_INFO, ("Name: [%.*s] Filename: [%.*s] Body: [%.*s]",
- (int) part.name.len, part.name.ptr,
- (int) part.filename.len, part.filename.ptr,
- (int) part.body.len, part.body.ptr));
- }
- } else {
- struct mg_http_serve_opts opts = {.root_dir = "web_root"};
- mg_http_serve_dir(c, ev_data, &opts);
- }
- }
-}
-```
## Websocket
diff --git a/examples/form-upload/Makefile b/examples/form-upload/Makefile
new file mode 100644
index 00000000..eaa5aa25
--- /dev/null
+++ b/examples/form-upload/Makefile
@@ -0,0 +1,10 @@
+PROG ?= example
+
+all: $(PROG)
+ $(DEBUGGER) ./$(PROG)
+
+$(PROG):
+ $(CC) ../../mongoose.c -I../.. $(CFLAGS) $(EXTRA) -o $(PROG) main.c
+
+clean:
+ rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb log.txt
diff --git a/examples/form-upload/main.c b/examples/form-upload/main.c
new file mode 100644
index 00000000..6872003c
--- /dev/null
+++ b/examples/form-upload/main.c
@@ -0,0 +1,42 @@
+// Copyright (c) 2021 Cesanta Software Limited
+// All rights reserved
+
+#include "mongoose.h"
+
+// HTTP request handler function. It implements the following endpoints:
+// /upload - prints all submitted form elements
+// all other URI - serves web_root/ directory
+static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
+ if (ev == MG_EV_HTTP_MSG) {
+ struct mg_http_message *hm = (struct mg_http_message *) ev_data;
+ LOG(LL_INFO, ("New request to: [%.*s]", (int) hm->uri.len, hm->uri.ptr));
+ if (mg_http_match_uri(hm, "/upload")) {
+ struct mg_http_part part;
+ size_t ofs = 0;
+ while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) {
+ LOG(LL_INFO,
+ ("Name: [%.*s] Filename: [%.*s] Body: [%.*s]", (int) part.name.len,
+ part.name.ptr, (int) part.filename.len, part.filename.ptr,
+ (int) part.body.len, part.body.ptr));
+ }
+ mg_http_reply(c, 200, "", "Thank you!");
+ } else {
+ struct mg_http_serve_opts opts = {.root_dir = "web_root"};
+ mg_http_serve_dir(c, ev_data, &opts);
+ }
+ }
+}
+
+int main(void) {
+ struct mg_mgr mgr;
+ struct mg_timer t1;
+
+ mg_mgr_init(&mgr);
+ mg_log_set("3");
+ mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
+
+ for (;;) mg_mgr_poll(&mgr, 50);
+ mg_mgr_free(&mgr);
+
+ return 0;
+}
diff --git a/examples/form-upload/web_root/index.html b/examples/form-upload/web_root/index.html
new file mode 100644
index 00000000..3b0a06c3
--- /dev/null
+++ b/examples/form-upload/web_root/index.html
@@ -0,0 +1,40 @@
+
+
+
+ example
+
+
+
+
+
+
+
+
+ Mongoose always buffers a full HTTP message before invoking
+ MG_EV_HTTP_MSG event. Big POST request require of lot
+ of RAM to buffer everything. Therefore, standard form uploads
+ should be used only when Mongoose runs on a system with lots of RAM.
+ Otherwise, please see file-updload example, how
+ a big file could be uploaded to a device with little RAM.
+
+ In this example, a standard HTML form upload is used, which uses
+ multipart-form-data encoding with
+ several variables and file upload. On a server side, a
+ mg_http_next_multipart() API is used to iterate over
+ all submitted form elements and print their payload.
+
+
+
+
+
+
+
diff --git a/mongoose.c b/mongoose.c
index 6093b3d3..23ed3362 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -470,8 +470,8 @@ size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
h1 = h2 = h2 + 2;
}
b1 = b2 = h2 + 2;
- while (b2 + 2 + (b - ofs) + 2 < max && s[b2] != '\r' && s[b2 + 1] != '\n' &&
- memcmp(&s[b2 + 2], s, b - ofs) != 0)
+ while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
+ memcmp(&s[b2 + 2], s, b - ofs) == 0))
b2++;
if (b2 + 2 >= max) return 0;
@@ -1224,9 +1224,12 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
size_t i;
for (i = 0; i + v.len + 2 < s.len; i++) {
if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
- const char *p2 = &s.ptr[i + v.len + 1], *p3 = p2;
- while (p2 < &s.ptr[s.len] && p2[0] != ';' && p2[0] != ' ') p2++;
- return stripquotes(mg_str_n(p3, p2 - p3));
+ const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
+ int q = p < x && *p == '"' ? 1 : 0;
+ while (p < x && (q ? p == b || *p != '"' : *p != ';' && *p != ' ')) p++;
+ // LOG(LL_INFO, ("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
+ // v.ptr, (int) (p - b), b));
+ return stripquotes(mg_str_n(b, p - b + q));
}
}
return mg_str_n(NULL, 0);
diff --git a/src/http.c b/src/http.c
index 13f072fd..2a3f546c 100644
--- a/src/http.c
+++ b/src/http.c
@@ -51,8 +51,8 @@ size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
h1 = h2 = h2 + 2;
}
b1 = b2 = h2 + 2;
- while (b2 + 2 + (b - ofs) + 2 < max && s[b2] != '\r' && s[b2 + 1] != '\n' &&
- memcmp(&s[b2 + 2], s, b - ofs) != 0)
+ while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' &&
+ memcmp(&s[b2 + 2], s, b - ofs) == 0))
b2++;
if (b2 + 2 >= max) return 0;
@@ -805,9 +805,12 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
size_t i;
for (i = 0; i + v.len + 2 < s.len; i++) {
if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
- const char *p2 = &s.ptr[i + v.len + 1], *p3 = p2;
- while (p2 < &s.ptr[s.len] && p2[0] != ';' && p2[0] != ' ') p2++;
- return stripquotes(mg_str_n(p3, p2 - p3));
+ const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
+ int q = p < x && *p == '"' ? 1 : 0;
+ while (p < x && (q ? p == b || *p != '"' : *p != ';' && *p != ' ')) p++;
+ // LOG(LL_INFO, ("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
+ // v.ptr, (int) (p - b), b));
+ return stripquotes(mg_str_n(b, p - b + q));
}
}
return mg_str_n(NULL, 0);
diff --git a/test/unit_test.c b/test/unit_test.c
index 4974ebb9..63ea4261 100644
--- a/test/unit_test.c
+++ b/test/unit_test.c
@@ -1265,9 +1265,9 @@ static void test_multipart(void) {
"--xyz\r\n"
"Content-Disposition: form-data; name=\"val\"\r\n"
"\r\n"
- "abcdef\r\n"
+ "abc\r\ndef\r\n"
"--xyz\r\n"
- "Content-Disposition: form-data; name=\"foo\"; filename=\"a.txt\"\r\n"
+ "Content-Disposition: form-data; name=\"foo\"; filename=\"a b.txt\"\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"hello world\r\n"
@@ -1276,12 +1276,14 @@ static void test_multipart(void) {
ASSERT(mg_http_next_multipart(mg_str(""), 0, NULL) == 0);
ASSERT((ofs = mg_http_next_multipart(mg_str(s), 0, &part)) > 0);
ASSERT(mg_strcmp(part.name, mg_str("val")) == 0);
- ASSERT(mg_strcmp(part.body, mg_str("abcdef")) == 0);
+ // LOG(LL_INFO, ("--> [%.*s]", (int) part.body.len, part.body.ptr));
+ ASSERT(mg_strcmp(part.body, mg_str("abc\r\ndef")) == 0);
ASSERT(part.filename.len == 0);
ASSERT((ofs = mg_http_next_multipart(mg_str(s), ofs, &part)) > 0);
ASSERT(mg_strcmp(part.name, mg_str("foo")) == 0);
- ASSERT(mg_strcmp(part.filename, mg_str("a.txt")) == 0);
- ASSERT(mg_strcmp(part.body, mg_str("hello world")) == 0);
+ // LOG(LL_INFO, ("--> [%.*s]", (int) part.filename.len, part.filename.ptr));
+ ASSERT(mg_strcmp(part.filename, mg_str("a b.txt")) == 0);
+ ASSERT(mg_strcmp(part.body, mg_str("hello world\r\n")) == 0);
ASSERT(mg_http_next_multipart(mg_str(s), ofs, &part) == 0);
}