diff --git a/build/test/unit_test.c b/build/test/unit_test.c
index c7cb6e76535c80b7af39b8002d7c2c3ab5c7d9e4..e6c35aed70870653c05255e1d265279a73b28367 100644
--- a/build/test/unit_test.c
+++ b/build/test/unit_test.c
@@ -453,6 +453,41 @@ static const char *test_server(void) {
   return NULL;
 }
 
+#define DISP "Content-Disposition: form/data; "
+#define CRLF "\r\n"
+#define BOUNDARY "--xyz"
+static const char *test_parse_multipart(void) {
+  char a[100], b[100];
+  const char *p;
+  static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF
+    "some_content" CRLF BOUNDARY CRLF
+    BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF
+    "another_content" CRLF BOUNDARY CRLF
+    "--" CRLF;
+  int n, n2, len, f1_len = sizeof(f1) - 1;
+
+  ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
+  ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
+  ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a),
+                                 b, sizeof(b), &p, &len)) > 0);
+  ASSERT(len == 12);
+  ASSERT(memcmp(p, "some_content", len) == 0);
+  ASSERT(strcmp(a, "f1") == 0);
+  ASSERT(b[0] == '\0');
+
+  ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a),
+                                  b, sizeof(b), &p, &len)) > 0);
+  ASSERT(len == 15);
+  ASSERT(memcmp(p, "another_content", len) == 0);
+  ASSERT(strcmp(a, "f2") == 0);
+  ASSERT(strcmp(b, "foo bar.txt") == 0);
+
+  ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a),
+                                  b, sizeof(b), &p, &len)) == 0);
+
+  return NULL;
+}
+
 static const char *run_all_tests(void) {
   RUN_TEST(test_should_keep_alive);
   RUN_TEST(test_match_prefix);
@@ -466,6 +501,7 @@ static const char *run_all_tests(void) {
   RUN_TEST(test_get_var);
   RUN_TEST(test_next_option);
   RUN_TEST(test_server);
+  RUN_TEST(test_parse_multipart);
   return NULL;
 }
 
diff --git a/mongoose.c b/mongoose.c
index f85681bdb20e9042381b58639ae81d0bb3171809..4be85dc4ce7e6123f9db10f8edcfff27f0f5e1bf 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -2979,24 +2979,24 @@ static int is_dav_mutation(const struct connection *conn) {
 }
 #endif // NO_AUTH
 
-int mg_parse_header(const char *str, const char *var_name, char *buf,
-                    size_t buf_size) {
+int parse_header(const char *str, int str_len, const char *var_name, char *buf,
+                 size_t buf_size) {
   int ch = ' ', len = 0, n = strlen(var_name);
-  const char *p, *s = NULL;
+  const char *p, *end = str + str_len, *s = NULL;
 
   if (buf != NULL) buf[0] = '\0';
 
   // Find where variable starts
-  while (str != NULL && (s = strstr(str, var_name)) != NULL &&
-         ((s > str && s[-1] != ' ') || s[n] != '=')) {
-    str = s + n;
+  for (s = str; s != NULL && &s[n] < end; s++) {
+    if ((s == str || s[-1] == ' ') && s[n] == '=' &&
+        !memcmp(s, var_name, n)) break;
   }
 
-  if (s != NULL && s[n + 1] != '\0') {
+  if (s != NULL && &s[n + 1] < end) {
     s += n + 1;
     if (*s == '"' || *s == '\'') ch = *s++;
     p = s;
-    while (p[0] != '\0' && p[0] != ch && len < (int) buf_size) {
+    while (p < end && p[0] != ch && len < (int) buf_size) {
       if (p[0] == '\\' && p[1] == ch) p++;
       buf[len++] = *p++;
     }
@@ -3004,6 +3004,7 @@ int mg_parse_header(const char *str, const char *var_name, char *buf,
       len = 0;
     } else {
       if (len > 0 && s[len - 1] == ',') len--;
+      if (len > 0 && s[len - 1] == ';') len--;
       buf[len] = '\0';
     }
   }
@@ -3011,6 +3012,11 @@ int mg_parse_header(const char *str, const char *var_name, char *buf,
   return len;
 }
 
+int mg_parse_header(const char *str, const char *var_name, char *buf,
+                    size_t buf_size) {
+  return parse_header(str, strlen(str), var_name, buf, buf_size);
+}
+
 #ifdef USE_LUA
 #include "lua_5.2.1.h"
 
@@ -3769,6 +3775,51 @@ int mg_get_var(const struct mg_connection *conn, const char *name,
   return len;
 }
 
+static int get_line_len(const char *buf, int buf_len) {
+  int len = 0;
+  while (len < buf_len && buf[len] != '\n') len++;
+  return buf[len] == '\n' ? len + 1: -1;
+}
+
+int mg_parse_multipart(const char *buf, int buf_len,
+                       char *var_name, int var_name_len,
+                       char *file_name, int file_name_len,
+                       const char **data, int *data_len) {
+  static const char cd[] = "Content-Disposition: ";
+  //struct mg_connection c;
+  int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
+  //char *p;
+
+  if (buf == NULL || buf_len <= 0) return 0;
+  if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
+  if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
+
+  // Get boundary length
+  bl = get_line_len(buf, buf_len);
+
+  // Loop through headers, fetch variable name and file name
+  var_name[0] = file_name[0] = '\0';
+  for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
+    if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
+      parse_header(buf + n + cdl, ll - (cdl + 2), "name",
+                   var_name, var_name_len);
+      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
+                   file_name, file_name_len);
+    }
+  }
+
+  // Scan body, search for terminating boundary
+  for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
+    if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
+      if (data_len != NULL) *data_len = (pos - 2) - hl;
+      if (data != NULL) *data = buf + hl;
+      return pos;
+    }
+  }
+
+  return 0;
+}
+
 const char **mg_get_valid_option_names(void) {
   return static_config_options;
 }
diff --git a/mongoose.h b/mongoose.h
index 0b644c9c6b1350b08d4ed3f25d5a2399a769391c..720dc36367c45ea6a3f5783aeca5c09acf929cbc 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -92,6 +92,10 @@ const char *mg_get_mime_type(const char *file_name);
 int mg_get_var(const struct mg_connection *conn, const char *var_name,
                char *buf, size_t buf_len);
 int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
+int mg_parse_multipart(const char *buf, int buf_len,
+                       char *var_name, int var_name_len,
+                       char *file_name, int file_name_len,
+                       const char **data, int *data_len);
 
 // Utility functions
 void *mg_start_thread(void *(*func)(void *), void *param);