Compare commits

..
165 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
cpq 8e52075636 Handle calloc failures 2021-01-26 12:16:58 +00:00
cpq 7200f079bf Remove dead code 2021-01-25 14:43:56 +00:00
cpq d155db81de Add socks5 server example 2021-01-25 14:43:08 +00:00
Sergey Lyubka 25e3bbf039 Merge pull request #1200 from Davonter/master
Fix README.md mg_mgr_free
2021-01-25 10:37:35 +00:00
cpq eecf3d854b Show accepted IPv6 peer corrently 2021-01-25 10:35:45 +00:00
cpq 7c2499fc06 -D -> -v 2021-01-25 10:35:14 +00:00
cpq 1aa605c620 desktop-server -> http-server 2021-01-25 10:09:48 +00:00
cpq 66287ad6c1 TLS nits 2 2021-01-25 10:08:15 +00:00
cpq 9a19485454 TLS nits 2021-01-24 13:57:40 +00:00
cpq 7d67d578fd TLS krypton compat 2021-01-23 17:47:51 +00:00
wangtun 960d3c7880 Fix README.md mg_mgr_free 2021-01-23 15:49:55 +08:00
cpq 9eabf3fa81 URL decoding URI 2021-01-21 17:14:08 +00:00
cpq 89beadd2ce Squash warning in unit test 2021-01-21 10:24:49 +00:00
cpq 6ecb6c1500 Fix DNS timeout, add unit test 2021-01-21 10:00:18 +00:00
cpq 51bc5f70c5 Disable native socketpair by default, fix multithreaded win32 example 2021-01-21 09:12:49 +00:00
cpq be62ac9c14 Add more docs 2021-01-20 11:28:47 +00:00
cpq 161c34384d Enable SO_EXCLUSIVEADDRUSE on Win32. Better listdir response. 2021-01-20 11:10:02 +00:00
cpq 89784ab90a Restore to a simple version 2021-01-19 14:21:50 +00:00
cpq d83e547fa9 Make mg_url_port() return 80 for ws:// 2021-01-18 18:39:22 +00:00
Sergey Lyubka 30d86928d5 Merge pull request #1191 from RiccardoDalFiume/master
FIX README mg_http_serve*
2021-01-17 11:18:32 +00:00
cpq 12c32d09fb Pass mg_http_message to MG_EV_WS_OPEN 2021-01-17 11:16:19 +00:00
cpq ae6fd6aa35 Add extra headers param to mg_ws_upgrade() 2021-01-16 22:48:43 +00:00
cpq 72d3a49352 Restore SO_EXCLUSIVEADDRUSE 2021-01-13 03:47:11 +00:00
cpq 87b62390b7 No static for linux 2021-01-13 03:10:42 +00:00
cpq 6c93446a1f Better reuseaddr handling on Win32 2021-01-13 03:08:11 +00:00
cpq 83c106a857 UI for Win32 2021-01-13 03:07:51 +00:00
cpq facb2846fd Bump version 2021-01-13 03:07:38 +00:00
cpq 596c70d3dc Add config file processing 2021-01-12 15:18:19 +00:00
Riccardo Dal Fiume 7beff0201d FIX README mg_http_serve* 2021-01-12 11:28:41 +01:00
cpq a4c9d1ecb7 Update mg_http_serve_file() doc 2021-01-07 16:21:01 +00:00
cpq b7a79a556a Add extra headers param to mg_http_serve_file 2021-01-07 12:48:20 +00:00
cpq 03ab9e4e0d Update axios 2021-01-06 17:11:53 +00:00
cpq 7e855f5c8f Add oss-fuzz badge 2021-01-05 18:10:01 +00:00
Sergey Lyubka 9e510d5831 Merge pull request #1185 from hakabahitoyo/master
Suppress error: cast from ‘void*’ to ‘int’ loses precision [-fpermissive] in MG_ENABLE_OPENSSL build.
2021-01-05 11:46:49 +00:00
Hakaba Hitoyo 1689a0d56b Update mongoose.c. 2021-01-05 12:35:28 +09:00
cpq 9ff774e7df Trigger test on pull requests 2021-01-04 12:02:55 +00:00
Hakaba Hitoyo c78ad0b574 Supress error: cast from ‘void*’ to ‘int’ loses precision [-fpermissive] 2021-01-04 16:25:06 +09:00
cpq 117aeaa15d Fix fd leak for cached static files 2021-01-03 09:39:46 +00:00
cpq e135743efa Fix example build 2021-01-03 08:55:00 +00:00
cpq 2c1324dc0a Remove -r option 2021-01-03 08:43:56 +00:00
cpq c7e09bd7b4 Add SSI include file=, include virtual= 2021-01-02 17:57:51 +00:00
cpq 86824795a0 Pass opts structure to mg_http_serve_dir() 2021-01-02 12:30:09 +00:00
cpq 1b0636c2d2 More checks in mg_ws_send 2021-01-02 12:20:08 +00:00
cpq 2f9d2bbdf6 Update readme 2020-12-29 18:15:06 +00:00
cpq 21a1b2c15c Update readme 2020-12-29 17:59:05 +00:00
cpq bdef3d6afd Fix win98 test 2020-12-28 06:32:55 +00:00
cpq a882aab30e Add captive dns server example 2020-12-28 05:25:29 +00:00
cpq 788b577d65 Add README 2020-12-27 01:32:58 +00:00
cpq 71a22adc06 Add ESP8266 example 2020-12-27 01:29:42 +00:00
cpq 7d1ef2ffe8 Add mingw tests to actions 2020-12-25 19:28:05 +00:00
cpq 55f5f74658 Add mingw gcc and g++ tests 2020-12-25 19:24:51 +00:00
cpq e1fd82bc46 Remove unused code 2020-12-25 15:04:43 +00:00
cpq 49b8480bfb Add http-reverse-proxy example 2020-12-25 04:32:56 +00:00
cpq b2ab484883 Fix #954 2020-12-24 19:10:54 +00:00
cpq 4c3b97be7d Squash -DMG_ENABLE_FS=0 build warning 2020-12-24 18:48:28 +00:00
cpq 60e1b64455 Fix https://github.com/cesanta/mongoose/issues/1030 2020-12-24 17:01:49 +00:00
cpq 6bcd02bb1c Enhance HTTP client example 2020-12-24 16:52:58 +00:00
cpq 66f302a613 Make DNS debug log less verbose 2020-12-24 16:52:33 +00:00
cpq 1716af6c72 Add HTTP client code 2020-12-24 16:52:12 +00:00
cpq 749326c83a Add more mg_http_parse unit tests 2020-12-24 14:33:23 +00:00
cpq 9657577c00 Fix typo in fuzz test 2020-12-24 09:09:25 +00:00
cpq 9f5216d259 Add more fuzz tests 2020-12-24 09:07:55 +00:00
cpq c8826e8f9c Refactor mg_ntoa and mg_straddr 2020-12-24 08:05:54 +00:00
cpq 25bc13ef1e Squash some warnings under mingw crosscompiler 2020-12-24 07:26:50 +00:00
cpq 4d123a15f9 Drop deprecated register keyword 2020-12-23 21:20:48 +00:00
cpq 2cf1b09767 Workaround for WIN32 C++ snprintf/vsnprintf/strdup calls 2020-12-23 21:05:46 +00:00
cpq abfd81cb9d Fix WS frame length encoding for len 0xffff 2020-12-23 16:18:49 +00:00
cpq 3895678bee Use MG_EV_WS_CTL for WS control messages 2020-12-23 11:58:08 +00:00
cpq e0a1feafe0 Fix localhost resolution 2020-12-23 10:15:09 +00:00
cpq c75eb24eb8 Add proxy client example 2020-12-22 21:01:14 +00:00
cpq c3aae185df Add ipv6 server test 2020-12-22 10:16:31 +00:00
cpq 2e87783246 Add IPv6 client test 2020-12-22 09:44:59 +00:00
cpq fbab767b9f Fix mg_prefix job 2020-12-21 12:39:03 +00:00
cpq 7bc38b5eaa Add examples build, fix prefix build 2020-12-21 12:33:27 +00:00
cpq b93456f512 Do not create socket until resolved 2020-12-21 12:26:44 +00:00
cpq ddb700eee5 Fix openssl build 2020-12-20 21:50:46 +00:00
cpq 1052ee0766 Fix openssl build, and add openssl test 2020-12-20 21:45:58 +00:00
cpq 29534218d7 IPv6 related refactoring 2020-12-20 16:55:33 +00:00
cpq d7abd53062 Always send MG_EV_CONNECT on TCP connect 2020-12-18 15:13:19 +00:00
cpq aede72fa37 Refactor HTTP client 2020-12-18 14:29:30 +00:00
cpq b0a5165b44 Uload coverage 2020-12-18 11:47:56 +00:00
cpq 4919f792f2 Make build badge a link 2020-12-18 11:32:59 +00:00
cpq d5927f5d50 Remove travis - migrated to Github Actions 2020-12-18 11:31:31 +00:00
cpq e68330e045 Rename workflow 2020-12-18 11:26:17 +00:00
cpq b5d5b3763f Add workflow badge 2020-12-18 11:24:36 +00:00
cpq b6a6687c1e Rename workflow 2020-12-18 11:24:06 +00:00
cpq 3843b670a5 Fix typo 2020-12-18 11:14:21 +00:00
cpq 249efdd378 Add windows test 2020-12-18 11:12:08 +00:00
cpq 18ce1669fc Add mac test 2020-12-18 11:09:03 +00:00
cpq e8d3b8030e Install mbedtls 2020-12-18 10:56:35 +00:00
Sergey Lyubka a752abf318 Create test.yml 2020-12-18 10:41:27 +00:00
cpq d3ebcaf5d2 Remove unneeded defines that break vc2015 build 2020-12-18 09:48:28 +00:00
cpq 0abb8322d9 Disable noisy debug 2020-12-18 09:48:05 +00:00
cpq 99ed58ec4d Better docstrings 2020-12-18 09:08:28 +00:00
cpq 04450ec659 Update http server example to showcase SSL 2020-12-18 09:01:14 +00:00
cpq 82a378e519 Add extra param to http_reply() 2020-12-17 22:47:55 +00:00
cpq 29d31ba919 More boundary checks for mg_dns_parse() 2020-12-17 19:14:17 +00:00
cpq 4519ea568d Add conditional for SSL_use_certificate_chain_file() 2020-12-17 12:12:46 +00:00
cpq 9f88f31f01 Add timer example 2020-12-17 11:44:30 +00:00
cpq 4bdea2b467 Add README to the esp32 example 2020-12-17 11:24:28 +00:00
cpq 2252a74cd7 Note on UDP 2020-12-17 11:19:13 +00:00
cpq d1fa7b88e0 Disable brew update 2020-12-16 14:05:36 +00:00
cpq 9ecf42578c Fix screenshot link 2020-12-16 10:54:47 +00:00
cpq 754fc25924 Add description & screenshots 2020-12-16 10:54:09 +00:00
cpq d9551c75b1 Refactor MQTT parsing 2020-12-16 10:14:00 +00:00
cpq 8d45cf6972 Add mqtt-server example 2020-12-15 20:29:47 +00:00
cpq f176e1ae59 Handle more mime types 2020-12-15 11:58:30 +00:00
cpq fa8f62fb5d Squash warnings under linux 2020-12-14 14:05:01 +00:00
cpq 0c142f20d7 Using linux for docker tests 2020-12-14 13:09:56 +00:00
cpq b641fc6b79 Add esp32 example 2020-12-14 12:58:27 +00:00
cpq 1252a61479 More docs 2020-12-14 09:36:46 +00:00
cpq 5f631c0b96 More docs 2020-12-14 09:35:03 +00:00
cpq 348c579521 Add dns test case 2020-12-14 09:31:23 +00:00
cpq 7ce9cdf0b9 More docs 2020-12-14 09:31:03 +00:00
cpq 09beadb98a Fix c++ fuzzer build 2020-12-13 16:56:30 +00:00
cpq 5b265a76bd Add minor case to fuzzer 2020-12-13 16:33:46 +00:00
cpq c5a4a1c6f2 Remove debug leftover 2020-12-13 00:37:04 +00:00
cpq 88ada54045 More tests 2020-12-12 20:38:09 +00:00
cpq c5b1c08d05 Use MSG_NONBLOCKING for recv/send 2020-12-12 16:48:47 +00:00
cpq a0765ade6b Use -DMG_ENABLE_LINES for fuzzer build 2020-12-12 14:23:28 +00:00
107 changed files with 6287 additions and 1651 deletions

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
name: build
on: [push, pull_request]
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install packages
run: sudo apt-get install libmbedtls-dev
- name: make
run: make test test++ IPV6=0
- name: openssl
run: make test SSL=OPENSSL IPV6=0
- name: examples
run: make ex
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Install packages
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl
- name: make
run: make test++ upload-coverage IPV6=0
- name: openssl
run: make test SSL=OPENSSL IPV6=0
- name: exports
run: make mg_prefix
windows:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: vc98
run: make vc98
- name: vc2017
run: make vc2017
- name: mingw
run: make mingw
- name: mingw++
run: make mingw++
-23
View File
@@ -1,23 +0,0 @@
language: c
sudo: false
after_success: bash <(curl -s https://codecov.io/bash)
install:
- if test "$TRAVIS_OS_NAME" = linux; then sudo apt-get install libmbedtls-dev; else brew install jq mbedtls; fi
os:
- linux
- osx
compiler:
- clang
- gcc
script: make test test++ ex
matrix:
include:
- os: osx
compiler: clang
script: make mg_prefix
- os: linux
compiler: msvc98
script: make vc98 vc2017
sudo: required
services:
- docker
+26 -10
View File
@@ -1,14 +1,16 @@
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 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))
VC98 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc98 VC98 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc98
VC2017 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc2017 VC2017 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/vc2017
MINGW = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) docker.io/mdashnet/mingw
GCC = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) mdashnet/cc2 GCC = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) mdashnet/cc2
VCFLAGS = /nologo /W3 /O2 /I. $(DEFS) $(TFLAGS) VCFLAGS = /nologo /W3 /O2 /I. $(DEFS) $(TFLAGS)
CLANG ?= clang # /usr/local/opt/llvm\@9/bin/clang CLANG ?= clang # /usr/local/opt/llvm\@9/bin/clang
IPV6 ?= 1
ASAN_OPTIONS ?= ASAN_OPTIONS ?=
EXAMPLES := $(wildcard examples/*) EXAMPLES := $(wildcard examples/*)
EXAMPLE_TARGET ?= example EXAMPLE_TARGET ?= example
@@ -22,12 +24,14 @@ CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include
LDFLAGS ?= -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509 LDFLAGS ?= -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
endif endif
ifeq "$(SSL)" "OPENSSL" ifeq "$(SSL)" "OPENSSL"
OPENSSLDIR ?= $(shell "$(brew --cellar openssl)/$(brew info openssl --json | jq -j .[0].installed[0].version)") OPENSSL_DIR ?= $(shell brew --cellar openssl)
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSLDIR)/include OPENSSL_VER ?= $(shell brew info openssl --json | jq -j .[0].installed[0].version)
LDFLAGS ?= -L$(OPENSSLDIR)/lib -lssl -lcrypto OPENSSL ?= $(OPENSSL_DIR)/$(OPENSSL_VER)
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
endif endif
all: mg_prefix test test++ ex vc98 vc2017 linux linux++ infer fuzz all: mg_prefix test test++ ex vc98 vc2017 mingw mingw++ linux linux++ infer fuzz
ex: ex:
@for X in $(EXAMPLES); do $(MAKE) -C $$X $(EXAMPLE_TARGET); done @for X in $(EXAMPLES); do $(MAKE) -C $$X $(EXAMPLE_TARGET); done
@@ -45,11 +49,11 @@ unamalgamated: $(SRCS) $(HDRS) Makefile
$(CLANG) src/*.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test $(CLANG) src/*.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
fuzz: mongoose.c mongoose.h Makefile test/fuzz.c fuzz: mongoose.c mongoose.h Makefile test/fuzz.c
$(CLANG) mongoose.c test/fuzz.c $(CFLAGS) -DMG_ENABLE_LOG=0 -fsanitize=fuzzer,signed-integer-overflow,address $(LDFLAGS) -g -o fuzzer $(CLANG) mongoose.c test/fuzz.c $(CFLAGS) -DMG_ENABLE_LINES -DMG_ENABLE_LOG=0 -fsanitize=fuzzer,signed-integer-overflow,address $(LDFLAGS) -g -o fuzzer
$(DEBUGGER) ./fuzzer $(DEBUGGER) ./fuzzer
# make CLANG=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1 # make CLANG=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
test: CFLAGS += -DMG_ENABLE_IPV6=1 -fsanitize=address#,undefined test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address#,undefined
test: mongoose.c mongoose.h Makefile test/unit_test.c test: mongoose.c mongoose.h Makefile test/unit_test.c
$(CLANG) mongoose.c test/unit_test.c $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test $(CLANG) mongoose.c test/unit_test.c $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test
ASAN_OPTIONS=$(ASAN_OPTIONS) $(DEBUGGER) ./unit_test ASAN_OPTIONS=$(ASAN_OPTIONS) $(DEBUGGER) ./unit_test
@@ -58,6 +62,9 @@ coverage: test
gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /' gcov -l -n *.gcno | sed '/^$$/d' | sed 'N;s/\n/ /'
gcov mongoose.gcno >/dev/null gcov mongoose.gcno >/dev/null
upload-coverage: coverage
curl -s https://codecov.io/bash | /bin/bash
infer: infer:
infer run -- cc test/unit_test.c -c -W -Wall -Werror -Isrc -I. -O2 -DMG_ENABLE_MBEDTLS=1 -DMG_ENABLE_LINES -I/usr/local/Cellar/mbedtls/2.23.0/include -DMG_ENABLE_IPV6=1 -g -o /dev/null infer run -- cc test/unit_test.c -c -W -Wall -Werror -Isrc -I. -O2 -DMG_ENABLE_MBEDTLS=1 -DMG_ENABLE_LINES -I/usr/local/Cellar/mbedtls/2.23.0/include -DMG_ENABLE_IPV6=1 -g -o /dev/null
@@ -66,12 +73,21 @@ vc98: Makefile mongoose.c mongoose.h test/unit_test.c
$(VC98) wine cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe $(VC98) wine cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
$(VC98) wine $@.exe $(VC98) wine $@.exe
vc2017: CFLAGS += -DMG_ENABLE_IPV6=1 #vc2017: VCFLAGS += -DMG_ENABLE_IPV6=1
vc2017: Makefile mongoose.c mongoose.h test/unit_test.c vc2017: Makefile mongoose.c mongoose.h test/unit_test.c
$(VC2017) wine64 cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe $(VC2017) wine64 cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe
$(VC2017) wine64 $@.exe $(VC2017) wine64 $@.exe
linux: CFLAGS += -DMG_ENABLE_IPV6=1 -fsanitize=address,undefined mingw: Makefile mongoose.c mongoose.h test/unit_test.c
$(MINGW) i686-w64-mingw32-gcc mongoose.c test/unit_test.c -W -Wall -Werror -I. $(DEFS) -lwsock32 -o test.exe
$(VC98) wine test.exe
mingw++: Makefile mongoose.c mongoose.h test/unit_test.c
$(MINGW) i686-w64-mingw32-g++ mongoose.c test/unit_test.c -W -Wall -Werror -I. $(DEFS) -lwsock32 -o test.exe
# Note: for some reason, a binary built with mingw g++, fails to run
#linux: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6)
linux: CFLAGS += -fsanitize=address,undefined
linux: Makefile mongoose.c mongoose.h test/unit_test.c linux: Makefile mongoose.c mongoose.h test/unit_test.c
$(GCC) $(CC) mongoose.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -o unit_test_gcc $(GCC) $(CC) mongoose.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -o unit_test_gcc
$(GCC) ./unit_test_gcc $(GCC) ./unit_test_gcc
@@ -83,7 +99,7 @@ mongoose.c: $(SRCS) Makefile
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/private.h src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ (cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/private.h src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
mongoose.h: $(HDRS) Makefile mongoose.h: $(HDRS) Makefile
(cat src/license.h src/version.h ; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,')> $@ (cat src/license.h src/version.h ; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,')> $@
clean: EXAMPLE_TARGET = clean clean: EXAMPLE_TARGET = clean
clean: ex clean: ex
+12 -15
View File
@@ -1,8 +1,9 @@
# Mongoose - Embedded Web Server / Embedded Networking Library # Mongoose - Embedded Web Server / Embedded Networking Library
![License: GPLv2](https://img.shields.io/badge/license-GPL_2-green.svg "License") ![License: GPLv2](https://img.shields.io/badge/license-GPL_2-green.svg "License")
[![Build Status](https://travis-ci.org/cesanta/mongoose.svg?branch=master)](https://travis-ci.org/cesanta/mongoose) [![Build Status]( https://github.com/cesanta/mongoose/workflows/build/badge.svg)](https://github.com/cesanta/mongoose/actions)
[![Code Coverage](https://codecov.io/gh/cesanta/mongoose/branch/master/graph/badge.svg)](https://codecov.io/gh/cesanta/mongoose) [![Code Coverage](https://codecov.io/gh/cesanta/mongoose/branch/master/graph/badge.svg)](https://codecov.io/gh/cesanta/mongoose)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/mongoose.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:mongoose)
Mongoose is a networking library for C/C++. It implements event-driven Mongoose is a networking library for C/C++. It implements event-driven
non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It has been designed non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It has been designed
@@ -20,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
@@ -32,17 +36,11 @@ 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 hardware platforms: TI CC3200, TI MSP432, NRF52, STM32, PIC32, ESP8266, ESP32 and more * Builtin protocols: plain TCP/UDP, HTTP, MQTT, Websocket
* Builtin protocols: * SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way) * Asynchronous DNS resolver
- HTTP client, HTTP server
- WebSocket client, WebSocket server
- MQTT client
- DNS client, async DNS resolver
* Single-threaded, asynchronous, non-blocking core with simple event-based API
* Native support for [LWIP embedded TCP/IP stack](https://en.wikipedia.org/wiki/LwIP)
* 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
* Very easy to integrate: just copy * Very easy to integrate: just copy
@@ -74,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)
+205 -49
View File
@@ -133,18 +133,20 @@ to an event handler:
```c ```c
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 NULL MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_MSG, // Websocket message received struct mg_ws_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_MQTT_MSG, // MQTT message struct mg_mqtt_message * MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received struct timeval * MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events MG_EV_USER, // Starting ID for user events
@@ -157,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:
@@ -221,12 +223,68 @@ 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_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth | |`MG_IO_SIZE` | 512 | Granularity of the send/recv IO buffer growth |
|`MG_MAX_RECV_BUF_SIZE` | (3 * 1024 * 1024) | Maximum recv buffer size | |`MG_MAX_RECV_BUF_SIZE` | (3 * 1024 * 1024) | Maximum recv buffer size |
|`MG_MAX_HTTP_HEADERS` | 40 | Maximum number of HTTP headers | |`MG_MAX_HTTP_HEADERS` | 40 | Maximum number of HTTP headers |
NOTE: `MG_IO_SIZE` controls the maximum UDP message size, see
https://github.com/cesanta/mongoose/issues/907 for details. If application
uses large UDP messages, increase the `MG_IO_SIZE` limit accordingly.
## Custom build
It is possible to use Mongoose on an architecture that is not yet supported
by the current codebase. In order to do so, follow these steps:
1. Create a file called `mongoose_custom.h`, with defines and includes that
are relevant to your platform. Mongoose uses `bool` type, `MG_DIRSEP` define,
and optionally other structures like `DIR *` depending on the functionality
you have enabled - see previous section. Below is an example:
```c
#include <dirent.h> // For DIR *
#include <stdbool.h> // For bool
#include <sys/time.h> // For gettimeofday()
#include <unistd.h> // For usleep()
#define MG_DIRSEP '/'
#define MG_ENABLE_SOCKET 0 // Disable BSD socket API, implement your own
```
2. Add `-DMG_ARCH=MG_ARCH_CUSTOM` to your build flags.
3. This step is optional. If you have disabled BSD socket API, your build is
going to fail due to several undefined symbols. Create `mongoose_custom.c`
and implement the following functions (take a look at `src/sock.c` for the
reference implementation):
```c
struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) {
// implement this!
}
void mg_connect_resolved(struct mg_connection *c) {
// implement this!
}
struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) {
// implement this!
}
void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
// implement this!
}
int mg_send(struct mg_connection *c, const void *buf, size_t len) {
// implement this!
}
```
## Minimal HTTP server ## Minimal HTTP server
This example is a simple static HTTP server that serves current directory: This example is a simple static HTTP server that serves current directory:
@@ -238,7 +296,8 @@ static const char *s_web_root_dir = ".";
static const char *s_listening_address = "http://localhost:8000"; static const char *s_listening_address = "http://localhost:8000";
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) mg_http_serve_dir(c, ev_data, s_web_root_dir); struct mg_http_serve_opts opts = {.root_dir = s_web_root_dir};
if (ev == MG_EV_HTTP_MSG) mg_http_serve_dir(c, ev_data, &opts);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -362,7 +421,7 @@ protocol-specific handler is called before user-specific handler. It parses
incoming data and may invoke protocol-specific events like `MG_EV_HTTP_MSG`. incoming data and may invoke protocol-specific events like `MG_EV_HTTP_MSG`.
### mh\_mgr\_free() ### mg\_mgr\_free()
```c ```c
void mg_mgr_free(struct mg_mgr *mgr); void mg_mgr_free(struct mg_mgr *mgr);
@@ -468,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()
@@ -621,34 +682,52 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
Write a chunk of data in chunked encoding format. Write a chunk of data in chunked encoding format.
### mg\_serve\_dir() ### mg\_http\_serve\_dir()
```c ```c
struct mg_http_serve_opts {
const char *root_dir; // Web root directory, must be non-NULL
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
};
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
const char *path); const struct mg_http_serve_opts *opts);
``` ```
Serve static files using `path` as a root directory. Serve static files according to the given options. Note that in order to
enable SSI, set a `-DMG_ENABLE_SSI=1` build flag.
### mg\_serve\_file() ### mg\_http\_serve\_file()
```c ```c
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm, void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
const char *path, const char *mimetype); const char *path, const char *mimetype,
const char *extra_headers);
``` ```
Serve static file. Serve static file. Note that the `extra_headers` must end with `\r\n`. Here
is an example call:
```c
mg_http_serve_file(c, hm, "a.png", "image/png", "AA: bb\r\nCC: dd\r\n");
```
### mg\_http\_reply() ### mg\_http\_reply()
```c ```c
void mg_http_reply(struct mg_connection *c, int code, const char *fmt, ...); void mg_http_reply(struct mg_connection *c, int status_code, const char *headers,
const char *body_fmt, ...);
``` ```
Send simple HTTP response using `printf()` semantic. Send simple HTTP response using `printf()` semantic. This function formats
`code` is HTTP response code, `fmt` is format for the HTTP body. response body according to a `body_fmt`, and automatically appends a correct
`Content-Length` header. Extra headers could be passed via `headers`
parameter.
- `status_code` - an HTTP response code
- `headers` - extra headers, default NULL. If not NULL, must end with `\r\n`
- `fmt` - a format string for the HTTP body, in a printf semantics
### mg\_http\_header() ### mg\_http\_header()
@@ -712,8 +791,34 @@ int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
const char *dir); const char *dir);
``` ```
Handle file upload. See file upload example. Handle file upload. See [file upload example](https://github.com/cesanta/mongoose/tree/master/examples/file-upload).
This function expects a series of POST requests with file data. POST requests
should have `name` and `offset` query string parameters set:
```text
POST /whatever_uri?name=myfile.txt&offset=1234 HTTP/1.0
Content-Length: 5
hello
```
- `name` - a mandatory query string parameter, specifies a file name. It it
created in the `dir` directory
- `offset` - an optional parameter, default `0`. If it set to `0`, or omitted,
then a file gets truncated before write. Otherwise, the body of
the POST request gets appended to the file
- Server must call `mg_http_upload()` when `/whatever_uri` is hit
So, the expected usage of this API function is this:
- A client splits a file into small enough chunks, to ensure that a chunk
fits into the server's RAM
- Then, each chunk is POST-ed to the server with using URI like
`/some_uri?name=FILENAME&offset=OFFSET`
- Initial OFFSET is `0`, and subsequent offsets are non-zero
- Each chunk gets appended to the file
- When the last chunk is POSTed, upload finishes
- POST data must not be encoded in any way, it it saved as-is
### mg\_http\_bauth() ### mg\_http\_bauth()
@@ -756,10 +861,13 @@ Create client Websocket connection.
### mg\_ws\_upgrade() ### mg\_ws\_upgrade()
```c ```c
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *); void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
const char *fmt, ...);
``` ```
Upgrade given HTTP connection to Websocket. Upgrade given HTTP connection to Websocket. The `fmt` is a printf-like
format string for the extra HTTP headers returned to the client in a
Websocket handshake. Set `fmt` to `NULL` if no extra headers needs to be passed.
### mg\_ws\_send() ### mg\_ws\_send()
@@ -840,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 {
@@ -851,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
@@ -942,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()
@@ -1089,16 +1236,16 @@ Parse 64-bit integer value held by string `s`.
### mg\_aton() ### mg\_aton()
``` ```
bool mg_aton(const char *s, uint32_t *ipaddr); bool mg_aton(struct mg_str str, uint32_t *ipaddr);
``` ```
Parse IP address held by `s` and store it in `ipaddr`. Return true on success. Parse IP address held by `str` and store it in `ipaddr`. Return true on success.
### mg\_ntoa() ### mg\_ntoa()
```c ```c
char *mg_ntoa(uint32_t ipaddr, char *buf, size_t len); char *mg_ntoa(const struct mg_addr *, char *buf, size_t len);
``` ```
Stringify IP address `ipaddr` into a buffer `buf`, `len`. Return `buf`. Stringify IP address `ipaddr` into a buffer `buf`, `len`. Return `buf`.
@@ -1128,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`.
+11
View File
@@ -0,0 +1,11 @@
PROG ?= example
CFLAGS ?= -DMG_IO_SIZE=8192 -DMG_ENABLE_LINES
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
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example captive DNS server (captive portal).
// 1. Run `make`. This builds and starts a server on port 5533
// 2. Run `dig -t A www.google.com -4 @localhost -p 5533`
#include "mongoose.h"
static const char *s_listen_url = "udp://0.0.0.0:5533";
// DNS answer section. We response with IP 1.2.3.4 - you can change it
// in the last 4 bytes of this array
uint8_t answer[] = {
0xc0, 0x0c, // Point to the name in the DNS question
0, 1, // 2 bytes - record type, A
0, 1, // 2 bytes - address class, INET
0, 0, 0, 120, // 4 bytes - TTL
0, 4, // 2 bytes - address length
1, 2, 3, 4 // 4 bytes - IP address
};
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_READ) {
struct mg_dns_rr rr; // Parse first question, offset 12 is header size
size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
if (n > 0) {
char buf[12 + n + sizeof(answer)];
struct mg_dns_header *h = (struct mg_dns_header *) buf;
memset(buf, 0, sizeof(buf)); // Clear the whole datagram
h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid
h->num_questions = mg_htons(1); // We use only the 1st question
h->num_answers = mg_htons(1); // And only one answer
h->flags = mg_htons(0x8400); // Authoritative response
memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question
memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer
mg_send(c, buf, sizeof(buf)); // And send it!
}
mg_iobuf_delete(&c->recv, c->recv.len);
}
(void) fn_data;
(void) ev_data;
}
int main(void) {
struct mg_mgr mgr;
mg_log_set("3"); // Set log level
mg_mgr_init(&mgr); // Initialise event manager
mg_listen(&mgr, s_listen_url, fn, NULL); // Start DNS server
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr);
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
# A complete device dashboard
This example is a demonstration of how Mongoose Library could be integrated
into an embedded device and provide a complete device dashbord with the
following features:
- Multiple logins with different permissions (admin and user)
- Login screen for non-authenticated connections
- A [preact](https://preactjs.com/)-based dashboard with multiple pages
- Internal variables
- A form that changes those variables
- All changes are propagates to all connected clients
- Live log stream
- Live video stream
In essense, this example is a combination of several other examples
pulled together (multiple-logins, dashboard, live-log, video-stream).
# Screenshots
This is a login screen that prompts for user/password
![](screenshots/login.png)
# Main dashboard
A main dashboard page shows internal data, live video stream, and a form
![](screenshots/dashboard.png)
A live log page shows live log stream coming from device
![](screenshots/logs.png)
+4 -3
View File
@@ -119,7 +119,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n" "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n"); "Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
} else if (mg_http_match_uri(hm, "/api/log/static")) { } else if (mg_http_match_uri(hm, "/api/log/static")) {
mg_http_serve_file(c, hm, "log.txt", "text/plain"); mg_http_serve_file(c, hm, "log.txt", "text/plain", "");
} else if (mg_http_match_uri(hm, "/api/log/live")) { } else if (mg_http_match_uri(hm, "/api/log/live")) {
c->label[0] = 'L'; // Mark that connection as live log listener c->label[0] = 'L'; // Mark that connection as live log listener
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
@@ -129,7 +129,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
u->token); u->token);
mg_http_printf_chunk(c, ""); mg_http_printf_chunk(c, "");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
@@ -146,7 +147,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
char *data = mg_file_read(path); // Read next file char *data = mg_file_read(path); // Read next file
struct mg_connection *c; struct mg_connection *c;
for (c = mgr->conns; c != NULL; c = c->next) { for (c = mgr->conns; c != NULL; c = c->next) {
if (c->label[0] != 'S') continue; // Skip non-stream connections if (c->label[0] != 'S') continue; // Skip non-stream connections
if (data == NULL || size == 0) continue; // Skip on file read error if (data == NULL || size == 0) continue; // Skip on file read error
mg_printf(c, mg_printf(c,
"--foo\r\nContent-Type: image/jpeg\r\n" "--foo\r\nContent-Type: image/jpeg\r\n"
Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

+2 -8
View File
@@ -1,9 +1,3 @@
/* axios v0.19.0-beta.1 | (c) 2018 by Matt Zabriskie */ /* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function w(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=w(t[n],e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function b(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=b(t[n],e):"object"==typeof e?t[n]=b({},e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function E(e,t,n){return v(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}var S=n(3),R=n(4),j=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:o,isBuffer:R,isFormData:s,isArrayBufferView:i,isString:a,isNumber:u,isObject:f,isUndefined:c,isDate:p,isFile:d,isBlob:l,isFunction:h,isStream:m,isURLSearchParams:y,isStandardBrowserEnv:x,forEach:v,merge:w,deepMerge:b,extend:E,trim:g}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t){/*! !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use stricLine truncated
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}return s&&(e+=(e.indexOf("?")===-1?"?":"&")+s),e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:Line truncated
//# sourceMappingURL=axios.min.map //# sourceMappingURL=axios.min.map
+2 -1
View File
@@ -66,7 +66,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
c->label[0] = 'W'; // Mark ourselves as a config watcher c->label[0] = 'W'; // Mark ourselves as a config watcher
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
+2 -8
View File
@@ -1,9 +1,3 @@
/* axios v0.19.0-beta.1 | (c) 2018 by Matt Zabriskie */ /* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function w(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=w(t[n],e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function b(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=b(t[n],e):"object"==typeof e?t[n]=b({},e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function E(e,t,n){return v(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}var S=n(3),R=n(4),j=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:o,isBuffer:R,isFormData:s,isArrayBufferView:i,isString:a,isNumber:u,isObject:f,isUndefined:c,isDate:p,isFile:d,isBlob:l,isFunction:h,isStream:m,isURLSearchParams:y,isStandardBrowserEnv:x,forEach:v,merge:w,deepMerge:b,extend:E,trim:g}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t){/*! !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use stricLine truncated
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}return s&&(e+=(e.indexOf("?")===-1?"?":"&")+s),e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:Line truncated
//# sourceMappingURL=axios.min.map //# sourceMappingURL=axios.min.map
-30
View File
@@ -1,30 +0,0 @@
PROG ?= example
ROOT ?= $(realpath $(CURDIR)/../..)
DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_DIRECTORY_LISTING=1
CFLAGS ?= -I../.. -W -Wall -DMG_ENABLE_IPV6=1 $(DEFS) $(EXTRA)
VCFLAGS = /nologo /W3 /O2 /I../.. $(DEFS) $(EXTRA)
VC98 = docker run --rm -e WINEDEBUG=-all -v $(ROOT):$(ROOT) -w $(CURDIR) docker.io/mdashnet/vc98
GCC = docker run --rm -v $(ROOT):$(ROOT) -w $(CURDIR) mdashnet/cc2
all: $(PROG)
$(DEBUGGER) ./$(PROG)
$(PROG):
$(CC) ../../mongoose.c main.c -I../.. $(CFLAGS) $(EXTRA) -o $@
mongoose_mac: $(PROG)
cp $(PROG) $@
mongoose_linux: main.c
$(GCC) $(CC) ../../mongoose.c main.c $(CFLAGS) -o $@
mongoose.exe: main.c
$(VC98) wine cl ../../mongoose.c main.c $(VCFLAGS) ws2_32.lib /Fe$@
zip: mongoose.exe mongoose_linux mongoose_mac
rm -rf mongoose; mkdir mongoose
cp mongoose.exe mongoose_mac mongoose_linux mongoose
zip -r mongoose.zip mongoose
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.*
+6
View File
@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mongoose-esp32-example)
+15
View File
@@ -0,0 +1,15 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../..)
all: example
example: main/main.c Makefile
docker run --rm -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf idf.py build
COMPORT ?= /dev/cu.SLAB_USBtoUART
ESPTOOL ?= esptool.py
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 0x100000 mongoose-esp32-example.bin
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* build sdkconfig
+28
View File
@@ -0,0 +1,28 @@
# An example ESP32 application
To build this application, follow these steps:
1. Make sure you have Docker installed
2. Clone whole mongoose repository on your machine:
```sh
git clone --depth 1 https://github.com/cesanta/mongoose.git
```
3. Start command line or terminal, and go into the example's directory:
```sh
cd mongoose/examples/esp32
```
4. Run `make`. This invokes a Docker-based build. A firmware will be built
in the `build/` directory:
```sh
make
```
# Flashing built firmware
If you have a real board to flash to, you can flash a built firmware.
First, make sure to have `esptool.py` script installed on your workstation.
Then, connect the board to your workstation and run the following command:
```sh
make flash COMPORT=YOUR_USB_PORT
```
+5
View File
@@ -0,0 +1,5 @@
idf_component_register(SRCS "main.c"
"wifi.c"
"../../../mongoose.c"
INCLUDE_DIRS "../../..")
component_compile_options(-DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_LINES)
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "esp_spiffs.h"
#include "freertos/FreeRTOS.h"
#include "mongoose.h"
#define WIFI_SSID "WIFI_NETWORK" // SET THIS!
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
#define FS_ROOT "/spiffs"
// Event handler for an server (accepted) connection. Implemented endpoints:
// /api/stats - return JSON object with ESP32 stats (free RAM)
// any other - serve files from the filesystem
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data;
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);
}
}
}
// SPIFFS is flat, so tell Mongoose that the FS root is a directory
// This cludge is not required for filesystems with directory support
bool mg_is_dir(const char *path) {
return strcmp(path, FS_ROOT) == 0;
}
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
// It blocks until connected to the configured WiFi network
void wifi_init(const char *ssid, const char *pass);
wifi_init(WIFI_SSID, WIFI_PASS);
// Connected to WiFi, now start HTTP server
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
}
+104
View File
@@ -0,0 +1,104 @@
// Code taken from the ESP32 IDF WiFi station Example
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "mongoose.h"
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about
* two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT &&
event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < 3) {
esp_wifi_connect();
s_retry_num++;
LOG(LL_INFO, ("retry to connect to the AP"));
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
LOG(LL_ERROR, ("connect to the AP fail"));
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
LOG(LL_INFO, ("got ip:" IPSTR, IP2STR(&event->ip_info.ip)));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init(const char *ssid, const char *pass) {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(
WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
wifi_config_t c = {.sta = {.threshold = {.authmode = WIFI_AUTH_WPA2_PSK},
.pmf_cfg = {.capable = true, .required = false}}};
snprintf((char *) c.sta.ssid, sizeof(c.sta.ssid), "%s", ssid);
snprintf((char *) c.sta.password, sizeof(c.sta.password), "%s", pass);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
ESP_ERROR_CHECK(esp_wifi_start());
LOG(LL_DEBUG, ("wifi_init_sta finished."));
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
LOG(LL_INFO, ("connected to ap SSID:%s password:%s", ssid, pass));
} else if (bits & WIFI_FAIL_BIT) {
LOG(LL_ERROR, ("Failed to connect to SSID:%s, password:%s", ssid, pass));
} else {
LOG(LL_ERROR, ("UNEXPECTED EVENT"));
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(
WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
}
+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"
+10
View File
@@ -0,0 +1,10 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../..)
all: example
example: src/main/main.c src/main/wifi.c Makefile
docker run --rm -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) docker.io/mdashnet/8266 make -C src defconfig app
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.* src/build src/sdkconfig
+18
View File
@@ -0,0 +1,18 @@
# An example ESP8266 application
To build this application, follow these steps:
1. Make sure you have Docker installed
2. Clone whole mongoose repository on your machine:
```sh
git clone https://github.com/cesanta/mongoose.git
```
3. Start command line or terminal, and go into the example's directory:
```sh
cd mongoose/examples/esp8266
```
4. Run `make`. This invokes a Docker-based build. A firmware will be built
in the `src/build/` directory:
```sh
make example
```
+9
View File
@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mongoose-example
include $(IDF_PATH)/make/project.mk
+10
View File
@@ -0,0 +1,10 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
COMPONENT_INCLUDES += ../../../../..
+54
View File
@@ -0,0 +1,54 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#define WIFI_SSID "WIFI_NETWORK" // SET THIS!
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
#define SERVER_URL "http://0.0.0.0:80"
#define CLIENT_URL "http://info.cern.ch"
// Event handler for an server (accepted) connection
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
mg_http_reply(c, 200, "", "Hello from ESP!\n");
}
}
// Event handler for a client connection - fetch the first web page in history
// To enable TLS for HTTP,
// 1. Copy "ca.pem" file to the ESP32 flash FS
// 2. Add TLS init snippet for the connection, see examples/http-client
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) {
// Setup wifi. This function is implemented in wifi.c
// It blocks until connected to the configured WiFi network
void wifi_init(const char *ssid, const char *pass);
wifi_init(WIFI_SSID, WIFI_PASS);
// Done connecting to WiFi, now start HTTP server
run_mongoose();
}
+1
View File
@@ -0,0 +1 @@
../../../../mongoose.c
+86
View File
@@ -0,0 +1,86 @@
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "mongoose.h"
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about
* two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT &&
event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < 3) {
esp_wifi_connect();
s_retry_num++;
LOG(LL_DEBUG, ("retry to connect to the AP"));
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
LOG(LL_ERROR, ("connect to the AP fail"));
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
LOG(LL_INFO, ("got ip:%s", ip4addr_ntoa(&event->ip_info.ip)));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init(const char *ssid, const char *pass) {
s_wifi_event_group = xEventGroupCreate();
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
&event_handler, NULL));
wifi_config_t c = {};
snprintf((char *) c.sta.ssid, sizeof(c.sta.ssid), "%s", ssid);
snprintf((char *) c.sta.password, sizeof(c.sta.password), "%s", pass);
if (pass != NULL && pass[0] != '\0') {
c.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &c));
ESP_ERROR_CHECK(esp_wifi_start());
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT) {
LOG(LL_INFO, ("connected to ap SSID:%s password:%s", ssid, pass));
} else if (bits & WIFI_FAIL_BIT) {
LOG(LL_ERROR, ("Failed to connect to SSID:%s, password:%s", ssid, pass));
} else {
LOG(LL_ERROR, ("UNEXPECTED EVENT"));
}
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
&event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID,
&event_handler));
vEventGroupDelete(s_wifi_event_group);
}
+2 -1
View File
@@ -12,7 +12,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (mg_http_match_uri(hm, "/upload")) { if (mg_http_match_uri(hm, "/upload")) {
mg_http_upload(c, hm, "/tmp"); mg_http_upload(c, hm, "/tmp");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
+9 -6
View File
@@ -1,11 +1,14 @@
PROG ?= example PROG ?= example
ARGS ?= http://www.ladyada.net/ 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)
+39 -29
View File
@@ -1,46 +1,56 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2021 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Example HTTP client. Usage: // Example HTTP client. Connect to `s_url`, send request, wait for a response,
// make // print the response and exit.
// ./example http://www.ladyada.net/ // 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. 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 // 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_HTTP_MSG) { 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_MSG) {
// 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; c->is_closing = 1; // Tell mongoose to close this connection
*(bool *) fn_data = true; *(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[]) { int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager bool done = false; // Event handler flips it to true
if (argc > 1) s_url = argv[1]; // Use URL from command line
if (argc != 2) { mg_log_set("3"); // Set to 0 to disable debug
fprintf(stderr, "Usage: %s URL\n", argv[0]); // Print error mg_mgr_init(&mgr); // Initialise event manager
} else { mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
bool done = false; // Event handler flips it to true while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
struct mg_connection *c = mg_http_connect(&mgr, argv[1], fn, &done); mg_mgr_free(&mgr); // Free resources
if (c != NULL) {
mg_printf(c, "GET %s HTTP/1.0\r\n\r\n", mg_url_uri(argv[1]));
// If target URL is SSL/TLS, command client connection to use TLS
if (mg_url_is_ssl(argv[1])) {
struct mg_tls_opts opts = {.ca = "ca.pem"};
mg_tls_init(c, &opts);
}
}
while (done == false) mg_mgr_poll(&mgr, 1000); // Event loop
}
mg_mgr_free(&mgr);
return 0; return 0;
} }
+19
View File
@@ -0,0 +1,19 @@
PROG ?= example
ARGS ?= http://www.ladyada.net/
MBEDTLS_DIR ?=
ifeq "$(MBEDTLS_DIR)" ""
else
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
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-----
+64
View File
@@ -0,0 +1,64 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example HTTP client that uses a proxy server. Usage:
// make
// ./example PROXY:PORT http://www.ladyada.net
//
// To enable SSL/TLS for this client, build it like this:
// make MBEDTLS_DIR=/path/to/your/mbedtls/installation
#include "mongoose.h"
// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
const char *url = fn_data;
static bool connected;
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
printf("%.*s", (int) hm->message.len, hm->message.ptr);
exit(EXIT_SUCCESS);
} else if (ev == MG_EV_CONNECT) {
// Proxy TCP connection established. Send CONNECT request
struct mg_str host = mg_url_host(url);
// c->is_hexdumping = 1;
mg_printf(c, "CONNECT %.*s:%hu HTTP/1.1\r\nHost: %.*s:%hu\r\n\r\n",
(int) host.len, host.ptr, mg_url_port(url), (int) host.len,
host.ptr, mg_url_port(url));
// If target URL is SSL/TLS, command client connection to use TLS
if (mg_url_is_ssl(url)) {
struct mg_tls_opts opts = {.ca = "ca.pem"};
mg_tls_init(c, &opts);
}
} else if (!connected && ev == MG_EV_READ) {
struct mg_http_message hm;
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
if (n > 0) {
struct mg_str host = mg_url_host(url);
// CONNECT response - tunnel is established
connected = true;
LOG(LL_DEBUG,
("Connected to proxy, status: %.*s", (int) hm.uri.len, hm.uri.ptr));
mg_iobuf_delete(&c->recv, n);
// Send request to the target server
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) host.len,
host.ptr);
}
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
if (argc != 3) {
fprintf(stderr, "Usage: %s PROXY_URL URL\n", argv[0]);
return EXIT_FAILURE;
}
mg_mgr_init(&mgr); // Initialise event manager
mg_http_connect(&mgr, argv[1], fn, argv[2]); // Connect to the proxy
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr);
return 0;
}
+6
View File
@@ -1,5 +1,11 @@
PROG ?= example PROG ?= example
ifeq "$(MBEDTLS_DIR)" ""
else
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
endif
all: $(PROG) all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS) $(DEBUGGER) ./$(PROG) $(ARGS)
+43
View File
@@ -0,0 +1,43 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
11:2a:0e:3c:6a:8c:85:ff:6e:6a:bc:db:95:51:70:ce:b4:30:78:c7
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = IE, L = Dublin, O = Cesanta, CN = Test Root
Validity
Not Before: May 9 21:51:44 2020 GMT
Not After : May 9 21:51:44 2050 GMT
Subject: C = IE, L = Dublin, O = Cesanta, CN = Test Root
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:2c:ab:d1:02:66:24:96:d7:12:3e:09:50:4f:f1:
50:ee:51:e8:55:03:5e:ba:b1:1d:98:b2:72:79:27:
a8:1b:31:0d:5d:50:21:ff:42:f2:da:74:17:5e:53:
b2:65:41:c1:fc:84:de:4a:11:b9:8c:f4:19:d9:c4:
ca:2b:ea:eb:2c
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints:
CA:TRUE
X509v3 Key Usage:
Digital Signature, Key Encipherment, Key Agreement, Certificate Sign, CRL Sign
Signature Algorithm: ecdsa-with-SHA256
30:46:02:21:00:9c:71:6c:00:8c:06:41:0c:91:2f:cd:41:d3:
87:47:e9:df:3a:22:ad:25:7c:bf:0e:2b:39:dd:7a:0c:4e:68:
1d:02:21:00:8f:c1:22:30:10:61:5d:51:10:ea:08:2d:02:63:
67:67:32:b5:06:63:96:57:bb:78:47:0a:88:d9:19:2e:f3:be
-----BEGIN CERTIFICATE-----
MIIBqjCCAU+gAwIBAgIUESoOPGqMhf9uarzblVFwzrQweMcwCgYIKoZIzj0EAwIw
RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50
YTESMBAGA1UEAwwJVGVzdCBSb290MCAXDTIwMDUwOTIxNTE0NFoYDzIwNTAwNTA5
MjE1MTQ0WjBEMQswCQYDVQQGEwJJRTEPMA0GA1UEBwwGRHVibGluMRAwDgYDVQQK
DAdDZXNhbnRhMRIwEAYDVQQDDAlUZXN0IFJvb3QwWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAAQsq9ECZiSW1xI+CVBP8VDuUehVA166sR2YsnJ5J6gbMQ1dUCH/QvLa
dBdeU7JlQcH8hN5KEbmM9BnZxMor6ussox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud
DwQEAwIBrjAKBggqhkjOPQQDAgNJADBGAiEAnHFsAIwGQQyRL81B04dH6d86Iq0l
fL8OKzndegxOaB0CIQCPwSIwEGFdURDqCC0CY2dnMrUGY5ZXu3hHCojZGS7zvg==
-----END CERTIFICATE-----
+26 -9
View File
@@ -1,25 +1,41 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2020 Cesanta Software Limited
// All rights reserved // All rights reserved
//
// HTTP server example. This server serves both static and dynamic content.
// It implements the following endpoints:
// /api/f1 - respond with JSON string {"result": 123}
// /api/f2/:id - wildcard example, respond with JSON string {"result": "URI"}
// any other URI serves static files from s_web_directory
//
// To enable SSL/TLS (using self-signed certificates in PEM files),
// 1. Change s_listen_on from http:// to https://
// 2. make MBEDTLS_DIR=/path/to/your/mbedtls/installation
// 3. curl -k https://127.0.0.1:8000
#include "mongoose.h" #include "mongoose.h"
static const char *s_listen_on = "http://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 = ".";
// This RESTful server implements the following endpoints:
// /api/f1 - respond with JSON string {"result": 123}
// /api/f2/:id - wildcard example, respond with JSON string {"result": "URI"}
// any other URI serves static files from 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) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_ACCEPT && mg_url_is_ssl(s_listen_on)) {
// If s_listen_on URL is https://, tell listening connection to use TLS
struct mg_tls_opts opts = {
//.ca = "ca.pem", // Uncomment to enable two-way SSL
.cert = "server.pem", // Certificate PEM file
.certkey = "server.pem", // This pem conains both cert and key
};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/f1")) { if (mg_http_match_uri(hm, "/api/f1")) {
mg_http_reply(c, 200, "{\"result\": %d}\n", 123); // Serve REST response mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123); // Serve REST
} else if (mg_http_match_uri(hm, "/api/f2/*")) { } else if (mg_http_match_uri(hm, "/api/f2/*")) {
mg_http_reply(c, 200, "{\"result\": \"%.*s\"}\n", (int) hm->uri.len, mg_http_reply(c, 200, "", "{\"result\": \"%.*s\"}\n", (int) hm->uri.len,
hm->uri.ptr); hm->uri.ptr);
} else { } else {
mg_http_serve_dir(c, ev_data, s_web_directory); // Serve static files struct mg_http_serve_opts opts = {.root_dir = s_web_directory};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
(void) fn_data; (void) fn_data;
@@ -27,6 +43,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
int main(void) { int main(void) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
mg_log_set("2"); // Set to 3 to enable debug
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
+50
View File
@@ -0,0 +1,50 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
6e:73:28:55:df:13:b5:61:f5:4f:4f:5d:00:d9:0a:d8:b5:3a:21:4b
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = IE, L = Dublin, O = Cesanta, CN = Test Root
Validity
Not Before: May 9 21:51:49 2020 GMT
Not After : May 9 21:51:49 2030 GMT
Subject: CN = server
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:92:e0:46:9c:89:c3:37:a9:74:eb:35:55:43:55:
5c:ac:eb:c7:e4:50:ee:f4:c0:ba:17:02:5c:d9:ed:
b4:d4:ff:21:12:9a:b4:43:f4:89:4b:69:e4:6d:2b:
96:1f:fc:01:4d:30:5a:79:73:76:ba:19:41:cc:c5:
16:2b:bf:74:28
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Key Encipherment, Key Agreement
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: ecdsa-with-SHA256
30:46:02:21:00:fa:3a:c7:1e:cb:8c:27:59:41:8d:77:dd:7b:
cb:8c:08:15:16:b9:6e:70:e6:47:38:d1:55:42:e0:d7:66:c8:
f0:02:21:00:cc:70:4d:96:28:00:d3:c7:39:53:74:b2:49:87:
27:92:1b:ab:1a:0e:74:06:59:42:23:47:98:43:d8:20:a7:fa
-----BEGIN CERTIFICATE-----
MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw
RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50
YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx
NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW
H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ
QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50
BllCI0eYQ9ggp/o=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w
kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70
wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo
-----END PRIVATE KEY-----
+19
View File
@@ -0,0 +1,19 @@
PROG ?= example
CFLAGS ?= -DMG_IO_SIZE=8192
MBEDTLS_DIR ?=
ifeq "$(MBEDTLS_DIR)" ""
else
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
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-----
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example HTTP reverse proxy
// 1. Run `make`. This builds and starts a proxy on port 8000
// 2. Start your browser, go to https://localhost:8000
//
// To enable SSL/TLS, build it like this:
// make MBEDTLS_DIR=/path/to/your/mbedtls/installation
static const char *s_backend_url = "https://cesanta.com";
static const char *s_listen_url = "http://localhost:8000";
#include "mongoose.h"
// Forward client request to the backend connection, rewriting the Host header
static void forward_request(struct mg_http_message *hm,
struct mg_connection *c) {
size_t i, max = sizeof(hm->headers) / sizeof(hm->headers[0]);
struct mg_str host = mg_url_host(s_backend_url);
mg_printf(c, "%.*s\r\n",
(int) (hm->proto.ptr + hm->proto.len - hm->message.ptr),
hm->message.ptr);
for (i = 0; i < max && hm->headers[i].name.len > 0; i++) {
struct mg_str *k = &hm->headers[i].name, *v = &hm->headers[i].value;
if (mg_strcmp(*k, mg_str("Host")) == 0) v = &host;
mg_printf(c, "%.*s: %.*s\r\n", (int) k->len, k->ptr, (int) v->len, v->ptr);
}
mg_send(c, "\r\n", 2);
mg_send(c, hm->body.ptr, hm->body.len);
LOG(LL_DEBUG, ("FORWARDING: %.*s %.*s", (int) hm->method.len, hm->method.ptr,
(int) hm->uri.len, hm->uri.ptr));
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_connection *c2 = fn_data;
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (c->label[0] != 'B' && c2 == NULL) {
// Client request, create backend connection Note that we're passing
// client connection `c` as fn_data for the created backend connection.
// This way, we tie together these two connections via `fn_data` pointer:
// client's c->fn_data points to the backend connection, and backend's
// c->fn_data points to the client connection
c2 = mg_connect(c->mgr, s_backend_url, fn, c);
c->fn_data = c2;
if (c2 != NULL) {
c2->is_hexdumping = 1;
c2->label[0] = 'B'; // Mark this connection as backend
} else {
c->is_closing = 1;
}
}
if (c2 != NULL && c2->label[0] == 'B') forward_request(hm, c2);
} else if (ev == MG_EV_READ) {
if (c->label[0] == 'B' && c2 != NULL) {
// All incoming data from the backend, forward to the client
mg_send(c2, c->recv.buf, c->recv.len);
mg_iobuf_delete(&c->recv, c->recv.len);
}
} else if (ev == MG_EV_CONNECT) {
if (mg_url_is_ssl(s_backend_url)) {
struct mg_tls_opts opts = {.ca = "ca.pem"};
mg_tls_init(c, &opts);
}
} else if (ev == MG_EV_CLOSE) {
if (c2 != NULL) c2->is_closing = 1;
c->fn_data = NULL;
}
}
int main(void) {
struct mg_mgr mgr;
mg_log_set("3"); // Set log level
mg_mgr_init(&mgr); // Initialise event manager
mg_http_listen(&mgr, s_listen_url, fn, NULL); // Start proxy
for (;;) mg_mgr_poll(&mgr, 1000); // Event loop
mg_mgr_free(&mgr);
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
PROG ?= example
ROOT ?= $(realpath $(CURDIR)/../..)
DEFS ?= -DMG_ENABLE_LINES=1 -DMG_ENABLE_DIRECTORY_LISTING=1 -DMG_ENABLE_SSI=1
CFLAGS ?= -I../.. -W -Wall -DMG_ENABLE_IPV6=1 $(DEFS) $(EXTRA)
VCFLAGS = /nologo /W3 /O2 /I../.. $(DEFS) $(EXTRA) /link /incremental:no /machine:IX86
VC98 = docker run --rm -e Tmp=. -v $(ROOT):$(ROOT) -w $(CURDIR) docker.io/mdashnet/vc98
all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS)
$(PROG): main.c Makefile
$(CC) ../../mongoose.c main.c -I../.. $(CFLAGS) -o $@
mongoose.exe: main.c
$(VC98) wine cl ../../mongoose.c main.c $(VCFLAGS) ws2_32.lib /out:$@
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mongoose mongoose_* mongoose.*
@@ -4,15 +4,15 @@
#include "mongoose.h" #include "mongoose.h"
static const char *s_debug_level = "2"; static const char *s_debug_level = "2";
static const char *s_web_root_dir = "."; static const char *s_root_dir = ".";
static const char *s_listening_address = "http://localhost:8000"; static const char *s_listening_address = "http://localhost:8000";
static const char *s_enable_hexdump = "no"; static const char *s_enable_hexdump = "no";
static const char *s_rewrites = ""; static const char *s_ssi_pattern = "#.shtml";
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) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_serve_opts opts = {s_root_dir, s_ssi_pattern};
mg_http_serve_dir(c, ev_data, s_web_root_dir); mg_http_serve_dir(c, ev_data, &opts);
} }
(void) fn_data; (void) fn_data;
} }
@@ -21,13 +21,13 @@ static void usage(const char *prog) {
fprintf(stderr, fprintf(stderr,
"Mongoose v.%s, built " __DATE__ " " __TIME__ "Mongoose v.%s, built " __DATE__ " " __TIME__
"\nUsage: %s OPTIONS\n" "\nUsage: %s OPTIONS\n"
" -D LEVEL - debug level, from 0 to 4, default: '%s'\n"
" -H yes|no - enable traffic hexdump, default: '%s'\n" " -H yes|no - enable traffic hexdump, default: '%s'\n"
" -S GLOB - glob pattern for SSI files, default: '%s'\n"
" -d DIR - directory to serve, default: '%s'\n" " -d DIR - directory to serve, default: '%s'\n"
" -l ADDR - listening address, default: '%s'\n" " -l ADDR - listening address, default: '%s'\n"
" -r LIST - list of URI=DIR,... URI rewrites, default: '%s'\n", " -v LEVEL - debug level, from 0 to 4, default: '%s'\n",
MG_VERSION, prog, s_debug_level, s_enable_hexdump, s_web_root_dir, MG_VERSION, prog, s_debug_level, s_enable_hexdump, s_ssi_pattern,
s_listening_address, s_rewrites); s_root_dir, s_listening_address);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -39,15 +39,15 @@ int main(int argc, char *argv[]) {
// Parse command-line flags // Parse command-line flags
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) { if (strcmp(argv[i], "-d") == 0) {
s_web_root_dir = argv[++i]; s_root_dir = argv[++i];
} else if (strcmp(argv[i], "-D") == 0) {
s_debug_level = argv[++i];
} else if (strcmp(argv[i], "-H") == 0) { } else if (strcmp(argv[i], "-H") == 0) {
s_enable_hexdump = argv[++i]; s_enable_hexdump = argv[++i];
} else if (strcmp(argv[i], "-S") == 0) {
s_ssi_pattern = argv[++i];
} else if (strcmp(argv[i], "-l") == 0) { } else if (strcmp(argv[i], "-l") == 0) {
s_listening_address = argv[++i]; s_listening_address = argv[++i];
} else if (strcmp(argv[i], "-r") == 0) { } else if (strcmp(argv[i], "-v") == 0) {
s_rewrites = argv[++i]; s_debug_level = argv[++i];
} else { } else {
usage(argv[0]); usage(argv[0]);
} }
@@ -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;
}
+3 -2
View File
@@ -11,12 +11,13 @@ 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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/log/static")) { if (mg_http_match_uri(hm, "/api/log/static")) {
mg_http_serve_file(c, hm, "log.txt", "text/plain"); mg_http_serve_file(c, hm, "log.txt", "text/plain", "");
} else if (mg_http_match_uri(hm, "/api/log/live")) { } else if (mg_http_match_uri(hm, "/api/log/live")) {
c->label[0] = 'L'; // Mark that connection as live log listener c->label[0] = 'L'; // Mark that connection as live log listener
mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); mg_printf(c, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
+1 -1
View File
@@ -18,7 +18,7 @@ static const char *s_topic = "mg/test";
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_ERROR) { if (ev == MG_EV_ERROR) {
// On error, log error message // On error, log error message
LOG(LL_ERROR, ("%p %s", c->fd, ev_data)); LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
// If target URL is SSL/TLS, command client connection to use TLS // If target URL is SSL/TLS, command client connection to use TLS
if (mg_url_is_ssl(s_url)) { if (mg_url_is_ssl(s_url)) {
+10
View File
@@ -0,0 +1,10 @@
PROG ?= example
all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS)
$(PROG): main.c
$(CC) ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 $(CFLAGS) -o $(PROG) main.c
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
+83
View File
@@ -0,0 +1,83 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example MQTT server. Usage:
// 1. Start this server, type `make`
// 2. Install mosquitto MQTT client
// 3. In one terminal, run: mosquitto_sub -h localhost -t foo -t bar
// 4. In another, run: mosquitto_pub -h localhost -t foo -m hi
#include "mongoose.h"
static const char *s_listen_on = "mqtt://0.0.0.0:1883";
// A list of subscription, held in memory
struct sub {
struct sub *next;
struct mg_connection *c;
struct mg_str topic;
uint8_t qos;
};
static struct sub *s_subs = NULL;
// Event handler function
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_MQTT_CMD) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
LOG(LL_DEBUG, ("cmd %d qos %d", mm->cmd, mm->qos));
switch (mm->cmd) {
case MQTT_CMD_CONNECT: {
// Client connects. Return success, do not check user/password
uint8_t response[] = {0, 0};
mg_mqtt_send_header(c, MQTT_CMD_CONNACK, 0, sizeof(response));
mg_send(c, response, sizeof(response));
break;
}
case MQTT_CMD_SUBSCRIBE: {
// Client subscribes
int pos = 4; // Initial topic offset, where ID ends
uint8_t qos;
struct mg_str topic;
while ((pos = mg_mqtt_next_sub(mm, &topic, &qos, pos)) > 0) {
struct sub *sub = calloc(1, sizeof(*sub));
sub->c = c;
sub->topic = mg_strdup(topic);
sub->qos = qos;
LIST_ADD_HEAD(struct sub, &s_subs, sub);
LOG(LL_INFO,
("SUB %p [%.*s]", c->fd, (int) sub->topic.len, sub->topic.ptr));
}
break;
}
case MQTT_CMD_PUBLISH: {
// Client published message. Push to all subscribed channels
LOG(LL_INFO, ("PUB %p [%.*s] -> [%.*s]", c->fd, (int) mm->data.len,
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
for (struct sub *sub = s_subs; sub != NULL; sub = sub->next) {
if (mg_strcmp(mm->topic, sub->topic) != 0) continue;
mg_mqtt_pub(sub->c, &mm->topic, &mm->data);
}
break;
}
}
} else if (ev == MG_EV_CLOSE) {
// Client disconnects. Remove from the subscription list
for (struct sub *next, *sub = s_subs; sub != NULL; sub = next) {
next = sub->next;
if (c != sub->c) continue;
LOG(LL_INFO,
("UNSUB %p [%.*s]", c->fd, (int) sub->topic.len, sub->topic.ptr));
LIST_DELETE(struct sub, &s_subs, sub);
}
}
(void) fn_data;
}
int main(void) {
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
mg_mqtt_listen(&mgr, s_listen_on, fn, NULL); // Create MQTT listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr);
return 0;
}
+14 -14
View File
@@ -31,20 +31,11 @@ struct response {
#define SLEEP_TIME 3 // Seconds to sleep to simulate calculation #define SLEEP_TIME 3 // Seconds to sleep to simulate calculation
#endif #endif
static void thread_function(void *param) {
int sock = (long) param; // Grab our blocking socket
struct response r = {strdup("hello"), 5}; // Create response
sleep(SLEEP_TIME); // Simulate long execution
send(sock, (void *) &r, sizeof(r), 0); // Send to request handler
close(sock); // Done, close socket, end thread
}
static void start_thread(void (*f)(void *), void *p) { static void start_thread(void (*f)(void *), void *p) {
#ifdef WINCE #ifdef _WIN32
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL);
#elif defined(_WIN32)
_beginthread((void(__cdecl *)(void *)) f, 0, p); _beginthread((void(__cdecl *)(void *)) f, 0, p);
#else #else
#define closesocket(x) close(x)
#include <pthread.h> #include <pthread.h>
pthread_t thread_id = (pthread_t) 0; pthread_t thread_id = (pthread_t) 0;
pthread_attr_t attr; pthread_attr_t attr;
@@ -55,6 +46,15 @@ static void start_thread(void (*f)(void *), void *p) {
#endif #endif
} }
static void thread_function(void *param) {
int sock = (long) param; // Grab our blocking socket
struct response r = {strdup("hello\n"), 6}; // Create response
mg_usleep(SLEEP_TIME * 1000000); // Simulate long execution
LOG(LL_INFO, ("got sock %d", sock)); // For debugging
send(sock, (void *) &r, sizeof(r), 0); // Send to request handler
closesocket(sock); // Done, close socket, end thread
}
// HTTP request callback // HTTP request callback
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) {
@@ -70,7 +70,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
"Content-Length: 3\r\n\r\n" // Set to allow keep-alive "Content-Length: 3\r\n\r\n" // Set to allow keep-alive
"hi\n"); "hi\n");
} else { } else {
int blocking, non_blocking; int blocking = -1, non_blocking = -1;
mg_socketpair(&blocking, &non_blocking); // Create connected pair mg_socketpair(&blocking, &non_blocking); // Create connected pair
// Pass blocking socket to the thread_function. // Pass blocking socket to the thread_function.
@@ -82,7 +82,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} }
} else if (ev == MG_EV_POLL && c->fn_data != NULL) { } else if (ev == MG_EV_POLL && c->fn_data != NULL) {
// On each poll iteration, try to receive response data // On each poll iteration, try to receive response data
int sock = (int) c->fn_data; int sock = (int) (long) c->fn_data;
struct response response = {NULL, 0}; struct response response = {NULL, 0};
if (recv(sock, (void *) &response, sizeof(response), 0) == if (recv(sock, (void *) &response, sizeof(response), 0) ==
sizeof(response)) { sizeof(response)) {
@@ -90,7 +90,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_printf(c, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%.*s", mg_printf(c, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%.*s",
response.len, response.len, response.data); response.len, response.len, response.data);
free(response.data); // We can free produced data now free(response.data); // We can free produced data now
close(sock); // And close our end of the socket pair closesocket(sock); // And close our end of the socket pair
c->fn_data = NULL; c->fn_data = NULL;
} }
} }
+2 -1
View File
@@ -71,7 +71,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
u->token); u->token);
mg_http_printf_chunk(c, ""); mg_http_printf_chunk(c, "");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
+2 -8
View File
@@ -1,9 +1,3 @@
/* axios v0.19.0-beta.1 | (c) 2018 by Matt Zabriskie */ /* axios v0.21.1 | (c) 2020 by Matt Zabriskie */
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function w(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=w(t[n],e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function b(){function e(e,n){"object"==typeof t[n]&&"object"==typeof e?t[n]=b(t[n],e):"object"==typeof e?t[n]=b({},e):t[n]=e}for(var t={},n=0,r=arguments.length;n<r;n++)v(arguments[n],e);return t}function E(e,t,n){return v(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}var S=n(3),R=n(4),j=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:o,isBuffer:R,isFormData:s,isArrayBufferView:i,isString:a,isNumber:u,isObject:f,isUndefined:c,isDate:p,isFile:d,isBlob:l,isFunction:h,isStream:m,isURLSearchParams:y,isStandardBrowserEnv:x,forEach:v,merge:w,deepMerge:b,extend:E,trim:g}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t){/*! !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(4),a=n(22),u=n(10),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(9),c.all=function(e){return Promise.all(e)},c.spread=n(25),c.isAxiosError=n(26),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===R.call(e)}function o(e){return"undefined"==typeof e}function s(e){return null!==e&&!o(e)&&null!==e.constructor&&!o(e.constructor)&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}function i(e){return"[object ArrayBuffer]"===R.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function u(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function c(e){return"string"==typeof e}function f(e){return"number"==typeof e}function p(e){return null!==e&&"object"==typeof e}function d(e){if("[object Object]"!==R.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Date]"===R.call(e)}function h(e){return"[object File]"===R.call(e)}function m(e){return"[object Blob]"===R.call(e)}function y(e){return"[object Function]"===R.call(e)}function g(e){return p(e)&&y(e.pipe)}function v(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function x(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function w(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n<o;n++)t.call(null,e[n],n,e);else for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.call(null,e[s],s,e)}function E(){function e(e,n){d(t[n])&&d(e)?t[n]=E(t[n],e):d(e)?t[n]=E({},e):r(e)?t[n]=e.slice():t[n]=e}for(var t={},n=0,o=arguments.length;n<o;n++)b(arguments[n],e);return t}function j(e,t,n){return b(t,function(t,r){n&&"function"==typeof t?e[r]=S(t,n):e[r]=t}),e}function C(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e}var S=n(3),R=Object.prototype.toString;e.exports={isArray:r,isArrayBuffer:i,isBuffer:s,isFormData:a,isArrayBufferView:u,isString:c,isNumber:f,isObject:p,isPlainObject:d,isUndefined:o,isDate:l,isFile:h,isBlob:m,isFunction:y,isStream:g,isURLSearchParams:v,isStandardBrowserEnv:w,forEach:b,merge:E,extend:j,trim:x,stripBOM:C}},function(e,t){"use strict";e.exports=function(e,t){return function(){for(var n=new Array(arguments.length),r=0;r<n.length;r++)n[r]=arguments[r];return e.apply(t,n)}}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(5),i=n(6),a=n(7),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(u(n||{},{method:e,url:t,data:(n||{}).data}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(u(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use stricLine truncated
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}return s&&(e+=(e.indexOf("?")===-1?"?":"&")+s),e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:Line truncated
//# sourceMappingURL=axios.min.map //# sourceMappingURL=axios.min.map
+19
View File
@@ -0,0 +1,19 @@
PROG ?= example
CFLAGS ?= -DMG_ENABLE_LINES $(CFLAGS_EXTRA)
MBEDTLS_DIR ?=
ifeq "$(MBEDTLS_DIR)" ""
else
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
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
+173
View File
@@ -0,0 +1,173 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example socks5 server. To test,
// 1. Run `make` to start this server on port 1080
// 2. Run `curl`
#include "mongoose.h"
static const char *s_lsn = "tcp://localhost:1080"; // Listening address
enum {
VERSION = 5, // Socks protocol version
STATE_HANDSHAKE = 0, // Connection state: in handshake
STATE_REQUEST = 1, // Connection state: connecting
STATE_ESTABLISHED = 2, // Connection state: established
HANDSHAKE_NOAUTH = 0, // Handshake method - no authentication
HANDSHAKE_GSSAPI = 1, // Handshake method - GSSAPI auth
HANDSHAKE_USERPASS = 2, // Handshake method - user/password auth
HANDSHAKE_FAILURE = 0xff, // Handshake method - failure
CMD_CONNECT = 1, // Command: CONNECT
CMD_BIND = 2, // Command: BIND
CMD_UDP_ASSOCIATE = 3, // Command: UDP ASSOCIATE
ADDR_TYPE_IPV4 = 1, // Address type: IPv4
ADDR_TYPE_DOMAIN = 3, // Address type: Domain name
ADDR_TYPE_IPV6 = 4, // Address type: IPv6
RESP_SUCCESS = 0, // Response: success
RESP_FAILURE = 1, // Response: failure
RESP_NOT_ALLOWED = 2, // Response status
RESP_NET_UNREACHABLE = 3, // Response status
RESP_HOST_UNREACHABLE = 4, // Response status
RESP_CONN_REFUSED = 5, // Response status
RESP_TTL_EXPIRED = 6, // Response status
RESP_CMD_NOT_SUPPORTED = 7, // Response status
RESP_ADDR_NOT_SUPPORTED = 8, // Response status
};
// https://www.ietf.org/rfc/rfc1928.txt paragraph 3, handle client handshake
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
static void handshake(struct mg_connection *c) {
struct mg_iobuf *r = &c->recv;
if (r->buf[0] != VERSION) {
c->is_closing = 1;
} else if (r->len > 2 && (size_t) r->buf[1] + 2 <= r->len) {
/* https://www.ietf.org/rfc/rfc1928.txt paragraph 3 */
uint8_t reply[2] = {VERSION, HANDSHAKE_FAILURE};
int i;
for (i = 2; i < r->buf[1] + 2; i++) {
// TODO(lsm): support other auth methods
if (r->buf[i] == HANDSHAKE_NOAUTH) reply[1] = r->buf[i];
}
mg_iobuf_delete(r, 2 + r->buf[1]);
mg_send(c, reply, sizeof(reply));
c->label[0] = STATE_REQUEST;
}
}
static void disband(struct mg_connection *c) {
struct mg_connection *c2 = (struct mg_connection *) c->fn_data;
if (c2 != NULL) {
c2->is_draining = 1;
c2->fn_data = NULL;
}
c->is_draining = 1;
c->fn_data = NULL;
}
static void exchange(struct mg_connection *c) {
struct mg_connection *c2 = (struct mg_connection *) c->fn_data;
if (c2 != NULL) {
mg_send(c2, c->recv.buf, c->recv.len);
mg_iobuf_delete(&c->recv, c->recv.len);
} else {
c->is_draining = 1;
}
}
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_READ) {
exchange(c);
} else if (ev == MG_EV_CLOSE) {
disband(c);
}
(void) ev_data;
(void) fn_data;
}
// Request, https://www.ietf.org/rfc/rfc1928.txt paragraph 4
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
static void request(struct mg_connection *c) {
struct mg_iobuf *r = &c->recv;
uint8_t *p = r->buf, addr_len = 4, reply = RESP_SUCCESS;
int ver, cmd, atyp;
char addr[1024];
if (r->len < 8) return; // return if not fully buffered. min DST.ADDR is 2
ver = p[0];
cmd = p[1];
atyp = p[3];
// TODO(lsm): support other commands
if (ver != VERSION || cmd != CMD_CONNECT) {
reply = RESP_CMD_NOT_SUPPORTED;
} else if (atyp == ADDR_TYPE_IPV4) {
addr_len = 4;
if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
snprintf(addr, sizeof(addr), "tcp://%d.%d.%d.%d:%d", p[4], p[5], p[6], p[7],
p[8] << 8 | p[9]);
c->fn_data = mg_connect(c->mgr, addr, fn2, c);
} else if (atyp == ADDR_TYPE_IPV6) {
addr_len = 16;
if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
snprintf(addr, sizeof(addr), "tcp://[%x:%x:%x:%x:%x:%x:%x:%x]:%d",
p[4] << 8 | p[5], p[6] << 8 | p[7], p[8] << 8 | p[9],
p[10] << 8 | p[11], p[12] << 8 | p[13], p[14] << 8 | p[15],
p[16] << 8 | p[17], p[18] << 8 | p[19], p[20] << 8 | p[21]);
c->fn_data = mg_connect(c->mgr, addr, fn2, c);
} else if (atyp == ADDR_TYPE_DOMAIN) {
addr_len = p[4] + 1;
if (r->len < (size_t) addr_len + 6) return; /* return if not buffered */
snprintf(addr, sizeof(addr), "tcp://%.*s:%d", p[4], p + 5,
p[4 + addr_len] << 8 | p[4 + addr_len + 1]);
c->fn_data = mg_connect(c->mgr, addr, fn2, c);
} else {
reply = RESP_ADDR_NOT_SUPPORTED;
}
// Reply, https://www.ietf.org/rfc/rfc1928.txt paragraph 5
//
// +----+-----+-------+------+----------+----------+
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
{
uint8_t buf[] = {VERSION, reply, 0};
mg_send(c, buf, sizeof(buf));
}
mg_send(c, r->buf + 3, addr_len + 1 + 2);
mg_iobuf_delete(r, 6 + addr_len); // Remove request from the input stream
c->label[0] = STATE_ESTABLISHED; // Mark ourselves as connected
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_READ) {
// We use the first label byte as a state
if (c->label[0] == STATE_HANDSHAKE) handshake(c);
if (c->label[0] == STATE_REQUEST) request(c);
if (c->label[0] == STATE_ESTABLISHED) exchange(c);
} else if (ev == MG_EV_CLOSE) {
disband(c);
}
(void) fn_data;
(void) ev_data;
}
int main(void) {
struct mg_mgr mgr; // Event manager
mg_log_set("3"); // Set to 0 to disable debug
mg_mgr_init(&mgr); // Initialise event manager
mg_listen(&mgr, s_lsn, fn, NULL); // Create client connection
while (true) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
PROG ?= example
all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS)
$(PROG): main.c
$(CC) ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 -DMG_ENABLE_DIRECTORY_LISTING=1 $(CFLAGS) -o $(PROG) main.c
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
+54
View File
@@ -0,0 +1,54 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example Websocket server with timers. This is a simple Websocket echo
// server, which sends a message to all connected clients periodically,
// using timer API.
//
// 1. Start this server, type `make`
// 2. Open https://www.websocket.org/echo.html in your browser
// 3. In the "Location" text field, type ws://127.0.0.1:8000/websocket
// 4. See "hi" messages appearing periodically
#include "mongoose.h"
static const char *s_listen_on = "http://localhost:8000";
// This RESTful server implements the following endpoints:
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
mg_ws_upgrade(c, hm, NULL);
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data. Echo it back!
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
mg_iobuf_delete(&c->recv, c->recv.len);
}
(void) fn_data;
}
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_connection *c;
// Broadcast "hi" message to all connected websocket clients.
// Traverse over all connections
for (c = mgr->conns; c != NULL; c = c->next) {
// Send only to accepted websocket connections
if (c->is_accepted && c->is_websocket) {
mg_ws_send(c, "hi", 2, WEBSOCKET_OP_TEXT);
}
}
}
int main(void) {
struct mg_mgr mgr; // Event manager
struct mg_timer t1; // Timer
mg_mgr_init(&mgr); // Initialise event manager
mg_timer_init(&t1, 300, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_timer_free(&t1); // Free timer resources
mg_mgr_free(&mgr); // Free manager resources
return 0;
}
+3 -2
View File
@@ -18,7 +18,8 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n" "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n"); "Content-Type: multipart/x-mixed-replace; boundary=--foo\r\n\r\n");
} else { } else {
mg_http_serve_dir(c, ev_data, "web_root"); struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
} }
} }
} }
@@ -35,7 +36,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
char *data = mg_file_read(path); // Read next file char *data = mg_file_read(path); // Read next file
struct mg_connection *c; struct mg_connection *c;
for (c = mgr->conns; c != NULL; c = c->next) { for (c = mgr->conns; c != NULL; c = c->next) {
if (c->label[0] != 'S') continue; // Skip non-stream connections if (c->label[0] != 'S') continue; // Skip non-stream connections
if (data == NULL || size == 0) continue; // Skip on file read error if (data == NULL || size == 0) continue; // Skip on file read error
mg_printf(c, mg_printf(c,
"--foo\r\nContent-Type: image/jpeg\r\n" "--foo\r\nContent-Type: image/jpeg\r\n"
+1 -1
View File
@@ -13,7 +13,7 @@ static const char *s_url = "ws://localhost:8000/websocket";
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_ERROR) { if (ev == MG_EV_ERROR) {
// On error, log error message // On error, log error message
LOG(LL_ERROR, ("%p %s", c->fd, ev_data)); LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_WS_OPEN) { } else if (ev == MG_EV_WS_OPEN) {
// When websocket handshake is successful, send message // When websocket handshake is successful, send message
mg_ws_send(c, "hello", 5, WEBSOCKET_OP_TEXT); mg_ws_send(c, "hello", 5, WEBSOCKET_OP_TEXT);
+4 -3
View File
@@ -21,13 +21,14 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (mg_http_match_uri(hm, "/websocket")) { if (mg_http_match_uri(hm, "/websocket")) {
// Upgrade to websocket. From now on, a connection is a full-duplex // Upgrade to websocket. From now on, a connection is a full-duplex
// Websocket connection, which will receive MG_EV_WS_MSG events. // Websocket connection, which will receive MG_EV_WS_MSG events.
mg_ws_upgrade(c, hm); mg_ws_upgrade(c, hm, NULL);
} else if (mg_http_match_uri(hm, "/rest")) { } else if (mg_http_match_uri(hm, "/rest")) {
// Serve REST response // Serve REST response
mg_http_reply(c, 200, "{\"result\": %d}\n", 123); mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
} else { } else {
// Serve static files // Serve static files
mg_http_serve_dir(c, ev_data, s_web_directory); struct mg_http_serve_opts opts = {.root_dir = s_web_directory};
mg_http_serve_dir(c, ev_data, &opts);
} }
} else if (ev == MG_EV_WS_MSG) { } else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data. Echo it back! // Got websocket frame. Received data is wm->data. Echo it back!
+1142 -573
View File
File diff suppressed because it is too large. Load diff
+198 -102
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.0" #define MG_VERSION "7.2"
#define MG_ARCH_CUSTOM 0 #define MG_ARCH_CUSTOM 0
@@ -61,10 +60,10 @@
#define MG_ARCH MG_ARCH_NXP_KINETIS #define MG_ARCH MG_ARCH_NXP_KINETIS
#elif defined(PIC32) #elif defined(PIC32)
#define MG_ARCH MG_ARCH_PIC32 #define MG_ARCH MG_ARCH_PIC32
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR) #elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
#define MG_ARCH MG_ARCH_ESP8266 #define MG_ARCH MG_ARCH_ESP8266
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ #elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2) defined(TARGET_IS_TM4C129_RA2)
#define MG_ARCH MG_ARCH_TM4C129 #define MG_ARCH MG_ARCH_TM4C129
@@ -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
@@ -128,26 +132,12 @@
#if MG_ARCH == MG_ARCH_ESP8266 #if MG_ARCH == MG_ARCH_ESP8266
#include <dirent.h> #include <dirent.h>
#include <esp_system.h>
#include <netdb.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#include <user_interface.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#ifndef PATH_MAX #define MG_INT64_FMT "%lld"
#define PATH_MAX 128
#endif
#define MG_ENABLE_LWIP 1
#define LWIP_TIMEVAL_PRIVATE 0 // struct timeval is defined in sys/time.h
#if LWIP_SOCKET
#include <lwip/sockets.h>
#else
#undef LWIP_SOCKET
#define LWIP_SOCKET 1
#include <lwip/sockets.h> // Source sockaddr_in anyway
#undef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#endif #endif
@@ -161,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
@@ -221,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>
@@ -232,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
@@ -243,31 +236,40 @@ static inline int ff_vfprintf(FF_FILE *fp, const char *fmt, va_list ap) {
typedef __int64 int64_t; typedef __int64 int64_t;
typedef unsigned __int64 uint64_t; typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t; typedef unsigned char uint8_t;
typedef char int8_t;
typedef unsigned short uint16_t; typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned int uint32_t; typedef unsigned int uint32_t;
typedef int bool; typedef int int32_t;
enum { false = 0, true = 1 }; typedef enum { false = 0, true = 1 } bool;
#else #else
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <ws2tcpip.h>
#endif #endif
#include <winsock2.h> #include <winsock2.h>
// Protect from calls like std::snprintf in app code
// See https://github.com/cesanta/mongoose/issues/1047
#ifndef __cplusplus
#define snprintf _snprintf #define snprintf _snprintf
#define vsnprintf _vsnprintf #define vsnprintf _vsnprintf
#define strdup(x) _strdup(x) #define strdup(x) _strdup(x)
#define sleep(x) Sleep((x) *1000) #endif
typedef int socklen_t; typedef int socklen_t;
#define PATH_MAX MAX_PATH
#define MG_DIRSEP '\\' #define MG_DIRSEP '\\'
#define inline __inline #ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef EINPROGRESS #ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS #define EINPROGRESS WSAEINPROGRESS
#endif #endif
#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
@@ -275,11 +277,12 @@ typedef int socklen_t;
#define va_copy(x, y) (x) = (y) #define va_copy(x, y) (x) = (y)
#endif #endif
#endif #endif
#ifndef S_ISDIR #ifndef S_ISDIR
#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
@@ -289,7 +292,7 @@ typedef int socklen_t;
#if MG_ENABLE_LWIP #if MG_ENABLE_LWIP
#define MG_ENABLE_SOCKET 0 #define MG_ENABLE_SOCKET 0
#else #elif !defined(MG_ENABLE_SOCKET)
#define MG_ENABLE_SOCKET 1 #define MG_ENABLE_SOCKET 1
#endif #endif
@@ -305,6 +308,10 @@ typedef int socklen_t;
#define MG_ENABLE_FS 1 #define MG_ENABLE_FS 1
#endif #endif
#ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 0
#endif
#ifndef MG_ENABLE_IPV6 #ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0 #define MG_ENABLE_IPV6 0
#endif #endif
@@ -325,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
@@ -347,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>
@@ -416,26 +423,36 @@ 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);
char *mg_hexdump(const void *buf, int len); uint32_t mg_crc32(uint32_t crc, const char *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);
unsigned long mg_unhexn(const char *s, int len); unsigned long mg_unhexn(const char *s, int len);
int mg_asprintf(char **buf, size_t size, const char *fmt, ...); int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
int64_t mg_to64(const char *s); int64_t mg_to64(struct mg_str str);
bool mg_aton(const char *s, uint32_t *ipaddr);
char *mg_ntoa(uint32_t ipaddr, char *buf, size_t len);
double mg_time(void); 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)
@@ -446,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
@@ -503,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);
@@ -547,36 +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 NULL MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
MG_EV_WS_MSG, // Websocket message received struct mg_ws_message * MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_MQTT_MSG, // MQTT message struct mg_mqtt_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_SNTP_TIME, // SNTP time received struct timeval * MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_USER, // Starting ID for user events MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events
}; };
@@ -585,19 +594,28 @@ enum {
struct mg_mgr { struct mg_dns {
struct mg_connection *conns; // List of active connections const char *url; // DNS server URL
struct mg_connection *dnsc; // DNS resolver connection struct mg_connection *c; // DNS server connection
const char *dnsserver; // DNS server URL
int dnstimeout; // DNS resolve timeout in milliseconds
#if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
}; };
struct mg_addr { struct mg_addr {
uint16_t port; // TCP or UDP port in network byte order uint16_t port; // TCP or UDP port in network byte order
uint32_t ip; // IP address in network byte order uint32_t ip; // IP address in network byte order
uint8_t ip6[16]; // IPv6 address
bool is_ip6; // True when address is IPv6 address
};
struct mg_mgr {
struct mg_connection *conns; // List of active connections
struct mg_dns dns4; // DNS for IPv4
struct mg_dns dns6; // DNS for IPv6
int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
#if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
}; };
struct mg_connection { struct mg_connection {
@@ -605,13 +623,14 @@ struct mg_connection {
struct mg_mgr *mgr; // Our container struct mg_mgr *mgr; // Our container
struct mg_addr peer; // Remote peer address struct mg_addr peer; // Remote peer address
void *fd; // Connected socket, or LWIP data void *fd; // Connected socket, or LWIP data
unsigned long id; // Auto-incrementing unique connection ID
struct mg_iobuf recv; // Incoming data struct mg_iobuf recv; // Incoming data
struct mg_iobuf send; // Outgoing data struct mg_iobuf send; // Outgoing data
mg_event_handler_t fn; // User-specified event handler function mg_event_handler_t fn; // User-specified event handler function
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
@@ -642,6 +661,8 @@ int mg_printf(struct mg_connection *, const char *fmt, ...);
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
char *mg_straddr(struct mg_connection *, char *, size_t); char *mg_straddr(struct mg_connection *, char *, size_t);
bool mg_socketpair(int *s1, int *s2); bool mg_socketpair(int *s1, int *s2);
bool mg_aton(struct mg_str str, struct mg_addr *addr);
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
@@ -657,30 +678,40 @@ struct mg_http_message {
// GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1 // GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1
// method |-| |----uri---| |--query--| |proto-| // method |-| |----uri---| |--query--| |proto-|
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()
struct mg_http_serve_opts {
const char *root_dir; // Web root directory, must be non-NULL
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
}; };
int mg_http_parse(const char *s, size_t len, struct mg_http_message *); 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_write_head(struct mg_connection *, int code, const char *fmt, ...);
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,
mg_event_handler_t fn, void *fn_data); mg_event_handler_t fn, void *fn_data);
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
const char *path); struct mg_http_serve_opts *);
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *, void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
const char *, const char *mime); const char *, const char *mime, const char *headers);
void mg_http_reply(struct mg_connection *c, int code, const char *fmt, ...); void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
const char *body_fmt, ...);
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name); 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,
@@ -688,13 +719,17 @@ int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
const char *fullpath);
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 *);
@@ -724,7 +759,8 @@ struct mg_ws_message {
struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data, mg_event_handler_t fn, void *fn_data,
const char *fmt, ...); const char *fmt, ...);
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *); void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
const char *fmt, ...);
size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op); size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);
@@ -739,6 +775,25 @@ int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv);
#define MQTT_CMD_CONNECT 1
#define MQTT_CMD_CONNACK 2
#define MQTT_CMD_PUBLISH 3
#define MQTT_CMD_PUBACK 4
#define MQTT_CMD_PUBREC 5
#define MQTT_CMD_PUBREL 6
#define MQTT_CMD_PUBCOMP 7
#define MQTT_CMD_SUBSCRIBE 8
#define MQTT_CMD_SUBACK 9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK 11
#define MQTT_CMD_PINGREQ 12
#define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14
#define MQTT_QOS(qos) ((qos) << 1)
#define MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
#define MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
struct mg_mqtt_opts { struct mg_mqtt_opts {
struct mg_str client_id; struct mg_str client_id;
struct mg_str will_topic; struct mg_str will_topic;
@@ -750,28 +805,69 @@ struct mg_mqtt_opts {
}; };
struct mg_mqtt_message { struct mg_mqtt_message {
struct mg_str topic; struct mg_str topic; // Parsed topic
struct mg_str data; struct mg_str data; // Parsed message
struct mg_str dgram; // Whole MQTT datagram, including headers
uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
uint8_t cmd; // MQTT command, one of MQTT_CMD_*
uint8_t qos; // Quality of service
uint8_t ack; // Connack return code. 0 - success
}; };
struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_mqtt_connect(struct mg_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);
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
void mg_mqtt_pub(struct mg_connection *, struct mg_str *topic, void mg_mqtt_pub(struct mg_connection *, struct mg_str *topic,
struct mg_str *data); struct mg_str *data);
void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic); void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic);
int mg_mqtt_parse(const unsigned char *buf, int len, struct mg_mqtt_message *m); int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len);
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
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 *);
// Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer.
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
struct mg_dns_message { struct mg_dns_message {
uint16_t txnid; // Transaction ID uint16_t txnid; // Transaction ID
uint32_t ipaddr; // Resolved IPv4 address bool resolved; // Resolve successful, addr is set
char name[256]; // Host name struct mg_addr addr; // Resolved address
char name[256]; // Host name
}; };
void mg_resolve(struct mg_mgr *, struct mg_connection *, struct mg_str *, int); struct mg_dns_header {
void mg_resolve_cancel(struct mg_mgr *, struct mg_connection *); uint16_t txnid; // Transaction ID
int mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); uint16_t flags;
uint16_t num_questions;
uint16_t num_answers;
uint16_t num_authority_prs;
uint16_t num_other_prs;
};
// DNS resource record
struct mg_dns_rr {
uint16_t nlen; // Name or pointer length
uint16_t atype; // Address type
uint16_t aclass; // Address class
uint16_t alen; // Address length
};
void mg_resolve(struct mg_connection *, struct mg_str *, int);
void mg_resolve_cancel(struct mg_connection *);
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
bool is_question, struct mg_dns_rr *);
size_t mg_dns_decode_name(const uint8_t *, size_t, size_t, char *, size_t);
+2 -2
View File
@@ -42,10 +42,10 @@
#define MG_ARCH MG_ARCH_NXP_KINETIS #define MG_ARCH MG_ARCH_NXP_KINETIS
#elif defined(PIC32) #elif defined(PIC32)
#define MG_ARCH MG_ARCH_PIC32 #define MG_ARCH MG_ARCH_PIC32
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR) #elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
#define MG_ARCH MG_ARCH_ESP8266 #define MG_ARCH MG_ARCH_ESP8266
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \ #elif defined(TARGET_IS_TM4C129_RA0) || defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2) defined(TARGET_IS_TM4C129_RA2)
#define MG_ARCH MG_ARCH_TM4C129 #define MG_ARCH MG_ARCH_TM4C129
+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
+3 -17
View File
@@ -3,25 +3,11 @@
#if MG_ARCH == MG_ARCH_ESP8266 #if MG_ARCH == MG_ARCH_ESP8266
#include <dirent.h> #include <dirent.h>
#include <esp_system.h>
#include <netdb.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#include <user_interface.h>
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#ifndef PATH_MAX #define MG_INT64_FMT "%lld"
#define PATH_MAX 128
#endif
#define MG_ENABLE_LWIP 1
#define LWIP_TIMEVAL_PRIVATE 0 // struct timeval is defined in sys/time.h
#if LWIP_SOCKET
#include <lwip/sockets.h>
#else
#undef LWIP_SOCKET
#define LWIP_SOCKET 1
#include <lwip/sockets.h> // Source sockaddr_in anyway
#undef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#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
+17 -7
View File
@@ -7,31 +7,40 @@
typedef __int64 int64_t; typedef __int64 int64_t;
typedef unsigned __int64 uint64_t; typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t; typedef unsigned char uint8_t;
typedef char int8_t;
typedef unsigned short uint16_t; typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned int uint32_t; typedef unsigned int uint32_t;
typedef int bool; typedef int int32_t;
enum { false = 0, true = 1 }; typedef enum { false = 0, true = 1 } bool;
#else #else
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <ws2tcpip.h>
#endif #endif
#include <winsock2.h> #include <winsock2.h>
// Protect from calls like std::snprintf in app code
// See https://github.com/cesanta/mongoose/issues/1047
#ifndef __cplusplus
#define snprintf _snprintf #define snprintf _snprintf
#define vsnprintf _vsnprintf #define vsnprintf _vsnprintf
#define strdup(x) _strdup(x) #define strdup(x) _strdup(x)
#define sleep(x) Sleep((x) *1000) #endif
typedef int socklen_t; typedef int socklen_t;
#define PATH_MAX MAX_PATH
#define MG_DIRSEP '\\' #define MG_DIRSEP '\\'
#define inline __inline #ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef EINPROGRESS #ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS #define EINPROGRESS WSAEINPROGRESS
#endif #endif
#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
@@ -39,9 +48,10 @@ typedef int socklen_t;
#define va_copy(x, y) (x) = (y) #define va_copy(x, y) (x) = (y)
#endif #endif
#endif #endif
#ifndef S_ISDIR #ifndef S_ISDIR
#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
+9 -5
View File
@@ -6,7 +6,7 @@
#if MG_ENABLE_LWIP #if MG_ENABLE_LWIP
#define MG_ENABLE_SOCKET 0 #define MG_ENABLE_SOCKET 0
#else #elif !defined(MG_ENABLE_SOCKET)
#define MG_ENABLE_SOCKET 1 #define MG_ENABLE_SOCKET 1
#endif #endif
@@ -22,6 +22,10 @@
#define MG_ENABLE_FS 1 #define MG_ENABLE_FS 1
#endif #endif
#ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 0
#endif
#ifndef MG_ENABLE_IPV6 #ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0 #define MG_ENABLE_IPV6 0
#endif #endif
@@ -42,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
@@ -63,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
+160 -101
View File
@@ -5,15 +5,6 @@
#include "timer.h" #include "timer.h"
#include "util.h" #include "util.h"
struct mg_dns_header {
uint16_t transaction_id;
uint16_t flags;
uint16_t num_questions;
uint16_t num_answers;
uint16_t num_authority_prs;
uint16_t num_other_prs;
};
struct dns_data { struct dns_data {
struct dns_data *next; struct dns_data *next;
struct mg_connection *c; struct mg_connection *c;
@@ -21,82 +12,130 @@ struct dns_data {
uint16_t txnid; uint16_t txnid;
}; };
static void mg_dns_free(struct dns_data **head, struct dns_data *d) { static struct dns_data *s_reqs; // Active DNS requests
LIST_DELETE(struct dns_data, head, d);
// LOG(LL_INFO, ("--> %p %p %d", d, d->c, d->c->fd)); static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int,
struct mg_dns *, bool);
static void mg_dns_free(struct dns_data *d) {
LIST_DELETE(struct dns_data, &s_reqs, d);
free(d); free(d);
} }
void mg_resolve_cancel(struct mg_mgr *mgr, struct mg_connection *c) { void mg_resolve_cancel(struct mg_connection *c) {
struct dns_data *tmp, *d, **head; struct dns_data *tmp, *d;
head = mgr->dnsc == NULL ? NULL : (struct dns_data **) &mgr->dnsc->pfn_data; for (d = s_reqs; d != NULL; d = tmp) {
for (d = head == NULL ? NULL : *head; d != NULL; d = tmp) {
tmp = d->next; tmp = d->next;
if (d->c == c) mg_dns_free(head, d); if (d->c == c) mg_dns_free(d);
} }
} }
static size_t mg_dns_parse_name(const uint8_t *s, const uint8_t *e, size_t off, static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
char *to, size_t tolen, int depth) { char *to, size_t tolen, int depth) {
size_t i = 0, j = 0; size_t i = 0, j = 0;
if (tolen > 0) to[0] = '\0';
if (depth > 5) return 0; if (depth > 5) return 0;
while (&s[off + i + 1] < e && s[off + i] > 0) { while (ofs + i + 1 < len) {
size_t n = s[off + i]; size_t n = s[ofs + i];
if (n & 0xc0) { if (n == 0) {
size_t ptr = (((n & 0x3f) << 8) | s[off + i + 1]) - 12; // 12 is hdr len
if (&s[ptr + 1] < e && (s[ptr] & 0xc0) == 0) {
j = mg_dns_parse_name(s, e, ptr, to, tolen, depth + 1);
}
i++; i++;
break; break;
} }
if (&s[off + i + n + 1] >= e) break; if (n & 0xc0) {
if (j > 0 && j < tolen) to[j++] = '.'; size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len
if (j + n < tolen) memcpy(&to[j], &s[off + i + 1], n); if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 &&
mg_dns_parse_name_depth(s, len, ptr, to, tolen, depth + 1) == 0)
return 0;
i += 2;
break;
}
if (ofs + i + n + 1 >= len) return 0;
if (j > 0) {
if (j < tolen) to[j] = '.';
j++;
}
if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n);
j += n; j += n;
i += n + 1; i += n + 1;
if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk
} }
if (j < tolen) to[j] = '\0'; // Zero-terminate the name if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term
to[tolen - 1] = '\0'; // Just in case
return i; return i;
} }
// txid flags numQ numA numAP numOP size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, char *dst,
// 0000 00 01 81 80 00 01 00 01 00 00 00 00 07 63 65 73 .............ces size_t dstlen) {
// 0010 61 6e 74 61 03 63 6f 6d 00 00 01 00 01 c0 0c 00 anta.com........ return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0);
// 0020 01 00 01 00 00 02 57 00 04 94 fb 36 ec ......W....6. }
int mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
struct mg_dns_header *h = (struct mg_dns_header *) buf; size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
const uint8_t *s = buf + sizeof(*h), *e = &buf[len]; bool is_question, struct mg_dns_rr *rr) {
size_t i, j = 0, n, ok = 0; const struct mg_dns_header *h = (struct mg_dns_header *) buf;
if (len < sizeof(*h)) return ok; const uint8_t *s = buf + ofs, *e = &buf[len];
memset(rr, 0, sizeof(*rr));
if (len < sizeof(*h)) return 0; // Too small, headers dont fit
if (len > 512) return 0; // Too large, we don't expect that
if (s >= e) return 0; // Overflow
if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
return 0;
s += rr->nlen + 4;
if (s > e) return 0;
rr->atype = ((uint16_t) s[-4] << 8) | s[-3];
rr->aclass = ((uint16_t) s[-2] << 8) | s[-1];
if (is_question) return rr->nlen + 4;
s += 6;
if (s > e) return 0;
rr->alen = ((uint16_t) s[-2] << 8) | s[-1];
if (s + rr->alen > e) return 0;
return rr->nlen + rr->alen + 10;
}
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
const struct mg_dns_header *h = (struct mg_dns_header *) buf;
struct mg_dns_rr rr;
size_t i, n, ofs = sizeof(*h);
memset(dm, 0, sizeof(*dm));
if (len < sizeof(*h)) return 0; // Too small, headers dont fit
if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity
if (mg_ntohs(h->num_answers) > 10) return 0; // Sanity
dm->txnid = mg_ntohs(h->txnid);
for (i = 0; i < mg_ntohs(h->num_questions); i++) { for (i = 0; i < mg_ntohs(h->num_questions); i++) {
j += mg_dns_parse_name(s, e, j, dm->name, sizeof(dm->name), 0) + 5; if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false;
// LOG(LL_INFO, ("QUE [%s]", name)); // LOG(LL_INFO, ("Q %zu %zu", ofs, n));
ofs += n;
} }
for (i = 0; i < mg_ntohs(h->num_answers); i++) { for (i = 0; i < mg_ntohs(h->num_answers); i++) {
j += mg_dns_parse_name(s, e, j, dm->name, sizeof(dm->name), 0) + 9; // LOG(LL_INFO, ("A -- %zu %zu %s", ofs, n, dm->name));
// LOG(LL_DEBUG, ("NAME %s", name)); if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false;
if (&s[j] + 2 > e) break; mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name));
n = ((int) s[j] << 8) | s[j + 1]; ofs += n;
if (&s[j] + 2 + n > e) break;
if (n == 4) { if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) {
dm->txnid = mg_ntohs(h->transaction_id); dm->addr.is_ip6 = false;
memcpy(&dm->ipaddr, &s[j + 2], 4); memcpy(&dm->addr.ip, &buf[ofs - 4], 4);
ok = 1; dm->resolved = true;
break; break; // Return success
} else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) {
dm->addr.is_ip6 = true;
memcpy(&dm->addr.ip6, &buf[ofs - 16], 16);
dm->resolved = true;
break; // Return success
} }
j += 2 + n;
} }
return ok; return true;
} }
static void dns_cb(struct mg_connection *c, int ev, void *ev_data, static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
void *fn_data) { void *fn_data) {
struct dns_data *d, *tmp, **head = (struct dns_data **) &c->pfn_data; struct dns_data *d, *tmp;
if (ev == MG_EV_POLL) { if (ev == MG_EV_POLL) {
unsigned long now = *(unsigned long *) ev_data; unsigned long now = *(unsigned long *) ev_data;
for (d = (struct dns_data *) fn_data; d != NULL; d = tmp) { for (d = s_reqs; d != NULL; d = tmp) {
tmp = d->next; tmp = d->next;
// LOG(LL_DEBUG, ("%lu %lu dns poll", d->expire, now)); // LOG(LL_DEBUG, ("%lu %lu dns poll", d->expire, now));
if (now > d->expire) mg_error(d->c, "DNS timeout"); if (now > d->expire) mg_error(d->c, "DNS timeout");
@@ -104,41 +143,62 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
} else if (ev == MG_EV_READ) { } else if (ev == MG_EV_READ) {
struct mg_dns_message dm; struct mg_dns_message dm;
int resolved = 0; int resolved = 0;
if (mg_dns_parse(c->recv.buf, c->recv.len, &dm)) { if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) {
for (d = (struct dns_data *) c->pfn_data; d != NULL; d = tmp) { char *s = mg_hexdump(c->recv.buf, c->recv.len);
LOG(LL_ERROR, ("Unexpected DNS response:\n%s\n", s));
free(s);
} else {
LOG(LL_VERBOSE_DEBUG, ("%s %d", dm.name, dm.resolved));
for (d = s_reqs; d != NULL; d = tmp) {
tmp = d->next; tmp = d->next;
// LOG(LL_INFO, ("d %p %p", d, tmp)); // LOG(LL_INFO, ("d %p %hu %hu", d, d->txnid, dm.txnid));
if (dm.txnid != d->txnid) continue; if (dm.txnid != d->txnid) continue;
if (d->c->is_resolving) { if (d->c->is_resolving) {
d->c->is_resolving = 0; d->c->is_resolving = 0;
d->c->peer.ip = dm.ipaddr; if (dm.resolved) {
mg_connect_resolved(d->c); #if MG_ENABLE_LOG
mg_dns_free(head, d); char buf[100];
#endif
dm.addr.port = d->c->peer.port; // Save port
d->c->peer = dm.addr; // Copy resolved address
LOG(LL_DEBUG, ("%lu %s resolved to %s", d->c->id, dm.name,
mg_ntoa(&d->c->peer, buf, sizeof(buf))));
mg_connect_resolved(d->c);
#if MG_ENABLE_IPV6
} else if (dm.addr.is_ip6 == false && dm.name[0] != '\0') {
struct mg_str x = mg_str(dm.name);
mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true);
#endif
} else {
mg_error(d->c, "%s DNS lookup failed", dm.name);
}
} else { } else {
LOG(LL_ERROR, ("%p already resolved", d->c->fd)); LOG(LL_ERROR, ("%lu already resolved", d->c->id));
} }
mg_dns_free(d);
resolved = 1; resolved = 1;
} }
} }
if (!resolved) LOG(LL_ERROR, ("stray DNS reply")); if (!resolved) LOG(LL_ERROR, ("stray DNS reply"));
c->recv.len = 0; c->recv.len = 0;
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
for (d = *head; d != NULL; d = tmp) { for (d = s_reqs; d != NULL; d = tmp) {
tmp = d->next; tmp = d->next;
mg_dns_free(head, d); mg_dns_free(d);
} }
} }
(void) fn_data;
} }
void mg_dns_send(struct mg_connection *c, const struct mg_str *name, void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
uint16_t txnid) { uint16_t txnid, bool ipv6) {
struct { struct {
struct mg_dns_header header; struct mg_dns_header header;
uint8_t data[256]; uint8_t data[256];
} pkt; } pkt;
size_t i, n; size_t i, n;
memset(&pkt, 0, sizeof(pkt)); memset(&pkt, 0, sizeof(pkt));
pkt.header.transaction_id = mg_htons(txnid); pkt.header.txnid = mg_htons(txnid);
pkt.header.flags = mg_htons(0x100); pkt.header.flags = mg_htons(0x100);
pkt.header.num_questions = mg_htons(1); pkt.header.num_questions = mg_htons(1);
for (i = n = 0; i < sizeof(pkt.data) - 5; i++) { for (i = n = 0; i < sizeof(pkt.data) - 5; i++) {
@@ -151,6 +211,9 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
} }
memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query
n += 5; n += 5;
if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query
// memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query
// n += 6;
mg_send(c, &pkt, sizeof(pkt.header) + n); mg_send(c, &pkt, sizeof(pkt.header) + n);
#if 0 #if 0
// Immediately after A query, send AAAA query. Whatever reply comes first, // Immediately after A query, send AAAA query. Whatever reply comes first,
@@ -161,46 +224,42 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
#endif #endif
} }
void mg_resolve(struct mg_mgr *mgr, struct mg_connection *c, static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
struct mg_str *name, int ms) { struct mg_dns *dnsc, bool ipv6) {
struct dns_data *d = NULL; struct dns_data *d = NULL;
int resolved = mg_aton(name->ptr, &c->peer.ip); if (dnsc->url == NULL) {
mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()");
// Try to parse name as IP address } else if (dnsc->c == NULL) {
if (mg_vcmp(name, "localhost") == 0) { dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL);
resolved = 1; if (dnsc->c != NULL) {
c->peer.ip = mg_htonl(0x7f000001); dnsc->c->pfn = dns_cb;
snprintf(dnsc->c->label, sizeof(dnsc->c->label), "%s", "DNS");
// dnsc->c->is_hexdumping = 1;
}
} }
if (dnsc->c == NULL) {
mg_error(c, "resolver");
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
mg_error(c, "resolve OOM");
} else {
d->txnid = s_reqs ? s_reqs->txnid + 1 : 1;
d->next = s_reqs;
s_reqs = d;
d->expire = mg_millis() + ms;
d->c = c;
c->is_resolving = 1;
LOG(LL_VERBOSE_DEBUG, ("%lu resolving %.*s, txnid %hu", c->id,
(int) name->len, name->ptr, d->txnid));
mg_dns_send(dnsc->c, name, d->txnid, ipv6);
}
}
if (resolved) { void mg_resolve(struct mg_connection *c, struct mg_str *name, int ms) {
if (mg_aton(*name, &c->peer)) {
// name is an IP address, do not fire name resolution // name is an IP address, do not fire name resolution
mg_connect_resolved(c); mg_connect_resolved(c);
} else { } else {
// name is not an IP, send DNS resolution request // name is not an IP, send DNS resolution request
if (mgr->dnsc == NULL) { mg_sendnsreq(c, name, ms, &c->mgr->dns4, false);
const char *srv = mgr->dnsserver ? mgr->dnsserver : "udp://8.8.8.8:53";
mgr->dnsc = mg_connect(mgr, srv, NULL, NULL);
if (mgr->dnsc != NULL) {
mgr->dnsc->pfn = dns_cb;
// mgr->dnsc->is_hexdumping = 1;
snprintf(mgr->dnsc->label, sizeof(mgr->dnsc->label), "%s", "RESOLVER");
}
}
if (mgr->dnsc == NULL) {
mg_error(c, "resolver");
} else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) {
mg_error(c, "resolve OOM");
} else {
struct dns_data **head = (struct dns_data **) &mgr->dnsc->pfn_data;
d->txnid = *head ? (*head)->txnid + 1 : 1;
d->next = *head;
*head = d;
d->expire = mg_millis() + ms;
d->c = c;
c->is_resolving = 1;
LOG(LL_DEBUG, ("%p resolving %.*s, txnid %hu", c->fd, (int) name->len,
name->ptr, d->txnid));
mg_dns_send(mgr->dnsc, name, d->txnid);
}
} }
} }
+31 -6
View File
@@ -3,12 +3,37 @@
#include "net.h" #include "net.h"
#include "str.h" #include "str.h"
// Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer.
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
struct mg_dns_message { struct mg_dns_message {
uint16_t txnid; // Transaction ID uint16_t txnid; // Transaction ID
uint32_t ipaddr; // Resolved IPv4 address bool resolved; // Resolve successful, addr is set
char name[256]; // Host name struct mg_addr addr; // Resolved address
char name[256]; // Host name
}; };
void mg_resolve(struct mg_mgr *, struct mg_connection *, struct mg_str *, int); struct mg_dns_header {
void mg_resolve_cancel(struct mg_mgr *, struct mg_connection *); uint16_t txnid; // Transaction ID
int mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); uint16_t flags;
uint16_t num_questions;
uint16_t num_answers;
uint16_t num_authority_prs;
uint16_t num_other_prs;
};
// DNS resource record
struct mg_dns_rr {
uint16_t nlen; // Name or pointer length
uint16_t atype; // Address type
uint16_t aclass; // Address class
uint16_t alen; // Address length
};
void mg_resolve(struct mg_connection *, struct mg_str *, int);
void mg_resolve_cancel(struct mg_connection *);
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
bool is_question, struct mg_dns_rr *);
size_t mg_dns_decode_name(const uint8_t *, size_t, size_t, char *, size_t);
+1 -1
View File
@@ -14,7 +14,7 @@ void mg_error(struct mg_connection *c, const char *fmt, ...) {
va_start(ap, fmt); va_start(ap, fmt);
mg_vasprintf(&buf, sizeof(mem), fmt, ap); mg_vasprintf(&buf, sizeof(mem), fmt, ap);
va_end(ap); va_end(ap);
LOG(LL_ERROR, ("%p %s", c->fd, buf)); LOG(LL_ERROR, ("%lu %s", c->id, buf));
mg_call(c, MG_EV_ERROR, buf); mg_call(c, MG_EV_ERROR, buf);
if (buf != mem) free(buf); if (buf != mem) free(buf);
c->is_closing = 1; c->is_closing = 1;
+18 -27
View File
@@ -3,35 +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 NULL MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
MG_EV_WS_MSG, // Websocket message received struct mg_ws_message * MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_MQTT_MSG, // MQTT message struct mg_mqtt_message * MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_SNTP_TIME, // SNTP time received struct timeval * MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_USER, // Starting ID for user events MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events
}; };
+346 -156
View File
@@ -1,18 +1,14 @@
#include "http.h"
#include "arch.h" #include "arch.h"
#include "base64.h" #include "base64.h"
#include "http.h"
#include "log.h" #include "log.h"
#include "net.h" #include "net.h"
#include "private.h" #include "private.h"
#include "ssi.h"
#include "util.h" #include "util.h"
#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
@@ -34,7 +30,7 @@ void mg_http_bauth(struct mg_connection *c, const char *user,
c->send.len += 21 + n + 2; c->send.len += 21 + n + 2;
memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2); memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2);
} else { } else {
LOG(LL_ERROR, ("%p %s cannot resize iobuf %d->%d ", c->fd, c->label, LOG(LL_ERROR, ("%lu %s cannot resize iobuf %d->%d ", c->id, c->label,
(int) c->send.size, (int) need)); (int) c->send.size, (int) need));
} }
} }
@@ -62,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;
} }
} }
@@ -90,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) {
@@ -105,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;
} }
@@ -149,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) {
@@ -169,7 +169,7 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
mg_http_parse_headers(s, end, hm->headers, mg_http_parse_headers(s, end, hm->headers,
sizeof(hm->headers) / sizeof(hm->headers[0])); sizeof(hm->headers) / sizeof(hm->headers[0]));
if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) { if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
hm->body.len = (size_t) mg_to64(cl->ptr); hm->body.len = (size_t) mg_to64(*cl);
hm->message.len = req_len + hm->body.len; hm->message.len = req_len + hm->body.len;
} }
@@ -193,6 +193,13 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
hm->message.len = req_len; hm->message.len = req_len;
} }
// The 204 (No content) responses also have 0 body length
if (hm->body.len == (size_t) ~0 && is_response &&
mg_vcasecmp(&hm->uri, "204") == 0) {
hm->body.len = 0;
hm->message.len = req_len;
}
return req_len; return req_len;
} }
@@ -219,34 +226,31 @@ void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) {
mg_send(c, "\r\n", 2); mg_send(c, "\r\n", 2);
} }
void mg_http_reply(struct mg_connection *c, int code, const char *fmt, ...) { void mg_http_reply(struct mg_connection *c, int code, const char *headers,
const char *fmt, ...) {
char mem[100], *buf = mem; char mem[100], *buf = mem;
va_list ap; va_list ap;
int len; int len;
va_start(ap, fmt); va_start(ap, fmt);
len = mg_vasprintf(&buf, sizeof(mem), fmt, ap); len = mg_vasprintf(&buf, sizeof(mem), fmt, ap);
va_end(ap); va_end(ap);
mg_printf(c, mg_printf(c, "HTTP/1.1 %d OK\r\n%sContent-Length: %d\r\n\r\n", code,
"HTTP/1.1 %d OK\r\nContent-Type: text/plain\r\n" headers == NULL ? "" : headers, len);
"Content-Length: %d\r\n\r\n",
code, len);
mg_send(c, buf, len); mg_send(c, buf, len);
if (buf != mem) free(buf); if (buf != mem) free(buf);
} }
#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);
} }
#if MG_ENABLE_FS char *mg_http_etag(char *buf, size_t len, mg_stat_t *st) {
char *mg_http_etag(char *buf, size_t len, struct stat *st) { snprintf(buf, len, "\"%lx." MG_INT64_FMT "\"", (unsigned long) st->st_mtime,
snprintf(buf, len, "\"%lx.%lu\"", (unsigned long) st->st_mtime, (int64_t) st->st_size);
(unsigned long) st->st_size);
return buf; return buf;
} }
@@ -256,7 +260,7 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
mg_http_get_var(&hm->query, "offset", offset, sizeof(offset)); mg_http_get_var(&hm->query, "offset", offset, sizeof(offset));
mg_http_get_var(&hm->query, "name", name, sizeof(name)); mg_http_get_var(&hm->query, "name", name, sizeof(name));
if (name[0] == '\0') { if (name[0] == '\0') {
mg_http_reply(c, 400, "%s", "name required"); mg_http_reply(c, 400, "", "%s", "name required");
return -1; return -1;
} else { } else {
FILE *fp; FILE *fp;
@@ -264,13 +268,13 @@ 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 {
fwrite(hm->body.ptr, 1, hm->body.len, fp); fwrite(hm->body.ptr, 1, hm->body.len, fp);
fclose(fp); fclose(fp);
mg_http_reply(c, 200, ""); mg_http_reply(c, 200, "", "");
return hm->body.len; return hm->body.len;
} }
} }
@@ -279,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) {
@@ -294,37 +298,92 @@ static void static_cb(struct mg_connection *c, int ev, void *ev_data,
} }
static const char *guess_content_type(const char *filename) { static const char *guess_content_type(const char *filename) {
int n = (int) strlen(filename); size_t n = strlen(filename);
if (mg_globmatch("#.html", 6, filename, n)) return "text/html"; #define MIME_ENTRY(_ext, _type) \
if (mg_globmatch("#.css", 5, filename, n)) return "text/css"; { _ext, sizeof(_ext) - 1, _type }
if (mg_globmatch("#.js", 4, filename, n)) return "text/javascript"; const struct {
return "text/plain"; const char *ext;
size_t ext_len;
const char *type;
} * t, types[] = {
MIME_ENTRY("html", "text/html; charset=utf-8"),
MIME_ENTRY("htm", "text/html; charset=utf-8"),
MIME_ENTRY("shtml", "text/html; charset=utf-8"),
MIME_ENTRY("css", "text/css"),
MIME_ENTRY("js", "text/javascript"),
MIME_ENTRY("mjs", "text/javascript"),
MIME_ENTRY("json", "application/json"),
MIME_ENTRY("ico", "image/x-icon"),
MIME_ENTRY("gif", "image/gif"),
MIME_ENTRY("jpg", "image/jpeg"),
MIME_ENTRY("jpeg", "image/jpeg"),
MIME_ENTRY("png", "image/png"),
MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("wav", "audio/wav"),
MIME_ENTRY("mp3", "audio/mpeg"),
MIME_ENTRY("mid", "audio/mid"),
MIME_ENTRY("ogg", "application/ogg"),
MIME_ENTRY("xml", "application/xml"),
MIME_ENTRY("ttf", "font/ttf"),
MIME_ENTRY("json", "application/json"),
MIME_ENTRY("xsl", "application/xml"),
MIME_ENTRY("doc", "application/msword"),
MIME_ENTRY("exe", "application/octet-stream"),
MIME_ENTRY("zip", "application/zip"),
MIME_ENTRY("xls", "application/excel"),
MIME_ENTRY("tgz", "application/tar-gz"),
MIME_ENTRY("tar", "application/tar"),
MIME_ENTRY("gz", "application/gzip"),
MIME_ENTRY("rar", "application/rar"),
MIME_ENTRY("rtf", "application/rtf"),
MIME_ENTRY("pdf", "application/pdf"),
MIME_ENTRY("mpg", "video/mpeg"),
MIME_ENTRY("webm", "video/webm"),
MIME_ENTRY("mpeg", "video/mpeg"),
MIME_ENTRY("mov", "video/quicktime"),
MIME_ENTRY("mp4", "video/mp4"),
MIME_ENTRY("avi", "video/x-msvideo"),
MIME_ENTRY("csv", "text/csv"),
MIME_ENTRY("bmp", "image/bmp"),
MIME_ENTRY("bin", "application/octet-stream"),
MIME_ENTRY("wasm", "application/wasm"),
{NULL, 0, NULL},
};
for (t = types; t->ext != NULL; t++) {
if (n < t->ext_len + 2) continue;
if (mg_ncasecmp(t->ext, &filename[n - t->ext_len], t->ext_len)) continue;
return t->type;
}
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 *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) {
mg_http_reply(c, 404, "%s", "Not found\n"); 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");
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);
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\r\n", "Etag: %s\r\nContent-Length: " MG_INT64_FMT "\r\n%s\r\n",
mime, etag, (unsigned long) st.st_size); 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));
d->fp = fp;
d->old_pfn_data = c->pfn_data;
c->pfn = static_cb; c->pfn = static_cb;
c->pfn_data = d; c->pfn_data = fp;
} }
} }
} }
@@ -333,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
} }
@@ -367,7 +428,6 @@ typedef struct win32_dir {
int gettimeofday(struct timeval *tv, void *tz) { int gettimeofday(struct timeval *tv, void *tz) {
FILETIME ft; FILETIME ft;
unsigned __int64 tmpres = 0; unsigned __int64 tmpres = 0;
static int tzflag = 0;
if (tv != NULL) { if (tv != NULL) {
GetSystemTimeAsFileTime(&ft); GetSystemTimeAsFileTime(&ft);
@@ -379,6 +439,7 @@ int gettimeofday(struct timeval *tv, void *tz) {
tv->tv_sec = (long) (tmpres / 1000000UL); tv->tv_sec = (long) (tmpres / 1000000UL);
tv->tv_usec = (long) (tmpres % 1000000UL); tv->tv_usec = (long) (tmpres % 1000000UL);
} }
(void) tz;
return 0; return 0;
} }
@@ -410,7 +471,7 @@ DIR *opendir(const char *name) {
if (name == NULL) { if (name == NULL) {
SetLastError(ERROR_BAD_ARGUMENTS); SetLastError(ERROR_BAD_ARGUMENTS);
} else if ((d = malloc(sizeof(*d))) == NULL) { } else if ((d = (DIR *) malloc(sizeof(*d))) == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY); SetLastError(ERROR_NOT_ENOUGH_MEMORY);
} else { } else {
to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0]));
@@ -462,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) {
@@ -482,96 +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( " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
c, "<td>%s</td><td>%s</td></tr>\n",
"<tr><td><a href=\"%.*s%s%s\">%s%s</a></td><td>%s</td><td>%s</td></tr>", 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';
// LOG(LL_DEBUG, ("%p [%s]", c->fd, dir));
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>", "<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, ("%p stat(%s): %d", c->fd, path, errno)); LOG(LL_ERROR, ("%s truncated", dp->d_name));
continue; } else if (mg_stat(path, &st) != 0) {
LOG(LL_ERROR, ("%lu stat(%s): %d", c->id, path, errno));
} 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>", 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, ("%p opendir(%s) -> %d", c->fd, 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,
const char *dir) { 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(dir, root) == NULL)
LOG(LL_DEBUG, ("realpath(%s): %d", 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 {
// 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;
size_t n = snprintf(path, sizeof(path), "%s%.*s", root, (int) hm->uri.len, size_t n1 = strlen(t1), n2;
hm->uri.ptr);
while (n > 0 && n < sizeof(path) && path[n - 1] == MG_DIRSEP) path[--n] = 0; mg_url_decode(hm->uri.ptr, hm->uri.len, t1 + n1, sizeof(t1) - n1, 0);
if (realpath(path, real) == NULL) t1[sizeof(t1) - 1] = '\0';
LOG(LL_DEBUG, ("realpath(%s): %d", dir, errno)); n2 = strlen(t1);
// LOG(LL_INFO, ("PATH: [%s] REAL: [%s]", path, real)); while (n2 > 0 && t1[n2 - 1] == '/') t1[--n2] = 0;
if (mg_is_dir(real)) {
strncat(real, "/index.html", sizeof(real) - strlen(real) - 1); if (realpath(t1, t2) == NULL) {
LOG(LL_ERROR, ("realpath(%s): %d", t1, errno));
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_HTTP_DEBUG_ENDPOINT #if MG_ENABLE_SSI
snprintf(c->label, sizeof(c->label) - 1, "<-F %s", real); if (is_index && fp == NULL) {
char *p = t2 + strlen(t2);
while (p > t2 && p[-1] != '/') p--;
strncpy(p, "index.shtml", &t2[sizeof(t2)] - p - 2);
t2[sizeof(t2) - 1] = '\0';
fp = mg_fopen(t2, "r");
}
#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
#if MG_ENABLE_SSI
} else if (opts->ssi_pattern != NULL &&
mg_globmatch(opts->ssi_pattern, strlen(opts->ssi_pattern), t2,
strlen(t2))) {
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)); mg_http_serve_file(c, hm, t2, guess_content_type(t2), NULL);
} }
if (fp != NULL) fclose(fp); if (fp != NULL) fclose(fp);
} }
@@ -579,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");
@@ -612,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, ("%p HTTP parse error", c->fd)); 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
snprintf(c->label, sizeof(c->label) - 1, "<-LSN");
#endif
return c; return c;
} }
+17 -7
View File
@@ -13,30 +13,40 @@ struct mg_http_message {
// GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1 // GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1
// method |-| |----uri---| |--query--| |proto-| // method |-| |----uri---| |--query--| |proto-|
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()
struct mg_http_serve_opts {
const char *root_dir; // Web root directory, must be non-NULL
const char *ssi_pattern; // SSI filename pattern, e.g. #.shtml
}; };
int mg_http_parse(const char *s, size_t len, struct mg_http_message *); 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_write_head(struct mg_connection *, int code, const char *fmt, ...);
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,
mg_event_handler_t fn, void *fn_data); mg_event_handler_t fn, void *fn_data);
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
const char *path); struct mg_http_serve_opts *);
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *, void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
const char *, const char *mime); const char *, const char *mime, const char *headers);
void mg_http_reply(struct mg_connection *c, int code, const char *fmt, ...); void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
const char *body_fmt, ...);
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name); 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 -1
View File
@@ -14,7 +14,7 @@ static void (*s_fn)(const void *, int, void *) = mg_log_stdout;
static void *s_fn_param = NULL; static void *s_fn_param = NULL;
void mg_log_set(const char *spec) { void mg_log_set(const char *spec) {
LOG(LL_INFO, ("Setting log level to %s", spec)); LOG(LL_DEBUG, ("Setting log level to %s", spec));
s_spec = spec; s_spec = spec;
} }
+3 -7
View File
@@ -87,7 +87,7 @@ int mg_send(struct mg_connection *c, const void *buf, size_t len) {
err_t err = udp_send(pcb, p); err_t err = udp_send(pcb, p);
pbuf_free(p); pbuf_free(p);
LOG(LL_DEBUG, LOG(LL_DEBUG,
("%p UDP %d bytes -> %x:%hu, err %ld", c->fd, (int) len, ("%lu UDP %d bytes -> %x:%hu, err %ld", c->id, (int) len,
(unsigned) *(uint32_t *) &pcb->remote_ip, pcb->remote_port, err)); (unsigned) *(uint32_t *) &pcb->remote_ip, pcb->remote_port, err));
if (err != ERR_OK) mg_error(c, "%p err %d", c->fd, err); if (err != ERR_OK) mg_error(c, "%p err %d", c->fd, err);
} else { } else {
@@ -146,7 +146,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
tcp_bind(c->fd, IP_ADDR_ANY, 0); tcp_bind(c->fd, IP_ADDR_ANY, 0);
tcp_nagle_disable((struct tcp_pcb *) c->fd); tcp_nagle_disable((struct tcp_pcb *) c->fd);
} }
LOG(LL_DEBUG, ("%p -> %s %s", c->fd, url, c->is_udp ? "UDP" : "TCP")); LOG(LL_DEBUG, ("%lu -> %s %s", c->id, url, c->is_udp ? "UDP" : "TCP"));
mg_resolve(mgr, c, &host, mgr->dnstimeout); mg_resolve(mgr, c, &host, mgr->dnstimeout);
return c; return c;
} }
@@ -156,7 +156,7 @@ void mg_connect_resolved(struct mg_connection *c) {
ip_addr_t ipaddr; ip_addr_t ipaddr;
memcpy(&ipaddr, &c->peer.ip, sizeof(ipaddr)); memcpy(&ipaddr, &c->peer.ip, sizeof(ipaddr));
mg_call(c, MG_EV_RESOLVE, NULL); mg_call(c, MG_EV_RESOLVE, NULL);
LOG(LL_DEBUG, ("%p resolved to %s", c->fd, mg_straddr(c, buf, sizeof(buf)))); LOG(LL_DEBUG, ("%lu resolved to %s", c->id, mg_straddr(c, buf, sizeof(buf))));
err_t err = c->is_udp ? udp_connect((struct udp_pcb *) c->fd, &ipaddr, err_t err = c->is_udp ? udp_connect((struct udp_pcb *) c->fd, &ipaddr,
mg_ntohs(c->peer.port)) mg_ntohs(c->peer.port))
: tcp_connect((struct tcp_pcb *) c->fd, &ipaddr, : tcp_connect((struct tcp_pcb *) c->fd, &ipaddr,
@@ -194,8 +194,4 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
mg_usleep(200 * 1000); mg_usleep(200 * 1000);
mg_timer_poll(mg_millis()); mg_timer_poll(mg_millis());
} }
void mg_mgr_init(struct mg_mgr *mgr) {
(void) mgr;
}
#endif #endif
+2 -2
View File
@@ -1,5 +1,5 @@
#include "md5.h"
#include <string.h> #include <string.h>
#include "md5.h"
#if MG_ENABLE_MD5 #if MG_ENABLE_MD5
#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER) #if !defined(BYTE_ORDER) && defined(__BYTE_ORDER)
@@ -50,7 +50,7 @@ void mg_md5_init(mg_md5_ctx *ctx) {
} }
static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) { static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) {
register uint32_t a, b, c, d; uint32_t a, b, c, d;
a = buf[0]; a = buf[0];
b = buf[1]; b = buf[1];
+92 -79
View File
@@ -1,27 +1,12 @@
#include "mqtt.h"
#include "arch.h" #include "arch.h"
#include "base64.h" #include "base64.h"
#include "event.h" #include "event.h"
#include "log.h" #include "log.h"
#include "mqtt.h"
#include "private.h" #include "private.h"
#include "url.h" #include "url.h"
#include "util.h" #include "util.h"
#define MQTT_CMD_CONNECT 1
#define MQTT_CMD_CONNACK 2
#define MQTT_CMD_PUBLISH 3
#define MQTT_CMD_PUBACK 4
#define MQTT_CMD_PUBREC 5
#define MQTT_CMD_PUBREL 6
#define MQTT_CMD_PUBCOMP 7
#define MQTT_CMD_SUBSCRIBE 8
#define MQTT_CMD_SUBACK 9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK 11
#define MQTT_CMD_PINGREQ 12
#define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14
#define MQTT_CLEAN_SESSION 0x02 #define MQTT_CLEAN_SESSION 0x02
#define MQTT_HAS_WILL 0x04 #define MQTT_HAS_WILL 0x04
#define MQTT_WILL_RETAIN 0x20 #define MQTT_WILL_RETAIN 0x20
@@ -29,20 +14,11 @@
#define MQTT_HAS_USER_NAME 0x80 #define MQTT_HAS_USER_NAME 0x80
#define MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3) #define MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3)
#define MQTT_SET_WILL_QOS(flags, qos) (flags) = ((flags) & ~0x18) | ((qos) << 3) #define MQTT_SET_WILL_QOS(flags, qos) (flags) = ((flags) & ~0x18) | ((qos) << 3)
#define MQTT_QOS(qos) ((qos) << 1)
#define MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
#define MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
struct mqtt_message { void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags,
size_t len, topic_offset, topic_len, data_offset, data_len; uint32_t len) {
uint16_t id;
uint8_t cmd, qos, connack_ret_code;
};
static void mqtt_send_header(struct mg_connection *c, uint8_t cmd,
uint8_t flags, uint32_t len) {
uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; uint8_t buf[1 + sizeof(len)], *vlen = &buf[1];
buf[0] = (cmd << 4) | flags; buf[0] = (cmd << 4) | flags;
do { do {
@@ -82,7 +58,7 @@ static void mqtt_login(struct mg_connection *c, const char *url,
if (opts->will_retain) flags |= MQTT_WILL_RETAIN; if (opts->will_retain) flags |= MQTT_WILL_RETAIN;
total_len += (uint32_t) opts->client_id.len; total_len += (uint32_t) opts->client_id.len;
mqtt_send_header(c, MQTT_CMD_CONNECT, 0, total_len); mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, total_len);
mg_send(c, "\00\04MQTT\04", 7); mg_send(c, "\00\04MQTT\04", 7);
mg_send(c, &flags, 1); mg_send(c, &flags, 1);
// keepalive == 0 means "do not disconnect us!" // keepalive == 0 means "do not disconnect us!"
@@ -109,10 +85,10 @@ void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
struct mg_str *data) { struct mg_str *data) {
uint8_t flags = MQTT_QOS(1); uint8_t flags = MQTT_QOS(1);
uint32_t total_len = 2 + (uint32_t) topic->len + (uint32_t) data->len; uint32_t total_len = 2 + (uint32_t) topic->len + (uint32_t) data->len;
LOG(LL_DEBUG, ("%p [%.*s] -> [%.*s]", c->fd, (int) topic->len, LOG(LL_DEBUG, ("%lu [%.*s] -> [%.*s]", c->id, (int) topic->len,
(char *) topic->ptr, (int) data->len, (char *) data->ptr)); (char *) topic->ptr, (int) data->len, (char *) data->ptr));
if (MQTT_GET_QOS(flags) > 0) total_len += 2; if (MQTT_GET_QOS(flags) > 0) total_len += 2;
mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, total_len); mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, total_len);
mg_send_u16(c, mg_htons((uint16_t) topic->len)); mg_send_u16(c, mg_htons((uint16_t) topic->len));
mg_send(c, topic->ptr, topic->len); mg_send(c, topic->ptr, topic->len);
if (MQTT_GET_QOS(flags) > 0) { if (MQTT_GET_QOS(flags) > 0) {
@@ -127,7 +103,8 @@ void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic) {
static uint16_t s_id; static uint16_t s_id;
uint8_t qos = 1; uint8_t qos = 1;
uint32_t total_len = 2 + (uint32_t) topic->len + 2 + 1; uint32_t total_len = 2 + (uint32_t) topic->len + 2 + 1;
mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, (uint8_t) MQTT_QOS(qos), total_len); mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, (uint8_t) MQTT_QOS(qos),
total_len);
if (++s_id == 0) ++s_id; if (++s_id == 0) ++s_id;
mg_send_u16(c, mg_htons(s_id)); mg_send_u16(c, mg_htons(s_id));
mg_send_u16(c, mg_htons((uint16_t) topic->len)); mg_send_u16(c, mg_htons((uint16_t) topic->len));
@@ -135,32 +112,33 @@ void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic) {
mg_send(c, &qos, sizeof(qos)); mg_send(c, &qos, sizeof(qos));
} }
static int parse(const uint8_t *in, size_t inlen, struct mqtt_message *m) { int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m) {
uint8_t lc = 0, *p, *end; uint8_t lc = 0, *p, *end;
uint32_t len = 0, len_len = 0; uint32_t n = 0, len_len = 0;
if (inlen < 2) return MQTT_INCOMPLETE; memset(m, 0, sizeof(*m));
m->cmd = in[0] >> 4; m->dgram.ptr = (char *) buf;
m->qos = (in[0] >> 1) & 3; if (len < 2) return MQTT_INCOMPLETE;
m->cmd = buf[0] >> 4;
m->qos = (buf[0] >> 1) & 3;
len = len_len = 0; n = len_len = 0;
p = (uint8_t *) in + 1; p = (uint8_t *) buf + 1;
while ((size_t)(p - in) < inlen) { while ((size_t)(p - buf) < len) {
lc = *((uint8_t *) p++); lc = *((uint8_t *) p++);
len += (lc & 0x7f) << 7 * len_len; n += (lc & 0x7f) << 7 * len_len;
len_len++; len_len++;
if (!(lc & 0x80)) break; if (!(lc & 0x80)) break;
if (len_len >= 4) return MQTT_MALFORMED; if (len_len >= 4) return MQTT_MALFORMED;
} }
end = p + n;
end = p + len; if (lc & 0x80 || end > buf + len) return MQTT_INCOMPLETE;
if (lc & 0x80 || end > in + inlen) return MQTT_INCOMPLETE; m->dgram.len = end - buf;
m->len = (int) (end - in);
switch (m->cmd) { switch (m->cmd) {
case MQTT_CMD_CONNACK: case MQTT_CMD_CONNACK:
if (end - p < 2) return MQTT_MALFORMED; if (end - p < 2) return MQTT_MALFORMED;
m->connack_ret_code = p[1]; m->ack = p[1];
break; break;
case MQTT_CMD_PUBACK: case MQTT_CMD_PUBACK:
case MQTT_CMD_PUBREC: case MQTT_CMD_PUBREC:
@@ -170,11 +148,17 @@ static int parse(const uint8_t *in, size_t inlen, struct mqtt_message *m) {
if (p + 2 > end) return MQTT_MALFORMED; if (p + 2 > end) return MQTT_MALFORMED;
m->id = (p[0] << 8) | p[1]; m->id = (p[0] << 8) | p[1];
break; break;
case MQTT_CMD_SUBSCRIBE: {
if (p + 2 > end) return MQTT_MALFORMED;
m->id = (p[0] << 8) | p[1];
p += 2;
break;
}
case MQTT_CMD_PUBLISH: { case MQTT_CMD_PUBLISH: {
if (p + 2 > end) return MQTT_MALFORMED; if (p + 2 > end) return MQTT_MALFORMED;
m->topic_len = (p[0] << 8) | p[1]; m->topic.len = (p[0] << 8) | p[1];
m->topic_offset = p + 2 - in; m->topic.ptr = (char *) p + 2;
p += 2 + m->topic_len; p += 2 + m->topic.len;
if (p > end) return MQTT_MALFORMED; if (p > end) return MQTT_MALFORMED;
if (m->qos > 0) { if (m->qos > 0) {
if (p + 2 > end) return MQTT_MALFORMED; if (p + 2 > end) return MQTT_MALFORMED;
@@ -182,8 +166,9 @@ static int parse(const uint8_t *in, size_t inlen, struct mqtt_message *m) {
p += 2; p += 2;
} }
if (p > end) return MQTT_MALFORMED; if (p > end) return MQTT_MALFORMED;
m->data_offset = p - in; m->data.ptr = (char *) p;
m->data_len = end - p; m->data.len = end - p;
break;
} }
default: default:
break; break;
@@ -191,55 +176,64 @@ static int parse(const uint8_t *in, size_t inlen, struct mqtt_message *m) {
return MQTT_OK; return MQTT_OK;
} }
int mg_mqtt_parse(const unsigned char *s, int n, struct mg_mqtt_message *mm) { static int mg_mqtt_next_topic(struct mg_mqtt_message *msg, struct mg_str *topic,
struct mqtt_message m; uint8_t *qos, int pos) {
int rc = parse(s, n, &m); unsigned char *buf = (unsigned char *) msg->dgram.ptr + pos;
if (rc == MQTT_OK) { int new_pos;
mm->topic.ptr = (char *) s + m.topic_offset; if ((size_t) pos >= msg->dgram.len) return -1;
mm->topic.len = m.topic_len;
mm->data.ptr = (char *) s + m.data_offset; topic->len = buf[0] << 8 | buf[1];
mm->data.len = m.data_len; topic->ptr = (char *) buf + 2;
} new_pos = pos + 2 + topic->len + (qos == NULL ? 0 : 1);
return rc; if ((size_t) new_pos > msg->dgram.len) return -1;
if (qos != NULL) *qos = buf[2 + topic->len];
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) {
struct mqtt_message m;
memset(&m, 0, sizeof(m));
for (;;) { for (;;) {
int rc = parse(c->recv.buf, c->recv.len, &m); struct mg_mqtt_message mm;
int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, &mm);
if (rc == MQTT_MALFORMED) { if (rc == MQTT_MALFORMED) {
LOG(LL_ERROR, ("%p MQTT malformed message", c->fd)); LOG(LL_ERROR, ("%lu MQTT malformed message", c->id));
c->is_closing = 1; c->is_closing = 1;
break; break;
} else if (rc == MQTT_OK) { } else if (rc == MQTT_OK) {
LOG(LL_VERBOSE_DEBUG, LOG(LL_VERBOSE_DEBUG,
("%p MQTT CMD %d len %d [%.*s]", c->fd, m.cmd, (int) m.len, ("%p MQTT CMD %d len %d [%.*s]", c->fd, mm.cmd, (int) mm.dgram.len,
(int) m.data_len, (char *) c->recv.buf + m.data_offset)); (int) mm.data.len, mm.data.ptr));
switch (m.cmd) { switch (mm.cmd) {
case MQTT_CMD_CONNACK: case MQTT_CMD_CONNACK:
mg_call(c, MG_EV_MQTT_OPEN, &m.connack_ret_code); mg_call(c, MG_EV_MQTT_OPEN, &mm.ack);
if (m.connack_ret_code == 0) { if (mm.ack == 0) {
LOG(LL_INFO, ("%p Connected", c->fd)); LOG(LL_INFO, ("%lu Connected", c->id));
} else { } else {
LOG(LL_ERROR, LOG(LL_ERROR, ("%lu MQTT auth failed, code %d", c->id, mm.ack));
("%p MQTT auth failed, code %d", c->fd, m.connack_ret_code));
c->is_closing = 1; c->is_closing = 1;
} }
break; break;
case MQTT_CMD_PUBLISH: { case MQTT_CMD_PUBLISH: {
struct mg_mqtt_message evd = { LOG(LL_DEBUG, ("%lu [%.*s] -> [%.*s]", c->id, (int) mm.topic.len,
{(char *) c->recv.buf + m.topic_offset, m.topic_len}, mm.topic.ptr, (int) mm.data.len, mm.data.ptr));
{(char *) c->recv.buf + m.data_offset, m.data_len}}; mg_call(c, MG_EV_MQTT_MSG, &mm);
LOG(LL_DEBUG, ("%p [%.*s] -> [%.*s]", c->fd, (int) evd.topic.len,
evd.topic.ptr, (int) evd.data.len, evd.data.ptr));
mg_call(c, MG_EV_MQTT_MSG, &evd);
break; break;
} }
} }
mg_iobuf_delete(&c->recv, m.len); mg_call(c, MG_EV_MQTT_CMD, &mm);
mg_iobuf_delete(&c->recv, mm.dgram.len);
} else { } else {
break; break;
} }
@@ -249,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) {
@@ -261,3 +267,10 @@ struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
} }
return c; return c;
} }
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data) {
struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr;
return c;
}
+38 -3
View File
@@ -3,6 +3,25 @@
#include "net.h" #include "net.h"
#include "str.h" #include "str.h"
#define MQTT_CMD_CONNECT 1
#define MQTT_CMD_CONNACK 2
#define MQTT_CMD_PUBLISH 3
#define MQTT_CMD_PUBACK 4
#define MQTT_CMD_PUBREC 5
#define MQTT_CMD_PUBREL 6
#define MQTT_CMD_PUBCOMP 7
#define MQTT_CMD_SUBSCRIBE 8
#define MQTT_CMD_SUBACK 9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK 11
#define MQTT_CMD_PINGREQ 12
#define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14
#define MQTT_QOS(qos) ((qos) << 1)
#define MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
#define MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)
struct mg_mqtt_opts { struct mg_mqtt_opts {
struct mg_str client_id; struct mg_str client_id;
struct mg_str will_topic; struct mg_str will_topic;
@@ -14,14 +33,30 @@ struct mg_mqtt_opts {
}; };
struct mg_mqtt_message { struct mg_mqtt_message {
struct mg_str topic; struct mg_str topic; // Parsed topic
struct mg_str data; struct mg_str data; // Parsed message
struct mg_str dgram; // Whole MQTT datagram, including headers
uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
uint8_t cmd; // MQTT command, one of MQTT_CMD_*
uint8_t qos; // Quality of service
uint8_t ack; // Connack return code. 0 - success
}; };
struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_mqtt_connect(struct mg_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);
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
void mg_mqtt_pub(struct mg_connection *, struct mg_str *topic, void mg_mqtt_pub(struct mg_connection *, struct mg_str *topic,
struct mg_str *data); struct mg_str *data);
void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic); void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic);
int mg_mqtt_parse(const unsigned char *buf, int len, struct mg_mqtt_message *m); int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len);
int mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
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 *);
+116 -4
View File
@@ -1,5 +1,5 @@
#include "net.h"
#include "log.h" #include "log.h"
#include "net.h"
#include "util.h" #include "util.h"
int mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) { int mg_vprintf(struct mg_connection *c, const char *fmt, va_list ap) {
@@ -20,8 +20,120 @@ int mg_printf(struct mg_connection *c, const char *fmt, ...) {
} }
char *mg_straddr(struct mg_connection *c, char *buf, size_t len) { char *mg_straddr(struct mg_connection *c, char *buf, size_t len) {
unsigned char *p = (unsigned char *) &c->peer.ip; char tmp[100];
snprintf(buf, len, "%d.%d.%d.%d:%hu", p[0], p[1], p[2], p[3], const char *fmt = c->peer.is_ip6 ? "[%s]:%d" : "%s:%d";
mg_ntohs(c->peer.port)); mg_ntoa(&c->peer, tmp, sizeof(tmp));
snprintf(buf, len, fmt, tmp, (int) mg_ntohs(c->peer.port));
return buf; return buf;
} }
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len) {
if (addr->is_ip6) {
uint16_t *p = (uint16_t *) addr->ip6;
snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x", mg_htons(p[0]),
mg_htons(p[1]), mg_htons(p[2]), mg_htons(p[3]), mg_htons(p[4]),
mg_htons(p[5]), mg_htons(p[6]), mg_htons(p[7]));
} else {
uint8_t p[4];
memcpy(p, &addr->ip, sizeof(p));
snprintf(buf, len, "%d.%d.%d.%d", (int) p[0], (int) p[1], (int) p[2],
(int) p[3]);
}
return buf;
}
static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
if (mg_vcasecmp(&str, "localhost") != 0) return false;
addr->ip = mg_htonl(0x7f000001);
addr->is_ip6 = false;
return true;
}
static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
uint8_t data[4] = {0, 0, 0, 0};
size_t i, num_dots = 0;
for (i = 0; i < str.len; i++) {
if (str.ptr[i] >= '0' && str.ptr[i] <= '9') {
int octet = data[num_dots] * 10 + (str.ptr[i] - '0');
if (octet > 255) return false;
data[num_dots] = octet;
} else if (str.ptr[i] == '.') {
if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false;
num_dots++;
} else {
return false;
}
}
if (num_dots != 3 || str.ptr[i - 1] == '.') return false;
memcpy(&addr->ip, data, sizeof(data));
addr->is_ip6 = false;
return true;
}
static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
size_t i, j = 0, n = 0, dc = 42;
for (i = 0; i < str.len; i++) {
if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') ||
(str.ptr[i] >= 'a' && str.ptr[i] <= 'f') ||
(str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) {
unsigned long val;
if (i > j + 3) return false;
// LOG(LL_DEBUG, ("%zu %zu [%.*s]", i, j, (int) (i - j + 1),
// &str.ptr[j]));
val = mg_unhexn(&str.ptr[j], i - j + 1);
addr->ip6[n] = (uint8_t)((val >> 8) & 255);
addr->ip6[n + 1] = (uint8_t)(val & 255);
} else if (str.ptr[i] == ':') {
j = i + 1;
if (i > 0 && str.ptr[i - 1] == ':') {
dc = n; // Double colon
if (i > 1 && str.ptr[i - 2] == ':') return false;
} else if (i > 0) {
n += 2;
}
if (n > 14) return false;
addr->ip6[n] = addr->ip6[n + 1] = 0; // For trailing ::
} else {
return false;
}
}
if (n < 14 && dc == 42) return false;
if (n < 14) {
memmove(&addr->ip6[dc + (14 - n)], &addr->ip6[dc], n - dc + 2);
memset(&addr->ip6[dc], 0, 14 - n);
}
addr->is_ip6 = true;
return true;
}
bool mg_aton(struct mg_str str, struct mg_addr *addr) {
// LOG(LL_INFO, ("[%.*s]", (int) str.len, str.ptr));
return mg_atonl(str, addr) || mg_aton4(str, addr) || mg_aton6(str, addr);
}
void mg_mgr_free(struct mg_mgr *mgr) {
struct mg_connection *c;
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ARCH == MG_ARCH_FREERTOS
FreeRTOS_DeleteSocketSet(mgr->ss);
#endif
LOG(LL_INFO, ("All connections closed"));
}
void mg_mgr_init(struct mg_mgr *mgr) {
#ifdef _WIN32
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#elif MG_ARCH == MG_ARCH_FREERTOS
mgr->ss = FreeRTOS_CreateSocketSet();
#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
// Ignore SIGPIPE signal, so if client cancels the request, it
// won't kill the whole process.
signal(SIGPIPE, SIG_IGN);
#endif
memset(mgr, 0, sizeof(*mgr));
mgr->dnstimeout = 3000;
mgr->dns4.url = "udp://8.8.8.8:53";
mgr->dns6.url = "udp://[2001:4860:4860::8888]:53";
}
+24 -11
View File
@@ -3,20 +3,30 @@
#include "arch.h" #include "arch.h"
#include "event.h" #include "event.h"
#include "iobuf.h" #include "iobuf.h"
#include "str.h"
struct mg_mgr { struct mg_dns {
struct mg_connection *conns; // List of active connections const char *url; // DNS server URL
struct mg_connection *dnsc; // DNS resolver connection struct mg_connection *c; // DNS server connection
const char *dnsserver; // DNS server URL
int dnstimeout; // DNS resolve timeout in milliseconds
#if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
}; };
struct mg_addr { struct mg_addr {
uint16_t port; // TCP or UDP port in network byte order uint16_t port; // TCP or UDP port in network byte order
uint32_t ip; // IP address in network byte order uint32_t ip; // IP address in network byte order
uint8_t ip6[16]; // IPv6 address
bool is_ip6; // True when address is IPv6 address
};
struct mg_mgr {
struct mg_connection *conns; // List of active connections
struct mg_dns dns4; // DNS for IPv4
struct mg_dns dns6; // DNS for IPv6
int dnstimeout; // DNS resolve timeout in milliseconds
unsigned long nextid; // Next connection ID
void *userdata; // Arbitrary user data pointer
#if MG_ARCH == MG_ARCH_FREERTOS
SocketSet_t ss; // NOTE(lsm): referenced from socket struct
#endif
}; };
struct mg_connection { struct mg_connection {
@@ -24,13 +34,14 @@ struct mg_connection {
struct mg_mgr *mgr; // Our container struct mg_mgr *mgr; // Our container
struct mg_addr peer; // Remote peer address struct mg_addr peer; // Remote peer address
void *fd; // Connected socket, or LWIP data void *fd; // Connected socket, or LWIP data
unsigned long id; // Auto-incrementing unique connection ID
struct mg_iobuf recv; // Incoming data struct mg_iobuf recv; // Incoming data
struct mg_iobuf send; // Outgoing data struct mg_iobuf send; // Outgoing data
mg_event_handler_t fn; // User-specified event handler function mg_event_handler_t fn; // User-specified event handler function
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
@@ -61,3 +72,5 @@ int mg_printf(struct mg_connection *, const char *fmt, ...);
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap); int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
char *mg_straddr(struct mg_connection *, char *, size_t); char *mg_straddr(struct mg_connection *, char *, size_t);
bool mg_socketpair(int *s1, int *s2); bool mg_socketpair(int *s1, int *s2);
bool mg_aton(struct mg_str str, struct mg_addr *addr);
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
+2 -2
View File
@@ -1,7 +1,7 @@
#include "sntp.h"
#include "arch.h" #include "arch.h"
#include "event.h" #include "event.h"
#include "log.h" #include "log.h"
#include "sntp.h"
#include "util.h" #include "util.h"
#define SNTP_INTERVAL_SEC (3600) #define SNTP_INTERVAL_SEC (3600)
@@ -49,7 +49,7 @@ static void sntp_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
void mg_sntp_send(struct mg_connection *c, unsigned long utc) { void mg_sntp_send(struct mg_connection *c, unsigned long utc) {
if (c->is_resolving) { if (c->is_resolving) {
LOG(LL_ERROR, ("%p wait until resolved", c->fd)); LOG(LL_ERROR, ("%lu wait until resolved", c->id));
} else if (utc > s_sntmp_next) { } else if (utc > s_sntmp_next) {
uint8_t buf[48] = {0}; uint8_t buf[48] = {0};
s_sntmp_next = utc + SNTP_INTERVAL_SEC; s_sntmp_next = utc + SNTP_INTERVAL_SEC;
+149 -133
View File
@@ -13,6 +13,9 @@
#if defined(_WIN32) #if defined(_WIN32)
#define MG_SOCK_ERRNO WSAGetLastError() #define MG_SOCK_ERRNO WSAGetLastError()
#define FD(C_) ((SOCKET)(C_)->fd) #define FD(C_) ((SOCKET)(C_)->fd)
#ifndef SO_EXCLUSIVEADDRUSE
#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR))
#endif
#elif MG_ARCH == MG_ARCH_FREERTOS #elif MG_ARCH == MG_ARCH_FREERTOS
#define MG_SOCK_ERRNO errno #define MG_SOCK_ERRNO errno
typedef Socket_t SOCKET; typedef Socket_t SOCKET;
@@ -26,6 +29,10 @@ typedef int SOCKET;
#define FD(C_) ((SOCKET)(long) (C_)->fd) #define FD(C_) ((SOCKET)(long) (C_)->fd)
#endif #endif
#ifndef MSG_NONBLOCKING
#define MSG_NONBLOCKING 0
#endif
union usa { union usa {
struct sockaddr sa; struct sockaddr sa;
struct sockaddr_in sin; struct sockaddr_in sin;
@@ -40,6 +47,13 @@ static union usa tousa(struct mg_addr *a) {
usa.sin.sin_family = AF_INET; usa.sin.sin_family = AF_INET;
usa.sin.sin_port = a->port; usa.sin.sin_port = a->port;
*(uint32_t *) &usa.sin.sin_addr = a->ip; *(uint32_t *) &usa.sin.sin_addr = a->ip;
#if MG_ENABLE_IPV6
if (a->is_ip6) {
usa.sin.sin_family = AF_INET6;
usa.sin6.sin6_port = a->port;
memcpy(&usa.sin6.sin6_addr, a->ip6, sizeof(a->ip6));
}
#endif
return usa; return usa;
} }
@@ -62,6 +76,7 @@ static struct mg_connection *alloc_conn(struct mg_mgr *mgr, int is_client,
c->is_client = is_client; c->is_client = is_client;
c->fd = (void *) (long) fd; c->fd = (void *) (long) fd;
c->mgr = mgr; c->mgr = mgr;
c->id = ++mgr->nextid;
} }
return c; return c;
} }
@@ -72,13 +87,23 @@ static int mg_sock_recv(struct mg_connection *c, void *buf, int len,
if (c->is_udp) { if (c->is_udp) {
union usa usa; union usa usa;
socklen_t slen = sizeof(usa.sin); socklen_t slen = sizeof(usa.sin);
n = recvfrom(FD(c), buf, len, 0, &usa.sa, &slen); #if MG_ENABLE_IPV6
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
#endif
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
if (n > 0) { if (n > 0) {
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr; if (c->peer.is_ip6) {
c->peer.port = usa.sin.sin_port; #if MG_ENABLE_IPV6
memcpy(c->peer.ip6, &usa.sin6.sin6_addr, sizeof(c->peer.ip6));
c->peer.port = usa.sin6.sin6_port;
#endif
} else {
c->peer.ip = *(uint32_t *) &usa.sin.sin_addr;
c->peer.port = usa.sin.sin_port;
}
} }
} else { } else {
n = recv(FD(c), buf, len, 0); n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
*fail = (n == 0) || (n < 0 && mg_sock_failed()); *fail = (n == 0) || (n < 0 && mg_sock_failed());
return n; return n;
@@ -89,9 +114,13 @@ static int mg_sock_send(struct mg_connection *c, const void *buf, int len,
int n = 0; int n = 0;
if (c->is_udp) { if (c->is_udp) {
union usa usa = tousa(&c->peer); union usa usa = tousa(&c->peer);
n = sendto(FD(c), buf, len, 0, &usa.sa, sizeof(usa.sin)); socklen_t slen = sizeof(usa.sin);
#if MG_ENABLE_IPV6
if (c->peer.is_ip6) slen = sizeof(usa.sin6);
#endif
n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
} else { } else {
n = send(FD(c), buf, len, 0); n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
} }
*fail = (n == 0) || (n < 0 && mg_sock_failed()); *fail = (n == 0) || (n < 0 && mg_sock_failed());
return n; return n;
@@ -101,12 +130,12 @@ static int ll_read(struct mg_connection *c, void *buf, int len, int *fail) {
int n = c->is_tls ? mg_tls_recv(c, buf, len, fail) int n = c->is_tls ? mg_tls_recv(c, buf, len, fail)
: mg_sock_recv(c, buf, len, fail); : mg_sock_recv(c, buf, len, fail);
LOG(*fail ? LL_DEBUG : LL_VERBOSE_DEBUG, LOG(*fail ? LL_DEBUG : LL_VERBOSE_DEBUG,
("%p %c%c%c %d/%d %d %d", c->fd, c->is_tls ? 'T' : 't', ("%lu %c%c%c %d/%d %d %d", c->id, c->is_tls ? 'T' : 't',
c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, len, c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, len,
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, len); char *s = mg_hexdump(buf, n);
LOG(LL_INFO, ("\n-- %p %s %s %d\n%s--", c->fd, c->label, "<-", len, 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;
@@ -117,12 +146,12 @@ static int ll_write(struct mg_connection *c, const void *buf, int len,
int n = c->is_tls ? mg_tls_send(c, buf, len, fail) int n = c->is_tls ? mg_tls_send(c, buf, len, fail)
: mg_sock_send(c, buf, len, fail); : mg_sock_send(c, buf, len, fail);
LOG(*fail ? LL_ERROR : LL_VERBOSE_DEBUG, LOG(*fail ? LL_ERROR : LL_VERBOSE_DEBUG,
("%p %c%c%c %d/%d %d", c->fd, c->is_tls ? 'T' : 't', ("%lu %c%c%c %d/%d %d", c->id, c->is_tls ? 'T' : 't',
c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, len, c->is_udp ? 'U' : 'u', c->is_connecting ? 'C' : 'c', n, 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-- %p %s %s %d\n%s--", c->fd, 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;
@@ -132,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;
} }
@@ -148,46 +178,51 @@ static void mg_set_non_blocking_mode(SOCKET fd) {
#endif #endif
} }
SOCKET mg_open_listener(const char *ip, uint16_t port, int is_udp) { SOCKET mg_open_listener(const char *url) {
union usa usa; struct mg_addr addr;
int on = 1; SOCKET fd = INVALID_SOCKET;
int proto = is_udp ? IPPROTO_UDP : IPPROTO_TCP;
int type = is_udp ? SOCK_DGRAM : SOCK_STREAM;
SOCKET fd;
memset(&usa, 0, sizeof(usa)); memset(&addr, 0, sizeof(addr));
usa.sin.sin_family = AF_INET; addr.port = mg_htons(mg_url_port(url));
usa.sin.sin_port = mg_htons(port); if (!mg_aton(mg_url_host(url), &addr)) {
mg_aton(ip, (uint32_t *) &usa.sin.sin_addr); LOG(LL_ERROR, ("invalid listening URL: %s", url));
if (!mg_casecmp(ip, "localhost")) { } else {
*(uint32_t *) &usa.sin.sin_addr = mg_htonl(0x7f000001); union usa usa = tousa(&addr);
} int on = 1, af = AF_INET;
int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
socklen_t slen = sizeof(usa.sin);
#if MG_ENABLE_IPV6
if (addr.is_ip6) af = AF_INET6, slen = sizeof(usa.sin6);
#endif
if ((fd = socket(AF_INET, type, proto)) != INVALID_SOCKET && if ((fd = socket(af, type, proto)) != INVALID_SOCKET &&
#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) #if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE)
// SO_RESUSEADDR is not enabled on Windows because the semantics of // SO_RESUSEADDR is not enabled on Windows because the semantics of
// SO_REUSEADDR on UNIX and Windows is different. On Windows, // SO_REUSEADDR on UNIX and Windows is different. On Windows,
// SO_REUSEADDR allows to bind a socket to a port without error even if // SO_REUSEADDR allows to bind a socket to a port without error even if
// the port is already open by another program. This is not the behavior // the port is already open by another program. This is not the behavior
// SO_REUSEADDR was designed for, and leads to hard-to-track failure // SO_REUSEADDR was designed for, and leads to hard-to-track failure
// scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
// SO_EXCLUSIVEADDRUSE is supported and set on a socket. // SO_EXCLUSIVEADDRUSE is supported and set on a socket.
!setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) &&
#endif #endif
#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE)
// "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE"
!setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, //! setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof(on))
sizeof(on)) && //! &&
!setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &on,
sizeof(on)) &&
#endif #endif
bind(fd, &usa.sa, sizeof(usa.sin)) == 0 && bind(fd, &usa.sa, slen) == 0 &&
// NOTE(lsm): FreeRTOS uses backlog value as a connection limit // NOTE(lsm): FreeRTOS uses backlog value as a connection limit
(type == SOCK_DGRAM || listen(fd, 128) == 0)) { (type == SOCK_DGRAM || listen(fd, 128) == 0)) {
mg_set_non_blocking_mode(fd); mg_set_non_blocking_mode(fd);
} else if (fd >= 0) { } else if (fd != INVALID_SOCKET) {
LOG(LL_ERROR, LOG(LL_ERROR, ("Failed to listen on %s, errno %d", url, MG_SOCK_ERRNO));
("Failed to listen on %s:%hu, errno %d", ip, port, MG_SOCK_ERRNO)); closesocket(fd);
closesocket(fd); fd = INVALID_SOCKET;
fd = INVALID_SOCKET; }
} }
return fd; return fd;
@@ -201,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);
@@ -228,22 +264,19 @@ static int write_conn(struct mg_connection *c) {
return rc; return rc;
} }
static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) { static void close_conn(struct mg_connection *c) {
// Unlink this connection from the list // Unlink this connection from the list
LIST_DELETE(struct mg_connection, &mgr->conns, c); LIST_DELETE(struct mg_connection, &c->mgr->conns, c);
#if 0 mg_resolve_cancel(c);
struct mg_connection **head = &mgr->conns; if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL;
while (*head != c) head = &(*head)->next; if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL;
*head = c->next;
#endif
mg_resolve_cancel(mgr, c);
mg_call(c, MG_EV_CLOSE, NULL); mg_call(c, MG_EV_CLOSE, NULL);
// while (c->callbacks != NULL) mg_fn_del(c, c->callbacks->fn); // while (c->callbacks != NULL) mg_fn_del(c, c->callbacks->fn);
LOG(LL_DEBUG, ("%p closed", c->fd)); LOG(LL_DEBUG, ("%lu closed", c->id));
if (FD(c) != INVALID_SOCKET) { if (FD(c) != INVALID_SOCKET) {
closesocket(FD(c)); closesocket(FD(c));
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
FreeRTOS_FD_CLR(c->fd, mgr->ss, eSELECT_ALL); FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL);
#endif #endif
} }
mg_tls_free(c); mg_tls_free(c);
@@ -251,29 +284,31 @@ static void close_conn(struct mg_mgr *mgr, struct mg_connection *c) {
free(c->send.buf); free(c->send.buf);
memset(c, 0, sizeof(*c)); memset(c, 0, sizeof(*c));
free(c); free(c);
if (c == mgr->dnsc) mgr->dnsc = NULL;
} }
static void setsockopts(struct mg_connection *c) { static void setsockopts(struct mg_connection *c) {
#if MG_ARCH == MG_ARCH_FREERTOS #if MG_ARCH == MG_ARCH_FREERTOS
FreeRTOS_FD_SET(c->fd, c->mgr->ss, eSELECT_READ | eSELECT_EXCEPT); FreeRTOS_FD_SET(c->fd, c->mgr->ss, eSELECT_READ | eSELECT_EXCEPT);
#else #else
int on = 1, cnt = 3, intvl = 20; int on = 1;
#if !defined(SOL_TCP) #if !defined(SOL_TCP)
#define SOL_TCP IPPROTO_TCP #define SOL_TCP IPPROTO_TCP
#endif #endif
setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
#if defined(TCP_QUICKACK) #if defined(TCP_QUICKACK)
setsockopt(FD(c), SOL_TCP, TCP_QUICKACK, (void *) &on, sizeof(on)); setsockopt(FD(c), SOL_TCP, TCP_QUICKACK, (char *) &on, sizeof(on));
#endif #endif
setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on)); setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
#if ESP32 || ESP8266 || defined(__linux__) #if ESP32 || ESP8266 || defined(__linux__)
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__)
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)); {
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)); int cnt = 3, intvl = 20;
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
setsockopt(FD(c), IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl));
}
#endif #endif
#endif #endif
} }
@@ -281,12 +316,27 @@ static void setsockopts(struct mg_connection *c) {
void mg_connect_resolved(struct mg_connection *c) { void mg_connect_resolved(struct mg_connection *c) {
char buf[40]; char buf[40];
int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM;
mg_call(c, MG_EV_RESOLVE, NULL); int af = AF_INET;
#if MG_ENABLE_IPV6
if (c->peer.is_ip6) af = AF_INET6;
#endif
mg_straddr(c, buf, sizeof(buf)); mg_straddr(c, buf, sizeof(buf));
LOG(LL_DEBUG, ("%p resolved: %s", c->fd, buf)); c->fd = (void *) (long) socket(af, type, 0);
if (FD(c) == INVALID_SOCKET) {
mg_error(c, "socket(): %d", MG_SOCK_ERRNO);
return;
}
mg_set_non_blocking_mode(FD(c));
mg_call(c, MG_EV_RESOLVE, NULL);
if (type == SOCK_STREAM) { if (type == SOCK_STREAM) {
union usa usa = tousa(&c->peer); union usa usa = tousa(&c->peer);
int rc = connect(FD(c), &usa.sa, sizeof(usa.sin)); socklen_t slen =
#if MG_ENABLE_IPV6
c->peer.is_ip6 ? sizeof(usa.sin6) :
#endif
sizeof(usa.sin);
int rc = connect(FD(c), &usa.sa, slen);
int fail = rc < 0 && mg_sock_failed() ? MG_SOCK_ERRNO : 0; int fail = rc < 0 && mg_sock_failed() ? MG_SOCK_ERRNO : 0;
if (fail) { if (fail) {
mg_error(c, "connect: %d", MG_SOCK_ERRNO); mg_error(c, "connect: %d", MG_SOCK_ERRNO);
@@ -300,21 +350,17 @@ void mg_connect_resolved(struct mg_connection *c) {
struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_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 = NULL; struct mg_connection *c = NULL;
int fd, type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; if ((c = alloc_conn(mgr, 1, INVALID_SOCKET)) == NULL) {
if ((fd = socket(AF_INET, type, 0)) == INVALID_SOCKET) { LOG(LL_ERROR, ("OOM"));
LOG(LL_ERROR, ("socket(): %d", MG_SOCK_ERRNO));
} else if ((c = alloc_conn(mgr, 1, fd)) == NULL) {
LOG(LL_ERROR, ("%p OOM", c->fd));
} else { } else {
struct mg_str host = mg_url_host(url); struct mg_str host = mg_url_host(url);
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
mg_set_non_blocking_mode(FD(c)); c->is_udp = (strncmp(url, "udp:", 4) == 0);
c->is_udp = type == SOCK_DGRAM;
c->peer.port = mg_htons(mg_url_port(url)); c->peer.port = mg_htons(mg_url_port(url));
c->fn = fn; c->fn = fn;
c->fn_data = fn_data; c->fn_data = fn_data;
LOG(LL_DEBUG, ("%p -> %s", c->fd, url)); LOG(LL_DEBUG, ("%lu -> %s", c->id, url));
mg_resolve(mgr, c, &host, mgr->dnstimeout); mg_resolve(c, &host, mgr->dnstimeout);
} }
return c; return c;
} }
@@ -322,25 +368,31 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) { static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
struct mg_connection *c = NULL; struct mg_connection *c = NULL;
union usa usa; union usa usa;
socklen_t sa_len = sizeof(usa.sin); socklen_t sa_len = sizeof(usa);
SOCKET fd = accept(FD(lsn), &usa.sa, &sa_len); SOCKET fd = accept(FD(lsn), &usa.sa, &sa_len);
if (fd == INVALID_SOCKET) { if (fd == INVALID_SOCKET) {
LOG(LL_ERROR, LOG(LL_ERROR, ("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERRNO));
("%p accept(%d) failed, errno %d", lsn->fd, FD(lsn), MG_SOCK_ERRNO));
#if !defined(_WIN32) #if !defined(_WIN32)
} else if (fd >= FD_SETSIZE) { } else if (fd >= FD_SETSIZE) {
LOG(LL_ERROR, ("%ld > %ld", (long) fd, (long) FD_SETSIZE)); LOG(LL_ERROR, ("%ld > %ld", (long) fd, (long) FD_SETSIZE));
closesocket(fd); closesocket(fd);
#endif #endif
} else if ((c = alloc_conn(mgr, 0, fd)) == NULL) { } else if ((c = alloc_conn(mgr, 0, fd)) == NULL) {
LOG(LL_ERROR, ("%p OOM", lsn->fd)); LOG(LL_ERROR, ("%lu OOM", lsn->id));
closesocket(fd); closesocket(fd);
} else { } else {
char buf[40]; char buf[40];
c->peer.port = usa.sin.sin_port; c->peer.port = usa.sin.sin_port;
memcpy(&c->peer.ip, &usa.sin.sin_addr, sizeof(c->peer.ip)); memcpy(&c->peer.ip, &usa.sin.sin_addr, sizeof(c->peer.ip));
#if MG_ENABLE_IPV6
if (sa_len == sizeof(usa.sin6)) {
memcpy(c->peer.ip6, &usa.sin6.sin6_addr, sizeof(c->peer.ip6));
c->peer.port = usa.sin6.sin6_port;
c->peer.is_ip6 = 1;
}
#endif
mg_straddr(c, buf, sizeof(buf)); mg_straddr(c, buf, sizeof(buf));
LOG(LL_DEBUG, ("%p accepted %s", c->fd, buf)); LOG(LL_DEBUG, ("%lu accepted %s", c->id, buf));
mg_set_non_blocking_mode(FD(c)); mg_set_non_blocking_mode(FD(c));
setsockopts(c); setsockopts(c);
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
@@ -356,17 +408,20 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
#if MG_ENABLE_SOCKETPAIR #if MG_ENABLE_SOCKETPAIR
bool mg_socketpair(int *s1, int *s2) { bool mg_socketpair(int *s1, int *s2) {
#if MG_ARCH == MG_ARCH_UNIX #ifdef MG_ENABLE_NATIVE_SOCKETPAIR
// For some reason, native socketpair() call fails on Macos
// Enable this codepath only when MG_ENABLE_NATIVE_SOCKETPAIR is defined
int sp[2], ret = 0; int sp[2], ret = 0;
if (socketpair(AF_INET, SOCK_DGRAM, 0, sp) == 0) { if (socketpair(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sp) == 0) {
*s1 = sp[0], *s2 = sp[1], ret = 1; *s1 = sp[0], *s2 = sp[1], ret = 1;
} }
LOG(LL_INFO, ("errno %d", errno));
return ret; return ret;
#else #else
union usa sa, sa2; union usa sa, sa2;
SOCKET sp[2] = {INVALID_SOCKET, INVALID_SOCKET}; SOCKET sp[2] = {INVALID_SOCKET, INVALID_SOCKET};
socklen_t len = sizeof(sa.sin); socklen_t len = sizeof(sa.sin);
int ret = 0, res[2] = {-1, -1}; int ret = 0;
(void) memset(&sa, 0, sizeof(sa)); (void) memset(&sa, 0, sizeof(sa));
sa.sin.sin_family = AF_INET; sa.sin.sin_family = AF_INET;
@@ -397,8 +452,7 @@ struct mg_connection *mg_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 = NULL; struct mg_connection *c = NULL;
int is_udp = strncmp(url, "udp:", 4) == 0; int is_udp = strncmp(url, "udp:", 4) == 0;
struct mg_str host = mg_url_host(url); SOCKET fd = mg_open_listener(url);
SOCKET fd = mg_open_listener(host.ptr, mg_url_port(url), is_udp);
if (fd == INVALID_SOCKET) { if (fd == INVALID_SOCKET) {
} else if ((c = alloc_conn(mgr, 0, fd)) == NULL) { } else if ((c = alloc_conn(mgr, 0, fd)) == NULL) {
LOG(LL_ERROR, ("OOM %s", url)); LOG(LL_ERROR, ("OOM %s", url));
@@ -411,7 +465,7 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
c->fn = fn; c->fn = fn;
c->fn_data = fn_data; c->fn_data = fn_data;
LOG(LL_INFO, ("%p accepting on %s", c->fd, url)); LOG(LL_INFO, ("%lu accepting on %s", c->id, url));
} }
return c; return c;
} }
@@ -449,7 +503,6 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) {
if (FD(c) > maxfd) maxfd = FD(c); if (FD(c) > maxfd) maxfd = FD(c);
if (c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0)) if (c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0))
FD_SET(FD(c), &wset); FD_SET(FD(c), &wset);
// LOG(LL_INFO, ("%d %d", c->fd, FD_ISSET(c->fd, &wset)));
} }
if ((rc = select(maxfd + 1, &rset, &wset, NULL, &tv)) < 0) { if ((rc = select(maxfd + 1, &rset, &wset, NULL, &tv)) < 0) {
@@ -478,11 +531,8 @@ static void connect_conn(struct mg_connection *c) {
char buf[40]; char buf[40];
mg_error(c, "error connecting to %s", mg_straddr(c, buf, sizeof(buf))); mg_error(c, "error connecting to %s", mg_straddr(c, buf, sizeof(buf)));
} else { } else {
if (c->is_tls_hs && mg_tls_handshake(c)) { if (c->is_tls_hs) mg_tls_handshake(c);
c->is_tls_hs = 0; mg_call(c, MG_EV_CONNECT, NULL);
mg_call(c, MG_EV_CONNECT, NULL);
}
if (c->is_tls == 0) mg_call(c, MG_EV_CONNECT, NULL);
} }
} }
@@ -498,59 +548,25 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
tmp = c->next; tmp = c->next;
mg_call(c, MG_EV_POLL, &now); mg_call(c, MG_EV_POLL, &now);
LOG(LL_VERBOSE_DEBUG, LOG(LL_VERBOSE_DEBUG,
("%p %c%c %c%c%c%c%c", c->fd, c->is_readable ? 'r' : '-', ("%lu %c%c %c%c%c%c%c", c->id, c->is_readable ? 'r' : '-',
c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't', c->is_writable ? 'w' : '-', c->is_tls ? 'T' : 't',
c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
if (c->is_resolving || c->is_closing) { if (c->is_resolving || c->is_closing) {
// Do nothing // Do nothing
} else if (c->is_listening) { } else if (c->is_listening && c->is_udp == 0) {
if (c->is_readable) accept_conn(mgr, c); if (c->is_readable) accept_conn(mgr, c);
#if 0
} else if (c->is_accepted && c->is_tls_hs) {
if (mg_tls_handshake(c)) c->is_tls_hs = 0;
#endif
} else if (c->is_connecting) { } else if (c->is_connecting) {
if (c->is_readable || c->is_writable) { if (c->is_readable || c->is_writable) connect_conn(c);
connect_conn(c);
}
} else if (c->is_tls_hs) { } else if (c->is_tls_hs) {
if ((c->is_readable || c->is_writable) && mg_tls_handshake(c)) { if ((c->is_readable || c->is_writable)) mg_tls_handshake(c);
c->is_tls_hs = 0;
mg_call(c, MG_EV_CONNECT, NULL);
}
} else { } else {
if (c->is_readable) read_conn(c, ll_read); if (c->is_readable) read_conn(c, ll_read);
if (c->is_writable) write_conn(c); if (c->is_writable) write_conn(c);
} }
if (c->is_draining && c->send.len == 0) c->is_closing = 1; if (c->is_draining && c->send.len == 0) c->is_closing = 1;
if (c->is_closing) close_conn(mgr, c); if (c->is_closing) close_conn(c);
} }
} }
void mg_mgr_free(struct mg_mgr *mgr) {
struct mg_connection *c;
for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1;
mg_mgr_poll(mgr, 0);
#if MG_ARCH == MG_ARCH_FREERTOS
FreeRTOS_DeleteSocketSet(mgr->ss);
#endif
LOG(LL_INFO, ("All connections closed"));
}
void mg_mgr_init(struct mg_mgr *mgr) {
#ifdef _WIN32
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#elif MG_ARCH == MG_ARCH_FREERTOS
mgr->ss = FreeRTOS_CreateSocketSet();
#elif defined(__unix) || defined(__unix__) || defined(__APPLE__)
// Ignore SIGPIPE signal, so if client cancels the request, it
// won't kill the whole process.
signal(SIGPIPE, SIG_IGN);
#endif
memset(mgr, 0, sizeof(*mgr));
mgr->dnstimeout = 3000;
}
#endif #endif
+84
View File
@@ -0,0 +1,84 @@
#include "ssi.h"
#include "log.h"
#include "util.h"
#ifndef MG_MAX_SSI_DEPTH
#define MG_MAX_SSI_DEPTH 5
#endif
#if MG_ENABLE_SSI
static char *mg_ssi(const char *path, const char *root, int depth) {
struct mg_iobuf b = {NULL, 0, 0};
FILE *fp = mg_fopen(path, "rb");
if (fp != NULL) {
char buf[BUFSIZ], arg[sizeof(buf)];
int ch, intag = 0;
size_t len = 0, align = MG_IO_SIZE;
while ((ch = fgetc(fp)) != EOF) {
if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') {
buf[len++] = ch & 0xff;
if (sscanf(buf, "<!--#include file=\"%[^\"]", arg)) {
char tmp[MG_PATH_MAX], *p = (char *) path + strlen(path), *data;
while (p > path && p[-1] != MG_DIRSEP && p[-1] != '/') p--;
snprintf(tmp, sizeof(tmp), "%.*s%s", (int) (p - path), path, arg);
if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
mg_iobuf_append(&b, data, strlen(data), align);
free(data);
} else {
LOG(LL_ERROR, ("%s: file=%s error or too deep", path, arg));
}
} else if (sscanf(buf, "<!--#include virtual=\"%[^\"]", arg)) {
char tmp[MG_PATH_MAX], *data;
snprintf(tmp, sizeof(tmp), "%s%s", root, arg);
if (depth < MG_MAX_SSI_DEPTH &&
(data = mg_ssi(tmp, root, depth + 1)) != NULL) {
mg_iobuf_append(&b, data, strlen(data), align);
free(data);
} else {
LOG(LL_ERROR, ("%s: virtual=%s error or too deep", path, arg));
}
} else {
// Unknown SSI tag
LOG(LL_INFO, ("Unknown SSI tag: %.*s", (int) len, buf));
mg_iobuf_append(&b, buf, len, align);
}
intag = 0;
len = 0;
} else if (ch == '<') {
intag = 1;
if (len > 0) mg_iobuf_append(&b, buf, len, align);
len = 0;
buf[len++] = ch & 0xff;
} else if (intag) {
if (len == 5 && strncmp(buf, "<!--#", 5) != 0) {
intag = 0;
} else if (len >= sizeof(buf) - 2) {
LOG(LL_ERROR, ("%s: SSI tag is too large", path));
len = 0;
}
buf[len++] = ch & 0xff;
} else {
buf[len++] = ch & 0xff;
if (len >= sizeof(buf)) {
mg_iobuf_append(&b, buf, len, align);
len = 0;
}
}
}
if (len > 0) mg_iobuf_append(&b, buf, len, align);
if (b.len > 0) mg_iobuf_append(&b, "", 1, align); // nul-terminate
fclose(fp);
}
(void) depth;
(void) root;
return (char *) b.buf;
}
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
const char *fullpath) {
char *data = mg_ssi(fullpath, root, 0);
mg_http_reply(c, 200, "", "%s", data == NULL ? "" : data);
free(data);
}
#endif
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include "http.h"
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
const char *fullpath);
+56 -17
View File
@@ -8,13 +8,15 @@
#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
#ifdef __cplusplus #ifdef __cplusplus
#define EXTERN_C extern "C" #define EXTERN_C extern "C"
#else #else
#define EXTERN_C #define EXTERN_C
#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);
@@ -33,11 +35,12 @@ int mg_tls_handshake(struct mg_connection *c) {
mbedtls_ssl_set_bio(&tls->ssl, &c->fd, mbedtls_net_send, mbedtls_net_recv, 0); mbedtls_ssl_set_bio(&tls->ssl, &c->fd, mbedtls_net_send, mbedtls_net_recv, 0);
rc = mbedtls_ssl_handshake(&tls->ssl); rc = mbedtls_ssl_handshake(&tls->ssl);
if (rc == 0) { // Success if (rc == 0) { // Success
LOG(LL_DEBUG, ("%p OK", c->fd)); LOG(LL_DEBUG, ("%lu success", c->id));
c->is_tls_hs = 0;
} else if (rc == MBEDTLS_ERR_SSL_WANT_READ || } else if (rc == MBEDTLS_ERR_SSL_WANT_READ ||
rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending
LOG(LL_VERBOSE_DEBUG, ("%p pending, %d%d %d (-%#x)", c->fd, c->is_connecting, LOG(LL_VERBOSE_DEBUG, ("%lu pending, %d%d %d (-%#x)", c->id,
c->is_tls_hs, rc, -rc)); c->is_connecting, c->is_tls_hs, rc, -rc));
} else { } else {
mg_error(c, "TLS handshake: -%#x", -rc); // Error mg_error(c, "TLS handshake: -%#x", -rc); // Error
} }
@@ -61,8 +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;
LOG(LL_DEBUG, ("%p Setting TLS, CA: %s, cert: %s, key: %s", c->fd, const char *ca = opts->ca == NULL ? "-"
opts->ca == NULL ? "null" : opts->ca, : opts->ca[0] == '-' ? "(emb)"
: opts->ca;
if (tls == NULL) {
mg_error(c, "TLS OOM");
goto fail;
}
LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, 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);
@@ -87,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') {
@@ -126,6 +144,9 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
c->tls = tls; c->tls = tls;
c->is_tls = 1; c->is_tls = 1;
c->is_tls_hs = 1; c->is_tls_hs = 1;
if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
mg_tls_handshake(c);
}
return 1; return 1;
fail: fail:
c->is_closing = 1; c->is_closing = 1;
@@ -162,7 +183,6 @@ int mg_tls_free(struct mg_connection *c) {
} }
#elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL #elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
@@ -192,11 +212,17 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
const char *id = "mongoose"; const char *id = "mongoose";
static unsigned char s_initialised = 0; static unsigned char s_initialised = 0;
int rc; int rc;
if (tls == NULL) {
mg_error(c, "TLS OOM");
goto fail;
}
if (!s_initialised) { if (!s_initialised) {
SSL_library_init(); SSL_library_init();
s_initialised++; s_initialised++;
} }
LOG(LL_DEBUG, ("%p Setting TLS, CA: %s, cert: %s, key: %s", c->fd, LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id,
opts->ca == NULL ? "null" : opts->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));
@@ -219,12 +245,12 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
#endif #endif
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;
} }
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
0);
} }
if (opts->cert != NULL && opts->cert[0] != '\0') { if (opts->cert != NULL && opts->cert[0] != '\0') {
const char *key = opts->certkey; const char *key = opts->certkey;
@@ -235,10 +261,12 @@ int mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
} else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) { } else if ((rc = SSL_use_PrivateKey_file(tls->ssl, key, 1)) != 1) {
mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc)); mg_error(c, "Invalid SSL key, err %d", mg_tls_err(tls, rc));
goto fail; goto fail;
#if OPENSSL_VERSION_NUMBER > 0x10002000L
} else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) != } else if ((rc = SSL_use_certificate_chain_file(tls->ssl, opts->cert)) !=
1) { 1) {
mg_error(c, "Invalid CA, err %d", mg_tls_err(tls, rc)); mg_error(c, "Invalid CA, err %d", mg_tls_err(tls, rc));
goto fail; goto fail;
#endif
} else { } else {
SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_set_mode(tls->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#if OPENSSL_VERSION_NUMBER > 0x10002000L #if OPENSSL_VERSION_NUMBER > 0x10002000L
@@ -247,10 +275,19 @@ 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;
if (c->is_client && c->is_resolving == 0 && c->is_connecting == 0) {
mg_tls_handshake(c);
}
c->is_hexdumping = 1;
LOG(LL_DEBUG, ("%lu SSL %s OK", c->id, c->is_accepted ? "accept" : "client"));
return 1; return 1;
fail: fail:
c->is_closing = 1; c->is_closing = 1;
@@ -261,14 +298,16 @@ 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;
// if (SSL_get_fd(tls->ssl) == -1) SSL_set_fd(tls->ssl, (long) c->fd);
SSL_set_fd(tls->ssl, (int) 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));
c->is_tls_hs = 0;
return 1; return 1;
} else { } else {
int code;
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
int code = mg_tls_err(tls, rc); code = mg_tls_err(tls, rc);
if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code); if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);
return 0; return 0;
} }
+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 *);
+10 -3
View File
@@ -1,5 +1,5 @@
#include "url.h"
#include <stdlib.h> #include <stdlib.h>
#include "url.h"
struct url { struct url {
int key, user, pass, host, port, uri, end; int key, user, pass, host, port, uri, end;
@@ -19,6 +19,8 @@ static struct url urlparse(const char *url) {
if (i > 0 && url[i - 1] == '/' && url[i] == '/') { if (i > 0 && url[i - 1] == '/' && url[i] == '/') {
u.host = i + 1; u.host = i + 1;
u.port = 0; u.port = 0;
} else if (url[i] == ']') {
u.port = 0; // IPv6 URLs, like http://[::1]/bar
} else if (url[i] == ':') { } else if (url[i] == ':') {
u.port = i + 1; u.port = i + 1;
} else if (url[i] == '@') { } else if (url[i] == '@') {
@@ -41,7 +43,12 @@ struct mg_str mg_url_host(const char *url) {
struct url u = urlparse(url); struct url u = urlparse(url);
int n = int n =
u.port ? u.port - u.host - 1 : u.uri ? u.uri - u.host : u.end - u.host; u.port ? u.port - u.host - 1 : u.uri ? u.uri - u.host : u.end - u.host;
return mg_str_n(url + u.host, n); struct mg_str s = mg_str_n(url + u.host, n);
if (s.len > 2 && s.ptr[0] == '[' && s.ptr[s.len - 1] == ']') {
s.len -= 2;
s.ptr++;
}
return s;
} }
const char *mg_url_uri(const char *url) { const char *mg_url_uri(const char *url) {
@@ -52,7 +59,7 @@ const char *mg_url_uri(const char *url) {
unsigned short mg_url_port(const char *url) { unsigned short mg_url_port(const char *url) {
struct url u = urlparse(url); struct url u = urlparse(url);
unsigned short port = 0; unsigned short port = 0;
if (memcmp(url, "http:", 5) == 0) port = 80; if (memcmp(url, "http:", 5) == 0 || memcmp(url, "ws:", 3) == 0) port = 80;
if (memcmp(url, "wss:", 4) == 0 || memcmp(url, "https:", 6) == 0) port = 443; if (memcmp(url, "wss:", 4) == 0 || memcmp(url, "https:", 6) == 0) port = 443;
if (memcmp(url, "mqtt:", 5) == 0) port = 1883; if (memcmp(url, "mqtt:", 5) == 0) port = 1883;
if (memcmp(url, "mqtts:", 6) == 0) port = 8883; if (memcmp(url, "mqtts:", 6) == 0) port = 8883;
+86 -46
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) {
@@ -107,16 +152,16 @@ uint16_t mg_ntohs(uint16_t net) {
return ((uint16_t) data[1] << 0) | ((uint32_t) data[0] << 8); return ((uint16_t) data[1] << 0) | ((uint32_t) data[0] << 8);
} }
char *mg_hexdump(const void *buf, int len) { char *mg_hexdump(const void *buf, size_t len) {
const unsigned char *p = (const unsigned char *) buf; const unsigned char *p = (const unsigned char *) buf;
int i, idx, n = 0, ofs = 0, dlen = len * 5 + 100; size_t i, idx, n = 0, ofs = 0, dlen = len * 5 + 100;
char ascii[17] = "", *dst = (char *) malloc(dlen); char ascii[17] = "", *dst = (char *) malloc(dlen);
if (dst == NULL) return dst; if (dst == NULL) return dst;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
idx = i % 16; idx = i % 16;
if (idx == 0) { if (idx == 0) {
if (i > 0 && dlen > n) n += snprintf(dst + n, dlen - n, " %s\n", ascii); if (i > 0 && dlen > n) n += snprintf(dst + n, dlen - n, " %s\n", ascii);
if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", i + ofs); if (dlen > n) n += snprintf(dst + n, dlen - n, "%04x ", (int) (i + ofs));
} }
if (dlen < n) break; if (dlen < n) break;
n += snprintf(dst + n, dlen - n, " %02x", p[i]); n += snprintf(dst + n, dlen - n, " %02x", p[i]);
@@ -190,7 +235,7 @@ int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) {
} }
// Microsoft version of vsnprintf() is not always null-terminated, so put // Microsoft version of vsnprintf() is not always null-terminated, so put
// the terminator manually // the terminator manually
(*buf)[len] = 0; if (*buf != NULL) (*buf)[len] = 0;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} else if (len >= (int) size) { } else if (len >= (int) size) {
/// Standard-compliant code path. Allocate a buffer that is large enough /// Standard-compliant code path. Allocate a buffer that is large enough
@@ -215,33 +260,27 @@ int mg_asprintf(char **buf, size_t size, const char *fmt, ...) {
return ret; return ret;
} }
int64_t mg_to64(const char *s) { int64_t mg_to64(struct mg_str str) {
int64_t result = 0, neg = 1; int64_t result = 0, neg = 1;
while (*s && isspace((unsigned char) *s)) s++; size_t i = 0;
if (*s == '-') neg = -1, s++; while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++;
while (isdigit((unsigned char) *s)) { if (i < str.len && str.ptr[i] == '-') neg = -1, i++;
while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') {
result *= 10; result *= 10;
result += (*s - '0'); result += (str.ptr[i] - '0');
s++; i++;
} }
return result * neg; return result * neg;
} }
bool mg_aton(const char *s, uint32_t *ip) { uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) {
uint8_t data[4] = {0, 0, 0, 0}, ok = 0; int i;
if (s != NULL && sscanf(s, "%hhu.%hhu.%hhu.%hhu", &data[0], &data[1], crc = ~crc;
&data[2], &data[3]) == 4) { while (len--) {
memcpy(ip, data, sizeof(data)); crc ^= *(unsigned char *) buf++;
ok = 1; for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
} }
return ok; return ~crc;
}
char *mg_ntoa(uint32_t ipaddr, char *buf, size_t len) {
uint8_t p[4];
memcpy(p, &ipaddr, sizeof(p));
snprintf(buf, len, "%hhu.%hhu.%hhu.%hhu", p[0], p[1], p[2], p[3]);
return buf;
} }
double mg_time(void) { double mg_time(void) {
@@ -285,7 +324,8 @@ unsigned long mg_millis(void) {
#elif MG_ARCH == MG_ARCH_ESP32 #elif MG_ARCH == MG_ARCH_ESP32
return esp_timer_get_time() / 1000; return esp_timer_get_time() / 1000;
#elif MG_ARCH == MG_ARCH_ESP8266 #elif MG_ARCH == MG_ARCH_ESP8266
return system_get_time() / 1000; // return system_get_time() / 1000;
return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_FREERTOS #elif MG_ARCH == MG_ARCH_FREERTOS
return xTaskGetTickCount() * portTICK_PERIOD_MS; return xTaskGetTickCount() * portTICK_PERIOD_MS;
#else #else
+17 -7
View File
@@ -4,26 +4,36 @@
#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);
char *mg_hexdump(const void *buf, int len); uint32_t mg_crc32(uint32_t crc, const char *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);
unsigned long mg_unhexn(const char *s, int len); unsigned long mg_unhexn(const char *s, int len);
int mg_asprintf(char **buf, size_t size, const char *fmt, ...); int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap); int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
int64_t mg_to64(const char *s); int64_t mg_to64(struct mg_str str);
bool mg_aton(const char *s, uint32_t *ipaddr);
char *mg_ntoa(uint32_t ipaddr, char *buf, size_t len);
double mg_time(void); 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)
@@ -34,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.0" #define MG_VERSION "7.2"
+31 -21
View File
@@ -15,25 +15,27 @@ struct ws_msg {
}; };
static void ws_handshake(struct mg_connection *c, const char *key, static void ws_handshake(struct mg_connection *c, const char *key,
size_t key_len) { size_t key_len, const char *fmt, va_list ap) {
const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
unsigned char sha[20], b64_sha[30]; unsigned char sha[20], b64_sha[30];
// mem[256], *buf = mem; char mem[128], *buf = mem;
// int len = 0;
mg_sha1_ctx sha_ctx; mg_sha1_ctx sha_ctx;
mg_sha1_init(&sha_ctx); mg_sha1_init(&sha_ctx);
mg_sha1_update(&sha_ctx, (unsigned char *) key, key_len); mg_sha1_update(&sha_ctx, (unsigned char *) key, key_len);
mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36); mg_sha1_update(&sha_ctx, (unsigned char *) magic, 36);
mg_sha1_final(sha, &sha_ctx); mg_sha1_final(sha, &sha_ctx);
mg_base64_encode(sha, sizeof(sha), (char *) b64_sha); mg_base64_encode(sha, sizeof(sha), (char *) b64_sha);
buf[0] = '\0';
if (fmt != NULL) mg_vasprintf(&buf, sizeof(mem), fmt, ap);
mg_printf(c, mg_printf(c,
"HTTP/1.1 101 Switching Protocols\r\n" "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n" "Upgrade: websocket\r\n"
"Connection: Upgrade\r\n" "Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n\r\n", "Sec-WebSocket-Accept: %s\r\n"
b64_sha); "%s\r\n",
// mg_send(c, buf, len); b64_sha, buf);
// if (buf != mem) free(buf); if (buf != mem) free(buf);
} }
static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) { static size_t ws_process(uint8_t *buf, size_t len, struct ws_msg *msg) {
@@ -72,7 +74,7 @@ size_t mg_ws_send(struct mg_connection *c, const char *buf, size_t len,
if (len < 126) { if (len < 126) {
header[1] = (unsigned char) len; header[1] = (unsigned char) len;
header_len = 2; header_len = 2;
} else if (len < 65535) { } else if (len < 65536) {
uint16_t tmp = mg_htons((uint16_t) len); uint16_t tmp = mg_htons((uint16_t) len);
header[1] = 126; header[1] = 126;
memcpy(&header[2], &tmp, sizeof(tmp)); memcpy(&header[2], &tmp, sizeof(tmp));
@@ -95,7 +97,7 @@ size_t mg_ws_send(struct mg_connection *c, const char *buf, size_t len,
} }
LOG(LL_VERBOSE_DEBUG, ("WS out: %d [%.*s]", (int) len, (int) len, buf)); LOG(LL_VERBOSE_DEBUG, ("WS out: %d [%.*s]", (int) len, (int) len, buf));
mg_send(c, buf, len); mg_send(c, buf, len);
if (c->is_client) { if (c->is_client && c->send.buf != NULL) {
uint8_t *p = c->send.buf + c->send.len - len; uint8_t *p = c->send.buf + c->send.len - len;
for (i = 0; i < len; i++) p[i] ^= mask[i & 3]; for (i = 0; i < len; i++) p[i] ^= mask[i & 3];
} }
@@ -113,11 +115,14 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
c->is_closing = 1; // Some just, not an HTTP request c->is_closing = 1; // Some just, not an HTTP request
} else if (n > 0) { } else if (n > 0) {
if (n < 15 || memcmp(c->recv.buf + 9, "101", 3) != 0) { if (n < 15 || memcmp(c->recv.buf + 9, "101", 3) != 0) {
LOG(LL_ERROR, ("%p WS handshake error: %.*s", c->fd, 15, c->recv.buf)); LOG(LL_ERROR,
("%lu WS handshake error: %.*s", c->id, 15, c->recv.buf));
c->is_closing = 1; c->is_closing = 1;
} else { } else {
struct mg_http_message hm;
mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
c->is_websocket = 1; c->is_websocket = 1;
mg_call(c, MG_EV_WS_OPEN, NULL); mg_call(c, MG_EV_WS_OPEN, &hm);
} }
mg_iobuf_delete(&c->recv, n); mg_iobuf_delete(&c->recv, n);
} else { } else {
@@ -127,24 +132,23 @@ static void mg_ws_cb(struct mg_connection *c, int ev, void *ev_data,
while (ws_process(c->recv.buf, c->recv.len, &msg) > 0) { while (ws_process(c->recv.buf, c->recv.len, &msg) > 0) {
char *s = (char *) c->recv.buf + msg.header_len; char *s = (char *) c->recv.buf + msg.header_len;
struct mg_ws_message m = {{s, msg.data_len}, msg.flags};
switch (msg.flags & WEBSOCKET_FLAGS_MASK_OP) { switch (msg.flags & WEBSOCKET_FLAGS_MASK_OP) {
case WEBSOCKET_OP_PING: case WEBSOCKET_OP_PING:
LOG(LL_DEBUG, ("%s", "WS PONG")); LOG(LL_DEBUG, ("%s", "WS PONG"));
mg_ws_send(c, s, msg.data_len, WEBSOCKET_OP_PONG); mg_ws_send(c, s, msg.data_len, WEBSOCKET_OP_PONG);
mg_call(c, MG_EV_WS_CTL, &m);
break; break;
case WEBSOCKET_OP_PONG: case WEBSOCKET_OP_PONG:
// Ignore mg_call(c, MG_EV_WS_CTL, &m);
break; break;
case WEBSOCKET_OP_CLOSE: { case WEBSOCKET_OP_CLOSE:
struct mg_ws_message evd = {{s, msg.data_len}, msg.flags}; LOG(LL_ERROR, ("%lu Got WS CLOSE", c->id));
LOG(LL_ERROR, ("%p Got WS CLOSE", c->fd)); mg_call(c, MG_EV_WS_CTL, &m);
mg_call(c, MG_EV_WS_MSG, &evd);
c->is_closing = 1; c->is_closing = 1;
return; return;
}
default: { default: {
struct mg_ws_message evd = {{s, msg.data_len}, msg.flags}; mg_call(c, MG_EV_WS_MSG, &m);
mg_call(c, MG_EV_WS_MSG, &evd);
break; break;
} }
} }
@@ -193,9 +197,15 @@ struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
return c; return c;
} }
void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm) { void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
const char *fmt, ...) {
struct mg_str *wskey = mg_http_get_header(hm, "Sec-WebSocket-Key"); struct mg_str *wskey = mg_http_get_header(hm, "Sec-WebSocket-Key");
c->pfn = mg_ws_cb; c->pfn = mg_ws_cb;
if (wskey != NULL) ws_handshake(c, wskey->ptr, wskey->len); if (wskey != NULL) {
va_list ap;
va_start(ap, fmt);
ws_handshake(c, wskey->ptr, wskey->len, fmt, ap);
va_end(ap);
}
c->is_websocket = 1; c->is_websocket = 1;
} }
+2 -1
View File
@@ -20,5 +20,6 @@ struct mg_ws_message {
struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data, mg_event_handler_t fn, void *fn_data,
const char *fmt, ...); const char *fmt, ...);
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *); void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
const char *fmt, ...);
size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op); size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff. Show more