Compare commits

...
45 Commits
Author SHA1 Message Date
cpq 452bcc68a4 Add mg_mqtt_next_unsub() signature 2021-03-09 11:29:53 +00:00
cpq 829827d46f Add mg_mqtt_next_unsub 2021-03-09 09:54:02 +00:00
cpq ff9d0e462e Tiny refactor in walkchunks() 2021-03-09 08:22:52 +00:00
cpq e84ea91a4a Process chunked body before sending MG_EV_HTTP_MSG 2021-03-09 08:21:29 +00:00
cpq 2dcb10ef36 Less verbose test 2021-03-08 15:45:09 +00:00
cpq 99fa7f0851 2020 -> 2021 2021-03-08 15:44:57 +00:00
cpq 1b5cc341a5 Add streaming client support, MG_EV_HTTP_CHUNK 2021-03-08 15:41:16 +00:00
cpq d50dc288cd Add mg_crc32 2021-03-08 15:40:39 +00:00
cpq 060bb29cdc Restore mg_mqtt_{ping,pong,disconnect} 2021-03-07 12:21:59 +00:00
cpq 366426e8bb Avoid extra allocation for static HTTP handling 2021-03-07 11:56:20 +00:00
cpq e710df4c80 Define WEAK to nothing on Cygwin 2021-03-04 14:25:50 +00:00
cpq 79e5841483 Fix QNX build 2021-03-04 11:09:57 +00:00
cpq 374ab4ecf0 Add struct mg_mgr::userdata 2021-02-28 16:40:27 +00:00
cpq 56980be48c Do not reveal full root dir path 2021-02-17 16:07:18 +00:00
cpq 602c5c0866 Refactor mg_url_encode 2021-02-15 14:42:55 +00:00
cpq 7ce5009de1 Remove reduntant code 2021-02-15 14:42:22 +00:00
cpq 51e3a1304b Fix TLS build 2021-02-13 10:45:27 +00:00
cpq d5a1d5a5e8 Stricter realpath checks 2021-02-11 15:03:22 +00:00
cpq ae5f6c0920 Increase conn label size 32 -> 50 2021-02-10 11:09:13 +00:00
cpq 0a9303bb07 Stricter mg_http_parse() 2021-02-09 21:16:33 +00:00
cpq 42bc7b1f47 Add "struct mg_http_message::head" attr 2021-02-09 13:27:17 +00:00
cpq 4844ed60bd Remove fflush in logging 2021-02-09 00:16:13 +00:00
cpq 41f5daeb47 Fix mg_random 2021-02-08 19:43:01 +00:00
cpq a3f8f33d50 Allow mbedtls CA being inline PEM 2021-02-08 17:50:00 +00:00
cpq fd8db5c53a Better unicode handling on win32 2021-02-07 22:43:49 +00:00
cpq a2f027e798 Change mws binary link 2021-02-07 14:51:37 +00:00
novlean dad5c8fe87 Update README.md 2021-02-05 16:56:06 +00:00
novlean 98783a41ba Update README.md 2021-02-05 16:55:26 +00:00
cpq 1517d2985a Update README 2021-02-04 10:40:33 +00:00
Sergey Lyubka acc0564957 Merge pull request #1213 from csimons/drainig
README.md: fix typo: is_drainig -> is_draining
2021-02-03 08:30:55 +00:00
Christopher Simons 421bc126b5 README.md: fix typo: is_drainig -> is_draining 2021-02-02 22:17:15 -05:00
cpq ed1a98c381 Remove debug logs 2021-02-03 02:19:17 +00:00
cpq 457d76b049 Fix #1211 - adjust ESP32 example to serve FS 2021-02-03 02:13:05 +00:00
cpq 6827972f71 Fix #1210 - on win32, open upload file in binary mode 2021-02-02 08:43:56 +00:00
cpq 7eb998891e Show served dir in the start message 2021-02-01 00:02:33 +00:00
cpq 582fde5c6e Fix #1209 2021-01-31 19:54:42 +00:00
cpq d327ee9834 Improve SNI 2021-01-30 13:03:11 +00:00
cpq 31cf6f8969 https://localhost -> http://0.0.0.0 2021-01-29 13:18:34 +00:00
cpq 86b1053678 Add error check for iobuf allocation 2021-01-29 12:32:34 +00:00
cpq 6678e36278 Use openssl/ssl.h 2021-01-28 08:45:24 +00:00
cpq 5118355732 Use 64bit file ops even on 32bit systems 2021-01-26 17:43:31 +00:00
cpq 5b4b7f7dc5 Fix test 2021-01-26 17:01:43 +00:00
cpq 7614a8101f Wrap util funcs that use FILE into MG_ENABLE_FS 2021-01-26 16:34:42 +00:00
cpq 3c851bcfc3 mg_file_write -> mg_file_printf 2021-01-26 16:30:44 +00:00
cpq 01691dafa0 Released 7.1, move HEAD to 7.2 2021-01-26 13:07:42 +00:00
42 changed files with 1863 additions and 604 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
SRCS = $(wildcard src/*.c) SRCS = $(wildcard src/*.c)
HDRS = $(wildcard src/*.h) HDRS = $(wildcard src/*.h)
DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_HTTP_DEBUG_ENDPOINT=1 -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1 DEFS ?= -DMG_MAX_HTTP_HEADERS=5 -DMG_ENABLE_LINES -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1
CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA) CFLAGS ?= -W -Wall -Werror -Isrc -I. -O0 -g $(DEFS) $(TFLAGS) $(EXTRA)
SSL ?= MBEDTLS SSL ?= MBEDTLS
CDIR ?= $(realpath $(CURDIR)) CDIR ?= $(realpath $(CURDIR))
+8 -7
View File
@@ -21,7 +21,10 @@ on the top 9 things to avoid when embedding a web server.
- [Download “9 Things NOT to do when embedding a web server” white paper here](https://www.cesanta.com/whitepaper.html) - [Download “9 Things NOT to do when embedding a web server” white paper here](https://www.cesanta.com/whitepaper.html)
Looking for a complete IoT solution? Check out # Looking for a pre-compiled Mongoose web server Windows or Mac binary?
- [Download pre-compiled Mongoose web server binary](https://mongoose.ws/)
# Looking for a complete IoT solution? Check out
- [VCON](https://vcon.io) - Arduino compatible MCU modules with built-in firmware OTA updates - [VCON](https://vcon.io) - Arduino compatible MCU modules with built-in firmware OTA updates
- [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for commercial Internet of Things products - [Mongoose OS](https://mongoose-os.com) - open source embedded operating system for low-power connected microcontrollers. Secure, designed for commercial Internet of Things products
@@ -33,11 +36,10 @@ Looking for a complete IoT solution? Check out
# Features # Features
* Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android, * Cross-platform: works on Linux/UNIX, MacOS, Windows, Android, FreeRTOS, etc
iPhone, FreeRTOS, etc * Supported embedded architectures: ESP32, ESP8266, TI, NRF52, STM32, PIC32, NXP, and more
* Supported embedded architectures: ESP32, ESP8266, TI CC3200, TI MSP432, NRF52, STM32, PIC32, NXP, and more
* Builtin protocols: plain TCP/UDP, HTTP, MQTT, Websocket * Builtin protocols: plain TCP/UDP, HTTP, MQTT, Websocket
* SSL/TLS support: mbedTLS, OpenSSL or custom via API * SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
* Asynchronous DNS resolver * Asynchronous DNS resolver
* Tiny static and run-time footprint * Tiny static and run-time footprint
* Source code is both ISO C and ISO C++ compliant * Source code is both ISO C and ISO C++ compliant
@@ -70,5 +72,4 @@ Contributions are welcome! Please follow the guidelines below:
- When making pull requests, please make sure that it has only one commit, - When making pull requests, please make sure that it has only one commit,
and imlements/fixes only one piece of functionality and imlements/fixes only one piece of functionality
# Looking for a pre-compiled Mongoose web server Windows or Mac binary?
- [Download pre-compiled Mongoose web server binary.](https://www.cesanta.com/binary.html)
+69 -20
View File
@@ -159,10 +159,10 @@ enum {
they can be either 0 or 1. Some flags are set by Mongoose and must be not they can be either 0 or 1. Some flags are set by Mongoose and must be not
changed by an application code, for example `is_udp` flag tells application if changed by an application code, for example `is_udp` flag tells application if
that connection is UDP or not. Some flags can be changed by application, for that connection is UDP or not. Some flags can be changed by application, for
example, `is_drainig` flag, if set by an application, tells Mongoose to send example, `is_draining` flag, if set by an application, tells Mongoose to send
the remaining data to peer, and when everything is sent, close the connection. the remaining data to peer, and when everything is sent, close the connection.
User-changeable flags are: `is_hexdumping`, `is_drainig`, `is_closing`. User-changeable flags are: `is_hexdumping`, `is_draining`, `is_closing`.
This is taken from `mongoose.h` as-is: This is taken from `mongoose.h` as-is:
@@ -223,7 +223,6 @@ Here is a list of build constants and their default values:
|`MG_ENABLE_LOG` | 1 | Enable `LOG()` macro | |`MG_ENABLE_LOG` | 1 | Enable `LOG()` macro |
|`MG_ENABLE_MD5` | 0 | Use native MD5 implementation | |`MG_ENABLE_MD5` | 0 | Use native MD5 implementation |
|`MG_ENABLE_DIRECTORY_LISTING` | 0 | Enable directory listing for HTTP server | |`MG_ENABLE_DIRECTORY_LISTING` | 0 | Enable directory listing for HTTP server |
|`MG_ENABLE_HTTP_DEBUG_ENDPOINT` | 0 | Enable `/debug/info` debug URI |
|`MG_ENABLE_SOCKETPAIR` | 0 | Enable `mg_socketpair()` for multi-threading | |`MG_ENABLE_SOCKETPAIR` | 0 | Enable `mg_socketpair()` for multi-threading |
|`MG_ENABLE_SSI` | 0 | Enable serving SSI files by `mg_http_serve_dir()` | |`MG_ENABLE_SSI` | 0 | Enable serving SSI files by `mg_http_serve_dir()` |
|`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth | |`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth |
@@ -528,21 +527,23 @@ by `buf`, and `len` specifies number of bytes currently stored.
### mg\_iobuf\_init() ### mg\_iobuf\_init()
```c ```c
void mg_iobuf_init(struct mg_iobuf *io, size_t size); int mg_iobuf_init(struct mg_iobuf *io, size_t size);
``` ```
Initialise IO buffer, allocate `size` bytes. Initialise IO buffer, allocate `size` bytes. Return 1 on success,
0 on allocation failure.
### mg\_iobuf\_resize() ### mg\_iobuf\_resize()
```c ```c
void mg_iobuf_resize(struct mg_iobuf *io, size_t size); int mg_iobuf_resize(struct mg_iobuf *io, size_t size);
``` ```
Resize IO buffer, set the new size to `size`. The `io->buf` pointer could Resize IO buffer, set the new size to `size`. The `io->buf` pointer could
change after this, for example if the buffer grows. If `size` is 0, then the change after this, for example if the buffer grows. If `size` is 0, then the
`io->buf` is freed and set to NULL, and both `size` and `len` are set to 0. `io->buf` is freed and set to NULL, and both `size` and `len` are set to 0.
Return 1 on success, 0 on allocation failure.
### mg\_iobuf\_free() ### mg\_iobuf\_free()
@@ -947,10 +948,45 @@ void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic);
Subscribe to topic `topic`. Subscribe to topic `topic`.
### mg\_mqtt\_next\_sub()
```c
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
uint8_t *qos, int pos);
```
Traverse list of subscribed topics.
Used to implement MQTT server when `MQTT_CMD_SUBSCRIBE` is received.
Return next position. Initial position `pos` should be 4. Example:
```c
if (ev == MG_EV_MQTT_CMD) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
if (mm->cmd == MQTT_CMD_SUBSCRIBE) {
int pos = 4;
uint8_t qos;
struct mg_str topic;
while ((pos = mg_mqtt_next_sub(mm, &topic, &qos, pos)) > 0) {
LOG(LL_INFO, ("SUB [%.*s]", c->id, (int) topic.len, topic.ptr));
}
}
}
```
### mg\_mqtt\_next\_unsub()
```c
int mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
int pos);
```
Same as `mg_mqtt_next_sub()`, but for unsubscribed topics. The difference
is that there is no QoS in unsubscribe request.
## TLS ## TLS
### struct mg\_tls\_opts ### mg\_tls\_init()
```c ```c
struct mg_tls_opts { struct mg_tls_opts {
@@ -958,20 +994,12 @@ struct mg_tls_opts {
const char *cert; // Certificate const char *cert; // Certificate
const char *certkey; // Certificate key const char *certkey; // Certificate key
const char *ciphers; // Cipher list const char *ciphers; // Cipher list
const char *srvname; // If not NULL, enables server name verification struct mg_str srvname; // If not empty, enables server name verification
}; };
```
Describes TLS parameters
### mg\_tls\_init()
```c
int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts); int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts);
``` ```
Turns on TLS on a given connection. Initialise TLS on a given connection.
## Timers ## Timers
@@ -1049,14 +1077,26 @@ size_t mg_file_size(const char *path);
Return file size, or 0 on failure. Empty files also report 0 length. Return file size, or 0 on failure. Empty files also report 0 length.
### mg\_file\_write() ### mg\_file\_write()
```c ```c
int mg_file_write(const char *path, const char *fmt, ...); bool mg_file_write(const char *path, const void *buf, size_t len);
``` ```
Write into a file `path` using `printf()` semantics. Return number of bytes written. Write data to a file, return `true` if written, `false` otherwise.
The write is atomic, i.e. data gets written to a temporary file first,
then `rename()-ed` to a destination file name.
### mg\_file\_printf()
```c
int mg_file_printf(const char *path, const char *fmt, ...);
```
Write into a file `path` using `printf()` semantics.
Return `true` on success, `false` otherwise. This function prints data to
a temporary in-memory buffer first, then calls `mg_file_write()`.
### mg\_random() ### mg\_random()
@@ -1235,3 +1275,12 @@ void mg_usleep(unsigned long usecs);
``` ```
Block for a given number of microseconds. Block for a given number of microseconds.
### mg\_crc32()
```c
uint32_t mg_crc32(uint32_t crc, const uint8_t *buf, size_t len);
```
Calculate CRC32 checksum for a given buffer. An initial `crc` value should
be `0`.
+1 -1
View File
@@ -9,7 +9,7 @@ example: main/main.c Makefile
COMPORT ?= /dev/cu.SLAB_USBtoUART COMPORT ?= /dev/cu.SLAB_USBtoUART
ESPTOOL ?= esptool.py ESPTOOL ?= esptool.py
flash: flash:
cd build && $(ESPTOOL) --chip esp32 -p $(COMPORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x10000 mongoose-esp32-example.bin cd build && $(ESPTOOL) --chip esp32 -p $(COMPORT) -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x100000 mongoose-esp32-example.bin
clean: clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig
+1
View File
@@ -2,3 +2,4 @@ idf_component_register(SRCS "main.c"
"wifi.c" "wifi.c"
"../../../mongoose.c" "../../../mongoose.c"
INCLUDE_DIRS "../../..") INCLUDE_DIRS "../../..")
component_compile_options(-DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_LINES)
+31 -32
View File
@@ -1,54 +1,53 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2020 Cesanta Software Limited
// All rights reserved // All rights reserved
#include "esp_spiffs.h"
#include "freertos/FreeRTOS.h"
#include "mongoose.h" #include "mongoose.h"
#define WIFI_SSID "WIFI_NETWORK" // SET THIS! #define WIFI_SSID "WIFI_NETWORK" // SET THIS!
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS! #define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
#define FS_ROOT "/spiffs"
#define SERVER_URL "http://0.0.0.0:80" // Event handler for an server (accepted) connection. Implemented endpoints:
#define CLIENT_URL "http://info.cern.ch" // /api/stats - return JSON object with ESP32 stats (free RAM)
// any other - serve files from the filesystem
// Event handler for an server (accepted) connection
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
mg_http_reply(c, 200, "", "Hello from ESP!\n"); struct mg_http_message *hm = ev_data;
if (mg_http_match_uri(hm, "/api/stats")) {
mg_http_reply(c, 200, "", "{\"ram\": %lu}\n", xPortGetFreeHeapSize());
} else {
struct mg_http_serve_opts opts = {.root_dir = FS_ROOT};
mg_http_serve_dir(c, hm, &opts);
}
} }
} }
// Event handler for a client connection - fetch the first web page in history // SPIFFS is flat, so tell Mongoose that the FS root is a directory
// To enable TLS for HTTP, // This cludge is not required for filesystems with directory support
// 1. Copy "ca.pem" file to the ESP32 flash FS bool mg_is_dir(const char *path) {
// 2. Add TLS init snippet for the connection, see examples/http-client return strcmp(path, FS_ROOT) == 0;
static void cb2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
struct mg_str s = mg_url_host(CLIENT_URL);
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr);
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data; // Print HTTP response
LOG(LL_INFO, ("Fetched:\n%.*s", (int) hm->message.len, hm->message.ptr));
c->is_closing = 1;
}
}
// Called after we're connected to WiFi network
static void run_mongoose(void) {
struct mg_mgr mgr;
mg_log_set("3");
mg_mgr_init(&mgr);
mg_http_listen(&mgr, SERVER_URL, cb, &mgr); // Listening server
mg_http_connect(&mgr, CLIENT_URL, cb2, &mgr); // Example client
LOG(LL_INFO, ("Starting Mongoose web server v%s", MG_VERSION));
for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
} }
void app_main(void) { void app_main(void) {
// Mount filesystem
esp_vfs_spiffs_conf_t conf = {
.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");
// Setup wifi. This function is implemented in wifi.c // Setup wifi. This function is implemented in wifi.c
// It blocks until connected to the configured WiFi network // It blocks until connected to the configured WiFi network
void wifi_init(const char *ssid, const char *pass); void wifi_init(const char *ssid, const char *pass);
wifi_init(WIFI_SSID, WIFI_PASS); wifi_init(WIFI_SSID, WIFI_PASS);
// Done connecting to WiFi, now start HTTP server // Connected to WiFi, now start HTTP server
run_mongoose(); struct mg_mgr mgr;
mg_log_set("3");
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", cb, &mgr); // Listening server
LOG(LL_INFO, ("Starting Mongoose web server v%s", MG_VERSION));
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
} }
+5
View File
@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
storage, data, spiffs, 0x10000, 0x10000,
factory, app, factory, 0x100000, 1M,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x6000
3 phy_init data phy 0xf000 0x1000
4 storage data spiffs 0x10000 0x10000
5 factory app factory 0x100000 1M
+3
View File
@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
+9 -6
View File
@@ -1,11 +1,14 @@
PROG ?= example PROG ?= example
CFLAGS ?= -DMG_ENABLE_LINES $(CFLAGS_EXTRA) CFLAGS ?= -DMG_ENABLE_LINES $(EXTRA)
MBEDTLS_DIR ?=
ifeq "$(MBEDTLS_DIR)" "" ifneq ($(MBEDTLS),)
else CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include CFLAGS += -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509 endif
ifneq ($(OPENSSL),)
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
CFLAGS += -L$(OPENSSL)/lib -lssl -lcrypto
endif endif
all: $(PROG) all: $(PROG)
+18 -9
View File
@@ -1,43 +1,52 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2021 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Example HTTP client. Connect to `s_url`, send request, wait for a response, // Example HTTP client. Connect to `s_url`, send request, wait for a response,
// print the response and exit. // print the response and exit.
// You can change `s_url` from the command line by executing: ./example YOUR_URL
// //
// To enable SSL/TLS for this client, build it like this: // To enable SSL/TLS for this client, build it like this:
// make MBEDTLS_DIR=/path/to/your/mbedtls/installation // make MBEDTLS=/path/to/your/mbedtls/installation
// make OPENSSL=/path/to/your/openssl/installation
#include "mongoose.h" #include "mongoose.h"
// The very first web page in history // The very first web page in history. You can replace it from command line
static const char *s_url = "http://info.cern.ch"; static const char *s_url = "http://info.cern.ch";
// Print HTTP response and signal that we're done // Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) { if (ev == MG_EV_CONNECT) {
// Connected to server // Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url); struct mg_str host = mg_url_host(s_url);
// If s_url is https://, tell client connection to use TLS
if (mg_url_is_ssl(s_url)) { if (mg_url_is_ssl(s_url)) {
// If s_url is https://, tell client connection to use TLS struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host};
struct mg_tls_opts opts = {.ca = "ca.pem"};
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
} }
// Send request // Send request
mg_printf(c, "GET %s HTTP/1.0\r\nHost: %.*s\r\n\r\n", mg_url_uri(s_url), mg_printf(c,
(int) host.len, host.ptr); "GET %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr);
} else if (ev == MG_EV_HTTP_MSG) { } else if (ev == MG_EV_HTTP_MSG) {
// Response is received. Print it // Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
printf("%.*s", (int) hm->message.len, hm->message.ptr); printf("%.*s", (int) hm->message.len, hm->message.ptr);
c->is_closing = 1; // Tell mongoose to close this connection c->is_closing = 1; // Tell mongoose to close this connection
*(bool *) fn_data = true; // Tell event loop to stop *(bool *) fn_data = true; // Tell event loop to stop
} else if (ev == MG_EV_ERROR) {
*(bool *) fn_data = true; // Error, tell event loop to stop
} }
} }
int main(void) { int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true bool done = false; // Event handler flips it to true
if (argc > 1) s_url = argv[1]; // Use URL from command line
mg_log_set("3"); // Set to 0 to disable debug mg_log_set("3"); // Set to 0 to disable debug
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
+1 -1
View File
@@ -14,7 +14,7 @@
#include "mongoose.h" #include "mongoose.h"
static const char *s_listen_on = "https://localhost:8000"; static const char *s_listen_on = "http://0.0.0.0:8000";
static const char *s_web_directory = "."; static const char *s_web_directory = ".";
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
+1 -1
View File
@@ -64,7 +64,7 @@ int main(int argc, char *argv[]) {
if (mg_casecmp(s_enable_hexdump, "yes") == 0) c->is_hexdumping = 1; if (mg_casecmp(s_enable_hexdump, "yes") == 0) c->is_hexdumping = 1;
// Start infinite event loop // Start infinite event loop
LOG(LL_INFO, ("Starting Mongoose web server v%s", MG_VERSION)); LOG(LL_INFO, ("Starting Mongoose v%s, serving [%s]", MG_VERSION, s_root_dir));
for (;;) mg_mgr_poll(&mgr, 1000); for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
return 0; return 0;
+22
View File
@@ -0,0 +1,22 @@
PROG ?= example
CFLAGS ?= -DMG_ENABLE_LINES $(EXTRA)
ifneq ($(MBEDTLS),)
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include
CFLAGS += -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
endif
ifneq ($(OPENSSL),)
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
CFLAGS += -L$(OPENSSL)/lib -lssl -lcrypto
endif
all: $(PROG)
$(DEBUGGER) ./$(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
+507
View File
@@ -0,0 +1,507 @@
Subject: O=Digital Signature Trust Co., CN=DST Root CA X3
Not Before: Sep 30 21:12:19 2000 GMT
Not After : Sep 30 14:01:15 2021 GMT
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
Not Before: May 12 18:46:00 2000 GMT
Not After : May 12 23:59:00 2025 GMT
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----
Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
Not Before: May 21 04:00:00 2002 GMT
Not After : May 21 04:00:00 2022 GMT
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----
Subject: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 ECC 256 bit SSL CA - G2
Not Before: May 12 00:00:00 2015 GMT
Not After : May 11 23:59:59 2025 GMT
-----BEGIN CERTIFICATE-----
MIIEajCCA1KgAwIBAgIQP5KHvp0dpKN6nfYoLndaxDANBgkqhkiG9w0BAQsFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTUwNTEyMDAwMDAwWhcNMjUwNTExMjM1OTU5WjCBgDEL
MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD
VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBD
bGFzcyAzIEVDQyAyNTYgYml0IFNTTCBDQSAtIEcyMFkwEwYHKoZIzj0CAQYIKoZI
zj0DAQcDQgAEDxukkdfnrOfRTk63ZFvhj39uBNOrONtEt0Bcbb2WljffeYmGZ/ex
Hwie/WM7RoyfvVPoFdyXPiuBRq2Gfw4BOaOCAV0wggFZMC4GCCsGAQUFBwEBBCIw
IDAeBggrBgEFBQcwAYYSaHR0cDovL3Muc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYB
Af8CAQAwZQYDVR0gBF4wXDBaBgpghkgBhvhFAQc2MEwwIwYIKwYBBQUHAgEWF2h0
dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5z
eW1jYi5jb20vcnBhMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNv
bS9wY2EzLWc1LmNybDAOBgNVHQ8BAf8EBAMCAQYwKwYDVR0RBCQwIqQgMB4xHDAa
BgNVBAMTE1NZTUMtRUNDLUNBLXAyNTYtMjIwHQYDVR0OBBYEFCXwiuFLetkBlQrt
xlPxjHgf2fP4MB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG
SIb3DQEBCwUAA4IBAQAMMGUXBaWTdaLxsTGtcB/naqjIQrLvoV9NG+7MoHpGd/69
dZ/h2zOy7sGFUHoG/0HGRA9rxT/5w5GkEVIVkxtWyIWWq6rs4CTZt8Bej/KHYRbo
jtEDUkCTZSTLiCvguPyvinXgxy+LHT+PmdtEfXsvcdbeBSWUYpOsDYvD2hNtz9dw
Od5nBosMApmdxt+z7LQyZu8wMnfI1U6IMO+RWowxZ8uy0oswdFYd32l9xe+aAE/k
y9alLu/M9pvxiUKufqHJRgDBKA6uDjHLMPX+/nxXaNCPX3SI4KVZ1stHQ/U5oNlM
dHN9umAvlU313g0IgJrjsQ2nIdf9dsdP+6lrmP7s
-----END CERTIFICATE-----
Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
Not Before: Nov 8 00:00:00 2006 GMT
Not After : Jul 16 23:59:59 2036 GMT
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----
Subject: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority
Not Before: Jun 29 17:06:20 2004 GMT
Not After : Jun 29 17:06:20 2034 GMT
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----
# Note: Amazon ATS endpoint uses this (2018/12/18)
Subject: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
Not Before: Jun 29 17:39:16 2004 GMT
Not After : Jun 29 17:39:16 2034 GMT
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----
Not Before: Sep 1 00:00:00 2009 GMT
Not After : Dec 31 23:59:59 2037 GMT
Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Root Certificate Authority - G2
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----
Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., CN=Starfield Services Root Certificate Authority - G2
Not Before: Sep 1 00:00:00 2009 GMT
Not After : Dec 31 23:59:59 2037 GMT
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
sSi6
-----END CERTIFICATE-----
Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
Not Before: Dec 15 08:00:00 2006 GMT
Not After : Dec 15 08:00:00 2021 GMT
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----
Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
Not Before: Nov 10 00:00:00 2006 GMT
Not After : Nov 10 00:00:00 2031 GMT
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA
Not Before: Nov 10 00:00:00 2006 GMT
Not After : Nov 10 00:00:00 2031 GMT
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
# *.azure-devices.de use this root.
Subject: C = DE, O = D-Trust GmbH, CN = D-TRUST Root Class 3 CA 2 2009
Not Before: Nov 5 08:35:58 2009 GMT
Not After : Nov 5 08:35:58 2029 GMT
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
Johw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----
# https://www.amazontrust.com/repository/
Subject: C = US, O = Amazon, CN = Amazon Root CA 1
Not Before: May 26 00:00:00 2015 GMT
Not After : Jan 17 00:00:00 2038 GMT
-----BEGIN CERTIFICATE-----
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
rqXRfboQnoZsG4q5WTP468SQvvG5
-----END CERTIFICATE-----
Subject: C = US, O = Amazon, CN = Amazon Root CA 2
Not Before: May 26 00:00:00 2015 GMT
Not After : May 26 00:00:00 2040 GMT
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK
gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ
W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg
1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K
8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r
2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me
z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR
8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj
mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz
7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6
+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI
0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm
UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2
LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS
k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl
7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm
btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl
urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+
fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63
n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE
76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H
9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT
4PsJYGw=
-----END CERTIFICATE-----
Not Before: May 26 00:00:00 2015 GMT
Not After : May 26 00:00:00 2040 GMT
Subject: C = US, O = Amazon, CN = Amazon Root CA 3
-----BEGIN CERTIFICATE-----
MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
YyRIHN8wfdVoOw==
-----END CERTIFICATE-----
Not Before: May 26 00:00:00 2015 GMT
Not After : May 26 00:00:00 2040 GMT
Subject: C = US, O = Amazon, CN = Amazon Root CA 4
-----BEGIN CERTIFICATE-----
MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
1KyLa2tJElMzrdfkviT8tQp21KW8EA==
-----END CERTIFICATE-----
Not Before: Nov 10 00:00:00 2006 GMT
Not After : Nov 10 00:00:00 2031 GMT
Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
Not Before: Nov 13 00:00:00 2012 GMT
Not After : Jan 19 03:14:07 2038 GMT
Subject: OU = GlobalSign ECC Root CA - R4, O = GlobalSign, CN = GlobalSign
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
ewv4n4Q=
-----END CERTIFICATE-----
Not Before: Nov 1 00:00:42 2018 GMT
Not After : Nov 1 00:00:42 2042 GMT
Subject: C = US, O = Google Trust Services LLC, CN = GTS LTSR
-----BEGIN CERTIFICATE-----
MIIBxTCCAWugAwIBAgINAfD3nVndblD3QnNxUDAKBggqhkjOPQQDAjBEMQswCQYD
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzERMA8G
A1UEAxMIR1RTIExUU1IwHhcNMTgxMTAxMDAwMDQyWhcNNDIxMTAxMDAwMDQyWjBE
MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
QzERMA8GA1UEAxMIR1RTIExUU1IwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATN
8YyO2u+yCQoZdwAkUNv5c3dokfULfrA6QJgFV2XMuENtQZIG5HUOS6jFn8f0ySlV
eORCxqFyjDJyRn86d+Iko0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUPv7/zFLrvzQ+PfNA0OQlsV+4u1IwCgYIKoZIzj0EAwID
SAAwRQIhAPKuf/VtBHqGw3TUwUIq7TfaExp3bH7bjCBmVXJupT9FAiBr0SmCtsuk
miGgpajjf/gFigGM34F9021bCWs1MbL0SA==
-----END CERTIFICATE-----
+65
View File
@@ -0,0 +1,65 @@
// Copyright (c) 2021 Cesanta Software Limited
// All rights reserved
//
// Example HTTP client.
// Instead of buffering the whole HTTP response in memory and wait for the
// MG_EV_HTTP_MSG event, this client catches MG_EV_HTTP_CHUNK events and
// prints them. This is useful to download large content without spooling
// everything to memory, or catch chunked content.
//
// You can change `s_url` from the command line by executing: ./example YOUR_URL
//
// To enable SSL/TLS for this client, build it like this:
// make MBEDTLS=/path/to/your/mbedtls/installation
// make OPENSSL=/path/to/your/openssl/installation
#include "mongoose.h"
// The very first web page in history. You can replace it from command line
static const char *s_url = "http://info.cern.ch";
// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
// Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url);
// If s_url is https://, tell client connection to use TLS
if (mg_url_is_ssl(s_url)) {
struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host};
mg_tls_init(c, &opts);
}
// Send request
mg_printf(c,
"GET %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr);
} else if (ev == MG_EV_HTTP_CHUNK) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
LOG(LL_INFO, ("%.*s", (int) hm->chunk.len, hm->chunk.ptr));
mg_http_delete_chunk(c, hm);
if (hm->chunk.len == 0) *(bool *) fn_data = true; // Last chunk
} else if (ev == MG_EV_HTTP_MSG) {
// Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
LOG(LL_INFO, ("%.*s", (int) hm->body.len, hm->body.ptr));
c->is_closing = 1; // Tell mongoose to close this connection
*(bool *) fn_data = true; // Tell event loop to stop
} else if (ev == MG_EV_ERROR) {
*(bool *) fn_data = true; // Error, tell event loop to stop
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true
if (argc > 1) s_url = argv[1]; // Use URL from command line
mg_log_set("3"); // Set to 0 to disable debug
mg_mgr_init(&mgr); // Initialise event manager
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources
return 0;
}
+408 -211
View File
File diff suppressed because it is too large. Load diff
+70 -48
View File
@@ -1,5 +1,5 @@
// Copyright (c) 2004-2013 Sergey Lyubka // Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2020 Cesanta Software Limited // Copyright (c) 2013-2021 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// This software is dual-licensed: you can redistribute it and/or modify // This software is dual-licensed: you can redistribute it and/or modify
@@ -14,9 +14,8 @@
// //
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <https://www.cesanta.com/license>. // license, as set out in <https://www.cesanta.com/license>.
#pragma once #pragma once
#define MG_VERSION "7.1" #define MG_VERSION "7.2"
#define MG_ARCH_CUSTOM 0 #define MG_ARCH_CUSTOM 0
@@ -120,7 +119,12 @@
#include <dirent.h> #include <dirent.h>
#include <netdb.h> #include <netdb.h>
#include <sys/stat.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 128
#endif
#endif #endif
@@ -133,6 +137,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"
#endif #endif
@@ -146,6 +151,7 @@
#include <FreeRTOS_Sockets.h> #include <FreeRTOS_Sockets.h>
#include <stdbool.h> #include <stdbool.h>
#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP #define IPPROTO_TCP FREERTOS_IPPROTO_TCP
#define IPPROTO_UDP FREERTOS_IPPROTO_UDP #define IPPROTO_UDP FREERTOS_IPPROTO_UDP
@@ -206,6 +212,7 @@ static inline int ff_vfprintf(FF_FILE *fp, const char *fmt, va_list ap) {
#include <arpa/inet.h> #include <arpa/inet.h>
#include <dirent.h> #include <dirent.h>
#include <inttypes.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <signal.h> #include <signal.h>
@@ -217,6 +224,7 @@ static inline int ff_vfprintf(FF_FILE *fp, const char *fmt, va_list ap) {
#include <unistd.h> #include <unistd.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_ENABLE_POSIX 1 #define MG_ENABLE_POSIX 1
#define MG_INT64_FMT "%" PRId64
#endif #endif
@@ -261,7 +269,7 @@ typedef int socklen_t;
#ifndef EWOULDBLOCK #ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK
#endif #endif
#define realpath(a, b) _fullpath((b), (a), 512) #define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
#ifndef va_copy #ifndef va_copy
#ifdef __va_copy #ifdef __va_copy
#define va_copy __va_copy #define va_copy __va_copy
@@ -273,6 +281,8 @@ typedef int socklen_t;
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
#endif #endif
#define MG_INT64_FMT "%I64d"
#endif #endif
@@ -322,10 +332,6 @@ typedef int socklen_t;
#define MG_ENABLE_DIRECTORY_LISTING 0 #define MG_ENABLE_DIRECTORY_LISTING 0
#endif #endif
#ifndef MG_ENABLE_HTTP_DEBUG_ENDPOINT
#define MG_ENABLE_HTTP_DEBUG_ENDPOINT 0
#endif
#ifndef MG_ENABLE_SOCKETPAIR #ifndef MG_ENABLE_SOCKETPAIR
#define MG_ENABLE_SOCKETPAIR 0 #define MG_ENABLE_SOCKETPAIR 0
#endif #endif
@@ -344,6 +350,10 @@ typedef int socklen_t;
#define MG_MAX_HTTP_HEADERS 40 #define MG_MAX_HTTP_HEADERS 40
#endif #endif
#ifndef MG_PATH_MAX
#define MG_PATH_MAX PATH_MAX
#endif
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
@@ -413,13 +423,15 @@ void mg_timer_poll(unsigned long uptime_ms);
char *mg_file_read(const char *path); char *mg_file_read(const char *path);
size_t mg_file_size(const char *path); int64_t mg_file_size(const char *path);
int mg_file_write(const char *path, const char *fmt, ...); 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); void mg_random(void *buf, size_t len);
bool mg_globmatch(const char *pattern, int plen, const char *s, int n); bool mg_globmatch(const char *pattern, int plen, const char *s, int n);
bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v); bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
uint16_t mg_ntohs(uint16_t net); uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net); uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
char *mg_hexdump(const void *buf, size_t len); char *mg_hexdump(const void *buf, size_t len);
char *mg_hex(const void *buf, int len, char *dst); char *mg_hex(const void *buf, int len, char *dst);
void mg_unhex(const char *buf, int len, unsigned char *to); void mg_unhex(const char *buf, int len, unsigned char *to);
@@ -431,6 +443,16 @@ double mg_time(void);
unsigned long mg_millis(void); unsigned long mg_millis(void);
void mg_usleep(unsigned long usecs); void mg_usleep(unsigned long usecs);
#if MG_ENABLE_FS
#ifdef _WIN32
typedef struct _stati64 mg_stat_t;
#else
typedef struct stat mg_stat_t;
#endif
int mg_stat(const char *path, mg_stat_t *);
FILE *mg_fopen(const char *fp, const char *mode);
#endif
#define mg_htons(x) mg_ntohs(x) #define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x) #define mg_htonl(x) mg_ntohl(x)
@@ -441,7 +463,7 @@ void mg_usleep(unsigned long usecs);
#if !defined(WEAK) #if !defined(WEAK)
#if (defined(__GNUC__) || defined(__clang__) || \ #if (defined(__GNUC__) || defined(__clang__) || \
defined(__TI_COMPILER_VERSION__)) && \ defined(__TI_COMPILER_VERSION__)) && \
!defined(_WIN32) !defined(_WIN32) && !defined(__CYGWIN__)
#define WEAK __attribute__((weak)) #define WEAK __attribute__((weak))
#else #else
#define WEAK #define WEAK
@@ -498,8 +520,8 @@ struct mg_iobuf {
size_t size, len; size_t size, len;
}; };
void mg_iobuf_init(struct mg_iobuf *, size_t); int mg_iobuf_init(struct mg_iobuf *, size_t);
void mg_iobuf_resize(struct mg_iobuf *, size_t); int mg_iobuf_resize(struct mg_iobuf *, size_t);
void mg_iobuf_free(struct mg_iobuf *); void mg_iobuf_free(struct mg_iobuf *);
size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t); size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t);
size_t mg_iobuf_delete(struct mg_iobuf *, size_t); size_t mg_iobuf_delete(struct mg_iobuf *, size_t);
@@ -542,38 +564,28 @@ void mg_hmac_sha1(const unsigned char *key, size_t key_len,
struct mg_connection; struct mg_connection;
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
void *ev_data, void *fn_data); void *ev_data, void *fn_data);
#if 0
struct mg_fn {
struct mg_fn *next;
mg_event_handler_t fn;
void *fn_data;
};
void mg_fn_add(struct mg_connection *c, mg_event_handler_t fn, void *fn_data);
void mg_fn_del(struct mg_connection *c, mg_event_handler_t fn);
#endif
void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_call(struct mg_connection *c, int ev, void *ev_data);
void mg_error(struct mg_connection *c, const char *fmt, ...); void mg_error(struct mg_connection *c, const char *fmt, ...);
enum { enum {
MG_EV_ERROR, // Error char *error_message MG_EV_ERROR, // Error char *error_message
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
MG_EV_RESOLVE, // Host name is resolved NULL MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_READ, // Data received from socket struct mg_str * MG_EV_READ, // Data received from socket struct mg_str *
MG_EV_WRITE, // Data written to socket int *num_bytes_written MG_EV_WRITE, // Data written to socket int *num_bytes_written
MG_EV_CLOSE, // Connection closed NULL MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_SNTP_TIME, // SNTP time received struct timeval * MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_USER, // Starting ID for user events MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events
}; };
@@ -600,6 +612,7 @@ struct mg_mgr {
struct mg_dns dns6; // DNS for IPv6 struct mg_dns dns6; // DNS for IPv6
int dnstimeout; // DNS resolve timeout in milliseconds int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif #endif
@@ -617,7 +630,7 @@ struct mg_connection {
void *fn_data; // User-speficied function parameter void *fn_data; // User-speficied function parameter
mg_event_handler_t pfn; // Protocol-specific handler function mg_event_handler_t pfn; // Protocol-specific handler function
void *pfn_data; // Protocol-specific function parameter void *pfn_data; // Protocol-specific function parameter
char label[32]; // Arbitrary label char label[50]; // Arbitrary label
void *tls; // TLS specific data void *tls; // TLS specific data
unsigned is_listening : 1; // Listening connection unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection unsigned is_client : 1; // Outbound (client) connection
@@ -668,7 +681,9 @@ struct mg_http_message {
struct mg_str method, uri, query, proto; // Request/response line struct mg_str method, uri, query, proto; // Request/response line
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
struct mg_str body; // Body struct mg_str body; // Body
struct mg_str message; // Request line + headers + body struct mg_str head; // Request + headers
struct mg_str chunk; // Chunk for chunked encoding, or partial body
struct mg_str message; // Request + headers + body
}; };
// Parameter for mg_http_serve_dir() // Parameter for mg_http_serve_dir()
@@ -681,6 +696,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data); mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
@@ -695,6 +711,7 @@ struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
void mg_http_event_handler(struct mg_connection *c, int ev); void mg_http_event_handler(struct mg_connection *c, int ev);
int mg_http_get_var(const struct mg_str *, const char *name, char *, int); int mg_http_get_var(const struct mg_str *, const char *name, char *, int);
int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
int mg_url_encode(const char *s, size_t n, char *buf, size_t len);
void mg_http_creds(struct mg_http_message *, char *user, int, char *pass, int); void mg_http_creds(struct mg_http_message *, char *user, int, char *pass, int);
bool mg_http_match_uri(const struct mg_http_message *, const char *glob); 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, int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
@@ -708,11 +725,11 @@ void mg_http_serve_ssi(struct mg_connection *c, const char *root,
struct mg_tls_opts { struct mg_tls_opts {
const char *ca; // CA certificate file. For both listeners and clients const char *ca; // CA certificate file. For both listeners and clients
const char *cert; // Certificate const char *cert; // Certificate
const char *certkey; // Certificate key const char *certkey; // Certificate key
const char *ciphers; // Cipher list const char *ciphers; // Cipher list
const char *srvname; // If not NULL, enables server name verification struct mg_str srvname; // If not empty, enables server name verification
}; };
int mg_tls_init(struct mg_connection *, struct mg_tls_opts *); int mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
@@ -810,6 +827,11 @@ void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len); uint32_t len);
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic, int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
uint8_t *qos, int pos); uint8_t *qos, int pos);
int mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
int pos);
void mg_mqtt_ping(struct mg_connection *);
void mg_mqtt_pong(struct mg_connection *);
void mg_mqtt_disconnect(struct mg_connection *);
+5
View File
@@ -4,6 +4,11 @@
#include <dirent.h> #include <dirent.h>
#include <netdb.h> #include <netdb.h>
#include <sys/stat.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 128
#endif
#endif #endif
+1
View File
@@ -8,5 +8,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"
#endif #endif
+1
View File
@@ -9,6 +9,7 @@
#include <FreeRTOS_Sockets.h> #include <FreeRTOS_Sockets.h>
#include <stdbool.h> #include <stdbool.h>
#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP #define IPPROTO_TCP FREERTOS_IPPROTO_TCP
#define IPPROTO_UDP FREERTOS_IPPROTO_UDP #define IPPROTO_UDP FREERTOS_IPPROTO_UDP
+2
View File
@@ -6,6 +6,7 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <dirent.h> #include <dirent.h>
#include <inttypes.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <signal.h> #include <signal.h>
@@ -17,5 +18,6 @@
#include <unistd.h> #include <unistd.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_ENABLE_POSIX 1 #define MG_ENABLE_POSIX 1
#define MG_INT64_FMT "%" PRId64
#endif #endif
+3 -1
View File
@@ -40,7 +40,7 @@ typedef int socklen_t;
#ifndef EWOULDBLOCK #ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK
#endif #endif
#define realpath(a, b) _fullpath((b), (a), 512) #define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
#ifndef va_copy #ifndef va_copy
#ifdef __va_copy #ifdef __va_copy
#define va_copy __va_copy #define va_copy __va_copy
@@ -52,4 +52,6 @@ typedef int socklen_t;
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR) #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
#endif #endif
#define MG_INT64_FMT "%I64d"
#endif #endif
+4 -4
View File
@@ -46,10 +46,6 @@
#define MG_ENABLE_DIRECTORY_LISTING 0 #define MG_ENABLE_DIRECTORY_LISTING 0
#endif #endif
#ifndef MG_ENABLE_HTTP_DEBUG_ENDPOINT
#define MG_ENABLE_HTTP_DEBUG_ENDPOINT 0
#endif
#ifndef MG_ENABLE_SOCKETPAIR #ifndef MG_ENABLE_SOCKETPAIR
#define MG_ENABLE_SOCKETPAIR 0 #define MG_ENABLE_SOCKETPAIR 0
#endif #endif
@@ -67,3 +63,7 @@
#ifndef MG_MAX_HTTP_HEADERS #ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 40 #define MG_MAX_HTTP_HEADERS 40
#endif #endif
#ifndef MG_PATH_MAX
#define MG_PATH_MAX PATH_MAX
#endif
+2
View File
@@ -156,7 +156,9 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
if (d->c->is_resolving) { if (d->c->is_resolving) {
d->c->is_resolving = 0; d->c->is_resolving = 0;
if (dm.resolved) { if (dm.resolved) {
#if MG_ENABLE_LOG
char buf[100]; char buf[100];
#endif
dm.addr.port = d->c->peer.port; // Save port dm.addr.port = d->c->peer.port; // Save port
d->c->peer = dm.addr; // Copy resolved address d->c->peer = dm.addr; // Copy resolved address
LOG(LL_DEBUG, ("%lu %s resolved to %s", d->c->id, dm.name, LOG(LL_DEBUG, ("%lu %s resolved to %s", d->c->id, dm.name,
+18 -28
View File
@@ -3,36 +3,26 @@
struct mg_connection; struct mg_connection;
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
void *ev_data, void *fn_data); void *ev_data, void *fn_data);
#if 0
struct mg_fn {
struct mg_fn *next;
mg_event_handler_t fn;
void *fn_data;
};
void mg_fn_add(struct mg_connection *c, mg_event_handler_t fn, void *fn_data);
void mg_fn_del(struct mg_connection *c, mg_event_handler_t fn);
#endif
void mg_call(struct mg_connection *c, int ev, void *ev_data); void mg_call(struct mg_connection *c, int ev, void *ev_data);
void mg_error(struct mg_connection *c, const char *fmt, ...); void mg_error(struct mg_connection *c, const char *fmt, ...);
enum { enum {
MG_EV_ERROR, // Error char *error_message MG_EV_ERROR, // Error char *error_message
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
MG_EV_RESOLVE, // Host name is resolved NULL MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_READ, // Data received from socket struct mg_str * MG_EV_READ, // Data received from socket struct mg_str *
MG_EV_WRITE, // Data written to socket int *num_bytes_written MG_EV_WRITE, // Data written to socket int *num_bytes_written
MG_EV_CLOSE, // Connection closed NULL MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message * MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_SNTP_TIME, // SNTP time received struct timeval * MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_USER, // Starting ID for user events MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events
}; };
+258 -152
View File
@@ -9,11 +9,6 @@
#include "version.h" #include "version.h"
#include "ws.h" #include "ws.h"
struct http_data {
void *old_pfn_data; // Previous pfn_data
FILE *fp; // For static file serving
};
// Multipart POST example: // Multipart POST example:
// https://gist.github.com/cpq/b8dd247571e6ee9c54ef7e8dfcfecf48 // https://gist.github.com/cpq/b8dd247571e6ee9c54ef7e8dfcfecf48
@@ -63,7 +58,7 @@ int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst,
s = (const char *) memchr(p, '&', (size_t)(e - p)); s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) s = e; if (s == NULL) s = e;
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
if (len == -1) len = -3; // Failed to decode if (len < 0) len = -3; // Failed to decode
break; break;
} }
} }
@@ -91,7 +86,7 @@ int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len,
} }
} }
if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination
return i >= src_len ? (int) j : -1; return i >= src_len && j < dst_len ? (int) j : -1;
} }
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
@@ -106,12 +101,12 @@ int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) {
return 0; return 0;
} }
static const char *skip(const char *s, const char *end, const char *delims, static const char *skip(const char *s, const char *e, const char *d,
struct mg_str *v) { struct mg_str *v) {
v->ptr = s; v->ptr = s;
while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++; while (s < e && *s != '\n' && strchr(d, *s) == NULL) s++;
v->len = s - v->ptr; v->len = s - v->ptr;
while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++; while (s < e && strchr(d, *s) != NULL) s++;
return s; return s;
} }
@@ -150,15 +145,19 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
memset(hm, 0, sizeof(*hm)); memset(hm, 0, sizeof(*hm));
if (req_len <= 0) return req_len; if (req_len <= 0) return req_len;
hm->message.ptr = s; hm->message.ptr = hm->head.ptr = s;
hm->body.ptr = s + req_len; hm->body.ptr = end;
hm->head.len = req_len;
hm->chunk.ptr = end;
hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite hm->message.len = hm->body.len = (size_t) ~0; // Set body length to infinite
// Parse request line // Parse request line
s = skip(s, end, " ", &hm->method); s = skip(s, end, " ", &hm->method);
s = skip(s, end, " ", &hm->uri); s = skip(s, end, " ", &hm->uri);
s = skip(s, end, "\r\n", &hm->proto); s = skip(s, end, "\r\n", &hm->proto);
if (hm->uri.ptr <= hm->method.ptr || hm->proto.ptr <= hm->uri.ptr) return -1;
// Sanity check
if (hm->method.len == 0 || hm->uri.len == 0 || hm->proto.len == 0) return -1;
// If URI contains '?' character, setup query string // If URI contains '?' character, setup query string
if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) { if ((qs = (const char *) memchr(hm->uri.ptr, '?', hm->uri.len)) != NULL) {
@@ -244,16 +243,14 @@ void mg_http_reply(struct mg_connection *c, int code, const char *headers,
#if MG_ENABLE_FS #if MG_ENABLE_FS
static void http_cb(struct mg_connection *, int, void *, void *); static void http_cb(struct mg_connection *, int, void *, void *);
static void restore_http_cb(struct mg_connection *c) { static void restore_http_cb(struct mg_connection *c) {
struct http_data *d = (struct http_data *) c->pfn_data; if (c->pfn_data != NULL) fclose((FILE *) c->pfn_data);
if (d->fp != NULL) fclose(d->fp); c->pfn_data = NULL;
c->pfn_data = d->old_pfn_data;
c->pfn = http_cb; c->pfn = http_cb;
free(d);
} }
char *mg_http_etag(char *buf, size_t len, struct stat *st) { char *mg_http_etag(char *buf, size_t len, mg_stat_t *st) {
snprintf(buf, len, "\"%lx.%lu\"", (unsigned long) st->st_mtime, snprintf(buf, len, "\"%lx." MG_INT64_FMT "\"", (unsigned long) st->st_mtime,
(unsigned long) st->st_size); (int64_t) st->st_size);
return buf; return buf;
} }
@@ -271,7 +268,7 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name); snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, name);
LOG(LL_DEBUG, LOG(LL_DEBUG,
("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name)); ("%p %d bytes @ %d [%s]", c->fd, (int) hm->body.len, (int) oft, name));
if ((fp = fopen(path, oft == 0 ? "wb" : "a")) == NULL) { if ((fp = mg_fopen(path, oft == 0 ? "wb" : "ab")) == NULL) {
mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno); mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
return -2; return -2;
} else { } else {
@@ -286,12 +283,12 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
static void static_cb(struct mg_connection *c, int ev, void *ev_data, static void static_cb(struct mg_connection *c, int ev, void *ev_data,
void *fn_data) { void *fn_data) {
if (ev == MG_EV_WRITE || ev == MG_EV_POLL) { if (ev == MG_EV_WRITE || ev == MG_EV_POLL) {
struct http_data *d = (struct http_data *) fn_data; FILE *fp = (FILE *) fn_data;
// Read to send IO buffer directly, avoid extra on-stack buffer // Read to send IO buffer directly, avoid extra on-stack buffer
size_t n, max = 2 * MG_IO_SIZE; size_t n, max = 2 * MG_IO_SIZE;
if (c->send.size < max) mg_iobuf_resize(&c->send, max); if (c->send.size < max) mg_iobuf_resize(&c->send, max);
if (c->send.len >= c->send.size) return; // Rate limit if (c->send.len >= c->send.size) return; // Rate limit
n = fread(c->send.buf + c->send.len, 1, c->send.size - c->send.len, d->fp); n = fread(c->send.buf + c->send.len, 1, c->send.size - c->send.len, fp);
if (n > 0) c->send.len += n; if (n > 0) c->send.len += n;
if (c->send.len < c->send.size) restore_http_cb(c); if (c->send.len < c->send.size) restore_http_cb(c);
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
@@ -309,9 +306,9 @@ static const char *guess_content_type(const char *filename) {
size_t ext_len; size_t ext_len;
const char *type; const char *type;
} * t, types[] = { } * t, types[] = {
MIME_ENTRY("html", "text/html"), MIME_ENTRY("html", "text/html; charset=utf-8"),
MIME_ENTRY("htm", "text/html"), MIME_ENTRY("htm", "text/html; charset=utf-8"),
MIME_ENTRY("shtml", "text/html"), MIME_ENTRY("shtml", "text/html; charset=utf-8"),
MIME_ENTRY("css", "text/css"), MIME_ENTRY("css", "text/css"),
MIME_ENTRY("js", "text/javascript"), MIME_ENTRY("js", "text/javascript"),
MIME_ENTRY("mjs", "text/javascript"), MIME_ENTRY("mjs", "text/javascript"),
@@ -322,7 +319,7 @@ static const char *guess_content_type(const char *filename) {
MIME_ENTRY("jpeg", "image/jpeg"), MIME_ENTRY("jpeg", "image/jpeg"),
MIME_ENTRY("png", "image/png"), MIME_ENTRY("png", "image/png"),
MIME_ENTRY("svg", "image/svg+xml"), MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("txt", "text/plain"), MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("wav", "audio/wav"), MIME_ENTRY("wav", "audio/wav"),
MIME_ENTRY("mp3", "audio/mpeg"), MIME_ENTRY("mp3", "audio/mpeg"),
MIME_ENTRY("mid", "audio/mid"), MIME_ENTRY("mid", "audio/mid"),
@@ -359,38 +356,34 @@ static const char *guess_content_type(const char *filename) {
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue; if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
return t->type; return t->type;
} }
return "text/plain"; return "text/plain; charset=utf-8";
} }
void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
const char *path, const char *mime, const char *hdrs) { const char *path, const char *mime, const char *hdrs) {
struct mg_str *inm = mg_http_get_header(hm, "If-None-Match"); struct mg_str *inm = mg_http_get_header(hm, "If-None-Match");
struct stat st; mg_stat_t st;
FILE *fp = fopen(path, "rb");
char etag[64]; char etag[64];
if (fp == NULL || stat(path, &st) != 0 || FILE *fp = mg_fopen(path, "rb");
if (fp == NULL || mg_stat(path, &st) != 0 ||
mg_http_etag(etag, sizeof(etag), &st) != etag) { mg_http_etag(etag, sizeof(etag), &st) != etag) {
LOG(LL_DEBUG,
("404 [%.*s] [%s] %p", (int) hm->uri.len, hm->uri.ptr, path, fp));
mg_http_reply(c, 404, "", "%s", "Not found\n"); mg_http_reply(c, 404, "", "%s", "Not found\n");
if (fp != NULL) fclose(fp);
} else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) { } else if (inm != NULL && mg_vcasecmp(inm, etag) == 0) {
fclose(fp); fclose(fp);
mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n"); mg_printf(c, "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n");
} else { } else {
mg_printf(c, mg_printf(c,
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n" "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n"
"Etag: %s\r\nContent-Length: %lu\r\n%s\r\n", "Etag: %s\r\nContent-Length: " MG_INT64_FMT "\r\n%s\r\n",
mime, etag, (unsigned long) st.st_size, hdrs ? hdrs : ""); mime, etag, (int64_t) st.st_size, hdrs ? hdrs : "");
if (mg_vcasecmp(&hm->method, "HEAD") == 0) { if (mg_vcasecmp(&hm->method, "HEAD") == 0) {
fclose(fp); fclose(fp);
} else { } else {
struct http_data *d = (struct http_data *) calloc(1, sizeof(*d)); c->pfn = static_cb;
if (d == NULL) { c->pfn_data = fp;
mg_error(c, "static HTTP OOM");
} else {
d->fp = fp;
d->old_pfn_data = c->pfn_data;
c->pfn = static_cb;
c->pfn_data = d;
}
} }
} }
} }
@@ -399,21 +392,23 @@ void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm,
MG_ARCH == MG_ARCH_FREERTOS MG_ARCH == MG_ARCH_FREERTOS
char *realpath(const char *src, char *dst) { char *realpath(const char *src, char *dst) {
int len = strlen(src); int len = strlen(src);
if (len > PATH_MAX - 1) len = PATH_MAX - 1; if (len > MG_PATH_MAX - 1) len = MG_PATH_MAX - 1;
strncpy(dst, src, len); strncpy(dst, src, len);
dst[len] = '\0'; dst[len] = '\0';
LOG(LL_DEBUG, ("[%s] -> [%s]", src, dst));
return dst; return dst;
} }
#endif #endif
// Try to avoid dirent API // Allow user to override this function
static int mg_is_dir(const char *path) { bool mg_is_dir(const char *path) WEAK;
bool mg_is_dir(const char *path) {
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
struct FF_STAT st; struct FF_STAT st;
return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR); return (ff_stat(path, &st) == 0) && (st.st_mode & FF_IFDIR);
#else #else
struct stat st; mg_stat_t st;
return (stat(path, &st) == 0) && (st.st_mode & S_IFDIR); return mg_stat(path, &st) == 0 && S_ISDIR(st.st_mode);
#endif #endif
} }
@@ -528,10 +523,10 @@ struct dirent *readdir(DIR *d) {
} }
#endif #endif
static void printdirentry(struct mg_connection *c, struct mg_http_message *hm, static void printdirentry(struct mg_connection *c, const char *name,
const char *name, struct stat *stp) { mg_stat_t *stp) {
char size[64], mod[64]; //, path[PATH_MAX]; char size[64], mod[64], path[MG_PATH_MAX];
int is_dir = S_ISDIR(stp->st_mode); int is_dir = S_ISDIR(stp->st_mode), n = 0;
const char *slash = is_dir ? "/" : ""; const char *slash = is_dir ? "/" : "";
if (is_dir) { if (is_dir) {
@@ -548,114 +543,156 @@ static void printdirentry(struct mg_connection *c, struct mg_http_message *hm,
} }
} }
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime)); strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&stp->st_mtime));
// mg_escape(file_name, path, sizeof(path)); n = mg_url_encode(name, strlen(name), path, sizeof(path));
// href = mg_url_encode(mg_mk_str(file_name)); mg_printf(c,
mg_http_printf_chunk(c, " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
" <tr><td><a href=\"%.*s%s%s\">%s%s</a></td>" "<td>%s</td><td>%s</td></tr>\n",
"<td>%s</td><td>%s</td></tr>\n", n, path, slash, name, slash, mod, size);
(int) hm->uri.len, hm->uri.ptr, name, slash, name, slash,
mod, size);
// free((void *) href.p);
} }
static void listdir(struct mg_connection *c, struct mg_http_message *hm, static void listdir(struct mg_connection *c, struct mg_http_message *hm,
char *dir) { char *dir) {
char path[PATH_MAX + 1], *p = &dir[strlen(dir) - 1]; char path[MG_PATH_MAX], *p = &dir[strlen(dir) - 1], tmp[10];
struct dirent *dp; struct dirent *dp;
DIR *dirp; DIR *dirp;
static const char *sort_js_code =
"<script>function srt(tb, sc, so, d) {"
"var tr = Array.prototype.slice.call(tb.rows, 0),"
"tr = tr.sort(function (a, b) { var c1 = a.cells[sc], c2 = b.cells[sc],"
"n1 = c1.getAttribute('name'), n2 = c2.getAttribute('name'), "
"t1 = a.cells[2].getAttribute('name'), "
"t2 = b.cells[2].getAttribute('name'); "
"return so * (t1 < 0 && t2 >= 0 ? -1 : t2 < 0 && t1 >= 0 ? 1 : "
"n1 ? parseInt(n2) - parseInt(n1) : "
"c1.textContent.trim().localeCompare(c2.textContent.trim())); });";
static const char *sort_js_code2 =
"for (var i = 0; i < tr.length; i++) tb.appendChild(tr[i]); "
"if (!d) window.location.hash = ('sc=' + sc + '&so=' + so); "
"};"
"window.onload = function() {"
"var tb = document.getElementById('tb');"
"var m = /sc=([012]).so=(1|-1)/.exec(window.location.hash) || [0, 2, 1];"
"var sc = m[1], so = m[2]; document.onclick = function(ev) { "
"var c = ev.target.rel; if (c) {if (c == sc) so *= -1; srt(tb, c, so); "
"sc = c; ev.preventDefault();}};"
"srt(tb, sc, so, true);"
"}"
"</script>";
while (p > dir && *p != '/') *p-- = '\0'; while (p > dir && *p != '/') *p-- = '\0';
if ((dirp = (opendir(dir))) != NULL) { if ((dirp = (opendir(dir))) != NULL) {
mg_printf(c, "%s\r\n", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"); size_t off, n;
mg_http_printf_chunk( mg_printf(c, "%s\r\n",
c, "HTTP/1.1 200 OK\r\n"
"<!DOCTYPE html><html><head><title>Index of %.*s</title>" "Content-Type: text/html; charset=utf-8\r\n"
"<style>th,td {text-align: left; padding-right: 1em; " "Content-Length: \r\n");
"font-family: monospace; }</style></head>" off = c->send.len; // Start of body
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>" mg_printf(c,
"<tr><th>Name</th><th>Modified</th><th>Size</th></tr>" "<!DOCTYPE html><html><head><title>Index of %.*s</title>%s%s"
"<tr><td colspan=\"3\"><hr></td></tr></thead><tbody>\n", "<style>th,td {text-align: left; padding-right: 1em; "
(int) hm->uri.len, hm->uri.ptr, (int) hm->uri.len, hm->uri.ptr); "font-family: monospace; }</style></head>"
"<body><h1>Index of %.*s</h1><table cellpadding=\"0\"><thead>"
"<tr><th><a href=\"#\" rel=\"0\">Name</a></th><th>"
"<a href=\"#\" rel=\"1\">Modified</a></th>"
"<th><a href=\"#\" rel=\"2\">Size</a></th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>"
"</thead>"
"<tbody id=\"tb\">\n",
(int) hm->uri.len, hm->uri.ptr, sort_js_code, sort_js_code2,
(int) hm->uri.len, hm->uri.ptr);
while ((dp = readdir(dirp)) != NULL) { while ((dp = readdir(dirp)) != NULL) {
struct stat st; mg_stat_t st;
const char *sep = dp->d_name[0] == MG_DIRSEP ? "/" : "";
// Do not show current dir and hidden files // Do not show current dir and hidden files
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
snprintf(path, sizeof(path), "%s/%s", dir, dp->d_name); // SPIFFS can report "/foo.txt" in the dp->d_name
if (stat(path, &st) != 0) { if (snprintf(path, sizeof(path), "%s%s%s", dir, sep, dp->d_name) < 0) {
LOG(LL_ERROR, ("%s truncated", dp->d_name));
} else if (mg_stat(path, &st) != 0) {
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno)); LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
continue; } else {
printdirentry(c, dp->d_name, &st);
} }
printdirentry(c, hm, dp->d_name, &st);
} }
closedir(dirp); closedir(dirp);
mg_http_printf_chunk( mg_printf(c,
c, "</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>"
"</tbody><tfoot><tr><td colspan=\"3\"><hr></td></tr></tfoot>" "</table><address>Mongoose v.%s</address></body></html>\n",
"</table><address>Mongoose v.%s</address></body></html>\n", MG_VERSION);
MG_VERSION); n = snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off));
mg_http_write_chunk(c, "", 0); memcpy(c->send.buf + off - 10, tmp, n); // Set content length
} else { } else {
mg_http_reply(c, 400, "", "Cannot open dir"); mg_http_reply(c, 400, "", "Cannot open dir");
LOG(LL_DEBUG, ("%lu opendir(%s) -> %d", c->id, dir, errno)); LOG(LL_ERROR, ("%lu opendir(%s) -> %d", c->id, dir, errno));
} }
} }
#endif #endif
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
struct mg_http_serve_opts *opts) { struct mg_http_serve_opts *opts) {
char path[PATH_MAX + 2], root[sizeof(path) - 2], real[sizeof(path) - 2]; char t1[MG_PATH_MAX], t2[sizeof(t1)];
path[0] = root[0] = real[0] = '\0'; t1[0] = t2[0] = '\0';
if (realpath(opts->root_dir, root) == NULL)
LOG(LL_DEBUG, ("realpath(%s): %d", opts->root_dir, errno)); if (realpath(opts->root_dir, t1) == NULL) {
if (!mg_is_dir(root)) { LOG(LL_ERROR, ("realpath(%s): %d", opts->root_dir, errno));
mg_http_reply(c, 400, "", "Bad web root [%s]\n", root); mg_http_reply(c, 400, "", "Bad web root [%s]\n", opts->root_dir);
} else if (!mg_is_dir(t1)) {
mg_http_reply(c, 400, "", "Invalid web root [%s]\n", t1);
} else { } else {
char dec[PATH_MAX];
// NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for // NOTE(lsm): Xilinx snprintf does not 0-terminate the detination for
// the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0 // the %.*s specifier, if the length is zero. Make sure hm->uri.len > 0
bool is_index = false; bool is_index = false;
int ndec = mg_url_decode(hm->uri.ptr, hm->uri.len, dec, sizeof(dec), 0); size_t n1 = strlen(t1), n2;
size_t n =
snprintf(path, sizeof(path), "%s%.*s", root, ndec < 0 ? 0 : ndec, dec); mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
while (n > 0 && n < sizeof(path) && path[n - 1] == '/') path[--n] = 0; t1[sizeof(t1) - 1] = '\0';
if (realpath(path, real) == NULL) n2 = strlen(t1);
LOG(LL_DEBUG, ("realpath(%s): %d", path, errno)); while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
// LOG(LL_INFO, ("[%s] [%s] [%s] [%s]", dir, root, path, real));
if (mg_is_dir(real)) { if (realpath(t1, t2) == NULL) {
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1); LOG(LL_ERROR, ("realpath(%s): %d", t1, errno));
real[sizeof(real) - 1] = '\0'; mg_http_reply(c, 404, "", "Not found [%.*s]\n", (int) hm->uri.len,
hm->uri.ptr);
return;
}
if (mg_is_dir(t2)) {
strncat(t2, "/index.html", sizeof(t2) - strlen(t2) - 1);
t2[sizeof(t2) - 1] = '\0';
is_index = true; is_index = true;
} }
if (strlen(real) < strlen(root) || memcmp(real, root, strlen(root)) != 0) {
mg_http_reply(c, 404, "", "Not found %.*s\n", hm->uri.len, hm->uri.ptr); if (strlen(t2) < n1 || memcmp(t1, t2, n1) != 0) {
// Requested file is located outside root directory, fail
mg_http_reply(c, 404, "", "Invalid URI [%.*s]\n", (int) hm->uri.len,
hm->uri.ptr);
} else { } else {
FILE *fp = fopen(real, "r"); FILE *fp = mg_fopen(t2, "r");
#if MG_ENABLE_SSI #if MG_ENABLE_SSI
if (is_index && fp == NULL) { if (is_index && fp == NULL) {
char *p = real + strlen(real); char *p = t2 + strlen(t2);
while (p > real && p[-1] != '/') p--; while (p > t2 && p[-1] != '/') p--;
strncpy(p, "index.shtml", &real[sizeof(real)] - p - 2); strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
real[sizeof(real) - 1] = '\0'; t2[sizeof(t2) - 1] = '\0';
fp = fopen(real, "r"); fp = mg_fopen(t2, "r");
} }
#endif
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real);
#endif #endif
if (is_index && fp == NULL) { if (is_index && fp == NULL) {
#if MG_ENABLE_DIRECTORY_LISTING #if MG_ENABLE_DIRECTORY_LISTING
listdir(c, hm, real); listdir(c, hm, t2);
#else #else
mg_http_reply(c, 403, "", "%s", "Directory listing not supported"); mg_http_reply(c, 403, "", "%s", "Directory listing not supported");
#endif #endif
#if MG_ENABLE_SSI #if MG_ENABLE_SSI
} else if (opts->ssi_pattern != NULL && } else if (opts->ssi_pattern != NULL &&
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), t2,
real, strlen(real))) { strlen(t2))) {
mg_http_serve_ssi(c, root, real); t1[n1] = '\0';
mg_http_serve_ssi(c, t1, t2);
#endif #endif
} else { } else {
mg_http_serve_file(c, hm, real, guess_content_type(real), NULL); mg_http_serve_file(c, hm, t2, guess_content_type(t2), NULL);
} }
if (fp != NULL) fclose(fp); if (fp != NULL) fclose(fp);
} }
@@ -663,6 +700,27 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
} }
#endif #endif
static bool mg_is_url_safe(int c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~';
}
int mg_url_encode(const char *s, size_t sl, char *buf, size_t len) {
size_t i, n = 0;
for (i = 0; i < sl; i++) {
int c = *(unsigned char *) &s[i];
if (n + 4 >= len) return 0;
if (mg_is_url_safe(c)) {
buf[n++] = s[i];
} else {
buf[n++] = '%';
mg_hex(&s[i], 1, &buf[n]);
n += 2;
}
}
return n;
}
void mg_http_creds(struct mg_http_message *hm, char *user, int userlen, void mg_http_creds(struct mg_http_message *hm, char *user, int userlen,
char *pass, int passlen) { char *pass, int passlen) {
struct mg_str *v = mg_http_get_header(hm, "Authorization"); struct mg_str *v = mg_http_get_header(hm, "Authorization");
@@ -696,73 +754,121 @@ bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len); return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len);
} }
static void http_cb(struct mg_connection *c, int ev, void *ev_data, static size_t get_chunk_length(const char *buf, size_t len, size_t *ll) {
void *fn_data) { size_t i = 0, n;
while (i < len && buf[i] != '\r' && i != '\n') i++;
n = mg_unhexn((char *) buf, i);
while (i < len && (buf[i] == '\r' || i == '\n')) i++;
// LOG(LL_INFO, ("len %zu i %zu n %zu ", len, i, n));
if (ll != NULL) *ll = i + 1;
if (i < len && i + n + 2 < len) return i + n + 3;
return 0;
}
// Walk through all chunks in the chunked body. For each chunk, fire
// an MG_EV_HTTP_CHUNK event.
static void walkchunks(struct mg_connection *c, struct mg_http_message *hm,
int reqlen) {
size_t off = 0, bl, ll;
while (off + reqlen < c->recv.len) {
char *buf = (char *) &c->recv.buf[reqlen];
size_t memo = c->recv.len;
size_t cl = get_chunk_length(&buf[off], memo - reqlen - off, &ll);
// LOG(LL_INFO, ("len %zu off %zu cl %zu ll %zu", len, off, cl, ll));
if (cl == 0) break;
hm->chunk = mg_str_n(&buf[off + ll], cl < ll + 2 ? 0 : cl - ll - 2);
mg_call(c, MG_EV_HTTP_CHUNK, hm);
// Increase offset only if user has not deleted this chunk
if (memo == c->recv.len) off += cl;
if (cl <= 5) {
// Zero chunk - last one. Prepare body - cut off chunk lengths
off = bl = 0;
while (off + reqlen < c->recv.len) {
char *buf = (char *) &c->recv.buf[reqlen];
size_t memo = c->recv.len;
size_t cl = get_chunk_length(&buf[off], memo - reqlen - off, &ll);
size_t n = cl < ll + 2 ? 0 : cl - ll - 2;
if (cl <= 5) break;
memmove(buf + bl, buf + off + ll, n);
bl += n;
off += cl;
}
// Set message length to indicate we've received
// everything, to fire MG_EV_HTTP_MSG
hm->message.len = bl + reqlen;
hm->body.len = bl;
break;
}
}
}
static bool mg_is_chunked(struct mg_http_message *hm) {
struct mg_str needle = mg_str_n("chunked", 7);
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
return te != NULL && mg_strstr(*te, needle) != NULL;
}
void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm) {
struct mg_str ch = hm->chunk;
if (mg_is_chunked(hm)) {
ch.len += 4; // \r\n before and after the chunk
ch.ptr -= 2;
while (ch.ptr > hm->body.ptr && *ch.ptr != '\n') ch.ptr--, ch.len++;
}
{
const char *end = &ch.ptr[ch.len];
size_t n = end - (char *) c->recv.buf;
if (c->recv.len > n) memmove((char *) ch.ptr, end, c->recv.len - n);
// LOG(LL_INFO, ("DELETING CHUNK: %zu %zu %zu\n%.*s", c->recv.len, n,
// ch.len, (int) ch.len, ch.ptr));
}
c->recv.len -= ch.len;
}
static void http_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
if (ev == MG_EV_READ || ev == MG_EV_CLOSE) { if (ev == MG_EV_READ || ev == MG_EV_CLOSE) {
struct mg_http_message hm; struct mg_http_message hm;
for (;;) { for (;;) {
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); 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) { if (ev == MG_EV_CLOSE) {
hm.message.len = c->recv.len; hm.message.len = c->recv.len;
hm.body.len = hm.message.len - (hm.body.ptr - hm.message.ptr); hm.body.len = hm.message.len - (hm.body.ptr - hm.message.ptr);
} else if (is_chunked) {
walkchunks(c, &hm, n);
} }
// LOG(LL_INFO,
//("---->%d %d\n%.*s", n, is_chunked, (int) c->recv.len, c->recv.buf));
if (n < 0 && ev == MG_EV_READ) { if (n < 0 && ev == MG_EV_READ) {
LOG(LL_ERROR, ("%lu HTTP parse error", c->id)); LOG(LL_ERROR, ("%lu HTTP parse error", c->id));
c->is_closing = 1; c->is_closing = 1;
break; break;
} else if (n > 0 && (size_t) c->recv.len >= hm.message.len) { } else if (n > 0 && (size_t) c->recv.len >= hm.message.len) {
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
snprintf(c->label, sizeof(c->label) - 1, "<-[%.*s]", (int) hm.uri.len,
hm.uri.ptr);
if (mg_http_match_uri(&hm, "/debug/info")) {
struct mg_mgr *mgr = (struct mg_mgr *) fn_data;
struct mg_connection *x;
mg_printf(c, "%s\r\n",
"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n");
for (x = mgr->conns; x != NULL; x = x->next) {
// LOG(LL_INFO, ("%p %s", x->fd, x->label));
mg_http_printf_chunk(
c,
"%-4p %-12s %04d.%04d/%04d.%04d"
" %d%d%d%d%d%d%d%d%d%d%d%d%d%d\n",
x->fd, x->label, x->recv.len, x->recv.size, x->send.len,
x->send.size, 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);
}
mg_http_write_chunk(c, "", 0);
mg_iobuf_delete(&c->recv, hm.message.len);
continue;
}
#endif
mg_call(c, MG_EV_HTTP_MSG, &hm); mg_call(c, MG_EV_HTTP_MSG, &hm);
mg_iobuf_delete(&c->recv, hm.message.len); mg_iobuf_delete(&c->recv, hm.message.len);
} else { } else {
if (n > 0 && !is_chunked) {
hm.chunk = mg_str_n((char *) &c->recv.buf[n], c->recv.len - n);
mg_call(c, MG_EV_HTTP_CHUNK, &hm);
}
break; break;
} }
} }
} }
(void) fn_data; (void) fnd;
(void) ev_data; (void) evd;
} }
struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) { mg_event_handler_t fn, void *fn_data) {
struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); struct mg_connection *c = mg_connect(mgr, url, fn, fn_data);
if (c != NULL) c->pfn = http_cb, c->pfn_data = mgr; if (c != NULL) c->pfn = http_cb;
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
snprintf(c->label, sizeof(c->label) - 1, "->%s", url);
#endif
return c; return c;
} }
struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) { mg_event_handler_t fn, void *fn_data) {
struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
if (c != NULL) c->pfn = http_cb, c->pfn_data = mgr; if (c != NULL) c->pfn = http_cb;
#if MG_ENABLE_HTTP_DEBUG_ENDPOINT
if (c != NULL) snprintf(c->label, sizeof(c->label) - 1, "<-LSN");
#endif
return c; return c;
} }
+5 -1
View File
@@ -16,7 +16,9 @@ struct mg_http_message {
struct mg_str method, uri, query, proto; // Request/response line struct mg_str method, uri, query, proto; // Request/response line
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
struct mg_str body; // Body struct mg_str body; // Body
struct mg_str message; // Request line + headers + body struct mg_str head; // Request + headers
struct mg_str chunk; // Chunk for chunked encoding, or partial body
struct mg_str message; // Request + headers + body
}; };
// Parameter for mg_http_serve_dir() // Parameter for mg_http_serve_dir()
@@ -29,6 +31,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data); mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
@@ -43,6 +46,7 @@ struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
void mg_http_event_handler(struct mg_connection *c, int ev); void mg_http_event_handler(struct mg_connection *c, int ev);
int mg_http_get_var(const struct mg_str *, const char *name, char *, int); int mg_http_get_var(const struct mg_str *, const char *name, char *, int);
int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
int mg_url_encode(const char *s, size_t n, char *buf, size_t len);
void mg_http_creds(struct mg_http_message *, char *user, int, char *pass, int); void mg_http_creds(struct mg_http_message *, char *user, int, char *pass, int);
bool mg_http_match_uri(const struct mg_http_message *, const char *glob); 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, int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
+7 -7
View File
@@ -3,7 +3,8 @@
#include <string.h> #include <string.h>
void mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
int ok = 1;
if (new_size == 0) { if (new_size == 0) {
free(io->buf); free(io->buf);
io->buf = NULL; io->buf = NULL;
@@ -18,16 +19,16 @@ void mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) {
io->buf = (unsigned char *) p; io->buf = (unsigned char *) p;
io->size = new_size; io->size = new_size;
} else { } else {
ok = 0;
LOG(LL_ERROR, LOG(LL_ERROR,
("%lu->%lu", (unsigned long) io->size, (unsigned long) new_size)); ("%lu->%lu", (unsigned long) io->size, (unsigned long) new_size));
} }
} }
return ok;
} }
void mg_iobuf_init(struct mg_iobuf *io, size_t size) { int mg_iobuf_init(struct mg_iobuf *io, size_t size) {
io->buf = NULL; return mg_iobuf_resize(io, size);
io->len = io->size = 0;
if (size > 0) mg_iobuf_resize(io, size);
} }
size_t mg_iobuf_append(struct mg_iobuf *io, const void *buf, size_t len, size_t mg_iobuf_append(struct mg_iobuf *io, const void *buf, size_t len,
@@ -49,6 +50,5 @@ size_t mg_iobuf_delete(struct mg_iobuf *io, size_t len) {
} }
void mg_iobuf_free(struct mg_iobuf *io) { void mg_iobuf_free(struct mg_iobuf *io) {
free(io->buf); mg_iobuf_resize(io, 0);
mg_iobuf_init(io, 0);
} }
+2 -2
View File
@@ -7,8 +7,8 @@ struct mg_iobuf {
size_t size, len; size_t size, len;
}; };
void mg_iobuf_init(struct mg_iobuf *, size_t); int mg_iobuf_init(struct mg_iobuf *, size_t);
void mg_iobuf_resize(struct mg_iobuf *, size_t); int mg_iobuf_resize(struct mg_iobuf *, size_t);
void mg_iobuf_free(struct mg_iobuf *); void mg_iobuf_free(struct mg_iobuf *);
size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t); size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t);
size_t mg_iobuf_delete(struct mg_iobuf *, size_t); size_t mg_iobuf_delete(struct mg_iobuf *, size_t);
+1 -2
View File
@@ -1,5 +1,5 @@
// Copyright (c) 2004-2013 Sergey Lyubka // Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2020 Cesanta Software Limited // Copyright (c) 2013-2021 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// This software is dual-licensed: you can redistribute it and/or modify // This software is dual-licensed: you can redistribute it and/or modify
@@ -14,4 +14,3 @@
// //
// Alternatively, you can license this software under a commercial // Alternatively, you can license this software under a commercial
// license, as set out in <https://www.cesanta.com/license>. // license, as set out in <https://www.cesanta.com/license>.
-1
View File
@@ -7,7 +7,6 @@
static void mg_log_stdout(const void *buf, int len, void *userdata) { static void mg_log_stdout(const void *buf, int len, void *userdata) {
(void) userdata; (void) userdata;
fwrite(buf, 1, len, stdout); fwrite(buf, 1, len, stdout);
fflush(stdout);
} }
static const char *s_spec = "2"; static const char *s_spec = "2";
+27 -4
View File
@@ -176,20 +176,31 @@ int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m) {
return MQTT_OK; return MQTT_OK;
} }
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic, static int mg_mqtt_next_topic(struct mg_mqtt_message *msg, struct mg_str *topic,
uint8_t *qos, int pos) { uint8_t *qos, int pos) {
unsigned char *buf = (unsigned char *) msg->dgram.ptr + pos; unsigned char *buf = (unsigned char *) msg->dgram.ptr + pos;
int new_pos; int new_pos;
if ((size_t) pos >= msg->dgram.len) return -1; if ((size_t) pos >= msg->dgram.len) return -1;
topic->len = buf[0] << 8 | buf[1]; topic->len = buf[0] << 8 | buf[1];
topic->ptr = (char *) buf + 2; topic->ptr = (char *) buf + 2;
new_pos = pos + 2 + topic->len + 1; new_pos = pos + 2 + topic->len + (qos == NULL ? 0 : 1);
if ((size_t) new_pos > msg->dgram.len) return -1; if ((size_t) new_pos > msg->dgram.len) return -1;
*qos = buf[2 + topic->len]; if (qos != NULL) *qos = buf[2 + topic->len];
return new_pos; return new_pos;
} }
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
uint8_t *qos, int pos) {
uint8_t tmp;
return mg_mqtt_next_topic(msg, topic, qos == NULL ? &tmp : qos, pos);
}
int mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
int pos) {
return mg_mqtt_next_topic(msg, topic, NULL, pos);
}
static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data, static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
void *fn_data) { void *fn_data) {
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
@@ -232,6 +243,18 @@ static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data,
(void) fn_data; (void) fn_data;
} }
void mg_mqtt_ping(struct mg_connection *nc) {
mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0);
}
void mg_mqtt_pong(struct mg_connection *nc) {
mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0);
}
void mg_mqtt_disconnect(struct mg_connection *nc) {
mg_mqtt_send_header(nc, MQTT_CMD_DISCONNECT, 0, 0);
}
struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
struct mg_mqtt_opts *opts, struct mg_mqtt_opts *opts,
mg_event_handler_t fn, void *fn_data) { mg_event_handler_t fn, void *fn_data) {
+5
View File
@@ -55,3 +55,8 @@ void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len); uint32_t len);
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic, int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
uint8_t *qos, int pos); uint8_t *qos, int pos);
int mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
int pos);
void mg_mqtt_ping(struct mg_connection *);
void mg_mqtt_pong(struct mg_connection *);
void mg_mqtt_disconnect(struct mg_connection *);
+2 -1
View File
@@ -23,6 +23,7 @@ struct mg_mgr {
struct mg_dns dns6; // DNS for IPv6 struct mg_dns dns6; // DNS for IPv6
int dnstimeout; // DNS resolve timeout in milliseconds int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif #endif
@@ -40,7 +41,7 @@ struct mg_connection {
void *fn_data; // User-speficied function parameter void *fn_data; // User-speficied function parameter
mg_event_handler_t pfn; // Protocol-specific handler function mg_event_handler_t pfn; // Protocol-specific handler function
void *pfn_data; // Protocol-specific function parameter void *pfn_data; // Protocol-specific function parameter
char label[32]; // Arbitrary label char label[50]; // Arbitrary label
void *tls; // TLS specific data void *tls; // TLS specific data
unsigned is_listening : 1; // Listening connection unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection unsigned is_client : 1; // Outbound (client) connection
+7 -5
View File
@@ -135,7 +135,7 @@ static int ll_read(struct mg_connection *c, void *buf, int len, int *fail) {
MG_SOCK_ERRNO, *fail)); MG_SOCK_ERRNO, *fail));
if (n > 0 && c->is_hexdumping) { if (n > 0 && c->is_hexdumping) {
char *s = mg_hexdump(buf, n); char *s = mg_hexdump(buf, n);
// LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", n, s)); LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "<-", n, s));
free(s); free(s);
} }
return n; return n;
@@ -151,7 +151,7 @@ static int ll_write(struct mg_connection *c, const void *buf, int len,
MG_SOCK_ERRNO)); MG_SOCK_ERRNO));
if (n > 0 && c->is_hexdumping) { if (n > 0 && c->is_hexdumping) {
char *s = mg_hexdump(buf, len); char *s = mg_hexdump(buf, len);
// LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "->", len, s)); LOG(LL_INFO, ("\n-- %lu %s %s %d\n%s--", c->id, c->label, "->", len, s));
free(s); free(s);
} }
return n; return n;
@@ -161,6 +161,7 @@ int mg_send(struct mg_connection *c, const void *buf, size_t len) {
int fail, n = c->is_udp int fail, n = c->is_udp
? ll_write(c, buf, (SOCKET) len, &fail) ? ll_write(c, buf, (SOCKET) len, &fail)
: (int) mg_iobuf_append(&c->send, buf, len, MG_IO_SIZE); : (int) mg_iobuf_append(&c->send, buf, len, MG_IO_SIZE);
if (len > 0 && n == 0) fail = 1;
return n; return n;
} }
@@ -235,8 +236,9 @@ static void read_conn(struct mg_connection *c,
// NOTE(lsm): do only one iteration of reads, cause some systems // NOTE(lsm): do only one iteration of reads, cause some systems
// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data // (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data
if (c->recv.size - c->recv.len < MG_IO_SIZE && if (c->recv.size - c->recv.len < MG_IO_SIZE &&
c->recv.size < MG_MAX_RECV_BUF_SIZE) { c->recv.size < MG_MAX_RECV_BUF_SIZE &&
mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE); !mg_iobuf_resize(&c->recv, c->recv.size + MG_IO_SIZE)) {
c->is_closing = 1;
} }
buf = c->recv.buf + c->recv.len; buf = c->recv.buf + c->recv.len;
len = (int) (c->recv.size - c->recv.len); len = (int) (c->recv.size - c->recv.len);
@@ -301,7 +303,7 @@ static void setsockopts(struct mg_connection *c) {
int idle = 60; int idle = 60;
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
#endif #endif
#ifndef _WIN32 #if !defined(_WIN32) && !defined(__QNX__)
{ {
int cnt = 3, intvl = 20; int cnt = 3, intvl = 20;
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)); setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
+5 -4
View File
@@ -1,5 +1,6 @@
#include "log.h"
#include "ssi.h" #include "ssi.h"
#include "log.h"
#include "util.h"
#ifndef MG_MAX_SSI_DEPTH #ifndef MG_MAX_SSI_DEPTH
#define MG_MAX_SSI_DEPTH 5 #define MG_MAX_SSI_DEPTH 5
@@ -8,7 +9,7 @@
#if MG_ENABLE_SSI #if MG_ENABLE_SSI
static char *mg_ssi(const char *path, const char *root, int depth) { static char *mg_ssi(const char *path, const char *root, int depth) {
struct mg_iobuf b = {NULL, 0, 0}; struct mg_iobuf b = {NULL, 0, 0};
FILE *fp = fopen(path, "rb"); FILE *fp = mg_fopen(path, "rb");
if (fp != NULL) { if (fp != NULL) {
char buf[BUFSIZ], arg[sizeof(buf)]; char buf[BUFSIZ], arg[sizeof(buf)];
int ch, intag = 0; int ch, intag = 0;
@@ -17,7 +18,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
buf[len++] = ch & 0xff; buf[len++] = ch & 0xff;
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) { if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
char tmp[PATH_MAX], *p = (char *) path + strlen(path), *data; char tmp[MG_PATH_MAX], *p = (char *) path + strlen(path), *data;
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--; while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg); snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
if (depth < MG_MAX_SSI_DEPTH && if (depth < MG_MAX_SSI_DEPTH &&
@@ -28,7 +29,7 @@ static char *mg_ssi(const char *path, const char *root, int depth) {
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg)); LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
} }
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) { } else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
char tmp[PATH_MAX], *data; char tmp[MG_PATH_MAX], *data;
snprintf(tmp, sizeof(tmp), "%s%s", root, arg); snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
if (depth < MG_MAX_SSI_DEPTH && if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) { (data = mg_ssi(tmp, root, depth + 1)) != NULL) {
+26 -16
View File
@@ -8,7 +8,6 @@
#include <mbedtls/debug.h> #include <mbedtls/debug.h>
#include <mbedtls/ssl.h> #include <mbedtls/ssl.h>
// Different versions have those in different files, so declare here
#ifndef EXTERN_C #ifndef EXTERN_C
#ifdef __cplusplus #ifdef __cplusplus
#define EXTERN_C extern "C" #define EXTERN_C extern "C"
@@ -17,6 +16,7 @@
#endif #endif
#endif #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_recv(void *, unsigned char *, size_t);
EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t); EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t);
@@ -64,12 +64,14 @@ static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) { int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls)); struct mg_tls *tls = (struct mg_tls *) calloc(1, sizeof(*tls));
int rc = 0; int rc = 0;
const char *ca = opts->ca == NULL ? "-"
: opts->ca[0] == '-' ? "(emb)"
: opts->ca;
if (tls == NULL) { if (tls == NULL) {
mg_error(c, "TLS OOM"); mg_error(c, "TLS OOM");
goto fail; goto fail;
} }
LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, ca,
opts->ca == NULL ? "null" : opts->ca,
opts->cert == NULL ? "null" : opts->cert, opts->cert == NULL ? "null" : opts->cert,
opts->certkey == NULL ? "null" : opts->certkey)); opts->certkey == NULL ? "null" : opts->certkey));
mbedtls_ssl_init(&tls->ssl); mbedtls_ssl_init(&tls->ssl);
@@ -94,17 +96,26 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
tls->cafile = strdup(opts->ca); tls->cafile = strdup(opts->ca);
rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL); rc = mbedtls_ssl_conf_ca_chain_file(&tls->conf, tls->cafile, NULL);
if (rc != 0) { if (rc != 0) {
mg_error(c, "parse on-disk chain(%s) err %#x", opts->ca, -rc); mg_error(c, "parse on-disk chain(%s) err %#x", ca, -rc);
goto fail; goto fail;
} }
#else #else
mbedtls_x509_crt_init(&tls->ca); mbedtls_x509_crt_init(&tls->ca);
if ((rc = mbedtls_x509_crt_parse_file(&tls->ca, opts->ca)) != 0) { rc = opts->ca[0] == '-'
mg_error(c, "parse(%s) err %#x", opts->ca, -rc); ? mbedtls_x509_crt_parse(&tls->ca, (uint8_t *) opts->ca,
strlen(opts->ca) + 1)
: mbedtls_x509_crt_parse_file(&tls->ca, opts->ca);
if (rc != 0) {
mg_error(c, "parse(%s) err %#x", ca, -rc);
goto fail; goto fail;
} }
mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL); mbedtls_ssl_conf_ca_chain(&tls->conf, &tls->ca, NULL);
#endif #endif
if (opts->srvname.len > 0) {
char buf[opts->srvname.len + 1];
sprintf(buf, "%.*s", (int) opts->srvname.len, opts->srvname.ptr);
mbedtls_ssl_set_hostname(&tls->ssl, buf);
}
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
} }
if (opts->cert != NULL && opts->cert[0] != '\0') { if (opts->cert != NULL && opts->cert[0] != '\0') {
@@ -172,13 +183,8 @@ int mg_tls_free(struct mg_connection *c) {
} }
#elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL #elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL
#include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#if defined(_MSC_VER) && _MSC_VER < 1700
typedef long ssize_t;
#endif
extern void ERR_clear_error(void); // Defined in openssl/err.h, but
extern void ERR_print_errors_fp(FILE *fp); // declare here for krypton
struct mg_tls { struct mg_tls {
SSL_CTX *ctx; SSL_CTX *ctx;
@@ -238,9 +244,9 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE); SSL_set_options(tls->ssl, SSL_OP_CIPHER_SERVER_PREFERENCE);
#endif #endif
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
0);
if (opts->ca != NULL && opts->ca[0] != '\0') { if (opts->ca != NULL && opts->ca[0] != '\0') {
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) { if ((rc = SSL_CTX_load_verify_locations(tls->ctx, opts->ca, NULL)) != 1) {
mg_error(c, "parse(%s): err %d", opts->ca, mg_tls_err(tls, rc)); mg_error(c, "parse(%s): err %d", opts->ca, mg_tls_err(tls, rc));
goto fail; goto fail;
@@ -269,7 +275,11 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
} }
} }
if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers); if (opts->ciphers != NULL) SSL_set_cipher_list(tls->ssl, opts->ciphers);
if (opts->srvname != NULL) SSL_set_tlsext_host_name(tls->ssl, opts->srvname); if (opts->srvname.len > 0) {
char buf[opts->srvname.len + 1];
sprintf(buf, "%.*s", (int) opts->srvname.len, opts->srvname.ptr);
SSL_set_tlsext_host_name(tls->ssl, buf);
}
c->tls = tls; c->tls = tls;
c->is_tls = 1; c->is_tls = 1;
c->is_tls_hs = 1; c->is_tls_hs = 1;
@@ -288,7 +298,7 @@ fail:
int mg_tls_handshake(struct mg_connection *c) { int mg_tls_handshake(struct mg_connection *c) {
struct mg_tls *tls = (struct mg_tls *) c->tls; struct mg_tls *tls = (struct mg_tls *) c->tls;
int rc; int rc;
SSL_set_fd(tls->ssl, (ssize_t) c->fd); SSL_set_fd(tls->ssl, (long) c->fd);
rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl); rc = c->is_client ? SSL_connect(tls->ssl) : SSL_accept(tls->ssl);
if (rc == 1) { if (rc == 1) {
LOG(LL_DEBUG, ("%lu success", c->id)); LOG(LL_DEBUG, ("%lu success", c->id));
+5 -5
View File
@@ -2,11 +2,11 @@
#include "net.h" #include "net.h"
struct mg_tls_opts { struct mg_tls_opts {
const char *ca; // CA certificate file. For both listeners and clients const char *ca; // CA certificate file. For both listeners and clients
const char *cert; // Certificate const char *cert; // Certificate
const char *certkey; // Certificate key const char *certkey; // Certificate key
const char *ciphers; // Cipher list const char *ciphers; // Cipher list
const char *srvname; // If not NULL, enables server name verification struct mg_str srvname; // If not empty, enables server name verification
}; };
int mg_tls_init(struct mg_connection *, struct mg_tls_opts *); int mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
+76 -21
View File
@@ -1,20 +1,43 @@
#include "config.h"
#include "util.h" #include "util.h"
size_t mg_file_size(const char *path) { #if MG_ENABLE_FS
int mg_stat(const char *path, mg_stat_t *st) {
#ifdef _WIN32
wchar_t tmp[MG_PATH_MAX];
MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0]));
return _wstati64(tmp, st);
#else
return stat(path, st);
#endif
}
FILE *mg_fopen(const char *path, const char *mode) {
#ifdef _WIN32
wchar_t b1[MG_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]));
return _wfopen(b1, b2);
#else
return fopen(path, mode);
#endif
}
int64_t mg_file_size(const char *path) {
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
struct FF_STAT st; struct FF_STAT st;
return ff_stat(path, &st) == 0 ? st.st_size : 0; return ff_stat(path, &st) == 0 ? st.st_size : 0;
#else #else
struct stat st; mg_stat_t st;
return stat(path, &st) == 0 ? st.st_size : 0; return mg_stat(path, &st) == 0 ? st.st_size : 0;
#endif #endif
} }
char *mg_file_read(const char *path) { char *mg_file_read(const char *path) {
FILE *fp; FILE *fp;
char *data = NULL; char *data = NULL;
size_t size = mg_file_size(path); size_t size = (size_t) mg_file_size(path);
if ((fp = fopen(path, "rb")) != NULL) { if ((fp = mg_fopen(path, "rb")) != NULL) {
data = (char *) malloc(size + 1); data = (char *) malloc(size + 1);
if (data != NULL) { if (data != NULL) {
if (fread(data, 1, size, fp) != size) { if (fread(data, 1, size, fp) != size) {
@@ -29,32 +52,54 @@ char *mg_file_read(const char *path) {
return data; return data;
} }
int mg_file_write(const char *path, const char *fmt, ...) { bool mg_file_write(const char *path, const void *buf, size_t len) {
char tmp[100]; bool result = false;
int written = 0;
FILE *fp; FILE *fp;
char tmp[MG_PATH_MAX];
snprintf(tmp, sizeof(tmp), "%s.%d", path, rand()); snprintf(tmp, sizeof(tmp), "%s.%d", path, rand());
fp = fopen(tmp, "wb"); fp = mg_fopen(tmp, "wb");
if (fp != NULL) { if (fp != NULL) {
va_list ap; result = fwrite(buf, 1, len, fp) == len;
va_start(ap, fmt);
written = vfprintf(fp, fmt, ap);
va_end(ap);
fclose(fp); fclose(fp);
remove(path); if (result) {
rename(tmp, path); remove(path);
rename(tmp, path);
} else {
remove(tmp);
}
} }
return written; return result;
} }
bool mg_file_printf(const char *path, const char *fmt, ...) {
char tmp[256], *buf = tmp;
bool result;
size_t 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);
if (buf != tmp) free(buf);
return result;
}
#endif
void mg_random(void *buf, size_t len) { void mg_random(void *buf, size_t len) {
FILE *fp = fopen("/dev/urandom", "rb"); bool done = false;
size_t i, n = 0; #if MG_ENABLE_FS
if (fp != NULL) n = fread(buf, 1, len, fp); FILE *fp = mg_fopen("/dev/urandom", "rb");
if (fp == NULL || n <= 0) { if (fp != NULL) {
fread(buf, 1, len, fp);
fclose(fp);
done = true;
}
#endif
if (!done) {
// Fallback to a pseudo random gen
size_t i;
for (i = 0; i < len; i++) ((unsigned char *) buf)[i] = rand() % 0xff; for (i = 0; i < len; i++) ((unsigned char *) buf)[i] = rand() % 0xff;
} }
if (fp != NULL) fclose(fp);
} }
bool mg_globmatch(const char *s1, int n1, const char *s2, int n2) { bool mg_globmatch(const char *s1, int n1, const char *s2, int n2) {
@@ -228,6 +273,16 @@ int64_t mg_to64(struct mg_str str) {
return result * neg; return result * neg;
} }
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
int i;
crc = ~crc;
while (len--) {
crc ^= *(unsigned char *) buf++;
for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
}
return ~crc;
}
double mg_time(void) { double mg_time(void) {
#if MG_ARCH == MG_ARCH_WIN32 #if MG_ARCH == MG_ARCH_WIN32
SYSTEMTIME sysnow; SYSTEMTIME sysnow;
+15 -3
View File
@@ -4,13 +4,15 @@
#include "str.h" #include "str.h"
char *mg_file_read(const char *path); char *mg_file_read(const char *path);
size_t mg_file_size(const char *path); int64_t mg_file_size(const char *path);
int mg_file_write(const char *path, const char *fmt, ...); 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); void mg_random(void *buf, size_t len);
bool mg_globmatch(const char *pattern, int plen, const char *s, int n); bool mg_globmatch(const char *pattern, int plen, const char *s, int n);
bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v); bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
uint16_t mg_ntohs(uint16_t net); uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net); uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
char *mg_hexdump(const void *buf, size_t len); char *mg_hexdump(const void *buf, size_t len);
char *mg_hex(const void *buf, int len, char *dst); char *mg_hex(const void *buf, int len, char *dst);
void mg_unhex(const char *buf, int len, unsigned char *to); void mg_unhex(const char *buf, int len, unsigned char *to);
@@ -22,6 +24,16 @@ double mg_time(void);
unsigned long mg_millis(void); unsigned long mg_millis(void);
void mg_usleep(unsigned long usecs); void mg_usleep(unsigned long usecs);
#if MG_ENABLE_FS
#ifdef _WIN32
typedef struct _stati64 mg_stat_t;
#else
typedef struct stat mg_stat_t;
#endif
int mg_stat(const char *path, mg_stat_t *);
FILE *mg_fopen(const char *fp, const char *mode);
#endif
#define mg_htons(x) mg_ntohs(x) #define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x) #define mg_htonl(x) mg_ntohl(x)
@@ -32,7 +44,7 @@ void mg_usleep(unsigned long usecs);
#if !defined(WEAK) #if !defined(WEAK)
#if (defined(__GNUC__) || defined(__clang__) || \ #if (defined(__GNUC__) || defined(__clang__) || \
defined(__TI_COMPILER_VERSION__)) && \ defined(__TI_COMPILER_VERSION__)) && \
!defined(_WIN32) !defined(_WIN32) && !defined(__CYGWIN__)
#define WEAK __attribute__((weak)) #define WEAK __attribute__((weak))
#else #else
#define WEAK #define WEAK
+1 -1
View File
@@ -1,2 +1,2 @@
#pragma once #pragma once
#define MG_VERSION "7.1" #define MG_VERSION "7.2"
+165 -9
View File
@@ -413,12 +413,15 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url,
ASSERT(c != NULL); ASSERT(c != NULL);
if (mg_url_is_ssl(url)) { if (mg_url_is_ssl(url)) {
struct mg_tls_opts opts; struct mg_tls_opts opts;
struct mg_str host = mg_url_host(url);
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.ca = "./test/data/ca.pem"; opts.ca = "./test/data/ca.pem";
if (strstr(url, "127.0.0.1") != NULL) { if (strstr(url, "127.0.0.1") != NULL) {
// Local connection, use self-signed certificates // Local connection, use self-signed certificates
opts.ca = "./test/data/ss_ca.pem"; opts.ca = "./test/data/ss_ca.pem";
opts.cert = "./test/data/ss_client.pem"; opts.cert = "./test/data/ss_client.pem";
} else {
opts.srvname = host;
} }
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
@@ -451,7 +454,7 @@ static void test_http_server(void) {
struct mg_mgr mgr; struct mg_mgr mgr;
const char *url = "http://127.0.0.1:12346"; const char *url = "http://127.0.0.1:12346";
char buf[FETCH_BUF_SIZE]; char buf[FETCH_BUF_SIZE];
struct stat st; mg_stat_t st;
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_http_listen(&mgr, url, eh1, NULL); mg_http_listen(&mgr, url, eh1, NULL);
@@ -463,9 +466,9 @@ static void test_http_server(void) {
ASSERT(cmpbody(buf, "hello\n") == 0); ASSERT(cmpbody(buf, "hello\n") == 0);
{ {
extern char *mg_http_etag(char *, size_t, struct stat *); extern char *mg_http_etag(char *, size_t, mg_stat_t *);
char etag[100]; char etag[100];
ASSERT(stat("./test/data/a.txt", &st) == 0); ASSERT(mg_stat("./test/data/a.txt", &st) == 0);
ASSERT(mg_http_etag(etag, sizeof(etag), &st) == etag); ASSERT(mg_http_etag(etag, sizeof(etag), &st) == etag);
ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\nIf-None-Match: %s\n\n", ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\nIf-None-Match: %s\n\n",
etag) == 304); etag) == 304);
@@ -493,7 +496,7 @@ static void test_http_server(void) {
ASSERT(fetch(&mgr, buf, url, "GET /badroot HTTP/1.0\r\n\n") == 400); ASSERT(fetch(&mgr, buf, url, "GET /badroot HTTP/1.0\r\n\n") == 400);
#if MG_ARCH == MG_ARCH_WIN32 #if MG_ARCH == MG_ARCH_WIN32
ASSERT(cmpbody(buf, "Bad web root [Z:\\BAAADDD!]\n") == 0); ASSERT(cmpbody(buf, "Invalid web root [Z:\\BAAADDD!]\n") == 0);
#else #else
// LOG(LL_INFO, ("--> [%s]", buf)); // LOG(LL_INFO, ("--> [%s]", buf));
ASSERT(cmpbody(buf, "Bad web root [/BAAADDD!]\n") == 0); ASSERT(cmpbody(buf, "Bad web root [/BAAADDD!]\n") == 0);
@@ -571,8 +574,12 @@ static void test_http_server(void) {
"POST /upload?name=uploaded.txt HTTP/1.0\r\n" "POST /upload?name=uploaded.txt HTTP/1.0\r\n"
"Content-Length: 5\r\n" "Content-Length: 5\r\n"
"\r\nhello") == 200); "\r\nhello") == 200);
ASSERT(fetch(&mgr, buf, url,
"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); ASSERT((p = mg_file_read("uploaded.txt")) != NULL);
ASSERT(strcmp(p, "hello") == 0); ASSERT(strcmp(p, "hello\nworld") == 0);
free(p); free(p);
remove("uploaded.txt"); remove("uploaded.txt");
} }
@@ -841,6 +848,16 @@ static void test_http_parse(void) {
ASSERT(mg_strcmp(hm.uri, mg_str("/foo")) == 0); ASSERT(mg_strcmp(hm.uri, mg_str("/foo")) == 0);
ASSERT(mg_strcmp(hm.query, mg_str("bar=baz")) == 0); ASSERT(mg_strcmp(hm.query, mg_str("bar=baz")) == 0);
} }
{
struct mg_http_message hm;
const char *req = "a b c\n\n";
ASSERT(mg_http_parse(req, strlen(req), &hm) == (int) strlen(req));
req = "a b\nc\n\n";
ASSERT(mg_http_parse(req, strlen(req), &hm) < 0);
req = "a\nb\nc\n\n";
ASSERT(mg_http_parse(req, strlen(req), &hm) < 0);
}
} }
static void test_http_range(void) { static void test_http_range(void) {
@@ -853,7 +870,8 @@ static void test_http_range(void) {
mg_http_listen(&mgr, url, eh1, NULL); mg_http_listen(&mgr, url, eh1, NULL);
ASSERT(fetch(&mgr, buf, url, "GET /range.txt HTTP/1.0\n\n") == 200); ASSERT(fetch(&mgr, buf, url, "GET /range.txt HTTP/1.0\n\n") == 200);
mg_http_parse(buf, strlen(buf), &hm); ASSERT(mg_http_parse(buf, strlen(buf), &hm) > 0);
LOG(LL_INFO, ("----%d\n[%s]", (int) hm.body.len, buf));
ASSERT(hm.body.len == 312); ASSERT(hm.body.len == 312);
// ASSERT(strlen(buf) == 312); // ASSERT(strlen(buf) == 312);
@@ -1045,12 +1063,16 @@ static void test_dns(void) {
} }
static void test_util(void) { static void test_util(void) {
char buf[100], *s = mg_hexdump("abc", 3); char buf[100], *s = mg_hexdump("abc", 3), *p;
struct mg_addr a; struct mg_addr a;
ASSERT(s != NULL); ASSERT(s != NULL);
free(s); free(s);
memset(&a, 0, sizeof(a)); memset(&a, 0, sizeof(a));
ASSERT(mg_file_write("data.txt", "%s", "hi") == 2); ASSERT(mg_file_printf("data.txt", "%s", "hi") == true);
ASSERT((p = mg_file_read("data.txt")) != NULL);
ASSERT(strcmp(p, "hi") == 0);
free(p);
remove("data.txt");
ASSERT(mg_aton(mg_str("0"), &a) == false); ASSERT(mg_aton(mg_str("0"), &a) == false);
ASSERT(mg_aton(mg_str("0.0.0."), &a) == false); ASSERT(mg_aton(mg_str("0.0.0."), &a) == false);
ASSERT(mg_aton(mg_str("0.0.0.256"), &a) == false); ASSERT(mg_aton(mg_str("0.0.0.256"), &a) == false);
@@ -1102,10 +1124,145 @@ static void test_util(void) {
ASSERT(strcmp(mg_hex("abc", 3, buf), "616263") == 0); ASSERT(strcmp(mg_hex("abc", 3, buf), "616263") == 0);
ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("a=%", 3, buf, sizeof(buf), 0) < 0);
ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0);
{
char buf[100];
int n;
ASSERT((n = mg_url_encode("", 0, buf, sizeof(buf))) == 0);
ASSERT((n = mg_url_encode("a", 1, buf, 0)) == 0);
ASSERT((n = mg_url_encode("a", 1, buf, sizeof(buf))) == 1);
ASSERT(strncmp(buf, "a", n) == 0);
ASSERT((n = mg_url_encode("._-~", 4, buf, sizeof(buf))) == 4);
ASSERT(strncmp(buf, "._-~", n) == 0);
ASSERT((n = mg_url_encode("a@%>", 4, buf, sizeof(buf))) == 10);
ASSERT(strncmp(buf, "a%40%25%3e", n) == 0);
ASSERT((n = mg_url_encode("a@b.c", 5, buf, sizeof(buf))) == 7);
ASSERT(strncmp(buf, "a%40b.c", n) == 0);
}
{
char buf[100], *s = buf;
mg_asprintf(&s, sizeof(buf), "%s", "%3d", 123);
ASSERT(s == buf);
ASSERT(strcmp(buf, "%3d") == 0);
mg_asprintf(&s, sizeof(buf), "%.*s", 7, "a%40b.c");
ASSERT(s == buf);
ASSERT(strcmp(buf, "a%40b.c") == 0);
}
}
static void test_crc32(void) {
// echo -n aaa | cksum -o3
ASSERT(mg_crc32(0, 0, 0) == 0);
ASSERT(mg_crc32(0, "a", 1) == 3904355907);
ASSERT(mg_crc32(0, "abc", 3) == 891568578);
ASSERT(mg_crc32(mg_crc32(0, "ab", 2), "c", 1) == 891568578);
}
// Streaming server event handler.
// Send one chunk immediately, then drain, then send two chunks in one go
static void eh2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
mg_http_printf_chunk(c, "chunk 0");
c->label[0] = 1;
} else if (ev == MG_EV_POLL) {
if (c->label[0] > 0 && c->send.len == 0) c->label[0]++;
if (c->label[0] > 10 && c->label[0] != 'x') {
mg_http_printf_chunk(c, "chunk 1");
mg_http_printf_chunk(c, "chunk 2");
mg_http_printf_chunk(c, "");
c->label[0] = 'x';
}
}
(void) ev_data;
(void) fn_data;
}
// Non-streaming client event handler. Make sure we've got full body
static void eh3(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET / HTTP/1.0\n\n");
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// LOG(LL_INFO, ("----> [%.*s]", (int) hm->body.len, hm->body.ptr));
c->is_closing = 1;
*(uint32_t *) fn_data = mg_crc32(0, hm->body.ptr, hm->body.len);
}
}
// Streaming client event handler. Make sure we've got all chunks
static void eh4(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET / HTTP/1.0\n\n");
} else if (ev == MG_EV_HTTP_CHUNK) {
uint32_t *crc = (uint32_t *) c->label;
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
*crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len);
} else if (ev == MG_EV_HTTP_MSG) {
uint32_t *crc = (uint32_t *) c->label;
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// LOG(LL_INFO, ("MSG [%.*s]", (int) hm->body.len, hm->body.ptr));
c->is_closing = 1;
*(int *) fn_data = mg_crc32(*crc, hm->body.ptr, hm->body.len);
}
(void) ev_data;
}
// Streaming client event handler. Delete chunks as they arrive
static void eh5(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET / HTTP/1.0\n\n");
} else if (ev == MG_EV_HTTP_CHUNK) {
uint32_t *crc = (uint32_t *) c->label;
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
*crc = mg_crc32(*crc, hm->chunk.ptr, hm->chunk.len);
// LOG(LL_INFO, ("CHUNK [%.*s]", (int) hm->chunk.len, hm->chunk.ptr));
mg_http_delete_chunk(c, hm);
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint32_t *crc = (uint32_t *) c->label;
c->is_closing = 1;
*(int *) fn_data = mg_crc32(*crc, hm->body.ptr, hm->body.len);
// LOG(LL_INFO, ("MSG [%.*s]", (int) hm->body.len, hm->body.ptr));
}
(void) ev_data;
}
static void test_http_chunked(void) {
struct mg_mgr mgr;
const char *data, *url = "http://127.0.0.1:12344";
uint32_t i, done = 0;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, url, eh2, NULL);
mg_http_connect(&mgr, url, eh3, &done);
for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1);
ASSERT(i < 50);
data = "chunk 0chunk 1chunk 2";
ASSERT(done == mg_crc32(0, data, strlen(data)));
done = 0;
mg_http_connect(&mgr, url, eh4, &done);
for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1);
data = "chunk 0chunk 0chunk 1chunk 2chunk 0chunk 1chunk 2";
ASSERT(done == mg_crc32(0, data, strlen(data)));
done = 0;
mg_http_connect(&mgr, url, eh5, &done);
for (i = 0; i < 50 && done == 0; i++) mg_mgr_poll(&mgr, 1);
data = "chunk 0chunk 1chunk 2";
ASSERT(done == mg_crc32(0, data, strlen(data)));
mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL);
} }
int main(void) { int main(void) {
mg_log_set("3"); mg_log_set("3");
test_crc32();
test_http_chunked();
test_http_parse();
test_util(); test_util();
test_sntp(); test_sntp();
test_dns(); test_dns();
@@ -1120,7 +1277,6 @@ int main(void) {
test_http_get_var(); test_http_get_var();
test_tls(); test_tls();
test_ws(); test_ws();
test_http_parse();
test_http_server(); test_http_server();
test_http_client(); test_http_client();
test_http_no_content_length(); test_http_no_content_length();