mirror of
https://github.com/cesanta/mongoose.git
synced 2024-11-24 02:59:01 +08:00
Change mg_http_upload API
This commit is contained in:
parent
56412193e7
commit
99f0688377
@ -1337,6 +1337,51 @@ A diagram below shows how `mg_http_next_multipart()` in action:
|
||||
|
||||
<img src="images/mg_http_next_multipart.svg" alt="Function mg_http_next_multipart()" />
|
||||
|
||||
### mg\_http\_upload()
|
||||
|
||||
```c
|
||||
|
||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *path, size_t max_size);
|
||||
```
|
||||
|
||||
This is a helper utility function that is used to upload large files by small
|
||||
chunks.
|
||||
|
||||
Append HTTP POST data to a file in a specified directory. A file name and
|
||||
file offset are specified by the query string parameters:
|
||||
`POST /upload?name=firmware.bin&offset=2048 HTTP/1.1`. If the offset is 0, then the
|
||||
file is truncated. It is a client's responsibility to divide a file into a
|
||||
smaller chunks and send a sequence of POST requests that will be handled by
|
||||
this function.
|
||||
|
||||
Parameters:
|
||||
- `c`- a connection
|
||||
- `hm` - a parsed HTTP message
|
||||
- `fs` - a filesystem where to write a file, e.g. `&mg_fs_posix`
|
||||
- `path` - a filename
|
||||
- `max_size` - a maximum allowed file size
|
||||
|
||||
Return value: file size after write, or negative number on error
|
||||
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
static void fn(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")) {
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "/tmp/myfile.bin", 99999);
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = {.root_dir = "."}; // Serve
|
||||
mg_http_serve_dir(c, ev_data, &opts); // static content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Websocket
|
||||
|
||||
### struct mg\_ws\_message
|
||||
|
@ -10,7 +10,14 @@ 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")) {
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "/tmp");
|
||||
char path[80], name[64];
|
||||
mg_http_get_var(&hm->query, "name", name, sizeof(name));
|
||||
if (name[0] == '\0') {
|
||||
mg_http_reply(c, 400, "", "%s", "name required");
|
||||
} else {
|
||||
mg_snprintf(path, sizeof(path), "/tmp/%s", name);
|
||||
mg_http_upload(c, hm, &mg_fs_posix, mg_remove_double_dots(path), 99999);
|
||||
}
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
|
@ -18,12 +18,19 @@ var sendFileData = function(name, data, chunkSize) {
|
||||
var chunk = data.subarray(offset, offset + chunkSize) || '';
|
||||
var opts = {method: 'POST', body: chunk};
|
||||
var url = '/upload?offset=' + offset + '&name=' + encodeURIComponent(name);
|
||||
var ok;
|
||||
setStatus(
|
||||
'sending bytes ' + offset + '..' + (offset + chunk.length) + ' of ' +
|
||||
data.length);
|
||||
fetch(url, opts).then(function(res) {
|
||||
if (chunk.length > 0) sendChunk(offset + chunk.length);
|
||||
});
|
||||
'Upoading ' + name + ', bytes ' + offset + '..' +
|
||||
(offset + chunk.length) + ' of ' + data.length);
|
||||
fetch(url, opts)
|
||||
.then(function(res) {
|
||||
if (res.ok && chunk.length > 0) sendChunk(offset + chunk.length);
|
||||
ok = res.ok;
|
||||
return res.text();
|
||||
})
|
||||
.then(function(text) {
|
||||
if (!ok) setStatus('Error: ' + text);
|
||||
});
|
||||
};
|
||||
sendChunk(0);
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
in small chunks.
|
||||
<br/><br/>
|
||||
In this example, the JavaScript code on this page sends the chosen
|
||||
file in 4K chunks using the <code>/upload</code> endpoint.
|
||||
file in 2Kb chunks using the <code>/upload</code> endpoint.
|
||||
The uploaded file is stored in the <code>/tmp</code> directory by
|
||||
the helper API function <code>mg_http_upload()</code>
|
||||
</div>
|
||||
|
50
mongoose.c
50
mongoose.c
@ -2253,31 +2253,37 @@ void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
if (c->pfn_data != NULL) c->pfn_data = (char *) c->pfn_data - ch.len;
|
||||
}
|
||||
|
||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *dir) {
|
||||
char offset[40] = "", name[200] = "", path[256];
|
||||
int res = 0;
|
||||
mg_http_get_var(&hm->query, "offset", offset, sizeof(offset));
|
||||
mg_http_get_var(&hm->query, "name", name, sizeof(name));
|
||||
if (name[0] == '\0') {
|
||||
mg_http_reply(c, 400, "", "%s", "name required");
|
||||
res = -1;
|
||||
} else if (hm->body.len == 0) {
|
||||
mg_http_reply(c, 200, "", "%d", res); // Nothing to write
|
||||
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *path, size_t max_size) {
|
||||
char buf[20] = "";
|
||||
long res = 0, offset;
|
||||
mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
|
||||
offset = strtol(buf, NULL, 0);
|
||||
if (hm->body.len == 0) {
|
||||
mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
|
||||
} else {
|
||||
struct mg_fd *fd;
|
||||
long oft = strtol(offset, NULL, 0);
|
||||
mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||
mg_remove_double_dots(path);
|
||||
MG_DEBUG(("%d bytes @ %ld [%s]", (int) hm->body.len, oft, path));
|
||||
if (oft == 0) fs->rm(path);
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
|
||||
size_t current_size = 0;
|
||||
MG_DEBUG(("%s -> %d bytes @ %ld", path, (int) hm->body.len, offset));
|
||||
if (offset == 0) fs->rm(path); // If offset if 0, truncate file
|
||||
fs->st(path, ¤t_size, NULL);
|
||||
if (offset < 0) {
|
||||
mg_http_reply(c, 400, "", "offset required");
|
||||
res = -1;
|
||||
} else if (offset > 0 && current_size != (size_t) offset) {
|
||||
mg_http_reply(c, 400, "", "%s: offset mismatch", path);
|
||||
res = -2;
|
||||
} else if ((size_t) offset + hm->body.len > max_size) {
|
||||
mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
|
||||
(unsigned long) max_size);
|
||||
res = -3;
|
||||
} else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
|
||||
res = -4;
|
||||
} else {
|
||||
res = (int) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
|
||||
res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
|
||||
mg_fs_close(fd);
|
||||
mg_http_reply(c, 200, "", "%d", res);
|
||||
mg_http_reply(c, 200, "", "%ld", res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@ -4795,7 +4801,7 @@ int64_t mg_to64(struct mg_str str) {
|
||||
}
|
||||
|
||||
char *mg_remove_double_dots(char *s) {
|
||||
char *p = s;
|
||||
char *saved = s, *p = s;
|
||||
while (*s != '\0') {
|
||||
*p++ = *s++;
|
||||
if (s[-1] == '/' || s[-1] == '\\') {
|
||||
@ -4812,7 +4818,7 @@ char *mg_remove_double_dots(char *s) {
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
return s;
|
||||
return saved;
|
||||
}
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
|
@ -1107,8 +1107,8 @@ int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
|
||||
size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
|
||||
void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
|
||||
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,
|
||||
struct mg_fs *fs, const char *dir);
|
||||
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *path, size_t max_size);
|
||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||
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 *);
|
||||
|
46
src/http.c
46
src/http.c
@ -838,31 +838,37 @@ void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
if (c->pfn_data != NULL) c->pfn_data = (char *) c->pfn_data - ch.len;
|
||||
}
|
||||
|
||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *dir) {
|
||||
char offset[40] = "", name[200] = "", path[256];
|
||||
int res = 0;
|
||||
mg_http_get_var(&hm->query, "offset", offset, sizeof(offset));
|
||||
mg_http_get_var(&hm->query, "name", name, sizeof(name));
|
||||
if (name[0] == '\0') {
|
||||
mg_http_reply(c, 400, "", "%s", "name required");
|
||||
res = -1;
|
||||
} else if (hm->body.len == 0) {
|
||||
mg_http_reply(c, 200, "", "%d", res); // Nothing to write
|
||||
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *path, size_t max_size) {
|
||||
char buf[20] = "";
|
||||
long res = 0, offset;
|
||||
mg_http_get_var(&hm->query, "offset", buf, sizeof(buf));
|
||||
offset = strtol(buf, NULL, 0);
|
||||
if (hm->body.len == 0) {
|
||||
mg_http_reply(c, 200, "", "%ld", res); // Nothing to write
|
||||
} else {
|
||||
struct mg_fd *fd;
|
||||
long oft = strtol(offset, NULL, 0);
|
||||
mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||
mg_remove_double_dots(path);
|
||||
MG_DEBUG(("%d bytes @ %ld [%s]", (int) hm->body.len, oft, path));
|
||||
if (oft == 0) fs->rm(path);
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
|
||||
size_t current_size = 0;
|
||||
MG_DEBUG(("%s -> %d bytes @ %ld", path, (int) hm->body.len, offset));
|
||||
if (offset == 0) fs->rm(path); // If offset if 0, truncate file
|
||||
fs->st(path, ¤t_size, NULL);
|
||||
if (offset < 0) {
|
||||
mg_http_reply(c, 400, "", "offset required");
|
||||
res = -1;
|
||||
} else if (offset > 0 && current_size != (size_t) offset) {
|
||||
mg_http_reply(c, 400, "", "%s: offset mismatch", path);
|
||||
res = -2;
|
||||
} else if ((size_t) offset + hm->body.len > max_size) {
|
||||
mg_http_reply(c, 400, "", "%s: over max size of %lu", path,
|
||||
(unsigned long) max_size);
|
||||
res = -3;
|
||||
} else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
|
||||
res = -4;
|
||||
} else {
|
||||
res = (int) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
|
||||
res = offset + (long) fs->wr(fd->fd, hm->body.ptr, hm->body.len);
|
||||
mg_fs_close(fd);
|
||||
mg_http_reply(c, 200, "", "%d", res);
|
||||
mg_http_reply(c, 200, "", "%ld", res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
@ -59,8 +59,8 @@ int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
|
||||
size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
|
||||
void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
|
||||
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,
|
||||
struct mg_fs *fs, const char *dir);
|
||||
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
struct mg_fs *fs, const char *path, size_t max_size);
|
||||
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
|
||||
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 *);
|
||||
|
@ -223,7 +223,7 @@ int64_t mg_to64(struct mg_str str) {
|
||||
}
|
||||
|
||||
char *mg_remove_double_dots(char *s) {
|
||||
char *p = s;
|
||||
char *saved = s, *p = s;
|
||||
while (*s != '\0') {
|
||||
*p++ = *s++;
|
||||
if (s[-1] == '/' || s[-1] == '\\') {
|
||||
@ -240,5 +240,5 @@ char *mg_remove_double_dots(char *s) {
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
return s;
|
||||
return saved;
|
||||
}
|
||||
|
@ -465,7 +465,15 @@ static void eh1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
|
||||
mg_http_reply(c, 200, "", "[%s]:[%s]", user, pass);
|
||||
} else if (mg_http_match_uri(hm, "/upload")) {
|
||||
mg_http_upload(c, hm, &mg_fs_posix, ".");
|
||||
char path[80], name[64];
|
||||
mg_http_get_var(&hm->query, "name", name, sizeof(name));
|
||||
if (name[0] == '\0') {
|
||||
mg_http_reply(c, 400, "", "%s", "name required");
|
||||
} else {
|
||||
mg_snprintf(path, sizeof(path), "./%s", name);
|
||||
mg_http_upload(c, hm, &mg_fs_posix, mg_remove_double_dots(path), 99999);
|
||||
c->is_hexdumping = 1;
|
||||
}
|
||||
} else if (mg_http_match_uri(hm, "/test/")) {
|
||||
struct mg_http_serve_opts sopts;
|
||||
memset(&sopts, 0, sizeof(sopts));
|
||||
|
Loading…
Reference in New Issue
Block a user