Compare commits

...
102 Commits
Author SHA1 Message Date
Sergey Lyubka 9d38e26b81 Add MG_HTTP_INDEX build var 2021-12-17 13:54:01 +00:00
Sergey Lyubka 4e75a0c81e 7.4 -> 7.5 2021-12-17 13:34:32 +00:00
Sergey Lyubka d311e967fa Add mingw target 2021-12-16 09:22:28 +00:00
Sergey Lyubka d0a6c111cb Call MG_EV_ERROR with c->is_closing set 2021-12-15 11:31:42 +00:00
Sergey Lyubka aa6d8b4662 Remove TLS custom_init() API 2021-12-15 11:04:43 +00:00
Sergey Lyubka 3f5fd89fd7 Pass more args to custom TLS init 2021-12-14 13:21:59 +00:00
Sergey Lyubka f43a5a005c Introduce custom TLS init function 2021-12-14 13:04:11 +00:00
Sergey Lyubka 755bf3c412 Split TLS files and introduce MG_ENABLE_CUSTOM_TLS 2021-12-14 12:42:41 +00:00
Sergey Lyubka 84f3fb0ba5 Move EXTERN_C to util.h 2021-12-14 12:11:05 +00:00
Sergey Lyubka f2df6c114d Example for mg_http_bauth 2021-12-13 15:14:29 +00:00
Sergey Lyubka f93659e303 Merge branch 'master' of github.com:cesanta/mongoose 2021-12-13 15:08:14 +00:00
Sergey Lyubka 226404d39c Example for mg_http_bauth 2021-12-13 15:08:05 +00:00
Sergey Lyubka c1bcc143b9 Merge pull request #1417 from DavidKorczynski/cifuzz-integration
Add CIFuzz integration
2021-12-13 11:35:31 +00:00
Sergey Lyubka 5ca9062d45 Less verbose serve_dir 2021-12-07 15:23:02 +00:00
Sergey Lyubka 0b209eaabb Add mg_http_get_header_var() 2021-12-03 09:54:03 +00:00
David Korczynski 47ef191ea2 Add CIFuzz integration 2021-11-30 22:48:20 +00:00
Sergey Lyubka 25e76d761e Fix usage message 2021-11-29 14:00:03 +00:00
Sergey Lyubka 9731a51dae Recognise :PORT as a valid URL 2021-11-29 13:57:33 +00:00
Sergey Lyubka 81220fa590 Remove noisy debug 2021-11-26 16:23:06 +00:00
Sergey Lyubka 24e8c4d81a Increase SNMP test timeout 2021-11-25 17:31:41 +00:00
Sergey Lyubka 013520b0c0 Add note for mg_http_serve_dir() 2021-11-25 12:29:45 +00:00
Sergey Lyubka f012bcb027 Update issue template 2021-11-25 11:47:26 +00:00
Sergey Lyubka 922e1ce529 Make captive portal work on Linux 2021-11-23 17:52:54 +00:00
Sergey Lyubka 7a707cc64b Add README 2021-11-23 15:08:34 +00:00
Sergey Lyubka 1967acfef2 Make it build on vc98 2021-11-23 13:24:21 +00:00
Sergey Lyubka e24cb04003 Update URL 2021-11-23 09:37:03 +00:00
Sergey Lyubka 67974746ff Add json-rpc-over-ws example 2021-11-22 16:45:14 +00:00
Sergey Lyubka 8d916d41d7 Fix #1409 - remove VLA which confuses MSVC 2021-11-22 12:24:58 +00:00
Sergey Lyubka 08444e3942 Fix links and typos 2021-11-16 12:30:40 +00:00
Sergey Lyubka 9c41a0cd26 Fix links 2021-11-16 09:54:36 +00:00
Sergey Lyubka 2c50069d98 Do not clean the recv buffer 2021-11-14 16:47:40 +00:00
Sergey Lyubka 31059f6728 Add test.html 2021-11-14 14:31:51 +00:00
Sergey Lyubka 2000752b2c Remove dead code 2021-11-11 15:15:47 +00:00
Sergey Lyubka dd34830443 Update README 2021-11-11 15:11:57 +00:00
Sergey Lyubka c58f4ee51b Add symlinks 2021-11-11 14:21:52 +00:00
Sergey Lyubka 00f7e8cb2a Support wildcards 2021-11-11 12:08:17 +00:00
Sergey Lyubka 439904e27f Add signal handler 2021-11-11 10:35:41 +00:00
Sergey Lyubka 4d6590ece2 Add readme 2021-11-11 10:29:16 +00:00
Sergey Lyubka 9b0c4b970d Add README.md 2021-11-11 08:26:31 +00:00
Sergey Lyubka a80ce1b12a Add signal handler 2021-11-11 07:36:25 +00:00
Sergey Lyubka 57dd74c180 Add reconnection logic 2021-11-10 12:57:02 +00:00
Sergey Lyubka 90c67e4226 Reformatting for better readability 2021-11-06 11:29:56 +00:00
Sergey Lyubka 093cd6a6a3 Fix FD_CLOEXEC setting 2021-11-05 13:30:12 +00:00
Sergey Lyubka 975db86306 Add READMEs 2021-11-05 13:02:36 +00:00
Sergey Lyubka eba688e11b Update docs 2021-11-04 23:07:47 +00:00
Sergey Lyubka 05b44204d5 Set FD_CLOEXEC properly 2021-11-04 18:28:32 +00:00
Sergey Lyubka 0bb017c1d5 Add examples/mqtt-client-aws-iot 2021-11-03 20:58:57 +00:00
Sergey Lyubka a6efd0b335 Merge branch 'master' into dec 2021-11-02 22:43:59 +00:00
Sergey Lyubka c3d237e9c6 More verbose log 2021-11-02 22:43:23 +00:00
Sergey Lyubka 09d28f5570 Print a clarifying message at start 2021-11-02 16:53:57 +00:00
Sergey Lyubka ba02937ec6 Squash warnings 2021-11-02 16:40:25 +00:00
Sergey Lyubka 3d78b14deb Fix #1396 - JS sorting code for dir listing 2021-11-02 16:01:06 +00:00
Sergey Lyubka f558551d1d Fix #1395 - show decoded URI in dir listing 2021-11-02 15:11:56 +00:00
Sergey Lyubka 12fe66370f Fix #1393 - cleanup mime types 2021-11-01 20:31:45 +00:00
Sergey Lyubka 423aaa492c Fix #1394 - mg_mqtt_sub(). Add MG_EV_OPEN, too. 2021-11-01 16:20:00 +00:00
Sergey Lyubka 2016586475 Clearer client init 2021-11-01 13:29:44 +00:00
Sergey Lyubka bf88167152 Add missing test files 2021-10-31 12:50:06 +00:00
Sergey Lyubka 52be84f04e Add user and pass to struct mg_mqtt_opts 2021-10-31 12:48:32 +00:00
Sergey Lyubka 03d187b147 Fix #1375 - change event processing order 2021-10-31 12:40:56 +00:00
Sergey Lyubka 3329047579 More explicit check on hm->uri.len 2021-10-31 12:37:40 +00:00
Sergey Lyubka 86c70e5abb Fix #1391 - make remove_double_dots less aggressive 2021-10-31 12:36:34 +00:00
Sergey Lyubka b17f4108ea Change MG_PATH_MAX for ESP8266 2021-10-30 19:34:53 +01:00
Sergey Lyubka 4e523cc455 Remove binary section 2021-10-27 20:13:05 +01:00
Sergey Lyubka 49c9be9159 Set MG_IO_SIZE -> 128 2021-10-27 19:20:52 +01:00
Sergey Lyubka f1bfe0760f Optimise TLS read 2021-10-27 19:18:44 +01:00
Sergey Lyubka 35f02fdaad Use small IO buffer 2021-10-27 19:18:19 +01:00
Sergey Lyubka 6ac09966ba Fix #1374 - fix ESP32 build 2021-10-22 22:06:10 +01:00
Sergey Lyubka af9a7617f5 Merge pull request #1381 from ptsolakos/master
Certificate revocation checking when using mbedtls
2021-10-22 20:30:47 +01:00
Sergey Lyubka 42c89732c8 Remove private.h 2021-10-22 19:41:26 +01:00
Sergey Lyubka 054475ddbf Fix #1376 - handle comma for unquoted header vars 2021-10-22 14:33:51 +01:00
Sergey Lyubka 126d556d6a Merge pull request #1380 from jameshilliard/fix-time
util: Actually return uptime on OSX/Linux for mg_millis
2021-10-22 14:06:44 +01:00
Sergey Lyubka 529a302e77 Merge pull request #1379 from jameshilliard/fix-sntp
sntp: Don't set Leap Indicator in client messages.
2021-10-22 14:04:54 +01:00
Sergey Lyubka acbf978452 Merge pull request #1378 from jameshilliard/undef
Fix some undefined macro warnings.
2021-10-22 14:03:42 +01:00
Sergey Lyubka 90b22716ea Merge branch 'master' into undef 2021-10-22 14:03:25 +01:00
Sergey Lyubka fb90c504a6 Remove private.h 2021-10-22 14:00:31 +01:00
Sergey Lyubka 65fd834654 Increate mqtt test timeout 2021-10-22 13:58:51 +01:00
Sergey Lyubka a84acf5b8b Merge pull request #1385 from zudwalex/move_stm32_examples
Move STM32 examples to stm32 folder
2021-10-22 13:39:13 +01:00
Sergey Lyubka e9505db146 Merge pull request #1384 from zudwalex/master
Add more NXP examples and move them to nxp folder
2021-10-22 13:39:02 +01:00
Sergey Lyubka 4fb49b011e Merge pull request #1377 from jameshilliard/missing-prototypes
Make private functions static and add missing prototypes.
2021-10-22 13:16:06 +01:00
Alex Alashkin 6742991469 Move STM32 examples to stm32 folder 2021-10-22 14:19:50 +03:00
Alex Alashkin 2430cc5aac Add more NXP examples and move them to nxp folder 2021-10-22 14:04:15 +03:00
Sergey Lyubka 54590aaf26 Use UDP sendto() on win32 only 2021-10-22 09:14:18 +01:00
Sergey Lyubka 7c5e67b272 Fix #1382 - use sendto for UDP send 2021-10-22 08:56:45 +01:00
Sergey Lyubka c62c8ea4dd Correct licensing URL 2021-10-20 10:04:49 +01:00
Panagiotis Tsolakos 383653d608 mbedtls CRL support
Certificate revocation checking when using mbedtls
2021-10-14 17:56:31 +02:00
James Hilliard 001dc3f33d util: Actually return uptime on OSX/Linux for mg_millis
The documentation for mg_millis indicates it will:
Return current uptime in milliseconds.

So we should use API's that actually return uptime.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2021-10-13 17:06:18 -06:00
James Hilliard 37d45b3fbb sntp: Don't set Leap Indicator in client messages.
Per RFC4330:
This field is significant only in server messages

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2021-10-13 16:48:33 -06:00
James Hilliard f72b8c6034 Fix some undefined macro warnings.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2021-10-13 13:06:00 -06:00
James Hilliard c11e5a9383 Make private functions static and add missing prototypes.
Fixes:
mongoose/mongoose.c:180:8: warning: no previous prototype for ‘mg_dns_parse_name’ [-Wmissing-prototypes]
  180 | size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, char *dst,
      |        ^~~~~~~~~~~~~~~~~
mongoose/mongoose.c:306:6: warning: no previous prototype for ‘mg_dns_send’ [-Wmissing-prototypes]
  306 | void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
      |      ^~~~~~~~~~~
mongoose/mongoose.c:925:6: warning: no previous prototype for ‘mg_http_parse_headers’ [-Wmissing-prototypes]
  925 | void mg_http_parse_headers(const char *s, const char *end,
      |      ^~~~~~~~~~~~~~~~~~~~~
mongoose/mongoose.c:1125:7: warning: no previous prototype for ‘mg_http_etag’ [-Wmissing-prototypes]
 1125 | char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
      |       ^~~~~~~~~~~~
mongoose/mongoose.c:2578:6: warning: no previous prototype for ‘mg_sha1_transform’ [-Wmissing-prototypes]
 2578 | void mg_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
      |      ^~~~~~~~~~~~~~~~~
mongoose/mongoose.c:2976:8: warning: no previous prototype for ‘mg_open_listener’ [-Wmissing-prototypes]
 2976 | SOCKET mg_open_listener(const char *url, struct mg_addr *addr) {
      |        ^~~~~~~~~~~~~~~~

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2021-10-12 20:18:11 -06:00
Sergey Lyubka b5147a149e Merge pull request #1373 from zudwalex/fix_board_ci
Use mongoose.[ch] from main repo
2021-10-06 12:31:27 +01:00
Alex Alashkin 655529f30b Use mongoose.[ch] from parent repo 2021-10-06 13:15:12 +03:00
novlean fd5319e0ce Merge pull request #1372 from cesanta/novlean-patch-1
license
2021-10-05 17:09:43 +01:00
novlean f8c84ca672 license 2021-10-05 17:09:35 +01:00
novlean 1b81ac8916 Merge pull request #1371 from cesanta/novlean-patch-1
mongoose.ws
2021-10-05 16:54:43 +01:00
novlean c64e2ebb16 mongoose.ws 2021-10-05 16:54:34 +01:00
Sergey Lyubka 3771f2c088 Merge branch 'master' of github.com:cesanta/mongoose 2021-10-04 19:47:34 +01:00
Sergey Lyubka 6d398bd5c7 Fix mg_random() for ESP32 2021-10-04 19:47:24 +01:00
Sergey Lyubka 5c355a5ff3 Merge pull request #1368 from zudwalex/nxp_examples
Add NXP examples
2021-10-04 12:04:00 +01:00
Alex Alashkin d2f8b346e6 Add nxp to actions 2021-10-04 13:15:32 +03:00
Alex Alashkin 5cdaa931f9 Add NXP examples 2021-10-04 12:54:15 +03:00
Sergey Lyubka 693863a51e Merge pull request #1366 from zudwalex/fix_rtos_lwip
Add missed include for FreeRTOS/LwIP arch
2021-10-02 22:45:08 +01:00
Alex Alashkin bc7178e5f7 Add missed include 2021-09-29 12:25:25 +03:00
139 changed files with 3135 additions and 955 deletions

No files matched your search

+4 -6
View File
@@ -6,9 +6,7 @@
# Environment # Environment
- mongoose version (e.g. 7.3): _X.Y_ - mongoose version: _X.Y_
- Compiler/IDE (e.g. arm-none-eabi-gcc 9.3): _?_ - Compiler/IDE and SDK: ZZZ version _X.Y_
- Target OS (e.g. FreeRTOS 10.3.1, Linux 5.8): _?_ - Target hardware/board: _?_
- Target hardware/board (e.g. STM32F411): _?_ - Connectivity chip/module: _?_
- Connectivity chip/module (e.g. WizNet W5500): _?_
- Target SDK (e.g. STM32CubeF1 1.8.4): _?_
+26
View File
@@ -0,0 +1,26 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'mongoose'
dry-run: false
language: c++
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'mongoose'
fuzz-seconds: 600
dry-run: false
language: c++
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts
+119 -5
View File
@@ -68,28 +68,142 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: stm32-freertos-tcp - name: stm32-freertos-tcp
run: make -C examples/stm32-freertos-tcp build run: make -C examples/stm32/stm32-freertos-tcp build
stm32-nucleo-f746z: stm32-nucleo-f746z:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: stm32-nucleo-f746z - name: stm32-nucleo-f746z
run: make -C examples/stm32-nucleo-f746z build run: make -C examples/stm32/stm32-nucleo-f746z build
stm32-nucleo-f429z: stm32-nucleo-f429z:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: stm32-nucleo-f429z - name: stm32-nucleo-f429z
run: make -C examples/stm32-nucleo-f429z build run: make -C examples/stm32/stm32-nucleo-f429z build
stm32-nucleo-h743z: stm32-nucleo-h743z:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: stm32-nucleo-h743z - name: stm32-nucleo-h743z
run: make -C examples/stm32-nucleo-h743z build run: make -C examples/stm32/stm32-nucleo-h743z build
nxp-mimxrt1020-azurertos: nxp-mimxrt1020-azurertos:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: nxp-mimxrt1020-azurertos - name: nxp-mimxrt1020-azurertos
run: make -C examples/nxp-mimxrt1020-azurertos build run: make -C examples/nxp/nxp-mimxrt1020-azurertos build
nxp-frdmk66f-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-frdmk66f-freertos
run: make -C examples/nxp/nxp-frdmk66f-freertos build
nxp-lpcxpresso54s018m-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-lpcxpresso54s018m-freertos
run: make -C examples/nxp/nxp-lpcxpresso54s018m-freertos build
nxp-mimxrt1020-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-mimxrt1020-freertos
run: make -C examples/nxp/nxp-mimxrt1020-freertos build
nxp-evkbimxrt1050-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkbimxrt1050-lwip-freertos
run: make -C examples/nxp/nxp-evkbimxrt1050-lwip-freertos build
nxp-evkmimxrt1020-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1020-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1020-lwip-freertos build
nxp-evkmimxrt1024-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1024-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1024-lwip-freertos build
nxp-evkmimxrt1060-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1060-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1060-lwip-freertos build
nxp-evkmimxrt1064-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1064-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1064-lwip-freertos build
nxp-evkmimxrt1160-cm7-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1160-cm7-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1160-cm7-lwip-freertos build
nxp-evkmimxrt1170-cm4-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1170-cm4-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1170-cm4-lwip-freertos build
nxp-evkmimxrt1170-cm7-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-evkmimxrt1170-cm7-lwip-freertos
run: make -C examples/nxp/nxp-evkmimxrt1170-cm7-lwip-freertos build
nxp-frdmk64f-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-frdmk64f-lwip-freertos
run: make -C examples/nxp/nxp-frdmk64f-lwip-freertos build
nxp-frdmk66f-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-frdmk66f-lwip-freertos
run: make -C examples/nxp/nxp-frdmk66f-lwip-freertos build
nxp-lpcxpresso54018-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-lpcxpresso54018-lwip-freertos
run: make -C examples/nxp/nxp-lpcxpresso54018-lwip-freertos build
nxp-lpcxpresso54608-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-lpcxpresso54608-lwip-freertos
run: make -C examples/nxp/nxp-lpcxpresso54608-lwip-freertos build
nxp-lpcxpresso54618-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-lpcxpresso54618-lwip-freertos
run: make -C examples/nxp/nxp-lpcxpresso54618-lwip-freertos build
nxp-lpcxpresso54628-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-lpcxpresso54628-lwip-freertos
run: make -C examples/nxp/nxp-lpcxpresso54628-lwip-freertos build
nxp-twrk65f180m-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-twrk65f180m-lwip-freertos
run: make -C examples/nxp/nxp-twrk65f180m-lwip-freertos build
nxp-twrkv58f220m-lwip-freertos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: nxp-twrkv58f220m-lwip-freertos
run: make -C examples/nxp/nxp-twrkv58f220m-lwip-freertos build
+1 -1
View File
@@ -13,4 +13,4 @@ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details. See the GNU General Public License for more details.
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://mongoose.ws/licensing/>.
+3 -3
View File
@@ -1,7 +1,7 @@
SRCS = mongoose.c test/unit_test.c test/packed_fs.c SRCS = mongoose.c test/unit_test.c test/packed_fs.c
HDRS = $(wildcard src/*.h) HDRS = $(wildcard src/*.h)
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1 DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1
WARN ?= -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion WARN ?= -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wmissing-prototypes -Wundef
OPTS ?= -O3 -g3 OPTS ?= -O3 -g3
INCS ?= -Isrc -I. INCS ?= -Isrc -I.
CFLAGS ?= $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) $(EXTRA) CFLAGS ?= $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) $(EXTRA)
@@ -122,10 +122,10 @@ uninstall:
rm -rf $(DESTDIR)$(PREFIX)/lib/libmongoose.a $(DESTDIR)$(PREFIX)/lib/libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/include/mongoose.h $(DESTDIR)$(PREFIX)/lib/libmongoose.so rm -rf $(DESTDIR)$(PREFIX)/lib/libmongoose.a $(DESTDIR)$(PREFIX)/lib/libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/include/mongoose.h $(DESTDIR)$(PREFIX)/lib/libmongoose.so
mongoose.c: Makefile $(wildcard src/*) mongoose.c: Makefile $(wildcard src/*)
(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/*.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; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/config.h src/arch.h src/arch_*.h src/str.h src/log.h src/timer.h src/util.h src/fs.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/config.h src/arch.h src/arch_*.h src/str.h src/log.h src/timer.h src/util.h src/fs.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
clean: clean:
rm -rf $(PROG) *.o *.dSYM unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack rm -rf $(PROG) *.o *.dSYM unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack
+5 -23
View File
@@ -33,15 +33,11 @@ robust, and easy. Features include:
interface on devices, RESTful API services, telemetry data exchange, remote interface on devices, RESTful API services, telemetry data exchange, remote
control for a product, remote software updates, remote monitoring, and others control for a product, remote software updates, remote monitoring, and others
- Deployed to hundreds of millions devices in production environment worldwide - Deployed to hundreds of millions devices in production environment worldwide
- See [Case Studies](https://cesanta.com/case-studies.html) from our respected - See [Case Studies](https://mongoose.ws/case-studies/) from our respected
customers like Schneider Electric (industrial automation), Broadcom (semiconductors), Schenck Process customers like [Schneider Electric](https://mongoose.ws/case-studies/schneider-electric/) (industrial automation), [Broadcom](https://mongoose.ws/case-studies/broadcom/) (semiconductors), [Pilz](https://mongoose.ws/case-studies/pilz/) (industrial automation), and others
(industrial engineering), and others - See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products
- See [Testimonials](https://cesanta.com/testimonials.html) from engineers that - We provide [commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration
integrated Mongoose in their commercial products assistance - don't hesitate to [contact us](https://mongoose.ws/contact/)
- We provide [commercial licensing](https://cesanta.com/licensing.html),
[support](https://cesanta.com/support.html), consultancy and integration
assistance - don't hesitate to
[contact us](https://www.cesanta.com/contact.html)
# Security # Security
@@ -80,20 +76,6 @@ This software is often used together with Mongoose:
Used to implement scripting support Used to implement scripting support
# Precompiled web server binary
We have built a ready-to-go, precompiled web server executable application for
Windows and Mac. It requires no installation and no configuration to start.
Simply copy `mongoose.exe` to any directory you want to serve, and double-click
on `mongoose.exe` to start it. Mongoose will iconify to a system tray and
start serving your directory to a local network.
It is a great tool for sharing your files, or for local web or game
development. It has a unique feature - an ability to share your local files via
a global URL.
Visit [download page](https://mongoose.ws/) to try it out.
# Contributions # Contributions
Contributions are welcome! Please follow the guidelines below: Contributions are welcome! Please follow the guidelines below:
+101 -43
View File
@@ -5,7 +5,7 @@
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 connects devices non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It connects devices
and brings them online. Since 2004, a number of open source and commerical and brings them online. Since 2004, a number of open source and commercial
products have utilized it. It even runs on the products have utilized it. It even runs on the
International Space Station! Mongoose makes embedded network programming fast, International Space Station! Mongoose makes embedded network programming fast,
robust, and easy. robust, and easy.
@@ -23,9 +23,9 @@ structure, which has a number of fields. All fields are exposed to the
application by design, to give an application full visibility into application by design, to give an application full visibility into
Mongoose's internals. Mongoose's internals.
## 2-minute intergration guide ## 2-minute integration guide
In order to integrate Mongoose into an existing C/C++ aplicaction or firmware, In order to integrate Mongoose into an existing C/C++ application or firmware,
please use the following steps: please use the following steps:
**Step 1.** Copy **Step 1.** Copy
@@ -94,7 +94,7 @@ and sends an `MG_EV_SEND` event.
Each connection has an event handler function associated with it, which Each connection has an event handler function associated with it, which
must be implemented by the user. Event handler is the key element of must be implemented by the user. Event handler is the key element of
Mongoose, since it defines the connection's behavior. See below Mongoose, since it defines the connection's behavior. See below
for an example of an event handler funcion: for an example of an event handler function:
```c ```c
// Event handler function defines connection's behavior // Event handler function defines connection's behavior
@@ -108,7 +108,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- `struct mg_connection *c` - A connection that received an event - `struct mg_connection *c` - A connection that received an event
- `int ev` - An event number, defined in mongoose.h. For example, when data - `int ev` - An event number, defined in mongoose.h. For example, when data
arrives on an inbound connection, ev would be `MG_EV_READ` arrives on an inbound connection, `ev` would be `MG_EV_READ`
- `void *ev_data` - Points to the event-specific data, and it has a different - `void *ev_data` - Points to the event-specific data, and it has a different
meaning for different events. For example, for an `MG_EV_READ` event, meaning for different events. For example, for an `MG_EV_READ` event,
`ev_data` `ev_data`
@@ -232,8 +232,9 @@ Here is a list of build constants and their default values:
| Name | Default | Description | | Name | Default | Description |
| ---- | ------- | ----------- | | ---- | ------- | ----------- |
|MG_ENABLE_SOCKET | 1 | Use BSD socket low-level API | |MG_ENABLE_SOCKET | 1 | Use BSD socket low-level API |
|MG_ENABLE_MBEDTLS | 0 | Enable Mbed TLS library | |MG_ENABLE_MBEDTLS | 0 | Enable mbedTLS library |
|MG_ENABLE_OPENSSL | 0 | Enable OpenSSL library | |MG_ENABLE_OPENSSL | 0 | Enable OpenSSL library |
|MG_ENABLE_CUSTOM_TLS | 0 | Enable custom TLS library |
|MG_ENABLE_IPV6 | 0 | Enable IPv6 | |MG_ENABLE_IPV6 | 0 | Enable IPv6 |
|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 |
@@ -244,6 +245,7 @@ Here is a list of build constants and their default values:
|MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth | |MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth |
|MG_MAX_RECV_BUF_SIZE | (3 * 1024 * 1024) | Maximum recv buffer size | |MG_MAX_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 |
|MG_HTTP_INDEX | "index.html" | Index file for HTML directory |
|MG_ENABLE_LINES | undefined | If defined, show source file names in logs | |MG_ENABLE_LINES | undefined | If defined, show source file names in logs |
<span class="badge bg-danger">NOTE:</span> the `MG_IO_SIZE` constant also sets <span class="badge bg-danger">NOTE:</span> the `MG_IO_SIZE` constant also sets
@@ -660,7 +662,7 @@ common use cases:
A pipe's event handler should push data to all client connection. A pipe's event handler should push data to all client connection.
Use `c->label` to mark client connections. Use `c->label` to mark client connections.
- In order to serve a request, a long blocking operation should be performed. - In order to serve a request, a long blocking operation should be performed.
In this case, request handler assignes some marker to `c->label` and then In this case, request handler assigns some marker to `c->label` and then
spawns a handler task and gives a pipe to a spawns a handler task and gives a pipe to a
handler task. A handler does its job, and when data is ready, wakes up a handler task. A handler does its job, and when data is ready, wakes up a
manager. A pipe's event handler pushes data to a marked connection. manager. A pipe's event handler pushes data to a marked connection.
@@ -676,7 +678,7 @@ Parameters:
Return value: Pointer to created connection or `NULL` in case of error Return value: Pointer to created connection or `NULL` in case of error
Usage example: see [examples/multi-threaded](../examples/multi-threaded). Usage example: see [examples/multi-threaded](https://github.com/cesanta/mongoose/tree/master/examples/multi-threaded).
### mg\_mgr\_wakeup() ### mg\_mgr\_wakeup()
@@ -692,7 +694,7 @@ Parameters:
Return value: None Return value: None
Usage example: see [examples/multi-threaded](../examples/multi-threaded). Usage example: see [examples/multi-threaded](https://github.com/cesanta/mongoose/tree/master/examples/multi-threaded).
## HTTP ## HTTP
@@ -912,6 +914,10 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
Serve static files according to the given options. Note that in order to Serve static files according to the given options. Note that in order to
enable SSI, set a `-DMG_ENABLE_SSI=1` build flag. enable SSI, set a `-DMG_ENABLE_SSI=1` build flag.
<span class="badge bg-danger">NOTE: </span> Avoid double dots `..` in
the `root_dir`. If you need to
reference an upper-level directory, use absolute path.
Parameters: Parameters:
- `c` - Connection to use - `c` - Connection to use
- `hm` - HTTP message, that should be served - `hm` - HTTP message, that should be served
@@ -989,7 +995,7 @@ Return value: None
Usage examples: Usage examples:
Send a simple JSON respose: Send a simple JSON response:
```c ```c
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{\"result\": %d}", 123); mg_http_reply(c, 200, "Content-Type: application/json\r\n", "{\"result\": %d}", 123);
``` ```
@@ -1043,6 +1049,33 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} }
``` ```
### mg\_http\_get\_header\_var()
```c
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
```
Parse HTTP header (e.g. Cookie header) which has form
`name1=value1; name2=value2; ...` and fetch a given variable.
Parameters:
- `s` - HTTP header
- `name` - variable name name
Return value: a requested variable, or an empty string.
Usage example:
```c
struct mg_str *cookie = mg_http_get_header(hm, "Cookie");
struct mg_str token = mg_str("");
if (cookie != NULL) {
token = mg_http_get_header_var(*cookie, mg_str("access_token"));
}
```
### mg\_http\_get\_var() ### mg\_http\_get\_var()
```c ```c
@@ -1143,7 +1176,7 @@ int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
const char *dir); const char *dir);
``` ```
Handle file upload. See [file upload example](https://github.com/cesanta/mongoose/tree/master/examples/file-upload). Handle file upload. See [file upload example](https://github.com/cesanta/mongoose/tree/master/examples/file-uploads/).
This function expects a series of POST requests with file data. POST requests This function expects a series of POST requests with file data. POST requests
should have `name` and `offset` query string parameters set: should have `name` and `offset` query string parameters set:
@@ -1177,7 +1210,7 @@ Parameters:
- `hm` - POST message, containing parameters described above - `hm` - POST message, containing parameters described above
- `dir` - Path to directory - `dir` - Path to directory
Return value: Request body len or negative value on error Return value: Request body length or negative value on error
Usage example: Usage example:
@@ -1205,10 +1238,18 @@ Parameters:
Return value: None Return value: None
Usage example: Usage example which uses Basic auth to create Stripe subscription:
```c ```c
mg_http_bauth(c, "user_name", "password") // "user_name:password" is now in output buffer mg_printf(c, "POST /v1/subscriptions HTTP/1.1\r\n"
"Host: api.stripe.com\r\n"
"Transfer-Encoding: chunked\r\n");
mg_http_bauth(c, stripe_private_key, NULL); // Add Basic auth header
mg_printf(c, "%s", "\r\n"); // End HTTP headers
mg_http_printf_chunk(c, "&customer=%s", customer_id); // Set customer
mg_http_printf_chunk(c, "&items[0][price]=%s", price); // And price
mg_http_printf_chunk(c, ""); // End request
``` ```
### mg\_http\_next\_multipart() ### mg\_http\_next\_multipart()
@@ -1239,7 +1280,7 @@ Parameters:
Return value: offset to the next chunk, or 0 if there are no more chunks. Return value: offset to the next chunk, or 0 if there are no more chunks.
See [examples/form-upload](../examples/form-upload) for full usage example. See [File upload tutorial](../tutorials/file-uploads/) for full usage example.
<img src="images/mg_http_next_multipart.png"> <img src="images/mg_http_next_multipart.png">
@@ -1282,7 +1323,7 @@ Note: this function does not connect to peer, it allocates required resources an
Parameters: Parameters:
- `mgr` - Event manager to use - `mgr` - Event manager to use
- `url` - Specifies remote URL, e.g. `http://google.com` - `url` - Specifies remote URL, e.g. `http://google.com`
- `opts` - MQTT options, with client ID, qos, etc - `opts` - MQTT options, with client ID, QoS, etc
- `fn` - An event handler function - `fn` - An event handler function
- `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an - `fn_data` - An arbitrary pointer, which will be passed as `fn_data` when an
event handler is called. This pointer is also stored in a connection event handler is called. This pointer is also stored in a connection
@@ -1375,7 +1416,7 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op)
``` ```
Convert data in output buffer to WebSocket format. Useful then implementing protocol over WebSocket Convert data in output buffer to WebSocket format. Useful then implementing protocol over WebSocket
See [examples/mqtt-over-ws-client](../examples/mqtt-over-ws-client) for full example. See [examples/mqtt-over-ws-client](https://github.com/cesanta/mongoose/tree/master/examples/mqtt-over-ws-client) for full example.
Parameters: Parameters:
- `c` - Connection to use - `c` - Connection to use
@@ -1607,7 +1648,7 @@ Subscribe to topic.
Parameters: Parameters:
- `c` - Connection to use - `c` - Connection to use
- `topic` - Topic to subscribe - `topic` - Topic to subscribe
- `qos` - Required Qos - `qos` - Required QoS
Return value: none Return value: none
@@ -1629,7 +1670,7 @@ Initial position `pos` should be 4.
Parameters: Parameters:
- `mgs` - MQTT message - `mgs` - MQTT message
- `topic` - Pointer to `mg_str` to receive topic - `topic` - Pointer to `mg_str` to receive topic
- `qos` - Pointer to `uint8_t` to receive qos - `qos` - Pointer to `uint8_t` to receive QoS
- `pos` - Position to list from - `pos` - Position to list from
Return value: Next position, or 0 when done Return value: Next position, or 0 when done
@@ -1802,6 +1843,7 @@ while ((mg_mqtt_parse(buf, len, &mm)) == 0) {
```c ```c
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 *crl; // Certificate Revocation List. For 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
@@ -1813,6 +1855,9 @@ TLS initialisation structure:
- `ca` - Certificate Authority. Can be a filename or a string. Used to verify - `ca` - Certificate Authority. Can be a filename or a string. Used to verify
a certificate that the other end sends to us. If NULL, then certificate checking a certificate that the other end sends to us. If NULL, then certificate checking
is disabled is disabled
- `crl` - Certificate Revocation List. Can be a filename or a string. Used to
verify a certificate that the other end sends to us. If NULL, then certificate
revocation checking is disabled
- `cert` - Our own certificate. Can be a filename, or a string. If NULL, then - `cert` - Our own certificate. Can be a filename, or a string. If NULL, then
we don't authenticate with the other peer we don't authenticate with the other peer
- `certkey` - A key for a `cert`. Sometimes, a certificate and its key are - `certkey` - A key for a `cert`. Sometimes, a certificate and its key are
@@ -2003,18 +2048,29 @@ mg_usleep(1000000 /* 1 sec */)
## String ## String
### mg\_str ### struct mg\_str
In most cases, Mongoose uses `mg_str` struct for string representation rather than NULL-terminated C-strings. ```c
```
struct mg_str { struct mg_str {
const char *ptr; // Pointer to string data const char *ptr; // Pointer to string data
size_t len; // String len size_t len; // String len
}; };
``` ```
Note, that in general, `ptr` points to non-NULL terminated string, so do not use functions from C standard library on it. This structure represent an arbitrary chunk of memory, not necessarily
zero-terminated. This is a "mongoose string", and it gets used extensively
in the codebase instead of C zero-terminated strings.
For example, when an HTTP request is received, Mongoose created a
`struct mg_http_message` which has a collection of `struct mg_str` pointing
to request method, URI, headers, and so on. This way, Mongoose avoids
any heap allocations and does not modify the received buffer - instead, it
uses `struct mg_str` to describe various parts of HTTP request.
Same goes with many other cases.
NOTE: since `ptr` is not necessarily zero-terminated, do not use libc string
functions against it - like `strlen()` or `sscanf()`.
### mg\_str() ### mg\_str()
@@ -2045,12 +2101,13 @@ struct mg_str str = mg_str("Hello, world!);
struct mg_str mg_str_n(const char *s, size_t n); struct mg_str mg_str_n(const char *s, size_t n);
``` ```
Create Mongoose string from C-string `s` (can be non-NULL terminated, len is specified in `n`). <br> Create Mongoose string from C-string `s` (can be non-NULL terminated, length is
Note: This function doesn't duplicate provided string, but stores pointer within created `mg_str` structure. specified in `n`). Note: This function doesn't duplicate provided string,
but stores pointer within created `mg_str` structure.
Parameters: Parameters:
- `s` - Pointer to string to store in created `mg_str` - `s` - Pointer to string to store in created `mg_str`
- `n` - String len - `n` - String length
Return value: Created Mongoose string Return value: Created Mongoose string
@@ -2110,7 +2167,7 @@ if (mg_ncasecmp("hello1", "HELLO2", 5) == 0) {
int mg_vcmp(const struct mg_str *s1, const char *s2); int mg_vcmp(const struct mg_str *s1, const char *s2);
``` ```
Сompare mongoose string and C-string. Compare mongoose string and C-string.
Parameters: Parameters:
- `s1` - Pointer to Mongoose string to compare - `s1` - Pointer to Mongoose string to compare
@@ -2158,7 +2215,7 @@ if (mg_vcasecmp(str, "HELLO") == 0) {
int mg_strcmp(const struct mg_str str1, const struct mg_str str2); int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
``` ```
Сompare two mongoose strings. Compare two mongoose strings.
Parameters: Parameters:
- `str1`, `str2` - Pointers to Mongoose strings to compare - `str1`, `str2` - Pointers to Mongoose strings to compare
@@ -2234,7 +2291,7 @@ struct mg_str mg_strstrip(struct mg_str s)
Remove heading and trailing whitespace from mongoose string `s`. Remove heading and trailing whitespace from mongoose string `s`.
Paramaters: Parameters:
- `s` - Mongoose string for trimming - `s` - Mongoose string for trimming
Return value: Input string Return value: Input string
@@ -2264,9 +2321,9 @@ The glob pattern matching rules are as follows:
Parameters: Parameters:
- `pattern` - Pattern to match for - `pattern` - Pattern to match for
- `p_len` - Pattetn lenght - `p_len` - Pattern length
- `s` - String to match - `s` - String to match
- `s_len` - String lenght - `s_len` - String length
Return value: `true` if matches, `false` otherwise Return value: `true` if matches, `false` otherwise
@@ -2322,7 +2379,7 @@ It is a caller's responsibility to free() returned pointer.
Parameters: Parameters:
- `buf` - Data to hexdump - `buf` - Data to hexdump
- `len` - Data lenght - `len` - Data length
Return value: malloc-ed buffer with hexdumped data Return value: malloc-ed buffer with hexdumped data
@@ -2684,7 +2741,7 @@ void mg_sha1_update(mg_sha1_ctx *c, const unsigned char *data, size_t len);
Hash `len` bytes of `data` using SHA1 algorithm. Hash `len` bytes of `data` using SHA1 algorithm.
Parameters: Parameters:
- `c` - Sha1 context - `c` - SHA1 context
- `data` - Data to hash - `data` - Data to hash
- `len` - Data length - `len` - Data length
@@ -2739,7 +2796,7 @@ Parameters:
- `out` - Pointer to buffer to write result - `out` - Pointer to buffer to write result
- `pos` - Position in output buffer to write result - `pos` - Position in output buffer to write result
Return value: New position for futher operations Return value: New position for further operations
Usage example: Usage example:
@@ -2760,7 +2817,7 @@ Parameters:
- `buf` - Pointer to buffer to write finish mark - `buf` - Pointer to buffer to write finish mark
- `pos` - Position to write - `pos` - Position to write
Return value: New position for futher operations Return value: New position for further operations
```c ```c
char buf[10]; char buf[10];
@@ -2876,7 +2933,7 @@ This function prints data to a
temporary in-memory buffer first, then calls `mg_file_write()`. temporary in-memory buffer first, then calls `mg_file_write()`.
Parameters: Parameters:
- `path`- ath to file - `path`- path to file
- `fmt` - Format string in `printf()` semantics - `fmt` - Format string in `printf()` semantics
Return value: `true` on success, `false` otherwise Return value: `true` on success, `false` otherwise
@@ -2995,7 +3052,7 @@ uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
Calculate CRC32 checksum for a given buffer. An initial `crc` value should be `0`. Calculate CRC32 checksum for a given buffer. An initial `crc` value should be `0`.
Parameters: Parameters:
- `crc` - Initial crc value - `crc` - Initial CRC value
- `buf` - Data to calculate CRC32 - `buf` - Data to calculate CRC32
- `len` - Data size - `len` - Data size
@@ -3040,7 +3097,7 @@ Decode URL-encoded string `s` and write it into `to` buffer.
Parameters: Parameters:
- `s` - String to encode - `s` - String to encode
- `n` - String to encode lenght - `n` - String to encode length
- `to` - Pointer to output buffer - `to` - Pointer to output buffer
- `to_len` - Output buffer size - `to_len` - Output buffer size
- `form` - If non-zero, then `+` is decoded as whitespace. - `form` - If non-zero, then `+` is decoded as whitespace.
@@ -3189,10 +3246,10 @@ Parameters:
- `io` - iobuf to add data - `io` - iobuf to add data
- `offset` - Offset to add data - `offset` - Offset to add data
- `buf` - Data to add - `buf` - Data to add
- `len` - Data lenth - `len` - Data length
- `align` - Align boundary - `align` - Align boundary
Return value: new `io` len Return value: new `io` length
Usage example: Usage example:
@@ -3218,7 +3275,7 @@ Parameters:
- `offset` - Start offset - `offset` - Start offset
- `len` - Amount of bytes to delete - `len` - Amount of bytes to delete
Return value: New `io` len Return value: New `io` length
Usage example: Usage example:
@@ -3246,7 +3303,8 @@ Return port for given URL
Parameters: Parameters:
- `url` - URL to extract port - `url` - URL to extract port
Return value: Port for given url or `0` if url doesn't contain port and there isn't default port for url protocol Return value: Port for given URL or `0` if URL doesn't contain port and there
isn't default port for URL protocol
Usage example: Usage example:
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/captive-dns-portal/
+6 -3
View File
@@ -21,11 +21,14 @@ uint8_t answer[] = {
}; };
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_READ) { if (ev == MG_EV_OPEN) {
c->is_hexdumping = 1;
} else if (ev == MG_EV_READ) {
struct mg_dns_rr rr; // Parse first question, offset 12 is header size 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); size_t n = mg_dns_parse_rr(c->recv.buf, c->recv.len, 12, true, &rr);
LOG(LL_INFO, ("DNS request parsed, result=%d", (int) n));
if (n > 0) { if (n > 0) {
char buf[12 + n + sizeof(answer)]; char buf[512];
struct mg_dns_header *h = (struct mg_dns_header *) buf; struct mg_dns_header *h = (struct mg_dns_header *) buf;
memset(buf, 0, sizeof(buf)); // Clear the whole datagram memset(buf, 0, sizeof(buf)); // Clear the whole datagram
h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid
@@ -34,7 +37,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
h->flags = mg_htons(0x8400); // Authoritative response h->flags = mg_htons(0x8400); // Authoritative response
memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question
memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer
mg_send(c, buf, sizeof(buf)); // And send it! mg_send(c, buf, 12 + n + sizeof(answer)); // And send it!
} }
mg_iobuf_del(&c->recv, 0, c->recv.len); mg_iobuf_del(&c->recv, 0, c->recv.len);
} }
+1 -28
View File
@@ -1,28 +1 @@
# An example ESP32 application See detailed tutorial at https://mongoose.ws/tutorials/esp32/
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
```
+1 -2
View File
@@ -1,5 +1,4 @@
idf_component_register(SRCS "main.c" idf_component_register(SRCS "main.c"
"wifi.c" "wifi.c"
"../../../mongoose.c" "mongoose.c")
INCLUDE_DIRS "../../..")
component_compile_options(-DMG_ENABLE_LINES) component_compile_options(-DMG_ENABLE_LINES)
+11 -7
View File
@@ -9,6 +9,14 @@
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS! #define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
#define FS_ROOT "/spiffs" #define FS_ROOT "/spiffs"
// SPIFFS is flat, so tell Mongoose that the FS root is a directory
// This cludge is not required for filesystems with directory support
static int my_stat(const char *path, size_t *size, time_t *mtime) {
int flags = mg_fs_posix.stat(path, size, mtime);
if (strcmp(path, FS_ROOT) == 0) flags |= MG_FS_DIR;
return flags;
}
// Event handler for an server (accepted) connection. Implemented endpoints: // Event handler for an server (accepted) connection. Implemented endpoints:
// /api/stats - return JSON object with ESP32 stats (free RAM) // /api/stats - return JSON object with ESP32 stats (free RAM)
// any other - serve files from the filesystem // any other - serve files from the filesystem
@@ -18,18 +26,14 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (mg_http_match_uri(hm, "/api/stats")) { if (mg_http_match_uri(hm, "/api/stats")) {
mg_http_reply(c, 200, "", "{\"ram\": %lu}\n", xPortGetFreeHeapSize()); mg_http_reply(c, 200, "", "{\"ram\": %lu}\n", xPortGetFreeHeapSize());
} else { } else {
struct mg_http_serve_opts opts = {.root_dir = FS_ROOT}; struct mg_fs fs = mg_fs_posix;
fs.stat = my_stat;
struct mg_http_serve_opts opts = {.root_dir = FS_ROOT, .fs = &fs};
mg_http_serve_dir(c, hm, &opts); 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) { void app_main(void) {
// Mount filesystem // Mount filesystem
esp_vfs_spiffs_conf_t conf = { esp_vfs_spiffs_conf_t conf = {
+1
View File
@@ -0,0 +1 @@
../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../mongoose.h
+1
View File
@@ -1,3 +1,4 @@
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
#CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/file-uploads/
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/file-uploads/
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/http-client/
+2 -1
View File
@@ -1,5 +1,6 @@
PROG ?= example PROG ?= example
CFLAGS ?= -W -Wall -DMG_ENABLE_LINES=1
ifneq ($(MBEDTLS_DIR),) ifneq ($(MBEDTLS_DIR),)
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509 CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
@@ -12,7 +13,7 @@ all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS) $(DEBUGGER) ./$(PROG) $(ARGS)
$(PROG): main.c $(PROG): main.c
$(CC) ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 $(CFLAGS) -o $(PROG) main.c $(CC) ../../mongoose.c -I../.. $(CFLAGS) $(EFLAGS) -o $(PROG) main.c
clean: clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
+1 -1
View File
@@ -1,5 +1,5 @@
PROG ?= example PROG ?= example
CFLAGS ?= -DMG_IO_SIZE=8192 CFLAGS ?= -DMG_IO_SIZE=128
MBEDTLS_DIR ?= MBEDTLS_DIR ?=
ifeq "$(MBEDTLS_DIR)" "" ifeq "$(MBEDTLS_DIR)" ""
+16 -25
View File
@@ -32,21 +32,13 @@ static void forward_request(struct mg_http_message *hm,
(int) hm->uri.len, hm->uri.ptr)); (int) hm->uri.len, hm->uri.ptr));
} }
static void forward_fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_connection *c2 = fn_data; if (ev == MG_EV_READ && fn_data != NULL) {
if (ev == MG_EV_READ) { // All incoming data from the backend, forward to the client
if (c2 != NULL) { mg_send((struct mg_connection *) fn_data, c->recv.buf, c->recv.len);
// All incoming data from the backend, forward to the client mg_iobuf_del(&c->recv, 0, c->recv.len);
mg_send(c2, c->recv.buf, c->recv.len);
mg_iobuf_del(&c->recv, 0, 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);
}
} }
(void)ev_data; (void) ev_data;
} }
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) {
@@ -55,18 +47,17 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// Client request, create backend connection Note that we're passing // Client request, create backend connection Note that we're passing
// client connection `c` as fn_data for the created backend connection. // client connection `c` as fn_data for the created backend connection.
c2 = mg_connect(c->mgr, s_backend_url, forward_fn, c); c2 = mg_connect(c->mgr, s_backend_url, fn2, c);
c->fn_data = c2; if (c2 == NULL) {
mg_error(c, "Cannot create backend connection");
if (c2 != NULL) {
forward_request(hm, c2);
} else { } else {
c->is_closing = 1; if (mg_url_is_ssl(s_backend_url)) {
} struct mg_tls_opts opts = {.ca = "ca.pem"};
} else if (ev == MG_EV_CONNECT) { mg_tls_init(c2, &opts);
if (mg_url_is_ssl(s_backend_url)) { }
struct mg_tls_opts opts = {.ca = "ca.pem"}; c->fn_data = c2;
mg_tls_init(c, &opts); forward_request(hm, c2);
// c2->is_hexdumping = 1;
} }
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
if (c2 != NULL) c2->is_closing = 1; if (c2 != NULL) c2->is_closing = 1;
+3
View File
@@ -15,6 +15,9 @@ $(PROG): main.c Makefile
mongoose.exe: main.c mongoose.exe: main.c
$(VC98) wine cl ../../mongoose.c main.c $(VCFLAGS) ws2_32.lib /out:$@ $(VC98) wine cl ../../mongoose.c main.c $(VCFLAGS) ws2_32.lib /out:$@
mingw:
gcc ../../mongoose.c main.c -I../.. -W -Wall -DMG_ENABLE_IPV6=1 -DMG_ENABLE_LINES=1 -D_POSIX_C_SOURCE=200000L -lws2_32 -o mongoose.exe
linux: all linux: all
linux: CFLAGS += -O2 -fsanitize=address,undefined,shift,null,return,bounds,alignment,object-size,bool,enum -static-libasan linux: CFLAGS += -O2 -fsanitize=address,undefined,shift,null,return,bounds,alignment,object-size,bool,enum -static-libasan
#linux: CC = $(LIN) cc #linux: CC = $(LIN) cc
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/http-server/
+8 -10
View File
@@ -5,10 +5,9 @@
#include "mongoose.h" #include "mongoose.h"
static const char *s_debug_level = "2"; static const char *s_debug_level = "2";
static const char *s_root_dir = "."; static const char *s_root_dir = "."; // Attention: avoid double-dots, `..` !
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_ssi_pattern = "#.shtml";
// Handle interrupts, like Ctrl-C // Handle interrupts, like Ctrl-C
static int s_signo; static int s_signo;
@@ -20,8 +19,8 @@ static void signal_handler(int signo) {
// Simply serve static files from `s_root_dir` // Simply serve static files from `s_root_dir`
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_serve_opts opts = {.root_dir = s_root_dir, struct mg_http_serve_opts opts = {0};
.ssi_pattern = s_ssi_pattern}; opts.root_dir = s_root_dir;
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
(void) fn_data; (void) fn_data;
@@ -32,12 +31,11 @@ static void usage(const char *prog) {
"Mongoose v.%s\n" "Mongoose v.%s\n"
"Usage: %s OPTIONS\n" "Usage: %s OPTIONS\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"
" -v LEVEL - debug level, from 0 to 4, default: '%s'\n", " -v LEVEL - debug level, from 0 to 4, default: '%s'\n",
MG_VERSION, prog, s_debug_level, s_enable_hexdump, s_ssi_pattern, MG_VERSION, prog, s_enable_hexdump, s_root_dir, s_listening_address,
s_root_dir, s_listening_address); s_debug_level);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -52,8 +50,6 @@ int main(int argc, char *argv[]) {
s_root_dir = argv[++i]; s_root_dir = 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], "-v") == 0) { } else if (strcmp(argv[i], "-v") == 0) {
@@ -76,7 +72,9 @@ 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 v%s, serving [%s]", MG_VERSION, s_root_dir)); LOG(LL_INFO, ("Mongoose version : v%s", MG_VERSION));
LOG(LL_INFO, ("Listening on : %s", s_listening_address));
LOG(LL_INFO, ("Web root : [%s]", s_root_dir));
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
LOG(LL_INFO, ("Exiting on signal %d", s_signo)); LOG(LL_INFO, ("Exiting on signal %d", s_signo));
+10
View File
@@ -0,0 +1,10 @@
PROG ?= example
all: $(PROG)
$(DEBUGGER) ./$(PROG) $(ARGS)
$(PROG): main.c
$(CC) main.c mjson.c ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 $(CFLAGS) -o $(PROG)
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/json-rpc-over-websocket/
+86
View File
@@ -0,0 +1,86 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// See https://mongoose.ws/tutorials/json-rpc-over-websocket/
#include "mjson.h"
#include "mongoose.h"
static const char *s_listen_on = "ws://localhost:8000";
static const char *s_web_root = "web_root";
static void sum(struct jsonrpc_request *r) {
double a = 0.0, b = 0.0;
mjson_get_number(r->params, r->params_len, "$[0]", &a);
mjson_get_number(r->params, r->params_len, "$[1]", &b);
jsonrpc_return_success(r, "%g", a + b);
}
static void multiply(struct jsonrpc_request *r) {
double a = 0.0, b = 0.0;
mjson_get_number(r->params, r->params_len, "$[0]", &a);
mjson_get_number(r->params, r->params_len, "$[1]", &b);
jsonrpc_return_success(r, "%g", a * b);
}
// This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server
// any other URI serves static files from s_web_root
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_WS_OPEN) {
c->label[0] = 'W'; // Mark this connection as an established WS client
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/websocket")) {
// Upgrade to websocket. From now on, a connection is a full-duplex
// Websocket connection, which will receive MG_EV_WS_MSG events.
mg_ws_upgrade(c, hm, NULL);
} else {
// Serve static files
struct mg_http_serve_opts opts = {.root_dir = s_web_root};
mg_http_serve_dir(c, ev_data, &opts);
}
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
struct mg_str req = wm->data;
char *response = NULL;
jsonrpc_process(req.ptr, req.len, mjson_print_dynamic_buf, &response, NULL);
mg_ws_send(c, response, strlen(response), WEBSOCKET_OP_TEXT);
LOG(LL_INFO, ("[%.*s] -> [%s]", (int) req.len, req.ptr, response));
free(response);
}
(void) fn_data;
}
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
// Broadcast "hi" message to all connected websocket clients.
// Traverse over all connections
for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) {
// Send JSON-RPC notifications to marked connections
const char *msg = "{\"method\":\"hiya!!\",\"params\":[1,2,3]}";
if (c->label[0] == 'W') mg_ws_send(c, msg, strlen(msg), WEBSOCKET_OP_TEXT);
}
}
int main(void) {
struct mg_mgr mgr; // Event manager
struct mg_timer t1; // Timer
mg_mgr_init(&mgr); // Init event manager
mg_timer_init(&t1, 5000, MG_TIMER_REPEAT, timer_fn, &mgr); // Init timer
jsonrpc_init(NULL, NULL); // Init JSON-RPC instance
jsonrpc_export("sum", sum); // And export a couple
jsonrpc_export("mul", multiply); // of RPC functions
printf("Starting WS listener on %s/websocket\n", s_listen_on);
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); // Deallocate event manager
return 0;
}
File diff suppressed because it is too large. Load diff
+204
View File
@@ -0,0 +1,204 @@
// Copyright (c) 2018-2020 Cesanta Software Limited
// All rights reserved
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef MJSON_H
#define MJSON_H
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#ifndef MJSON_ENABLE_PRINT
#define MJSON_ENABLE_PRINT 1
#endif
#ifndef MJSON_ENABLE_RPC
#define MJSON_ENABLE_RPC 1
#endif
#ifndef MJSON_ENABLE_BASE64
#define MJSON_ENABLE_BASE64 1
#endif
#ifndef MJSON_ENABLE_MERGE
#define MJSON_ENABLE_MERGE 1
#endif
#ifndef MJSON_ENABLE_PRETTY
#define MJSON_ENABLE_PRETTY 1
#endif
#ifndef MJSON_ENABLE_NEXT
#define MJSON_ENABLE_NEXT 1
#endif
#ifndef MJSON_RPC_LIST_NAME
#define MJSON_RPC_LIST_NAME "rpc.list"
#endif
#ifndef MJSON_DYNBUF_CHUNK
#define MJSON_DYNBUF_CHUNK 256 // Allocation granularity for print_dynamic_buf
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define MJSON_ERROR_INVALID_INPUT (-1)
#define MJSON_ERROR_TOO_DEEP (-2)
#define MJSON_TOK_INVALID 0
#define MJSON_TOK_KEY 1
#define MJSON_TOK_STRING 11
#define MJSON_TOK_NUMBER 12
#define MJSON_TOK_TRUE 13
#define MJSON_TOK_FALSE 14
#define MJSON_TOK_NULL 15
#define MJSON_TOK_ARRAY 91
#define MJSON_TOK_OBJECT 123
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
#ifndef MJSON_MAX_DEPTH
#define MJSON_MAX_DEPTH 20
#endif
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
int mjson_find(const char *s, int len, const char *jp, const char **, int *);
int mjson_get_number(const char *s, int len, const char *path, double *v);
int mjson_get_bool(const char *s, int len, const char *path, int *v);
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
#if MJSON_ENABLE_NEXT
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
int *vlen, int *vtype);
#endif
#if MJSON_ENABLE_BASE64
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
#endif
#if MJSON_ENABLE_PRINT
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
struct mjson_fixedbuf {
char *ptr;
int size, len;
};
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list *ap);
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
int mjson_print_dbl(mjson_print_fn_t fn, void *, double, int width);
int mjson_print_null(const char *ptr, int len, void *userdata);
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
int mjson_snprintf(char *buf, size_t len, const char *fmt, ...);
char *mjson_aprintf(const char *fmt, ...);
#if MJSON_ENABLE_PRETTY
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
#endif
#if MJSON_ENABLE_MERGE
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
#endif
#endif // MJSON_ENABLE_PRINT
#if MJSON_ENABLE_RPC
void jsonrpc_init(mjson_print_fn_t, void *userdata);
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
struct jsonrpc_request {
struct jsonrpc_ctx *ctx;
const char *frame; // Points to the whole frame
int frame_len; // Frame length
const char *params; // Points to the "params" in the request frame
int params_len; // Length of the "params"
const char *id; // Points to the "id" in the request frame
int id_len; // Length of the "id"
const char *method; // Points to the "method" in the request frame
int method_len; // Length of the "method"
mjson_print_fn_t fn; // Printer function
void *fndata; // Printer function data
void *userdata; // Callback's user data as specified at export time
};
struct jsonrpc_method {
const char *method;
int method_sz;
void (*cb)(struct jsonrpc_request *);
struct jsonrpc_method *next;
};
// Main RPC context, stores current request information and a list of
// exported RPC methods.
struct jsonrpc_ctx {
struct jsonrpc_method *methods;
mjson_print_fn_t response_cb;
void *response_cb_data;
};
// Registers function fn under the given name within the given RPC context
#define jsonrpc_ctx_export(ctx, name, fn) \
do { \
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0}; \
m.next = (ctx)->methods; \
(ctx)->methods = &m; \
} while (0)
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
const char *message, const char *data_fmt, ...);
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
...);
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
mjson_print_fn_t fn, void *fndata, void *userdata);
extern struct jsonrpc_ctx jsonrpc_default_context;
extern void jsonrpc_list(struct jsonrpc_request *r);
#define jsonrpc_export(name, fn) \
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
#define jsonrpc_process(buf, len, fn, fnd, ud) \
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
#endif // MJSON_ENABLE_RPC
#ifdef __cplusplus
}
#endif
#endif // MJSON_H
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<body>
<h1>JSON-RPC over Websocket demo</h1>
<input id="url" type="text" placeholder="Type URL" value="ws://localhost:8000/websocket" style="width:20em;" />
<button id="connect">connect</button>
<div style="height: 0.3em;">&nbsp;</div>
<button id="btn1">Calculate 1 + 2</button>
<button id="btn2">Calculate 2 * 3</button>
<div style="margin-top: 1em;">Event log:</div>
<div id="log" style="background: #eee; height: 10em; padding: 0.5em; overflow:auto;"><div>
</body>
<script src="rpc-over-websocket.js"></script>
<script>
var rpc, E = function(id) { return document.getElementById(id); };
var url = E('url'), connect = E('connect'), btn1 = E('btn1'), btn2 = E('btn2'), msglog = E('log');
var enable = function(en) { btn1.disabled = btn2.disabled = !en; url.disabled = en; connect.innerHTML = en ? 'disconnect' : 'connect'; };
var log = text => msglog.innerHTML += text + '<br/>\n';
enable(false);
connect.onclick = function() {
console.log(rpc);
if (rpc) { rpc.close(); rpc = null; return; }
rpc = jsonrpc(url.value,
() => enable(true),
() => enable(false),
msg => log('NOTIFICATION: ' + JSON.stringify(msg)));
};
btn1.onclick = ev => rpc.call('sum', [1, 2]).then(res => log('SUM:' + JSON.stringify(res)));
btn2.onclick = ev => rpc.call('mul', [2, 3]).then(res => log('MUL:' + JSON.stringify(res)));
</script>
</html>
@@ -0,0 +1,36 @@
// JSON-RPC over Websocket implementation
var JSONRPC_TIMEOUT_MS = 1000;
var jsonrpc = function(url, onopen, onclose, onnotification) {
var rpcid = 0, pending = {}, ws = new WebSocket(url);
if (!ws) return null;
ws.onclose = onclose;
ws.onmessage = function(ev) {
const frame = JSON.parse(ev.data);
console.log('rcvd', frame, 'pending:', pending);
if (frame.id !== undefined) {
if (pending[frame.id] !== undefined) pending[frame.id](frame); // Resolve
delete (pending[frame.id]);
} else {
if (onnotification) onnotification(frame);
}
};
if (onopen) onopen();
return {
close: () => ws.close(),
call: function(method, params) {
const id = rpcid++, request = {id, method, params};
ws.send(JSON.stringify(request));
console.log('sent', request);
return new Promise(function(resolve, reject) {
setTimeout(JSONRPC_TIMEOUT_MS, function() {
if (pending[id] === undefined) return;
log('Timing out frame ', JSON.stringify(request));
delete (pending[id]);
reject();
});
pending[id] = x => resolve(x);
});
},
};
};
+11
View File
@@ -0,0 +1,11 @@
PROG ?= example
CFLAGS ?= -W -Wall -O2 -DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509
all: $(PROG)
$(RUN) ./$(PROG) $(ARGS)
$(PROG): main.c
$(CC) main.c ../../mongoose.c -I../.. $(CFLAGS) -o $(PROG)
clean:
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/aws-iot/
+1
View File
@@ -0,0 +1 @@
../../test/data/ca.pem
+91
View File
@@ -0,0 +1,91 @@
// Copyright (c) 2020 Cesanta Software Limited
// All rights reserved
//
// Example MQTT client. It performs the following steps:
// 1. Connects to the AWS IoT MQTT server
// 2. When connected, subscribes to the topic `s_rx_topic`
// 3. Publishes message `hello` to the `s_tx_topic` periodically
//
// This example requires TLS support. By default, it is built with mbedTLS,
// therefore make sure mbedTLS is installed. To build with OpenSSL, execute:
// make clean all CFLAGS="-W -Wall -DMG_ENABLE_OPENSSL=1 -lssl"
// In order to get MQTT URL, login to AWS IoT, click on "Settings" on the left
// bar, copy the "Endpoint" URL.
static const char *s_url =
"mqtts://a1pjwh2bop1ojt-ats.iot.eu-west-1.amazonaws.com";
// To create certificates:
// 1. Click Policies -> Create, fill fields:
// Name : Policy1
// Action : iot:*
// Resource ARN: *
// Effect : allow
// then, click "Create"
// 2. Click Manage -> Things -> Create things -> Create single thing -> Next
// Thing name: t1, no shadow, Next
// Auto-generate new certificate, Next
// Select policy Policy1, Create thing
// 3. From the dialog box that appears, download:
// xxx-certificate.pem.crt as cert.pem to the example directory
// xxx-private.pem.key as key.pem to the example directory
static const char *s_cert = "cert.pem";
static const char *s_key = "key.pem";
static const char *s_rx_topic = "d/rx";
static const char *s_tx_topic = "d/tx";
static int s_qos = 1;
#include "mongoose.h"
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_ERROR) {
// On error, log error message
LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_CONNECT) {
// Set up 2-way TLS that is required by AWS IoT
struct mg_tls_opts opts = {
.ca = "ca.pem", .cert = s_cert, .certkey = s_key};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful
struct mg_str topic = mg_str(s_rx_topic);
LOG(LL_INFO, ("Connected to %s", s_url));
LOG(LL_INFO, ("Subscribing to %s", s_rx_topic));
mg_mqtt_sub(c, &topic, s_qos);
c->label[0] = 'X'; // Set a label that we're logged in
} else if (ev == MG_EV_MQTT_MSG) {
// When we receive MQTT message, print it
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
LOG(LL_INFO, ("Received on %.*s : %.*s", (int) mm->topic.len, mm->topic.ptr,
(int) mm->data.len, mm->data.ptr));
} else if (ev == MG_EV_POLL && c->label[0] == 'X') {
static unsigned long prev_second;
unsigned long now_second = (*(unsigned long *) ev_data) / 1000;
if (now_second != prev_second) {
struct mg_str topic = mg_str(s_tx_topic), data = mg_str("{\"a\":123}");
LOG(LL_INFO, ("Publishing to %s", s_tx_topic));
mg_mqtt_pub(c, &topic, &data, s_qos, false);
prev_second = now_second;
}
}
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
LOG(LL_INFO, ("Got event %d, stopping...", ev));
*(bool *) fn_data = true; // Signal that we're done
}
}
int main(void) {
struct mg_mgr mgr;
struct mg_mqtt_opts opts = {.qos = s_qos};
bool done = false;
mg_mgr_init(&mgr); // Initialise event manager
LOG(LL_INFO, ("Connecting to %s", s_url)); // Inform that we're starting
mg_mqtt_connect(&mgr, s_url, &opts, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 1000); // Loop until done
mg_mgr_free(&mgr); // Finished, cleanup
return 0;
}
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/mqtt-client/
+51 -24
View File
@@ -3,9 +3,10 @@
// //
// Example MQTT client. It performs the following steps: // Example MQTT client. It performs the following steps:
// 1. Connects to the MQTT server specified by `s_url` variable // 1. Connects to the MQTT server specified by `s_url` variable
// 2. When connected, subscribes to the topic `s_topic` // 2. When connected, subscribes to the topic `s_sub_topic`
// 3. Publishes message `hello` to the `s_topic` // 3. Publishes message `hello` to the `s_pub_topic`
// 4. Receives that message back from the subscribed topic and exits // 4. Receives that message back from the subscribed topic and closes
// 5. Timer-based reconnection logic revives the connection when it is down
// //
// 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_DIR=/path/to/your/mbedtls/installation
@@ -13,10 +14,22 @@
#include "mongoose.h" #include "mongoose.h"
static const char *s_url = "mqtt://broker.hivemq.com:1883"; static const char *s_url = "mqtt://broker.hivemq.com:1883";
static const char *s_topic = "mg/test"; static const char *s_sub_topic = "mg/+/test";
static const char *s_pub_topic = "mg/clnt/test";
static int s_qos = 1;
static struct mg_connection *s_conn;
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo) {
s_signo = signo;
}
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_OPEN) {
LOG(LL_INFO, ("CREATED"));
// c->is_hexdumping = 1;
} else if (ev == MG_EV_ERROR) {
// On error, log error message // On error, log error message
LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data)); LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
@@ -27,36 +40,50 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} }
} else if (ev == MG_EV_MQTT_OPEN) { } else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful // MQTT connect is successful
struct mg_str topic = mg_str(s_topic), data = mg_str("hello"); struct mg_str subt = mg_str(s_sub_topic);
struct mg_str pubt = mg_str(s_pub_topic), data = mg_str("hello");
LOG(LL_INFO, ("CONNECTED to %s", s_url)); LOG(LL_INFO, ("CONNECTED to %s", s_url));
mg_mqtt_sub(c, &topic, 1); mg_mqtt_sub(c, &subt, s_qos);
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) topic.len, topic.ptr)); LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) subt.len, subt.ptr));
mg_mqtt_pub(c, &topic, &data, 1, false);
mg_mqtt_pub(c, &pubt, &data, s_qos, false);
LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr, LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
(int) topic.len, topic.ptr)); (int) pubt.len, pubt.ptr));
} else if (ev == MG_EV_MQTT_MSG) { } else if (ev == MG_EV_MQTT_MSG) {
// When we get echo response, print it // When we get echo response, print it
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
LOG(LL_INFO, ("RECEIVED %.*s <- %.*s", (int) mm->data.len, mm->data.ptr, LOG(LL_INFO, ("RECEIVED %.*s <- %.*s", (int) mm->data.len, mm->data.ptr,
(int) mm->topic.len, mm->topic.ptr)); (int) mm->topic.len, mm->topic.ptr));
c->is_closing = 1;
} else if (ev == MG_EV_CLOSE) {
LOG(LL_INFO, ("CLOSED"));
s_conn = NULL; // Mark that we're closed
} }
(void) fn_data;
}
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE || ev == MG_EV_MQTT_MSG) { // Timer function - recreate client connection if it is closed
*(bool *) fn_data = true; // Signal that we're done static void timer_fn(void *arg) {
} struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts = {.qos = s_qos,
.will_topic = mg_str(s_pub_topic),
.will_message = mg_str("goodbye")};
if (s_conn == NULL) s_conn = mg_mqtt_connect(mgr, s_url, &opts, fn, NULL);
} }
int main(void) { int main(void) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr;
struct mg_mqtt_opts opts; // MQTT connection options struct mg_timer timer;
bool done = false; // Event handler flips it to true when done int topts = MG_TIMER_REPEAT | MG_TIMER_RUN_NOW;
mg_mgr_init(&mgr); // Initialise event manager
memset(&opts, 0, sizeof(opts)); // Set MQTT options signal(SIGINT, signal_handler); // Setup signal handlers - exist event
opts.qos = 1; // Set QoS to 1 signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
opts.will_topic = mg_str(s_topic); // Set last will topic
opts.will_message = mg_str("goodbye"); // And last will message mg_mgr_init(&mgr); // Init event manager
mg_mqtt_connect(&mgr, s_url, &opts, fn, &done); // Create client connection mg_timer_init(&timer, 3000, topts, timer_fn, &mgr); // Init timer
while (done == false) mg_mgr_poll(&mgr, 1000); // Event loop while (s_signo == 0) mg_mgr_poll(&mgr, 1000); // Event loop, 1s timeout
mg_mgr_free(&mgr); // Finished, cleanup mg_mgr_free(&mgr); // Finished, cleanup
mg_timer_free(&timer); // Free timer resources
return 0; return 0;
} }
+1 -1
View File
@@ -32,7 +32,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
.will_topic = mg_str(s_topic), .will_topic = mg_str(s_topic),
.will_message = mg_str("goodbye")}; .will_message = mg_str("goodbye")};
size_t len = c->send.len; size_t len = c->send.len;
mg_mqtt_login(c, s_url, &opts); mg_mqtt_login(c, &opts);
mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY); mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY);
} else if (ev == MG_EV_WS_MSG) { } else if (ev == MG_EV_WS_MSG) {
struct mg_mqtt_message mm; struct mg_mqtt_message mm;
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/mqtt-server/
+22 -7
View File
@@ -20,6 +20,12 @@ struct sub {
}; };
static struct sub *s_subs = NULL; static struct sub *s_subs = NULL;
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo) {
s_signo = signo;
}
// Event handler function // Event handler function
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_MQTT_CMD) { if (ev == MG_EV_MQTT_CMD) {
@@ -53,6 +59,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
LIST_ADD_HEAD(struct sub, &s_subs, sub); LIST_ADD_HEAD(struct sub, &s_subs, sub);
LOG(LL_INFO, LOG(LL_INFO,
("SUB %p [%.*s]", c->fd, (int) sub->topic.len, sub->topic.ptr)); ("SUB %p [%.*s]", c->fd, (int) sub->topic.len, sub->topic.ptr));
// Change '+' to '*' for topic matching using mg_globmatch
for (size_t i = 0; i < sub->topic.len; i++) {
if (sub->topic.ptr[i] == '+') ((char *) sub->topic.ptr)[i] = '*';
}
resp[num_topics++] = qos; resp[num_topics++] = qos;
} }
mg_mqtt_send_header(c, MQTT_CMD_SUBACK, 0, num_topics + 2); mg_mqtt_send_header(c, MQTT_CMD_SUBACK, 0, num_topics + 2);
@@ -66,8 +76,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
LOG(LL_INFO, ("PUB %p [%.*s] -> [%.*s]", c->fd, (int) mm->data.len, LOG(LL_INFO, ("PUB %p [%.*s] -> [%.*s]", c->fd, (int) mm->data.len,
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr)); mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
for (struct sub *sub = s_subs; sub != NULL; sub = sub->next) { for (struct sub *sub = s_subs; sub != NULL; sub = sub->next) {
if (mg_strcmp(mm->topic, sub->topic) != 0) continue; if (mg_globmatch(sub->topic.ptr, sub->topic.len, mm->topic.ptr,
mg_mqtt_pub(sub->c, &mm->topic, &mm->data, 1, false); mm->topic.len)) {
mg_mqtt_pub(sub->c, &mm->topic, &mm->data, 1, false);
}
} }
break; break;
} }
@@ -88,10 +100,13 @@ 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_mgr_init(&mgr); // Initialise event manager signal(SIGINT, signal_handler); // Setup signal handlers - exist event
mg_mqtt_listen(&mgr, s_listen_on, fn, NULL); // Create MQTT listener signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop mg_mgr_init(&mgr); // Initialise event manager
mg_mgr_free(&mgr); LOG(LL_INFO, ("Starting on %s", s_listen_on)); // Inform that we're starting
mg_mqtt_listen(&mgr, s_listen_on, fn, NULL); // Create MQTT listener
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); // Event loop, 1s timeout
mg_mgr_free(&mgr); // Cleanup
return 0; return 0;
} }
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
build: build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME) git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/source/
make -C $(NAME) build make -C $(NAME) build
clean: clean:
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
build: build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME) git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/source/
make -C $(NAME) build make -C $(NAME) build
clean: clean:
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
build: build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME) git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/source/
make -C $(NAME) build make -C $(NAME) build
clean: clean:
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
build: build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME) git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/source/
make -C $(NAME) build make -C $(NAME) build
clean: clean:
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -1,13 +1,13 @@
# FreeRTOS/{FreeRTOS-Kernel V10.4.3,FreeRTOS-Plus-TCP V2.3.2} # FreeRTOS/{FreeRTOS-Kernel V10.4.3,FreeRTOS-Plus-TCP V2.3.2}
PROG = firmware PROG = firmware
ARCH = stm32f7 ARCH = stm32f7
PROJECT_ROOT_PATH = $(realpath $(CURDIR)/../..) PROJECT_ROOT_PATH = $(realpath $(CURDIR)/../../..)
DOCKER ?= docker run --rm -v $(PROJECT_ROOT_PATH):$(PROJECT_ROOT_PATH) -w $(CURDIR) mdashnet/armgcc DOCKER ?= docker run --rm -v $(PROJECT_ROOT_PATH):$(PROJECT_ROOT_PATH) -w $(CURDIR) mdashnet/armgcc
FREERTOS_KERNEL_PATH ?= $(PROJECT_ROOT_PATH)/test/freertos-kernel FREERTOS_KERNEL_PATH ?= $(PROJECT_ROOT_PATH)/test/freertos-kernel
FREERTOS_PLUS_TCP_PATH ?= $(PROJECT_ROOT_PATH)/test/freertos-tcp FREERTOS_PLUS_TCP_PATH ?= $(PROJECT_ROOT_PATH)/test/freertos-tcp
MONGOOSE_FLAGS = -DMG_ARCH=MG_ARCH_FREERTOS_TCP MONGOOSE_FLAGS = -DMG_ARCH=MG_ARCH_FREERTOS_TCP
MCU_FLAGS = -mcpu=cortex-m7 -mthumb -mfloat-abi=softfp -mfpu=vfpv4 MCU_FLAGS = -mcpu=cortex-m7 -mthumb -mfloat-abi=softfp -mfpu=vfpv4
#-mcpu=cortex-m7 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard #-mcpu=cortex-m7 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard
INCLUDES = -I$(PROJECT_ROOT_PATH) -I$(ARCH) -I$(FREERTOS_KERNEL_PATH)/include -I$(FREERTOS_PLUS_TCP_PATH)/include INCLUDES = -I$(PROJECT_ROOT_PATH) -I$(ARCH) -I$(FREERTOS_KERNEL_PATH)/include -I$(FREERTOS_PLUS_TCP_PATH)/include
@@ -36,19 +36,19 @@ SOURCES += $(FREERTOS_KERNEL_PATH)/portable/MemMang/heap_5.c
SOURCES += $(FREERTOS_KERNEL_PATH)/portable/GCC/ARM_CM7/r0p1/port.c SOURCES += $(FREERTOS_KERNEL_PATH)/portable/GCC/ARM_CM7/r0p1/port.c
# FreeRTOS TCP stack sources # FreeRTOS TCP stack sources
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_ARP.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_ARP.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DHCP.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DHCP.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DNS.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DNS.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_IP.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_IP.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Sockets.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Sockets.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Stream_Buffer.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Stream_Buffer.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_TCP_IP.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_TCP_IP.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_TCP_WIN.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_TCP_WIN.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_UDP_IP.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_UDP_IP.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/Common/phyHandling.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/Common/phyHandling.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/BufferManagement/BufferAllocation_1.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/BufferManagement/BufferAllocation_1.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/STM32Fxx/NetworkInterface.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/STM32Fxx/NetworkInterface.c
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c SOURCES += $(FREERTOS_PLUS_TCP_PATH)/portable/NetworkInterface/STM32Fxx/stm32fxx_hal_eth.c
OBJECTS = obj/boot.o $(SOURCES:%.c=obj/%.o) OBJECTS = obj/boot.o $(SOURCES:%.c=obj/%.o)
File renamed without changes.
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/Middlewares/Third_Party/Mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/Middlewares/Third_Party/Mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1,9 @@
NAME ?= $(notdir $(CURDIR))
build:
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
cp ./../../../mongoose.[ch] $(NAME)/Middlewares/Third_Party/Mongoose/
make -C $(NAME) build
clean:
rm -rf $(NAME)
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/stm32-nucleo-h743z/
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/websocket-client/
+1
View File
@@ -0,0 +1 @@
See detailed tutorial at https://mongoose.ws/tutorials/websocket-server/
+11 -12
View File
@@ -1,22 +1,21 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2020 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Example Websocket server. Usage: // Example Websocket server. See https://mongoose.ws/tutorials/websocket-server/
// 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
#include "mongoose.h" #include "mongoose.h"
static const char *s_listen_on = "http://localhost:8000"; static const char *s_listen_on = "ws://localhost:8000";
static const char *s_web_directory = "."; static const char *s_web_root = ".";
// This RESTful server implements the following endpoints: // This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server // /websocket - upgrade to Websocket, and implement websocket echo server
// /api/rest - respond with JSON string {"result": 123} // /api/rest - respond with JSON string {"result": 123}
// any other URI serves static files from s_web_directory // any other URI serves static files from s_web_root
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_OPEN) {
// c->is_hexdumping = 1;
} 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, "/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
@@ -27,21 +26,21 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
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
struct mg_http_serve_opts opts = {.root_dir = s_web_directory}; struct mg_http_serve_opts opts = {.root_dir = s_web_root};
mg_http_serve_dir(c, ev_data, &opts); 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!
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; 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_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
mg_iobuf_del(&c->recv, 0, c->recv.len);
} }
(void) fn_data; (void) fn_data;
} }
int main(void) { int main(void) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
printf("Starting WS listener on %s/websocket\n", s_listen_on);
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
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Websocket test client</h1>
<input id="url" type="text" placeholder="Type URL" value="ws://localhost:8000/websocket" style="width:20em;" />
<button id="connect">connect</button>
<div style="height: 0.3em;">&nbsp;</div>
<input id="message" type="text" placeholder="Type message" style="width: 20em;" />
<button id="send">send message</button>
<div style="margin-top: 1em;">Event log:</div>
<div id="log" style="background: #eee; height: 10em; padding: 0.5em;"><div>
</body>
<script>
var ws, E = function(id) { return document.getElementById(id); };
var url = E('url'), connect = E('connect'), message = E('message'), send = E('send'), log = E('log');
var enable = function(en) { message.disabled = send.disabled = !en; url.disabled = en; connect.innerHTML = en ? 'disconnect' : 'connect'; };
enable(false)
connect.onclick = function() {
if (ws) { ws.close(); return; }
ws = new WebSocket(url.value);
if (!ws) return;
ws.onopen = function() { log.innerHTML += 'CONNECTION OPENED<br/>'; }
ws.onmessage = function(ev) { log.innerHTML += 'RECEIVED: ' + ev.data + '<br/>'; }
ws.onerror = function(ev) { log.innerHTML += 'ERROR: ' + ev + '<br/>'; }
ws.onclose = function() { log.innerHTML += 'CONNECTION CLOSED<br/>'; enable(false); ws = null; }
enable(true);
};
send.onclick = function() {
if (!ws) return;
log.innerHTML += 'SENT: ' + message.value + '<br/>';
ws.send(message.value);
}
</script>
</html>
+215 -192
View File
@@ -5,7 +5,7 @@
// This software is dual-licensed: you can redistribute it and/or modify // This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as // it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this // published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>. // license, see http://www.gnu.org/licenses/
// //
// You are free to use this software under the terms of the GNU General // You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied // Public License, but WITHOUT ANY WARRANTY; without even the implied
@@ -13,18 +13,14 @@
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// 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.mongoose.ws/licensing/
#include "mongoose.h" #include "mongoose.h"
#ifdef MG_ENABLE_LINES
#line 1 "src/private.h"
#endif
void mg_connect_resolved(struct mg_connection *);
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
#line 1 "src/base64.c" #line 1 "src/base64.c"
#endif #endif
#include <string.h> #include <string.h>
static int mg_b64idx(int c) { static int mg_b64idx(int c) {
@@ -118,7 +114,6 @@ int mg_base64_decode(const char *src, int n, char *dst) {
struct dns_data { struct dns_data {
struct dns_data *next; struct dns_data *next;
struct mg_connection *c; struct mg_connection *c;
@@ -177,8 +172,8 @@ static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs,
return i; return i;
} }
size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, char *dst, static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs,
size_t dstlen) { char *dst, size_t dstlen) {
return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0); return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0);
} }
@@ -188,22 +183,22 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
memset(rr, 0, sizeof(*rr)); memset(rr, 0, sizeof(*rr));
if (len < sizeof(struct mg_dns_header)) return 0; // Too small if (len < sizeof(struct mg_dns_header)) return 0; // Too small
if (len > 512) return 0; // Too large, we don't expect that if (len > 512) return 0; // Too large, we don't expect that
if (s >= e) return 0; // Overflow if (s >= e) return 0; // Overflow
if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0) if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0)
return 0; return 0;
s += rr->nlen + 4; s += rr->nlen + 4;
if (s > e) return 0; if (s > e) return 0;
rr->atype = (uint16_t)(((uint16_t) s[-4] << 8) | s[-3]); rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]);
rr->aclass = (uint16_t)(((uint16_t) s[-2] << 8) | s[-1]); rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
if (is_question) return (size_t)(rr->nlen + 4); if (is_question) return (size_t) (rr->nlen + 4);
s += 6; s += 6;
if (s > e) return 0; if (s > e) return 0;
rr->alen = (uint16_t)(((uint16_t) s[-2] << 8) | s[-1]); rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]);
if (s + rr->alen > e) return 0; if (s + rr->alen > e) return 0;
return (size_t)(rr->nlen + rr->alen + 10); return (size_t) (rr->nlen + rr->alen + 10);
} }
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) { bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
@@ -303,8 +298,8 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data,
(void) fn_data; (void) fn_data;
} }
void mg_dns_send(struct mg_connection *c, const struct mg_str *name, static void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
uint16_t txnid, bool ipv6) { 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];
@@ -316,7 +311,7 @@ void mg_dns_send(struct mg_connection *c, const struct mg_str *name,
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++) {
if (name->ptr[i] == '.' || i >= name->len) { if (name->ptr[i] == '.' || i >= name->len) {
pkt.data[n] = (uint8_t)(i - n); pkt.data[n] = (uint8_t) (i - n);
memcpy(&pkt.data[n + 1], name->ptr + n, i - n); memcpy(&pkt.data[n + 1], name->ptr + n, i - n);
n = i + 1; n = i + 1;
} }
@@ -358,7 +353,7 @@ static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms,
#if MG_ENABLE_LOG #if MG_ENABLE_LOG
char buf[100]; char buf[100];
#endif #endif
d->txnid = s_reqs ? (uint16_t)(s_reqs->txnid + 1) : 1; d->txnid = s_reqs ? (uint16_t) (s_reqs->txnid + 1) : 1;
d->next = s_reqs; d->next = s_reqs;
s_reqs = d; s_reqs = d;
d->expire = mg_millis() + (unsigned long) ms; d->expire = mg_millis() + (unsigned long) ms;
@@ -390,8 +385,11 @@ void mg_resolve(struct mg_connection *c, struct mg_str *name, int ms) {
void mg_call(struct mg_connection *c, int ev, void *ev_data) { void mg_call(struct mg_connection *c, int ev, void *ev_data) {
if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data); // Run user-defined handler first, in order to give it an ability
// to intercept processing (e.g. clean input buffer) before the
// protocol handler kicks in
if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data); if (c->fn != NULL) c->fn(c, ev, ev_data, c->fn_data);
if (c->pfn != NULL) c->pfn(c, ev, ev_data, c->pfn_data);
} }
void mg_error(struct mg_connection *c, const char *fmt, ...) { void mg_error(struct mg_connection *c, const char *fmt, ...) {
@@ -401,9 +399,9 @@ void mg_error(struct mg_connection *c, const char *fmt, ...) {
mg_vasprintf(&buf, sizeof(mem), fmt, ap); mg_vasprintf(&buf, sizeof(mem), fmt, ap);
va_end(ap); va_end(ap);
LOG(LL_ERROR, ("%lu %s", c->id, buf)); LOG(LL_ERROR, ("%lu %s", c->id, buf));
mg_call(c, MG_EV_ERROR, buf); c->is_closing = 1; // Set is_closing before sending MG_EV_CALL
mg_call(c, MG_EV_ERROR, buf); // Let user handler to override it
if (buf != mem) free(buf); if (buf != mem) free(buf);
c->is_closing = 1;
} }
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
@@ -693,8 +691,9 @@ static size_t p_write(void *fp, const void *buf, size_t len) {
} }
static size_t p_seek(void *fp, size_t offset) { static size_t p_seek(void *fp, size_t offset) {
#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L || \ #if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \
_XOPEN_SOURCE >= 600 (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \
(defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
fseeko((FILE *) fp, (off_t) offset, SEEK_SET); fseeko((FILE *) fp, (off_t) offset, SEEK_SET);
#else #else
fseek((FILE *) fp, (long) offset, SEEK_SET); fseek((FILE *) fp, (long) offset, SEEK_SET);
@@ -758,7 +757,6 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close,
// Multipart POST example: // Multipart POST example:
// --xyz // --xyz
// Content-Disposition: form-data; name="val" // Content-Disposition: form-data; name="val"
@@ -922,8 +920,8 @@ struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) {
return NULL; return NULL;
} }
void mg_http_parse_headers(const char *s, const char *end, static void mg_http_parse_headers(const char *s, const char *end,
struct mg_http_header *h, int max_headers) { struct mg_http_header *h, int max_headers) {
int i; int i;
for (i = 0; i < max_headers; i++) { for (i = 0; i < max_headers; i++) {
struct mg_str k, v, tmp; struct mg_str k, v, tmp;
@@ -1122,6 +1120,7 @@ static void restore_http_cb(struct mg_connection *c) {
c->pfn = http_cb; c->pfn = http_cb;
} }
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime);
char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) { char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) {
snprintf(buf, len, "\"%lx." MG_INT64_FMT "\"", (unsigned long) mtime, snprintf(buf, len, "\"%lx." MG_INT64_FMT "\"", (unsigned long) mtime,
(int64_t) size); (int64_t) size);
@@ -1184,50 +1183,30 @@ static struct mg_str guess_content_type(struct mg_str path, const char *extra) {
MIME_ENTRY("js", "text/javascript; charset=utf-8"), MIME_ENTRY("js", "text/javascript; charset=utf-8"),
MIME_ENTRY("gif", "image/gif"), MIME_ENTRY("gif", "image/gif"),
MIME_ENTRY("png", "image/png"), MIME_ENTRY("png", "image/png"),
MIME_ENTRY("jpg", "image/jpeg"),
MIME_ENTRY("jpeg", "image/jpeg"),
MIME_ENTRY("woff", "font/woff"), MIME_ENTRY("woff", "font/woff"),
MIME_ENTRY("ttf", "font/ttf"), MIME_ENTRY("ttf", "font/ttf"),
MIME_ENTRY("aac", "audio/aac"), MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("avi", "video/x-msvideo"), MIME_ENTRY("avi", "video/x-msvideo"),
MIME_ENTRY("azw", "application/vnd.amazon.ebook"),
MIME_ENTRY("bin", "application/octet-stream"),
MIME_ENTRY("bmp", "image/bmp"),
MIME_ENTRY("bz", "application/x-bzip"),
MIME_ENTRY("bz2", "application/x-bzip2"),
MIME_ENTRY("csv", "text/csv"), MIME_ENTRY("csv", "text/csv"),
MIME_ENTRY("doc", "application/msword"), MIME_ENTRY("doc", "application/msword"),
MIME_ENTRY("epub", "application/epub+zip"),
MIME_ENTRY("exe", "application/octet-stream"), MIME_ENTRY("exe", "application/octet-stream"),
MIME_ENTRY("gz", "application/gzip"), MIME_ENTRY("gz", "application/gzip"),
MIME_ENTRY("ico", "image/x-icon"), MIME_ENTRY("ico", "image/x-icon"),
MIME_ENTRY("json", "application/json"), MIME_ENTRY("json", "application/json"),
MIME_ENTRY("mid", "audio/mid"),
MIME_ENTRY("mjs", "text/javascript"),
MIME_ENTRY("mov", "video/quicktime"), MIME_ENTRY("mov", "video/quicktime"),
MIME_ENTRY("mp3", "audio/mpeg"), MIME_ENTRY("mp3", "audio/mpeg"),
MIME_ENTRY("mp4", "video/mp4"), MIME_ENTRY("mp4", "video/mp4"),
MIME_ENTRY("mpeg", "video/mpeg"), MIME_ENTRY("mpeg", "video/mpeg"),
MIME_ENTRY("mpg", "video/mpeg"),
MIME_ENTRY("ogg", "application/ogg"),
MIME_ENTRY("pdf", "application/pdf"), MIME_ENTRY("pdf", "application/pdf"),
MIME_ENTRY("rar", "application/rar"),
MIME_ENTRY("rtf", "application/rtf"),
MIME_ENTRY("shtml", "text/html; charset=utf-8"), MIME_ENTRY("shtml", "text/html; charset=utf-8"),
MIME_ENTRY("svg", "image/svg+xml"),
MIME_ENTRY("tar", "application/tar"),
MIME_ENTRY("tgz", "application/tar-gz"), MIME_ENTRY("tgz", "application/tar-gz"),
MIME_ENTRY("txt", "text/plain; charset=utf-8"),
MIME_ENTRY("wasm", "application/wasm"),
MIME_ENTRY("wav", "audio/wav"), MIME_ENTRY("wav", "audio/wav"),
MIME_ENTRY("weba", "audio/webm"),
MIME_ENTRY("webm", "video/webm"),
MIME_ENTRY("webp", "image/webp"), MIME_ENTRY("webp", "image/webp"),
MIME_ENTRY("xls", "application/excel"),
MIME_ENTRY("xml", "application/xml"),
MIME_ENTRY("xsl", "application/xml"),
MIME_ENTRY("zip", "application/zip"), MIME_ENTRY("zip", "application/zip"),
MIME_ENTRY("3gp", "video/3gpp"), MIME_ENTRY("3gp", "video/3gpp"),
MIME_ENTRY("7z", "application/x-7z-compressed"),
MIME_ENTRY("7z", "application/x-7z-compressed"),
{{0, 0}, {0, 0}}, {{0, 0}, {0, 0}},
}; };
size_t i = 0; size_t i = 0;
@@ -1343,18 +1322,18 @@ static void printdirentry(const char *name, void *userdata) {
struct printdirentrydata *d = (struct printdirentrydata *) userdata; struct printdirentrydata *d = (struct printdirentrydata *) userdata;
struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs;
size_t size = 0; size_t size = 0;
time_t mtime = 0; time_t t = 0;
char path[MG_PATH_MAX], sz[64], mod[64]; char path[MG_PATH_MAX], sz[64], mod[64];
int flags, n = 0; int flags, n = 0;
// LOG(LL_DEBUG, ("[%s] [%s]", d->dir, name)); // LOG(LL_DEBUG, ("[%s] [%s]", d->dir, name));
if (snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) < 0) { if (snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) < 0) {
LOG(LL_ERROR, ("%s truncated", name)); LOG(LL_ERROR, ("%s truncated", name));
} else if ((flags = fs->stat(path, &size, &mtime)) == 0) { } else if ((flags = fs->stat(path, &size, &t)) == 0) {
LOG(LL_ERROR, ("%lu stat(%s): %d", d->c->id, path, errno)); LOG(LL_ERROR, ("%lu stat(%s): %d", d->c->id, path, errno));
} else { } else {
const char *slash = flags & MG_FS_DIR ? "/" : ""; const char *slash = flags & MG_FS_DIR ? "/" : "";
struct tm t; struct tm tm;
if (flags & MG_FS_DIR) { if (flags & MG_FS_DIR) {
snprintf(sz, sizeof(sz), "%s", "[DIR]"); snprintf(sz, sizeof(sz), "%s", "[DIR]");
} else if (size < 1024) { } else if (size < 1024) {
@@ -1366,12 +1345,13 @@ static void printdirentry(const char *name, void *userdata) {
} else { } else {
snprintf(sz, sizeof(sz), "%.1fG", (double) size / 1073741824); snprintf(sz, sizeof(sz), "%.1fG", (double) size / 1073741824);
} }
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime_r(&mtime, &t)); strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime_r(&t, &tm));
n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); n = (int) mg_url_encode(name, strlen(name), path, sizeof(path));
mg_printf(d->c, mg_printf(d->c,
" <tr><td><a href=\"%.*s%s\">%s%s</a></td>" " <tr><td><a href=\"%.*s%s\">%s%s</a></td>"
"<td>%s</td><td>%s</td></tr>\n", "<td name=%lu>%s</td><td name=" MG_INT64_FMT ">%s</td></tr>\n",
n, path, slash, name, slash, mod, sz); n, path, slash, name, slash, (unsigned long) t, mod,
flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz);
} }
} }
@@ -1402,8 +1382,10 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
"</script>"; "</script>";
struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs;
struct printdirentrydata d = {c, hm, opts, dir}; struct printdirentrydata d = {c, hm, opts, dir};
char tmp[10]; char tmp[10], buf[MG_PATH_MAX];
size_t off, n; size_t off, n;
int len = mg_url_decode(hm->uri.ptr, hm->uri.len, buf, sizeof(buf), 0);
struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri;
mg_printf(c, mg_printf(c,
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
@@ -1423,8 +1405,8 @@ static void listdir(struct mg_connection *c, struct mg_http_message *hm,
"<tr><td colspan=\"3\"><hr></td></tr>" "<tr><td colspan=\"3\"><hr></td></tr>"
"</thead>" "</thead>"
"<tbody id=\"tb\">\n", "<tbody id=\"tb\">\n",
(int) hm->uri.len, hm->uri.ptr, sort_js_code, sort_js_code2, (int) uri.len, uri.ptr, sort_js_code, sort_js_code2, (int) uri.len,
(int) hm->uri.len, hm->uri.ptr); uri.ptr);
fs->list(dir, printdirentry, &d); fs->list(dir, printdirentry, &d);
mg_printf(c, mg_printf(c,
@@ -1445,7 +1427,8 @@ static void remove_double_dots(char *s) {
while (s[0] != '\0') { while (s[0] != '\0') {
if (s[0] == '/' || s[0] == '\\') { if (s[0] == '/' || s[0] == '\\') {
s++; s++;
} else if (s[0] == '.' && s[1] == '.') { } else if (s[0] == '.' && s[1] == '.' &&
(s[2] == '/' || s[2] == '\\')) {
s += 2; s += 2;
} else { } else {
break; break;
@@ -1475,13 +1458,23 @@ static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm,
path[path_size - 1] = '\0'; // Double-check path[path_size - 1] = '\0'; // Double-check
remove_double_dots(path); remove_double_dots(path);
n = strlen(path); n = strlen(path);
LOG(LL_DEBUG, ("--> %s", path)); LOG(LL_VERBOSE_DEBUG,
("%lu %.*s -> %s", c->id, (int) hm->uri.len, hm->uri.ptr, path));
while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes while (n > 0 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes
flags = fs->stat(path, NULL, NULL); // Does it exist? flags = fs->stat(path, NULL, NULL); // Does it exist?
if (flags == 0) { if (flags == 0) {
mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh mg_http_reply(c, 404, "", "Not found\n"); // Does not exist, doh
} else if ((flags & MG_FS_DIR) && hm->uri.len > 0 &&
hm->uri.ptr[hm->uri.len - 1] != '/') {
mg_printf(c,
"HTTP/1.1 301 Moved\r\n"
"Location: %.*s/\r\n"
"Content-Length: 0\r\n"
"\r\n",
(int) hm->uri.len, hm->uri.ptr);
flags = 0;
} else if (flags & MG_FS_DIR) { } else if (flags & MG_FS_DIR) {
if (((snprintf(path + n, path_size - n, "/index.html") > 0 && if (((snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0) || (tmp = fs->stat(path, NULL, NULL)) != 0) ||
(snprintf(path + n, path_size - n, "/index.shtml") > 0 && (snprintf(path + n, path_size - n, "/index.shtml") > 0 &&
(tmp = fs->stat(path, NULL, NULL)) != 0))) { (tmp = fs->stat(path, NULL, NULL)) != 0))) {
@@ -1514,7 +1507,8 @@ void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
const char *sp = opts->ssi_pattern; const char *sp = opts->ssi_pattern;
int flags = uri_to_path(c, hm, opts, path, sizeof(path)); int flags = uri_to_path(c, hm, opts, path, sizeof(path));
if (flags == 0) return; if (flags == 0) return;
LOG(LL_DEBUG, ("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags)); LOG(LL_VERBOSE_DEBUG,
("%.*s %s %d", (int) hm->uri.len, hm->uri.ptr, path, flags));
if (flags & MG_FS_DIR) { if (flags & MG_FS_DIR) {
listdir(c, hm, opts, path); listdir(c, hm, opts, path);
} else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) { } else if (sp != NULL && mg_globmatch(sp, strlen(sp), path, strlen(path))) {
@@ -1575,11 +1569,13 @@ static struct mg_str stripquotes(struct mg_str s) {
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) { struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
size_t i; size_t i;
for (i = 0; i + v.len + 2 < s.len; i++) { for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) {
if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) { if (s.ptr[i + v.len] == '=' && memcmp(&s.ptr[i], v.ptr, v.len) == 0) {
const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len]; const char *p = &s.ptr[i + v.len + 1], *b = p, *x = &s.ptr[s.len];
int q = p < x && *p == '"' ? 1 : 0; int q = p < x && *p == '"' ? 1 : 0;
while (p < x && (q ? p == b || *p != '"' : *p != ';' && *p != ' ')) p++; while (p < x &&
(q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ','))
p++;
// LOG(LL_INFO, ("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len, // LOG(LL_INFO, ("[%.*s] [%.*s] [%.*s]", (int) s.len, s.ptr, (int) v.len,
// v.ptr, (int) (p - b), b)); // v.ptr, (int) (p - b), b));
return stripquotes(mg_str_n(b, (size_t) (p - b + q))); return stripquotes(mg_str_n(b, (size_t) (p - b + q)));
@@ -1870,7 +1866,7 @@ void mg_log_set_callback(void (*fn)(const void *, size_t, void *), void *fnd) {
#include <string.h> #include <string.h>
#if MG_ENABLE_MD5 #if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5
#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER) #if !defined(BYTE_ORDER) && defined(__BYTE_ORDER)
#define BYTE_ORDER __BYTE_ORDER #define BYTE_ORDER __BYTE_ORDER
#ifndef LITTLE_ENDIAN #ifndef LITTLE_ENDIAN
@@ -2077,14 +2073,11 @@ void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
#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
#define MQTT_HAS_PASSWORD 0x40 #define MQTT_HAS_PASSWORD 0x40
#define MQTT_HAS_USER_NAME 0x80 #define MQTT_HAS_USER_NAME 0x80
#define MQTT_GET_WILL_QOS(flags) (((flags) &0x18) >> 3)
#define MQTT_SET_WILL_QOS(flags, qos) (flags) = ((flags) & ~0x18) | ((qos) << 3)
enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
@@ -2105,50 +2098,56 @@ static void mg_send_u16(struct mg_connection *c, uint16_t value) {
mg_send(c, &value, sizeof(value)); mg_send(c, &value, sizeof(value));
} }
void mg_mqtt_login(struct mg_connection *c, const char *url, void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts) {
struct mg_mqtt_opts *opts) { char rnd[9], client_id[16];
struct mg_str cid = opts->client_id;
uint32_t total_len = 7 + 1 + 2 + 2; uint32_t total_len = 7 + 1 + 2 + 2;
uint16_t flags = (uint16_t) (((uint16_t) opts->qos & 3) << 3); uint8_t connflag = (uint8_t) ((opts->qos & 3) << 1);
struct mg_str user = mg_url_user(url);
struct mg_str pass = mg_url_pass(url);
if (user.len > 0) { if (cid.len == 0) {
total_len += 2 + (uint32_t) user.len; mg_random(rnd, sizeof(rnd));
flags |= MQTT_HAS_USER_NAME; mg_base64_encode((unsigned char *) rnd, sizeof(rnd), client_id);
client_id[sizeof(client_id) - 1] = '\0';
cid = mg_str(client_id);
} }
if (pass.len > 0) {
total_len += 2 + (uint32_t) pass.len; if (opts->user.len > 0) {
flags |= MQTT_HAS_PASSWORD; total_len += 2 + (uint32_t) opts->user.len;
connflag |= MQTT_HAS_USER_NAME;
}
if (opts->pass.len > 0) {
total_len += 2 + (uint32_t) opts->pass.len;
connflag |= MQTT_HAS_PASSWORD;
} }
if (opts->will_topic.len > 0 && opts->will_message.len > 0) { if (opts->will_topic.len > 0 && opts->will_message.len > 0) {
total_len += total_len +=
4 + (uint32_t) opts->will_topic.len + (uint32_t) opts->will_message.len; 4 + (uint32_t) opts->will_topic.len + (uint32_t) opts->will_message.len;
flags |= MQTT_HAS_WILL; connflag |= MQTT_HAS_WILL;
} }
if (opts->clean || opts->client_id.len == 0) flags |= MQTT_CLEAN_SESSION; if (opts->clean || cid.len == 0) connflag |= MQTT_CLEAN_SESSION;
if (opts->will_retain) flags |= MQTT_WILL_RETAIN; if (opts->will_retain) connflag |= MQTT_WILL_RETAIN;
total_len += (uint32_t) opts->client_id.len; total_len += (uint32_t) cid.len;
mg_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, &connflag, sizeof(connflag));
// keepalive == 0 means "do not disconnect us!" // keepalive == 0 means "do not disconnect us!"
mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)); mg_send_u16(c, mg_htons((uint16_t) opts->keepalive));
mg_send_u16(c, mg_htons((uint16_t) opts->client_id.len)); mg_send_u16(c, mg_htons((uint16_t) cid.len));
mg_send(c, opts->client_id.ptr, opts->client_id.len); mg_send(c, cid.ptr, cid.len);
if (flags & MQTT_HAS_WILL) { if (connflag & MQTT_HAS_WILL) {
mg_send_u16(c, mg_htons((uint16_t) opts->will_topic.len)); mg_send_u16(c, mg_htons((uint16_t) opts->will_topic.len));
mg_send(c, opts->will_topic.ptr, opts->will_topic.len); mg_send(c, opts->will_topic.ptr, opts->will_topic.len);
mg_send_u16(c, mg_htons((uint16_t) opts->will_message.len)); mg_send_u16(c, mg_htons((uint16_t) opts->will_message.len));
mg_send(c, opts->will_message.ptr, opts->will_message.len); mg_send(c, opts->will_message.ptr, opts->will_message.len);
} }
if (user.len > 0) { if (opts->user.len > 0) {
mg_send_u16(c, mg_htons((uint16_t) user.len)); mg_send_u16(c, mg_htons((uint16_t) opts->user.len));
mg_send(c, user.ptr, user.len); mg_send(c, opts->user.ptr, opts->user.len);
} }
if (pass.len > 0) { if (opts->pass.len > 0) {
mg_send_u16(c, mg_htons((uint16_t) pass.len)); mg_send_u16(c, mg_htons((uint16_t) opts->pass.len));
mg_send(c, pass.ptr, pass.len); mg_send(c, opts->pass.ptr, opts->pass.len);
} }
} }
@@ -2158,11 +2157,11 @@ void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
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, ("%lu [%.*s] -> [%.*s]", c->id, (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 (qos > 0) total_len += 2;
mg_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 (qos > 0) {
static uint16_t s_id; static uint16_t s_id;
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));
@@ -2174,8 +2173,7 @@ void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic, int qos) {
static uint16_t s_id; static uint16_t s_id;
uint8_t qos_ = qos & 3; uint8_t qos_ = qos & 3;
uint32_t total_len = 2 + (uint32_t) topic->len + 2 + 1; uint32_t total_len = 2 + (uint32_t) topic->len + 2 + 1;
mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, (uint8_t) MQTT_QOS(qos_), mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, total_len);
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));
@@ -2334,7 +2332,7 @@ struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url,
if (c != NULL) { if (c != NULL) {
struct mg_mqtt_opts empty; struct mg_mqtt_opts empty;
memset(&empty, 0, sizeof(empty)); memset(&empty, 0, sizeof(empty));
mg_mqtt_login(c, url, opts == NULL ? &empty : opts); mg_mqtt_login(c, opts == NULL ? &empty : opts);
c->pfn = mqtt_cb; c->pfn = mqtt_cb;
} }
return c; return c;
@@ -2401,6 +2399,13 @@ static bool mg_atonl(struct mg_str str, struct mg_addr *addr) {
return true; return true;
} }
static bool mg_atone(struct mg_str str, struct mg_addr *addr) {
if (str.len > 0) return false;
addr->ip = 0;
addr->is_ip6 = false;
return true;
}
static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { static bool mg_aton4(struct mg_str str, struct mg_addr *addr) {
uint8_t data[4] = {0, 0, 0, 0}; uint8_t data[4] = {0, 0, 0, 0};
size_t i, num_dots = 0; size_t i, num_dots = 0;
@@ -2477,7 +2482,8 @@ static bool mg_aton6(struct mg_str str, struct mg_addr *addr) {
bool mg_aton(struct mg_str str, struct mg_addr *addr) { bool mg_aton(struct mg_str str, struct mg_addr *addr) {
// LOG(LL_INFO, ("[%.*s]", (int) str.len, str.ptr)); // LOG(LL_INFO, ("[%.*s]", (int) str.len, str.ptr));
return mg_atonl(str, addr) || mg_aton4(str, addr) || mg_aton6(str, addr); return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) ||
mg_aton6(str, addr);
} }
void mg_mgr_free(struct mg_mgr *mgr) { void mg_mgr_free(struct mg_mgr *mgr) {
@@ -2575,7 +2581,7 @@ static uint32_t blk0(union char64long16 *block, int i) {
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
w = rol(w, 30); w = rol(w, 30);
void mg_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) { static void mg_sha1_transform(uint32_t state[5], const unsigned char buffer[64]) {
uint32_t a, b, c, d, e; uint32_t a, b, c, d, e;
union char64long16 block[1]; union char64long16 block[1];
@@ -2826,10 +2832,9 @@ void mg_sntp_send(struct mg_connection *c, unsigned long utc) {
} 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;
buf[0] = (3 << 6) | (4 << 3) | 3; buf[0] = (0 << 6) | (4 << 3) | 3;
mg_send(c, buf, sizeof(buf)); mg_send(c, buf, sizeof(buf));
LOG(LL_DEBUG, LOG(LL_DEBUG, ("%lu ct %lu, next at %lu", c->id, utc, s_sntmp_next));
("%p request sent, ct %lu, next at %lu", c->fd, utc, s_sntmp_next));
} }
} }
@@ -2854,7 +2859,6 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
#if MG_ENABLE_SOCKET #if MG_ENABLE_SOCKET
#if defined(_WIN32) && MG_ENABLE_WINSOCK #if defined(_WIN32) && MG_ENABLE_WINSOCK
#define MG_SOCK_ERRNO WSAGetLastError() #define MG_SOCK_ERRNO WSAGetLastError()
@@ -2947,7 +2951,19 @@ static struct mg_connection *alloc_conn(struct mg_mgr *mgr, bool is_client,
} }
static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) { static long mg_sock_send(struct mg_connection *c, const void *buf, size_t len) {
long n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); long n;
#if !defined(__APPLE__)
// See #1338, #1382. On Windows, UDP send() can fail despite connected.
// Use sendto() instead. But not on Mac - we'll get EISCONN
if (c->is_udp) {
union usa usa;
socklen_t slen = tousa(&c->peer, &usa);
n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen);
} else
#endif
{
n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING);
}
return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n; return n == 0 ? -1 : n < 0 && mg_sock_would_block() ? 0 : n;
} }
@@ -2969,11 +2985,12 @@ static void mg_set_non_blocking_mode(SOCKET fd) {
#elif MG_ARCH == MG_ARCH_AZURERTOS #elif MG_ARCH == MG_ARCH_AZURERTOS
fcntl(fd, F_SETFL, O_NONBLOCK); fcntl(fd, F_SETFL, O_NONBLOCK);
#else #else
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK, FD_CLOEXEC); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); // Set non-blocking mode
fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec
#endif #endif
} }
SOCKET mg_open_listener(const char *url, struct mg_addr *addr) { static SOCKET mg_open_listener(const char *url, struct mg_addr *addr) {
SOCKET fd = INVALID_SOCKET; SOCKET fd = INVALID_SOCKET;
int s_err = 0; // Memoized socket error, in case closesocket() overrides it int s_err = 0; // Memoized socket error, in case closesocket() overrides it
memset(addr, 0, sizeof(*addr)); memset(addr, 0, sizeof(*addr));
@@ -3051,7 +3068,8 @@ static long mg_sock_recv(struct mg_connection *c, void *buf, size_t len) {
// 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
static void read_conn(struct mg_connection *c) { static long read_conn(struct mg_connection *c) {
long n = -1;
if (c->recv.len >= MG_MAX_RECV_BUF_SIZE) { if (c->recv.len >= MG_MAX_RECV_BUF_SIZE) {
mg_error(c, "max_recv_buf_size reached"); mg_error(c, "max_recv_buf_size reached");
} else if (c->recv.size - c->recv.len < MG_IO_SIZE && } else if (c->recv.size - c->recv.len < MG_IO_SIZE &&
@@ -3060,7 +3078,7 @@ static void read_conn(struct mg_connection *c) {
} else { } else {
char *buf = (char *) &c->recv.buf[c->recv.len]; char *buf = (char *) &c->recv.buf[c->recv.len];
size_t len = c->recv.size - c->recv.len; size_t len = c->recv.size - c->recv.len;
long n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_sock_recv(c, buf, len); n = c->is_tls ? mg_tls_recv(c, buf, len) : mg_sock_recv(c, buf, len);
LOG(n > 0 ? LL_VERBOSE_DEBUG : LL_DEBUG, LOG(n > 0 ? LL_VERBOSE_DEBUG : LL_DEBUG,
("%-3lu %d%d%d%d%d%d%d%d%d%d%d%d%d%d %7ld %ld/%ld err %d", c->id, ("%-3lu %d%d%d%d%d%d%d%d%d%d%d%d%d%d %7ld %ld/%ld err %d", c->id,
c->is_listening, c->is_client, c->is_accepted, c->is_resolving, c->is_listening, c->is_client, c->is_accepted, c->is_resolving,
@@ -3082,6 +3100,7 @@ static void read_conn(struct mg_connection *c) {
mg_call(c, MG_EV_READ, &evd); mg_call(c, MG_EV_READ, &evd);
} }
} }
return n;
} }
static void write_conn(struct mg_connection *c) { static void write_conn(struct mg_connection *c) {
@@ -3150,7 +3169,8 @@ static void setsockopts(struct mg_connection *c) {
setsockopt(FD(c), SOL_TCP, TCP_QUICKACK, (char *) &on, sizeof(on)); setsockopt(FD(c), SOL_TCP, TCP_QUICKACK, (char *) &on, sizeof(on));
#endif #endif
setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)); setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
#if ESP32 || ESP8266 || defined(__linux__) #if (defined(ESP32) && ESP32) || (defined(ESP8266) && 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
@@ -3201,6 +3221,7 @@ struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
c->fn = fn; c->fn = fn;
c->fn_data = fn_data; c->fn_data = fn_data;
LOG(LL_DEBUG, ("%lu -> %s", c->id, url)); LOG(LL_DEBUG, ("%lu -> %s", c->id, url));
mg_call(c, MG_EV_OPEN, NULL);
mg_resolve(c, &host, mgr->dnstimeout); mg_resolve(c, &host, mgr->dnstimeout);
} }
return c; return c;
@@ -3213,12 +3234,12 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
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) {
#if MG_ARCH == MG_ARCH_AZURERTOS #if MG_ARCH == MG_ARCH_AZURERTOS
// AzureRTOS, in non-block socket mode can mark listening socket readable // AzureRTOS, in non-block socket mode can mark listening socket readable
// even it is not. See comment for 'select' func implementation in nx_bsd.c // even it is not. See comment for 'select' func implementation in nx_bsd.c
// That's not an error, just should try later // That's not an error, just should try later
if (MG_SOCK_ERRNO != EAGAIN) if (MG_SOCK_ERRNO != EAGAIN)
#endif #endif
LOG(LL_ERROR, ("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERRNO)); LOG(LL_ERROR, ("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERRNO));
#if (!defined(_WIN32) && (MG_ARCH != MG_ARCH_FREERTOS_TCP)) #if (!defined(_WIN32) && (MG_ARCH != MG_ARCH_FREERTOS_TCP))
} else if ((long) fd >= FD_SETSIZE) { } else if ((long) fd >= FD_SETSIZE) {
LOG(LL_ERROR, ("%ld > %ld", (long) fd, (long) FD_SETSIZE)); LOG(LL_ERROR, ("%ld > %ld", (long) fd, (long) FD_SETSIZE));
@@ -3241,6 +3262,7 @@ static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) {
c->pfn_data = lsn->pfn_data; c->pfn_data = lsn->pfn_data;
c->fn = lsn->fn; c->fn = lsn->fn;
c->fn_data = lsn->fn_data; c->fn_data = lsn->fn_data;
mg_call(c, MG_EV_OPEN, NULL);
mg_call(c, MG_EV_ACCEPT, NULL); mg_call(c, MG_EV_ACCEPT, NULL);
} }
} }
@@ -3301,6 +3323,7 @@ struct mg_connection *mg_mkpipe(struct mg_mgr *mgr, mg_event_handler_t fn,
c->pfn_data = (void *) (size_t) sp[0]; c->pfn_data = (void *) (size_t) sp[0];
c->fn = fn; c->fn = fn;
c->fn_data = fn_data; c->fn_data = fn_data;
mg_call(c, MG_EV_OPEN, NULL);
LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c);
} }
return c; return c;
@@ -3325,7 +3348,8 @@ 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, mg_call(c, MG_EV_OPEN, NULL);
LOG(LL_DEBUG,
("%lu accepting on %s (port %u)", c->id, url, mg_ntohs(c->peer.port))); ("%lu accepting on %s (port %u)", c->id, url, mg_ntohs(c->peer.port)));
} }
return c; return c;
@@ -3374,9 +3398,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) {
for (c = mgr->conns; c != NULL; c = c->next) { for (c = mgr->conns; c != NULL; c = c->next) {
// TLS might have stuff buffered, so dig everything // TLS might have stuff buffered, so dig everything
c->is_readable = c->is_tls && c->is_readable c->is_readable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &rset);
? 1
: FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &rset);
c->is_writable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &wset); c->is_writable = FD(c) != INVALID_SOCKET && FD_ISSET(FD(c), &wset);
} }
#endif #endif
@@ -3426,6 +3448,7 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
} else { } else {
if (c->is_readable) read_conn(c); if (c->is_readable) read_conn(c);
if (c->is_writable) write_conn(c); if (c->is_writable) write_conn(c);
while (c->is_tls && read_conn(c) > 0) (void) 0; // Read buffered TLS data
} }
if (c->is_draining && c->send.len == 0) c->is_closing = 1; if (c->is_draining && c->send.len == 0) c->is_closing = 1;
@@ -3635,7 +3658,7 @@ struct mg_timer *g_timers;
void mg_timer_init(struct mg_timer *t, unsigned long ms, unsigned flags, void mg_timer_init(struct mg_timer *t, unsigned long ms, unsigned flags,
void (*fn)(void *), void *arg) { void (*fn)(void *), void *arg) {
struct mg_timer tmp = {ms, flags, fn, arg, 0UL, g_timers}; struct mg_timer tmp = {ms, 0UL, flags, fn, arg, g_timers};
*t = tmp; *t = tmp;
g_timers = t; g_timers = t;
if (flags & MG_TIMER_RUN_NOW) fn(arg); if (flags & MG_TIMER_RUN_NOW) fn(arg);
@@ -3671,45 +3694,35 @@ void mg_timer_poll(unsigned long now_ms) {
} }
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
#line 1 "src/tls.c" #line 1 "src/tls_dummy.c"
#endif #endif
#if MG_ENABLE_MBEDTLS ///////////////////////////////////////// MBEDTLS #if !MG_ENABLE_MBEDTLS && !MG_ENABLE_OPENSSL && !MG_ENABLE_CUSTOM_TLS
void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
(void) opts;
mg_error(c, "TLS is not enabled");
}
#include <mbedtls/debug.h> void mg_tls_handshake(struct mg_connection *c) {
#include <mbedtls/ssl.h> (void) c;
}
#ifndef EXTERN_C void mg_tls_free(struct mg_connection *c) {
#ifdef __cplusplus (void) c;
#define EXTERN_C extern "C" }
#else long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
#define EXTERN_C return c == NULL || buf == NULL || len == 0 ? 0 : -1;
#endif }
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
return c == NULL || buf == NULL || len == 0 ? 0 : -1;
}
#endif #endif
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 #ifdef MG_ENABLE_LINES
#define RNG , rng_get, NULL #line 1 "src/tls_mbed.c"
#else
#define RNG
#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_send(void *, const unsigned char *, size_t);
struct mg_tls {
char *cafile; // CA certificate path
mbedtls_x509_crt ca; // Parsed CA certificate
mbedtls_x509_crt cert; // Parsed certificate
mbedtls_ssl_context ssl; // SSL/TLS context
mbedtls_ssl_config conf; // SSL-TLS config
mbedtls_pk_context pk; // Private key context
};
#if MG_ENABLE_MBEDTLS
void mg_tls_handshake(struct mg_connection *c) { void 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;
@@ -3741,7 +3754,7 @@ static void debug_cb(void *c, int lev, const char *s, int n, const char *s2) {
(void) lev; (void) lev;
} }
#if MBEDTLS_VERSION_NUMBER >= 0x03000000 #if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
static int rng_get(void *p_rng, unsigned char *buf, size_t len) { static int rng_get(void *p_rng, unsigned char *buf, size_t len) {
(void) p_rng; (void) p_rng;
mg_random(buf, len); mg_random(buf, len);
@@ -3755,6 +3768,9 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
const char *ca = opts->ca == NULL ? "-" const char *ca = opts->ca == NULL ? "-"
: opts->ca[0] == '-' ? "(emb)" : opts->ca[0] == '-' ? "(emb)"
: opts->ca; : opts->ca;
const char *crl = opts->crl == NULL ? "-"
: opts->crl[0] == '-' ? "(emb)"
: opts->crl;
const char *cert = opts->cert == NULL ? "-" const char *cert = opts->cert == NULL ? "-"
: opts->cert[0] == '-' ? "(emb)" : opts->cert[0] == '-' ? "(emb)"
: opts->cert; : opts->cert;
@@ -3765,11 +3781,12 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
mg_error(c, "TLS OOM"); mg_error(c, "TLS OOM");
goto fail; goto fail;
} }
LOG(LL_DEBUG, LOG(LL_DEBUG, ("%lu Setting TLS, CA: %s, CRL: %s, cert: %s, key: %s", c->id,
("%lu Setting TLS, CA: %s, cert: %s, key: %s", c->id, ca, cert, certkey)); ca, crl, cert, certkey));
mbedtls_ssl_init(&tls->ssl); mbedtls_ssl_init(&tls->ssl);
mbedtls_ssl_config_init(&tls->conf); mbedtls_ssl_config_init(&tls->conf);
mbedtls_x509_crt_init(&tls->ca); mbedtls_x509_crt_init(&tls->ca);
mbedtls_x509_crl_init(&tls->crl);
mbedtls_x509_crt_init(&tls->cert); mbedtls_x509_crt_init(&tls->cert);
mbedtls_pk_init(&tls->pk); mbedtls_pk_init(&tls->pk);
mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c); mbedtls_ssl_conf_dbg(&tls->conf, debug_cb, c);
@@ -3788,9 +3805,19 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
} }
if (opts->ca != NULL && opts->ca[0] != '\0') { if (opts->ca != NULL && opts->ca[0] != '\0') {
if (opts->crl != NULL && opts->crl[0] != '\0') {
rc = opts->crl[0] == '-'
? mbedtls_x509_crl_parse(&tls->crl, (uint8_t *) opts->crl,
strlen(opts->crl) + 1)
: mbedtls_x509_crl_parse_file(&tls->crl, opts->crl);
if (rc != 0) {
mg_error(c, "parse(%s) err %#x", crl, -rc);
goto fail;
}
}
#if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK) #if defined(MBEDTLS_X509_CA_CHAIN_ON_DISK)
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, &tls->crl);
if (rc != 0) { if (rc != 0) {
mg_error(c, "parse on-disk chain(%s) err %#x", ca, -rc); mg_error(c, "parse on-disk chain(%s) err %#x", ca, -rc);
goto fail; goto fail;
@@ -3804,7 +3831,7 @@ void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
mg_error(c, "parse(%s) err %#x", ca, -rc); 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, &tls->crl);
#endif #endif
if (opts->srvname.len > 0) { if (opts->srvname.len > 0) {
char mem[128], *buf = mem; char mem[128], *buf = mem;
@@ -3877,21 +3904,20 @@ void mg_tls_free(struct mg_connection *c) {
mbedtls_ssl_free(&tls->ssl); mbedtls_ssl_free(&tls->ssl);
mbedtls_pk_free(&tls->pk); mbedtls_pk_free(&tls->pk);
mbedtls_x509_crt_free(&tls->ca); mbedtls_x509_crt_free(&tls->ca);
mbedtls_x509_crl_free(&tls->crl);
mbedtls_x509_crt_free(&tls->cert); mbedtls_x509_crt_free(&tls->cert);
mbedtls_ssl_config_free(&tls->conf); mbedtls_ssl_config_free(&tls->conf);
free(tls); free(tls);
c->tls = NULL; c->tls = NULL;
} }
#elif MG_ENABLE_OPENSSL ///////////////////////////////////////// OPENSSL #endif
#include <openssl/err.h> #ifdef MG_ENABLE_LINES
#include <openssl/ssl.h> #line 1 "src/tls_openssl.c"
#endif
struct mg_tls {
SSL_CTX *ctx;
SSL *ssl;
};
#if MG_ENABLE_OPENSSL
static int mg_tls_err(struct mg_tls *tls, int res) { static int mg_tls_err(struct mg_tls *tls, int res) {
int err = SSL_get_error(tls->ssl, res); int err = SSL_get_error(tls->ssl, res);
// We've just fetched the last error from the queue. // We've just fetched the last error from the queue.
@@ -4035,26 +4061,6 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
int n = SSL_write(tls->ssl, buf, (int) len); int n = SSL_write(tls->ssl, buf, (int) len);
return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n; return n == 0 ? -1 : n < 0 && mg_tls_err(tls, n) == 0 ? 0 : n;
} }
#else ////////////////////////////////////////// NO TLS
void mg_tls_init(struct mg_connection *c, struct mg_tls_opts *opts) {
(void) opts;
mg_error(c, "TLS is not enabled");
}
void mg_tls_handshake(struct mg_connection *c) {
(void) c;
}
void mg_tls_free(struct mg_connection *c) {
(void) c;
}
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
return c == NULL || buf == NULL || len == 0 ? 0 : -1;
}
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
return c == NULL || buf == NULL || len == 0 ? 0 : -1;
}
#endif #endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
@@ -4153,6 +4159,10 @@ struct mg_str mg_url_pass(const char *url) {
#if MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
#include <mach/mach_time.h>
#endif
char *mg_file_read(const char *path, size_t *sizep) { char *mg_file_read(const char *path, size_t *sizep) {
FILE *fp; FILE *fp;
char *data = NULL; char *data = NULL;
@@ -4215,6 +4225,7 @@ void mg_random(void *buf, size_t len) {
unsigned char *p = (unsigned char *) buf; unsigned char *p = (unsigned char *) buf;
#if MG_ARCH == MG_ARCH_ESP32 #if MG_ARCH == MG_ARCH_ESP32
while (len--) *p++ = (unsigned char) (esp_random() & 255); while (len--) *p++ = (unsigned char) (esp_random() & 255);
done = true;
#elif MG_ARCH == MG_ARCH_WIN32 #elif MG_ARCH == MG_ARCH_WIN32
#elif MG_ARCH_UNIX #elif MG_ARCH_UNIX
FILE *fp = fopen("/dev/urandom", "rb"); FILE *fp = fopen("/dev/urandom", "rb");
@@ -4223,10 +4234,8 @@ void mg_random(void *buf, size_t len) {
fclose(fp); fclose(fp);
} }
#endif #endif
// Fallback to a pseudo random gen // If everything above did not work, fallback to a pseudo random generator
if (!done) { while (!done && len--) *p++ = (unsigned char) (rand() & 255);
while (len--) *p++ = (unsigned char) (rand() & 255);
}
} }
#endif #endif
@@ -4499,9 +4508,24 @@ unsigned long mg_millis(void) {
return xTaskGetTickCount() * portTICK_PERIOD_MS; return xTaskGetTickCount() * portTICK_PERIOD_MS;
#elif MG_ARCH == MG_ARCH_AZURERTOS #elif MG_ARCH == MG_ARCH_AZURERTOS
return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND); return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND);
#elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__)
uint64_t ticks = mach_absolute_time();
static mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
double ticks_to_nanos = (double)timebase.numer / timebase.denom;
uint64_t uptime_nanos = (uint64_t)(ticks_to_nanos * ticks);
return (unsigned long) (uptime_nanos / 1000000);
#else #else
struct timespec ts; struct timespec ts;
#ifdef _POSIX_MONOTONIC_CLOCK
#ifdef CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
#else
clock_gettime(CLOCK_MONOTONIC, &ts);
#endif
#else
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_REALTIME, &ts);
#endif
return (unsigned long) ((uint64_t) ts.tv_sec * 1000 + return (unsigned long) ((uint64_t) ts.tv_sec * 1000 +
(uint64_t) ts.tv_nsec / 1000000); (uint64_t) ts.tv_nsec / 1000000);
#endif #endif
@@ -4519,7 +4543,6 @@ unsigned long mg_millis(void) {
struct ws_msg { struct ws_msg {
uint8_t flags; uint8_t flags;
size_t header_len; size_t header_len;
+85 -19
View File
@@ -5,7 +5,7 @@
// This software is dual-licensed: you can redistribute it and/or modify // This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as // it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this // published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>. // license, see http://www.gnu.org/licenses/
// //
// You are free to use this software under the terms of the GNU General // You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied // Public License, but WITHOUT ANY WARRANTY; without even the implied
@@ -13,12 +13,12 @@
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// 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.mongoose.ws/licensing/
#ifndef MONGOOSE_H #ifndef MONGOOSE_H
#define MONGOOSE_H #define MONGOOSE_H
#define MG_VERSION "7.4" #define MG_VERSION "7.5"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -37,6 +37,10 @@ extern "C" {
#define MG_ENABLE_OPENSSL 0 #define MG_ENABLE_OPENSSL 0
#endif #endif
#ifndef MG_ENABLE_CUSTOM_TLS
#define MG_ENABLE_CUSTOM_TLS 0
#endif
#ifndef MG_ENABLE_SSI #ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 1 #define MG_ENABLE_SSI 1
#endif #endif
@@ -84,6 +88,10 @@ extern "C" {
#define MG_MAX_HTTP_HEADERS 40 #define MG_MAX_HTTP_HEADERS 40
#endif #endif
#ifndef MG_HTTP_INDEX
#define MG_HTTP_INDEX "index.html"
#endif
#ifndef MG_PATH_MAX #ifndef MG_PATH_MAX
#define MG_PATH_MAX PATH_MAX #define MG_PATH_MAX PATH_MAX
#endif #endif
@@ -197,12 +205,12 @@ static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#undef MG_PATH_MAX
#undef MG_ENABLE_DIRLIST
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld" #define MG_INT64_FMT "%lld"
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 128 #define MG_PATH_MAX 128
#endif
#undef MG_ENABLE_DIRLIST
#define MG_ENABLE_DIRLIST 1 #define MG_ENABLE_DIRLIST 1
#endif #endif
@@ -229,8 +237,13 @@ static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
#include <esp_system.h> #include <esp_system.h>
#undef MG_PATH_MAX
#undef MG_ENABLE_DIRLIST
#define MG_DIRSEP '/' #define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld" #define MG_INT64_FMT "%lld"
#define MG_PATH_MAX 128
#define MG_ENABLE_DIRLIST 1
#endif #endif
@@ -241,6 +254,7 @@ static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#if defined(__GNUC__) #if defined(__GNUC__)
#include <sys/stat.h> #include <sys/stat.h>
@@ -559,22 +573,21 @@ void mg_log_set_callback(void (*fn)(const void *, size_t, void *), void *param);
struct mg_timer { struct mg_timer {
unsigned long period_ms; // Timer period in milliseconds unsigned long period_ms; // Timer period in milliseconds
unsigned flags; // Possible flags values below
void (*fn)(void *); // Function to call
void *arg; // Function argument
unsigned long expire; // Expiration timestamp in milliseconds unsigned long expire; // Expiration timestamp in milliseconds
struct mg_timer *next; // Linkage in g_timers list unsigned flags; // Possible flags values below
};
#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once #define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once
#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
void (*fn)(void *); // Function to call
void *arg; // Function argument
struct mg_timer *next; // Linkage in g_timers list
};
extern struct mg_timer *g_timers; // Global list of timers extern struct mg_timer *g_timers; // Global list of timers
void mg_timer_init(struct mg_timer *, unsigned long ms, unsigned, void mg_timer_init(struct mg_timer *, unsigned long ms, unsigned,
void (*fn)(void *), void *); void (*fn)(void *), void *);
void mg_timer_free(struct mg_timer *); void mg_timer_free(struct mg_timer *);
void mg_timer_poll(unsigned long uptime_ms); void mg_timer_poll(unsigned long current_time_ms);
@@ -604,6 +617,14 @@ void mg_usleep(unsigned long usecs);
#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)
#ifndef EXTERN_C
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C
#endif
#endif
// Expands to a string representation of its argument: e.g. // Expands to a string representation of its argument: e.g.
// MG_STRINGIFY_LITERAL(5) expands to "5" // MG_STRINGIFY_LITERAL(5) expands to "5"
#if !defined(_MSC_VER) || _MSC_VER >= 1900 #if !defined(_MSC_VER) || _MSC_VER >= 1900
@@ -731,6 +752,7 @@ 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_OPEN, // Connection created NULL
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
@@ -818,6 +840,7 @@ struct mg_connection *mg_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_connect(struct mg_mgr *, const char *url, struct mg_connection *mg_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_connect_resolved(struct mg_connection *);
bool mg_send(struct mg_connection *, const void *, size_t); bool mg_send(struct mg_connection *, const void *, size_t);
int mg_printf(struct mg_connection *, const char *fmt, ...); 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);
@@ -897,8 +920,12 @@ void mg_http_serve_ssi(struct mg_connection *c, const char *root,
struct mg_tls_opts { struct mg_tls_opts {
const char *ca; // CA certificate file. For both listeners and clients const char *ca; // CA certificate file. For both listeners and clients
const char *crl; // Certificate Revocation List. For 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
@@ -912,6 +939,48 @@ long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
void mg_tls_handshake(struct mg_connection *); void mg_tls_handshake(struct mg_connection *);
#if MG_ENABLE_MBEDTLS
#include <mbedtls/debug.h>
#include <mbedtls/ssl.h>
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= 0x03000000
#define RNG , rng_get, NULL
#else
#define RNG
#endif
// Different versions have those in different files, so declare here
EXTERN_C int mbedtls_net_recv(void *, unsigned char *, size_t);
EXTERN_C int mbedtls_net_send(void *, const unsigned char *, size_t);
struct mg_tls {
char *cafile; // CA certificate path
mbedtls_x509_crt ca; // Parsed CA certificate
mbedtls_x509_crl crl; // Parsed Certificate Revocation List
mbedtls_x509_crt cert; // Parsed certificate
mbedtls_ssl_context ssl; // SSL/TLS context
mbedtls_ssl_config conf; // SSL-TLS config
mbedtls_pk_context pk; // Private key context
};
#endif
#if MG_ENABLE_OPENSSL
#include <openssl/err.h>
#include <openssl/ssl.h>
struct mg_tls {
SSL_CTX *ctx;
SSL *ssl;
};
#endif
#define WEBSOCKET_OP_CONTINUE 0 #define WEBSOCKET_OP_CONTINUE 0
#define WEBSOCKET_OP_TEXT 1 #define WEBSOCKET_OP_TEXT 1
#define WEBSOCKET_OP_BINARY 2 #define WEBSOCKET_OP_BINARY 2
@@ -961,11 +1030,9 @@ int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv);
#define MQTT_CMD_PINGRESP 13 #define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14 #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 user; // Username, can be empty
struct mg_str pass; // Password, can be empty
struct mg_str client_id; // Client ID struct mg_str client_id; // Client ID
struct mg_str will_topic; // Will topic struct mg_str will_topic; // Will topic
struct mg_str will_message; // Will message struct mg_str will_message; // Will message
@@ -990,8 +1057,7 @@ struct mg_connection *mg_mqtt_connect(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_mqtt_listen(struct mg_mgr *mgr, const char *url, struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data); mg_event_handler_t fn, void *fn_data);
void mg_mqtt_login(struct mg_connection *c, const char *url, void mg_mqtt_login(struct mg_connection *c, struct mg_mqtt_opts *opts);
struct mg_mqtt_opts *opts);
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic, void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
struct mg_str *data, int qos, bool retain); struct mg_str *data, int qos, bool retain);
void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic, int qos); void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic, int qos);
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more