mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b82aa02aa | ||
|
|
e565a7e73d | ||
|
|
59c4dfa33b | ||
|
|
1a8e3c2e31 | ||
|
|
226917e1cd | ||
|
|
fe813ee247 | ||
|
|
4cd830a0bf | ||
|
|
1259ca9ac9 | ||
|
|
d0857fe36c | ||
|
|
56a7438114 | ||
|
|
09df542db8 | ||
|
|
1df102ab93 | ||
|
|
02b3b780bb | ||
|
|
f84f3da012 | ||
|
|
c65c8fdaaa | ||
|
|
b484123f1b | ||
|
|
b60119790e | ||
|
|
216427cea0 | ||
|
|
c53e2fcc14 | ||
|
|
9fd80fd136 | ||
|
|
5fc284722f | ||
|
|
ad6e395da0 | ||
|
|
6c79a406cd | ||
|
|
456e8bd249 | ||
|
|
de95f28598 | ||
|
|
43405afdb2 | ||
|
|
97f95e17db | ||
|
|
6725d61b3f | ||
|
|
ebda2cb100 | ||
|
|
bb72b2494d | ||
|
|
3b0a509cae | ||
|
|
2f981f9383 | ||
|
|
fc3c08e411 | ||
|
|
ff811f287a | ||
|
|
8ebb3bc37b | ||
|
|
f7ff571c5f | ||
|
|
6a82cbee71 | ||
|
|
b0585bd0a9 | ||
|
|
6ad4d242c2 | ||
|
|
030b1d9826 | ||
|
|
f6ffa7b3b9 | ||
|
|
137efe2233 | ||
|
|
62411d039e | ||
|
|
54b1716e77 | ||
|
|
9f7aba8039 | ||
|
|
a176b8d566 | ||
|
|
26a97ec36c | ||
|
|
f29807aad8 | ||
|
|
92d3c7a4cd | ||
|
|
af3a0f6af0 | ||
|
|
ce6164ca20 | ||
|
|
e9fc3fcecf | ||
|
|
25a1a24890 | ||
|
|
bf7fc1d5a9 | ||
|
|
2126b49ab3 | ||
|
|
ae6767b1d2 | ||
|
|
133629dbee | ||
|
|
fb0a9bc7e3 | ||
|
|
2a3492766f | ||
|
|
2ec7e5653e | ||
|
|
30bfb245f5 | ||
|
|
84d89bc4f5 | ||
|
|
7f55fabfec | ||
|
|
9e0f04dc8b | ||
|
|
574c2954b3 | ||
|
|
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
|
||||||
+123
-7
@@ -7,8 +7,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: sudo apt-get install libmbedtls-dev
|
run: sudo apt-get install libmbedtls-dev
|
||||||
- name: make
|
- name: gcc
|
||||||
run: make test test++ IPV6=0
|
run: make test test++ IPV6=0 CC=gcc
|
||||||
|
- name: clang
|
||||||
|
run: make test test++ IPV6=0 CC=clang
|
||||||
- name: openssl
|
- name: openssl
|
||||||
run: make test SSL=OPENSSL IPV6=0
|
run: make test SSL=OPENSSL IPV6=0
|
||||||
- name: unamalgamated
|
- name: unamalgamated
|
||||||
@@ -68,28 +70,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 -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)
|
||||||
@@ -27,7 +27,7 @@ CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include
|
|||||||
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
|
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: mg_prefix unpacked test test++ arm examples vc98 vc2017 mingw mingw++ linux linux++ fuzz
|
all: mg_prefix unamalgamated unpacked test test++ arm examples vc98 vc2017 mingw mingw++ linux linux++ fuzz
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
@for X in $(EXAMPLES); do $(MAKE) -C $$X example || break; done
|
@for X in $(EXAMPLES); do $(MAKE) -C $$X example || break; done
|
||||||
@@ -59,7 +59,7 @@ fuzz: fuzzer
|
|||||||
$(RUN) ./fuzzer
|
$(RUN) ./fuzzer
|
||||||
|
|
||||||
# make CC=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
|
# make CC=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1
|
||||||
test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address#,undefined
|
test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address,undefined
|
||||||
test: mongoose.h Makefile $(SRCS)
|
test: mongoose.h Makefile $(SRCS)
|
||||||
$(CC) $(SRCS) $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test
|
$(CC) $(SRCS) $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test
|
||||||
@@ -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/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||||
|
|
||||||
clean:
|
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 [Evaluation and 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:
|
||||||
|
|||||||
+164
-135
@@ -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`
|
||||||
@@ -132,24 +132,25 @@ to an event handler:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
enum {
|
enum {
|
||||||
MG_EV_ERROR, // Error char *error_message
|
MG_EV_ERROR, // Error char *error_message
|
||||||
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
|
MG_EV_OPEN, // Connection created NULL
|
||||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
MG_EV_POLL, // mg_mgr_poll iteration int64_t *milliseconds
|
||||||
MG_EV_CONNECT, // Connection established NULL
|
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||||
MG_EV_ACCEPT, // Connection accepted NULL
|
MG_EV_CONNECT, // Connection established NULL
|
||||||
MG_EV_READ, // Data received from socket struct mg_str *
|
MG_EV_ACCEPT, // Connection accepted NULL
|
||||||
MG_EV_WRITE, // Data written to socket int *num_bytes_written
|
MG_EV_READ, // Data received from socket struct mg_str *
|
||||||
MG_EV_CLOSE, // Connection closed NULL
|
MG_EV_WRITE, // Data written to socket long *bytes_written
|
||||||
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
MG_EV_CLOSE, // Connection closed NULL
|
||||||
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
||||||
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
||||||
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
||||||
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
||||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
||||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||||
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
|
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||||
MG_EV_USER, // Starting ID for user events
|
MG_EV_SNTP_TIME, // SNTP time received int64_t *milliseconds
|
||||||
|
MG_EV_USER, // Starting ID for user events
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -162,7 +163,8 @@ that connection is UDP or not. Some flags can be changed by application, for
|
|||||||
example, the `is_draining` flag, if set by an application, tells Mongoose to send
|
example, the `is_draining` flag, if set by an application, tells Mongoose to send
|
||||||
the remaining data to a peer, and when everything is sent, close the connection.
|
the remaining data to a peer, and when everything is sent, close the connection.
|
||||||
|
|
||||||
<span class="badge bg-danger">NOTE: </span>User-changeable flags are: `is_hexdumping`, `is_draining`, `is_closing`.
|
<span class="badge bg-danger me-1"> NOTE: </span>User-changeable flags are:
|
||||||
|
`is_hexdumping`, `is_draining`, `is_closing`.
|
||||||
|
|
||||||
This is taken from `mongoose.h` as-is:
|
This is taken from `mongoose.h` as-is:
|
||||||
|
|
||||||
@@ -232,8 +234,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 |
|
||||||
@@ -241,9 +244,12 @@ Here is a list of build constants and their default values:
|
|||||||
|MG_ENABLE_DIRLIST | 0 | Enable directory listing |
|
|MG_ENABLE_DIRLIST | 0 | Enable directory listing |
|
||||||
|MG_ENABLE_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
|
|MG_ENABLE_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
|
||||||
|MG_ENABLE_PACKED_FS | 0 | Enable embedded FS support |
|
|MG_ENABLE_PACKED_FS | 0 | Enable embedded FS support |
|
||||||
|
|MG_ENABLE_FATFS | 0 | Enable embedded FAT FS support |
|
||||||
|MG_IO_SIZE | 2048 | Granularity of the send/recv IO buffer growth |
|
|MG_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_FATFS_ROOT | "/" | FAT FS root 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
|
||||||
@@ -624,13 +630,13 @@ void foo(struct mg_connection *c, const char *fmt, ...) {
|
|||||||
### mg\_straddr
|
### mg\_straddr
|
||||||
|
|
||||||
```c
|
```c
|
||||||
char *mg_straddr(struct mg_connection *c, char *buf, size_t len);
|
char *mg_straddr(struct mg_addr *addr, char *buf, size_t len);
|
||||||
```
|
```
|
||||||
|
|
||||||
Write stringified IP address, associated with given connection to `buf` (maximum size `len`)
|
Write stringified IP address, associated with given connection to `buf` (maximum size `len`)
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
- `c` - A connection pointer
|
- `addr` - A address pointer
|
||||||
- `buf` - A pointer to a buffer that will hold stringified address
|
- `buf` - A pointer to a buffer that will hold stringified address
|
||||||
- `len` - A buffer size
|
- `len` - A buffer size
|
||||||
|
|
||||||
@@ -639,8 +645,8 @@ Return value: `buf` value
|
|||||||
Usage example:
|
Usage example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
char buf[1024];
|
char buf[100];
|
||||||
mg_straddr(c, buf, sizeof(buf)); // `buf` is now IP address string, like "127.0.0.1:8080"
|
LOG(LL_INFO, ("%s", mg_straddr(&c->peer, buf, sizeof(buf))));
|
||||||
```
|
```
|
||||||
|
|
||||||
### mg\_mkpipe()
|
### mg\_mkpipe()
|
||||||
@@ -660,7 +666,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,23 +682,26 @@ 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()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void mg_mgr_wakeup(struct mg_connection *pipe);
|
void mg_mgr_wakeup(struct mg_connection *pipe, const void *buf, size_len len);
|
||||||
```
|
```
|
||||||
|
|
||||||
Wake up an event manager that sleeps in `mg_mgr_poll()` call. This function
|
Wake up an event manager that sleeps in `mg_mgr_poll()` call. This function
|
||||||
must be called from a separate task/thread. Parameters:
|
must be called from a separate task/thread. A calling thread can pass
|
||||||
|
some specific data to the IO thread via `buf`, `len`. Parameters:
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
- `pipe` - a special connection created by the `mg_mkpipe()` call
|
- `pipe` - a special connection created by the `mg_mkpipe()` call
|
||||||
|
- `buf` - a data to send to the pipe connection. Use `""` if there is no data
|
||||||
|
- `len` - a data length
|
||||||
|
|
||||||
Return value: None
|
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 +921,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 +1002,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 +1056,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
|
||||||
@@ -1140,10 +1180,10 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
int mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
const char *dir);
|
struct mg_fs *fs, const char *dir);
|
||||||
```
|
```
|
||||||
|
|
||||||
Handle file upload. See [file upload example](https://github.com/cesanta/mongoose/tree/master/examples/file-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:
|
||||||
@@ -1175,9 +1215,10 @@ The expected usage of this API function follows:
|
|||||||
Parameters:
|
Parameters:
|
||||||
- `c` - Connection to use
|
- `c` - Connection to use
|
||||||
- `hm` - POST message, containing parameters described above
|
- `hm` - POST message, containing parameters described above
|
||||||
|
- `fs` - Filesystem to use
|
||||||
- `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:
|
||||||
|
|
||||||
@@ -1186,7 +1227,7 @@ Usage example:
|
|||||||
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
mg_http_upload(c, hm, "."); // Upload to current folder
|
mg_http_upload(c, hm, &mg_fs_posix, "."); // Upload to current folder
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1205,10 +1246,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 +1288,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 +1331,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 +1424,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
|
||||||
@@ -1455,7 +1504,7 @@ struct mg_mqtt_opts {
|
|||||||
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
|
||||||
uint8_t qos; // Quality of service
|
uint8_t will_qos; // Will message quality of service
|
||||||
bool will_retain; // Retain last will
|
bool will_retain; // Retain last will
|
||||||
bool clean; // Use clean session, 0 or 1
|
bool clean; // Use clean session, 0 or 1
|
||||||
uint16_t keepalive; // Keep-alive timer in seconds
|
uint16_t keepalive; // Keep-alive timer in seconds
|
||||||
@@ -1573,8 +1622,8 @@ void fn(struct mg_connection *c, int ev, void *evd, void *fnd) {
|
|||||||
### mg\_mqtt\_pub()
|
### mg\_mqtt\_pub()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
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);
|
||||||
```
|
```
|
||||||
|
|
||||||
Publish message.
|
Publish message.
|
||||||
@@ -1591,15 +1640,13 @@ Return value: None
|
|||||||
Usage example:
|
Usage example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
struct mg_str topic = mg_str("topic");
|
mg_mqtt_pub(c, mg_str("topic"), mg_str("my data"), 1, false);
|
||||||
struct mg_str data = mg_str("data");
|
|
||||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### mg\_mqtt\_sub()
|
### mg\_mqtt\_sub()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void mg_mqtt_sub(struct mg_connection *c, struct mg_str *topic, int qos);
|
void mg_mqtt_sub(struct mg_connection *c, struct mg_str topic, int qos);
|
||||||
```
|
```
|
||||||
|
|
||||||
Subscribe to topic.
|
Subscribe to topic.
|
||||||
@@ -1607,13 +1654,12 @@ 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
|
||||||
|
|
||||||
```c
|
```c
|
||||||
struct mg_str topic = mg_str("topic");
|
mg_mqtt_sub(c, mg_str("my/topic"), 1);
|
||||||
mg_mqtt_sub(c, &topic, 1);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### mg\_mqtt\_next\_sub()
|
### mg\_mqtt\_next\_sub()
|
||||||
@@ -1629,7 +1675,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,10 +1848,12 @@ 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
|
||||||
struct mg_str srvname; // If not empty, enables server name verification
|
struct mg_str srvname; // If not empty, enables server name verification
|
||||||
|
struct mg_fs *fs; // FS API for reading certificate files
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1813,6 +1861,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
|
||||||
@@ -1857,16 +1908,15 @@ mg_tls_init(c, &opts);
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
struct mg_timer {
|
struct mg_timer {
|
||||||
int period_ms; // Timer period in milliseconds
|
int64_t period_ms; // Timer period in milliseconds
|
||||||
int flags; // Possible flags values below
|
int64_t expire; // Expiration timestamp in milliseconds
|
||||||
void (*fn)(void *); // Function to call
|
unsigned flags; // Possible flags values below
|
||||||
void *arg; // Function argument
|
|
||||||
unsigned long expire; // Expiration timestamp in milliseconds
|
|
||||||
struct mg_timer *next; // Linkage in g_timers list
|
|
||||||
};
|
|
||||||
|
|
||||||
#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
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Timer structure. Describes a software timer. Timer granularity is the same
|
Timer structure. Describes a software timer. Timer granularity is the same
|
||||||
@@ -1875,7 +1925,7 @@ as the `mg_mgr_poll()` timeout argument in the main event loop.
|
|||||||
### mg\_timer\_init()
|
### mg\_timer\_init()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void mg_timer_init(struct mg_timer *t, unsigned long ms, unsigned flags,
|
void mg_timer_init(struct mg_timer *t, int64_t period_ms, unsigned flags,
|
||||||
void (*fn)(void *), void *fn_data);
|
void (*fn)(void *), void *fn_data);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1923,7 +1973,7 @@ mg_timer_free(&timer);
|
|||||||
### mg\_timer\_poll()
|
### mg\_timer\_poll()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void mg_timer_poll(unsigned long uptime_ms);
|
void mg_timer_poll(int64_t uptime_ms);
|
||||||
```
|
```
|
||||||
|
|
||||||
Traverse list of timers and call them if current timestamp `uptime_ms` is
|
Traverse list of timers and call them if current timestamp `uptime_ms` is
|
||||||
@@ -1940,34 +1990,16 @@ Return value: None
|
|||||||
Usage example:
|
Usage example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
unsigned long now = mg_millis();
|
int64_t now = mg_millis();
|
||||||
mg_timer_poll(now);
|
mg_timer_poll(now);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Time
|
## Time
|
||||||
|
|
||||||
### mg\_time()
|
|
||||||
|
|
||||||
```
|
|
||||||
double mg_time(void);
|
|
||||||
```
|
|
||||||
|
|
||||||
Return current time as UNIX epoch, using `double` value for sub-second accuracy.
|
|
||||||
|
|
||||||
Parameters: None
|
|
||||||
|
|
||||||
Return value: Current time
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
|
|
||||||
```c
|
|
||||||
double now = mg_time()
|
|
||||||
```
|
|
||||||
|
|
||||||
### mg\_millis()
|
### mg\_millis()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
unsigned long mg_millis(void);
|
int64_t mg_millis(void);
|
||||||
```
|
```
|
||||||
|
|
||||||
Return current uptime in milliseconds.
|
Return current uptime in milliseconds.
|
||||||
@@ -1979,42 +2011,34 @@ Return value: Current uptime
|
|||||||
Usage example:
|
Usage example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
unsigned long uptime = mg_millis();
|
int64_t uptime = mg_millis();
|
||||||
```
|
|
||||||
|
|
||||||
### mg\_usleep()
|
|
||||||
|
|
||||||
```c
|
|
||||||
void mg_usleep(unsigned long usecs);
|
|
||||||
```
|
|
||||||
|
|
||||||
Block thread execution for a given number of microseconds.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- `usecs` - number of microseconds to block thread
|
|
||||||
|
|
||||||
Return value: None
|
|
||||||
|
|
||||||
Usage example:
|
|
||||||
|
|
||||||
```c
|
|
||||||
mg_usleep(1000000 /* 1 sec */)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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 +2069,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 +2135,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 +2183,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 +2259,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 +2289,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 +2347,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 +2709,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 +2764,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 +2785,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];
|
||||||
@@ -2817,7 +2842,7 @@ mg_base64_decode("Q2VzYW50YQ==", 12, buf); // buf is now "Cesanta"
|
|||||||
### mg\_file\_read()
|
### mg\_file\_read()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
char *mg_file_read(const char *path, size_t *sizep);
|
char *mg_file_read(struct mg_fs *fs, const char *path, size_t *sizep);
|
||||||
```
|
```
|
||||||
|
|
||||||
Read file contents into a nul-terminated malloc-ed string. It is a caller's
|
Read file contents into a nul-terminated malloc-ed string. It is a caller's
|
||||||
@@ -2825,6 +2850,7 @@ responsibility to free() a returned pointer. If `sizep` is not NULL, it will
|
|||||||
return a file size in bytes. Return `NULL` on error.
|
return a file size in bytes. Return `NULL` on error.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
- `fs` - Filesystem to use
|
||||||
- `path` - Path to file to read
|
- `path` - Path to file to read
|
||||||
- `sizep` - Pointer to `size_t` to receive file size
|
- `sizep` - Pointer to `size_t` to receive file size
|
||||||
|
|
||||||
@@ -2834,7 +2860,7 @@ Usage example:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
char* data = mg_file_read("myfile.txt", &file_size);
|
char* data = mg_file_read(&mg_fs_posix, "myfile.txt", &file_size);
|
||||||
if (data != NULL) {
|
if (data != NULL) {
|
||||||
// `data` is now pointer to information readen from file and `file_size` is it size.
|
// `data` is now pointer to information readen from file and `file_size` is it size.
|
||||||
}
|
}
|
||||||
@@ -2844,12 +2870,13 @@ free(data);
|
|||||||
### mg\_file\_write()
|
### mg\_file\_write()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
bool mg_file_write(const char *path, const void *buf, size_t len);
|
bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, size_t len);
|
||||||
```
|
```
|
||||||
Write data to a file.
|
Write data to a file.
|
||||||
The write is atomic, i.e. data gets written to a temporary file first, then `rename()-ed` to a destination file name.
|
The write is atomic, i.e. data gets written to a temporary file first, then `rename()-ed` to a destination file name.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
- `fs` - Filesystem to use
|
||||||
- `path` - Path to file
|
- `path` - Path to file
|
||||||
- `buf` - Data to write
|
- `buf` - Data to write
|
||||||
- `len` - Data length
|
- `len` - Data length
|
||||||
@@ -2860,7 +2887,7 @@ Usage example:
|
|||||||
|
|
||||||
```c
|
```c
|
||||||
char data[] = "Hello, world!";
|
char data[] = "Hello, world!";
|
||||||
if(mg_file_write("my_file.txt", data, sizeof(data) - 1)) {
|
if(mg_file_write(&mg_fs_posix, "my_file.txt", data, sizeof(data) - 1)) {
|
||||||
// File contains "Hello, world!" string
|
// File contains "Hello, world!" string
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -2868,7 +2895,7 @@ if(mg_file_write("my_file.txt", data, sizeof(data) - 1)) {
|
|||||||
### mg\_file\_printf()
|
### mg\_file\_printf()
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int mg_file_printf(const char *path, const char *fmt, ...);
|
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
|
||||||
```
|
```
|
||||||
|
|
||||||
Write into a file `path` using `printf()` semantics.
|
Write into a file `path` using `printf()` semantics.
|
||||||
@@ -2876,13 +2903,14 @@ 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
|
- `fs` - Filesystem to use
|
||||||
|
- `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
|
||||||
|
|
||||||
```c
|
```c
|
||||||
if (mg_file_printf("my_file.txt", "Hello, %s!", "world") {
|
if (mg_file_printf(&mg_fs_posix, "my_file.txt", "Hello, %s!", "world") {
|
||||||
// File contains "Hello, world!" string
|
// File contains "Hello, world!" string
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -2995,7 +3023,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 +3068,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 +3217,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 +3246,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 +3274,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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ static void broadcast_mjpeg_frame(struct mg_mgr *mgr) {
|
|||||||
static size_t i;
|
static size_t i;
|
||||||
const char *path = files[i++ % nfiles];
|
const char *path = files[i++ % nfiles];
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char *data = mg_file_read(path, &size); // Read next file
|
char *data = mg_file_read(&mg_fs_posix, path, &size); // Read next file
|
||||||
struct mg_connection *c;
|
struct mg_connection *c;
|
||||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||||
|
|||||||
@@ -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,25 +26,21 @@ 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 = {
|
||||||
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
||||||
int res = esp_vfs_spiffs_register(&conf);
|
int res = esp_vfs_spiffs_register(&conf);
|
||||||
LOG(res == ESP_OK ? LL_INFO : LL_ERROR, ("FS %s, %d", conf.base_path, res));
|
LOG(res == ESP_OK ? LL_INFO : LL_ERROR, ("FS %s, %d", conf.base_path, res));
|
||||||
mg_file_printf(FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
mg_file_printf(&mg_fs_posix, FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||||
|
|
||||||
// Setup wifi. This function is implemented in wifi.c
|
// Setup wifi. This function is implemented in wifi.c
|
||||||
// It blocks until connected to the configured WiFi network
|
// It blocks until connected to the configured WiFi network
|
||||||
|
|||||||
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/
|
||||||
@@ -10,10 +10,10 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
if (ev == MG_EV_HTTP_MSG) {
|
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, "/upload")) {
|
if (mg_http_match_uri(hm, "/upload")) {
|
||||||
mg_http_upload(c, hm, "/tmp");
|
mg_http_upload(c, hm, &mg_fs_posix, "/tmp");
|
||||||
} else {
|
} else {
|
||||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 @@ 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;
|
struct mg_connection *c2 = fn_data;
|
||||||
if (ev == MG_EV_READ) {
|
if (ev == MG_EV_READ) {
|
||||||
if (c2 != NULL) {
|
// All incoming data from the backend, forward to the client
|
||||||
// All incoming data from the backend, forward to the client
|
if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len);
|
||||||
mg_send(c2, c->recv.buf, c->recv.len);
|
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
}
|
if (c2 != NULL) c2->fn_data = NULL;
|
||||||
} 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,22 +50,21 @@ 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;
|
||||||
c->fn_data = NULL;
|
if (c2 != NULL) c2->fn_data = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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/
|
||||||
+27
-10
@@ -5,10 +5,10 @@
|
|||||||
#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 = NULL;
|
||||||
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";
|
static const char *s_ssi_pattern = "#.html";
|
||||||
|
|
||||||
// Handle interrupts, like Ctrl-C
|
// Handle interrupts, like Ctrl-C
|
||||||
static int s_signo;
|
static int s_signo;
|
||||||
@@ -20,9 +20,18 @@ 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_message *hm = ev_data, tmp = {};
|
||||||
.ssi_pattern = s_ssi_pattern};
|
struct mg_str unknown = mg_str_n("?", 1), *cl;
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
struct mg_http_serve_opts opts = {0};
|
||||||
|
opts.root_dir = s_root_dir;
|
||||||
|
opts.ssi_pattern = s_ssi_pattern;
|
||||||
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
|
mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
|
||||||
|
cl = mg_http_get_header(&tmp, "Content-Length");
|
||||||
|
if (cl == NULL) cl = &unknown;
|
||||||
|
LOG(LL_INFO, ("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr,
|
||||||
|
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len,
|
||||||
|
tmp.uri.ptr, (int) cl->len, cl->ptr));
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
(void) fn_data;
|
||||||
}
|
}
|
||||||
@@ -32,16 +41,18 @@ 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"
|
" -S PAT - SSI filename pattern, 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_ssi_pattern, s_root_dir,
|
||||||
s_root_dir, s_listening_address);
|
s_listening_address, s_debug_level);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
char path[MG_PATH_MAX] = ".";
|
||||||
|
const char *root_dir = ".";
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
struct mg_connection *c;
|
struct mg_connection *c;
|
||||||
int i;
|
int i;
|
||||||
@@ -49,7 +60,7 @@ int main(int argc, char *argv[]) {
|
|||||||
// Parse command-line flags
|
// Parse command-line flags
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "-d") == 0) {
|
if (strcmp(argv[i], "-d") == 0) {
|
||||||
s_root_dir = argv[++i];
|
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) {
|
} else if (strcmp(argv[i], "-S") == 0) {
|
||||||
@@ -63,6 +74,10 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Root directory must not contain double dots. Make it absolute
|
||||||
|
realpath(root_dir, path);
|
||||||
|
s_root_dir = path;
|
||||||
|
|
||||||
// Initialise stuff
|
// Initialise stuff
|
||||||
signal(SIGINT, signal_handler);
|
signal(SIGINT, signal_handler);
|
||||||
signal(SIGTERM, signal_handler);
|
signal(SIGTERM, signal_handler);
|
||||||
@@ -76,7 +91,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,11 @@
|
|||||||
|
PROG ?= example
|
||||||
|
CFLAGS ?= -W -Wall -Wextra -O0 -g
|
||||||
|
|
||||||
|
all: $(PROG)
|
||||||
|
$(RUN) ./$(PROG) $(ARGS)
|
||||||
|
|
||||||
|
$(PROG): main.c
|
||||||
|
$(CC) ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 $(CFLAGS) -o $(PROG) main.c mjson.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2021 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// Example that demonstrates how to send a large responses with limited memory.
|
||||||
|
// We're going to send a JSON array of many integer values, s_data.
|
||||||
|
// The idea is to send a response in small chunks, and let the client request
|
||||||
|
// the next chunk.
|
||||||
|
// Periodically, s_data changes, which is tracked by s_version.
|
||||||
|
// Client requests a range and a version, to ensure data integrity.
|
||||||
|
//
|
||||||
|
// 1. Start this server, type `make`
|
||||||
|
// 2. Open http://localhost:8000 in your browser
|
||||||
|
|
||||||
|
#include "mjson.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
static const char *s_listen_on = "http://localhost:8000";
|
||||||
|
static const char *s_root_dir = "web_root";
|
||||||
|
|
||||||
|
#define DATA_SIZE 10000 // Total number of elements
|
||||||
|
#define CHUNK_SIZE 1000 // Max number returned in one API call
|
||||||
|
static int s_data[DATA_SIZE]; // Simulate some complex big data
|
||||||
|
static unsigned s_version = 0; // Data "version"
|
||||||
|
|
||||||
|
static double getparam(struct mg_http_message *hm, const char *json_path) {
|
||||||
|
double dv = 0;
|
||||||
|
mjson_get_number(hm->body.ptr, hm->body.len, json_path, &dv);
|
||||||
|
return dv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_message *hm = ev_data;
|
||||||
|
if (mg_http_match_uri(hm, "/api/data")) {
|
||||||
|
unsigned start = getparam(hm, "$.start");
|
||||||
|
unsigned version = getparam(hm, "$.version");
|
||||||
|
char *response = NULL;
|
||||||
|
if (version > 0 && version != s_version) {
|
||||||
|
// Version mismatch: s_data has changed while client fetches it
|
||||||
|
// Tell client to restart
|
||||||
|
response = mjson_aprintf("{%Q:%Q, %Q:%u}", "error", "wrong version",
|
||||||
|
"version", version);
|
||||||
|
} else {
|
||||||
|
// Return data, up to CHUNK_SIZE elements
|
||||||
|
unsigned max = start + CHUNK_SIZE;
|
||||||
|
const char *comma = "";
|
||||||
|
if (max > DATA_SIZE) max = DATA_SIZE;
|
||||||
|
mjson_printf(mjson_print_dynamic_buf, &response, "{%Q:%u,%Q:%u,%Q:[",
|
||||||
|
"version", s_version, "start", start, "data");
|
||||||
|
while (start < max) {
|
||||||
|
mjson_printf(mjson_print_dynamic_buf, &response, "%s%d", comma,
|
||||||
|
s_data[start]);
|
||||||
|
comma = ",";
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
mjson_printf(mjson_print_dynamic_buf, &response, "]}");
|
||||||
|
}
|
||||||
|
mg_http_reply(c, 200, "Content-Type: text/json\r\n", "%s", response);
|
||||||
|
free(response);
|
||||||
|
} else {
|
||||||
|
struct mg_http_serve_opts opts = {0};
|
||||||
|
opts.root_dir = s_root_dir;
|
||||||
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(void) fn_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_fn(void *arg) {
|
||||||
|
for (int i = 0; i < DATA_SIZE; i++) {
|
||||||
|
s_data[i] = rand();
|
||||||
|
}
|
||||||
|
s_version++;
|
||||||
|
(void) arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
struct mg_timer t1;
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
srand(time(NULL));
|
||||||
|
mg_timer_init(&t1, 1000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, NULL);
|
||||||
|
mg_http_listen(&mgr, s_listen_on, fn, NULL);
|
||||||
|
LOG(LL_INFO, ("Listening on %s", s_listen_on));
|
||||||
|
for (;;) mg_mgr_poll(&mgr, 1000);
|
||||||
|
mg_timer_free(&t1);
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,204 @@
|
|||||||
|
// Copyright (c) 2018-2020 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef MJSON_H
|
||||||
|
#define MJSON_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_PRINT
|
||||||
|
#define MJSON_ENABLE_PRINT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_RPC
|
||||||
|
#define MJSON_ENABLE_RPC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_BASE64
|
||||||
|
#define MJSON_ENABLE_BASE64 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_MERGE
|
||||||
|
#define MJSON_ENABLE_MERGE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_PRETTY
|
||||||
|
#define MJSON_ENABLE_PRETTY 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_ENABLE_NEXT
|
||||||
|
#define MJSON_ENABLE_NEXT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_RPC_LIST_NAME
|
||||||
|
#define MJSON_RPC_LIST_NAME "rpc.list"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MJSON_DYNBUF_CHUNK
|
||||||
|
#define MJSON_DYNBUF_CHUNK 256 // Allocation granularity for print_dynamic_buf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MJSON_ERROR_INVALID_INPUT (-1)
|
||||||
|
#define MJSON_ERROR_TOO_DEEP (-2)
|
||||||
|
#define MJSON_TOK_INVALID 0
|
||||||
|
#define MJSON_TOK_KEY 1
|
||||||
|
#define MJSON_TOK_STRING 11
|
||||||
|
#define MJSON_TOK_NUMBER 12
|
||||||
|
#define MJSON_TOK_TRUE 13
|
||||||
|
#define MJSON_TOK_FALSE 14
|
||||||
|
#define MJSON_TOK_NULL 15
|
||||||
|
#define MJSON_TOK_ARRAY 91
|
||||||
|
#define MJSON_TOK_OBJECT 123
|
||||||
|
#define MJSON_TOK_IS_VALUE(t) ((t) > 10 && (t) < 20)
|
||||||
|
|
||||||
|
typedef int (*mjson_cb_t)(int ev, const char *s, int off, int len, void *ud);
|
||||||
|
|
||||||
|
#ifndef MJSON_MAX_DEPTH
|
||||||
|
#define MJSON_MAX_DEPTH 20
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int mjson(const char *s, int len, mjson_cb_t cb, void *ud);
|
||||||
|
int mjson_find(const char *s, int len, const char *jp, const char **, int *);
|
||||||
|
int mjson_get_number(const char *s, int len, const char *path, double *v);
|
||||||
|
int mjson_get_bool(const char *s, int len, const char *path, int *v);
|
||||||
|
int mjson_get_string(const char *s, int len, const char *path, char *to, int n);
|
||||||
|
int mjson_get_hex(const char *s, int len, const char *path, char *to, int n);
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_NEXT
|
||||||
|
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
|
||||||
|
int *vlen, int *vtype);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_BASE64
|
||||||
|
int mjson_get_base64(const char *s, int len, const char *path, char *to, int n);
|
||||||
|
int mjson_base64_dec(const char *src, int n, char *dst, int dlen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_PRINT
|
||||||
|
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
|
||||||
|
typedef int (*mjson_vprint_fn_t)(mjson_print_fn_t, void *, va_list *);
|
||||||
|
|
||||||
|
struct mjson_fixedbuf {
|
||||||
|
char *ptr;
|
||||||
|
int size, len;
|
||||||
|
};
|
||||||
|
|
||||||
|
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
|
||||||
|
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list *ap);
|
||||||
|
int mjson_print_str(mjson_print_fn_t, void *, const char *s, int len);
|
||||||
|
int mjson_print_int(mjson_print_fn_t, void *, int value, int is_signed);
|
||||||
|
int mjson_print_long(mjson_print_fn_t, void *, long value, int is_signed);
|
||||||
|
int mjson_print_buf(mjson_print_fn_t fn, void *, const char *buf, int len);
|
||||||
|
int mjson_print_dbl(mjson_print_fn_t fn, void *, double, int width);
|
||||||
|
|
||||||
|
int mjson_print_null(const char *ptr, int len, void *userdata);
|
||||||
|
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
|
||||||
|
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
|
||||||
|
|
||||||
|
int mjson_snprintf(char *buf, size_t len, const char *fmt, ...);
|
||||||
|
char *mjson_aprintf(const char *fmt, ...);
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_PRETTY
|
||||||
|
int mjson_pretty(const char *, int, const char *, mjson_print_fn_t, void *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_MERGE
|
||||||
|
int mjson_merge(const char *, int, const char *, int, mjson_print_fn_t, void *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // MJSON_ENABLE_PRINT
|
||||||
|
|
||||||
|
#if MJSON_ENABLE_RPC
|
||||||
|
|
||||||
|
void jsonrpc_init(mjson_print_fn_t, void *userdata);
|
||||||
|
int mjson_globmatch(const char *s1, int n1, const char *s2, int n2);
|
||||||
|
|
||||||
|
struct jsonrpc_request {
|
||||||
|
struct jsonrpc_ctx *ctx;
|
||||||
|
const char *frame; // Points to the whole frame
|
||||||
|
int frame_len; // Frame length
|
||||||
|
const char *params; // Points to the "params" in the request frame
|
||||||
|
int params_len; // Length of the "params"
|
||||||
|
const char *id; // Points to the "id" in the request frame
|
||||||
|
int id_len; // Length of the "id"
|
||||||
|
const char *method; // Points to the "method" in the request frame
|
||||||
|
int method_len; // Length of the "method"
|
||||||
|
mjson_print_fn_t fn; // Printer function
|
||||||
|
void *fndata; // Printer function data
|
||||||
|
void *userdata; // Callback's user data as specified at export time
|
||||||
|
};
|
||||||
|
|
||||||
|
struct jsonrpc_method {
|
||||||
|
const char *method;
|
||||||
|
int method_sz;
|
||||||
|
void (*cb)(struct jsonrpc_request *);
|
||||||
|
struct jsonrpc_method *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main RPC context, stores current request information and a list of
|
||||||
|
// exported RPC methods.
|
||||||
|
struct jsonrpc_ctx {
|
||||||
|
struct jsonrpc_method *methods;
|
||||||
|
mjson_print_fn_t response_cb;
|
||||||
|
void *response_cb_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registers function fn under the given name within the given RPC context
|
||||||
|
#define jsonrpc_ctx_export(ctx, name, fn) \
|
||||||
|
do { \
|
||||||
|
static struct jsonrpc_method m = {(name), sizeof(name) - 1, (fn), 0}; \
|
||||||
|
m.next = (ctx)->methods; \
|
||||||
|
(ctx)->methods = &m; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void jsonrpc_ctx_init(struct jsonrpc_ctx *ctx, mjson_print_fn_t, void *);
|
||||||
|
void jsonrpc_return_error(struct jsonrpc_request *r, int code,
|
||||||
|
const char *message, const char *data_fmt, ...);
|
||||||
|
void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt,
|
||||||
|
...);
|
||||||
|
void jsonrpc_ctx_process(struct jsonrpc_ctx *ctx, const char *req, int req_sz,
|
||||||
|
mjson_print_fn_t fn, void *fndata, void *userdata);
|
||||||
|
|
||||||
|
extern struct jsonrpc_ctx jsonrpc_default_context;
|
||||||
|
extern void jsonrpc_list(struct jsonrpc_request *r);
|
||||||
|
|
||||||
|
#define jsonrpc_export(name, fn) \
|
||||||
|
jsonrpc_ctx_export(&jsonrpc_default_context, (name), (fn))
|
||||||
|
|
||||||
|
#define jsonrpc_process(buf, len, fn, fnd, ud) \
|
||||||
|
jsonrpc_ctx_process(&jsonrpc_default_context, (buf), (len), (fn), (fnd), (ud))
|
||||||
|
|
||||||
|
#define JSONRPC_ERROR_INVALID -32700 /* Invalid JSON was received */
|
||||||
|
#define JSONRPC_ERROR_NOT_FOUND -32601 /* The method does not exist */
|
||||||
|
#define JSONRPC_ERROR_BAD_PARAMS -32602 /* Invalid params passed */
|
||||||
|
#define JSONRPC_ERROR_INTERNAL -32603 /* Internal JSON-RPC error */
|
||||||
|
|
||||||
|
#endif // MJSON_ENABLE_RPC
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // MJSON_H
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>example</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<style>
|
||||||
|
#container { margin-right: auto; margin-left: auto; max-width: 480px; }
|
||||||
|
#info { background: #e0f0f0; border-radius: .5em; padding: 2em; margin-bottom: 1em; }
|
||||||
|
#result { margin-top: 1em; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="info">
|
||||||
|
On devices with limited RAM, it is important to limit the response size
|
||||||
|
of API calls. This example demonstrates how to fetch a large
|
||||||
|
data in smaller chunks, and guarantee its integrity.
|
||||||
|
Data gets returned in a series of request/response transactions,
|
||||||
|
where each response is small enough to fit into available device RAM.
|
||||||
|
<br/><br/>
|
||||||
|
Data integrity is implemented by versioning.
|
||||||
|
The idea is that the first response includes the current "version" of the
|
||||||
|
data, and that version is passed to all subsequent requests.
|
||||||
|
If data version changes in the middle of the request series,
|
||||||
|
client fails with 'wrong version' error.
|
||||||
|
</div>
|
||||||
|
<button id="btn">fetch data</button>
|
||||||
|
<div id="result"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const getchunk = (start, version) =>
|
||||||
|
fetch('/api/data', {method: 'POST', body:JSON.stringify({start, version})})
|
||||||
|
.then(r => r.json());
|
||||||
|
document.getElementById('btn').onclick = function() {
|
||||||
|
var data = [], version = 0;
|
||||||
|
const load = offset => getchunk(offset, version)
|
||||||
|
.then(r => {
|
||||||
|
// console.log(r);
|
||||||
|
if (r.error) {
|
||||||
|
document.getElementById('result').innerText = 'Error: ' + r.error;
|
||||||
|
} else {
|
||||||
|
version = r.version;
|
||||||
|
data = data.concat(r.data);
|
||||||
|
if (r.data.length == 0) {
|
||||||
|
document.getElementById('result').innerText = 'Version: ' +
|
||||||
|
version + ', data: \n' +
|
||||||
|
JSON.stringify(data, null, 2);
|
||||||
|
} else {
|
||||||
|
load(offset + r.data.length, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
load(0);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -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 = {.clean = true};
|
||||||
|
bool done = false;
|
||||||
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
|
LOG(LL_INFO, ("Connecting to %s", s_url)); // Inform that we're starting
|
||||||
|
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/
|
||||||
+52
-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,51 @@ 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 = {.clean = true,
|
||||||
|
.will_qos = s_qos,
|
||||||
|
.will_topic = mg_str(s_pub_topic),
|
||||||
|
.will_message = mg_str("goodbye")};
|
||||||
|
if (s_conn == NULL) s_conn = mg_mqtt_connect(mgr, s_url, &opts, fn, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -28,11 +28,11 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
} else if (ev == MG_EV_WS_OPEN) {
|
} else if (ev == MG_EV_WS_OPEN) {
|
||||||
// WS connection established. Perform MQTT login
|
// WS connection established. Perform MQTT login
|
||||||
LOG(LL_INFO, ("Connected to WS. Logging in to MQTT..."));
|
LOG(LL_INFO, ("Connected to WS. Logging in to MQTT..."));
|
||||||
struct mg_mqtt_opts opts = {.qos = 1,
|
struct mg_mqtt_opts opts = {.will_qos = 1,
|
||||||
.will_topic = mg_str(s_topic),
|
.will_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;
|
||||||
@@ -46,10 +46,10 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
struct mg_str topic = mg_str(s_topic), data = mg_str("hello");
|
struct mg_str topic = mg_str(s_topic), data = mg_str("hello");
|
||||||
size_t len = c->send.len;
|
size_t len = c->send.len;
|
||||||
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, topic, 1);
|
||||||
mg_ws_wrap(c, len, WEBSOCKET_OP_BINARY);
|
mg_ws_wrap(c, len, WEBSOCKET_OP_BINARY);
|
||||||
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) topic.len, topic.ptr));
|
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) topic.len, topic.ptr));
|
||||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
mg_mqtt_pub(c, topic, data, 1, false);
|
||||||
LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
|
LOG(LL_INFO, ("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
|
||||||
(int) topic.len, topic.ptr));
|
(int) topic.len, topic.ptr));
|
||||||
len = mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY);
|
len = mg_ws_wrap(c, c->send.len - len, WEBSOCKET_OP_BINARY);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,8 @@ static void start_thread(void (*f)(void *), void *p) {
|
|||||||
|
|
||||||
static void thread_function(void *param) {
|
static void thread_function(void *param) {
|
||||||
struct mg_connection *c = param; // Pipe connection
|
struct mg_connection *c = param; // Pipe connection
|
||||||
mg_usleep(2 * 1000000); // Simulate long execution
|
sleep(2); // Simulate long execution
|
||||||
mg_mgr_wakeup(c); // Wakeup event manager
|
mg_mgr_wakeup(c, "hi", 2); // Wakeup event manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP request callback
|
// HTTP request callback
|
||||||
@@ -62,8 +62,9 @@ static void pcb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
struct mg_connection *t;
|
struct mg_connection *t;
|
||||||
for (t = c->mgr->conns; t != NULL; t = t->next) {
|
for (t = c->mgr->conns; t != NULL; t = t->next) {
|
||||||
if (t->label[0] != 'W') continue; // Ignore un-marked connections
|
if (t->label[0] != 'W') continue; // Ignore un-marked connections
|
||||||
mg_http_reply(t, 200, "Host: foo.com\r\n", "hi\n"); // Respond!
|
mg_http_reply(t, 200, "Host: foo.com\r\n", "%.*s\n", c->recv.len,
|
||||||
t->label[0] = 0; // Clear mark
|
c->recv.buf); // Respond!
|
||||||
|
t->label[0] = 0; // Clear mark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user