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
|
||||
|
||||
- mongoose version (e.g. 7.3): _X.Y_
|
||||
- Compiler/IDE (e.g. arm-none-eabi-gcc 9.3): _?_
|
||||
- Target OS (e.g. FreeRTOS 10.3.1, Linux 5.8): _?_
|
||||
- Target hardware/board (e.g. STM32F411): _?_
|
||||
- Connectivity chip/module (e.g. WizNet W5500): _?_
|
||||
- Target SDK (e.g. STM32CubeF1 1.8.4): _?_
|
||||
- mongoose version: _X.Y_
|
||||
- Compiler/IDE and SDK: ZZZ version _X.Y_
|
||||
- Target hardware/board: _?_
|
||||
- Connectivity chip/module: _?_
|
||||
@@ -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
|
||||
- name: Install packages
|
||||
run: sudo apt-get install libmbedtls-dev
|
||||
- name: make
|
||||
run: make test test++ IPV6=0
|
||||
- name: gcc
|
||||
run: make test test++ IPV6=0 CC=gcc
|
||||
- name: clang
|
||||
run: make test test++ IPV6=0 CC=clang
|
||||
- name: openssl
|
||||
run: make test SSL=OPENSSL IPV6=0
|
||||
- name: unamalgamated
|
||||
@@ -68,28 +70,142 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: stm32-freertos-tcp
|
||||
run: make -C examples/stm32-freertos-tcp build
|
||||
run: make -C examples/stm32/stm32-freertos-tcp build
|
||||
stm32-nucleo-f746z:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: stm32-nucleo-f746z
|
||||
run: make -C examples/stm32-nucleo-f746z build
|
||||
run: make -C examples/stm32/stm32-nucleo-f746z build
|
||||
stm32-nucleo-f429z:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: stm32-nucleo-f429z
|
||||
run: make -C examples/stm32-nucleo-f429z build
|
||||
run: make -C examples/stm32/stm32-nucleo-f429z build
|
||||
stm32-nucleo-h743z:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: stm32-nucleo-h743z
|
||||
run: make -C examples/stm32-nucleo-h743z build
|
||||
run: make -C examples/stm32/stm32-nucleo-h743z build
|
||||
nxp-mimxrt1020-azurertos:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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.
|
||||
|
||||
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
|
||||
HDRS = $(wildcard src/*.h)
|
||||
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
|
||||
INCS ?= -Isrc -I.
|
||||
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
|
||||
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:
|
||||
@for X in $(EXAMPLES); do $(MAKE) -C $$X example || break; done
|
||||
@@ -59,7 +59,7 @@ fuzz: fuzzer
|
||||
$(RUN) ./fuzzer
|
||||
|
||||
# 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)
|
||||
$(CC) $(SRCS) $(CFLAGS) -coverage $(LDFLAGS) -g -o 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
|
||||
|
||||
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
|
||||
(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:
|
||||
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
|
||||
control for a product, remote software updates, remote monitoring, and others
|
||||
- Deployed to hundreds of millions devices in production environment worldwide
|
||||
- See [Case Studies](https://cesanta.com/case-studies.html) from our respected
|
||||
customers like Schneider Electric (industrial automation), Broadcom (semiconductors), Schenck Process
|
||||
(industrial engineering), and others
|
||||
- See [Testimonials](https://cesanta.com/testimonials.html) from engineers that
|
||||
integrated Mongoose in their commercial products
|
||||
- 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)
|
||||
- See [Case Studies](https://mongoose.ws/case-studies/) from our respected
|
||||
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
|
||||
- See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products
|
||||
- We provide [Evaluation and Commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration
|
||||
assistance - don't hesitate to [contact us](https://mongoose.ws/contact/)
|
||||
|
||||
|
||||
# Security
|
||||
@@ -80,20 +76,6 @@ This software is often used together with Mongoose:
|
||||
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 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,
|
||||
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
|
||||
International Space Station! Mongoose makes embedded network programming fast,
|
||||
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
|
||||
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:
|
||||
|
||||
**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
|
||||
must be implemented by the user. Event handler is the key element of
|
||||
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
|
||||
// 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
|
||||
- `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
|
||||
meaning for different events. For example, for an `MG_EV_READ` event,
|
||||
`ev_data`
|
||||
@@ -132,24 +132,25 @@ to an event handler:
|
||||
|
||||
```c
|
||||
enum {
|
||||
MG_EV_ERROR, // Error char *error_message
|
||||
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
|
||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||
MG_EV_CONNECT, // Connection established NULL
|
||||
MG_EV_ACCEPT, // Connection accepted NULL
|
||||
MG_EV_READ, // Data received from socket struct mg_str *
|
||||
MG_EV_WRITE, // Data written to socket int *num_bytes_written
|
||||
MG_EV_CLOSE, // Connection closed NULL
|
||||
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
||||
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
||||
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
||||
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
||||
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
|
||||
MG_EV_USER, // Starting ID for user events
|
||||
MG_EV_ERROR, // Error char *error_message
|
||||
MG_EV_OPEN, // Connection created NULL
|
||||
MG_EV_POLL, // mg_mgr_poll iteration int64_t *milliseconds
|
||||
MG_EV_RESOLVE, // Host name is resolved NULL
|
||||
MG_EV_CONNECT, // Connection established NULL
|
||||
MG_EV_ACCEPT, // Connection accepted NULL
|
||||
MG_EV_READ, // Data received from socket struct mg_str *
|
||||
MG_EV_WRITE, // Data written to socket long *bytes_written
|
||||
MG_EV_CLOSE, // Connection closed NULL
|
||||
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
|
||||
MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
|
||||
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
|
||||
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
|
||||
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
|
||||
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
|
||||
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
|
||||
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
|
||||
MG_EV_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
|
||||
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:
|
||||
|
||||
@@ -232,8 +234,9 @@ Here is a list of build constants and their default values:
|
||||
| Name | Default | Description |
|
||||
| ---- | ------- | ----------- |
|
||||
|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_CUSTOM_TLS | 0 | Enable custom TLS library |
|
||||
|MG_ENABLE_IPV6 | 0 | Enable IPv6 |
|
||||
|MG_ENABLE_LOG | 1 | Enable `LOG()` macro |
|
||||
|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_CUSTOM_RANDOM | 0 | Provide custom RNG function `mg_random()` |
|
||||
|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_MAX_RECV_BUF_SIZE | (3 * 1024 * 1024) | Maximum recv buffer size |
|
||||
|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 |
|
||||
|
||||
<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
|
||||
|
||||
```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`)
|
||||
|
||||
Parameters:
|
||||
- `c` - A connection pointer
|
||||
- `addr` - A address pointer
|
||||
- `buf` - A pointer to a buffer that will hold stringified address
|
||||
- `len` - A buffer size
|
||||
|
||||
@@ -639,8 +645,8 @@ Return value: `buf` value
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
char buf[1024];
|
||||
mg_straddr(c, buf, sizeof(buf)); // `buf` is now IP address string, like "127.0.0.1:8080"
|
||||
char buf[100];
|
||||
LOG(LL_INFO, ("%s", mg_straddr(&c->peer, buf, sizeof(buf))));
|
||||
```
|
||||
|
||||
### mg\_mkpipe()
|
||||
@@ -660,7 +666,7 @@ common use cases:
|
||||
A pipe's event handler should push data to all client connection.
|
||||
Use `c->label` to mark client connections.
|
||||
- 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
|
||||
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.
|
||||
@@ -676,23 +682,26 @@ Parameters:
|
||||
|
||||
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()
|
||||
|
||||
```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
|
||||
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:
|
||||
- `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
|
||||
|
||||
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
|
||||
@@ -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
|
||||
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:
|
||||
- `c` - Connection to use
|
||||
- `hm` - HTTP message, that should be served
|
||||
@@ -989,7 +1002,7 @@ Return value: None
|
||||
|
||||
Usage examples:
|
||||
|
||||
Send a simple JSON respose:
|
||||
Send a simple JSON response:
|
||||
```c
|
||||
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()
|
||||
|
||||
```c
|
||||
@@ -1140,10 +1180,10 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
|
||||
```c
|
||||
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
|
||||
should have `name` and `offset` query string parameters set:
|
||||
@@ -1175,9 +1215,10 @@ The expected usage of this API function follows:
|
||||
Parameters:
|
||||
- `c` - Connection to use
|
||||
- `hm` - POST message, containing parameters described above
|
||||
- `fs` - Filesystem to use
|
||||
- `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:
|
||||
|
||||
@@ -1186,7 +1227,7 @@ Usage example:
|
||||
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
mg_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
|
||||
|
||||
Usage example:
|
||||
Usage example which uses Basic auth to create Stripe subscription:
|
||||
|
||||
```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()
|
||||
@@ -1239,7 +1288,7 @@ Parameters:
|
||||
|
||||
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">
|
||||
|
||||
@@ -1282,7 +1331,7 @@ Note: this function does not connect to peer, it allocates required resources an
|
||||
Parameters:
|
||||
- `mgr` - Event manager to use
|
||||
- `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_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
|
||||
@@ -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
|
||||
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:
|
||||
- `c` - Connection to use
|
||||
@@ -1455,7 +1504,7 @@ struct mg_mqtt_opts {
|
||||
struct mg_str client_id; // Client id
|
||||
struct mg_str will_topic; // Will Topic
|
||||
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 clean; // Use clean session, 0 or 1
|
||||
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()
|
||||
|
||||
```c
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
|
||||
struct mg_str *data, int qos, bool retain);
|
||||
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
|
||||
struct mg_str data, int qos, bool retain);
|
||||
```
|
||||
|
||||
Publish message.
|
||||
@@ -1591,15 +1640,13 @@ Return value: None
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
struct mg_str topic = mg_str("topic");
|
||||
struct mg_str data = mg_str("data");
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
mg_mqtt_pub(c, mg_str("topic"), mg_str("my data"), 1, false);
|
||||
```
|
||||
|
||||
### mg\_mqtt\_sub()
|
||||
|
||||
```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.
|
||||
@@ -1607,13 +1654,12 @@ Subscribe to topic.
|
||||
Parameters:
|
||||
- `c` - Connection to use
|
||||
- `topic` - Topic to subscribe
|
||||
- `qos` - Required Qos
|
||||
- `qos` - Required QoS
|
||||
|
||||
Return value: none
|
||||
|
||||
```c
|
||||
struct mg_str topic = mg_str("topic");
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
mg_mqtt_sub(c, mg_str("my/topic"), 1);
|
||||
```
|
||||
|
||||
### mg\_mqtt\_next\_sub()
|
||||
@@ -1629,7 +1675,7 @@ Initial position `pos` should be 4.
|
||||
Parameters:
|
||||
- `mgs` - MQTT message
|
||||
- `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
|
||||
|
||||
Return value: Next position, or 0 when done
|
||||
@@ -1802,10 +1848,12 @@ while ((mg_mqtt_parse(buf, len, &mm)) == 0) {
|
||||
```c
|
||||
struct mg_tls_opts {
|
||||
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 *certkey; // Certificate key
|
||||
const char *ciphers; // Cipher list
|
||||
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
|
||||
a certificate that the other end sends to us. If NULL, then certificate checking
|
||||
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
|
||||
we don't authenticate with the other peer
|
||||
- `certkey` - A key for a `cert`. Sometimes, a certificate and its key are
|
||||
@@ -1857,16 +1908,15 @@ mg_tls_init(c, &opts);
|
||||
|
||||
```c
|
||||
struct mg_timer {
|
||||
int period_ms; // Timer period in milliseconds
|
||||
int flags; // Possible flags values below
|
||||
void (*fn)(void *); // Function to call
|
||||
void *arg; // Function argument
|
||||
unsigned long expire; // Expiration timestamp in milliseconds
|
||||
struct mg_timer *next; // Linkage in g_timers list
|
||||
};
|
||||
|
||||
int64_t period_ms; // Timer period in milliseconds
|
||||
int64_t expire; // Expiration timestamp in milliseconds
|
||||
unsigned flags; // Possible flags values below
|
||||
#define MG_TIMER_REPEAT 1 // Call function periodically, otherwise run once
|
||||
#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
|
||||
@@ -1875,7 +1925,7 @@ as the `mg_mgr_poll()` timeout argument in the main event loop.
|
||||
### mg\_timer\_init()
|
||||
|
||||
```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);
|
||||
```
|
||||
|
||||
@@ -1923,7 +1973,7 @@ mg_timer_free(&timer);
|
||||
### mg\_timer\_poll()
|
||||
|
||||
```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
|
||||
@@ -1940,34 +1990,16 @@ Return value: None
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
unsigned long now = mg_millis();
|
||||
int64_t now = mg_millis();
|
||||
mg_timer_poll(now);
|
||||
```
|
||||
|
||||
## 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()
|
||||
|
||||
```c
|
||||
unsigned long mg_millis(void);
|
||||
int64_t mg_millis(void);
|
||||
```
|
||||
|
||||
Return current uptime in milliseconds.
|
||||
@@ -1979,42 +2011,34 @@ Return value: Current uptime
|
||||
Usage example:
|
||||
|
||||
```c
|
||||
unsigned long 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 */)
|
||||
int64_t uptime = mg_millis();
|
||||
```
|
||||
|
||||
## 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 {
|
||||
const char *ptr; // Pointer to string data
|
||||
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()
|
||||
|
||||
@@ -2045,12 +2069,13 @@ struct mg_str str = mg_str("Hello, world!);
|
||||
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>
|
||||
Note: This function doesn't duplicate provided string, but stores pointer within created `mg_str` structure.
|
||||
Create Mongoose string from C-string `s` (can be non-NULL terminated, length is
|
||||
specified in `n`). Note: This function doesn't duplicate provided string,
|
||||
but stores pointer within created `mg_str` structure.
|
||||
|
||||
Parameters:
|
||||
- `s` - Pointer to string to store in created `mg_str`
|
||||
- `n` - String len
|
||||
- `n` - String length
|
||||
|
||||
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);
|
||||
```
|
||||
|
||||
Сompare mongoose string and C-string.
|
||||
Compare mongoose string and C-string.
|
||||
|
||||
Parameters:
|
||||
- `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);
|
||||
```
|
||||
|
||||
Сompare two mongoose strings.
|
||||
Compare two mongoose strings.
|
||||
|
||||
Parameters:
|
||||
- `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`.
|
||||
|
||||
Paramaters:
|
||||
Parameters:
|
||||
- `s` - Mongoose string for trimming
|
||||
|
||||
Return value: Input string
|
||||
@@ -2264,9 +2289,9 @@ The glob pattern matching rules are as follows:
|
||||
|
||||
Parameters:
|
||||
- `pattern` - Pattern to match for
|
||||
- `p_len` - Pattetn lenght
|
||||
- `p_len` - Pattern length
|
||||
- `s` - String to match
|
||||
- `s_len` - String lenght
|
||||
- `s_len` - String length
|
||||
|
||||
Return value: `true` if matches, `false` otherwise
|
||||
|
||||
@@ -2322,7 +2347,7 @@ It is a caller's responsibility to free() returned pointer.
|
||||
|
||||
Parameters:
|
||||
- `buf` - Data to hexdump
|
||||
- `len` - Data lenght
|
||||
- `len` - Data length
|
||||
|
||||
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.
|
||||
|
||||
Parameters:
|
||||
- `c` - Sha1 context
|
||||
- `c` - SHA1 context
|
||||
- `data` - Data to hash
|
||||
- `len` - Data length
|
||||
|
||||
@@ -2739,7 +2764,7 @@ Parameters:
|
||||
- `out` - Pointer to 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:
|
||||
|
||||
@@ -2760,7 +2785,7 @@ Parameters:
|
||||
- `buf` - Pointer to buffer to write finish mark
|
||||
- `pos` - Position to write
|
||||
|
||||
Return value: New position for futher operations
|
||||
Return value: New position for further operations
|
||||
|
||||
```c
|
||||
char buf[10];
|
||||
@@ -2817,7 +2842,7 @@ mg_base64_decode("Q2VzYW50YQ==", 12, buf); // buf is now "Cesanta"
|
||||
### mg\_file\_read()
|
||||
|
||||
```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
|
||||
@@ -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.
|
||||
|
||||
Parameters:
|
||||
- `fs` - Filesystem to use
|
||||
- `path` - Path to file to read
|
||||
- `sizep` - Pointer to `size_t` to receive file size
|
||||
|
||||
@@ -2834,7 +2860,7 @@ Usage example:
|
||||
|
||||
```c
|
||||
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) {
|
||||
// `data` is now pointer to information readen from file and `file_size` is it size.
|
||||
}
|
||||
@@ -2844,12 +2870,13 @@ free(data);
|
||||
### mg\_file\_write()
|
||||
|
||||
```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.
|
||||
The write is atomic, i.e. data gets written to a temporary file first, then `rename()-ed` to a destination file name.
|
||||
|
||||
Parameters:
|
||||
- `fs` - Filesystem to use
|
||||
- `path` - Path to file
|
||||
- `buf` - Data to write
|
||||
- `len` - Data length
|
||||
@@ -2860,7 +2887,7 @@ Usage example:
|
||||
|
||||
```c
|
||||
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
|
||||
}
|
||||
```
|
||||
@@ -2868,7 +2895,7 @@ if(mg_file_write("my_file.txt", data, sizeof(data) - 1)) {
|
||||
### mg\_file\_printf()
|
||||
|
||||
```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.
|
||||
@@ -2876,13 +2903,14 @@ This function prints data to a
|
||||
temporary in-memory buffer first, then calls `mg_file_write()`.
|
||||
|
||||
Parameters:
|
||||
- `path`- ath to file
|
||||
- `fs` - Filesystem to use
|
||||
- `path`- path to file
|
||||
- `fmt` - Format string in `printf()` semantics
|
||||
|
||||
Return value: `true` on success, `false` otherwise
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
@@ -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`.
|
||||
|
||||
Parameters:
|
||||
- `crc` - Initial crc value
|
||||
- `crc` - Initial CRC value
|
||||
- `buf` - Data to calculate CRC32
|
||||
- `len` - Data size
|
||||
|
||||
@@ -3040,7 +3068,7 @@ Decode URL-encoded string `s` and write it into `to` buffer.
|
||||
|
||||
Parameters:
|
||||
- `s` - String to encode
|
||||
- `n` - String to encode lenght
|
||||
- `n` - String to encode length
|
||||
- `to` - Pointer to output buffer
|
||||
- `to_len` - Output buffer size
|
||||
- `form` - If non-zero, then `+` is decoded as whitespace.
|
||||
@@ -3189,10 +3217,10 @@ Parameters:
|
||||
- `io` - iobuf to add data
|
||||
- `offset` - Offset to add data
|
||||
- `buf` - Data to add
|
||||
- `len` - Data lenth
|
||||
- `len` - Data length
|
||||
- `align` - Align boundary
|
||||
|
||||
Return value: new `io` len
|
||||
Return value: new `io` length
|
||||
|
||||
Usage example:
|
||||
|
||||
@@ -3218,7 +3246,7 @@ Parameters:
|
||||
- `offset` - Start offset
|
||||
- `len` - Amount of bytes to delete
|
||||
|
||||
Return value: New `io` len
|
||||
Return value: New `io` length
|
||||
|
||||
Usage example:
|
||||
|
||||
@@ -3246,7 +3274,8 @@ Return port for given URL
|
||||
Parameters:
|
||||
- `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:
|
||||
|
||||
|
||||
@@ -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) {
|
||||
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
|
||||
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) {
|
||||
char buf[12 + n + sizeof(answer)];
|
||||
char buf[512];
|
||||
struct mg_dns_header *h = (struct mg_dns_header *) buf;
|
||||
memset(buf, 0, sizeof(buf)); // Clear the whole datagram
|
||||
h->txnid = ((struct mg_dns_header *) c->recv.buf)->txnid; // Copy tnxid
|
||||
@@ -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
|
||||
memcpy(buf + sizeof(*h), c->recv.buf + sizeof(*h), n); // Copy question
|
||||
memcpy(buf + sizeof(*h) + n, answer, sizeof(answer)); // And answer
|
||||
mg_send(c, buf, sizeof(buf)); // And send it!
|
||||
mg_send(c, buf, 12 + n + sizeof(answer)); // And send it!
|
||||
}
|
||||
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;
|
||||
const char *path = files[i++ % nfiles];
|
||||
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;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->label[0] != 'S') continue; // Skip non-stream connections
|
||||
|
||||
@@ -1,28 +1 @@
|
||||
# An example ESP32 application
|
||||
|
||||
To build this application, follow these steps:
|
||||
|
||||
1. Make sure you have Docker installed
|
||||
2. Clone whole mongoose repository on your machine:
|
||||
```sh
|
||||
git clone --depth 1 https://github.com/cesanta/mongoose.git
|
||||
```
|
||||
3. Start command line or terminal, and go into the example's directory:
|
||||
```sh
|
||||
cd mongoose/examples/esp32
|
||||
```
|
||||
4. Run `make`. This invokes a Docker-based build. A firmware will be built
|
||||
in the `build/` directory:
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
# Flashing built firmware
|
||||
|
||||
If you have a real board to flash to, you can flash a built firmware.
|
||||
First, make sure to have `esptool.py` script installed on your workstation.
|
||||
Then, connect the board to your workstation and run the following command:
|
||||
|
||||
```sh
|
||||
make flash COMPORT=YOUR_USB_PORT
|
||||
```
|
||||
See detailed tutorial at https://mongoose.ws/tutorials/esp32/
|
||||
@@ -1,5 +1,4 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
"wifi.c"
|
||||
"../../../mongoose.c"
|
||||
INCLUDE_DIRS "../../..")
|
||||
"mongoose.c")
|
||||
component_compile_options(-DMG_ENABLE_LINES)
|
||||
@@ -9,6 +9,14 @@
|
||||
#define WIFI_PASS "WIFI_PASSWORD" // SET THIS!
|
||||
#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:
|
||||
// /api/stats - return JSON object with ESP32 stats (free RAM)
|
||||
// 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")) {
|
||||
mg_http_reply(c, 200, "", "{\"ram\": %lu}\n", xPortGetFreeHeapSize());
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SPIFFS is flat, so tell Mongoose that the FS root is a directory
|
||||
// This cludge is not required for filesystems with directory support
|
||||
bool mg_is_dir(const char *path) {
|
||||
return strcmp(path, FS_ROOT) == 0;
|
||||
}
|
||||
|
||||
void app_main(void) {
|
||||
// Mount filesystem
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = FS_ROOT, .max_files = 20, .format_if_mount_failed = true};
|
||||
int res = esp_vfs_spiffs_register(&conf);
|
||||
LOG(res == ESP_OK ? LL_INFO : LL_ERROR, ("FS %s, %d", conf.base_path, res));
|
||||
mg_file_printf(FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||
mg_file_printf(&mg_fs_posix, FS_ROOT "/hello.txt", "%s", "hello from ESP");
|
||||
|
||||
// Setup wifi. This function is implemented in wifi.c
|
||||
// It blocks until connected to the configured WiFi network
|
||||
|
||||
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_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) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_http_match_uri(hm, "/upload")) {
|
||||
mg_http_upload(c, hm, "/tmp");
|
||||
mg_http_upload(c, hm, &mg_fs_posix, "/tmp");
|
||||
} else {
|
||||
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
|
||||
|
||||
CFLAGS ?= -W -Wall -DMG_ENABLE_LINES=1
|
||||
ifneq ($(MBEDTLS_DIR),)
|
||||
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS_DIR)/include -I/usr/include
|
||||
CFLAGS += -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedcrypto -lmbedx509
|
||||
@@ -12,7 +13,7 @@ all: $(PROG)
|
||||
$(DEBUGGER) ./$(PROG) $(ARGS)
|
||||
|
||||
$(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:
|
||||
rm -rf $(PROG) *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
|
||||
@@ -1,5 +1,5 @@
|
||||
PROG ?= example
|
||||
CFLAGS ?= -DMG_IO_SIZE=8192
|
||||
CFLAGS ?= -DMG_IO_SIZE=128
|
||||
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));
|
||||
}
|
||||
|
||||
static void forward_fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
struct mg_connection *c2 = fn_data;
|
||||
if (ev == MG_EV_READ) {
|
||||
if (c2 != NULL) {
|
||||
// All incoming data from the backend, forward to the client
|
||||
mg_send(c2, c->recv.buf, c->recv.len);
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
}
|
||||
} else if (ev == MG_EV_CONNECT) {
|
||||
if (mg_url_is_ssl(s_backend_url)) {
|
||||
struct mg_tls_opts opts = {.ca = "ca.pem"};
|
||||
mg_tls_init(c, &opts);
|
||||
}
|
||||
// All incoming data from the backend, forward to the client
|
||||
if (c2 != NULL) mg_send(c2, c->recv.buf, 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;
|
||||
}
|
||||
(void)ev_data;
|
||||
(void) ev_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;
|
||||
// Client request, create backend connection Note that we're passing
|
||||
// client connection `c` as fn_data for the created backend connection.
|
||||
c2 = mg_connect(c->mgr, s_backend_url, forward_fn, c);
|
||||
c->fn_data = c2;
|
||||
|
||||
if (c2 != NULL) {
|
||||
forward_request(hm, c2);
|
||||
c2 = mg_connect(c->mgr, s_backend_url, fn2, c);
|
||||
if (c2 == NULL) {
|
||||
mg_error(c, "Cannot create backend connection");
|
||||
} else {
|
||||
c->is_closing = 1;
|
||||
}
|
||||
} 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);
|
||||
if (mg_url_is_ssl(s_backend_url)) {
|
||||
struct mg_tls_opts opts = {.ca = "ca.pem"};
|
||||
mg_tls_init(c2, &opts);
|
||||
}
|
||||
c->fn_data = c2;
|
||||
forward_request(hm, c2);
|
||||
c2->is_hexdumping = 1;
|
||||
}
|
||||
} else if (ev == MG_EV_CLOSE) {
|
||||
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
|
||||
$(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: CFLAGS += -O2 -fsanitize=address,undefined,shift,null,return,bounds,alignment,object-size,bool,enum -static-libasan
|
||||
#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"
|
||||
|
||||
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_enable_hexdump = "no";
|
||||
static const char *s_ssi_pattern = "#.shtml";
|
||||
static const char *s_ssi_pattern = "#.html";
|
||||
|
||||
// Handle interrupts, like Ctrl-C
|
||||
static int s_signo;
|
||||
@@ -20,9 +20,18 @@ static void signal_handler(int signo) {
|
||||
// Simply serve static files from `s_root_dir`
|
||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_serve_opts opts = {.root_dir = s_root_dir,
|
||||
.ssi_pattern = s_ssi_pattern};
|
||||
mg_http_serve_dir(c, ev_data, &opts);
|
||||
struct mg_http_message *hm = ev_data, tmp = {};
|
||||
struct mg_str unknown = mg_str_n("?", 1), *cl;
|
||||
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;
|
||||
}
|
||||
@@ -32,16 +41,18 @@ static void usage(const char *prog) {
|
||||
"Mongoose v.%s\n"
|
||||
"Usage: %s OPTIONS\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"
|
||||
" -l ADDR - listening address, 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,
|
||||
s_root_dir, s_listening_address);
|
||||
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
|
||||
s_listening_address, s_debug_level);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char path[MG_PATH_MAX] = ".";
|
||||
const char *root_dir = ".";
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *c;
|
||||
int i;
|
||||
@@ -49,7 +60,7 @@ int main(int argc, char *argv[]) {
|
||||
// Parse command-line flags
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-d") == 0) {
|
||||
s_root_dir = argv[++i];
|
||||
root_dir = argv[++i];
|
||||
} else if (strcmp(argv[i], "-H") == 0) {
|
||||
s_enable_hexdump = argv[++i];
|
||||
} 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
|
||||
signal(SIGINT, 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;
|
||||
|
||||
// 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);
|
||||
mg_mgr_free(&mgr);
|
||||
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:
|
||||
// 1. Connects to the MQTT server specified by `s_url` variable
|
||||
// 2. When connected, subscribes to the topic `s_topic`
|
||||
// 3. Publishes message `hello` to the `s_topic`
|
||||
// 4. Receives that message back from the subscribed topic and exits
|
||||
// 2. When connected, subscribes to the topic `s_sub_topic`
|
||||
// 3. Publishes message `hello` to the `s_pub_topic`
|
||||
// 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:
|
||||
// make MBEDTLS_DIR=/path/to/your/mbedtls/installation
|
||||
@@ -13,10 +14,22 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
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) {
|
||||
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
|
||||
LOG(LL_ERROR, ("%p %s", c->fd, (char *) ev_data));
|
||||
} 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) {
|
||||
// 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));
|
||||
mg_mqtt_sub(c, &topic, 1);
|
||||
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) topic.len, topic.ptr));
|
||||
mg_mqtt_pub(c, &topic, &data, 1, false);
|
||||
mg_mqtt_sub(c, subt, s_qos);
|
||||
LOG(LL_INFO, ("SUBSCRIBED to %.*s", (int) subt.len, subt.ptr));
|
||||
|
||||
mg_mqtt_pub(c, pubt, data, s_qos, false);
|
||||
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) {
|
||||
// When we get echo response, print it
|
||||
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
|
||||
LOG(LL_INFO, ("RECEIVED %.*s <- %.*s", (int) mm->data.len, mm->data.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) {
|
||||
*(bool *) fn_data = true; // Signal that we're done
|
||||
}
|
||||
// Timer function - recreate client connection if it is closed
|
||||
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) {
|
||||
struct mg_mgr mgr; // Event manager
|
||||
struct mg_mqtt_opts opts; // MQTT connection options
|
||||
bool done = false; // Event handler flips it to true when done
|
||||
mg_mgr_init(&mgr); // Initialise event manager
|
||||
memset(&opts, 0, sizeof(opts)); // Set MQTT options
|
||||
opts.qos = 1; // Set QoS to 1
|
||||
opts.will_topic = mg_str(s_topic); // Set last will topic
|
||||
opts.will_message = mg_str("goodbye"); // And last will message
|
||||
mg_mqtt_connect(&mgr, s_url, &opts, fn, &done); // Create client connection
|
||||
while (done == false) mg_mgr_poll(&mgr, 1000); // Event loop
|
||||
mg_mgr_free(&mgr); // Finished, cleanup
|
||||
struct mg_mgr mgr;
|
||||
struct mg_timer timer;
|
||||
int topts = MG_TIMER_REPEAT | MG_TIMER_RUN_NOW;
|
||||
|
||||
signal(SIGINT, signal_handler); // Setup signal handlers - exist event
|
||||
signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
|
||||
|
||||
mg_mgr_init(&mgr); // Init event manager
|
||||
mg_timer_init(&timer, 3000, topts, timer_fn, &mgr); // Init timer
|
||||
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); // Event loop, 1s timeout
|
||||
mg_mgr_free(&mgr); // Finished, cleanup
|
||||
mg_timer_free(&timer); // Free timer resources
|
||||
|
||||
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) {
|
||||
// WS connection established. Perform MQTT login
|
||||
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_message = mg_str("goodbye")};
|
||||
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);
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
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");
|
||||
size_t len = c->send.len;
|
||||
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);
|
||||
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,
|
||||
(int) topic.len, topic.ptr));
|
||||
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;
|
||||
|
||||
// Handle interrupts, like Ctrl-C
|
||||
static int s_signo;
|
||||
static void signal_handler(int signo) {
|
||||
s_signo = signo;
|
||||
}
|
||||
|
||||
// Event handler function
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
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);
|
||||
LOG(LL_INFO,
|
||||
("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;
|
||||
}
|
||||
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,
|
||||
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
|
||||
for (struct sub *sub = s_subs; sub != NULL; sub = sub->next) {
|
||||
if (mg_strcmp(mm->topic, sub->topic) != 0) continue;
|
||||
mg_mqtt_pub(sub->c, &mm->topic, &mm->data, 1, false);
|
||||
if (mg_globmatch(sub->topic.ptr, sub->topic.len, mm->topic.ptr,
|
||||
mm->topic.len)) {
|
||||
mg_mqtt_pub(sub->c, &mm->topic, &mm->data, 1, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -88,10 +100,13 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr; // Event manager
|
||||
mg_mgr_init(&mgr); // Initialise event manager
|
||||
mg_mqtt_listen(&mgr, s_listen_on, fn, NULL); // Create MQTT listener
|
||||
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
||||
mg_mgr_free(&mgr);
|
||||
struct mg_mgr mgr; // Event manager
|
||||
signal(SIGINT, signal_handler); // Setup signal handlers - exist event
|
||||
signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
|
||||
mg_mgr_init(&mgr); // Initialise event manager
|
||||
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;
|
||||
}
|
||||
@@ -36,8 +36,8 @@ static void start_thread(void (*f)(void *), void *p) {
|
||||
|
||||
static void thread_function(void *param) {
|
||||
struct mg_connection *c = param; // Pipe connection
|
||||
mg_usleep(2 * 1000000); // Simulate long execution
|
||||
mg_mgr_wakeup(c); // Wakeup event manager
|
||||
sleep(2); // Simulate long execution
|
||||
mg_mgr_wakeup(c, "hi", 2); // Wakeup event manager
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (t = c->mgr->conns; t != NULL; t = t->next) {
|
||||
if (t->label[0] != 'W') continue; // Ignore un-marked connections
|
||||
mg_http_reply(t, 200, "Host: foo.com\r\n", "hi\n"); // Respond!
|
||||
t->label[0] = 0; // Clear mark
|
||||
mg_http_reply(t, 200, "Host: foo.com\r\n", "%.*s\n", c->recv.len,
|
||||
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:
|
||||
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
|
||||
cp ./../../../mongoose.[ch] $(NAME)/source/
|
||||
make -C $(NAME) build
|
||||
|
||||
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:
|
||||
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
|
||||
cp ./../../../mongoose.[ch] $(NAME)/source/
|
||||
make -C $(NAME) build
|
||||
|
||||
clean:
|
||||
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
|
||||
|
||||
build:
|
||||
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
|
||||
cp ./../../../mongoose.[ch] $(NAME)/source/
|
||||
make -C $(NAME) build
|
||||
|
||||
clean:
|
||||
@@ -2,6 +2,7 @@ NAME ?= $(notdir $(CURDIR))
|
||||
|
||||
build:
|
||||
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
|
||||
cp ./../../../mongoose.[ch] $(NAME)/source/
|
||||
make -C $(NAME) build
|
||||
|
||||
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}
|
||||
PROG = firmware
|
||||
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
|
||||
FREERTOS_KERNEL_PATH ?= $(PROJECT_ROOT_PATH)/test/freertos-kernel
|
||||
FREERTOS_PLUS_TCP_PATH ?= $(PROJECT_ROOT_PATH)/test/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
|
||||
|
||||
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
|
||||
|
||||
# FreeRTOS TCP stack sources
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_ARP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DHCP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DNS.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_IP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Sockets.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Stream_Buffer.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_ARP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DHCP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_DNS.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_IP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_Sockets.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_WIN.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_UDP_IP.c
|
||||
SOURCES += $(FREERTOS_PLUS_TCP_PATH)/FreeRTOS_TCP_WIN.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/BufferManagement/BufferAllocation_1.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)
|
||||
|
||||
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