mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-20 11:19:56 +00:00
move to new skeleton
This commit is contained in:
1 parent
ea4986dec5
commit
fc0364b321
28 files changed
+9
-9
No files matched your search
@@ -0,0 +1,25 @@
|
||||
PROG ?= example # Program we are building
|
||||
DELETE = rm -rf # Command to remove files
|
||||
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||
SOURCES = main.c mongoose.c # Source code files
|
||||
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||
|
||||
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES
|
||||
|
||||
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
|
||||
PROG ?= example.exe # Use .exe suffix for the binary
|
||||
CC = gcc # Use MinGW gcc compiler
|
||||
CFLAGS += -lws2_32 # Link against Winsock library
|
||||
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
|
||||
OUT ?= -o $(PROG) # Build output
|
||||
endif
|
||||
|
||||
all: $(PROG) # Default target. Build and run program
|
||||
$(RUN) ./$(PROG) $(ARGS)
|
||||
|
||||
$(PROG): $(SOURCES) # Build program from sources
|
||||
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
|
||||
|
||||
clean: # Cleanup. Delete built program and all build artifacts
|
||||
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
|
||||
@@ -0,0 +1 @@
|
||||
See detailed tutorial at https://mongoose.ws/tutorials/http/file-uploads/
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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
|
||||
//
|
||||
// ///////////////// IMPORTANT //////////////////////////
|
||||
//
|
||||
// Mongoose has a limit on input buffer, which also limits maximum upload size.
|
||||
// It is controlled by the MG_MAX_RECV_SIZE constant, which is set by
|
||||
// default to (3 * 1024 * 1024), i.e. 3 megabytes.
|
||||
// Use -DMG_MAX_BUF_SIZE=NEW_LIMIT to override it.
|
||||
//
|
||||
// Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer
|
||||
// increment when reading data.
|
||||
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
|
||||
hm->uri.buf, (unsigned long) hm->body.len));
|
||||
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
|
||||
struct mg_http_part part;
|
||||
size_t ofs = 0;
|
||||
while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) {
|
||||
MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
|
||||
(int) part.name.len, part.name.buf, (int) part.filename.len,
|
||||
part.filename.buf, (unsigned long) part.body.len));
|
||||
}
|
||||
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;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
|
||||
|
||||
for (;;) mg_mgr_poll(&mgr, 50);
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>example</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
|
||||
#info { background: #e0f0f0; border-radius: .5em; padding: 2em; }
|
||||
#wrapper { margin-top: 1em; }
|
||||
form * { margin: 0.2em 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
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 <code>file-updload</code> example, how
|
||||
a big file could be uploaded to a device with little RAM.
|
||||
<br/><br/>
|
||||
In this example, a standard HTML form upload is used, which uses
|
||||
<code>multipart-form-data</code> encoding with
|
||||
several variables and file upload. On a server side, a
|
||||
<code>mg_http_next_multipart()</code> API is used to iterate over
|
||||
all submitted form elements and print their payload.
|
||||
</div>
|
||||
<div id="wrapper">
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<input type="text" name="field1" value="type some text here" /> <br/>
|
||||
<input type="file" name="file1" /> </br>
|
||||
<button type="submit">submit form</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
PROG ?= example # Program we are building
|
||||
DELETE = rm -rf # Command to remove files
|
||||
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||
SOURCES = main.c mongoose.c # Source code files
|
||||
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||
|
||||
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES
|
||||
|
||||
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
|
||||
PROG ?= example.exe # Use .exe suffix for the binary
|
||||
CC = gcc # Use MinGW gcc compiler
|
||||
CFLAGS += -lws2_32 # Link against Winsock library
|
||||
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
|
||||
OUT ?= -o $(PROG) # Build output
|
||||
endif
|
||||
|
||||
all: $(PROG) # Default target. Build and run program
|
||||
$(RUN) ./$(PROG) $(ARGS)
|
||||
|
||||
$(PROG): $(SOURCES) # Build program from sources
|
||||
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
|
||||
|
||||
clean: # Cleanup. Delete built program and all build artifacts
|
||||
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
|
||||
@@ -0,0 +1 @@
|
||||
See detailed tutorial at https://mongoose.ws/tutorials/http/file-uploads/
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2020 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
// HTTP request handler function. It implements the following endpoints:
|
||||
// /upload - Saves the next file chunk
|
||||
// all other URI - serves web_root/ directory
|
||||
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999);
|
||||
} 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;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
|
||||
|
||||
for (;;) mg_mgr_poll(&mgr, 50);
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2020 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
// Helper function to display upload status
|
||||
var setStatus = function(text) {
|
||||
document.getElementById('el3').innerText = text;
|
||||
};
|
||||
|
||||
// When user clicks on a button, trigger file selection dialog
|
||||
var button = document.getElementById('el2');
|
||||
button.onclick = function(ev) {
|
||||
input.click();
|
||||
};
|
||||
|
||||
// Send a large blob of data chunk by chunk
|
||||
var sendFileData = function(name, data, chunkSize) {
|
||||
var sendChunk = function(offset) {
|
||||
var chunk = data.subarray(offset, offset + chunkSize) || '';
|
||||
var opts = {method: 'POST', body: chunk};
|
||||
var url = '/upload?offset=' + offset + '&file=' + encodeURIComponent(name);
|
||||
var ok;
|
||||
setStatus(
|
||||
'Uploading ' + 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);
|
||||
};
|
||||
|
||||
// If user selected a file, read it into memory and trigger sendFileData()
|
||||
var input = document.getElementById('el1');
|
||||
input.onchange = function(ev) {
|
||||
if (!ev.target.files[0]) return;
|
||||
var f = ev.target.files[0], r = new FileReader();
|
||||
r.readAsArrayBuffer(f);
|
||||
r.onload = function() {
|
||||
ev.target.value = '';
|
||||
sendFileData(f.name, new Uint8Array(r.result), 2048);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>example</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
|
||||
#info { background: #e0f0f0; border-radius: .5em; padding: 2em; }
|
||||
#wrapper { margin-top: 1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
Mongoose always buffers a full HTTP message before invoking
|
||||
the MG_EV_HTTP_MSG event. A big POST request would require a lot
|
||||
of RAM to buffer everything. Therefore, in order to upload large
|
||||
files on memory-constrained systems, a large file should be sent
|
||||
in small chunks.
|
||||
<br/><br/>
|
||||
In this example, the JavaScript code on this page sends the chosen
|
||||
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>
|
||||
<div id="wrapper">
|
||||
<input type="file" id="el1" style="display: none"/>
|
||||
<button id="el2">choose file...</button>
|
||||
<div id="el3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="app.js"></script>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
PROG ?= example # Program we are building
|
||||
DELETE = rm -rf # Command to remove files
|
||||
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||
SOURCES = main.c mongoose.c # Source code files
|
||||
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||
|
||||
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES
|
||||
|
||||
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
|
||||
PROG ?= example.exe # Use .exe suffix for the binary
|
||||
CC = gcc # Use MinGW gcc compiler
|
||||
CFLAGS += -lws2_32 # Link against Winsock library
|
||||
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
|
||||
OUT ?= -o $(PROG) # Build output
|
||||
endif
|
||||
|
||||
all: $(PROG) # Default target. Build and run program
|
||||
$(RUN) ./$(PROG) $(ARGS)
|
||||
|
||||
$(PROG): $(SOURCES) # Build program from sources
|
||||
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
|
||||
|
||||
clean: # Cleanup. Delete built program and all build artifacts
|
||||
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
|
||||
@@ -0,0 +1 @@
|
||||
See detailed tutorial at https://mongoose.ws/tutorials/http/file-uploads/
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2020-2024 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Streaming upload example. Demonstrates how to use MG_EV_READ events
|
||||
// to save a large file without buffering it fully in memory.
|
||||
//
|
||||
// curl http://localhost:8000/upload?name=a.txt --data-binary @large_file.txt
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#define UPLOAD_DIR "/tmp"
|
||||
|
||||
struct upload_state {
|
||||
size_t expected; // POST data length, bytes
|
||||
size_t received; // Already received bytes
|
||||
void *fp; // Opened uploaded file
|
||||
};
|
||||
|
||||
static void handle_uploads(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct upload_state *us = (struct upload_state *) c->data;
|
||||
struct mg_fs *fs = &mg_fs_posix;
|
||||
|
||||
// Catch /upload requests early, without buffering whole body
|
||||
// When we receive MG_EV_HTTP_HDRS event, that means we've received all
|
||||
// HTTP headers but not necessarily full HTTP body
|
||||
if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/upload/*"), NULL)) {
|
||||
char path[MG_PATH_MAX];
|
||||
mg_snprintf(path, sizeof(path), "%s/%.*s", UPLOAD_DIR, hm->uri.len - 8,
|
||||
hm->uri.buf + 8);
|
||||
us->expected = hm->body.len; // Store number of bytes we expect
|
||||
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||
c->pfn = NULL; // Silence HTTP protocol handler, we'll use MG_EV_READ
|
||||
if (mg_path_is_sane(mg_str(path))) {
|
||||
fs->rm(path); // Delete file if it exists
|
||||
us->fp = fs->op(path, MG_FS_WRITE); // Open file for writing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Catch uploaded file data for both MG_EV_READ and MG_EV_HTTP_HDRS
|
||||
if (us->expected > 0 && c->recv.len > 0) {
|
||||
us->received += c->recv.len;
|
||||
if (us->fp) fs->wr(us->fp, c->recv.buf, c->recv.len); // Write to file
|
||||
c->recv.len = 0; // Delete received data
|
||||
if (us->received >= us->expected) {
|
||||
// Uploaded everything. Send response back
|
||||
MG_INFO(("Uploaded %lu bytes", us->received));
|
||||
mg_http_reply(c, 200, NULL, "%lu ok\n", us->received);
|
||||
if (us->fp) fs->cl(us->fp); // Close file
|
||||
memset(us, 0, sizeof(*us)); // Cleanup upload state
|
||||
c->is_draining = 1; // Close connection when response gets sent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
handle_uploads(c, ev, ev_data);
|
||||
|
||||
// Non-upload requests, we serve normally
|
||||
// NOTE: handle_uploads() may delete request and reset c->pfn
|
||||
if (ev == MG_EV_HTTP_MSG && c->pfn != NULL) {
|
||||
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;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
||||
mg_http_listen(&mgr, "http://localhost:8000", fn, NULL);
|
||||
|
||||
for (;;) mg_mgr_poll(&mgr, 50);
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>example</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style>
|
||||
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
|
||||
#info { background: #e0f0f0; border-radius: .5em; padding: 2em; }
|
||||
#wrapper { margin-top: 1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
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.
|
||||
<br><br>
|
||||
In order to upload large files to a memory-constrained system, use
|
||||
<code>MG_EV_READ</code> on a server side. It fires when
|
||||
a partial HTTP message has been received. We discourage this method,
|
||||
since the client can send chunked-encoded data. Instead, please
|
||||
split the upload into smaller pieces and send sequentially.
|
||||
<br><br>
|
||||
In this example, JavaScript code uses "fetch()" browser API.
|
||||
Uploaded file is not saved, but rather printed by server side.
|
||||
</div>
|
||||
<div id="wrapper">
|
||||
<input type="file" id="el1" style="display: none"/>
|
||||
<button id="el2">choose file...</button>
|
||||
<span>Selected file:</span> <span id="el4"></span> <br/>
|
||||
<button id="el5" style="margin: 0.5em 0;" disabled>upload file</button>
|
||||
<div id="el3" style="margin-top: 1em;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
var f; // selected file
|
||||
|
||||
// When user clicks on a button, trigger file selection dialog
|
||||
document.getElementById('el2').onclick = function(ev) {
|
||||
document.getElementById('el1').click();
|
||||
};
|
||||
|
||||
document.getElementById('el5').onclick = function(ev) {
|
||||
var r = new FileReader();
|
||||
r.readAsArrayBuffer(f);
|
||||
r.onload = function() {
|
||||
ev.target.value = '';
|
||||
document.getElementById('el3').innerText = 'Uploading...';
|
||||
fetch(`/upload/${encodeURIComponent(f.name)}`, {
|
||||
method: 'POST',
|
||||
body: r.result,
|
||||
}).then(function(res) {
|
||||
document.getElementById('el3').innerText = 'Uploaded ' + r.result.byteLength + ' bytes';
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// If user selected a file, read it into memory and trigger sendFileData()
|
||||
document.getElementById('el1').onchange = function(ev) {
|
||||
f = ev.target.files[0];
|
||||
if (!f) return;
|
||||
document.getElementById('el4').innerText = f.name;
|
||||
document.getElementById('el5').removeAttribute('disabled');
|
||||
};
|
||||
|
||||
</script>
|
||||
</html>
|
||||
Reference in new issue
Block a user