Compare commits

...
65 Commits
Author SHA1 Message Date
Sergey Lyubka 1b82aa02aa 7.5 -> 7.6 2022-01-21 12:41:52 +00:00
Sergey Lyubka e565a7e73d Fix #1453 - do not allow empty wakeup buffer 2022-01-21 12:35:28 +00:00
Sergey Lyubka 59c4dfa33b Fix 1452 - byte order issue in mg_check_ip_acl 2022-01-21 12:24:58 +00:00
Sergey Lyubka 1a8e3c2e31 Decrese debug verbosity 2022-01-19 18:15:47 +00:00
Sergey Lyubka 226917e1cd Use mg_str, not pointers, in mg_mqtt_{sub,pub} 2022-01-19 09:57:27 +00:00
Sergey Lyubka fe813ee247 Add buf,len params to mg_mgr_wakeup() 2022-01-19 09:25:01 +00:00
Sergey Lyubka 4cd830a0bf Improve ff_write - save large blocks in chunks 2022-01-19 08:43:34 +00:00
Sergey Lyubka 1259ca9ac9 Add missing mkdir() on ARM targets 2022-01-18 19:31:10 +00:00
Sergey Lyubka d0857fe36c Add mkdir to the FS API 2022-01-18 19:19:34 +00:00
Sergey Lyubka 56a7438114 Add fs arg to mg_file* and mg_http_upload API 2022-01-18 17:11:02 +00:00
Sergey Lyubka 09df542db8 Fix unamalgamated build 2022-01-18 08:02:41 +00:00
Sergey Lyubka 1df102ab93 Add Fat FS support 2022-01-18 07:44:30 +00:00
Sergey Lyubka 02b3b780bb Add missing fs.c 2022-01-17 14:44:59 +00:00
Sergey Lyubka f84f3da012 Change in FS API for fs->open() and fs->close() 2022-01-17 14:42:41 +00:00
Sergey Lyubka c65c8fdaaa Protect against the directory traversal in mg_upload() 2022-01-17 12:08:23 +00:00
Sergey Lyubka b484123f1b gcc and clang 2022-01-15 01:45:31 +00:00
Sergey Lyubka b60119790e Fix wrong memcmp usage in mg_url_port() 2022-01-14 12:45:55 +00:00
Sergey Lyubka 216427cea0 Cleanup suseconds_t usage 2022-01-14 12:33:06 +00:00
Sergey Lyubka c53e2fcc14 Fix #1444 - clang warns on undefined behavior with null pointers 2022-01-14 09:55:15 +00:00
Sergey Lyubka 9fd80fd136 Fix #1445 - send error to a resolving connection when DNS error happens 2022-01-14 08:50:01 +00:00
Sergey Lyubka 5fc284722f Fix #1448 - fix MG_ENABLE_FILE build 2022-01-14 08:21:12 +00:00
Sergey Lyubka ad6e395da0 Fix #1447 - typo in mg_random() 2022-01-14 08:18:28 +00:00
Sergey Lyubka 6c79a406cd Fix localtime_r and gmtmime_r signatures 2022-01-11 19:15:38 +00:00
Sergey Lyubka 456e8bd249 Use 3 params for fcntl() 2022-01-10 18:21:13 +00:00
Sergey Lyubka de95f28598 Make stat and struct stat changeable 2022-01-10 18:07:45 +00:00
Sergey Lyubka 43405afdb2 Dont use ctype.h from str.h 2022-01-10 17:30:51 +00:00
Sergey Lyubka 97f95e17db Better strategy for restoring multicast address 2022-01-07 18:40:48 +00:00
Sergey Lyubka 6725d61b3f Re-init SSDP peer address before every search 2022-01-07 18:28:51 +00:00
Sergey Lyubka ebda2cb100 Use sendto on Mac too 2022-01-07 16:31:29 +00:00
Sergey Lyubka bb72b2494d Fix unamalgamated target 2022-01-07 15:48:09 +00:00
Sergey Lyubka 3b0a509cae Change mg_resolve() 2022-01-07 15:00:10 +00:00
Sergey Lyubka 2f981f9383 Skip connect() on UDP connection 2022-01-06 15:42:55 +00:00
Sergey Lyubka fc3c08e411 Change mg_straddr() API 2022-01-05 19:09:11 +00:00
Sergey Lyubka ff811f287a Squash warnings 2022-01-05 13:57:30 +00:00
Sergey Lyubka 8ebb3bc37b More detailed debug log - show ip/port for sender and receiver 2022-01-05 13:47:11 +00:00
Sergey Lyubka f7ff571c5f Added SSDP search example 2022-01-05 13:22:00 +00:00
Sergey Lyubka 6a82cbee71 Send MG_EV_WRITE for UDP messages 2022-01-05 13:03:56 +00:00
Sergey Lyubka b0585bd0a9 Do not set c->label for DNS connections - let labels be user-only-settable 2022-01-05 11:46:26 +00:00
Sergey Lyubka 6ad4d242c2 Better cleanup 2022-01-05 11:45:22 +00:00
Sergey Lyubka 030b1d9826 Remove wrong comment 2021-12-27 03:31:33 +00:00
Sergey Lyubka f6ffa7b3b9 Add huge-response example 2021-12-27 03:25:28 +00:00
Sergey Lyubka 137efe2233 Show requests in flight 2021-12-24 20:46:31 +00:00
Sergey Lyubka 62411d039e Increase mqtt timeouts 2021-12-24 12:14:06 +00:00
novlean 54b1716e77 Merge pull request #1436 from cesanta/novlean-patch-1
Update README.md
2021-12-23 17:18:55 +00:00
novlean 9f7aba8039 Update README.md 2021-12-23 17:18:45 +00:00
Sergey Lyubka a176b8d566 Upate docs w.r.t. mg_mqtt_opts 2021-12-23 11:12:10 +00:00
Sergey Lyubka 26a97ec36c Fix #1434 - set will QoS in CONNECT message correctly, and rename qos -> will_qos in struct mg_mqtt_opts 2021-12-23 11:00:18 +00:00
Sergey Lyubka f29807aad8 Resolve root path before serving to avoid double dots 2021-12-22 18:05:03 +00:00
Sergey Lyubka 92d3c7a4cd Add realpath() for Win32 2021-12-22 18:04:36 +00:00
Sergey Lyubka af3a0f6af0 Add realpath() for Win32 2021-12-22 18:04:27 +00:00
Sergey Lyubka ce6164ca20 Fail stat() if S_ISDIR is not defined 2021-12-22 18:04:16 +00:00
Sergey Lyubka e9fc3fcecf Update event enum 2021-12-21 22:20:42 +00:00
Sergey Lyubka 25a1a24890 Fallback to time(0) in mg_millis() 2021-12-21 22:04:03 +00:00
Sergey Lyubka bf7fc1d5a9 Amalgamate 2021-12-21 21:58:03 +00:00
Sergey Lyubka 2126b49ab3 Merge pull request #1433 from ryuukk/patch-2
Remove debugging left over
2021-12-21 21:56:41 +00:00
Sergey Lyubka ae6767b1d2 Use int64_t for timers and mg_millis() 2021-12-21 21:50:18 +00:00
ryuukk 133629dbee Remove debugging left over 2021-12-21 21:28:22 +01:00
Sergey Lyubka fb0a9bc7e3 Fix build 2021-12-21 18:16:12 +00:00
Sergey Lyubka 2a3492766f Remove mg_time() and mg_usleep() 2021-12-21 17:44:34 +00:00
Sergey Lyubka 2ec7e5653e Merge branch 'master' of github.com:cesanta/mongoose 2021-12-21 17:40:04 +00:00
Sergey Lyubka 30bfb245f5 Make mongoose more bare-metal Keil friendly. Declare settings after arch. Introduce MG_ENABLE_FILE 2021-12-21 17:39:55 +00:00
Sergey Lyubka 84d89bc4f5 Merge pull request #1431 from ryuukk/patch-1
Closed WS connection is not an error for the logger
2021-12-21 08:47:21 +00:00
ryuukk 7f55fabfec Closed WS connection is not an error for the logger 2021-12-21 01:34:48 +01:00
Sergey Lyubka 9e0f04dc8b Add -S option 2021-12-20 16:35:16 +00:00
Sergey Lyubka 574c2954b3 Docs nitpick 2021-12-19 10:53:17 +00:00
65 changed files with 3323 additions and 1516 deletions

No files matched your search

+5 -3
View File
@@ -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
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
}
```
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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);
}
}
}
+7 -4
View File
@@ -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;
}
}
+24 -5
View File
@@ -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);
+11
View File
@@ -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
+89
View File
@@ -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
+204
View File
@@ -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>
+3 -3
View File
@@ -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
+4 -3
View File
@@ -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);
+3 -3
View File
@@ -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);
+5 -4
View File
@@ -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
}
}
}
+10
View File
@@ -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
+47
View File
@@ -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;
}
+1 -1
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+506 -478
View File
File diff suppressed because it is too large. Load diff
+11 -10
View File
@@ -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
-5
View File
@@ -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
-5
View File
@@ -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
+3 -4
View File
@@ -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
+2 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
+32
View File
@@ -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
+13 -9
View File
@@ -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);
}
}
+1 -1
View File
@@ -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
View File
@@ -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
};
+75
View File
@@ -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;
}
+15 -4
View File
@@ -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, ...);
+90
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 *);
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+6 -6
View File
@@ -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 {
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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:
+5 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
#define MG_VERSION "7.5"
#define MG_VERSION "7.6"
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
}
+1 -4
View File
@@ -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
View File
@@ -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);