mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d38e26b81 | ||
|
|
4e75a0c81e | ||
|
|
d311e967fa | ||
|
|
d0a6c111cb | ||
|
|
aa6d8b4662 | ||
|
|
3f5fd89fd7 | ||
|
|
f43a5a005c | ||
|
|
755bf3c412 | ||
|
|
84f3fb0ba5 | ||
|
|
f2df6c114d | ||
|
|
f93659e303 | ||
|
|
226404d39c | ||
|
|
c1bcc143b9 | ||
|
|
5ca9062d45 | ||
|
|
0b209eaabb | ||
|
|
47ef191ea2 | ||
|
|
25e76d761e | ||
|
|
9731a51dae | ||
|
|
81220fa590 | ||
|
|
24e8c4d81a | ||
|
|
013520b0c0 | ||
|
|
f012bcb027 | ||
|
|
922e1ce529 | ||
|
|
7a707cc64b | ||
|
|
1967acfef2 | ||
|
|
e24cb04003 | ||
|
|
67974746ff | ||
|
|
8d916d41d7 | ||
|
|
08444e3942 | ||
|
|
9c41a0cd26 | ||
|
|
2c50069d98 | ||
|
|
31059f6728 | ||
|
|
2000752b2c | ||
|
|
dd34830443 | ||
|
|
c58f4ee51b | ||
|
|
00f7e8cb2a | ||
|
|
439904e27f | ||
|
|
4d6590ece2 | ||
|
|
9b0c4b970d | ||
|
|
a80ce1b12a | ||
|
|
57dd74c180 | ||
|
|
90c67e4226 | ||
|
|
093cd6a6a3 | ||
|
|
975db86306 | ||
|
|
eba688e11b | ||
|
|
05b44204d5 | ||
|
|
0bb017c1d5 | ||
|
|
a6efd0b335 | ||
|
|
c3d237e9c6 | ||
|
|
09d28f5570 | ||
|
|
ba02937ec6 | ||
|
|
3d78b14deb | ||
|
|
f558551d1d | ||
|
|
12fe66370f | ||
|
|
423aaa492c | ||
|
|
2016586475 | ||
|
|
bf88167152 | ||
|
|
52be84f04e | ||
|
|
03d187b147 | ||
|
|
3329047579 | ||
|
|
86c70e5abb | ||
|
|
b17f4108ea | ||
|
|
4e523cc455 | ||
|
|
49c9be9159 | ||
|
|
f1bfe0760f | ||
|
|
35f02fdaad | ||
|
|
6ac09966ba | ||
|
|
af9a7617f5 | ||
|
|
42c89732c8 | ||
|
|
054475ddbf | ||
|
|
126d556d6a | ||
|
|
529a302e77 | ||
|
|
acbf978452 | ||
|
|
90b22716ea | ||
|
|
fb90c504a6 | ||
|
|
65fd834654 | ||
|
|
a84acf5b8b | ||
|
|
e9505db146 | ||
|
|
4fb49b011e | ||
|
|
6742991469 | ||
|
|
2430cc5aac | ||
|
|
54590aaf26 | ||
|
|
7c5e67b272 | ||
|
|
c62c8ea4dd | ||
|
|
383653d608 | ||
|
|
001dc3f33d | ||
|
|
37d45b3fbb | ||
|
|
f72b8c6034 | ||
|
|
c11e5a9383 | ||
|
|
b5147a149e | ||
|
|
655529f30b | ||
|
|
fd5319e0ce | ||
|
|
f8c84ca672 | ||
|
|
1b81ac8916 | ||
|
|
c64e2ebb16 | ||
|
|
3771f2c088 | ||
|
|
6d398bd5c7 | ||
|
|
5c355a5ff3 | ||
|
|
d2f8b346e6 | ||
|
|
5cdaa931f9 | ||
|
|
693863a51e | ||
|
|
bc7178e5f7 |
No files matched your search
@@ -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): _?_
|
|
||||||
@@ -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
@@ -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
|
||||||
@@ -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/>.
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/captive-dns-portal/
|
||||||
@@ -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 +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,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)
|
||||||
@@ -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 = {
|
||||||
|
|||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.c
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../../mongoose.h
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/file-uploads/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/file-uploads/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/http-client/
|
||||||
@@ -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,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)" ""
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/http-server/
|
||||||
@@ -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));
|
||||||
|
|||||||
@@ -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/
|
||||||
@@ -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
@@ -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;"> </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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/aws-iot/
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../test/data/ca.pem
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/mqtt-client/
|
||||||
+51
-24
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/mqtt-server/
|
||||||
@@ -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)
|
||||||
+1
@@ -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)
|
||||||
+1
@@ -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)
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
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/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/websocket-client/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
See detailed tutorial at https://mongoose.ws/tutorials/websocket-server/
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;"> </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
@@ -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
@@ -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
Reference in new issue
Block a user