mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-27 15:01:03 +08:00
Add mg_http_next_multipart()
This commit is contained in:
parent
bc00d5159a
commit
5bd22cb5e4
@ -829,6 +829,45 @@ void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||
|
||||
Write a Basic `Authorization` header to the output buffer.
|
||||
|
||||
### mg\_http\_next_\multipart()
|
||||
|
||||
```c
|
||||
// Parameter for mg_http_next_multipart
|
||||
struct mg_http_part {
|
||||
struct mg_str name; // Form field name
|
||||
struct mg_str filename; // Filename for file uploads
|
||||
struct mg_str body; // Part contents
|
||||
};
|
||||
|
||||
size_t mg_http_next_multipart(struct mg_str body, size_t offset, struct mg_http_part *part);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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
|
||||
|
||||
|
84
mongoose.c
84
mongoose.c
@ -429,12 +429,55 @@ void mg_error(struct mg_connection *c, const char *fmt, ...) {
|
||||
|
||||
|
||||
// Multipart POST example:
|
||||
// https://gist.github.com/cpq/b8dd247571e6ee9c54ef7e8dfcfecf48
|
||||
int mg_http_next_multipart(struct mg_str body, int offset,
|
||||
struct mg_http_part *part) {
|
||||
(void) body;
|
||||
(void) part;
|
||||
return offset;
|
||||
// --xyz
|
||||
// Content-Disposition: form-data; name="val"
|
||||
//
|
||||
// abcdef
|
||||
// --xyz
|
||||
// Content-Disposition: form-data; name="foo"; filename="a.txt"
|
||||
// Content-Type: text/plain
|
||||
//
|
||||
// hello world
|
||||
//
|
||||
// --xyz--
|
||||
size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
|
||||
struct mg_http_part *part) {
|
||||
struct mg_str cd = mg_str_n("Content-Disposition", 19);
|
||||
const char *s = body.ptr;
|
||||
size_t b = ofs, h1, h2, b1, b2, max = body.len;
|
||||
|
||||
// Init part params
|
||||
if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
|
||||
|
||||
// Skip boundary
|
||||
while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
|
||||
if (b <= ofs || b + 2 >= max) return 0;
|
||||
// LOG(LL_INFO, ("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
|
||||
|
||||
// Skip headers
|
||||
h1 = h2 = b + 2;
|
||||
for (;;) {
|
||||
while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
|
||||
if (h2 == h1) break;
|
||||
if (h2 + 2 >= max) return 0;
|
||||
// LOG(LL_INFO, ("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
|
||||
if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
|
||||
mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) {
|
||||
struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
|
||||
part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
|
||||
part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
|
||||
}
|
||||
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)
|
||||
b2++;
|
||||
|
||||
if (b2 + 2 >= max) return 0;
|
||||
if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
|
||||
// LOG(LL_INFO, ("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
|
||||
return b2 + 2;
|
||||
}
|
||||
|
||||
void mg_http_bauth(struct mg_connection *c, const char *user,
|
||||
@ -1164,20 +1207,31 @@ void mg_http_creds(struct mg_http_message *hm, char *user, int userlen,
|
||||
} else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) {
|
||||
snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->ptr + 7);
|
||||
} else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
|
||||
size_t i;
|
||||
for (i = 0; i < v->len - 13; i++) {
|
||||
if (memcmp(&v->ptr[i], "access_token=", 13) == 0) {
|
||||
const char *p2 = v->ptr + i + 13, *p3 = p2;
|
||||
while (p2 < &v->ptr[v->len] && p2[0] != ';' && p2[0] != ' ') p2++;
|
||||
snprintf(pass, passlen, "%.*s", (int) (p2 - p3), p3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
|
||||
if (t.len > 0) snprintf(pass, passlen, "%.*s", (int) t.len, t.ptr);
|
||||
} else {
|
||||
mg_http_get_var(&hm->query, "access_token", pass, passlen);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mg_str stripquotes(struct mg_str s) {
|
||||
return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"'
|
||||
? mg_str_n(s.ptr + 1, s.len - 2)
|
||||
: s;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
return mg_str_n(NULL, 0);
|
||||
}
|
||||
|
||||
bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
|
||||
return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len);
|
||||
}
|
||||
|
@ -752,7 +752,7 @@ struct mg_http_serve_opts {
|
||||
struct mg_http_part {
|
||||
struct mg_str name; // Form field name
|
||||
struct mg_str filename; // Filename for file uploads
|
||||
struct mg_str part; // Part contents
|
||||
struct mg_str body; // Part contents
|
||||
};
|
||||
|
||||
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
||||
@ -780,7 +780,8 @@ bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
|
||||
int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
|
||||
const char *dir);
|
||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||
int mg_http_next_multipart(struct mg_str, int, struct mg_http_part *);
|
||||
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
|
||||
size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
||||
|
||||
|
||||
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
|
||||
|
85
src/http.c
85
src/http.c
@ -10,13 +10,55 @@
|
||||
#include "ws.h"
|
||||
|
||||
// Multipart POST example:
|
||||
// https://gist.github.com/cpq/b8dd247571e6ee9c54ef7e8dfcfecf48
|
||||
int mg_http_next_multipart(struct mg_str body, int offset,
|
||||
struct mg_http_part *part) {
|
||||
if (o
|
||||
(void) body;
|
||||
(void) part;
|
||||
return 0;
|
||||
// --xyz
|
||||
// Content-Disposition: form-data; name="val"
|
||||
//
|
||||
// abcdef
|
||||
// --xyz
|
||||
// Content-Disposition: form-data; name="foo"; filename="a.txt"
|
||||
// Content-Type: text/plain
|
||||
//
|
||||
// hello world
|
||||
//
|
||||
// --xyz--
|
||||
size_t mg_http_next_multipart(struct mg_str body, size_t ofs,
|
||||
struct mg_http_part *part) {
|
||||
struct mg_str cd = mg_str_n("Content-Disposition", 19);
|
||||
const char *s = body.ptr;
|
||||
size_t b = ofs, h1, h2, b1, b2, max = body.len;
|
||||
|
||||
// Init part params
|
||||
if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0);
|
||||
|
||||
// Skip boundary
|
||||
while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++;
|
||||
if (b <= ofs || b + 2 >= max) return 0;
|
||||
// LOG(LL_INFO, ("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s));
|
||||
|
||||
// Skip headers
|
||||
h1 = h2 = b + 2;
|
||||
for (;;) {
|
||||
while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++;
|
||||
if (h2 == h1) break;
|
||||
if (h2 + 2 >= max) return 0;
|
||||
// LOG(LL_INFO, ("Header: [%.*s]", (int) (h2 - h1), &s[h1]));
|
||||
if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' &&
|
||||
mg_ncasecmp(&s[h1], cd.ptr, cd.len) == 0) {
|
||||
struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2));
|
||||
part->name = mg_http_get_header_var(v, mg_str_n("name", 4));
|
||||
part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8));
|
||||
}
|
||||
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)
|
||||
b2++;
|
||||
|
||||
if (b2 + 2 >= max) return 0;
|
||||
if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1);
|
||||
// LOG(LL_INFO, ("Body: [%.*s]", (int) (b2 - b1), &s[b1]));
|
||||
return b2 + 2;
|
||||
}
|
||||
|
||||
void mg_http_bauth(struct mg_connection *c, const char *user,
|
||||
@ -746,20 +788,31 @@ void mg_http_creds(struct mg_http_message *hm, char *user, int userlen,
|
||||
} else if (v != NULL && v->len > 7 && memcmp(v->ptr, "Bearer ", 7) == 0) {
|
||||
snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->ptr + 7);
|
||||
} else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) {
|
||||
size_t i;
|
||||
for (i = 0; i < v->len - 13; i++) {
|
||||
if (memcmp(&v->ptr[i], "access_token=", 13) == 0) {
|
||||
const char *p2 = v->ptr + i + 13, *p3 = p2;
|
||||
while (p2 < &v->ptr[v->len] && p2[0] != ';' && p2[0] != ' ') p2++;
|
||||
snprintf(pass, passlen, "%.*s", (int) (p2 - p3), p3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12));
|
||||
if (t.len > 0) snprintf(pass, passlen, "%.*s", (int) t.len, t.ptr);
|
||||
} else {
|
||||
mg_http_get_var(&hm->query, "access_token", pass, passlen);
|
||||
}
|
||||
}
|
||||
|
||||
static struct mg_str stripquotes(struct mg_str s) {
|
||||
return s.len > 1 && s.ptr[0] == '"' && s.ptr[s.len - 1] == '"'
|
||||
? mg_str_n(s.ptr + 1, s.len - 2)
|
||||
: s;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
return mg_str_n(NULL, 0);
|
||||
}
|
||||
|
||||
bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
|
||||
return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ struct mg_http_serve_opts {
|
||||
struct mg_http_part {
|
||||
struct mg_str name; // Form field name
|
||||
struct mg_str filename; // Filename for file uploads
|
||||
struct mg_str part; // Part contents
|
||||
struct mg_str body; // Part contents
|
||||
};
|
||||
|
||||
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
|
||||
@ -60,4 +60,5 @@ bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
|
||||
int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
|
||||
const char *dir);
|
||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||
int mg_http_next_multipart(struct mg_str, int, struct mg_http_part *);
|
||||
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
|
||||
size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
|
||||
|
@ -1258,9 +1258,37 @@ static void test_http_chunked(void) {
|
||||
ASSERT(mgr.conns == NULL);
|
||||
}
|
||||
|
||||
static void test_multipart(void) {
|
||||
struct mg_http_part part;
|
||||
size_t ofs;
|
||||
const char *s =
|
||||
"--xyz\r\n"
|
||||
"Content-Disposition: form-data; name=\"val\"\r\n"
|
||||
"\r\n"
|
||||
"abcdef\r\n"
|
||||
"--xyz\r\n"
|
||||
"Content-Disposition: form-data; name=\"foo\"; filename=\"a.txt\"\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"\r\n"
|
||||
"hello world\r\n"
|
||||
"\r\n"
|
||||
"--xyz--\r\n";
|
||||
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);
|
||||
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);
|
||||
ASSERT(mg_http_next_multipart(mg_str(s), ofs, &part) == 0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
mg_log_set("3");
|
||||
test_crc32();
|
||||
test_multipart();
|
||||
test_http_chunked();
|
||||
test_http_parse();
|
||||
test_util();
|
||||
|
Loading…
x
Reference in New Issue
Block a user