mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b82aa02aa | ||
|
|
e565a7e73d | ||
|
|
59c4dfa33b | ||
|
|
1a8e3c2e31 | ||
|
|
226917e1cd | ||
|
|
fe813ee247 | ||
|
|
4cd830a0bf | ||
|
|
1259ca9ac9 | ||
|
|
d0857fe36c | ||
|
|
56a7438114 | ||
|
|
09df542db8 | ||
|
|
1df102ab93 | ||
|
|
02b3b780bb | ||
|
|
f84f3da012 | ||
|
|
c65c8fdaaa | ||
|
|
b484123f1b | ||
|
|
b60119790e | ||
|
|
216427cea0 | ||
|
|
c53e2fcc14 | ||
|
|
9fd80fd136 | ||
|
|
5fc284722f | ||
|
|
ad6e395da0 | ||
|
|
6c79a406cd | ||
|
|
456e8bd249 | ||
|
|
de95f28598 | ||
|
|
43405afdb2 | ||
|
|
97f95e17db | ||
|
|
6725d61b3f | ||
|
|
ebda2cb100 | ||
|
|
bb72b2494d | ||
|
|
3b0a509cae | ||
|
|
2f981f9383 | ||
|
|
fc3c08e411 | ||
|
|
ff811f287a | ||
|
|
8ebb3bc37b | ||
|
|
f7ff571c5f | ||
|
|
6a82cbee71 | ||
|
|
b0585bd0a9 | ||
|
|
6ad4d242c2 | ||
|
|
030b1d9826 | ||
|
|
f6ffa7b3b9 | ||
|
|
137efe2233 | ||
|
|
62411d039e | ||
|
|
54b1716e77 | ||
|
|
9f7aba8039 | ||
|
|
a176b8d566 | ||
|
|
26a97ec36c | ||
|
|
f29807aad8 | ||
|
|
92d3c7a4cd | ||
|
|
af3a0f6af0 | ||
|
|
ce6164ca20 | ||
|
|
e9fc3fcecf | ||
|
|
25a1a24890 | ||
|
|
bf7fc1d5a9 | ||
|
|
2126b49ab3 | ||
|
|
ae6767b1d2 | ||
|
|
133629dbee | ||
|
|
fb0a9bc7e3 | ||
|
|
2a3492766f | ||
|
|
2ec7e5653e | ||
|
|
30bfb245f5 | ||
|
|
84d89bc4f5 | ||
|
|
7f55fabfec | ||
|
|
9e0f04dc8b | ||
|
|
574c2954b3 |
No files matched your search
@@ -7,8 +7,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install packages
|
||||
run: sudo apt-get install libmbedtls-dev
|
||||
- name: make
|
||||
run: make test test++ IPV6=0
|
||||
- name: gcc
|
||||
run: make test test++ IPV6=0 CC=gcc
|
||||
- name: clang
|
||||
run: make test test++ IPV6=0 CC=clang
|
||||
- name: openssl
|
||||
run: make test SSL=OPENSSL IPV6=0
|
||||
- name: unamalgamated
|
||||
@@ -206,4 +208,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: nxp-twrkv58f220m-lwip-freertos
|
||||
run: make -C examples/nxp/nxp-twrkv58f220m-lwip-freertos build
|
||||
run: make -C examples/nxp/nxp-twrkv58f220m-lwip-freertos build
|
||||
@@ -1,7 +1,7 @@
|
||||
SRCS = mongoose.c test/unit_test.c test/packed_fs.c
|
||||
HDRS = $(wildcard src/*.h)
|
||||
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1
|
||||
WARN ?= -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wmissing-prototypes -Wundef
|
||||
WARN ?= -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
|
||||
OPTS ?= -O3 -g3
|
||||
INCS ?= -Isrc -I.
|
||||
CFLAGS ?= $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) $(EXTRA)
|
||||
@@ -27,7 +27,7 @@ CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
|
||||
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
|
||||
endif
|
||||
|
||||
all: mg_prefix unpacked test test++ arm examples vc98 vc2017 mingw mingw++ linux linux++ fuzz
|
||||
all: mg_prefix unamalgamated unpacked test test++ arm examples vc98 vc2017 mingw mingw++ linux linux++ fuzz
|
||||
|
||||
examples:
|
||||
@for X in $(EXAMPLES); do $(MAKE) -C $$X example || break; done
|
||||
@@ -59,7 +59,7 @@ fuzz: fuzzer
|
||||
$(RUN) ./fuzzer
|
||||
|
||||
# make CC=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
|
||||
test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address#,undefined
|
||||
test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address,undefined
|
||||
test: mongoose.h Makefile $(SRCS)
|
||||
$(CC) $(SRCS) $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test
|
||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
||||
@@ -125,7 +125,7 @@ mongoose.c: Makefile $(wildcard src/*)
|
||||
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
||||
|
||||
mongoose.h: $(HDRS) Makefile
|
||||
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/config.h src/arch.h src/arch_*.h src/str.h src/log.h src/timer.h src/util.h src/fs.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.o *.dSYM unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack
|
||||
|
||||
@@ -36,7 +36,7 @@ robust, and easy. Features include:
|
||||
- See [Case Studies](https://mongoose.ws/case-studies/) from our respected
|
||||
customers like [Schneider Electric](https://mongoose.ws/case-studies/schneider-electric/) (industrial automation), [Broadcom](https://mongoose.ws/case-studies/broadcom/) (semiconductors), [Pilz](https://mongoose.ws/case-studies/pilz/) (industrial automation), and others
|
||||
- See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products
|
||||
- We provide [commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration
|
||||
- We provide [Evaluation and Commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration
|
||||
assistance - don't hesitate to [contact us](https://mongoose.ws/contact/)
|
||||
|
||||
|
||||
|
||||
+63
-92
@@ -132,24 +132,25 @@ to an event handler:
|
||||
|
||||
```c
|
||||
enum {
|
||||
MG_EV_ERROR, // Error char *error_message
|
||||
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
|
||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||
MG_EV_CONNECT, // Connection established NULL
|
||||
MG_EV_ACCEPT, // Connection accepted NULL
|
||||
MG_EV_READ, // Data received from socket struct mg_str *
|
||||
MG_EV_WRITE, // Data written to socket int *num_bytes_written
|
||||
MG_EV_CLOSE, // Connection closed NULL
|
||||
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
||||
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
||||
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
||||
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
||||
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
|
||||
MG_EV_USER, // Starting ID for user events
|
||||
MG_EV_ERROR, // Error char *error_message
|
||||
MG_EV_OPEN, // Connection created NULL
|
||||
MG_EV_POLL, // mg_mgr_poll iteration int64_t *milliseconds
|
||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||
MG_EV_CONNECT, // Connection established NULL
|
||||
MG_EV_ACCEPT, // Connection accepted NULL
|
||||
MG_EV_READ, // Data received from socket struct mg_str *
|
||||
MG_EV_WRITE, // Data written to socket long *bytes_written
|
||||
MG_EV_CLOSE, // Connection closed NULL
|
||||
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
||||
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
||||
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
||||
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
||||
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||
MG_EV_SNTP_TIME, // SNTP time received int64_t *milliseconds
|
||||
MG_EV_USER, // Starting ID for user events
|
||||
};
|
||||
```
|
||||
|
||||
@@ -162,7 +163,8 @@ that connection is UDP or not. Some flags can be changed by application, for
|
||||
example, the `is_draining` flag, if set by an application, tells Mongoose to send
|
||||
the remaining data to a peer, and when everything is sent, close the connection.
|
||||
|
||||
<span class="badge bg-danger">NOTE: </span>User-changeable flags are: `is_hexdumping`, `is_draining`, `is_closing`.
|
||||
<span class="badge bg-danger me-1"> NOTE: </span>User-changeable flags are:
|
||||
`is_hexdumping`, `is_draining`, `is_closing`.
|
||||
|
||||
This is taken from `mongoose.h` as-is:
|
||||
|
||||
@@ -242,10 +244,12 @@ Here is a list of build constants and their default values:
|
||||
|MG_ENABLE_DIRLIST | 0 | Enable directory listing |
|
||||
|MG_ENABLE_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
|
||||
|MG_ENABLE_PACKED_FS | 0 | Enable embedded FS support |
|
||||
|MG_ENABLE_FATFS | 0 | Enable embedded FAT FS support |
|
||||
|MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth |
|
||||
|MG_MAX_RECV_BUF_SIZE | (3 * 1024 * 1024) | Maximum recv buffer size |
|
||||
|MG_MAX_HTTP_HEADERS | 40 | Maximum number of HTTP headers |
|
||||
|MG_HTTP_INDEX | "index.html" | Index file for HTML directory |
|
||||
|MG_FATFS_ROOT | "/" | FAT FS root directory |
|
||||
|MG_ENABLE_LINES | undefined | If defined, show source file names in logs |
|
||||
|
||||
<span class="badge bg-danger">NOTE:</span> the `MG_IO_SIZE` constant also sets
|
||||
@@ -626,13 +630,13 @@ void foo(struct mg_connection *c, const char *fmt, ...) {
|
||||
### mg\_straddr
|
||||
|
||||
```c
|
||||
char *mg_straddr(struct mg_connection *c, char *buf, size_t len);
|
||||
char *mg_straddr(struct mg_addr *addr, char *buf, size_t len);
|
||||
```
|
||||
|
||||
Write stringified IP address, associated with given connection to `buf` (maximum size `len`)
|
||||
|
||||
Parameters:
|
||||
- `c` - A connection pointer
|
||||
- `addr` - A address pointer
|
||||
- `buf` - A pointer to a buffer that will hold stringified address
|
||||
- `len` - A buffer size
|
||||
|
||||
@@ -641,8 +645,8 @@ Return value: `buf` value
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
char buf[1024];
|
||||
mg_straddr(c, buf, sizeof(buf)); // `buf` is now IP address string, like "127.0.0.1:8080"
|
||||
char buf[100];
|
||||
LOG(LL_INFO, ("%s", mg_straddr(&c->peer, buf, sizeof(buf))));
|
||||
```
|
||||
|
||||
### mg\_mkpipe()
|
||||
@@ -683,14 +687,17 @@ Usage example: see [examples/multi-threaded](https://github.com/cesanta/mongoose
|
||||
### mg\_mgr\_wakeup()
|
||||
|
||||
```c
|
||||
void mg_mgr_wakeup(struct mg_connection *pipe);
|
||||
void mg_mgr_wakeup(struct mg_connection *pipe, const void *buf, size_len len);
|
||||
```
|
||||
|
||||
Wake up an event manager that sleeps in `mg_mgr_poll()` call. This function
|
||||
must be called from a separate task/thread. Parameters:
|
||||
must be called from a separate task/thread. A calling thread can pass
|
||||
some specific data to the IO thread via `buf`, `len`. Parameters:
|
||||
|
||||
Parameters:
|
||||
- `pipe` - a special connection created by the `mg_mkpipe()` call
|
||||
- `buf` - a data to send to the pipe connection. Use `""` if there is no data
|
||||
- `len` - a data length
|
||||
|
||||
Return value: None
|
||||
|
||||
@@ -1173,7 +1180,7 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
|
||||
```c
|
||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
const char *dir);
|
||||
struct mg_fs *fs, const char *dir);
|
||||
```
|
||||
|
||||
Handle file upload. See [file upload example](https://github.com/cesanta/mongoose/tree/master/examples/file-uploads/).
|
||||
@@ -1208,6 +1215,7 @@ The expected usage of this API function follows:
|
||||
Parameters:
|
||||
- `c` - Connection to use
|
||||
- `hm` - POST message, containing parameters described above
|
||||
- `fs` - Filesystem to use
|
||||
- `dir` - Path to directory
|
||||
|
||||
Return value: Request body length or negative value on error
|
||||
@@ -1219,7 +1227,7 @@ Usage example:
|
||||
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;
|
||||
mg_http_upload(c, hm, "."); // Upload to current folder
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "."); // Upload to current folder
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1496,7 +1504,7 @@ struct mg_mqtt_opts {
|
||||
struct mg_str client_id; // Client id
|
||||
struct mg_str will_topic; // Will Topic
|
||||
struct mg_str will_message; // Will Message
|
||||
uint8_t qos; // Quality of service
|
||||
uint8_t will_qos; // Will message quality of service
|
||||
bool will_retain; // Retain last will
|
||||
bool clean; // Use clean session, 0 or 1
|
||||
uint16_t keepalive; // Keep-alive timer in seconds
|
||||
@@ -1614,8 +1622,8 @@ void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
||||
### mg\_mqtt\_pub()
|
||||
|
||||
```c
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
|
||||
struct mg_str *data, int qos, bool retain);
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
|
||||
struct mg_str data, int qos, bool retain);
|
||||
```
|
||||
|
||||
Publish message.
|
||||
@@ -1632,15 +1640,13 @@ Return value: None
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
struct mg_str topic = mg_str("topic");
|
||||
struct mg_str data = mg_str("data");
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
mg_mqtt_pub(c, mg_str("topic"), mg_str("my data"), 1, false);
|
||||
```
|
||||
|
||||
### mg\_mqtt\_sub()
|
||||
|
||||
```c
|
||||
void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic, int qos);
|
||||
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos);
|
||||
```
|
||||
|
||||
Subscribe to topic.
|
||||
@@ -1653,8 +1659,7 @@ Parameters:
|
||||
Return value: none
|
||||
|
||||
```c
|
||||
struct mg_str topic = mg_str("topic");
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
mg_mqtt_sub(c, mg_str("my/topic"), 1);
|
||||
```
|
||||
|
||||
### mg\_mqtt\_next\_sub()
|
||||
@@ -1848,6 +1853,7 @@ struct mg_tls_opts {
|
||||
const char *certkey; // Certificate key
|
||||
const char *ciphers; // Cipher list
|
||||
struct mg_str srvname; // If not empty, enables server name verification
|
||||
struct mg_fs *fs; // FS API for reading certificate files
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1902,16 +1908,15 @@ mg_tls_init(c, &opts);
|
||||
|
||||
```c
|
||||
struct mg_timer {
|
||||
int period_ms; // Timer period in milliseconds
|
||||
int flags; // Possible flags values below
|
||||
void (*fn)(void *); // Function to call
|
||||
void *arg; // Function argument
|
||||
unsigned long expire; // Expiration timestamp in milliseconds
|
||||
struct mg_timer *next; // Linkage in g_timers list
|
||||
};
|
||||
|
||||
int64_t period_ms; // Timer period in milliseconds
|
||||
int64_t expire; // Expiration timestamp in milliseconds
|
||||
unsigned flags; // Possible flags values below
|
||||
#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once
|
||||
#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
|
||||
void (*fn)(void *); // Function to call
|
||||
void *arg; // Function argument
|
||||
struct mg_timer *next; // Linkage in g_timers list
|
||||
};
|
||||
```
|
||||
|
||||
Timer structure. Describes a software timer. Timer granularity is the same
|
||||
@@ -1920,7 +1925,7 @@ as the `mg_mgr_poll()` timeout argument in the main event loop.
|
||||
### mg\_timer\_init()
|
||||
|
||||
```c
|
||||
void mg_timer_init(struct mg_timer *t, unsigned long ms, unsigned flags,
|
||||
void mg_timer_init(struct mg_timer *t, int64_t period_ms, unsigned flags,
|
||||
void (*fn)(void *), void *fn_data);
|
||||
```
|
||||
|
||||
@@ -1968,7 +1973,7 @@ mg_timer_free(&timer);
|
||||
### mg\_timer\_poll()
|
||||
|
||||
```c
|
||||
void mg_timer_poll(unsigned long uptime_ms);
|
||||
void mg_timer_poll(int64_t uptime_ms);
|
||||
```
|
||||
|
||||
Traverse list of timers and call them if current timestamp `uptime_ms` is
|
||||
@@ -1985,34 +1990,16 @@ Return value: None
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
unsigned long now = mg_millis();
|
||||
int64_t now = mg_millis();
|
||||
mg_timer_poll(now);
|
||||
```
|
||||
|
||||
## Time
|
||||
|
||||
### mg\_time()
|
||||
|
||||
```
|
||||
double mg_time(void);
|
||||
```
|
||||
|
||||
Return current time as UNIX epoch, using `double` value for sub-second accuracy.
|
||||
|
||||
Parameters: None
|
||||
|
||||
Return value: Current time
|
||||
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
double now = mg_time()
|
||||
```
|
||||
|
||||
### mg\_millis()
|
||||
|
||||
```c
|
||||
unsigned long mg_millis(void);
|
||||
int64_t mg_millis(void);
|
||||
```
|
||||
|
||||
Return current uptime in milliseconds.
|
||||
@@ -2024,26 +2011,7 @@ Return value: Current uptime
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
unsigned long uptime = mg_millis();
|
||||
```
|
||||
|
||||
### mg\_usleep()
|
||||
|
||||
```c
|
||||
void mg_usleep(unsigned long usecs);
|
||||
```
|
||||
|
||||
Block thread execution for a given number of microseconds.
|
||||
|
||||
Parameters:
|
||||
- `usecs` - number of microseconds to block thread
|
||||
|
||||
Return value: None
|
||||
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
mg_usleep(1000000 /* 1 sec */)
|
||||
int64_t uptime = mg_millis();
|
||||
```
|
||||
|
||||
## String
|
||||
@@ -2874,7 +2842,7 @@ mg_base64_decode("Q2VzYW50YQ==", 12, buf); // buf is now "Cesanta"
|
||||
### mg\_file\_read()
|
||||
|
||||
```c
|
||||
char *mg_file_read(const char *path, size_t *sizep);
|
||||
char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep);
|
||||
```
|
||||
|
||||
Read file contents into a nul-terminated malloc-ed string. It is a caller's
|
||||
@@ -2882,6 +2850,7 @@ responsibility to free() a returned pointer. If `sizep` is not NULL, it will
|
||||
return a file size in bytes. Return `NULL` on error.
|
||||
|
||||
Parameters:
|
||||
- `fs` - Filesystem to use
|
||||
- `path` - Path to file to read
|
||||
- `sizep` - Pointer to `size_t` to receive file size
|
||||
|
||||
@@ -2891,7 +2860,7 @@ Usage example:
|
||||
|
||||
```c
|
||||
size_t file_size;
|
||||
char* data = mg_file_read("myfile.txt", &file_size);
|
||||
char* data = mg_file_read(&mg_fs_posix, "myfile.txt", &file_size);
|
||||
if (data != NULL) {
|
||||
// `data` is now pointer to information readen from file and `file_size` is it size.
|
||||
}
|
||||
@@ -2901,12 +2870,13 @@ free(data);
|
||||
### mg\_file\_write()
|
||||
|
||||
```c
|
||||
bool mg_file_write(const char *path, const void *buf, size_t len);
|
||||
bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, size_t len);
|
||||
```
|
||||
Write data to a file.
|
||||
The write is atomic, i.e. data gets written to a temporary file first, then `rename()-ed` to a destination file name.
|
||||
|
||||
Parameters:
|
||||
- `fs` - Filesystem to use
|
||||
- `path` - Path to file
|
||||
- `buf` - Data to write
|
||||
- `len` - Data length
|
||||
@@ -2917,7 +2887,7 @@ Usage example:
|
||||
|
||||
```c
|
||||
char data[] = "Hello, world!";
|
||||
if(mg_file_write("my_file.txt", data, sizeof(data) - 1)) {
|
||||
if(mg_file_write(&mg_fs_posix, "my_file.txt", data, sizeof(data) - 1)) {
|
||||
// File contains "Hello, world!" string
|
||||
}
|
||||
```
|
||||
@@ -2925,7 +2895,7 @@ if(mg_file_write("my_file.txt", data, sizeof(data) - 1)) {
|
||||
### mg\_file\_printf()
|
||||
|
||||
```c
|
||||
int mg_file_printf(const char *path, const char *fmt, ...);
|
||||
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
|
||||
```
|
||||
|
||||
Write into a file `path` using `printf()` semantics.
|
||||
@@ -2933,13 +2903,14 @@ This function prints data to a
|
||||
temporary in-memory buffer first, then calls `mg_file_write()`.
|
||||
|
||||
Parameters:
|
||||
- `fs` - Filesystem to use
|
||||
- `path`- path to file
|
||||
- `fmt` - Format string in `printf()` semantics
|
||||
|
||||
Return value: `true` on success, `false` otherwise
|
||||
|
||||
```c
|
||||
if (mg_file_printf("my_file.txt", "Hello, %s!", "world") {
|
||||
if (mg_file_printf(&mg_fs_posix, "my_file.txt", "Hello, %s!", "world") {
|
||||
// File contains "Hello, world!" string
|
||||
}
|
||||
```
|
||||
|
||||
@@ -147,7 +147,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
||||
static size_t i;
|
||||
const char *path = files[i++ % nfiles];
|
||||
size_t size = 0;
|
||||
char *data = mg_file_read(path, &size); // Read next file
|
||||
char *data = mg_file_read(&mg_fs_posix, path, &size); // Read next file
|
||||
struct mg_connection *c;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||
|
||||
@@ -40,7 +40,7 @@ void app_main(void) {
|
||||
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
||||
int res = esp_vfs_spiffs_register(&conf);
|
||||
LOG(res == ESP_OK ? LL_INFO : LL_ERROR, ("FS %s, %d", conf.base_path, res));
|
||||
mg_file_printf(FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||
mg_file_printf(&mg_fs_posix, FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||
|
||||
// Setup wifi. This function is implemented in wifi.c
|
||||
// It blocks until connected to the configured WiFi network
|
||||
|
||||
@@ -10,10 +10,10 @@ 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, "/tmp");
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "/tmp");
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
mg_http_serve_dir(c, hm, &opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,13 @@ static void forward_request(struct mg_http_message *hm,
|
||||
}
|
||||
|
||||
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_READ && fn_data != NULL) {
|
||||
struct mg_connection *c2 = fn_data;
|
||||
if (ev == MG_EV_READ) {
|
||||
// All incoming data from the backend, forward to the client
|
||||
mg_send((struct mg_connection *) fn_data, c->recv.buf, c->recv.len);
|
||||
if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len);
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
if (c2 != NULL) c2->fn_data = NULL;
|
||||
}
|
||||
(void) ev_data;
|
||||
}
|
||||
@@ -57,11 +60,11 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
}
|
||||
c->fn_data = c2;
|
||||
forward_request(hm, c2);
|
||||
// c2->is_hexdumping = 1;
|
||||
c2->is_hexdumping = 1;
|
||||
}
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
if (c2 != NULL) c2->is_closing = 1;
|
||||
c->fn_data = NULL;
|
||||
if (c2 != NULL) c2->fn_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_debug_level = "2";
|
||||
static const char *s_root_dir = "."; // Attention: avoid double-dots, `..` !
|
||||
static const char *s_root_dir = NULL;
|
||||
static const char *s_listening_address = "http://localhost:8000";
|
||||
static const char *s_enable_hexdump = "no";
|
||||
static const char *s_ssi_pattern = "#.html";
|
||||
|
||||
// Handle interrupts, like Ctrl-C
|
||||
static int s_signo;
|
||||
@@ -19,9 +20,18 @@ static void signal_handler(int signo) {
|
||||
// Simply serve static files from `s_root_dir`
|
||||
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 = ev_data, tmp = {};
|
||||
struct mg_str unknown = mg_str_n("?", 1), *cl;
|
||||
struct mg_http_serve_opts opts = {0};
|
||||
opts.root_dir = s_root_dir;
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
opts.ssi_pattern = s_ssi_pattern;
|
||||
mg_http_serve_dir(c, hm, &opts);
|
||||
mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
|
||||
cl = mg_http_get_header(&tmp, "Content-Length");
|
||||
if (cl == NULL) cl = &unknown;
|
||||
LOG(LL_INFO, ("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr,
|
||||
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len,
|
||||
tmp.uri.ptr, (int) cl->len, cl->ptr));
|
||||
}
|
||||
(void) fn_data;
|
||||
}
|
||||
@@ -31,15 +41,18 @@ static void usage(const char *prog) {
|
||||
"Mongoose v.%s\n"
|
||||
"Usage: %s OPTIONS\n"
|
||||
" -H yes|no - enable traffic hexdump, default: '%s'\n"
|
||||
" -S PAT - SSI filename pattern, default: '%s'\n"
|
||||
" -d DIR - directory to serve, default: '%s'\n"
|
||||
" -l ADDR - listening address, default: '%s'\n"
|
||||
" -v LEVEL - debug level, from 0 to 4, default: '%s'\n",
|
||||
MG_VERSION, prog, s_enable_hexdump, s_root_dir, s_listening_address,
|
||||
s_debug_level);
|
||||
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
|
||||
s_listening_address, s_debug_level);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char path[MG_PATH_MAX] = ".";
|
||||
const char *root_dir = ".";
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *c;
|
||||
int i;
|
||||
@@ -47,9 +60,11 @@ int main(int argc, char *argv[]) {
|
||||
// Parse command-line flags
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-d") == 0) {
|
||||
s_root_dir = argv[++i];
|
||||
root_dir = argv[++i];
|
||||
} else if (strcmp(argv[i], "-H") == 0) {
|
||||
s_enable_hexdump = argv[++i];
|
||||
} else if (strcmp(argv[i], "-S") == 0) {
|
||||
s_ssi_pattern = argv[++i];
|
||||
} else if (strcmp(argv[i], "-l") == 0) {
|
||||
s_listening_address = argv[++i];
|
||||
} else if (strcmp(argv[i], "-v") == 0) {
|
||||
@@ -59,6 +74,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Root directory must not contain double dots. Make it absolute
|
||||
realpath(root_dir, path);
|
||||
s_root_dir = path;
|
||||
|
||||
// Initialise stuff
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
PROG ?= example
|
||||
CFLAGS ?= -W -Wall -Wextra -O0 -g
|
||||
|
||||
all: $(PROG)
|
||||
$(RUN) ./$(PROG) $(ARGS)
|
||||
|
||||
$(PROG): main.c
|
||||
$(CC) ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 $(CFLAGS) -o $(PROG) main.c mjson.c
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2021 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Example that demonstrates how to send a large responses with limited memory.
|
||||
// We're going to send a JSON array of many integer values, s_data.
|
||||
// The idea is to send a response in small chunks, and let the client request
|
||||
// the next chunk.
|
||||
// Periodically, s_data changes, which is tracked by s_version.
|
||||
// Client requests a range and a version, to ensure data integrity.
|
||||
//
|
||||
// 1. Start this server, type `make`
|
||||
// 2. Open http://localhost:8000 in your browser
|
||||
|
||||
#include "mjson.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_listen_on = "http://localhost:8000";
|
||||
static const char *s_root_dir = "web_root";
|
||||
|
||||
#define DATA_SIZE 10000 // Total number of elements
|
||||
#define CHUNK_SIZE 1000 // Max number returned in one API call
|
||||
static int s_data[DATA_SIZE]; // Simulate some complex big data
|
||||
static unsigned s_version = 0; // Data "version"
|
||||
|
||||
static double getparam(struct mg_http_message *hm, const char *json_path) {
|
||||
double dv = 0;
|
||||
mjson_get_number(hm->body.ptr, hm->body.len, json_path, &dv);
|
||||
return dv;
|
||||
}
|
||||
|
||||
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 = ev_data;
|
||||
if (mg_http_match_uri(hm, "/api/data")) {
|
||||
unsigned start = getparam(hm, "$.start");
|
||||
unsigned version = getparam(hm, "$.version");
|
||||
char *response = NULL;
|
||||
if (version > 0 && version != s_version) {
|
||||
// Version mismatch: s_data has changed while client fetches it
|
||||
// Tell client to restart
|
||||
response = mjson_aprintf("{%Q:%Q, %Q:%u}", "error", "wrong version",
|
||||
"version", version);
|
||||
} else {
|
||||
// Return data, up to CHUNK_SIZE elements
|
||||
unsigned max = start + CHUNK_SIZE;
|
||||
const char *comma = "";
|
||||
if (max > DATA_SIZE) max = DATA_SIZE;
|
||||
mjson_printf(mjson_print_dynamic_buf, &response, "{%Q:%u,%Q:%u,%Q:[",
|
||||
"version", s_version, "start", start, "data");
|
||||
while (start < max) {
|
||||
mjson_printf(mjson_print_dynamic_buf, &response, "%s%d", comma,
|
||||
s_data[start]);
|
||||
comma = ",";
|
||||
start++;
|
||||
}
|
||||
mjson_printf(mjson_print_dynamic_buf, &response, "]}");
|
||||
}
|
||||
mg_http_reply(c, 200, "Content-Type: text/json\r\n", "%s", response);
|
||||
free(response);
|
||||
} else {
|
||||
struct mg_http_serve_opts opts = {0};
|
||||
opts.root_dir = s_root_dir;
|
||||
mg_http_serve_dir(c, hm, &opts);
|
||||
}
|
||||
}
|
||||
(void) fn_data;
|
||||
}
|
||||
|
||||
static void timer_fn(void *arg) {
|
||||
for (int i = 0; i < DATA_SIZE; i++) {
|
||||
s_data[i] = rand();
|
||||
}
|
||||
s_version++;
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_timer t1;
|
||||
mg_mgr_init(&mgr);
|
||||
srand(time(NULL));
|
||||
mg_timer_init(&t1, 1000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, NULL);
|
||||
mg_http_listen(&mgr, s_listen_on, fn, NULL);
|
||||
LOG(LL_INFO, ("Listening on %s", s_listen_on));
|
||||
for (;;) mg_mgr_poll(&mgr, 1000);
|
||||
mg_timer_free(&t1);
|
||||
mg_mgr_free(&mgr);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2018-2020 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef MJSON_H
|
||||
#define MJSON_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef MJSON_ENABLE_PRINT
|
||||
#define MJSON_ENABLE_PRINT 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_ENABLE_RPC
|
||||
#define MJSON_ENABLE_RPC 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_ENABLE_BASE64
|
||||
#define MJSON_ENABLE_BASE64 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_ENABLE_MERGE
|
||||
#define MJSON_ENABLE_MERGE 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_ENABLE_PRETTY
|
||||
#define MJSON_ENABLE_PRETTY 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_ENABLE_NEXT
|
||||
#define MJSON_ENABLE_NEXT 1
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_RPC_LIST_NAME
|
||||
#define MJSON_RPC_LIST_NAME "rpc.list"
|
||||
#endif
|
||||
|
||||
#ifndef MJSON_DYNBUF_CHUNK
|
||||
#define MJSON_DYNBUF_CHUNK 256 // Allocation granularity for print_dynamic_buf
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MJSON_ERROR_INVALID_INPUT (-1)
|
||||
#define MJSON_ERROR_TOO_DEEP (-2)
|
||||
#define MJSON_TOK_INVALID 0
|
||||
#define MJSON_TOK_KEY 1
|
||||
#define MJSON_TOK_STRING 11
|
||||
#define MJSON_TOK_NUMBER 12
|
||||
#define MJSON_TOK_TRUE 13
|
||||
#define MJSON_TOK_FALSE 14
|
||||
#define MJSON_TOK_NULL 15
|
||||
#define MJSON_TOK_ARRAY 91
|
||||
#define MJSON_TOK_OBJECT 123
|
||||
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
|
||||
|
||||
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
|
||||
|
||||
#ifndef MJSON_MAX_DEPTH
|
||||
#define MJSON_MAX_DEPTH 20
|
||||
#endif
|
||||
|
||||
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
|
||||
int mjson_find(const char *s, int len, const char *jp, const char **, int *);
|
||||
int mjson_get_number(const char *s, int len, const char *path, double *v);
|
||||
int mjson_get_bool(const char *s, int len, const char *path, int *v);
|
||||
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
|
||||
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
|
||||
|
||||
#if MJSON_ENABLE_NEXT
|
||||
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
||||
int *vlen, int *vtype);
|
||||
#endif
|
||||
|
||||
#if MJSON_ENABLE_BASE64
|
||||
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
|
||||
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
|
||||
#endif
|
||||
|
||||
#if MJSON_ENABLE_PRINT
|
||||
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
|
||||
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
|
||||
|
||||
struct mjson_fixedbuf {
|
||||
char *ptr;
|
||||
int size, len;
|
||||
};
|
||||
|
||||
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
|
||||
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list *ap);
|
||||
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
|
||||
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
|
||||
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
|
||||
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
|
||||
int mjson_print_dbl(mjson_print_fn_t fn, void *, double, int width);
|
||||
|
||||
int mjson_print_null(const char *ptr, int len, void *userdata);
|
||||
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
|
||||
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
|
||||
|
||||
int mjson_snprintf(char *buf, size_t len, const char *fmt, ...);
|
||||
char *mjson_aprintf(const char *fmt, ...);
|
||||
|
||||
#if MJSON_ENABLE_PRETTY
|
||||
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
|
||||
#endif
|
||||
|
||||
#if MJSON_ENABLE_MERGE
|
||||
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
|
||||
#endif
|
||||
|
||||
#endif // MJSON_ENABLE_PRINT
|
||||
|
||||
#if MJSON_ENABLE_RPC
|
||||
|
||||
void jsonrpc_init(mjson_print_fn_t, void *userdata);
|
||||
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
|
||||
|
||||
struct jsonrpc_request {
|
||||
struct jsonrpc_ctx *ctx;
|
||||
const char *frame; // Points to the whole frame
|
||||
int frame_len; // Frame length
|
||||
const char *params; // Points to the "params" in the request frame
|
||||
int params_len; // Length of the "params"
|
||||
const char *id; // Points to the "id" in the request frame
|
||||
int id_len; // Length of the "id"
|
||||
const char *method; // Points to the "method" in the request frame
|
||||
int method_len; // Length of the "method"
|
||||
mjson_print_fn_t fn; // Printer function
|
||||
void *fndata; // Printer function data
|
||||
void *userdata; // Callback's user data as specified at export time
|
||||
};
|
||||
|
||||
struct jsonrpc_method {
|
||||
const char *method;
|
||||
int method_sz;
|
||||
void (*cb)(struct jsonrpc_request *);
|
||||
struct jsonrpc_method *next;
|
||||
};
|
||||
|
||||
// Main RPC context, stores current request information and a list of
|
||||
// exported RPC methods.
|
||||
struct jsonrpc_ctx {
|
||||
struct jsonrpc_method *methods;
|
||||
mjson_print_fn_t response_cb;
|
||||
void *response_cb_data;
|
||||
};
|
||||
|
||||
// Registers function fn under the given name within the given RPC context
|
||||
#define jsonrpc_ctx_export(ctx, name, fn) \
|
||||
do { \
|
||||
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0}; \
|
||||
m.next = (ctx)->methods; \
|
||||
(ctx)->methods = &m; \
|
||||
} while (0)
|
||||
|
||||
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
|
||||
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
||||
const char *message, const char *data_fmt, ...);
|
||||
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
||||
...);
|
||||
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
||||
mjson_print_fn_t fn, void *fndata, void *userdata);
|
||||
|
||||
extern struct jsonrpc_ctx jsonrpc_default_context;
|
||||
extern void jsonrpc_list(struct jsonrpc_request *r);
|
||||
|
||||
#define jsonrpc_export(name, fn) \
|
||||
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
|
||||
|
||||
#define jsonrpc_process(buf, len, fn, fnd, ud) \
|
||||
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
|
||||
|
||||
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
|
||||
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
|
||||
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
|
||||
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
|
||||
|
||||
#endif // MJSON_ENABLE_RPC
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // MJSON_H
|
||||
@@ -0,0 +1,59 @@
|
||||
<!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; margin-bottom: 1em; }
|
||||
#result { margin-top: 1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
On devices with limited RAM, it is important to limit the response size
|
||||
of API calls. This example demonstrates how to fetch a large
|
||||
data in smaller chunks, and guarantee its integrity.
|
||||
Data gets returned in a series of request/response transactions,
|
||||
where each response is small enough to fit into available device RAM.
|
||||
<br/><br/>
|
||||
Data integrity is implemented by versioning.
|
||||
The idea is that the first response includes the current "version" of the
|
||||
data, and that version is passed to all subsequent requests.
|
||||
If data version changes in the middle of the request series,
|
||||
client fails with 'wrong version' error.
|
||||
</div>
|
||||
<button id="btn">fetch data</button>
|
||||
<div id="result"></div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
const getchunk = (start, version) =>
|
||||
fetch('/api/data', {method: 'POST', body:JSON.stringify({start, version})})
|
||||
.then(r => r.json());
|
||||
document.getElementById('btn').onclick = function() {
|
||||
var data = [], version = 0;
|
||||
const load = offset => getchunk(offset, version)
|
||||
.then(r => {
|
||||
// console.log(r);
|
||||
if (r.error) {
|
||||
document.getElementById('result').innerText = 'Error: ' + r.error;
|
||||
} else {
|
||||
version = r.version;
|
||||
data = data.concat(r.data);
|
||||
if (r.data.length == 0) {
|
||||
document.getElementById('result').innerText = 'Version: ' +
|
||||
version + ', data: \n' +
|
||||
JSON.stringify(data, null, 2);
|
||||
} else {
|
||||
load(offset + r.data.length, version);
|
||||
}
|
||||
}
|
||||
});
|
||||
load(0);
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
@@ -54,7 +54,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct mg_str topic = mg_str(s_rx_topic);
|
||||
LOG(LL_INFO, ("Connected to %s", s_url));
|
||||
LOG(LL_INFO, ("Subscribing to %s", s_rx_topic));
|
||||
mg_mqtt_sub(c, &topic, s_qos);
|
||||
mg_mqtt_sub(c, topic, s_qos);
|
||||
c->label[0] = 'X'; // Set a label that we're logged in
|
||||
} else if (ev == MG_EV_MQTT_MSG) {
|
||||
// When we receive MQTT message, print it
|
||||
@@ -67,7 +67,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (now_second != prev_second) {
|
||||
struct mg_str topic = mg_str(s_tx_topic), data = mg_str("{\"a\":123}");
|
||||
LOG(LL_INFO, ("Publishing to %s", s_tx_topic));
|
||||
mg_mqtt_pub(c, &topic, &data, s_qos, false);
|
||||
mg_mqtt_pub(c, topic, data, s_qos, false);
|
||||
prev_second = now_second;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_mqtt_opts opts = {.qos = s_qos};
|
||||
struct mg_mqtt_opts opts = {.clean = true};
|
||||
bool done = false;
|
||||
mg_mgr_init(&mgr); // Initialise event manager
|
||||
LOG(LL_INFO, ("Connecting to %s", s_url)); // Inform that we're starting
|
||||
|
||||
@@ -43,10 +43,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct mg_str subt = mg_str(s_sub_topic);
|
||||
struct mg_str pubt = mg_str(s_pub_topic), data = mg_str("hello");
|
||||
LOG(LL_INFO, ("CONNECTED to %s", s_url));
|
||||
mg_mqtt_sub(c, &subt, s_qos);
|
||||
mg_mqtt_sub(c, subt, s_qos);
|
||||
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) subt.len, subt.ptr));
|
||||
|
||||
mg_mqtt_pub(c, &pubt, &data, s_qos, false);
|
||||
mg_mqtt_pub(c, pubt, data, s_qos, false);
|
||||
LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
|
||||
(int) pubt.len, pubt.ptr));
|
||||
} else if (ev == MG_EV_MQTT_MSG) {
|
||||
@@ -65,7 +65,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
// Timer function - recreate client connection if it is closed
|
||||
static void timer_fn(void *arg) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *) arg;
|
||||
struct mg_mqtt_opts opts = {.qos = s_qos,
|
||||
struct mg_mqtt_opts opts = {.clean = true,
|
||||
.will_qos = s_qos,
|
||||
.will_topic = mg_str(s_pub_topic),
|
||||
.will_message = mg_str("goodbye")};
|
||||
if (s_conn == NULL) s_conn = mg_mqtt_connect(mgr, s_url, &opts, fn, NULL);
|
||||
|
||||
@@ -28,7 +28,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
} else if (ev == MG_EV_WS_OPEN) {
|
||||
// WS connection established. Perform MQTT login
|
||||
LOG(LL_INFO, ("Connected to WS. Logging in to MQTT..."));
|
||||
struct mg_mqtt_opts opts = {.qos = 1,
|
||||
struct mg_mqtt_opts opts = {.will_qos = 1,
|
||||
.will_topic = mg_str(s_topic),
|
||||
.will_message = mg_str("goodbye")};
|
||||
size_t len = c->send.len;
|
||||
@@ -46,10 +46,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct mg_str topic = mg_str(s_topic), data = mg_str("hello");
|
||||
size_t len = c->send.len;
|
||||
LOG(LL_INFO, ("CONNECTED to %s", s_url));
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
mg_mqtt_sub(c, topic, 1);
|
||||
mg_ws_wrap(c, len, WEBSOCKET_OP_BINARY);
|
||||
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) topic.len, topic.ptr));
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
mg_mqtt_pub(c, topic, data, 1, false);
|
||||
LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
|
||||
(int) topic.len, topic.ptr));
|
||||
len = mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY);
|
||||
|
||||
@@ -36,8 +36,8 @@ static void start_thread(void (*f)(void *), void *p) {
|
||||
|
||||
static void thread_function(void *param) {
|
||||
struct mg_connection *c = param; // Pipe connection
|
||||
mg_usleep(2 * 1000000); // Simulate long execution
|
||||
mg_mgr_wakeup(c); // Wakeup event manager
|
||||
sleep(2); // Simulate long execution
|
||||
mg_mgr_wakeup(c, "hi", 2); // Wakeup event manager
|
||||
}
|
||||
|
||||
// HTTP request callback
|
||||
@@ -62,8 +62,9 @@ static void pcb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct mg_connection *t;
|
||||
for (t = c->mgr->conns; t != NULL; t = t->next) {
|
||||
if (t->label[0] != 'W') continue; // Ignore un-marked connections
|
||||
mg_http_reply(t, 200, "Host: foo.com\r\n", "hi\n"); // Respond!
|
||||
t->label[0] = 0; // Clear mark
|
||||
mg_http_reply(t, 200, "Host: foo.com\r\n", "%.*s\n", c->recv.len,
|
||||
c->recv.buf); // Respond!
|
||||
t->label[0] = 0; // Clear mark
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
PROG ?= example
|
||||
|
||||
all: $(PROG)
|
||||
$(RUN) ./$(PROG) $(ARGS)
|
||||
|
||||
$(PROG): main.c
|
||||
$(CC) ../../mongoose.c -I../.. -W -Wall $(CFLAGS) -o $(PROG) main.c
|
||||
|
||||
clean:
|
||||
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
static const char *s_ssdp_url = "udp://239.255.255.250:1900";
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
LOG(LL_DEBUG, ("%p got event: %d %p %p", c, ev, ev_data, fn_data));
|
||||
if (ev == MG_EV_OPEN) {
|
||||
c->is_hexdumping = 1;
|
||||
} else if (ev == MG_EV_RESOLVE) {
|
||||
// c->peer gets populated with multicast address. Store it in c->label
|
||||
memcpy(c->label, &c->peer, sizeof(c->peer));
|
||||
} else if (ev == MG_EV_READ) {
|
||||
// Each response to the SSDP socket will change c->peer.
|
||||
// We can now do mg_printf(c, "haha"); to respond back to the remote side.
|
||||
// But in our case, we should restore the multicast address in order
|
||||
// to have next search to go to the multicast address
|
||||
memcpy(&c->peer, c->label, sizeof(c->peer));
|
||||
}
|
||||
}
|
||||
|
||||
static void tfn(void *param) {
|
||||
struct mg_connection *c = param;
|
||||
if (c == NULL) return;
|
||||
LOG(LL_INFO, ("Sending M-SEARCH"));
|
||||
mg_printf(c, "%s",
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: 239.255.255.250:1900\r\n"
|
||||
"MAN: \"ssdp:discover\"\r\n"
|
||||
"ST: urn:schemas-upnp-org:device:MediaRenderer:1\r\n"
|
||||
"MX: 5\r\n"
|
||||
"\r\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_timer timer;
|
||||
static struct mg_connection *c;
|
||||
mg_mgr_init(&mgr);
|
||||
c = mg_connect(&mgr, s_ssdp_url, fn, NULL);
|
||||
mg_timer_init(&timer, 2000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, tfn, c);
|
||||
while (true) mg_mgr_poll(&mgr, 200);
|
||||
mg_mgr_free(&mgr);
|
||||
return 0;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
||||
static size_t i;
|
||||
const char *path = files[i++ % nfiles];
|
||||
size_t size = 0;
|
||||
char *data = mg_file_read(path, &size); // Read next file
|
||||
char *data = mg_file_read(&mg_fs_posix, path, &size); // Read next file
|
||||
struct mg_connection *c;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||
|
||||
+511
-376
File diff suppressed because it is too large.
Load diff
+506
-478
File diff suppressed because it is too large.
Load diff
+11
-10
@@ -3,21 +3,21 @@
|
||||
#if MG_ARCH == MG_ARCH_AZURERTOS
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <fx_api.h>
|
||||
#include <tx_api.h>
|
||||
#include <fx_api.h>
|
||||
|
||||
#include <tx_port.h>
|
||||
#include <nx_port.h>
|
||||
#include <nx_api.h>
|
||||
#include <nx_bsd.h>
|
||||
#include <nx_port.h>
|
||||
#include <tx_port.h>
|
||||
|
||||
#ifdef __REDLIB__
|
||||
#define va_copy(d,s)__builtin_va_copy(d,s)
|
||||
#define va_copy(d, s) __builtin_va_copy(d, s)
|
||||
#endif
|
||||
|
||||
#define PATH_MAX FX_MAXIMUM_PATH
|
||||
@@ -28,9 +28,10 @@
|
||||
#define gmtime_r(a, b) gmtime(a)
|
||||
#define MG_INT64_FMT "%lld"
|
||||
|
||||
static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
|
||||
(void) tm;
|
||||
return localtime(t);
|
||||
static __inline struct tm *localtime_r(const time_t *t, struct tm *tm) {
|
||||
struct tm *x = localtime(t);
|
||||
*tm = *x;
|
||||
return tm;
|
||||
}
|
||||
|
||||
#undef FOPEN_MAX
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#undef MG_PATH_MAX
|
||||
#undef MG_ENABLE_DIRLIST
|
||||
|
||||
#define MG_DIRSEP '/'
|
||||
#define MG_INT64_FMT "%lld"
|
||||
#define MG_PATH_MAX 128
|
||||
#define MG_ENABLE_DIRLIST 1
|
||||
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
|
||||
#include <esp_system.h>
|
||||
|
||||
#undef MG_PATH_MAX
|
||||
#undef MG_ENABLE_DIRLIST
|
||||
|
||||
#define MG_DIRSEP '/'
|
||||
#define MG_INT64_FMT "%lld"
|
||||
#define MG_PATH_MAX 128
|
||||
#define MG_ENABLE_DIRLIST 1
|
||||
|
||||
|
||||
@@ -5,17 +5,16 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
typedef long suseconds_t;
|
||||
struct timeval {
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
long tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -48,7 +47,7 @@ static inline void *mg_calloc(int cnt, size_t size) {
|
||||
#define calloc(a, b) mg_calloc((a), (b))
|
||||
#define free(a) vPortFree(a)
|
||||
#define malloc(a) pvPortMalloc(a)
|
||||
|
||||
#define gmtime_r(a, b) gmtime(a)
|
||||
#define mkdir(a, b) (-1)
|
||||
|
||||
#endif // MG_ARCH == MG_ARCH_FREERTOS_LWIP
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <FreeRTOS_Sockets.h>
|
||||
#include <task.h>
|
||||
|
||||
#define MG_INT64_FMT "%lld"
|
||||
#define MG_DIRSEP '/'
|
||||
|
||||
// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
|
||||
// with FreeRTOS_ prefix, is beyond me
|
||||
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
|
||||
@@ -59,15 +56,15 @@ static inline void *mg_calloc(int cnt, size_t size) {
|
||||
#define calloc(a, b) mg_calloc((a), (b))
|
||||
#define free(a) vPortFree(a)
|
||||
#define malloc(a) pvPortMalloc(a)
|
||||
#define mkdir(a, b) (-1)
|
||||
|
||||
#define gmtime_r(a, b) gmtime(a)
|
||||
|
||||
#if !defined(__GNUC__)
|
||||
// copied from GCC on ARM; for some reason useconds are signed
|
||||
typedef long suseconds_t;
|
||||
struct timeval {
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
long tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
+3
-2
@@ -29,9 +29,10 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MG_DIRSEP '/'
|
||||
#define MG_INT64_FMT "%" PRId64
|
||||
#undef MG_ENABLE_DIRLIST
|
||||
|
||||
#ifndef MG_ENABLE_DIRLIST
|
||||
#define MG_ENABLE_DIRLIST 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+15
-8
@@ -15,6 +15,7 @@
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <direct.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -56,7 +57,6 @@ typedef enum { false = 0, true = 1 } bool;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef unsigned suseconds_t;
|
||||
typedef int socklen_t;
|
||||
#define MG_DIRSEP '\\'
|
||||
#ifndef PATH_MAX
|
||||
@@ -69,6 +69,10 @@ typedef int socklen_t;
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#endif
|
||||
|
||||
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
|
||||
#define sleep(x) Sleep(x)
|
||||
#define mkdir(a, b) _mkdir(a)
|
||||
|
||||
#ifndef va_copy
|
||||
#ifdef __va_copy
|
||||
#define va_copy __va_copy
|
||||
@@ -82,18 +86,21 @@ typedef int socklen_t;
|
||||
|
||||
#define MG_INT64_FMT "%I64d"
|
||||
|
||||
#undef MG_ENABLE_DIRLIST
|
||||
#ifndef MG_ENABLE_DIRLIST
|
||||
#define MG_ENABLE_DIRLIST 1
|
||||
#endif
|
||||
|
||||
// https://lgtm.com/rules/2154840805/ -gmtime, localtime, ctime and asctime
|
||||
static __inline struct tm *gmtime_r(time_t *t, struct tm *tm) {
|
||||
(void) tm;
|
||||
return gmtime(t);
|
||||
static __inline struct tm *gmtime_r(const time_t *t, struct tm *tm) {
|
||||
struct tm *x = gmtime(t);
|
||||
*tm = *x;
|
||||
return tm;
|
||||
}
|
||||
|
||||
static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
|
||||
(void) tm;
|
||||
return localtime(t);
|
||||
static __inline struct tm *localtime_r(const time_t *t, struct tm *tm) {
|
||||
struct tm *x = localtime(t);
|
||||
*tm = *x;
|
||||
return tm;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MG_ENABLE_FATFS
|
||||
#define MG_ENABLE_FATFS 0
|
||||
#endif
|
||||
|
||||
#ifndef MG_FATFS_ROOT
|
||||
#define MG_FATFS_ROOT "/"
|
||||
#endif
|
||||
|
||||
#ifndef MG_FATFS_BSIZE
|
||||
#define MG_FATFS_BSIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_SOCKET
|
||||
#define MG_ENABLE_SOCKET 1
|
||||
#endif
|
||||
@@ -68,9 +80,29 @@
|
||||
#endif
|
||||
|
||||
#ifndef MG_PATH_MAX
|
||||
#ifdef PATH_MAX
|
||||
#define MG_PATH_MAX PATH_MAX
|
||||
#else
|
||||
#define MG_PATH_MAX 128
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
|
||||
#define MG_SOCK_LISTEN_BACKLOG_SIZE 128
|
||||
#endif
|
||||
|
||||
#ifndef MG_DIRSEP
|
||||
#define MG_DIRSEP '/'
|
||||
#endif
|
||||
|
||||
#ifndef MG_INT64_FMT
|
||||
#define MG_INT64_FMT "%lld"
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_FILE
|
||||
#if defined(FOPEN_MAX)
|
||||
#define MG_ENABLE_FILE 1
|
||||
#else
|
||||
#define MG_ENABLE_FILE 0
|
||||
#endif
|
||||
#endif
|
||||
@@ -2,12 +2,13 @@
|
||||
#include "log.h"
|
||||
#include "str.h"
|
||||
#include "timer.h"
|
||||
#include "url.h"
|
||||
#include "util.h"
|
||||
|
||||
struct dns_data {
|
||||
struct dns_data *next;
|
||||
struct mg_connection *c;
|
||||
unsigned long expire;
|
||||
int64_t expire;
|
||||
uint16_t txnid;
|
||||
};
|
||||
|
||||
@@ -132,7 +133,7 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *fn_data) {
|
||||
struct dns_data *d, *tmp;
|
||||
if (ev == MG_EV_POLL) {
|
||||
unsigned long now = *(unsigned long *) ev_data;
|
||||
int64_t now = *(int64_t *) ev_data;
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
// LOG(LL_DEBUG, ("%lu %lu dns poll", d->expire, now));
|
||||
@@ -182,6 +183,7 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
for (d = s_reqs; d != NULL; d = tmp) {
|
||||
tmp = d->next;
|
||||
mg_error(d->c, "DNS error");
|
||||
mg_dns_free(d);
|
||||
}
|
||||
}
|
||||
@@ -231,7 +233,7 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
||||
dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
|
||||
if (dnsc->c != NULL) {
|
||||
dnsc->c->pfn = dns_cb;
|
||||
snprintf(dnsc->c->label, sizeof(dnsc->c->label), "%s", "DNS");
|
||||
// snprintf(dnsc->c->label, sizeof(dnsc->c->label), "%s", "DNS");
|
||||
// dnsc->c->is_hexdumping = 1;
|
||||
}
|
||||
}
|
||||
@@ -246,7 +248,7 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
||||
d->txnid = s_reqs ? (uint16_t) (s_reqs->txnid + 1) : 1;
|
||||
d->next = s_reqs;
|
||||
s_reqs = d;
|
||||
d->expire = mg_millis() + (unsigned long) ms;
|
||||
d->expire = mg_millis() + (int64_t) ms;
|
||||
d->c = c;
|
||||
c->is_resolving = 1;
|
||||
LOG(LL_VERBOSE_DEBUG,
|
||||
@@ -256,12 +258,14 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
|
||||
}
|
||||
}
|
||||
|
||||
void mg_resolve(struct mg_connection *c, struct mg_str *name, int ms) {
|
||||
if (mg_aton(*name, &c->peer)) {
|
||||
// name is an IP address, do not fire name resolution
|
||||
void mg_resolve(struct mg_connection *c, const char *url) {
|
||||
struct mg_str host = mg_url_host(url);
|
||||
c->peer.port = mg_htons(mg_url_port(url));
|
||||
if (mg_aton(host, &c->peer)) {
|
||||
// host is an IP address, do not fire name resolution
|
||||
mg_connect_resolved(c);
|
||||
} else {
|
||||
// name is not an IP, send DNS resolution request
|
||||
mg_sendnsreq(c, name, ms, &c->mgr->dns4, false);
|
||||
// host is not an IP, send DNS resolution request
|
||||
mg_sendnsreq(c, &host, c->mgr->dnstimeout, &c->mgr->dns4, false);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ struct mg_dns_rr {
|
||||
uint16_t alen; // Address length
|
||||
};
|
||||
|
||||
void mg_resolve(struct mg_connection *, struct mg_str *, int);
|
||||
void mg_resolve(struct mg_connection *, const char *url);
|
||||
void mg_resolve_cancel(struct mg_connection *);
|
||||
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
|
||||
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ void mg_error(struct mg_connection *c, const char *fmt, ...);
|
||||
enum {
|
||||
MG_EV_ERROR, // Error char *error_message
|
||||
MG_EV_OPEN, // Connection created NULL
|
||||
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
|
||||
MG_EV_POLL, // mg_mgr_poll iteration int64_t *milliseconds
|
||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||
MG_EV_CONNECT, // Connection established NULL
|
||||
MG_EV_ACCEPT, // Connection accepted NULL
|
||||
@@ -24,6 +24,6 @@ enum {
|
||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
|
||||
MG_EV_SNTP_TIME, // SNTP time received int64_t *milliseconds
|
||||
MG_EV_USER, // Starting ID for user events
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "fs.h"
|
||||
#include "util.h"
|
||||
|
||||
struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) {
|
||||
struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd));
|
||||
if (fd != NULL) {
|
||||
fd->fd = fs->open(path, flags);
|
||||
fd->fs = fs;
|
||||
if (fd->fd == NULL) {
|
||||
free(fd);
|
||||
fd = NULL;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void mg_fs_close(struct mg_fd *fd) {
|
||||
if (fd != NULL) {
|
||||
fd->fs->close(fd->fd);
|
||||
free(fd);
|
||||
}
|
||||
}
|
||||
|
||||
char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep) {
|
||||
struct mg_fd *fd;
|
||||
char *data = NULL;
|
||||
size_t size = 0;
|
||||
fs->stat(path, &size, NULL);
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_READ)) != NULL) {
|
||||
data = (char *) calloc(1, size + 1);
|
||||
if (data != NULL) {
|
||||
if (fs->read(fd->fd, data, size) != size) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
} else {
|
||||
data[size] = '\0';
|
||||
if (sizep != NULL) *sizep = size;
|
||||
}
|
||||
}
|
||||
mg_fs_close(fd);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf,
|
||||
size_t len) {
|
||||
bool result = false;
|
||||
struct mg_fd *fd;
|
||||
char tmp[MG_PATH_MAX];
|
||||
snprintf(tmp, sizeof(tmp), "%s..%d", path, rand());
|
||||
if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) {
|
||||
result = fs->write(fd->fd, buf, len) == len;
|
||||
mg_fs_close(fd);
|
||||
if (result) {
|
||||
fs->remove(path);
|
||||
fs->rename(tmp, path);
|
||||
} else {
|
||||
fs->remove(tmp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) {
|
||||
char tmp[256], *buf = tmp;
|
||||
bool result;
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap);
|
||||
va_end(ap);
|
||||
result = mg_file_write(fs, path, buf, len > 0 ? (size_t) len : 0);
|
||||
if (buf != tmp) free(buf);
|
||||
return result;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "config.h"
|
||||
|
||||
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
|
||||
|
||||
@@ -10,18 +11,28 @@ enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
|
||||
struct mg_fs {
|
||||
int (*stat)(const char *path, size_t *size, time_t *mtime);
|
||||
void (*list)(const char *path, void (*fn)(const char *, void *), void *);
|
||||
struct mg_fd *(*open)(const char *path, int flags); // Open file
|
||||
void (*close)(struct mg_fd *fd); // Close file
|
||||
void *(*open)(const char *path, int flags); // Open file
|
||||
void (*close)(void *fd); // Close file
|
||||
size_t (*read)(void *fd, void *buf, size_t len); // Read file
|
||||
size_t (*write)(void *fd, const void *buf, size_t len); // Write file
|
||||
size_t (*seek)(void *fd, size_t offset); // Set file position
|
||||
bool (*rename)(const char *from, const char *to); // Rename
|
||||
bool (*remove)(const char *path); // Delete file
|
||||
bool (*mkd)(const char *path); // Create directory
|
||||
};
|
||||
|
||||
extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
|
||||
extern struct mg_fs mg_fs_packed; // Packed FS, see examples/complete
|
||||
extern struct mg_fs mg_fs_fat; // FAT FS
|
||||
|
||||
// File descriptor
|
||||
struct mg_fd {
|
||||
void *fd;
|
||||
struct mg_fs *fs;
|
||||
};
|
||||
|
||||
extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
|
||||
extern struct mg_fs mg_fs_packed; // Packed FS, see examples/complete
|
||||
struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
|
||||
void mg_fs_close(struct mg_fd *fd);
|
||||
char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size);
|
||||
bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t);
|
||||
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "fs.h"
|
||||
|
||||
#if MG_ENABLE_FATFS
|
||||
#include <ff.h>
|
||||
|
||||
static int ff_stat(const char *path, size_t *size, time_t *mtime) {
|
||||
FILINFO fi;
|
||||
if (path[0] == '\0' || strcmp(path, MG_FATFS_ROOT) == 0) {
|
||||
if (size) *size = 0;
|
||||
if (mtime) *mtime = 0;
|
||||
return MG_FS_DIR;
|
||||
} else if (f_stat(path, &fi) == 0) {
|
||||
if (size) *size = (size_t) fi.fsize;
|
||||
if (mtime) *mtime = (fi.fdate << 16) | fi.ftime;
|
||||
return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ff_list(const char *dir, void (*fn)(const char *, void *),
|
||||
void *userdata) {
|
||||
DIR d;
|
||||
FILINFO fi;
|
||||
if (f_opendir(&d, dir) == FR_OK) {
|
||||
while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') {
|
||||
if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue;
|
||||
fn(fi.fname, userdata);
|
||||
}
|
||||
f_closedir(&d);
|
||||
}
|
||||
}
|
||||
|
||||
static void *ff_open(const char *path, int flags) {
|
||||
FIL f;
|
||||
unsigned char mode =
|
||||
flags == MG_FS_READ ? FA_READ : FA_READ | FA_WRITE | FA_OPEN_APPEND;
|
||||
if (f_open(&f, path, mode) == 0) {
|
||||
FIL *fp = calloc(1, sizeof(*fp));
|
||||
*fp = f;
|
||||
return fp;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ff_close(void *fp) {
|
||||
if (fp != NULL) {
|
||||
f_close((FIL *) fp);
|
||||
free(fp);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t ff_read(void *fp, void *buf, size_t len) {
|
||||
unsigned n = 0;
|
||||
f_read((FIL *) fp, buf, len, &n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t ff_write(void *fp, const void *buf, size_t len) {
|
||||
unsigned n, sum = 0, bs = MG_FATFS_BSIZE;
|
||||
while ((size_t) sum < len &&
|
||||
f_write((FIL *) fp, (char *) buf + sum,
|
||||
sum + bs > len ? len - sum : bs, &n) == FR_OK &&
|
||||
n > 0) {
|
||||
sum += n;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static size_t ff_seek(void *fp, size_t offset) {
|
||||
f_lseek((FIL *) fp, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
static bool ff_rename(const char *from, const char *to) {
|
||||
return f_rename(from, to) == FR_OK;
|
||||
}
|
||||
|
||||
static bool ff_remove(const char *path) {
|
||||
return f_unlink(path) == FR_OK;
|
||||
}
|
||||
|
||||
static bool ff_mkdir(const char *path) {
|
||||
return f_mkdir(path) == FR_OK;
|
||||
}
|
||||
|
||||
struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read,
|
||||
ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir};
|
||||
#endif
|
||||
+22
-11
@@ -57,24 +57,20 @@ static void packed_list(const char *dir, void (*fn)(const char *, void *),
|
||||
}
|
||||
}
|
||||
|
||||
static struct mg_fd *packed_open(const char *path, int flags) {
|
||||
static void *packed_open(const char *path, int flags) {
|
||||
size_t size = 0;
|
||||
const char *data = mg_unpack(path, &size, NULL);
|
||||
struct packed_file *fp = NULL;
|
||||
struct mg_fd *fd = NULL;
|
||||
if (data == NULL) return NULL;
|
||||
if (flags & MG_FS_WRITE) return NULL;
|
||||
fp = (struct packed_file *) calloc(1, sizeof(*fp));
|
||||
fd = (struct mg_fd *) calloc(1, sizeof(*fd));
|
||||
fp->size = size;
|
||||
fp->data = data;
|
||||
fd->fd = fp;
|
||||
fd->fs = &mg_fs_packed;
|
||||
return fd;
|
||||
return (void *) fp;
|
||||
}
|
||||
|
||||
static void packed_close(struct mg_fd *fd) {
|
||||
if (fd) free(fd->fd), free(fd);
|
||||
static void packed_close(void *fp) {
|
||||
if (fp != NULL) free(fp);
|
||||
}
|
||||
|
||||
static size_t packed_read(void *fd, void *buf, size_t len) {
|
||||
@@ -97,6 +93,21 @@ static size_t packed_seek(void *fd, size_t offset) {
|
||||
return fp->pos;
|
||||
}
|
||||
|
||||
struct mg_fs mg_fs_packed = {packed_stat, packed_list, packed_open,
|
||||
packed_close, packed_read, packed_write,
|
||||
packed_seek};
|
||||
static bool packed_rename(const char *from, const char *to) {
|
||||
(void) from, (void) to;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool packed_remove(const char *path) {
|
||||
(void) path;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool packed_mkdir(const char *path) {
|
||||
(void) path;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mg_fs mg_fs_packed = {
|
||||
packed_stat, packed_list, packed_open, packed_close, packed_read,
|
||||
packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir};
|
||||
+57
-38
@@ -1,19 +1,33 @@
|
||||
#include "fs.h"
|
||||
|
||||
#if defined(FOPEN_MAX)
|
||||
#if MG_ENABLE_FILE
|
||||
|
||||
#ifndef MG_STAT_STRUCT
|
||||
#define MG_STAT_STRUCT stat
|
||||
#endif
|
||||
|
||||
#ifndef MG_STAT_FUNC
|
||||
#define MG_STAT_FUNC stat
|
||||
#endif
|
||||
|
||||
static int p_stat(const char *path, size_t *size, time_t *mtime) {
|
||||
#ifdef _WIN32
|
||||
#if !defined(S_ISDIR)
|
||||
LOG(LL_ERROR, ("stat() API is not supported. %p %p %p", path, size, mtime));
|
||||
return 0;
|
||||
#else
|
||||
#if defined(_WIN32)
|
||||
struct _stati64 st;
|
||||
wchar_t tmp[PATH_MAX];
|
||||
MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
|
||||
if (_wstati64(tmp, &st) != 0) return 0;
|
||||
#else
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0) return 0;
|
||||
struct MG_STAT_STRUCT st;
|
||||
if (MG_STAT_FUNC(path, &st) != 0) return 0;
|
||||
#endif
|
||||
if (size) *size = (size_t) st.st_size;
|
||||
if (mtime) *mtime = st.st_mtime;
|
||||
return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -141,30 +155,20 @@ static void p_list(const char *dir, void (*fn)(const char *, void *),
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct mg_fd *p_open(const char *path, int flags) {
|
||||
const char *mode = flags == (MG_FS_READ | MG_FS_WRITE) ? "r+b"
|
||||
: flags & MG_FS_READ ? "rb"
|
||||
: flags & MG_FS_WRITE ? "wb"
|
||||
: "";
|
||||
void *fp = NULL;
|
||||
struct mg_fd *fd = NULL;
|
||||
static void *p_open(const char *path, int flags) {
|
||||
const char *mode = flags == MG_FS_READ ? "rb" : "a+b";
|
||||
#ifdef _WIN32
|
||||
wchar_t b1[PATH_MAX], b2[10];
|
||||
MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0]));
|
||||
MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0]));
|
||||
fp = (void *) _wfopen(b1, b2);
|
||||
return (void *) _wfopen(b1, b2);
|
||||
#else
|
||||
fp = (void *) fopen(path, mode);
|
||||
return (void *) fopen(path, mode);
|
||||
#endif
|
||||
if (fp == NULL) return NULL;
|
||||
fd = (struct mg_fd *) calloc(1, sizeof(*fd));
|
||||
fd->fd = fp;
|
||||
fd->fs = &mg_fs_posix;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void p_close(struct mg_fd *fd) {
|
||||
if (fd != NULL) fclose((FILE *) fd->fd), free(fd);
|
||||
static void p_close(void *fp) {
|
||||
fclose((FILE *) fp);
|
||||
}
|
||||
|
||||
static size_t p_read(void *fp, void *buf, size_t len) {
|
||||
@@ -176,55 +180,70 @@ static size_t p_write(void *fp, const void *buf, size_t len) {
|
||||
}
|
||||
|
||||
static size_t p_seek(void *fp, size_t offset) {
|
||||
#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
|
||||
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
|
||||
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
|
||||
#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
|
||||
(defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
|
||||
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
|
||||
fseeko((FILE *) fp, (off_t) offset, SEEK_SET);
|
||||
#else
|
||||
fseek((FILE *) fp, (long) offset, SEEK_SET);
|
||||
#endif
|
||||
return (size_t) ftell((FILE *) fp);
|
||||
}
|
||||
#else
|
||||
static char *p_realpath(const char *path, char *resolved_path) {
|
||||
(void) path, (void) resolved_path;
|
||||
return NULL;
|
||||
|
||||
static bool p_rename(const char *from, const char *to) {
|
||||
return rename(from, to) == 0;
|
||||
}
|
||||
|
||||
static bool p_remove(const char *path) {
|
||||
return remove(path) == 0;
|
||||
}
|
||||
|
||||
static bool p_mkdir(const char *path) {
|
||||
return mkdir(path, 0775) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int p_stat(const char *path, size_t *size, time_t *mtime) {
|
||||
(void) path, (void) size, (void) mtime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void p_list(const char *path, void (*fn)(const char *, void *),
|
||||
void *userdata) {
|
||||
(void) path, (void) fn, (void) userdata;
|
||||
}
|
||||
|
||||
static struct mg_fd *p_open(const char *path, int flags) {
|
||||
static void *p_open(const char *path, int flags) {
|
||||
(void) path, (void) flags;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void p_close(struct mg_fd *fd) {
|
||||
(void) fd;
|
||||
static void p_close(void *fp) {
|
||||
(void) fp;
|
||||
}
|
||||
|
||||
static size_t p_read(void *fd, void *buf, size_t len) {
|
||||
(void) fd, (void) buf, (void) len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t p_write(void *fd, const void *buf, size_t len) {
|
||||
(void) fd, (void) buf, (void) len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t p_seek(void *fd, size_t offset) {
|
||||
(void) fd, (void) offset;
|
||||
return (size_t) ~0;
|
||||
}
|
||||
static bool p_rename(const char *from, const char *to) {
|
||||
(void) from, (void) to;
|
||||
return false;
|
||||
}
|
||||
static bool p_remove(const char *path) {
|
||||
(void) path;
|
||||
return false;
|
||||
}
|
||||
static bool p_mkdir(const char *path) {
|
||||
(void) path;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close,
|
||||
p_read, p_write, p_seek};
|
||||
struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
||||
p_write, p_seek, p_rename, p_remove, p_mkdir};
|
||||
+33
-33
@@ -365,8 +365,7 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers,
|
||||
|
||||
static void http_cb(struct mg_connection *, int, void *, void *);
|
||||
static void restore_http_cb(struct mg_connection *c) {
|
||||
struct mg_fd *fd = (struct mg_fd *) c->pfn_data;
|
||||
if (fd != NULL) fd->fs->close(fd);
|
||||
mg_fs_close((struct mg_fd *) c->pfn_data);
|
||||
c->pfn_data = NULL;
|
||||
c->pfn = http_cb;
|
||||
}
|
||||
@@ -378,32 +377,6 @@ char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||
const char *dir) {
|
||||
char offset[40] = "", name[200] = "", path[256];
|
||||
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");
|
||||
return -1;
|
||||
} else {
|
||||
FILE *fp;
|
||||
size_t oft = strtoul(offset, NULL, 0);
|
||||
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||
LOG(LL_DEBUG,
|
||||
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
|
||||
if ((fp = fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
|
||||
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
|
||||
return -2;
|
||||
} else {
|
||||
fwrite(hm->body.ptr, 1, hm->body.len, fp);
|
||||
fclose(fp);
|
||||
mg_http_reply(c, 200, "", "");
|
||||
return (int) hm->body.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void static_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
void *fn_data) {
|
||||
if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
|
||||
@@ -505,7 +478,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
const char *path, struct mg_http_serve_opts *opts) {
|
||||
char etag[64];
|
||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||
struct mg_fd *fd = fs->open(path, MG_FS_READ);
|
||||
struct mg_fd *fd = mg_fs_open(fs, path, MG_FS_READ);
|
||||
size_t size = 0;
|
||||
time_t mtime = 0;
|
||||
struct mg_str *inm = NULL;
|
||||
@@ -513,12 +486,12 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
if (fd == NULL || fs->stat(path, &size, &mtime) == 0) {
|
||||
LOG(LL_DEBUG, ("404 [%s] %p", path, (void *) fd));
|
||||
mg_http_reply(c, 404, "", "%s", "Not found\n");
|
||||
fs->close(fd);
|
||||
mg_fs_close(fd);
|
||||
// NOTE: mg_http_etag() call should go first!
|
||||
} else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL &&
|
||||
(inm = mg_http_get_header(hm, "If-None-Match")) != NULL &&
|
||||
mg_vcasecmp(inm, etag) == 0) {
|
||||
fs->close(fd);
|
||||
mg_fs_close(fd);
|
||||
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
|
||||
} else {
|
||||
int n, status = 200;
|
||||
@@ -554,7 +527,7 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
|
||||
etag, cl, range, opts->extra_headers ? opts->extra_headers : "");
|
||||
if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
|
||||
c->is_draining = 1;
|
||||
fs->close(fd);
|
||||
mg_fs_close(fd);
|
||||
} else {
|
||||
c->pfn = static_cb;
|
||||
c->pfn_data = fd;
|
||||
@@ -914,10 +887,37 @@ void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
|
||||
c->recv.len -= 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];
|
||||
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");
|
||||
return -1;
|
||||
} else {
|
||||
struct mg_fd *fd;
|
||||
long oft = strtol(offset, NULL, 0);
|
||||
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
|
||||
remove_double_dots(path);
|
||||
LOG(LL_DEBUG, ("%d bytes @ %ld [%s]", (int) hm->body.len, oft, path));
|
||||
if (oft == 0) fs->remove(path);
|
||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||
mg_http_reply(c, 400, "", "open(%s): %d", path, errno);
|
||||
return -2;
|
||||
} else {
|
||||
int written = (int) fs->write(fd->fd, hm->body.ptr, hm->body.len);
|
||||
mg_fs_close(fd);
|
||||
mg_http_reply(c, 200, "", "%d", written);
|
||||
return (int) hm->body.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
||||
if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
|
||||
struct mg_http_message hm;
|
||||
for (;;) {
|
||||
while (c->recv.buf != NULL && c->recv.len > 0) {
|
||||
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
|
||||
bool is_chunked = n > 0 && mg_is_chunked(&hm);
|
||||
if (ev == MG_EV_CLOSE) {
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "config.h"
|
||||
#include "fs.h"
|
||||
#include "net.h"
|
||||
@@ -57,7 +58,7 @@ 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,
|
||||
const char *dir);
|
||||
struct mg_fs *fs, const char *dir);
|
||||
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 *);
|
||||
@@ -4,7 +4,9 @@
|
||||
#if MG_ENABLE_LOG
|
||||
static void mg_log_stdout(const void *buf, size_t len, void *userdata) {
|
||||
(void) userdata;
|
||||
#if MG_ENABLE_FILE
|
||||
fwrite(buf, 1, len, stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *s_spec = "2";
|
||||
|
||||
+14
-14
@@ -35,7 +35,7 @@ void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts) {
|
||||
char rnd[9], client_id[16];
|
||||
struct mg_str cid = opts->client_id;
|
||||
uint32_t total_len = 7 + 1 + 2 + 2;
|
||||
uint8_t connflag = (uint8_t) ((opts->qos & 3) << 1);
|
||||
uint8_t connflag = (uint8_t) ((opts->will_qos & 3) << 3);
|
||||
|
||||
if (cid.len == 0) {
|
||||
mg_random(rnd, sizeof(rnd));
|
||||
@@ -84,33 +84,33 @@ void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts) {
|
||||
}
|
||||
}
|
||||
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
|
||||
struct mg_str *data, int qos, bool retain) {
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
|
||||
struct mg_str data, int qos, bool retain) {
|
||||
uint8_t flags = (uint8_t) (((qos & 3) << 1) | (retain ? 1 : 0));
|
||||
uint32_t total_len = 2 + (uint32_t) topic->len + (uint32_t) data->len;
|
||||
LOG(LL_DEBUG, ("%lu [%.*s] -> [%.*s]", c->id, (int) topic->len,
|
||||
(char *) topic->ptr, (int) data->len, (char *) data->ptr));
|
||||
uint32_t total_len = 2 + (uint32_t) topic.len + (uint32_t) data.len;
|
||||
LOG(LL_DEBUG, ("%lu [%.*s] -> [%.*s]", c->id, (int) topic.len,
|
||||
(char *) topic.ptr, (int) data.len, (char *) data.ptr));
|
||||
if (qos > 0) total_len += 2;
|
||||
mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, total_len);
|
||||
mg_send_u16(c, mg_htons((uint16_t) topic->len));
|
||||
mg_send(c, topic->ptr, topic->len);
|
||||
mg_send_u16(c, mg_htons((uint16_t) topic.len));
|
||||
mg_send(c, topic.ptr, topic.len);
|
||||
if (qos > 0) {
|
||||
static uint16_t s_id;
|
||||
if (++s_id == 0) s_id++;
|
||||
mg_send_u16(c, mg_htons(s_id));
|
||||
}
|
||||
mg_send(c, data->ptr, data->len);
|
||||
mg_send(c, data.ptr, data.len);
|
||||
}
|
||||
|
||||
void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic, int qos) {
|
||||
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos) {
|
||||
static uint16_t s_id;
|
||||
uint8_t qos_ = qos & 3;
|
||||
uint32_t total_len = 2 + (uint32_t) topic->len + 2 + 1;
|
||||
uint32_t total_len = 2 + (uint32_t) topic.len + 2 + 1;
|
||||
mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, total_len);
|
||||
if (++s_id == 0) ++s_id;
|
||||
mg_send_u16(c, mg_htons(s_id));
|
||||
mg_send_u16(c, mg_htons((uint16_t) topic->len));
|
||||
mg_send(c, topic->ptr, topic->len);
|
||||
mg_send_u16(c, mg_htons((uint16_t) topic.len));
|
||||
mg_send(c, topic.ptr, topic.len);
|
||||
mg_send(c, &qos_, sizeof(qos_));
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
case MQTT_CMD_CONNACK:
|
||||
mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
|
||||
if (mm.ack == 0) {
|
||||
LOG(LL_INFO, ("%lu Connected", c->id));
|
||||
LOG(LL_DEBUG, ("%lu Connected", c->id));
|
||||
} else {
|
||||
LOG(LL_ERROR, ("%lu MQTT auth failed, code %d", c->id, mm.ack));
|
||||
c->is_closing = 1;
|
||||
|
||||
+4
-4
@@ -24,7 +24,7 @@ struct mg_mqtt_opts {
|
||||
struct mg_str client_id; // Client ID
|
||||
struct mg_str will_topic; // Will topic
|
||||
struct mg_str will_message; // Will message
|
||||
uint8_t qos; // Quality of service
|
||||
uint8_t will_qos; // Will message quality of service
|
||||
bool will_retain; // Retain last will
|
||||
bool clean; // Use clean session, 0 or 1
|
||||
uint16_t keepalive; // Keep-alive timer in seconds
|
||||
@@ -46,9 +46,9 @@ struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
|
||||
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data);
|
||||
void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts);
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
|
||||
struct mg_str *data, int qos, bool retain);
|
||||
void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic, int qos);
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
|
||||
struct mg_str data, int qos, bool retain);
|
||||
void mg_mqtt_sub(struct mg_connection *, struct mg_str topic, int qos);
|
||||
int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
|
||||
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
|
||||
uint32_t len);
|
||||
|
||||
@@ -19,18 +19,18 @@ int mg_printf(struct mg_connection *c, const char *fmt, ...) {
|
||||
return len;
|
||||
}
|
||||
|
||||
char *mg_straddr(struct mg_connection *c, char *buf, size_t len) {
|
||||
char tmp[100];
|
||||
const char *fmt = c->peer.is_ip6 ? "[%s]:%d" : "%s:%d";
|
||||
mg_ntoa(&c->peer, tmp, sizeof(tmp));
|
||||
snprintf(buf, len, fmt, tmp, (int) mg_ntohs(c->peer.port));
|
||||
char *mg_straddr(struct mg_addr *a, char *buf, size_t len) {
|
||||
char tmp[30];
|
||||
const char *fmt = a->is_ip6 ? "[%s]:%d" : "%s:%d";
|
||||
mg_ntoa(a, tmp, sizeof(tmp));
|
||||
snprintf(buf, len, fmt, tmp, (int) mg_ntohs(a->port));
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len) {
|
||||
if (addr->is_ip6) {
|
||||
uint16_t *p = (uint16_t *) addr->ip6;
|
||||
snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x", mg_htons(p[0]),
|
||||
snprintf(buf, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", mg_htons(p[0]),
|
||||
mg_htons(p[1]), mg_htons(p[2]), mg_htons(p[3]), mg_htons(p[4]),
|
||||
mg_htons(p[5]), mg_htons(p[6]), mg_htons(p[7]));
|
||||
} else {
|
||||
|
||||
@@ -71,9 +71,9 @@ void mg_connect_resolved(struct mg_connection *);
|
||||
bool mg_send(struct mg_connection *, const void *, size_t);
|
||||
int mg_printf(struct mg_connection *, const char *fmt, ...);
|
||||
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
|
||||
char *mg_straddr(struct mg_connection *, char *, size_t);
|
||||
char *mg_straddr(struct mg_addr *, char *, size_t);
|
||||
bool mg_aton(struct mg_str str, struct mg_addr *addr);
|
||||
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
|
||||
|
||||
struct mg_connection *mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *);
|
||||
void mg_mgr_wakeup(struct mg_connection *pipe);
|
||||
void mg_mgr_wakeup(struct mg_connection *pipe, const void *buf, size_t len);
|
||||
+14
-12
@@ -4,13 +4,14 @@
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
#define SNTP_INTERVAL_SEC (3600)
|
||||
#define SNTP_INTERVAL_SEC 3600
|
||||
#define SNTP_TIME_OFFSET 2208988800UL
|
||||
|
||||
static unsigned long s_sntmp_next;
|
||||
|
||||
int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv) {
|
||||
int mode = len > 0 ? buf[0] & 7 : 0, res = -1;
|
||||
int64_t mg_sntp_parse(const unsigned char *buf, size_t len) {
|
||||
int64_t res = -1;
|
||||
int mode = len > 0 ? buf[0] & 7 : 0;
|
||||
if (len < 48) {
|
||||
LOG(LL_ERROR, ("%s", "corrupt packet"));
|
||||
} else if ((buf[0] & 0x38) >> 3 != 4) {
|
||||
@@ -21,21 +22,22 @@ int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv) {
|
||||
LOG(LL_ERROR, ("%s", "server sent a kiss of death"));
|
||||
} else {
|
||||
uint32_t *data = (uint32_t *) &buf[40];
|
||||
tv->tv_sec = (time_t) (mg_ntohl(data[0]) - SNTP_TIME_OFFSET);
|
||||
tv->tv_usec = (suseconds_t) mg_ntohl(data[1]);
|
||||
s_sntmp_next = (unsigned long) (tv->tv_sec + SNTP_INTERVAL_SEC);
|
||||
res = 0;
|
||||
unsigned long seconds = mg_ntohl(data[0]) - SNTP_TIME_OFFSET;
|
||||
unsigned long useconds = mg_ntohl(data[1]);
|
||||
// LOG(LL_DEBUG, ("%lu %lu %lu", time(0), seconds, useconds));
|
||||
res = ((int64_t) seconds) * 1000 + (int64_t) ((useconds / 1000) % 1000);
|
||||
s_sntmp_next = seconds + SNTP_INTERVAL_SEC;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
||||
if (ev == MG_EV_READ) {
|
||||
struct timeval tv = {0, 0};
|
||||
if (mg_sntp_parse(c->recv.buf, c->recv.len, &tv) == 0) {
|
||||
mg_call(c, MG_EV_SNTP_TIME, &tv);
|
||||
LOG(LL_DEBUG, ("%u.%u, next at %lu", (unsigned) tv.tv_sec,
|
||||
(unsigned) tv.tv_usec, s_sntmp_next));
|
||||
int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len);
|
||||
if (milliseconds > 0) {
|
||||
mg_call(c, MG_EV_SNTP_TIME, &milliseconds);
|
||||
LOG(LL_DEBUG, ("%u.%u, next at %lu", (unsigned) (milliseconds / 1000),
|
||||
(unsigned) (milliseconds % 1000), s_sntmp_next));
|
||||
}
|
||||
c->recv.len = 0; // Clear receive buffer
|
||||
} else if (ev == MG_EV_CONNECT) {
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@
|
||||
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data);
|
||||
void mg_sntp_send(struct mg_connection *c, unsigned long utc);
|
||||
int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv);
|
||||
int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
|
||||
+76
-70
@@ -99,26 +99,66 @@ static struct mg_connection *alloc_conn(struct mg_mgr *mgr, bool is_client,
|
||||
return c;
|
||||
}
|
||||
|
||||
static void iolog(struct mg_connection *c, char *buf, long n, bool r) {
|
||||
LOG(n > 0 ? LL_VERBOSE_DEBUG : LL_DEBUG,
|
||||
("%-3lu %d%d%d%d%d%d%d%d%d%d%d%d%d%d %d:%d %ld err %d", c->id,
|
||||
c->is_listening, c->is_client, c->is_accepted, c->is_resolving,
|
||||
c->is_connecting, c->is_tls, c->is_tls_hs, c->is_udp, c->is_websocket,
|
||||
c->is_hexdumping, c->is_draining, c->is_closing, c->is_readable,
|
||||
c->is_writable, (int) c->send.len, (int) c->recv.len, n, MG_SOCK_ERRNO));
|
||||
if (n == 0) {
|
||||
// Do nothing
|
||||
} else if (n < 0) {
|
||||
c->is_closing = 1; // Error, or normal termination
|
||||
} else if (n > 0) {
|
||||
if (c->is_hexdumping) {
|
||||
union usa usa;
|
||||
char t1[50] = "", t2[50] = "";
|
||||
socklen_t slen = sizeof(usa.sin);
|
||||
char *s = mg_hexdump(buf, (size_t) n);
|
||||
struct mg_addr a;
|
||||
memset(&usa, 0, sizeof(usa));
|
||||
memset(&a, 0, sizeof(a));
|
||||
getsockname(FD(c), &usa.sa, &slen);
|
||||
tomgaddr(&usa, &a, c->peer.is_ip6);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %s %s %ld\n%s", c->id,
|
||||
mg_straddr(&a, t1, sizeof(t1)), r ? "<-" : "->",
|
||||
mg_straddr(&c->peer, t2, sizeof(t2)), c->label, n, s));
|
||||
free(s);
|
||||
(void) t1, (void) t2; // Silence warnings for MG_ENABLE_LOG=0
|
||||
}
|
||||
if (r) {
|
||||
struct mg_str evd = mg_str_n(buf, (size_t) n);
|
||||
c->recv.len += (size_t) n;
|
||||
mg_call(c, MG_EV_READ, &evd);
|
||||
} else {
|
||||
mg_iobuf_del(&c->send, 0, (size_t) n);
|
||||
if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
|
||||
mg_call(c, MG_EV_WRITE, &n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
long n;
|
||||
#if !defined(__APPLE__)
|
||||
// See #1338, #1382. On Windows, UDP send() can fail despite connected.
|
||||
// Use sendto() instead. But not on Mac - we'll get EISCONN
|
||||
if (c->is_udp) {
|
||||
union usa usa;
|
||||
socklen_t slen = tousa(&c->peer, &usa);
|
||||
n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
||||
}
|
||||
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n;
|
||||
}
|
||||
|
||||
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
return c->is_udp ? mg_sock_send(c, buf, len) > 0
|
||||
: mg_iobuf_add(&c->send, c->send.len, buf, len, MG_IO_SIZE);
|
||||
if (c->is_udp) {
|
||||
long n = mg_sock_send(c, buf, len);
|
||||
iolog(c, (char *) buf, n, false);
|
||||
return n > 0;
|
||||
} else {
|
||||
return mg_iobuf_add(&c->send, c->send.len, buf, len, MG_IO_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_set_non_blocking_mode(SOCKET fd) {
|
||||
@@ -134,8 +174,8 @@ static void mg_set_non_blocking_mode(SOCKET fd) {
|
||||
#elif MG_ARCH == MG_ARCH_AZURERTOS
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#else
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); // Set non-blocking mode
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -228,26 +268,7 @@ static long read_conn(struct mg_connection *c) {
|
||||
char *buf = (char *) &c->recv.buf[c->recv.len];
|
||||
size_t len = c->recv.size - c->recv.len;
|
||||
n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_sock_recv(c, buf, len);
|
||||
LOG(n > 0 ? LL_VERBOSE_DEBUG : LL_DEBUG,
|
||||
("%-3lu %d%d%d%d%d%d%d%d%d%d%d%d%d%d %7ld %ld/%ld err %d", c->id,
|
||||
c->is_listening, c->is_client, c->is_accepted, c->is_resolving,
|
||||
c->is_connecting, c->is_tls, c->is_tls_hs, c->is_udp, c->is_websocket,
|
||||
c->is_hexdumping, c->is_draining, c->is_closing, c->is_readable,
|
||||
c->is_writable, (long) c->recv.len, n, (long) len, MG_SOCK_ERRNO));
|
||||
if (n == 0) {
|
||||
// Do nothing
|
||||
} else if (n < 0) {
|
||||
c->is_closing = 1; // Error, or normal termination
|
||||
} else if (n > 0) {
|
||||
struct mg_str evd = mg_str_n(buf, (size_t) n);
|
||||
if (c->is_hexdumping) {
|
||||
char *s = mg_hexdump(buf, (size_t) n);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %ld\n%s", c->id, c->label, "<-", n, s));
|
||||
free(s);
|
||||
}
|
||||
c->recv.len += (size_t) n;
|
||||
mg_call(c, MG_EV_READ, &evd);
|
||||
}
|
||||
iolog(c, buf, n, true);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -256,30 +277,7 @@ static void write_conn(struct mg_connection *c) {
|
||||
char *buf = (char *) c->send.buf;
|
||||
size_t len = c->send.len;
|
||||
long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_sock_send(c, buf, len);
|
||||
|
||||
LOG(n > 0 ? LL_VERBOSE_DEBUG : LL_DEBUG,
|
||||
("%-3lu %d%d%d%d%d%d%d%d%d%d%d%d%d%d %7ld %ld err %d", c->id,
|
||||
c->is_listening, c->is_client, c->is_accepted, c->is_resolving,
|
||||
c->is_connecting, c->is_tls, c->is_tls_hs, c->is_udp, c->is_websocket,
|
||||
c->is_hexdumping, c->is_draining, c->is_closing, c->is_readable,
|
||||
c->is_writable, (long) c->send.len, n, MG_SOCK_ERRNO));
|
||||
|
||||
if (n == 0) {
|
||||
// Do nothing
|
||||
} else if (n < 0) {
|
||||
c->is_closing = 1; // Error, or normal termination
|
||||
} else if (n > 0) {
|
||||
// Hexdump before deleting
|
||||
if (c->is_hexdumping) {
|
||||
char *s = mg_hexdump(buf, (size_t) n);
|
||||
LOG(LL_INFO, ("\n-- %lu %s %s %ld\n%s", c->id, c->label, "<-", n, s));
|
||||
free(s);
|
||||
}
|
||||
mg_iobuf_del(&c->send, 0, (size_t) n);
|
||||
if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
|
||||
mg_call(c, MG_EV_WRITE, &n);
|
||||
// if (c->send.len == 0) mg_iobuf_resize(&c->send, 0);
|
||||
}
|
||||
iolog(c, buf, n, false);
|
||||
}
|
||||
|
||||
static void close_conn(struct mg_connection *c) {
|
||||
@@ -334,18 +332,21 @@ static void setsockopts(struct mg_connection *c) {
|
||||
}
|
||||
|
||||
void mg_connect_resolved(struct mg_connection *c) {
|
||||
char buf[40];
|
||||
// char buf[40];
|
||||
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
|
||||
int rc, af = c->peer.is_ip6 ? AF_INET6 : AF_INET;
|
||||
mg_straddr(c, buf, sizeof(buf));
|
||||
// mg_straddr(&c->peer, buf, sizeof(buf));
|
||||
c->fd = S2PTR(socket(af, type, 0));
|
||||
if (FD(c) == INVALID_SOCKET) {
|
||||
mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
|
||||
} else if (c->is_udp) {
|
||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
} else {
|
||||
union usa usa;
|
||||
socklen_t slen = tousa(&c->peer, &usa);
|
||||
if (c->is_udp == 0) mg_set_non_blocking_mode(FD(c));
|
||||
if (c->is_udp == 0) setsockopts(c);
|
||||
mg_set_non_blocking_mode(FD(c));
|
||||
setsockopts(c);
|
||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||
if ((rc = connect(FD(c), &usa.sa, slen)) == 0) {
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
@@ -360,18 +361,18 @@ void mg_connect_resolved(struct mg_connection *c) {
|
||||
struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data) {
|
||||
struct mg_connection *c = NULL;
|
||||
if ((c = alloc_conn(mgr, 1, INVALID_SOCKET)) == NULL) {
|
||||
if (url == NULL || url[0] == '\0') {
|
||||
LOG(LL_ERROR, ("null url"));
|
||||
} else if ((c = alloc_conn(mgr, 1, INVALID_SOCKET)) == NULL) {
|
||||
LOG(LL_ERROR, ("OOM"));
|
||||
} else {
|
||||
struct mg_str host = mg_url_host(url);
|
||||
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
|
||||
c->is_udp = (strncmp(url, "udp:", 4) == 0);
|
||||
c->peer.port = mg_htons(mg_url_port(url));
|
||||
c->fn = fn;
|
||||
c->fn_data = fn_data;
|
||||
LOG(LL_DEBUG, ("%lu -> %s", c->id, url));
|
||||
mg_call(c, MG_EV_OPEN, NULL);
|
||||
mg_resolve(c, &host, mgr->dnstimeout);
|
||||
mg_resolve(c, url);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -400,7 +401,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
|
||||
} else {
|
||||
char buf[40];
|
||||
tomgaddr(&usa, &c->peer, sa_len != sizeof(usa.sin));
|
||||
mg_straddr(c, buf, sizeof(buf));
|
||||
mg_straddr(&c->peer, buf, sizeof(buf));
|
||||
LOG(LL_DEBUG, ("%lu accepted %s", c->id, buf));
|
||||
mg_set_non_blocking_mode(FD(c));
|
||||
setsockopts(c);
|
||||
@@ -443,13 +444,17 @@ static bool mg_socketpair(SOCKET sp[2], union usa usa[2]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void mg_mgr_wakeup(struct mg_connection *c) {
|
||||
LOG(LL_INFO, ("skt: %p", c->pfn_data));
|
||||
send((SOCKET) (size_t) c->pfn_data, "\x01", 1, MSG_NONBLOCKING);
|
||||
void mg_mgr_wakeup(struct mg_connection *c, const void *buf, size_t len) {
|
||||
if (buf == NULL || len == 0) buf = (void *) "", len = 1;
|
||||
send((SOCKET) (size_t) c->pfn_data, (const char *) buf, len, MSG_NONBLOCKING);
|
||||
}
|
||||
|
||||
static void pf1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_READ) mg_iobuf_free(&c->recv);
|
||||
if (ev == MG_EV_READ) {
|
||||
mg_iobuf_free(&c->recv);
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
closesocket((SOCKET) (size_t) c->pfn_data);
|
||||
}
|
||||
(void) ev_data, (void) fn_data;
|
||||
}
|
||||
|
||||
@@ -562,8 +567,9 @@ static void connect_conn(struct mg_connection *c) {
|
||||
if (rc == EAGAIN || rc == EWOULDBLOCK) rc = 0;
|
||||
c->is_connecting = 0;
|
||||
if (rc) {
|
||||
char buf[40];
|
||||
mg_error(c, "error connecting to %s", mg_straddr(c, buf, sizeof(buf)));
|
||||
char buf[50];
|
||||
mg_error(c, "error connecting to %s",
|
||||
mg_straddr(&c->peer, buf, sizeof(buf)));
|
||||
} else {
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
@@ -572,7 +578,7 @@ static void connect_conn(struct mg_connection *c) {
|
||||
|
||||
void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
||||
struct mg_connection *c, *tmp;
|
||||
unsigned long now;
|
||||
int64_t now;
|
||||
|
||||
mg_iotest(mgr, ms);
|
||||
now = mg_millis();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "arch.h"
|
||||
|
||||
struct mg_str {
|
||||
const char *ptr; // Pointer to string data
|
||||
|
||||
+6
-7
@@ -6,7 +6,7 @@
|
||||
|
||||
struct mg_timer *g_timers;
|
||||
|
||||
void mg_timer_init(struct mg_timer *t, unsigned long ms, unsigned flags,
|
||||
void mg_timer_init(struct mg_timer *t, int64_t ms, unsigned flags,
|
||||
void (*fn)(void *), void *arg) {
|
||||
struct mg_timer tmp = {ms, 0UL, flags, fn, arg, g_timers};
|
||||
*t = tmp;
|
||||
@@ -20,11 +20,11 @@ void mg_timer_free(struct mg_timer *t) {
|
||||
if (*head) *head = t->next;
|
||||
}
|
||||
|
||||
void mg_timer_poll(unsigned long now_ms) {
|
||||
void mg_timer_poll(int64_t now_ms) {
|
||||
// If time goes back (wrapped around), reset timers
|
||||
struct mg_timer *t, *tmp;
|
||||
static unsigned long oldnow; // Timestamp in a previous invocation
|
||||
if (oldnow > now_ms) { // If it is wrapped, reset timers
|
||||
static int64_t oldnow; // Timestamp in a previous invocation
|
||||
if (oldnow > now_ms) { // If it is wrapped, reset timers
|
||||
for (t = g_timers; t != NULL; t = t->next) t->expire = 0;
|
||||
}
|
||||
oldnow = now_ms;
|
||||
@@ -36,9 +36,8 @@ void mg_timer_poll(unsigned long now_ms) {
|
||||
t->fn(t->arg);
|
||||
// Try to tick timers with the given period as accurate as possible,
|
||||
// even if this polling function is called with some random period.
|
||||
t->expire = now_ms - t->expire > (unsigned long) t->period_ms
|
||||
? now_ms + t->period_ms
|
||||
: t->expire + t->period_ms;
|
||||
t->expire = now_ms - t->expire > t->period_ms ? now_ms + t->period_ms
|
||||
: t->expire + t->period_ms;
|
||||
if (!(t->flags & MG_TIMER_REPEAT)) mg_timer_free(t);
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
|
||||
struct mg_timer {
|
||||
unsigned long period_ms; // Timer period in milliseconds
|
||||
unsigned long expire; // Expiration timestamp in milliseconds
|
||||
int64_t period_ms; // Timer period in milliseconds
|
||||
int64_t expire; // Expiration timestamp in milliseconds
|
||||
unsigned flags; // Possible flags values below
|
||||
#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once
|
||||
#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
|
||||
@@ -13,7 +15,7 @@ struct mg_timer {
|
||||
|
||||
extern struct mg_timer *g_timers; // Global list of timers
|
||||
|
||||
void mg_timer_init(struct mg_timer *, unsigned long ms, unsigned,
|
||||
void (*fn)(void *), void *);
|
||||
void mg_timer_init(struct mg_timer *, int64_t, unsigned, void (*)(void *),
|
||||
void *);
|
||||
void mg_timer_free(struct mg_timer *);
|
||||
void mg_timer_poll(unsigned long current_time_ms);
|
||||
void mg_timer_poll(int64_t current_time_ms);
|
||||
@@ -11,6 +11,7 @@ struct mg_tls_opts {
|
||||
const char *certkey; // Certificate key
|
||||
const char *ciphers; // Cipher list
|
||||
struct mg_str srvname; // If not empty, enables server name verification
|
||||
struct mg_fs *fs; // FS API for reading certificate files
|
||||
};
|
||||
|
||||
void mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
|
||||
|
||||
+76
-61
@@ -1,10 +1,54 @@
|
||||
#include "fs.h"
|
||||
#include "tls.h"
|
||||
|
||||
#if MG_ENABLE_MBEDTLS
|
||||
|
||||
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
|
||||
#define MGRNG , rng_get, NULL
|
||||
#else
|
||||
#define MGRNG
|
||||
#endif
|
||||
|
||||
void mg_tls_free(struct mg_connection *c) {
|
||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||
if (tls != NULL) {
|
||||
free(tls->cafile);
|
||||
mbedtls_ssl_free(&tls->ssl);
|
||||
mbedtls_pk_free(&tls->pk);
|
||||
mbedtls_x509_crt_free(&tls->ca);
|
||||
mbedtls_x509_crl_free(&tls->crl);
|
||||
mbedtls_x509_crt_free(&tls->cert);
|
||||
mbedtls_ssl_config_free(&tls->conf);
|
||||
free(tls);
|
||||
c->tls = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mg_wouldblock(int n) {
|
||||
return n < 0 &&
|
||||
(errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK);
|
||||
}
|
||||
|
||||
static int mg_net_send(void *ctx, const unsigned char *buf, size_t len) {
|
||||
int fd = *(int *) ctx;
|
||||
int n = (int) send(fd, buf, len, 0);
|
||||
if (n > 0) return n;
|
||||
if (mg_wouldblock(n)) return MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
return MBEDTLS_ERR_NET_SEND_FAILED;
|
||||
}
|
||||
|
||||
static int mg_net_recv(void *ctx, unsigned char *buf, size_t len) {
|
||||
int fd = *(int *) ctx;
|
||||
int n = (int) recv(fd, buf, len, 0);
|
||||
if (n > 0) return n;
|
||||
if (mg_wouldblock(n)) return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
return MBEDTLS_ERR_NET_RECV_FAILED;
|
||||
}
|
||||
|
||||
void mg_tls_handshake(struct mg_connection *c) {
|
||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||
int rc;
|
||||
mbedtls_ssl_set_bio(&tls->ssl, &c->fd, mbedtls_net_send, mbedtls_net_recv, 0);
|
||||
mbedtls_ssl_set_bio(&tls->ssl, &c->fd, mg_net_send, mg_net_recv, 0);
|
||||
rc = mbedtls_ssl_handshake(&tls->ssl);
|
||||
if (rc == 0) { // Success
|
||||
LOG(LL_DEBUG, ("%lu success", c->id));
|
||||
@@ -40,27 +84,23 @@ static int rng_get(void *p_rng, unsigned char *buf, size_t len) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct mg_str mg_loadfile(struct mg_fs *fs, const char *path) {
|
||||
size_t n = 0;
|
||||
if (path[0] == '-') return mg_str(path);
|
||||
char *p = mg_file_read(fs, path, &n);
|
||||
return mg_str_n(p, n);
|
||||
}
|
||||
|
||||
void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
|
||||
struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
|
||||
int rc = 0;
|
||||
const char *ca = opts->ca == NULL ? "-"
|
||||
: opts->ca[0] == '-' ? "(emb)"
|
||||
: opts->ca;
|
||||
const char *crl = opts->crl == NULL ? "-"
|
||||
: opts->crl[0] == '-' ? "(emb)"
|
||||
: opts->crl;
|
||||
const char *cert = opts->cert == NULL ? "-"
|
||||
: opts->cert[0] == '-' ? "(emb)"
|
||||
: opts->cert;
|
||||
const char *certkey = opts->certkey == NULL ? "-"
|
||||
: opts->certkey[0] == '-' ? "(emb)"
|
||||
: opts->certkey;
|
||||
if (tls == NULL) {
|
||||
c->tls = tls;
|
||||
if (c->tls == NULL) {
|
||||
mg_error(c, "TLS OOM");
|
||||
goto fail;
|
||||
}
|
||||
LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, CRL: %s, cert: %s, key: %s", c->id,
|
||||
ca, crl, cert, certkey));
|
||||
LOG(LL_DEBUG, ("%lu Setting TLS", c->id));
|
||||
mbedtls_ssl_init(&tls->ssl);
|
||||
mbedtls_ssl_config_init(&tls->conf);
|
||||
mbedtls_x509_crt_init(&tls->ca);
|
||||
@@ -68,9 +108,6 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
mbedtls_x509_crt_init(&tls->cert);
|
||||
mbedtls_pk_init(&tls->pk);
|
||||
mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
|
||||
//#if !defined(ESP_PLATFORM)
|
||||
// mbedtls_debug_set_threshold(5);
|
||||
//#endif
|
||||
if ((rc = mbedtls_ssl_config_defaults(
|
||||
&tls->conf,
|
||||
c->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
|
||||
@@ -81,15 +118,13 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
mbedtls_ssl_conf_rng(&tls->conf, mbed_rng, c);
|
||||
if (opts->ca == NULL || strcmp(opts->ca, "*") == 0) {
|
||||
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
|
||||
}
|
||||
if (opts->ca != NULL && opts->ca[0] != '\0') {
|
||||
} else if (opts->ca != NULL && opts->ca[0] != '\0') {
|
||||
if (opts->crl != NULL && opts->crl[0] != '\0') {
|
||||
rc = opts->crl[0] == '-'
|
||||
? mbedtls_x509_crl_parse(&tls->crl, (uint8_t *) opts->crl,
|
||||
strlen(opts->crl) + 1)
|
||||
: mbedtls_x509_crl_parse_file(&tls->crl, opts->crl);
|
||||
struct mg_str s = mg_loadfile(fs, opts->crl);
|
||||
rc = mbedtls_x509_crl_parse(&tls->crl, (uint8_t *) s.ptr, s.len + 1);
|
||||
if (opts->crl[0] != '-') free((char *) s.ptr);
|
||||
if (rc != 0) {
|
||||
mg_error(c, "parse(%s) err %#x", crl, -rc);
|
||||
mg_error(c, "parse(%s) err %#x", opts->crl, -rc);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -101,12 +136,11 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
rc = opts->ca[0] == '-'
|
||||
? mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) opts->ca,
|
||||
strlen(opts->ca) + 1)
|
||||
: mbedtls_x509_crt_parse_file(&tls->ca, opts->ca);
|
||||
struct mg_str s = mg_loadfile(fs, opts->ca);
|
||||
rc = mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) s.ptr, s.len + 1);
|
||||
if (opts->ca[0] != '-') free((char *) s.ptr);
|
||||
if (rc != 0) {
|
||||
mg_error(c, "parse(%s) err %#x", ca, -rc);
|
||||
mg_error(c, "parse(%s) err %#x", opts->ca, -rc);
|
||||
goto fail;
|
||||
}
|
||||
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, &tls->crl);
|
||||
@@ -121,24 +155,20 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||
}
|
||||
if (opts->cert != NULL && opts->cert[0] != '\0') {
|
||||
const char *key = opts->certkey;
|
||||
if (key == NULL) {
|
||||
key = opts->cert;
|
||||
certkey = cert;
|
||||
}
|
||||
rc = opts->cert[0] == '-'
|
||||
? mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) opts->cert,
|
||||
strlen(opts->cert) + 1)
|
||||
: mbedtls_x509_crt_parse_file(&tls->cert, opts->cert);
|
||||
struct mg_str s = mg_loadfile(fs, opts->cert);
|
||||
const char *key = opts->certkey == NULL ? opts->cert : opts->certkey;
|
||||
rc = mbedtls_x509_crt_parse(&tls->cert, (uint8_t *) s.ptr, s.len + 1);
|
||||
if (opts->cert[0] != '-') free((char *) s.ptr);
|
||||
if (rc != 0) {
|
||||
mg_error(c, "parse(%s) err %#x", cert, -rc);
|
||||
mg_error(c, "parse(%s) err %#x", opts->cert, -rc);
|
||||
goto fail;
|
||||
}
|
||||
rc = key[0] == '-' ? mbedtls_pk_parse_key(&tls->pk, (uint8_t *) key,
|
||||
strlen(key) + 1, NULL, 0 RNG)
|
||||
: mbedtls_pk_parse_keyfile(&tls->pk, key, NULL RNG);
|
||||
s = mg_loadfile(fs, key);
|
||||
rc = mbedtls_pk_parse_key(&tls->pk, (uint8_t *) s.ptr, s.len + 1, NULL,
|
||||
0 MGRNG);
|
||||
if (key[0] != '-') free((char *) s.ptr);
|
||||
if (rc != 0) {
|
||||
mg_error(c, "tls key(%s) %#x", certkey, -rc);
|
||||
mg_error(c, "tls key(%s) %#x", key, -rc);
|
||||
goto fail;
|
||||
}
|
||||
rc = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->cert, &tls->pk);
|
||||
@@ -159,8 +189,7 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
c->is_closing = 1;
|
||||
free(tls);
|
||||
mg_tls_free(c);
|
||||
}
|
||||
|
||||
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
|
||||
@@ -174,18 +203,4 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
long n = mbedtls_ssl_write(&tls->ssl, (unsigned char *) buf, len);
|
||||
return n == 0 ? -1 : n == MBEDTLS_ERR_SSL_WANT_WRITE ? 0 : n;
|
||||
}
|
||||
|
||||
void mg_tls_free(struct mg_connection *c) {
|
||||
struct mg_tls *tls = (struct mg_tls *) c->tls;
|
||||
if (tls == NULL) return;
|
||||
free(tls->cafile);
|
||||
mbedtls_ssl_free(&tls->ssl);
|
||||
mbedtls_pk_free(&tls->pk);
|
||||
mbedtls_x509_crt_free(&tls->ca);
|
||||
mbedtls_x509_crl_free(&tls->crl);
|
||||
mbedtls_x509_crt_free(&tls->cert);
|
||||
mbedtls_ssl_config_free(&tls->conf);
|
||||
free(tls);
|
||||
c->tls = NULL;
|
||||
}
|
||||
#endif
|
||||
+1
-10
@@ -6,18 +6,9 @@
|
||||
#include "util.h"
|
||||
|
||||
#include <mbedtls/debug.h>
|
||||
#include <mbedtls/net_sockets.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
|
||||
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
|
||||
#define RNG , rng_get, NULL
|
||||
#else
|
||||
#define RNG
|
||||
#endif
|
||||
|
||||
// Different versions have those in different files, so declare here
|
||||
EXTERN_C int mbedtls_net_recv(void *, unsigned char *, size_t);
|
||||
EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t);
|
||||
|
||||
struct mg_tls {
|
||||
char *cafile; // CA certificate path
|
||||
mbedtls_x509_crt ca; // Parsed CA certificate
|
||||
|
||||
@@ -102,7 +102,6 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
|
||||
if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
|
||||
mg_tls_handshake(c);
|
||||
}
|
||||
c->is_hexdumping = 1;
|
||||
LOG(LL_DEBUG, ("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
|
||||
return;
|
||||
fail:
|
||||
|
||||
@@ -56,10 +56,11 @@ const char *mg_url_uri(const char *url) {
|
||||
unsigned short mg_url_port(const char *url) {
|
||||
struct url u = urlparse(url);
|
||||
unsigned short port = 0;
|
||||
if (memcmp(url, "http:", 5) == 0 || memcmp(url, "ws:", 3) == 0) port = 80;
|
||||
if (memcmp(url, "wss:", 4) == 0 || memcmp(url, "https:", 6) == 0) port = 443;
|
||||
if (memcmp(url, "mqtt:", 5) == 0) port = 1883;
|
||||
if (memcmp(url, "mqtts:", 6) == 0) port = 8883;
|
||||
if (strncmp(url, "http:", 5) == 0 || strncmp(url, "ws:", 3) == 0) port = 80;
|
||||
if (strncmp(url, "wss:", 4) == 0 || strncmp(url, "https:", 6) == 0)
|
||||
port = 443;
|
||||
if (strncmp(url, "mqtt:", 5) == 0) port = 1883;
|
||||
if (strncmp(url, "mqtts:", 6) == 0) port = 8883;
|
||||
if (u.port) port = (unsigned short) atoi(url + u.port);
|
||||
return port;
|
||||
}
|
||||
|
||||
+13
-109
@@ -1,66 +1,9 @@
|
||||
#include "config.h" // Must go first - important for test++
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#if MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
char *mg_file_read(const char *path, size_t *sizep) {
|
||||
FILE *fp;
|
||||
char *data = NULL;
|
||||
size_t size = 0;
|
||||
if ((fp = fopen(path, "rb")) != NULL) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = (size_t) ftell(fp);
|
||||
rewind(fp);
|
||||
data = (char *) calloc(1, size + 1);
|
||||
if (data != NULL) {
|
||||
if (fread(data, 1, size, fp) != size) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
} else {
|
||||
data[size] = '\0';
|
||||
if (sizep != NULL) *sizep = size;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
bool mg_file_write(const char *path, const void *buf, size_t len) {
|
||||
bool result = false;
|
||||
FILE *fp;
|
||||
char tmp[MG_PATH_MAX];
|
||||
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
|
||||
fp = fopen(tmp, "wb");
|
||||
if (fp != NULL) {
|
||||
result = fwrite(buf, 1, len, fp) == len;
|
||||
fclose(fp);
|
||||
if (result) {
|
||||
remove(path);
|
||||
rename(tmp, path);
|
||||
} else {
|
||||
remove(tmp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool mg_file_printf(const char *path, const char *fmt, ...) {
|
||||
char tmp[256], *buf = tmp;
|
||||
bool result;
|
||||
int len;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap);
|
||||
va_end(ap);
|
||||
result = mg_file_write(path, buf, len > 0 ? (size_t) len : 0);
|
||||
if (buf != tmp) free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if MG_ENABLE_CUSTOM_RANDOM
|
||||
#else
|
||||
void mg_random(void *buf, size_t len) {
|
||||
@@ -70,7 +13,7 @@ void mg_random(void *buf, size_t len) {
|
||||
while (len--) *p++ = (unsigned char) (esp_random() & 255);
|
||||
done = true;
|
||||
#elif MG_ARCH == MG_ARCH_WIN32
|
||||
#elif MG_ARCH_UNIX
|
||||
#elif MG_ARCH == MG_ARCH_UNIX
|
||||
FILE *fp = fopen("/dev/urandom", "rb");
|
||||
if (fp != NULL) {
|
||||
if (fread(buf, 1, len, fp) == len) done = true;
|
||||
@@ -113,8 +56,9 @@ static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
|
||||
}
|
||||
|
||||
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
|
||||
size_t koff = 0, klen = 0, voff = 0, vlen = 0;
|
||||
size_t off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
|
||||
size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
|
||||
if (s->ptr == NULL || s->len == 0) return 0;
|
||||
off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
|
||||
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
|
||||
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
|
||||
*s = mg_str_n(s->ptr + off, s->len - off);
|
||||
@@ -294,53 +238,12 @@ int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) {
|
||||
uint32_t net, mask;
|
||||
if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1;
|
||||
if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2;
|
||||
if ((remote_ip & mask) == net) allowed = k.ptr[0];
|
||||
if ((mg_ntohl(remote_ip) & mask) == net) allowed = k.ptr[0];
|
||||
}
|
||||
return allowed == '+';
|
||||
}
|
||||
|
||||
double mg_time(void) {
|
||||
#if MG_ARCH == MG_ARCH_WIN32
|
||||
SYSTEMTIME sysnow;
|
||||
FILETIME ftime;
|
||||
GetLocalTime(&sysnow);
|
||||
SystemTimeToFileTime(&sysnow, &ftime);
|
||||
/*
|
||||
* 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64
|
||||
* This should not cause a problems in this (21th) century
|
||||
* 2. Windows FILETIME is a number of 100-nanosecond intervals since January
|
||||
* 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC,
|
||||
* thus, we need to convert to seconds and adjust amount (subtract 11644473600
|
||||
* seconds)
|
||||
*/
|
||||
return (double) (((int64_t) ftime.dwLowDateTime +
|
||||
((int64_t) ftime.dwHighDateTime << 32)) /
|
||||
10000000.0) -
|
||||
11644473600;
|
||||
#elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS
|
||||
return mg_millis() / 1000.0;
|
||||
#else
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL /* tz */) != 0) return 0;
|
||||
return (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0);
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
void mg_usleep(unsigned long usecs) {
|
||||
#if MG_ARCH == MG_ARCH_WIN32
|
||||
Sleep(usecs / 1000);
|
||||
#elif MG_ARCH == MG_ARCH_ESP8266
|
||||
ets_delay_us(usecs);
|
||||
#elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP
|
||||
vTaskDelay(pdMS_TO_TICKS(usecs / 1000));
|
||||
#elif MG_ARCH == MG_ARCH_AZURERTOS
|
||||
tx_thread_sleep((usecs / 1000) * TX_TIMER_TICKS_PER_SECOND);
|
||||
#else
|
||||
usleep((useconds_t) usecs);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long mg_millis(void) {
|
||||
int64_t mg_millis(void) {
|
||||
#if MG_ARCH == MG_ARCH_WIN32
|
||||
return GetTickCount();
|
||||
#elif MG_ARCH == MG_ARCH_ESP32
|
||||
@@ -355,10 +258,10 @@ unsigned long mg_millis(void) {
|
||||
uint64_t ticks = mach_absolute_time();
|
||||
static mach_timebase_info_data_t timebase;
|
||||
mach_timebase_info(&timebase);
|
||||
double ticks_to_nanos = (double)timebase.numer / timebase.denom;
|
||||
uint64_t uptime_nanos = (uint64_t)(ticks_to_nanos * ticks);
|
||||
return (unsigned long) (uptime_nanos / 1000000);
|
||||
#else
|
||||
double ticks_to_nanos = (double) timebase.numer / timebase.denom;
|
||||
uint64_t uptime_nanos = (uint64_t) (ticks_to_nanos * ticks);
|
||||
return (int64_t) (uptime_nanos / 1000000);
|
||||
#elif MG_ARCH == MG_ARCH_UNIX
|
||||
struct timespec ts;
|
||||
#ifdef _POSIX_MONOTONIC_CLOCK
|
||||
#ifdef CLOCK_MONOTONIC_RAW
|
||||
@@ -369,7 +272,8 @@ unsigned long mg_millis(void) {
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
#endif
|
||||
return (unsigned long) ((uint64_t) ts.tv_sec * 1000 +
|
||||
(uint64_t) ts.tv_nsec / 1000000);
|
||||
return ((int64_t) ts.tv_sec * 1000 + (int64_t) ts.tv_nsec / 1000000);
|
||||
#else
|
||||
return time(NULL) * 1000;
|
||||
#endif
|
||||
}
|
||||
+3
-7
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "config.h"
|
||||
#include "str.h"
|
||||
|
||||
char *mg_file_read(const char *path, size_t *size);
|
||||
bool mg_file_write(const char *path, const void *buf, size_t len);
|
||||
bool mg_file_printf(const char *path, const char *fmt, ...);
|
||||
void mg_random(void *buf, size_t len);
|
||||
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
|
||||
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
|
||||
@@ -18,11 +16,9 @@ void mg_unhex(const char *buf, size_t len, unsigned char *to);
|
||||
unsigned long mg_unhexn(const char *s, size_t len);
|
||||
int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
|
||||
int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
|
||||
int64_t mg_to64(struct mg_str str);
|
||||
int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
|
||||
double mg_time(void);
|
||||
unsigned long mg_millis(void);
|
||||
void mg_usleep(unsigned long usecs);
|
||||
int64_t mg_to64(struct mg_str str);
|
||||
int64_t mg_millis(void);
|
||||
|
||||
#define mg_htons(x) mg_ntohs(x)
|
||||
#define mg_htonl(x) mg_ntohl(x)
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define MG_VERSION "7.5"
|
||||
#define MG_VERSION "7.6"
|
||||
@@ -173,7 +173,7 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
|
||||
if (final) mg_call(c, MG_EV_WS_MSG, &m);
|
||||
break;
|
||||
case WEBSOCKET_OP_CLOSE:
|
||||
LOG(LL_ERROR, ("%lu Got WS CLOSE", c->id));
|
||||
LOG(LL_DEBUG, ("%lu Got WS CLOSE", c->id));
|
||||
mg_call(c, MG_EV_WS_CTL, &m);
|
||||
c->is_closing = 1;
|
||||
break;
|
||||
|
||||
+2
-3
@@ -27,9 +27,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
mg_mqtt_parse(data, size, &mm);
|
||||
mg_mqtt_parse(NULL, 0, &mm);
|
||||
|
||||
struct timeval tv;
|
||||
mg_sntp_parse(data, size, &tv);
|
||||
mg_sntp_parse(NULL, 0, &tv);
|
||||
mg_sntp_parse(data, size);
|
||||
mg_sntp_parse(NULL, 0);
|
||||
|
||||
char buf[size * 4 / 3 + 5]; // At least 4 chars and nul termination
|
||||
mg_base64_decode((char *) data, (int) size, buf);
|
||||
|
||||
+5
-18
@@ -1,20 +1,8 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
int usleep(useconds_t us) {
|
||||
for (useconds_t i = 0; i < us * 99; i++) asm("nop");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clock_gettime(clockid_t clock_id, struct timespec *tp) {
|
||||
(void) clock_id;
|
||||
memset(tp, 0, sizeof(*tp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *realpath(const char *path, char *resolved_path) {
|
||||
if (resolved_path == NULL) resolved_path = malloc(strlen(path) + 1);
|
||||
strcpy(resolved_path, path);
|
||||
return resolved_path;
|
||||
int mkdir(const char *path, mode_t mode) {
|
||||
(void) path, (void) mode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
|
||||
@@ -43,8 +31,8 @@ bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mg_mgr_wakeup(struct mg_connection *c) {
|
||||
(void) c;
|
||||
void mg_mgr_wakeup(struct mg_connection *c, const void *buf, size_t len) {
|
||||
(void) c, (void) buf, (void) len;
|
||||
}
|
||||
|
||||
struct mg_connection *mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn,
|
||||
@@ -53,6 +41,5 @@ struct mg_connection *mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _fini(void);
|
||||
void _fini(void) {
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#define _POSIX_TIMERS
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
@@ -15,9 +16,5 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MG_DIRSEP '/'
|
||||
#define MG_INT64_FMT "%lld"
|
||||
#undef MG_PATH_MAX
|
||||
#define MG_PATH_MAX 100
|
||||
#undef MG_ENABLE_SOCKET
|
||||
#define MG_ENABLE_SOCKET 0
|
||||
+78
-55
@@ -243,24 +243,24 @@ static void test_iobuf(void) {
|
||||
|
||||
static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
||||
if (ev == MG_EV_SNTP_TIME) {
|
||||
*(struct timeval *) fnd = *(struct timeval *) evd;
|
||||
*(int64_t *) fnd = *(int64_t *) evd;
|
||||
}
|
||||
(void) c;
|
||||
}
|
||||
|
||||
static void test_sntp(void) {
|
||||
struct timeval tv = {0, 0};
|
||||
int64_t ms = 0;
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *c = NULL;
|
||||
int i;
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
c = mg_sntp_connect(&mgr, NULL, sntp_cb, &tv);
|
||||
c = mg_sntp_connect(&mgr, NULL, sntp_cb, &ms);
|
||||
ASSERT(c != NULL);
|
||||
ASSERT(c->is_udp == 1);
|
||||
mg_sntp_send(c, (unsigned long) time(NULL));
|
||||
for (i = 0; i < 300 && tv.tv_sec == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(tv.tv_sec > 0);
|
||||
for (i = 0; i < 300 && ms == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(ms > 0);
|
||||
mg_mgr_free(&mgr);
|
||||
|
||||
{
|
||||
@@ -274,22 +274,21 @@ static void test_sntp(void) {
|
||||
"\xc9\xd6\xa2\xdb\xde\xea\x30\x91\x86\xb7\x10\xdb\xde"
|
||||
"\xed\x98\x00\x00\x00\xde\xdb\xde\xed\x99\x0a\xe2\xc7"
|
||||
"\x96\xdb\xde\xed\x99\x0a\xe4\x6b\xda";
|
||||
struct timeval tv2 = {0, 0};
|
||||
struct tm *tm;
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
ASSERT(mg_sntp_parse(sntp_good, sizeof(sntp_good), &tv2) == 0);
|
||||
t = tv2.tv_sec;
|
||||
tm = gmtime(&t);
|
||||
ASSERT(tm->tm_year == 116);
|
||||
ASSERT(tm->tm_mon == 10);
|
||||
ASSERT(tm->tm_mday == 22);
|
||||
ASSERT(tm->tm_hour == 16);
|
||||
ASSERT(tm->tm_min == 15);
|
||||
ASSERT(tm->tm_sec == 21);
|
||||
ASSERT(mg_sntp_parse(bad_good, sizeof(bad_good), &tv2) == -1);
|
||||
ASSERT((ms = mg_sntp_parse(sntp_good, sizeof(sntp_good))) > 0);
|
||||
t = (time_t) (ms / 1000);
|
||||
gmtime_r(&t, &tm);
|
||||
ASSERT(tm.tm_year == 116);
|
||||
ASSERT(tm.tm_mon == 10);
|
||||
ASSERT(tm.tm_mday == 22);
|
||||
ASSERT(tm.tm_hour == 16);
|
||||
ASSERT(tm.tm_min == 15);
|
||||
ASSERT(tm.tm_sec == 21);
|
||||
ASSERT(mg_sntp_parse(bad_good, sizeof(bad_good)) < 0);
|
||||
}
|
||||
|
||||
ASSERT(mg_sntp_parse(NULL, 0, &tv) == -1);
|
||||
ASSERT(mg_sntp_parse(NULL, 0) == -1);
|
||||
}
|
||||
|
||||
static void mqtt_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
||||
@@ -323,30 +322,30 @@ static void test_mqtt(void) {
|
||||
|
||||
// Connect with empty client ID
|
||||
c = mg_mqtt_connect(&mgr, url, NULL, mqtt_cb, buf);
|
||||
for (i = 0; i < 100 && buf[0] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
for (i = 0; i < 200 && buf[0] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(buf[0] == 'X');
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
for (i = 0; i < 100 && buf[1] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
mg_mqtt_sub(c, topic, 1);
|
||||
mg_mqtt_pub(c, topic, data, 1, false);
|
||||
for (i = 0; i < 300 && buf[1] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
// LOG(LL_INFO, ("[%s]", buf));
|
||||
ASSERT(strcmp(buf, "Xx/f12/hi") == 0);
|
||||
|
||||
// Set params
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.qos = 1;
|
||||
opts.clean = true;
|
||||
opts.will_qos = 1;
|
||||
opts.will_retain = true;
|
||||
opts.keepalive = 20;
|
||||
opts.client_id = mg_str("mg_client");
|
||||
opts.will_topic = mg_str("mg_will_topic");
|
||||
opts.will_message = mg_str("mg_will_messsage");
|
||||
c = mg_mqtt_connect(&mgr, url, &opts, mqtt_cb, buf);
|
||||
for (i = 0; i < 200 && buf[0] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
for (i = 0; i < 300 && buf[0] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(buf[0] == 'X');
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
for (i = 0; i < 200 && buf[1] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
mg_mqtt_sub(c, topic, 1);
|
||||
mg_mqtt_pub(c, topic, data, 1, false);
|
||||
for (i = 0; i < 500 && buf[1] == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(strcmp(buf, "Xx/f12/hi") == 0);
|
||||
|
||||
mg_mgr_free(&mgr);
|
||||
@@ -381,7 +380,7 @@ 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_http_upload(c, hm, &mg_fs_posix, ".");
|
||||
} else if (mg_http_match_uri(hm, "/test/")) {
|
||||
struct mg_http_serve_opts sopts;
|
||||
memset(&sopts, 0, sizeof(sopts));
|
||||
@@ -417,9 +416,9 @@ struct fetch_data {
|
||||
};
|
||||
|
||||
static void fcb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct fetch_data *fd = (struct fetch_data *) fn_data;
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
struct fetch_data *fd = (struct fetch_data *) fn_data;
|
||||
snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len,
|
||||
hm->message.ptr);
|
||||
fd->code = atoi(hm->uri.ptr);
|
||||
@@ -449,6 +448,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
|
||||
opts.srvname = host;
|
||||
}
|
||||
mg_tls_init(c, &opts);
|
||||
if (c->tls == NULL) fd.closed = 1;
|
||||
// c->is_hexdumping = 1;
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
@@ -618,7 +618,7 @@ static void test_http_server(void) {
|
||||
ASSERT(cmpbody(buf, "Invalid web root [/BAAADDD!]\n") == 0);
|
||||
|
||||
{
|
||||
char *data = mg_file_read("./test/data/ca.pem", NULL);
|
||||
char *data = mg_file_read(&mg_fs_posix, "./test/data/ca.pem", NULL);
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /ca.pem HTTP/1.0\r\n\n") == 200);
|
||||
ASSERT(cmpbody(buf, data) == 0);
|
||||
free(data);
|
||||
@@ -679,8 +679,7 @@ static void test_http_server(void) {
|
||||
// Test upload
|
||||
char *p;
|
||||
remove("uploaded.txt");
|
||||
ASSERT((p = mg_file_read("uploaded.txt", NULL)) == NULL);
|
||||
|
||||
ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) == NULL);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /upload HTTP/1.0\n"
|
||||
"Content-Length: 1\n\nx") == 400);
|
||||
@@ -693,12 +692,27 @@ static void test_http_server(void) {
|
||||
"POST /upload?name=uploaded.txt&offset=5 HTTP/1.0\r\n"
|
||||
"Content-Length: 6\r\n"
|
||||
"\r\n\nworld") == 200);
|
||||
ASSERT((p = mg_file_read("uploaded.txt", NULL)) != NULL);
|
||||
ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) != NULL);
|
||||
ASSERT(strcmp(p, "hello\nworld") == 0);
|
||||
free(p);
|
||||
remove("uploaded.txt");
|
||||
}
|
||||
|
||||
{
|
||||
// Test upload directory traversal
|
||||
char *p;
|
||||
remove("uploaded.txt");
|
||||
ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) == NULL);
|
||||
ASSERT(fetch(&mgr, buf, url,
|
||||
"POST /upload?name=../uploaded.txt HTTP/1.0\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\nhello") == 200);
|
||||
ASSERT((p = mg_file_read(&mg_fs_posix, "uploaded.txt", NULL)) != NULL);
|
||||
ASSERT(strcmp(p, "hello") == 0);
|
||||
free(p);
|
||||
remove("uploaded.txt");
|
||||
}
|
||||
|
||||
// HEAD request
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200);
|
||||
ASSERT(fetch(&mgr, buf, url, "HEAD /a.txt HTTP/1.0\n\n") == 200);
|
||||
@@ -1153,6 +1167,23 @@ static void fn1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
(void) c;
|
||||
}
|
||||
|
||||
static void test_dns_timeout(const char *dns_server_url, const char *errstr) {
|
||||
// Test timeout
|
||||
struct mg_mgr mgr;
|
||||
char buf[100] = "";
|
||||
int i;
|
||||
mg_mgr_init(&mgr);
|
||||
mgr.dns4.url = dns_server_url;
|
||||
mgr.dnstimeout = 10;
|
||||
LOG(LL_DEBUG, ("opening dummy DNS listener..."));
|
||||
mg_listen(&mgr, mgr.dns4.url, NULL, NULL); // Just discard our queries
|
||||
mg_http_connect(&mgr, "http://google.com", fn1, buf);
|
||||
for (i = 0; i < 50 && buf[0] == '\0'; i++) mg_mgr_poll(&mgr, 1);
|
||||
mg_mgr_free(&mgr);
|
||||
LOG(LL_DEBUG, ("buf: [%s]", buf));
|
||||
ASSERT(strcmp(buf, errstr) == 0);
|
||||
}
|
||||
|
||||
static void test_dns(void) {
|
||||
struct mg_dns_message dm;
|
||||
// txid flags numQ numA numAP numOP
|
||||
@@ -1172,21 +1203,9 @@ static void test_dns(void) {
|
||||
ASSERT(mg_dns_parse(data, sizeof(data), &dm) == 1);
|
||||
ASSERT(strcmp(dm.name, "") == 0);
|
||||
|
||||
{
|
||||
// Test timeout
|
||||
struct mg_mgr mgr;
|
||||
char buf[100] = "";
|
||||
int i;
|
||||
mg_mgr_init(&mgr);
|
||||
mgr.dns4.url = "udp://127.0.0.1:12345";
|
||||
mgr.dnstimeout = 10;
|
||||
LOG(LL_DEBUG, ("opening dummy DNS listener..."));
|
||||
mg_listen(&mgr, mgr.dns4.url, NULL, NULL); // Just discard our queries
|
||||
mg_http_connect(&mgr, "http://google.com", fn1, buf);
|
||||
for (i = 0; i < 50 && buf[0] == '\0'; i++) mg_mgr_poll(&mgr, 1);
|
||||
mg_mgr_free(&mgr);
|
||||
ASSERT(strcmp(buf, "DNS timeout") == 0);
|
||||
}
|
||||
test_dns_timeout("udp://127.0.0.1:12345", "DNS timeout");
|
||||
test_dns_timeout("", "resolver");
|
||||
test_dns_timeout("tcp://0.0.0.0:0", "DNS error");
|
||||
}
|
||||
|
||||
static void test_util(void) {
|
||||
@@ -1195,8 +1214,9 @@ static void test_util(void) {
|
||||
ASSERT(s != NULL);
|
||||
free(s);
|
||||
memset(&a, 0, sizeof(a));
|
||||
ASSERT(mg_file_printf("data.txt", "%s", "hi") == true);
|
||||
ASSERT((p = mg_file_read("data.txt", NULL)) != NULL);
|
||||
ASSERT(mg_file_printf(&mg_fs_posix, "data.txt", "%s", "hi") == true);
|
||||
if (system("ls -l") != 0) (void) 0;
|
||||
ASSERT((p = mg_file_read(&mg_fs_posix, "data.txt", NULL)) != NULL);
|
||||
ASSERT(strcmp(p, "hi") == 0);
|
||||
free(p);
|
||||
remove("data.txt");
|
||||
@@ -1444,7 +1464,8 @@ static void eh7(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
static void test_packed(void) {
|
||||
struct mg_mgr mgr;
|
||||
const char *url = "http://127.0.0.1:12351";
|
||||
char buf[FETCH_BUF_SIZE] = "", *data = mg_file_read("Makefile", NULL);
|
||||
char buf[FETCH_BUF_SIZE] = "",
|
||||
*data = mg_file_read(&mg_fs_posix, "Makefile", NULL);
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, url, eh7, NULL);
|
||||
|
||||
@@ -1454,7 +1475,7 @@ static void test_packed(void) {
|
||||
free(data);
|
||||
|
||||
// Load file deeper in the FS tree directly
|
||||
data = mg_file_read("src/ssi.h", NULL);
|
||||
data = mg_file_read(&mg_fs_posix, "src/ssi.h", NULL);
|
||||
ASSERT(fetch(&mgr, buf, url, "GET /src/ssi.h HTTP/1.0\n\n") == 200);
|
||||
ASSERT(cmpbody(buf, data) == 0);
|
||||
free(data);
|
||||
@@ -1483,7 +1504,7 @@ static void test_pipe(void) {
|
||||
int i, done = 0;
|
||||
mg_mgr_init(&mgr);
|
||||
ASSERT((c = mg_mkpipe(&mgr, eh6, (void *) &done)) != NULL);
|
||||
mg_mgr_wakeup(c);
|
||||
mg_mgr_wakeup(c, "", 1);
|
||||
for (i = 0; i < 10 && done == 0; i++) mg_mgr_poll(&mgr, 1);
|
||||
ASSERT(done == 1);
|
||||
mg_mgr_free(&mgr);
|
||||
@@ -1494,6 +1515,8 @@ static void u1(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_CONNECT) {
|
||||
((int *) fn_data)[0] += 1;
|
||||
mg_send(c, "hi", 2);
|
||||
} else if (ev == MG_EV_WRITE) {
|
||||
((int *) fn_data)[0] += 100;
|
||||
} else if (ev == MG_EV_READ) {
|
||||
((int *) fn_data)[0] += 10;
|
||||
mg_iobuf_free(&c->recv);
|
||||
@@ -1510,13 +1533,13 @@ static void test_udp(void) {
|
||||
mg_connect(&mgr, url, u1, (void *) &done);
|
||||
for (i = 0; i < 5; i++) mg_mgr_poll(&mgr, 1);
|
||||
// LOG(LL_INFO, ("%d", done));
|
||||
ASSERT(done == 11);
|
||||
ASSERT(done == 111);
|
||||
mg_mgr_free(&mgr);
|
||||
ASSERT(mgr.conns == NULL);
|
||||
}
|
||||
|
||||
static void test_check_ip_acl(void) {
|
||||
uint32_t ip = 0x01020304;
|
||||
uint32_t ip = mg_htonl(0x01020304);
|
||||
ASSERT(mg_check_ip_acl(mg_str(NULL), ip) == 1);
|
||||
ASSERT(mg_check_ip_acl(mg_str(""), ip) == 1);
|
||||
ASSERT(mg_check_ip_acl(mg_str("invalid"), ip) == -1);
|
||||
|
||||
Reference in new issue
Block a user