Change mg_http_upload API

This commit is contained in:
Sergey Lyubka 2022-06-30 21:54:50 +01:00
parent 56412193e7
commit 99f0688377
10 changed files with 135 additions and 56 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);
};

View File

@ -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>

View File

@ -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, &current_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

View File

@ -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 *);

View File

@ -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, &current_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;

View File

@ -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 *);

View File

@ -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;
}

View File

@ -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));