Compare commits

..

95 Commits

Author SHA1 Message Date
eb12d05bf3 Merge pull request #773 from david-cermak/fix/mdns_add_version_macro
[mdns]: Bump 1.7.0 -> 1.8.0
2025-03-04 15:59:54 +01:00
520b8194fc fix(common): Fix clang-tidy runner to use supported versions 2025-03-04 15:29:38 +01:00
40142cdcfd bump(mdns): 1.7.0 -> 1.8.0
1.8.0
Features
- Add version keys (e01e67e7)
Bug Fixes
- Reformat mdns sources per indent-cont=120 (c7663cde)
2025-03-04 15:20:49 +01:00
c7663cdef2 fix(mdns): Reformat mdns sources per indent-cont=120 2025-02-27 10:42:03 +01:00
e01e67e7eb feat(mdns): Add version keys 2025-02-27 10:33:53 +01:00
37f84ee5a4 Merge pull request #768 from deodatomatheus/fix/handle-nullptr-dte-constructors
fix(modem): handle nullptr in DTE constructors to prevent invalid access (IDFGH-14688)
2025-02-27 09:19:16 +01:00
95b56600ed fix(modem): handle nullptr in DTE constructors to prevent invalid access 2025-02-26 11:37:02 -03:00
a22c3da49e Merge pull request #762 from david-cermak/fix/examples_target_tests
[examples]: Test the SLIP netif example on target as well
2025-02-20 14:32:54 +01:00
5d0fd5c1c5 Merge pull request #770 from david-cermak/fix/lws_license
[lws]: Adds missing license info
2025-02-20 14:17:47 +01:00
7ea6879a19 fix(lws): Adds missing license info 2025-02-20 12:34:40 +01:00
2db11bbb8c fix(examples): Fix SLIP netif example to make esp_netif use special netif
by enabling PPP netif, so esp_netif knows that lwip's netif->state
will be reserved for special netif info (slip)

Closes https://github.com/espressif/esp-protocols/issues/759
2025-02-20 11:58:54 +01:00
cdb7bfd188 fix(examples): Test the SLIP netif example on target as well 2025-02-20 11:58:49 +01:00
c679ceed85 Merge pull request #718 from glmfe/feat/lws
[lws]: Add initial support for libwebsockets
2025-02-19 11:26:03 -03:00
c009892968 bump(lws): First version 4.3.3
4.3.3
Features
- Add initial support libwebsockets component (ef3443d)
2025-02-19 10:21:35 -03:00
5f66f35f5f feat(lws): Add initial support libwebsockets component 2025-02-19 10:16:42 -03:00
dea5f1c431 Merge pull request #764 from david-cermak/feat/mdns_malloc_caps
[mdns]: Allow allocate memory with configured caps
2025-02-18 13:23:41 +01:00
384d1c23ba bump(mdns): 1.6.0 -> 1.7.0
1.7.0
Features
- Support user defined allocators (88162d1f)
- Allow allocate memory with configured caps (7d29b476)
Bug Fixes
- Adjust some formatting per indent-cont=120 (5b2077e3)
2025-02-18 12:53:46 +01:00
88162d1f3a feat(mdns): Support user defined allocators
Defines mem-alloc function optionally weak, so users can override them
and implement their own allocation, or a static/bss memory for the mdns
task stack.
2025-02-18 12:52:21 +01:00
e65f22ab6c ci(mdns): Check mdns sources for std alloc functions 2025-02-13 15:02:18 +01:00
7d29b47676 feat(mdns): Allow allocate memory with configured caps 2025-02-13 13:51:22 +01:00
f1a72ec42c Merge pull request #760 from glmfe/feat/add-ws-unregister
feat(websocket): add unregister event to websocket client
2025-02-12 18:50:52 +01:00
ce160505dc feat(websocket): add unregister event to websocket client 2025-02-12 08:46:39 -03:00
87e96b4682 Merge pull request #761 from david-cermak/fix/ci_astyle
[ci]: Fix pre-commit hook to call astyle correctly
2025-02-12 11:47:40 +01:00
5b2077e373 fix(common): Adjust some formatting per indent-cont=120
As we updated astyle configuration to be in line with IDF style
2025-02-12 11:40:21 +01:00
b45fe143a4 ci(common): Fix pre-commit hook to call astyle correctly 2025-02-11 17:16:11 +01:00
ee2fbbbee7 Merge pull request #756 from david-cermak/feat/mdns_alloc_task
[mdns]: support allocating mDNS task with caps
2025-02-10 11:50:59 +01:00
cb061c9c38 bump(mdns): 1.5.3 -> 1.6.0
1.6.0
Features
- support allocating mDNS task from SPIRAM (8fcad10c)
Bug Fixes
- Use correct task delete function (eb4ab524)
Updated
- ci(mdns): Fix mdns host test layers with static task creation (0690eba3)
2025-02-10 11:18:55 +01:00
0690eba3a8 ci(mdns): Fix mdns host test layers with static task creation 2025-02-10 11:18:19 +01:00
eb4ab52487 fix(mdns): Use correct task delete function 2025-02-10 11:18:19 +01:00
zwx
8fcad10ccf feat(mdns): support allocating mDNS task from SPIRAM 2025-02-10 11:18:19 +01:00
936e43f9d8 Merge pull request #758 from david-cermak/fix/mdns_ignore_only_invalid_queries
[mdns]: Fix the responder to ignore only invalid queries
2025-02-07 15:11:14 +01:00
64d818b2d3 bump(mdns): 1.5.2 -> 1.5.3
1.5.3
Bug Fixes
- Fix responder to ignore only invalid queries (cd07228f, #754)
2025-02-07 14:31:18 +01:00
cd07228f81 fix(mdns): Fix responder to ignore only invalid queries
not the entire packet, so we can still reply to next questions

Closes https://github.com/espressif/esp-protocols/issues/754
2025-02-07 14:30:10 +01:00
1c6580e22b Merge pull request #753 from david-cermak/fix/ci_mosq
[mosquitto]: Fix minor CI issue
2025-02-03 14:54:07 +01:00
87f835af0f fix(mosq): Run IDF build test only on master or labeled PRs 2025-01-31 11:19:00 +01:00
85a8dac42d Merge pull request #750 from david-cermak/bump/ws_client
[websocket]:  Bump 1.3.0 -> 1.4.0
2025-01-30 08:48:45 +01:00
39866116f5 bump(websocket): 1.3.0 -> 1.4.0
1.4.0
Features
- Support DS peripheral for mutual TLS (55385ec3)
Bug Fixes
- wait for task on destroy (42674b49)
- Fix pytest to verify client correctly (9046af8f)
- propagate error type (eeeb9006)
- fix example buffer leak (5219c39d)
Updated
- chore(websocket): align structure members (beb6e57e)
- chore(websocket): remove unused client variable (15d3a01e)
2025-01-30 07:16:24 +01:00
7740b591b6 Merge pull request #749 from david-cermak/fix/ws_client_test
[websocket]: Fix pytest to verify client correctly
2025-01-30 07:09:34 +01:00
b57979d967 Merge pull request #751 from johanstokking/fix/await-task-on-destroy
fix(websocket): wait for task on destroy (IDFGH-14533)
2025-01-29 15:27:38 +01:00
42674b49f9 fix(websocket): wait for task on destroy 2025-01-29 14:30:32 +01:00
e069ae7762 Merge pull request #520 from johanstokking/feature/websocket/client_ds_data
feat(websocket): Support DS peripheral for mutual TLS (IDFGH-12285)
2025-01-28 16:59:34 +01:00
44d476fc50 docs(websocket): fix minor readability issues 2025-01-28 14:59:24 +01:00
55385ec312 feat(websocket): Support DS peripheral for mutual TLS 2025-01-28 14:58:54 +01:00
a3c2bbed9e Merge pull request #748 from david-cermak/fix/mdns_coverity
[mdns]: Bump v1.5.2
2025-01-28 10:23:24 +01:00
84c47c37f1 bump(mdns): 1.5.1 -> 1.5.2
1.5.2
Bug Fixes
- Fix potential NULL deref when sending sub-buy (e7273c46)
- Fix _mdns_append_fqdn excessive stack usage (bd23c233)
2025-01-28 09:54:54 +01:00
e7273c46ec fix(mdns): Fix potential NULL deref when sending sub-buy
Closes coverity reported issue: 473829 Dereference null return value
2025-01-28 09:54:16 +01:00
2e7d240abd Merge pull request #744 from andrew-lifx/fix/mdns_append_fqdn
fix(mdns): Fix _mdns_append_fqdn excessive stack usage (IDFGH-14506)
2025-01-28 07:28:00 +01:00
d4a004b5b4 Merge pull request #694 from johanstokking/fix/websocket-esp-tls-errors
fix(websocket): propagate error type (IDFGH-14518)
2025-01-27 15:50:32 +01:00
9046af8f8d fix(websocket): Fix pytest to verify client correctly 2025-01-27 15:34:49 +01:00
eeeb9006eb fix(websocket): propagate error type 2025-01-25 21:52:06 +01:00
b167aa315f Merge pull request #747 from david-cermak/fix/publish_asio_1.32
bump(asio): Publish 1.32.0 to component manager
2025-01-24 15:53:54 +01:00
72ba24470d bump(asio): Publish 1.32.0 to component manager
Related to the bump-commit: ac6a388cdd
but missed to update idf_component.yml
This publishes ASIO-1.32-0~0
2025-01-24 14:32:20 +01:00
7dc87d28b2 Merge pull request #717 from david-cermak/feat/asio_sync_upstream
[asio]: Drop esp-asio patches in favor of sock-utils
2025-01-24 13:49:53 +01:00
ac6a388cdd bump(asio): 1.28.0 -> 1.32.0
1.32.0
Features
- Upgrade asio to 1.32 (9bdd429c)
- Drop esp/asio patches in favor of sock-utils (27435b7f)
Bug Fixes
- Fix chat example to print only the message body (76aaea08)
- Make asio enable if_nametoindex to fix linking (5db32cce)
- Re-applie refs to common comps idf_component.yml (9fe44a45)
- Reference common component from IDF (74fc228c)
- Revert referencing protocol_examples_common from IDF (f9e0281a)
- reference protocol_examples_common from IDF (09abb18b)
- specify override_path in example manifest files (1d8923cf)
Updated
- docs(asio): Updates asio docs (ce9337d3)
2025-01-24 12:40:45 +01:00
76aaea08d2 fix(asio): Fix chat example to print only the message body 2025-01-24 12:37:39 +01:00
5db32cce30 fix(asio): Make asio enable if_nametoindex to fix linking 2025-01-24 12:37:39 +01:00
9bdd429c7c feat(asio): Upgrade asio to 1.32 2025-01-24 10:47:54 +01:00
6f7c52cc3f fix(asio): Add tests for IDF-v5.4 2025-01-24 10:47:54 +01:00
27435b7f34 feat(asio): Drop esp/asio patches in favor of sock-utils 2025-01-24 10:47:54 +01:00
813331f003 Merge pull request #743 from david-cermak/fix/modem_ci
[modem]: Fix deprecated CI download action
2025-01-24 08:27:37 +01:00
bd23c233a4 fix(mdns): Fix _mdns_append_fqdn excessive stack usage
Move "mdns_name_t name" declaration inside loop to move it out of scope and reclain stack space before recursion call.
2025-01-24 17:39:04 +10:30
4eda7d472f fix(modem): Fix deprecated download action 2025-01-23 19:15:28 +01:00
163029c0b6 Merge pull request #742 from david-cermak/bump/mdns_1.5.1
[mdns]: Bump 1.5.0 -> 1.5.1
2025-01-23 15:32:49 +01:00
96eae25096 bump(mdns): 1.5.0 -> 1.5.1
1.5.1
Bug Fixes
- Fix incorrect memory free for mdns browse (4451a8c5)
2025-01-23 09:00:01 +01:00
ebec8eff63 Merge pull request #740 from gytxxsy/fix/fix_wrong_mem_free_of_mdns_browse
[mdns]: fix incorrect memory free for mdns browse
2025-01-23 08:58:05 +01:00
4451a8c5ad fix(mdns): Fix incorrect memory free for mdns browse 2025-01-23 09:08:25 +08:00
6d19aabb02 Merge pull request #721 from david-cermak/feat/mosq_target_tests
[mosq]: Add IDF MQTT stress tests to mosquitto CI
2025-01-21 12:55:49 +01:00
9162de1150 fix(mosq): Remove temp modification of IDF files 2025-01-21 12:27:09 +01:00
dbd164dd91 fix(mosq): Add a note about stack size 2025-01-21 12:27:09 +01:00
90d663ad01 feat(mosq): Add IDF MQTT stress tests to mosquitto CI 2025-01-21 12:27:09 +01:00
a83f1b6787 Merge pull request #736 from david-cermak/bump/mdns_1.4.4
[mdns]: Bump 1.4.3 -> 1.5.0
2025-01-21 10:56:48 +01:00
84caca465d bump(mdns): 1.4.3 -> 1.5.0
1.5.0
Features
- supported removal of subtype when updating service (4ad88e29)
Bug Fixes
- Fix zero-sized VLA clang-tidy warnings (196198ec)
- Remove dead store to arg variable shared (e838bf03)
- Fix name mangling not to use strcpy() (99b54ac3)
- Fix potential null derefernce in _mdns_execute_action() (f5be2f41)
- Fix AFL test mock per espressif/esp-idf@a5bc08fb55 (3d8835cf)
- Fixed potential out-of-bound interface error (24f55ce9)
- Fixed incorrect error conversion (8f8516cc)
- Fixed potential overflow when allocating txt data (75a8e864)
- Move MDNS_NAME_BUF_LEN to public headers (907087c0, #724)
- Cleanup includes in mdns.c (68a9e148, #725)
- Allow advertizing service with port==0 (827ea65f)
- Fixed complier warning if MDNS_MAX_SERVICES==0 (95377216, #611)
2025-01-20 17:57:07 +01:00
8f81478fff Merge pull request #735 from david-cermak/fix/docs_build
[common]: Fix esp-docs dependencies
2025-01-20 14:50:32 +01:00
ae5a8ceeda fix(mosq): Run mosquitto version check only on labeled job or push 2025-01-20 12:56:02 +01:00
774bab22ea fix(common): Update esp-docs dependencies to fix docs-build job 2025-01-20 12:52:52 +01:00
265e38d684 Merge pull request #731 from tanyanquan/feat/update_subtype_removal
feat(mdns): supported removal of subtype when updating service (IDFGH-14413)
2025-01-20 09:48:27 +01:00
93f772171c Merge pull request #732 from david-cermak/fix/mdns_clangtiy_warns
[mdns]: Fixes clang-tidy warnings
2025-01-20 09:47:56 +01:00
4ad88e297f feat(mdns): supported removal of subtype when updating service 2025-01-16 10:56:54 +08:00
196198ecc9 fix(mdns): Fix zero-sized VLA clang-tidy warnings
and some minor leaks in creation of browse results
2025-01-15 14:23:34 +01:00
e838bf03f4 fix(mdns): Remove dead store to arg variable shared
Fixing: warning: Value stored to 'shared' is never read [clang-analyzer-deadcode.DeadStores]
2025-01-15 14:23:34 +01:00
99b54ac384 fix(mdns): Fix name mangling not to use strcpy()
Since it was flagged by clang-tidy as insecture API
2025-01-15 14:23:34 +01:00
f5be2f4115 fix(mdns): Fix potential null derefernce in _mdns_execute_action()
We did check for null-deref before checking 'a->type', but contol
continues and passes potential null-ptr to the processing function
_mdns_execute_action()
Fixed by asserting action != NULL, since it is an invalid state which
should never happen.
2025-01-15 14:21:59 +01:00
9b74256b51 Merge pull request #730 from david-cermak/fix/mdns_minor
[mdns]: Fixed some minor bugs
2025-01-15 12:27:05 +01:00
3d8835cfb9 fix(mdns): Fix AFL test mock per espressif/esp-idf@a5bc08fb55 2025-01-15 11:42:34 +01:00
24f55ce9b4 fix(mdns): Fixed potential out-of-bound interface error
invalid mdns_if was handled for enabling/announcing pcbs,
but not for the consequent browsing

Closes coverity isssue: 470162 Out-of-bounds access
2025-01-15 10:46:15 +01:00
8f8516cc3f fix(mdns): Fixed incorrect error conversion
Mixing esp_err_t (int) with err_t (uint8_t) from lwip.

Closes coverity isssue: 470139 Overflowed return value
2025-01-15 10:46:15 +01:00
75a8e8640a fix(mdns): Fixed potential overflow when allocating txt data
Closes coverity warning: 470092 Overflowed integer argument
2025-01-15 10:46:15 +01:00
907087c09b fix(mdns): Move MDNS_NAME_BUF_LEN to public headers
Since it's used by public API as maximum length of user buffer

Closes https://github.com/espressif/esp-protocols/issues/724
2025-01-15 10:46:10 +01:00
68a9e14898 fix(mdns): Cleanup includes in mdns.c
Closes https://github.com/espressif/esp-protocols/issues/725
2025-01-15 10:44:12 +01:00
827ea65fd5 fix(mdns): Allow advertizing service with port==0
Closes https://github.com/espressif/esp-idf/issues/14335
2025-01-15 10:44:12 +01:00
9537721600 fix(mdns): Fixed complier warning if MDNS_MAX_SERVICES==0
Closes https://github.com/espressif/esp-protocols/issues/611
2025-01-15 10:44:07 +01:00
4394f845fc Merge pull request #728 from david-cermak/bump/mdns_1.4.3
[mdns]: bump: 1.4.2 -> 1.4.3
2025-01-08 10:24:01 +01:00
9b0ba6060f bump(mdns): 1.4.2 -> 1.4.3
1.4.3
Features
- support zero item when update subtype (5bd82c01)
2025-01-08 09:42:38 +01:00
7c6a3098af Merge pull request #726 from zwx1995esp/feature/support_update_zero_item_of_subtype_for_mdns
feat(mdns): support zero item when update subtype (IDFGH-14369)
2025-01-07 18:40:25 +01:00
zwx
5bd82c01a5 feat(mdns): support zero item when update subtype 2025-01-07 10:28:02 +08:00
89 changed files with 3188 additions and 721 deletions

View File

@ -13,7 +13,7 @@ jobs:
name: Build
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_target: ["esp32", "esp32s2"]
example: ["asio_chat", "async_request", "socks4", "ssl_client_server", "tcp_echo_server", "udp_echo_server"]
runs-on: ubuntu-22.04
@ -64,7 +64,7 @@ jobs:
name: Target tests
strategy:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
idf_target: ["esp32"]
example: ["asio_chat", "tcp_echo_server", "udp_echo_server", "ssl_client_server"]
needs: build_asio

View File

@ -9,10 +9,10 @@ on:
jobs:
build:
name: Run clang-tidy
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
container: espressif/idf:latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: 'true'
- name: Install esp-clang
@ -50,7 +50,7 @@ jobs:
results.sarif
results.sarif.raw
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
category: clang-tidy

View File

@ -13,12 +13,15 @@ jobs:
name: Build examples
strategy:
matrix:
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3"]
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
include:
- idf_ver: "latest"
warning: "Warning: The smallest app partition is nearly full"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TARGET_TEST: examples/esp_netif/slip_custom_netif/
TARGET_TEST_DIR: build_esp32c3_target
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
@ -31,6 +34,16 @@ jobs:
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py examples -m examples/.build-test-rules.yml -d -c
# Build target tests
python ./ci/build_apps.py ${TARGET_TEST} -r sdkconfig.ci=target
cd ${TARGET_TEST}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
zip -qur artifacts.zip ${TARGET_TEST_DIR}
- uses: actions/upload-artifact@v4
with:
name: slip_target_${{ matrix.idf_ver }}
path: ${{ env.TARGET_TEST }}/artifacts.zip
if-no-files-found: error
build_and_run_on_host:
if: contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push'
@ -51,3 +64,38 @@ jobs:
python ./ci/build_apps.py examples/mqtt -l -t linux
timeout 5 ./examples/mqtt/build_linux_default/esp_mqtt_demo.elf | tee test.log || true
grep 'MQTT_EVENT_DATA' test.log
run_on_target:
# Skip running on forks since it won't have access to secrets
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'examples') || github.event_name == 'push' )
name: Slip example target test
needs: build_all_examples
strategy:
matrix:
idf_ver: ["release-v5.4", "latest"]
runs-on:
- self-hosted
- modem
env:
TARGET_TEST: examples/esp_netif/slip_custom_netif/
TARGET_TEST_DIR: build_esp32c3_target
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: slip_target_${{ matrix.idf_ver }}
path: ${{ env.TARGET_TEST }}/ci/
- name: Run Test
working-directory: ${{ env.TARGET_TEST }}
run: |
python -m venv .venv
source .venv/bin/activate
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool netifaces
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
python -m pytest --log-cli-level DEBUG --target=esp32c3
done

86
.github/workflows/lws_build.yml vendored Normal file
View File

@ -0,0 +1,86 @@
name: "lws: build-tests"
on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, labeled]
jobs:
build_lws:
if: contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push'
name: Libwebsockets build
strategy:
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
test: [ { app: example, path: "examples/client" }]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps
python ./ci/build_apps.py ${TEST_DIR}
cd ${TEST_DIR}
for dir in `ls -d build_esp32_*`; do
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
zip -qur artifacts.zip $dir
done
- uses: actions/upload-artifact@v4
with:
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/artifacts.zip
if-no-files-found: error
run-target-lws:
# Skip running on forks since it won't have access to secrets
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'lws') || github.event_name == 'push' )
name: Target test
needs: build_lws
strategy:
fail-fast: false
matrix:
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
idf_target: ["esp32"]
test: [ { app: example, path: "examples/client" }]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: components/libwebsockets/${{ matrix.test.path }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
run: |
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
run: |
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
done
- uses: actions/upload-artifact@v4
if: always()
with:
name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
path: components/libwebsockets/${{ matrix.test.path }}/*.xml

View File

@ -35,30 +35,36 @@ jobs:
# Next we run the pytest (using the console app)
pytest
build_afl_host_test_mdns:
host_compat_checks:
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
name: Build AFL host test
name: Set of compatibility checks
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
with:
path: esp-protocols
- name: Install Necessary Libs
run: |
apt-get update -y
apt-get install -y libbsd-dev
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
env:
IDF_TARGET: ${{ matrix.idf_target }}
- name: Test AFL compat build
shell: bash
run: |
. ${IDF_PATH}/export.sh
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_afl_fuzz_host/
cd components/mdns/tests/test_afl_fuzz_host/
make INSTR=off
- name: Test no malloc functions
shell: bash
run: |
cd components/mdns
for file in $(ls *.c); do
cp $file /tmp
echo -n "Checking that $file does not call any std allocations directly..."
python mem_prefix_script.py $file
diff -q $file /tmp/$file || exit 1
echo "OK"
done

View File

@ -42,7 +42,7 @@ jobs:
idf.py set-target ${{ matrix.idf_target }}
idf.py build
$GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build
@ -75,7 +75,7 @@ jobs:
run: |
sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/build

View File

@ -75,6 +75,7 @@ jobs:
done
check_consistency:
if: contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push'
name: Checks that API docs and versions are consistent
runs-on: ubuntu-latest
steps:
@ -100,3 +101,80 @@ jobs:
exit 1
fi
echo "Versions are consistent: $CONFIG_VERSION"
build_idf_tests_with_mosq:
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push' )
name: Build IDF tests
strategy:
matrix:
idf_ver: ["latest"]
idf_target: ["esp32"]
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
runs-on: ubuntu-20.04
container: espressif/idf:${{ matrix.idf_ver }}
env:
TARGET_TEST_DIR: build_esp32_local_broker
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
shell: bash
run: |
. ${IDF_PATH}/export.sh
pip install idf-component-manager idf-build-apps --upgrade
export MOSQUITTO_PATH=`pwd`/components/mosquitto
# to use the actual version of mosquitto
sed -i '/espressif\/mosquitto:/a \ \ \ \ override_path: "${MOSQUITTO_PATH}"' ${IDF_PATH}/${{matrix.test.path}}/main/idf_component.yml
export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function"
export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes"
export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}"
cd ${IDF_PATH}/${{matrix.test.path}}
idf-build-apps find --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
idf-build-apps build --config sdkconfig.ci.local_broker -vv --target ${{ matrix.idf_target }} --build-dir=${TARGET_TEST_DIR}
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
# to replace mqtt test configs with specific mosquitto markers
python ${MOSQUITTO_PATH}/test/replace_decorators.py pytest_mqtt_publish_app.py ${TARGET_TEST_DIR}/pytest_mosquitto.py
zip -qur ${GITHUB_WORKSPACE}/artifacts.zip ${TARGET_TEST_DIR}
- uses: actions/upload-artifact@v4
with:
name: mosq_publish_esp32_${{ matrix.idf_ver }}
path: artifacts.zip
if-no-files-found: error
test_idf_ci_with_mosq:
# Skip running on forks since it won't have access to secrets
if: |
github.repository == 'espressif/esp-protocols' &&
( contains(github.event.pull_request.labels.*.name, 'mosquitto') || github.event_name == 'push' )
name: Mosquitto IDF target tests
needs: build_idf_tests_with_mosq
strategy:
matrix:
idf_ver: ["latest"]
runs-on:
- self-hosted
- ESP32-ETHERNET-KIT
env:
TEST_DIR: examples
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: mosq_publish_esp32_${{ matrix.idf_ver }}
path: ${{ env.TEST_DIR }}/ci/
- name: Run Test
working-directory: ${{ env.TEST_DIR }}
run: |
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2"
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults
mv $dir build
mv build/*.py .
# Run only "test_mosquitto" marked tests
python -m pytest --log-cli-level DEBUG --junit-xml=./results_esp32_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=esp32 -m test_mosquitto
done

View File

@ -102,5 +102,6 @@ jobs:
components/mbedtls_cxx;
components/mosquitto;
components/sock_utils;
components/libwebsockets;
namespace: "espressif"
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

5
.gitmodules vendored
View File

@ -1,6 +1,9 @@
[submodule "components/asio/asio"]
path = components/asio/asio
url = https://github.com/espressif/asio
url = https://github.com/chriskohlhoff/asio
[submodule "components/mosquitto/mosquitto"]
path = components/mosquitto/mosquitto
url = https://github.com/eclipse/mosquitto
[submodule "components/libwebsockets/libwebsockets"]
path = components/libwebsockets/libwebsockets
url = https://github.com/warmcat/libwebsockets.git

View File

@ -51,7 +51,7 @@ repos:
rev: v1.0.5
hooks:
- id: astyle_py
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-pointer=name', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--exclude-list=ci/ignore_astyle.txt']
args: ['--style=otbs', '--attach-namespaces', '--attach-classes', '--indent=spaces=4', '--convert-tabs', '--align-reference=name', '--keep-one-line-statements', '--pad-header', '--pad-oper', '--unpad-paren', '--max-continuation-indent=120', '--exclude-list=ci/ignore_astyle.txt']
- repo: https://github.com/commitizen-tools/commitizen
rev: v2.42.1
hooks:
@ -61,8 +61,8 @@ repos:
- repo: local
hooks:
- id: commit message scopes
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls"
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls)\)\:)'
name: "commit message must be scoped with: mdns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws"
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws)\)\:)'
language: pygrep
args: [--multiline]
stages: [commit-msg]

View File

@ -66,3 +66,7 @@ Please refer to instructions in [ESP-IDF](https://github.com/espressif/esp-idf)
### Socket helpers (sock-utils)
* Brief introduction [README](components/sock_utils/README.md)
### libwebsockets
* Brief introduction [README](components/libwebsockets/README.md)

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -64,7 +64,7 @@ static struct generic_queue_handle *create_generic_queue(queue_type_t type, uint
return h;
}
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize )
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize)
{
return (QueueHandle_t)create_generic_queue(QUEUE, uxQueueLength, uxItemSize);
}
@ -75,7 +75,7 @@ uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t
return osal_queue_send(h->q, (uint8_t *)pvItemToQueue, h->item_size) ? pdTRUE : pdFAIL;
}
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait )
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait)
{
return xQueueSend(xQueue, pvItemToQueue, xTicksToWait);
}
@ -86,7 +86,7 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
return osal_queue_recv(h->q, (uint8_t *)pvBuffer, h->item_size, xTicksToWait) ? pdTRUE : pdFAIL;
}
BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
BaseType_t xSemaphoreGive(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
@ -96,7 +96,7 @@ BaseType_t xSemaphoreGive( QueueHandle_t xQueue)
return xQueueSend(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
@ -106,7 +106,7 @@ BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue)
return pdFALSE;
}
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX) {
@ -116,7 +116,7 @@ BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask )
return xQueueReceive(xQueue, &s_semaphore_data, portMAX_DELAY);
}
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask)
{
struct generic_queue_handle *h = xQueue;
if (h->type == MUTEX_REC) {
@ -126,7 +126,7 @@ BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask )
return pdFALSE;
}
void vQueueDelete( QueueHandle_t xQueue )
void vQueueDelete(QueueHandle_t xQueue)
{
struct generic_queue_handle *h = xQueue;
if (h->q) {
@ -176,14 +176,14 @@ void vTaskSuspend(void *task)
vTaskDelete(task);
}
TickType_t xTaskGetTickCount( void )
TickType_t xTaskGetTickCount(void)
{
struct timespec spec;
clock_gettime(CLOCK_REALTIME, &spec);
return spec.tv_nsec / 1000000 + spec.tv_sec * 1000;
}
void vTaskDelay( const TickType_t xTicksToDelay )
void vTaskDelay(const TickType_t xTicksToDelay)
{
usleep(xTicksToDelay * 1000);
}
@ -212,13 +212,27 @@ void *pthread_task(void *params)
return NULL;
}
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StaticTask_t *const pxTaskBuffer,
const BaseType_t xCoreID)
{
static TaskHandle_t pvCreatedTask;
xTaskCreate(pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &pvCreatedTask);
return pvCreatedTask;
}
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID)
{
xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask);
return pdTRUE;
@ -266,7 +280,7 @@ void xTaskNotifyGive(TaskHandle_t task)
}
}
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time )
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time)
{
return true;
}
@ -276,32 +290,32 @@ TaskHandle_t xTaskGetCurrentTaskHandle(void)
return (TaskHandle_t)pthread_self();
}
EventGroupHandle_t xEventGroupCreate( void )
EventGroupHandle_t xEventGroupCreate(void)
{
return osal_signal_create();
}
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
void vEventGroupDelete(EventGroupHandle_t xEventGroup)
{
osal_signal_delete(xEventGroup);
}
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear)
{
return osal_signal_clear(xEventGroup, uxBitsToClear);
}
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup)
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup)
{
return osal_signal_get(xEventGroup);
}
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet)
{
return osal_signal_set(xEventGroup, uxBitsToSet);
}
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait)
{
return osal_signal_wait(xEventGroup, uxBitsToWaitFor, xWaitForAllBits, xTicksToWait);
}

View File

@ -1,11 +1,12 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "esp_heap_caps.h"
#ifdef __cplusplus
extern "C" {
@ -15,7 +16,10 @@ extern "C" {
#define TaskHandle_t TaskHandle_t
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
void vTaskDelay( const TickType_t xTicksToDelay );
typedef void *StackType_t;
typedef void *StaticTask_t;
void vTaskDelay(const TickType_t xTicksToDelay);
void xTaskNotifyGive(TaskHandle_t task);
@ -23,39 +27,48 @@ void ulTaskNotifyTake(bool stuff, uint32_t timeout);
TaskHandle_t xTaskGetCurrentTaskHandle(void);
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time );
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID);
TaskHandle_t xTaskCreateStaticPinnedToCore(TaskFunction_t pxTaskCode,
const char *const pcName,
const uint32_t ulStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
StackType_t *const puxStackBuffer,
StaticTask_t *const pxTaskBuffer,
const BaseType_t xCoreID);
BaseType_t xTaskCreatePinnedToCore(TaskFunction_t pvTaskCode,
const char *const pcName,
const uint32_t usStackDepth,
void *const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *const pvCreatedTask,
const BaseType_t xCoreID);
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, const uint32_t usStackDepth, void *const pvParameters, UBaseType_t uxPriority, TaskHandle_t *const pvCreatedTask);
TickType_t xTaskGetTickCount( void );
TickType_t xTaskGetTickCount(void);
void vQueueDelete( QueueHandle_t xQueue );
void vQueueDelete(QueueHandle_t xQueue);
QueueHandle_t xSemaphoreCreateBinary(void);
QueueHandle_t xSemaphoreCreateMutex(void);
QueueHandle_t xSemaphoreCreateRecursiveMutex(void);
BaseType_t xSemaphoreGive( QueueHandle_t xQueue);
BaseType_t xSemaphoreGive(QueueHandle_t xQueue);
BaseType_t xSemaphoreTake( QueueHandle_t xQueue, TickType_t pvTask );
BaseType_t xSemaphoreTake(QueueHandle_t xQueue, TickType_t pvTask);
BaseType_t xSemaphoreGiveRecursive( QueueHandle_t xQueue);
BaseType_t xSemaphoreGiveRecursive(QueueHandle_t xQueue);
BaseType_t xSemaphoreTakeRecursive( QueueHandle_t xQueue, TickType_t pvTask );
BaseType_t xSemaphoreTakeRecursive(QueueHandle_t xQueue, TickType_t pvTask);
void vTaskDelete(TaskHandle_t *task);
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
uint32_t uxItemSize );
QueueHandle_t xQueueCreate(uint32_t uxQueueLength,
uint32_t uxItemSize);
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
@ -63,23 +76,26 @@ uint32_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksTo
void vTaskSuspend(void *task);
EventGroupHandle_t xEventGroupCreate( void );
void vEventGroupDelete( EventGroupHandle_t xEventGroup );
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );
EventGroupHandle_t xEventGroupCreate(void);
void vEventGroupDelete(EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear);
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait );
EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait);
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupGetBits(EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet );
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet);
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );
uint32_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
void *heap_caps_malloc(size_t size, uint32_t caps);
void heap_caps_free(void *ptr);
#ifdef __cplusplus
}

View File

@ -1,7 +1,8 @@
---
commitizen:
bump_message: 'bump(asio): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py asio
tag_format: asio-v$version
version: 1.28.0~0
version: 1.32.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,26 @@
# Changelog
## [1.32.0](https://github.com/espressif/esp-protocols/commits/asio-v1.32.0)
### Features
- Upgrade asio to 1.32 ([9bdd429c](https://github.com/espressif/esp-protocols/commit/9bdd429c))
- Drop esp/asio patches in favor of sock-utils ([27435b7f](https://github.com/espressif/esp-protocols/commit/27435b7f))
### Bug Fixes
- Fix chat example to print only the message body ([76aaea08](https://github.com/espressif/esp-protocols/commit/76aaea08))
- Make asio enable if_nametoindex to fix linking ([5db32cce](https://github.com/espressif/esp-protocols/commit/5db32cce))
- Re-applie refs to common comps idf_component.yml ([9fe44a45](https://github.com/espressif/esp-protocols/commit/9fe44a45))
- Reference common component from IDF ([74fc228c](https://github.com/espressif/esp-protocols/commit/74fc228c))
- Revert referencing protocol_examples_common from IDF ([f9e0281a](https://github.com/espressif/esp-protocols/commit/f9e0281a))
- reference protocol_examples_common from IDF ([09abb18b](https://github.com/espressif/esp-protocols/commit/09abb18b))
- specify override_path in example manifest files ([1d8923cf](https://github.com/espressif/esp-protocols/commit/1d8923cf))
### Updated
- docs(asio): Updates asio docs ([ce9337d3](https://github.com/espressif/esp-protocols/commit/ce9337d3))
## [1.28.2~0](https://github.com/espressif/esp-protocols/commits/asio-1.28.2_0)
### Bug Fixes

View File

@ -6,8 +6,8 @@ if(NOT CONFIG_LWIP_IPV6 AND NOT CMAKE_BUILD_EARLY_EXPANSION)
return()
endif()
set(asio_sources "asio/asio/src/asio.cpp")
set(asio_requires lwip)
set(asio_sources "asio/asio/src/asio.cpp" "port/src/asio_stub.cpp")
set(asio_requires lwip sock_utils)
if(CONFIG_ASIO_SSL_SUPPORT)
list(APPEND asio_sources
@ -18,7 +18,7 @@ if(CONFIG_ASIO_SSL_SUPPORT)
endif()
idf_component_register(SRCS ${asio_sources}
INCLUDE_DIRS "asio/asio/include" "port/include"
INCLUDE_DIRS "port/include" "asio/asio/include"
PRIV_INCLUDE_DIRS ${asio_priv_includes}
PRIV_REQUIRES ${asio_requires})
@ -30,6 +30,7 @@ target_compile_definitions(${COMPONENT_LIB} PUBLIC SA_RESTART=0x01
ASIO_STANDALONE
ASIO_HAS_PTHREADS
OPENSSL_NO_ENGINE
ASIO_DETAIL_IMPL_POSIX_EVENT_IPP # this replaces asio's posix_event constructor
)
if(NOT CONFIG_COMPILER_CXX_EXCEPTIONS)

View File

@ -1,6 +1,15 @@
menu "ESP-ASIO"
visible if LWIP_IPV6
config ASIO_IS_ENABLED
# Invisible option that is enabled if ASIO is added to the IDF components.
# This is used to "select" LWIP_NETIF_API option
# which enables if_indextoname() and if_nametoindex() functions
# (these are optionally used in asio)
bool
default "y"
select LWIP_NETIF_API
config ASIO_SSL_SUPPORT
bool "Enable SSL/TLS support of ASIO"
default n

View File

@ -120,7 +120,7 @@ private:
asio::buffer(read_msg_.body(), read_msg_.body_length()),
[this, self](std::error_code ec, std::size_t /*length*/) {
if (!ec) {
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
ESP_LOGD("asio-chat", "%.*s", read_msg_.body_length(), read_msg_.body());
room_.deliver(read_msg_);
do_read_header();
} else {

View File

@ -1,4 +1,4 @@
version: "1.28.2~0"
version: "1.32.0"
description: Cross-platform C++ library for network and I/O programming
url: https://github.com/espressif/esp-protocols/tree/master/components/asio
issues: https://github.com/espressif/esp-protocols/issues
@ -7,3 +7,5 @@ repository: https://github.com/espressif/esp-protocols.git
dependencies:
idf:
version: ">=5.0"
espressif/sock_utils:
version: "^0.1"

View File

@ -0,0 +1,11 @@
//
// SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
//
// SPDX-License-Identifier: BSL-1.0
//
#pragma once
#include "sys/socket.h"
#include "socketpair.h"
#include_next "asio/detail/config.hpp"

View File

@ -1,12 +0,0 @@
/*
* SPDX-FileCopyrightText: 2018-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ESP_ASIO_CONFIG_H_
#define _ESP_ASIO_CONFIG_H_
#define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
#include "openssl_stub.hpp"
#endif // _ESP_ASIO_CONFIG_H_

View File

@ -8,7 +8,7 @@
//
#include "asio/detail/config.hpp"
#include "openssl_stub.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
#include <cstring>
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"

View File

@ -7,7 +7,7 @@
//
#include "asio/detail/config.hpp"
#include "openssl_stub.hpp"
#include "asio/ssl/detail/openssl_types.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/ssl/detail/engine.hpp"

View File

@ -0,0 +1,36 @@
//
// SPDX-FileCopyrightText: 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// SPDX-License-Identifier: BSL-1.0
//
// SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
//
#include "asio/detail/posix_event.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
#include "asio/detail/push_options.hpp"
#include <unistd.h>
#include <climits>
namespace asio::detail {
// This replaces asio's posix_event constructor
// since the default POSIX version uses pthread_condattr_t operations (init, setclock, destroy),
// which are not available on all IDF versions (some are defined in compilers' headers, others in
// pthread library, but they typically return `ENOSYS` which causes trouble in the event wrapper)
// IMPORTANT: Check implementation of posix_event() when upgrading upstream asio in order not to
// miss any initialization step.
posix_event::posix_event()
: state_(0)
{
int error = ::pthread_cond_init(&cond_, nullptr);
asio::error_code ec(error, asio::error::get_system_category());
asio::detail::throw_error(ec, "event");
}
} // namespace asio::detail
extern "C" int pause (void)
{
while (true) {
::sleep(UINT_MAX);
}
}

View File

@ -14,38 +14,53 @@ using namespace esp_modem;
static const size_t dte_default_buffer_size = 1000;
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF)
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> terminal)
: buffer(config->dte_buffer_size),
cmux_term(nullptr),
primary_term(std::move(terminal)),
secondary_term(primary_term),
mode(modem_mode::UNDEF)
{
ESP_MODEM_THROW_IF_FALSE(primary_term != nullptr, "Invalid argument: terminal cannot be null");
set_command_callbacks();
}
DTE::DTE(std::unique_ptr<Terminal> terminal):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(terminal)), secondary_term(primary_term),
mode(modem_mode::UNDEF)
DTE::DTE(std::unique_ptr<Terminal> terminal)
: buffer(dte_default_buffer_size),
cmux_term(nullptr),
primary_term(std::move(terminal)),
secondary_term(primary_term),
mode(modem_mode::UNDEF)
{
ESP_MODEM_THROW_IF_FALSE(primary_term != nullptr, "Invalid argument: terminal cannot be null");
set_command_callbacks();
}
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(config->dte_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE)
DTE::DTE(const esp_modem_dte_config *config, std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s)
: buffer(config->dte_buffer_size),
cmux_term(nullptr),
primary_term(std::move(t)),
secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE)
{
ESP_MODEM_THROW_IF_FALSE(primary_term != nullptr, "Invalid argument: primary terminal cannot be null");
ESP_MODEM_THROW_IF_FALSE(secondary_term != nullptr, "Invalid argument: secondary terminal cannot be null");
set_command_callbacks();
}
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s):
buffer(dte_default_buffer_size),
cmux_term(nullptr), primary_term(std::move(t)), secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE)
DTE::DTE(std::unique_ptr<Terminal> t, std::unique_ptr<Terminal> s)
: buffer(dte_default_buffer_size),
cmux_term(nullptr),
primary_term(std::move(t)),
secondary_term(std::move(s)),
mode(modem_mode::DUAL_MODE)
{
ESP_MODEM_THROW_IF_FALSE(primary_term != nullptr, "Invalid argument: primary terminal cannot be null");
ESP_MODEM_THROW_IF_FALSE(secondary_term != nullptr, "Invalid argument: secondary terminal cannot be null");
set_command_callbacks();
}
void DTE::set_command_callbacks()
{
primary_term->set_read_cb([this](uint8_t *data, size_t len) {

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(websocket): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py esp_websocket_client
tag_format: websocket-v$version
version: 1.3.0
version: 1.4.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,23 @@
# Changelog
## [1.4.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.4.0)
### Features
- Support DS peripheral for mutual TLS ([55385ec3](https://github.com/espressif/esp-protocols/commit/55385ec3))
### Bug Fixes
- wait for task on destroy ([42674b49](https://github.com/espressif/esp-protocols/commit/42674b49))
- Fix pytest to verify client correctly ([9046af8f](https://github.com/espressif/esp-protocols/commit/9046af8f))
- propagate error type ([eeeb9006](https://github.com/espressif/esp-protocols/commit/eeeb9006))
- fix example buffer leak ([5219c39d](https://github.com/espressif/esp-protocols/commit/5219c39d))
### Updated
- chore(websocket): align structure members ([beb6e57e](https://github.com/espressif/esp-protocols/commit/beb6e57e))
- chore(websocket): remove unused client variable ([15d3a01e](https://github.com/espressif/esp-protocols/commit/15d3a01e))
## [1.3.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.3.0)
### Features

View File

@ -93,10 +93,13 @@ typedef struct {
size_t client_cert_len;
const char *client_key;
size_t client_key_len;
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
void *client_ds_data;
#endif
bool use_global_ca_store;
bool skip_cert_common_name_check;
const char *cert_common_name;
esp_err_t (*crt_bundle_attach)(void *conf);
esp_err_t (*crt_bundle_attach)(void *conf);
esp_transport_handle_t ext_transport;
} websocket_config_storage_t;
@ -185,9 +188,9 @@ static void esp_websocket_free_buf(esp_websocket_client_handle_t client, bool is
}
static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
const char *data,
int data_len)
esp_websocket_event_id_t event,
const char *data,
int data_len)
{
esp_err_t err;
esp_websocket_event_data_t event_data;
@ -203,8 +206,8 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
if (client->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
event_data.error_handle.esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle(client->transport),
&client->error_handle.esp_tls_stack_err,
&client->error_handle.esp_tls_cert_verify_flags);
&client->error_handle.esp_tls_stack_err,
&client->error_handle.esp_tls_cert_verify_flags);
event_data.error_handle.esp_tls_stack_err = client->error_handle.esp_tls_stack_err;
event_data.error_handle.esp_tls_cert_verify_flags = client->error_handle.esp_tls_cert_verify_flags;
event_data.error_handle.esp_transport_sock_errno = esp_transport_get_errno(client->transport);
@ -234,14 +237,14 @@ static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_hand
} else {
client->reconnect_tick_ms = _tick_get_ms();
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
client->error_handle.error_type = error_type;
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
}
client->error_handle.error_type = error_type;
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
return ESP_OK;
}
static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client, const char *format, ...) __attribute__((format(printf, 2, 3)));
static esp_err_t esp_websocket_client_error(esp_websocket_client_handle_t client, const char *format, ...)
{
va_list myargs;
@ -446,6 +449,21 @@ static void destroy_and_free_resources(esp_websocket_client_handle_t client)
client = NULL;
}
static esp_err_t stop_wait_task(esp_websocket_client_handle_t client)
{
/* A running client cannot be stopped from the websocket task/event handler */
TaskHandle_t running_task = xTaskGetCurrentTaskHandle();
if (running_task == client->task_handle) {
ESP_LOGE(TAG, "Client cannot be stopped from websocket task");
return ESP_FAIL;
}
client->run = false;
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
}
static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_handle_t client, const char *scheme)
{
esp_transport_handle_t trans = esp_transport_list_get_transport(client->transport_list, scheme);
@ -531,6 +549,10 @@ static esp_err_t esp_websocket_client_create_transport(esp_websocket_client_hand
} else {
esp_transport_ssl_set_client_key_data_der(ssl, client->config->client_key, client->config->client_key_len);
}
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
} else if (client->config->client_ds_data) {
esp_transport_ssl_set_ds_data(ssl, client->config->client_ds_data);
#endif
}
if (client->config->crt_bundle_attach) {
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
@ -653,7 +675,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
client->keep_alive_cfg.keep_alive_enable = true;
client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? WEBSOCKET_KEEP_ALIVE_IDLE : config->keep_alive_idle;
client->keep_alive_cfg.keep_alive_interval = (config->keep_alive_interval == 0) ? WEBSOCKET_KEEP_ALIVE_INTERVAL : config->keep_alive_interval;
client->keep_alive_cfg.keep_alive_count = (config->keep_alive_count == 0) ? WEBSOCKET_KEEP_ALIVE_COUNT : config->keep_alive_count;
client->keep_alive_cfg.keep_alive_count = (config->keep_alive_count == 0) ? WEBSOCKET_KEEP_ALIVE_COUNT : config->keep_alive_count;
}
if (config->if_name) {
@ -696,6 +718,9 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
client->config->client_cert_len = config->client_cert_len;
client->config->client_key = config->client_key;
client->config->client_key_len = config->client_key_len;
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
client->config->client_ds_data = config->client_ds_data;
#endif
client->config->skip_cert_common_name_check = config->skip_cert_common_name_check;
client->config->cert_common_name = config->cert_common_name;
client->config->crt_bundle_attach = config->crt_bundle_attach;
@ -744,6 +769,7 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
ESP_WS_CLIENT_MEM_CHECK(TAG, client->status_bits, {
goto _websocket_init_fail;
});
xEventGroupSetBits(client->status_bits, STOPPED_BIT);
client->buffer_size = buffer_size;
return client;
@ -758,9 +784,11 @@ esp_err_t esp_websocket_client_destroy(esp_websocket_client_handle_t client)
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (client->run) {
esp_websocket_client_stop(client);
if (client->status_bits && (STOPPED_BIT & xEventGroupGetBits(client->status_bits)) == 0) {
stop_wait_task(client);
}
destroy_and_free_resources(client);
return ESP_OK;
}
@ -922,7 +950,7 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
client->last_fin = esp_transport_ws_get_fin_flag(client->transport);
client->last_opcode = esp_transport_ws_get_read_opcode(client->transport);
if (rlen == 0 && client->last_opcode == WS_TRANSPORT_OPCODES_NONE ) {
if (rlen == 0 && client->last_opcode == WS_TRANSPORT_OPCODES_NONE) {
ESP_LOGV(TAG, "esp_transport_read timeouts");
esp_websocket_free_buf(client, false);
return ESP_OK;
@ -1028,7 +1056,7 @@ static void esp_websocket_client_task(void *pv)
}
}
if ( _tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec * 1000 ) {
if (_tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec * 1000) {
if (client->wait_for_pong_resp) {
esp_websocket_client_error(client, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT);
@ -1149,23 +1177,13 @@ esp_err_t esp_websocket_client_stop(esp_websocket_client_handle_t client)
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!client->run) {
if (xEventGroupGetBits(client->status_bits) & STOPPED_BIT) {
ESP_LOGW(TAG, "Client was not started");
return ESP_FAIL;
}
/* A running client cannot be stopped from the websocket task/event handler */
TaskHandle_t running_task = xTaskGetCurrentTaskHandle();
if (running_task == client->task_handle) {
ESP_LOGE(TAG, "Client cannot be stopped from websocket task");
return ESP_FAIL;
}
client->run = false;
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
return stop_wait_task(client);
}
static int esp_websocket_client_send_close(esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout)
@ -1355,3 +1373,13 @@ esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client,
}
return esp_event_handler_register_with(client->event_handle, WEBSOCKET_EVENTS, event, event_handler, event_handler_arg);
}
esp_err_t esp_websocket_unregister_events(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
esp_event_handler_t event_handler)
{
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
return esp_event_handler_unregister_with(client->event_handle, WEBSOCKET_EVENTS, event, event_handler);
}

View File

@ -227,6 +227,7 @@ static void websocket_app_start(void)
xSemaphoreTake(shutdown_sema, portMAX_DELAY);
esp_websocket_client_close(client, portMAX_DELAY);
ESP_LOGI(TAG, "Websocket Stopped");
esp_websocket_unregister_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler);
esp_websocket_client_destroy(client);
}

View File

@ -55,7 +55,7 @@ class Websocket(object):
ssl_context.load_cert_chain(certfile='main/certs/server/server_cert.pem', keyfile='main/certs/server/server_key.pem')
if self.client_verify is True:
ssl_context.load_verify_locations(cafile='main/certs/ca_cert.pem')
ssl_context.verify = ssl.CERT_REQUIRED
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
self.server = SimpleSSLWebSocketServer('', self.port, WebsocketTestEcho, ssl_context=ssl_context)
else:

View File

@ -1,4 +1,4 @@
version: "1.3.0"
version: "1.4.0"
description: WebSocket protocol client for ESP-IDF
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_websocket_client
dependencies:

View File

@ -108,10 +108,13 @@ typedef struct {
int buffer_size; /*!< Websocket buffer size */
const char *cert_pem; /*!< Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len. */
size_t cert_len; /*!< Length of the buffer pointed to by cert_pem. May be 0 for null-terminated pem */
const char *client_cert; /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */
const char *client_cert; /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key` or `client_ds_data` (if supported) has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */
size_t client_cert_len; /*!< Length of the buffer pointed to by client_cert. May be 0 for null-terminated pem */
const char *client_key; /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */
const char *client_key; /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided and `client_ds_data` (if supported) gets ignored. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */
size_t client_key_len; /*!< Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem */
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
void *client_ds_data; /*!< Pointer to the encrypted private key data for SSL mutual authentication using the DS peripheral, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided. It is ignored if `client_key` is provided */
#endif
esp_websocket_transport_t transport; /*!< Websocket transport type, see `esp_websocket_transport_t */
const char *subprotocol; /*!< Websocket subprotocol */
const char *user_agent; /*!< Websocket user-agent */
@ -457,6 +460,18 @@ esp_err_t esp_websocket_register_events(esp_websocket_client_handle_t client,
esp_event_handler_t event_handler,
void *event_handler_arg);
/**
* @brief Unegister the Websocket Events
*
* @param client The client handle
* @param event The event id
* @param event_handler The callback function
* @return esp_err_t
*/
esp_err_t esp_websocket_unregister_events(esp_websocket_client_handle_t client,
esp_websocket_event_id_t event,
esp_event_handler_t event_handler);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,8 @@
---
commitizen:
bump_message: 'bump(lws): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py libwebsockets
tag_format: lws-v$version
version: 4.3.3
version_files:
- idf_component.yml

View File

@ -0,0 +1,7 @@
# Changelog
## [4.3.3](https://github.com/espressif/esp-protocols/commits/lws-v4.3.3)
### Features
- Add initial support libwebsockets component ([ef3443d](https://github.com/espressif/esp-protocols/commit/ef3443d))

View File

@ -0,0 +1,20 @@
idf_component_register(REQUIRES mbedtls)
set(LWS_WITH_EXPORT_LWSTARGETS OFF CACHE BOOL "Export libwebsockets CMake targets. Disable if they conflict with an outer cmake project.")
set(LWS_WITH_MBEDTLS ON CACHE BOOL "Use mbedTLS (>=2.0) replacement for OpenSSL.")
set(LWS_WITH_JPEG OFF CACHE BOOL "Enable stateful JPEG stream decoder")
set(WRAP_FUNCTIONS mbedtls_ssl_handshake_step
lws_adopt_descriptor_vhost)
foreach(wrap ${WRAP_FUNCTIONS})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${wrap}")
endforeach()
target_link_libraries(${COMPONENT_LIB} INTERFACE websockets)
target_sources(${COMPONENT_LIB} INTERFACE "port/lws_port.c")
add_subdirectory(libwebsockets)

View File

@ -0,0 +1,9 @@
Libwebsockets and included programs are provided under the terms of the
MIT license, with the exception that some sources are under
a similar permissive license like BSD, or are explicitly CC0 / public
domain to remove any obstacles from basing differently-licensed code on
them.
Please refer to the LWS LICENSE for more details.
Espressif port files are licensed under Apache-2.0 license.
Espressif examples and tests are distributed under Unlicense/CC0.

View File

@ -0,0 +1,24 @@
# ESP32 libwebsockets Port
This is a lightweight port of the libwebsockets library designed to run on the ESP32. It provides WebSocket client functionalities.
## Supported Options
The ESP-IDF port of libwebsockets supports a set of common WebSocket configurations for clients. These options can be configured through a structure passed to the `lws_create_context()` function.
Key features supported:
- WebSocket with optional SSL/TLS
- HTTP/1.1 client support
## Memory Footprint Considerations
The memory consumption primarily depends on the number of concurrent connections and the selected options for WebSocket frames, protocol handling, and SSL/TLS features. It consumes approximately 300 kB of program memory.
### Client:
The values bellow were extracted from the client example (./examples/client/main/lws-client.c)
- **Initial Memory Usage**: ~8 kB of heap on startup
- **Connected Memory Usage Over TLS**: ~35.3 kB of heap after connected to a server
- **Connected Memory Usage Over TCP (Plain)**: ~4.5 kB of heap after connected to a server
#### When configuring a WebSocket client, ensure that you have enough heap space to handle the desired number of concurrent client connections.
#### SSL/TLS configurations may require additional memory overhead, depending on the certificate size and cryptographic settings.

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(client_example)

View File

@ -0,0 +1,311 @@
Libwebsockets and included programs are provided under the terms of the
MIT license shown below, with the exception that some sources are under
a similar permissive license like BSD, or are explicitly CC0 / public
domain to remove any obstacles from basing differently-licensed code on
them.
Original liberal license retained:
- lib/misc/sha-1.c - 3-clause BSD license retained, link to original [BSD3]
- win32port/zlib
- lib/drivers/display/upng.* - ZLIB license (see zlib.h) [ZLIB]
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls) [APACHE2]
- lib/tls/mbedtls/mbedtls-extensions.c
- lib/misc/base64-decode.c - already MIT
- contrib/mcufont/encoder
- lib/misc/ieeehalfprecision.c - 2-clause BSD license retained [BSD2]
- contrib/mcufont/fonts - Open Font License [OFL]
Relicensed to MIT:
- lib/misc/daemonize.c - relicensed from Public Domain to MIT,
link to original Public Domain version
- lib/plat/windows/windows-resolv.c - relicensed from "Beerware v42" to MIT
Public Domain (CC-zero) to simplify reuse:
- test-apps/*.c
- test-apps/*.h
- minimal-examples/*
- lwsws/*
Although libwebsockets is available under a permissive license, it does not
change the reality of dealing with large lumps of external code... if your
copy diverges it is guaranteed to contain security problems after a while
and can be very painful to pick backports (especially since historically,
we are very hot on cleaning and refactoring the codebase). The least
painful and lowest risk way remains sending your changes and fixes upstream
to us so you can easily use later releases and fixes.
## MIT License applied to libwebsockets
https://opensource.org/licenses/MIT
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.
## BSD2
```
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
```
## BSD3
For convenience, a copy of the license on `./lib/misc/sha-1.c`. In binary
distribution, this applies to builds with ws support enabled, and without
`LWS_WITHOUT_BUILTIN_SHA1` at cmake.
```
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH
```
## ZLIB
For convenience, a copy of the license on zlib. In binary distribution,
this applies for win32 builds with internal zlib only. You can avoid
building any zlib usage or copy at all with `-DLWS_WITH_ZLIB=0` (the
default), and so avoid needing to observe the license for binary
distribution that doesn't include the related code.
```
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
jloup@gzip.org madler@alumni.caltech.edu
```
## APACHE2
For convenience, a copy of the license on the mbedtls wrapper part. In binary
distribution, this applies only when building lws against mbedtls.
The canonical license application to source files uses the URL reference, so the
whole is not reproduced here.
```
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
```
## CC0
For convenience,the full text of CC0 dedication found on the lws examples.
The intention of this is to dedicate the examples to the public domain, so
users can build off and modify them without any constraint.
```
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
moral rights retained by the original author(s) and/or performer(s);
publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
rights protecting the extraction, dissemination, use and reuse of data in a Work;
database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
```
## OFL: Open Font License
```
Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
```

View File

@ -0,0 +1,87 @@
# Websocket LWS client example
This example will shows how to set up and communicate over a websocket.
## How to Use Example
### Hardware Required
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet or a local server.
### Configure the project
* Open the project configuration menu (`idf.py menuconfig`)
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu.
* Configure the websocket endpoint URI under "Example Configuration"
### Server Certificate Verification
* Mutual Authentication: When `CONFIG_WS_OVER_TLS_MUTUAL_AUTH=y` is enabled, it's essential to provide valid certificates for both the server and client.
This ensures a secure two-way verification process.
* Server-Only Authentication: To perform verification of the server's certificate only (without requiring a client certificate), set `CONFIG_WS_OVER_TLS_SERVER_AUTH=y`.
This method skips client certificate verification.
* Example below demonstrates how to generate a new self signed certificates for the server and client using the OpenSSL command line tool
Please note: This example represents an extremely simplified approach to generating self-signed certificates/keys with a single common CA, devoid of CN checks, lacking password protection, and featuring hardcoded key sizes and types. It is intended solely for testing purposes.
In the outlined steps, we are omitting the configuration of the CN (Common Name) field due to the context of a testing environment. However, it's important to recognize that the CN field is a critical element of SSL/TLS certificates, significantly influencing the security and efficacy of HTTPS communications. This field facilitates the verification of a website's identity, enhancing trust and security in web interactions. In practical deployments beyond testing scenarios, ensuring the CN field is accurately set is paramount for maintaining the integrity and reliability of secure communications
It is **strongly recommended** to not reuse the example certificate in your application;
it is included only for demonstration.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (18208) lws-client: LWS minimal ws client echo
219868: lws_create_context: LWS: 4.3.99-v4.3.0-424-ga74362ff, MbedTLS-3.6.2 NET CLI SRV H1 H2 WS SS-JSON-POL ConMon IPv6-absent
219576: mem: platform fd map: 20 bytes
217880: __lws_lc_tag: ++ [wsi|0|pipe] (1)
216516: __lws_lc_tag: ++ [vh|0|default||-1] (1)
I (18248) lws-client: connect_cb: connecting
210112: __lws_lc_tag: ++ [wsicli|0|WS/h1/default/echo.websocket.events] (1)
204800: [wsicli|0|WS/h1/default/echo.websocket.events]: lws_client_connect_3_connect: trying 13.248.241.119
180776: lws_ssl_client_bio_create: allowing selfsigned
I (19998) wifi:<ba-add>idx:0 (ifx:0, b4:89:01:63:9d:08), tid:0, ssn:321, winSize:64
I (20768) lws-client: WEBSOCKET_EVENT_CONNECTED
I (20768) lws-client: Sending hello 0000
I (20778) lws-client: WEBSOCKET_EVENT_DATA
W (20778) lws-client: Received=echo.websocket.events sponsored by Lob.com
I (20968) lws-client: WEBSOCKET_EVENT_DATA
W (20968) lws-client: Received=hello 0000
I (22978) lws-client: Sending hello 0001
I (23118) lws-client: WEBSOCKET_EVENT_DATA
W (23118) lws-client: Received=hello 0001
I (23778) lws-client: Sending hello 0002
I (23938) lws-client: WEBSOCKET_EVENT_DATA
W (23938) lws-client: Received=hello 0002
I (25948) lws-client: Sending hello 0003
I (26088) lws-client: WEBSOCKET_EVENT_DATA
W (26088) lws-client: Received=hello 0003
I (26948) lws-client: Sending hello 0004
I (27118) lws-client: WEBSOCKET_EVENT_DATA
W (27118) lws-client: Received=hello 0004
```

View File

@ -0,0 +1,12 @@
set(SRC_FILES "lws-client.c") # Define source files
set(INCLUDE_DIRS ".") # Define include directories
set(EMBED_FILES "") # Initialize an empty list for files to embed
list(APPEND EMBED_FILES
"certs/client_cert.pem"
"certs/ca_cert.pem"
"certs/client_key.pem")
idf_component_register(SRCS "${SRC_FILES}"
INCLUDE_DIRS "${INCLUDE_DIRS}"
EMBED_TXTFILES "${EMBED_FILES}")

View File

@ -0,0 +1,45 @@
menu "Example Configuration"
choice WEBSOCKET_URI_SOURCE
prompt "Websocket URI source"
default WEBSOCKET_URI_FROM_STRING
help
Selects the source of the URI used in the example.
config WEBSOCKET_URI_FROM_STRING
bool "From string"
config WEBSOCKET_URI_FROM_STDIN
bool "From stdin"
endchoice
config WEBSOCKET_URI
string "Websocket endpoint URI"
default "echo.websocket.events"
help
URL or IP of websocket endpoint this example connects to and sends echo
config WEBSOCKET_PORT
int "Websocket endpoint PORT"
default 443
help
Port of websocket endpoint this example connects to and sends echo
config WS_OVER_TLS_SERVER_AUTH
bool "Enable WebSocket over TLS with Server Certificate Verification Only"
default n
help
Enables WebSocket connections over TLS (WSS) with server certificate verification.
This setting mandates the client to verify the servers certificate, while the server
does not require client certificate verification.
config WS_OVER_TLS_MUTUAL_AUTH
bool "Enable WebSocket over TLS with Server Client Mutual Authentification"
default y
help
Enables WebSocket connections over TLS (WSS) with server and client mutual certificate verification.
config WS_OVER_TLS_SKIP_COMMON_NAME_CHECK
bool "Skip common name(CN) check during TLS authentification"
default n
help
Skipping Common Name(CN) check during TLS(WSS) authentification
endmenu

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUL04QhbSEt5oNbV4f7CeLLqTCw2gwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA2MjVaFw0zNDAy
MjAwODA2MjVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDjc78SuXAmJeBc0el2/m+2lwtk3J/VrNxHYkhjHa8K
/ybU89VvKGuv9+L3IP67WMguFTaMgivJYUePjfMchtNJLJ+4cR9BkBKH4JnyXDae
s0a5181LxRo8rqcaOw9hmJTgt9R4dIRTR3GN2/VLhlR+L9OTYA54RUtMyMMpyk5M
YIJbcOwiwkVLsIYnexXDfgz9vQGl/2vBQ/RBtDBvbSyBiWox9SuzOrya1HUBzJkM
Iu5L0bSa0LAeXHT3i3P1Y4WPt9ub70OhUNfJtHC+XbGFSEkkQG+lfbXU75XLoMWa
iATMREOcb3Mq+pn1G8o1ZHVc6lBHUkfrNfxs5P/GQcSvAgMBAAGjUzBRMB0GA1Ud
DgQWBBQGkdK2gR2HrQTnZnbuWO7I1+wdxDAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
ZnbuWO7I1+wdxDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBx
G0hFtMwV/agIwC3ZaYC36ZWiijFzWkJSZG+fqAy32mSoVL2uQvOT8vEfF0ZnAcPc
JI4oI059dBhAVlwqv6uLHyD4Gf2bF4oSLljdTz3X23llF+/wrTC2LLqMrm09aUC0
ac74Q0FVwVJJcqH1HgemCMVjna5MkwNA6B+q7uR3eQ692VqXk6vjd4fRLBg1bBO1
hXjasfNxA8A9quORF5+rjYrwyUZHuzcs0FfSClckIt4tHKtt4moLufOW6/PM4fRe
AgdDfiTupxYLJFz4hFPhfgCh4TjQ+f9+uP4IAjW42dJmTVZjLEku/hm5lxCFObAq
RgfaNwH8Ug1r1xswjSZG
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIUUPCOgMA2v09E29fCkogx3RUBRtEwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA3MzFaFw0zNDAy
MjAwODA3MzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCrNeomxI2aoP+4iUy5SiA+41oHUDZDFeJOBjv5JCsK
mlvFqxE9zynmPOVpuABErOJwzerPTa4NYKvuvs5CxVJUV5CXtWANuu9majioZNzj
f877MDNX/GnZHK2gnkxVrZCPaDmx9yiMsFMXgmfdrDhwoUpXbdgSyeU/al9Ds2kF
0hrHOH2LBWt/mVeLbONU5CC1HOdVVw+uRlhVlxnfhTPd/Nru3rJx7R0sN7qXcZpJ
PL87WvrszLVOux24DeaOz9oiD2b7egFyUuq1BM25iCwi8s/Ths8xd0Ca1d8mEcHW
FVd4w2+nUMXFE+IbP+wo6FXuiSaOBNri3rztpvCCMaWjAgMBAAGjQjBAMB0GA1Ud
DgQWBBSOlA+9Vfbcfy8iS4HSd4V0KPtm4jAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
ZnbuWO7I1+wdxDANBgkqhkiG9w0BAQsFAAOCAQEAOmzm/MwowKTrSpMSrmfA3MmW
ULzsfa25WyAoTl90ATlg4653Y7pRaNfdvVvyi2V2LlPcmc7E0rfD53t1NxjDH1uM
LgFMTNEaZ9nMRSW0kMiwaRpvmXS8Eb9PXfvIM/Mw0co/aMOtAQnfTGIqsgkQwKyk
1GG7QKQq3p4QGu5ZaTnjnaoa79hODt+0xQDD1wp6C9xwBY0M4gndAi3wkOeFkGv+
OmGPtaCBu5V9tJCZ9dfZvjkaK44NGwDw0urAcYRK2h7asnlflu7cnlGMBB0qY4kQ
BX5WI8UjN6rECBHbtNRvEh06ogDdHbxYV+TibrqkkeDRw6HX1qqiEJ+iCgWEDQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrNeomxI2aoP+4
iUy5SiA+41oHUDZDFeJOBjv5JCsKmlvFqxE9zynmPOVpuABErOJwzerPTa4NYKvu
vs5CxVJUV5CXtWANuu9majioZNzjf877MDNX/GnZHK2gnkxVrZCPaDmx9yiMsFMX
gmfdrDhwoUpXbdgSyeU/al9Ds2kF0hrHOH2LBWt/mVeLbONU5CC1HOdVVw+uRlhV
lxnfhTPd/Nru3rJx7R0sN7qXcZpJPL87WvrszLVOux24DeaOz9oiD2b7egFyUuq1
BM25iCwi8s/Ths8xd0Ca1d8mEcHWFVd4w2+nUMXFE+IbP+wo6FXuiSaOBNri3rzt
pvCCMaWjAgMBAAECggEAOTWjz16AXroLmRMv8v5E9h6sN6Ni7lnCrAXDRoYCZ+Ga
Ztu5wCiYPJn+oqvcUxZd+Ammu6yeS1QRP468h20+DHbSFw+BUDU1x8gYtJQ3h0Fu
3VqG3ZC3odfGYNRkd4CuvGy8Uq5e+1vz9/gYUuc4WNJccAiBWg3ir6UQviOWJV46
LGfdEd9hVvIGl5pmArMBVYdpj9+JHunDtG4uQxiWla5pdLjlkC2mGexD18T9d718
6I+o3YHv1Y9RPT1d4rNhYQWx6YdTTD2rmS7nTrzroj/4fXsblpXzR+/l7crlNERY
67RMPwgDR1NiAbCAJKsSbMS66lRCNlhTM4YffGAN6QKBgQDkIdcNm9j49SK5Wbl5
j8U6UbcVYPzPG+2ea+fDfUIafA0VQHIuX6FgA17Kp7BDX9ldKtSBpr0Z8vetVswr
agmXVMR/7QdvnZ9NpL66YA/BRs67CvsryVu4AVAzThFGySmlcXGlPq47doWDQ3B9
0BOEnVoeDXR3SabaNsEbhDYn1wKBgQDAIAUyhJcgz+LcgaAtBwdnEN57y66JlRVZ
bsb6cEG/MNmnLjQYsplJjNbz4yrB5ukTChPTGRF/JQRqHoXh6DGQFHvobukwwA6x
RAIIq0NLJ5HUipfOi+VpCbWUHdoUNhwjAB2qVtD4LXE2Lyn46C8ET5eRtRjUKpzV
lpsq63KHFQKBgFB+cDbpCoGtXPcxZXQy+lA9jPAKLKmXHRyMzlX32F8n7iXVe3RJ
YdNS3Rt8V4EuTK/G8PxeLNL/G80ZlyiqXX/79Ol+ZOVJJHBs9K8mPejgZwkwMrec
cLRYIkg3/3iOehdaE9NOboOkqi9KmGKMDJb6PlXkQXflkO3l6/UdjU45AoGAen0v
sxiTncjMU1eVfn+nuY8ouXaPbYoOFXmqBItDb5i+e3baohBj6F+Rv+ZKIVuNp6Ta
JNErtYstOFcDdpbp2nkk0ni71WftNhkszsgZ3DV7JS3DQV0xwvj8ulUZ757b63is
cShujHu0XR5OvTGSoEX6VVxHWyVb3lTp0sBPwU0CgYBe2Ieuya0X8mAbputFN64S
Kv++dqktTUT8i+tp07sIrpDeYwO3D89x9kVSJj4ImlmhiBVGkxFWPkpGyBLotdse
Ai/E6f5I7CDSZZC0ZucgcItNd4Yy459QY+dFwFtT3kIaD9ml8fnqQ83J9W8DWtv9
6mY9FnUUufbJcpHxN58RTw==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIUUPCOgMA2v09E29fCkogx3RUBRtAwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjMwODA2NTlaFw0zNDAy
MjAwODA2NTlaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC8WWbDxnLzTSfuQaO+kQnnzbwjhUHWn58s+BIEaO8M
GG6bX+8r/SH9XjMfFS36qAN3qxgRun3YoRTaHc2QByiGjf5IL4EAPDnLN+NzUIL5
7Gi2QPQP/GksAsOGKWk/nMRPk1vcMptkFVIWSp474SQ0A92Z9z0dUIqBpjRa34kr
HsAIcT59/EG7YBBadMk0fQIxQVLh3Vosky85q+0waFihe47Ef5U2UftexoUx4Vcz
6EtP60Wx+4qN+FLsr+n2B7Oz2ITqfwgqLzjNLZwm9bMjcLZ0fWm1A/W1C989MXwI
w6DAPEZv7pbgp8r9phyrNieSDuuRaCvFsaXh6troAjLxAgMBAAGjQjBAMB0GA1Ud
DgQWBBRJCYAQG2+1FN5P/wyAR1AsrAyb4DAfBgNVHSMEGDAWgBQGkdK2gR2HrQTn
ZnbuWO7I1+wdxDANBgkqhkiG9w0BAQsFAAOCAQEAmllul/GIH7RVq85mM/SxP47J
M7Z7T032KuR3n/Psyv2iq/uEV2CUje3XrKNwR2PaJL4Q6CtoWy7xgIP+9CBbjddR
M7sdNQab8P2crAUtBKnkNOl/na/5KnXnjwi/PmWJJ9i2Cqt0PPkaykTWp/MLfYIw
RPkY2Yo8f8gEiqXQd+0qTuMgumbgkPq3V8Lk1ocy62F5/qUhXxH+ifAXEoUQS6EG
8DlgwdZlfUY+jeM6N56WzYmxD1syjNW7faPio+qXINfpYatROhqphaMQ5SA6TRj6
jcnLa31TdDdWmWYDcYgZntAv6yGi3rh0MdYqeNS0FKlMKmaH81VHs7V1UUXwUQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8WWbDxnLzTSfu
QaO+kQnnzbwjhUHWn58s+BIEaO8MGG6bX+8r/SH9XjMfFS36qAN3qxgRun3YoRTa
Hc2QByiGjf5IL4EAPDnLN+NzUIL57Gi2QPQP/GksAsOGKWk/nMRPk1vcMptkFVIW
Sp474SQ0A92Z9z0dUIqBpjRa34krHsAIcT59/EG7YBBadMk0fQIxQVLh3Vosky85
q+0waFihe47Ef5U2UftexoUx4Vcz6EtP60Wx+4qN+FLsr+n2B7Oz2ITqfwgqLzjN
LZwm9bMjcLZ0fWm1A/W1C989MXwIw6DAPEZv7pbgp8r9phyrNieSDuuRaCvFsaXh
6troAjLxAgMBAAECggEACNVCggTxCCMCr+RJKxs/NS1LWPkbZNbYjrHVmnpXV6Bf
s460t0HoUasUx6zlGp+9heOyvcYat8maIj6KkOodBu5q0fTUXm/0n+ivlI1ejxz8
ritupr9GKWe5xrVzd6XA+SBmivWenvt2/Y+jSxica4oQ3vMe3RyVWk4yn15jXu+9
7B9lNyNeZtOBr6OozHGLYw4dwWcBNv2S6wevRKfHPwn/Ch5yTH1uAskgoMxUuyK2
ynNVHWUhyS4pFU7Tex5ENDel15VYdbxV/2lQ2W6fHMLtC5GWKJXXbigCX7pfOpzC
BFJEfZl7ze/qptE9AR7DkLFYyMtrS7OlebYbLDOM9wKBgQD+rTdwULZibpKwlI3a
9Y22d4N/EDFvuu8LnuEiVQnXgwg9M+tlaa2liP18j1a7y/FCfoXf5sjUWCsdYR6d
C0TuiOGI59hYGI94NvVLAmOutR+vJ/3jhbv5wyqEQLhJ42Yz9kWBrDCI+V3q3TdO
H7wcH6suUIZpeLEJF4qHzY/1dwKBgQC9U/Pvswiww8sfysmd5shUNo4ofAZnTM1A
ak6pWE3lSyiOkSm+3B2GqxYWLRoo1v+pTyhhXDtRRmxGtMNrKCsmlHef/o3c6kkG
cuC2h/DiSmoITHy3BYKJoDeE54E8ubXUUKqHo41LYUs+D7M/IGxeiO13MUoIrEtF
AwzVWPBU1wKBgH8barD2x6Bm+XWCHy6qIZlxGsMfDN1r2gTdvhWJhcj3D/Sj5heO
X+lfbsxtKee+yOHcDesK3y8D9jjKkSHmTvgSfyX6OML3NxvTqidOwPugUHj2J8QX
qhLk8mJhftj50reacWRf0TV76ADhecnXEuaic6hA7mTTpOAZzL0svm3PAoGBALWF
r6VLX3KzVqZVtLb7FWmAoQ35093pCgXPpznAW3cTd4Axd/fxbTG4CUYb2i/760X2
ij3Gw2yqe5fTKmYsLisgQA2bb4K28msHa6I2dmNQe5cXVp/X3Y98mJ6JpCSH3ekB
qm7ABfGXCCApx28n9B8zY5JbJKNqJgS15vELA+ojAoGAAkaV2w46+3iQ6gJtQepr
zGNybiYBx/Wo5fDdTS5u0xN+ZdC9fl2Zs0n7sMmUT8bWdDLcMnntHHO+oDIKyRHs
TQh1n68vQ4JoegQv3Z9Z/TLEKqr9gyJC1Ao6M4bZpPhUWQwupfHColtsr2TskcnJ
Nf2FpJZ7z6fQEShGlK1yTXM=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,6 @@
dependencies:
espressif/libwebsockets:
version: "*"
override_path: "../../../"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@ -0,0 +1,323 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ESP libwebsockets client example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <libwebsockets.h>
#include <stdio.h>
#include <stdlib.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_task_wdt.h"
#include <cJSON.h>
static uint32_t disconnect_timeout = 5000000; // microseconds
static uint8_t message_count = 0;
static const char *TAG = "lws-client";
static struct lws_context *context;
static struct lws *client_wsi;
static lws_sorted_usec_list_t sul;
static unsigned char msg[LWS_PRE + 128];
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 3,
.secs_since_valid_hangup = 10,
};
#if CONFIG_WEBSOCKET_URI_FROM_STDIN
static void get_string(char *line, size_t size)
{
int count = 0;
while (count < size) {
int c = fgetc(stdin);
if (c == '\n') {
line[count] = '\0';
break;
} else if (c > 0 && c < 127) {
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
esp_task_wdt_reset();
}
}
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */
static void send_data(struct lws *wsi, const char *data, size_t len, enum lws_write_protocol type)
{
unsigned char buf[LWS_PRE + len];
unsigned char *p = &buf[LWS_PRE];
memcpy(p, data, len);
int n = lws_write(wsi, p, len, type);
if (n < len) {
ESP_LOGE(TAG, "ERROR %d writing ws\n", n);
}
}
static void send_large_text_data(struct lws *wsi)
{
const int size = 2000;
char *long_data = malloc(size);
memset(long_data, 'a', size);
send_data(wsi, long_data, size, LWS_WRITE_TEXT);
free(long_data);
}
static void send_fragmented_text_data(struct lws *wsi)
{
char data[32];
memset(data, 'a', sizeof(data));
send_data(wsi, data, sizeof(data), LWS_WRITE_TEXT | LWS_WRITE_NO_FIN);
memset(data, 'b', sizeof(data));
send_data(wsi, data, sizeof(data), LWS_WRITE_CONTINUATION);
}
static void send_fragmented_binary_data(struct lws *wsi)
{
char binary_data[5];
memset(binary_data, 0, sizeof(binary_data));
send_data(wsi, binary_data, sizeof(binary_data), LWS_WRITE_BINARY | LWS_WRITE_NO_FIN);
memset(binary_data, 1, sizeof(binary_data));
send_data(wsi, binary_data, sizeof(binary_data), LWS_WRITE_CONTINUATION);
}
static void connect_cb(lws_sorted_usec_list_t *_sul)
{
struct lws_client_connect_info connect_info;
ESP_LOGI(TAG, "%s: connecting\n", __func__);
memset(&connect_info, 0, sizeof(connect_info));
#if CONFIG_WEBSOCKET_URI_FROM_STDIN
char line[128];
ESP_LOGI(TAG, "Please enter uri of websocket endpoint");
get_string(line, sizeof(line));
connect_info.address = line;
ESP_LOGI(TAG, "Endpoint uri: %s\n", line);
#else
connect_info.address = CONFIG_WEBSOCKET_URI;
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */
connect_info.context = context;
connect_info.port = CONFIG_WEBSOCKET_PORT;
connect_info.host = connect_info.address;
connect_info.origin = connect_info.address;
connect_info.local_protocol_name = "lws-echo";
connect_info.pwsi = &client_wsi;
connect_info.retry_and_idle_policy = &retry;
#if defined(CONFIG_WS_OVER_TLS_MUTUAL_AUTH) || defined(CONFIG_WS_OVER_TLS_SERVER_AUTH)
connect_info.ssl_connection = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED;
#if defined(CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK) && defined(CONFIG_WS_OVER_TLS_SERVER_AUTH)
connect_info.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
#endif
#else
connect_info.ssl_connection = LCCSCF_ALLOW_INSECURE;
#endif
if (!lws_client_connect_via_info(&connect_info)) {
lws_sul_schedule(context, 0, _sul, connect_cb, 5 * LWS_USEC_PER_SEC);
}
}
static int callback_minimal_echo(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
ESP_LOGE(TAG, "CLIENT_CONNECTION_ERROR: %s\n",
in ? (char *)in : "(null)");
lws_sul_schedule(context, 0, &sul, connect_cb, 5 * LWS_USEC_PER_SEC);
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
if (message_count < 5) {
char text_data[32];
sprintf(text_data, "hello %04d", message_count++);
ESP_LOGI(TAG, "Sending text: %s", text_data);
send_data(wsi, text_data, strlen(text_data), LWS_WRITE_TEXT);
} else if (message_count == 5) {
ESP_LOGI(TAG, "Sending fragmented text message");
send_fragmented_text_data(wsi);
ESP_LOGI(TAG, "Sending fragmented binary message");
send_fragmented_binary_data(wsi);
ESP_LOGI(TAG, "Sending text longer than ws buffer (1024)");
send_large_text_data(wsi);
message_count++;
}
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
if (lws_frame_is_binary(wsi)) {
ESP_LOGI(TAG, "Received binary data");
ESP_LOG_BUFFER_HEX("Received binary data", in, len);
} else {
ESP_LOGW(TAG, "Received=%.*s\n\n", len, (char *)in);
}
size_t remain = lws_remaining_packet_payload(wsi);
// If received data is larger than the ws buffer
if (remain > 0) {
ESP_LOGW(TAG, "Total payload length=%u, data_len=%u\n\n", remain + len, len);
}
// If received data contains json structure it succeed to parse
cJSON *root = cJSON_Parse(in);
if (root) {
for (int i = 0 ; i < cJSON_GetArraySize(root) ; i++) {
cJSON *elem = cJSON_GetArrayItem(root, i);
cJSON *id = cJSON_GetObjectItem(elem, "id");
cJSON *name = cJSON_GetObjectItem(elem, "name");
ESP_LOGW(TAG, "Json={'id': '%s', 'name': '%s'}", id->valuestring, name->valuestring);
}
cJSON_Delete(root);
}
/* Reset the timeout*/
lws_set_timer_usecs(wsi, disconnect_timeout);
break;
case LWS_CALLBACK_TIMER:
ESP_LOGW(TAG, "Closing connection");
lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL, (unsigned char *)"bye", 3);
/* Return non-null to close the connection. */
return -1;
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static const struct lws_protocols protocols[] = {
{
.name = "lws-echo",
.callback = callback_minimal_echo,
.per_session_data_size = 1024,
.rx_buffer_size = 1024,
.id = 0,
.user = NULL,
.tx_packet_size = 0
},
LWS_PROTOCOL_LIST_TERM
};
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
/* Configure WDT. */
TaskHandle_t handle = xTaskGetCurrentTaskHandle();
esp_task_wdt_add(handle);
/* Create LWS Context - Client. */
struct lws_context_creation_info info;
int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
memset(msg, 'x', sizeof(msg));
lws_set_log_level(logs, NULL);
ESP_LOGI(TAG, "LWS minimal ws client echo\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
info.fd_limit_per_thread = 1 + 1 + 1;
#if CONFIG_WS_OVER_TLS_MUTUAL_AUTH
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
/* Configuring client certificates for mutual authentification */
extern const char cert_start[] asm("_binary_client_cert_pem_start"); // Client certificate
extern const char cert_end[] asm("_binary_client_cert_pem_end");
extern const char key_start[] asm("_binary_client_key_pem_start"); // Client private key
extern const char key_end[] asm("_binary_client_key_pem_end");
info.client_ssl_cert_mem = cert_start;
info.client_ssl_cert_mem_len = cert_end - cert_start;
info.client_ssl_key_mem = key_start;
info.client_ssl_key_mem_len = key_end - key_start;
#elif CONFIG_WS_OVER_TLS_SERVER_AUTH
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
extern const char cacert_start[] asm("_binary_ca_cert_pem_start"); // CA certificate
extern const char cacert_end[] asm("_binary_ca_cert_pem_end");
info.client_ssl_ca_mem = cacert_start;
info.client_ssl_ca_mem_len = cacert_end - cacert_start;
#endif
context = lws_create_context(&info);
if (!context) {
ESP_LOGE(TAG, "lws init failed");
} else {
lws_sul_schedule(context, 0, &sul, connect_cb, 100);
/*
* Holds the result of the lws_service call:
* = 0 -> service succeeded and events were processed,
* < 0 -> an error occurred or the event loop should stop
*/
int service_result = 0;
while (service_result >= 0) {
service_result = lws_service(context, 0);
}
lws_context_destroy(context);
}
while (1) {
//Should not get here, Spin undefinitely.
vTaskDelay(10);
taskYIELD();
}
}

View File

@ -0,0 +1,237 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import json
import random
import re
import socket
import ssl
import string
import sys
from threading import Event, Thread
from SimpleWebSocketServer import (SimpleSSLWebSocketServer,
SimpleWebSocketServer, WebSocket)
def get_my_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
# doesn't even have to be reachable
s.connect(('8.8.8.8', 1))
IP = s.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
s.close()
return IP
class WebsocketTestEcho(WebSocket):
def handleMessage(self):
if isinstance(self.data, bytes):
print(f'\n Server received binary data: {self.data.hex()}\n')
self.sendMessage(self.data, binary=True)
else:
print(f'\n Server received: {self.data}\n')
self.sendMessage(self.data)
def handleConnected(self):
print('Connection from: {}'.format(self.address))
def handleClose(self):
print('{} closed the connection'.format(self.address))
# Simple Websocket server for testing purposes
class Websocket(object):
def send_data(self, data):
for nr, conn in self.server.connections.items():
conn.sendMessage(data)
def run(self):
if self.use_tls is True:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile='main/certs/server/server_cert.pem', keyfile='main/certs/server/server_key.pem')
if self.client_verify is True:
ssl_context.load_verify_locations(cafile='main/certs/ca_cert.pem')
ssl_context.verify = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
self.server = SimpleSSLWebSocketServer('', self.port, WebsocketTestEcho, ssl_context=ssl_context)
else:
self.server = SimpleWebSocketServer('', self.port, WebsocketTestEcho)
while not self.exit_event.is_set():
self.server.serveonce()
def __init__(self, port, use_tls, verify):
self.port = port
self.use_tls = use_tls
self.client_verify = verify
self.exit_event = Event()
self.thread = Thread(target=self.run)
self.thread.start()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.exit_event.set()
self.thread.join(10)
if self.thread.is_alive():
print('Thread cannot be joined', 'orange')
def test_examples_protocol_websocket(dut):
"""
steps:
1. obtain IP address
2. connect to uri specified in the config
3. send and receive data
"""
# Test for echo functionality:
# Sends a series of simple "hello" messages to the WebSocket server and verifies that each one is echoed back correctly.
# This tests the basic responsiveness and correctness of the WebSocket connection.
def test_echo(dut):
dut.expect('WEBSOCKET_EVENT_CONNECTED')
for i in range(0, 5):
dut.expect(re.compile(b'Received=hello (\\d)'))
print('All echos received')
sys.stdout.flush()
# Test for clean closure of the WebSocket connection:
# Ensures that the WebSocket can correctly receive a close frame and terminate the connection without issues.
def test_close(dut):
dut.expect('__lws_lc_untag')
# Test for JSON message handling:
# Sends a JSON formatted string and verifies that the received message matches the expected JSON structure.
def test_json(dut, websocket):
json_string = """
[
{
"id":"1",
"name":"user1"
},
{
"id":"2",
"name":"user2"
}
]
"""
websocket.send_data(json_string)
data = json.loads(json_string)
match = dut.expect(
re.compile(b'Json=({[a-zA-Z0-9]*).*}')).group(0).decode()[5:]
if match == str(data[0]):
print('\n Sent message and received message are equal \n')
sys.stdout.flush()
else:
raise ValueError(
'DUT received string do not match sent string, \nexpected: {}\nwith length {}\
\nreceived: {}\nwith length {}'.format(
data[0], len(data[0]), match, len(match)))
# Test for receiving long messages:
# This sends a message with a specified length (2000 characters) to ensure the WebSocket can handle large data payloads. Repeated 3 times for reliability.
def test_recv_long_msg(dut, websocket, msg_len, repeats):
send_msg = ''.join(
random.choice(string.ascii_uppercase + string.ascii_lowercase +
string.digits) for _ in range(msg_len))
for _ in range(repeats):
websocket.send_data(send_msg)
recv_msg = ''
while len(recv_msg) < msg_len:
match = dut.expect(re.compile(
b'Received=([a-zA-Z0-9]*).*\n')).group(1).decode()
recv_msg += match
if recv_msg == send_msg:
print('\n Sent message and received message are equal \n')
sys.stdout.flush()
else:
raise ValueError(
'DUT received string do not match sent string, \nexpected: {}\nwith length {}\
\nreceived: {}\nwith length {}'.format(
send_msg, len(send_msg), recv_msg, len(recv_msg)))
# Test for receiving the first fragment of a large message:
# Verifies the WebSocket's ability to correctly process the initial segment of a fragmented message.
def test_recv_fragmented_msg1(dut):
dut.expect('Total payload length=2000, data_len=1024')
# Test for receiving fragmented text messages:
# Checks if the WebSocket can accurately reconstruct a message sent in several smaller parts.
def test_fragmented_txt_msg(dut):
dut.expect('Received=' + 32 * 'a' + 32 * 'b')
print('\nFragmented data received\n')
# Extract the hexdump portion of the log line
def parse_hexdump(line):
match = re.search(r'\(.*\) Received binary data: ([0-9A-Fa-f ]+)', line)
if match:
hexdump = match.group(1).strip().replace(' ', '')
# Convert the hexdump string to a bytearray
return bytearray.fromhex(hexdump)
return bytearray()
# Capture the binary log output from the DUT
def test_fragmented_binary_msg(dut):
match = dut.expect(r'\(.*\) Received binary data: .*')
if match:
line = match.group(0).strip()
if isinstance(line, bytes):
line = line.decode('utf-8')
# Parse the hexdump from the log line
received_data = parse_hexdump(line)
# Create the expected bytearray with the specified pattern
expected_data = bytearray([0, 0, 0, 0, 0, 1, 1, 1, 1, 1])
# Validate the received data
assert received_data == expected_data, f'Received data does not match expected data. Received: {received_data}, Expected: {expected_data}'
print('\nFragmented data received\n')
else:
assert False, 'Log line with binary data not found'
# Starting of the test
try:
if dut.app.sdkconfig.get('WEBSOCKET_URI_FROM_STDIN') is True:
uri_from_stdin = True
else:
uri = dut.app.sdkconfig['WEBSOCKET_URI']
uri_from_stdin = False
if dut.app.sdkconfig.get('WS_OVER_TLS_MUTUAL_AUTH') is True:
use_tls = True
client_verify = True
else:
use_tls = False
client_verify = False
except Exception:
print('ENV_TEST_FAILURE: Cannot find uri settings in sdkconfig')
raise
if uri_from_stdin:
server_port = 8080
with Websocket(server_port, use_tls, client_verify) as ws:
uri = '{}'.format(get_my_ip())
print('DUT connecting to {}'.format(uri))
dut.expect('Please enter uri of websocket endpoint', timeout=30)
dut.write(uri)
test_echo(dut)
test_recv_long_msg(dut, ws, 2000, 3)
test_json(dut, ws)
test_fragmented_txt_msg(dut)
test_fragmented_binary_msg(dut)
test_recv_fragmented_msg1(dut)
test_close(dut)
else:
print('DUT connecting to {}'.format(uri))
test_echo(dut)

View File

@ -0,0 +1,13 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_TARGET_LINUX=n
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120

View File

@ -0,0 +1,18 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_TARGET_LINUX=n
CONFIG_WEBSOCKET_URI_FROM_STDIN=y
CONFIG_WEBSOCKET_URI_FROM_STRING=n
CONFIG_WEBSOCKET_PORT=8080
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_WS_OVER_TLS_MUTUAL_AUTH=y
CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120

View File

@ -0,0 +1,18 @@
CONFIG_IDF_TARGET="esp32"
CONFIG_IDF_TARGET_LINUX=n
CONFIG_WEBSOCKET_URI_FROM_STDIN=y
CONFIG_WEBSOCKET_URI_FROM_STRING=n
CONFIG_WEBSOCKET_PORT=8080
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
CONFIG_EXAMPLE_CONNECT_IPV6=y
CONFIG_WS_OVER_TLS_MUTUAL_AUTH=n
CONFIG_WS_OVER_TLS_SERVER_AUTH=n
CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120

View File

@ -0,0 +1,5 @@
version: "4.3.3"
url: https://github.com/espressif/esp-protocols/tree/master/components/libwebsockets
description: The component provides a simple ESP-IDF port of libwebsockets client.
dependencies:
idf: '>=5.3'

View File

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "stdio.h"
#include <libwebsockets.h>
/*
* External function prototype for the wrapped 'mbedtls_ssl_handshake_step'.
* The "real" function is not being called, this prototype is just to improve
* the code readability.
*/
extern int __real_mbedtls_ssl_handshake_step(mbedtls_ssl_context *ssl);
int __wrap_mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl )
{
int ret = 0;
while (ssl->MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
ret = __real_mbedtls_ssl_handshake_step(ssl);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
continue;
}
if (ret != 0) {
break;
}
}
return ret;
}
/*
* External function prototype for the wrapped 'lws_adopt_descriptor_vhost'.
* The "real" function is not being called, this prototype is just to improve
* the code readability.
*/
extern struct lws *__real_lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, lws_sock_file_fd_type fd, const char *vh_prot_name, struct lws *parent);
struct lws *__wrap_lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, lws_sock_file_fd_type fd, const char *vh_prot_name, struct lws *parent)
{
lws_adopt_desc_t info;
char nullstr[] = "(null)";
memset(&info, 0, sizeof(info));
info.vh = vh;
info.type = type;
info.fd = fd;
info.vh_prot_name = vh_prot_name;
info.parent = parent;
info.fi_wsi_name = nullstr;
return lws_adopt_descriptor_vhost_via_info(&info);
}

View File

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(mdns): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py mdns
tag_format: mdns-v$version
version: 1.4.2
version: 1.8.0
version_files:
- idf_component.yml

View File

@ -1,5 +1,86 @@
# Changelog
## [1.8.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.0)
### Features
- Add version keys ([e01e67e7](https://github.com/espressif/esp-protocols/commit/e01e67e7))
### Bug Fixes
- Reformat mdns sources per indent-cont=120 ([c7663cde](https://github.com/espressif/esp-protocols/commit/c7663cde))
## [1.7.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.7.0)
### Features
- Support user defined allocators ([88162d1f](https://github.com/espressif/esp-protocols/commit/88162d1f))
- Allow allocate memory with configured caps ([7d29b476](https://github.com/espressif/esp-protocols/commit/7d29b476))
### Bug Fixes
- Adjust some formatting per indent-cont=120 ([5b2077e3](https://github.com/espressif/esp-protocols/commit/5b2077e3))
## [1.6.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.6.0)
### Features
- support allocating mDNS task from SPIRAM ([8fcad10c](https://github.com/espressif/esp-protocols/commit/8fcad10c))
### Bug Fixes
- Use correct task delete function ([eb4ab524](https://github.com/espressif/esp-protocols/commit/eb4ab524))
### Updated
- ci(mdns): Fix mdns host test layers with static task creation ([0690eba3](https://github.com/espressif/esp-protocols/commit/0690eba3))
## [1.5.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.3)
### Bug Fixes
- Fix responder to ignore only invalid queries ([cd07228f](https://github.com/espressif/esp-protocols/commit/cd07228f), [#754](https://github.com/espressif/esp-protocols/issues/754))
## [1.5.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.2)
### Bug Fixes
- Fix potential NULL deref when sending sub-buy ([e7273c46](https://github.com/espressif/esp-protocols/commit/e7273c46))
- Fix _mdns_append_fqdn excessive stack usage ([bd23c233](https://github.com/espressif/esp-protocols/commit/bd23c233))
## [1.5.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.1)
### Bug Fixes
- Fix incorrect memory free for mdns browse ([4451a8c5](https://github.com/espressif/esp-protocols/commit/4451a8c5))
## [1.5.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.5.0)
### Features
- supported removal of subtype when updating service ([4ad88e29](https://github.com/espressif/esp-protocols/commit/4ad88e29))
### Bug Fixes
- Fix zero-sized VLA clang-tidy warnings ([196198ec](https://github.com/espressif/esp-protocols/commit/196198ec))
- Remove dead store to arg variable shared ([e838bf03](https://github.com/espressif/esp-protocols/commit/e838bf03))
- Fix name mangling not to use strcpy() ([99b54ac3](https://github.com/espressif/esp-protocols/commit/99b54ac3))
- Fix potential null derefernce in _mdns_execute_action() ([f5be2f41](https://github.com/espressif/esp-protocols/commit/f5be2f41))
- Fix AFL test mock per espressif/esp-idf@a5bc08fb55c ([3d8835cf](https://github.com/espressif/esp-protocols/commit/3d8835cf))
- Fixed potential out-of-bound interface error ([24f55ce9](https://github.com/espressif/esp-protocols/commit/24f55ce9))
- Fixed incorrect error conversion ([8f8516cc](https://github.com/espressif/esp-protocols/commit/8f8516cc))
- Fixed potential overflow when allocating txt data ([75a8e864](https://github.com/espressif/esp-protocols/commit/75a8e864))
- Move MDNS_NAME_BUF_LEN to public headers ([907087c0](https://github.com/espressif/esp-protocols/commit/907087c0), [#724](https://github.com/espressif/esp-protocols/issues/724))
- Cleanup includes in mdns.c ([68a9e148](https://github.com/espressif/esp-protocols/commit/68a9e148), [#725](https://github.com/espressif/esp-protocols/issues/725))
- Allow advertizing service with port==0 ([827ea65f](https://github.com/espressif/esp-protocols/commit/827ea65f))
- Fixed complier warning if MDNS_MAX_SERVICES==0 ([95377216](https://github.com/espressif/esp-protocols/commit/95377216), [#611](https://github.com/espressif/esp-protocols/issues/611))
## [1.4.3](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.3)
### Features
- support zero item when update subtype ([5bd82c01](https://github.com/espressif/esp-protocols/commit/5bd82c01))
## [1.4.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.4.2)
### Features

View File

@ -10,15 +10,17 @@ else()
set(MDNS_CONSOLE "")
endif()
set(MDNS_MEMORY "mdns_mem_caps.c")
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
set(dependencies esp_netif_linux esp_event)
set(private_dependencies esp_timer console esp_system)
set(srcs "mdns.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE})
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
else()
set(dependencies lwip console esp_netif)
set(private_dependencies esp_timer esp_wifi)
set(srcs "mdns.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE})
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
endif()
idf_component_register(
@ -32,7 +34,9 @@ if(${target} STREQUAL "linux")
target_link_libraries(${COMPONENT_LIB} PRIVATE "-lbsd")
endif()
if(CONFIG_ETH_ENABLED)
idf_component_optional_requires(PRIVATE esp_eth)
endif()
idf_component_get_property(MDNS_VERSION ${COMPONENT_NAME} COMPONENT_VERSION)
target_compile_definitions(${COMPONENT_LIB} PUBLIC "-DESP_MDNS_VERSION_NUMBER=\"${MDNS_VERSION}\"")

View File

@ -61,6 +61,48 @@ menu "mDNS"
default 0x0 if MDNS_TASK_AFFINITY_CPU0
default 0x1 if MDNS_TASK_AFFINITY_CPU1
menu "MDNS Memory Configuration"
choice MDNS_TASK_MEMORY_ALLOC_FROM
prompt "Select mDNS task create on which type of memory"
default MDNS_TASK_CREATE_FROM_INTERNAL
config MDNS_TASK_CREATE_FROM_SPIRAM
bool "mDNS task creates on the SPIRAM (READ HELP)"
depends on (SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC)
help
mDNS task creates on the SPIRAM.
This option requires FreeRTOS component to allow creating
tasks on the external memory.
Please read the documentation about FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
config MDNS_TASK_CREATE_FROM_INTERNAL
bool "mDNS task creates on the internal RAM"
endchoice
choice MDNS_MEMORY_ALLOC_FROM
prompt "Select mDNS memory allocation type"
default MDNS_MEMORY_ALLOC_INTERNAL
config MDNS_MEMORY_ALLOC_SPIRAM
bool "Allocate mDNS memory from SPIRAM"
depends on (SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC)
config MDNS_MEMORY_ALLOC_INTERNAL
bool "Allocate mDNS memory from internal RAM"
endchoice
config MDNS_MEMORY_CUSTOM_IMPL
bool "Implement custom memory functions"
default n
help
Enable to implement custom memory functions for mDNS library.
This option is useful when the application wants to use custom
memory allocation functions for mDNS library.
endmenu # MDNS Memory Configuration
config MDNS_SERVICE_ADD_TIMEOUT_MS
int "mDNS adding service timeout (ms)"
range 10 30000

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -44,12 +44,12 @@ static void initialise_mdns(void)
char *hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
ESP_ERROR_CHECK(mdns_init());
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_ERROR_CHECK(mdns_hostname_set(hostname));
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE) );
ESP_ERROR_CHECK(mdns_instance_name_set(EXAMPLE_MDNS_INSTANCE));
//structure with TXT records
mdns_txt_item_t serviceTxtData[3] = {
@ -59,10 +59,10 @@ static void initialise_mdns(void)
};
//initialize service
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3) );
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, serviceTxtData, 3));
ESP_ERROR_CHECK(mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server"));
#if CONFIG_MDNS_MULTIPLE_INSTANCE
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0) );
ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer1", "_http", "_tcp", 80, NULL, 0));
#endif
#if CONFIG_MDNS_PUBLISH_DELEGATE_HOST
@ -78,15 +78,15 @@ static void initialise_mdns(void)
addr6.addr.type = ESP_IPADDR_TYPE_V6;
addr4.next = &addr6;
addr6.next = NULL;
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3) );
ESP_ERROR_CHECK(mdns_delegate_hostname_add(delegated_hostname, &addr4));
ESP_ERROR_CHECK(mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, serviceTxtData, 3));
free(delegated_hostname);
#endif // CONFIG_MDNS_PUBLISH_DELEGATE_HOST
//add another TXT item
ESP_ERROR_CHECK( mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar") );
ESP_ERROR_CHECK(mdns_service_txt_item_set("_http", "_tcp", "path", "/foobar"));
//change TXT item value
ESP_ERROR_CHECK( mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")) );
ESP_ERROR_CHECK(mdns_service_txt_item_set_with_explicit_value_len("_http", "_tcp", "u", "admin", strlen("admin")));
free(hostname);
}
@ -314,6 +314,8 @@ void app_main(void)
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_LOGI(TAG, "mDNS Ver: %s", ESP_MDNS_VERSION_NUMBER);
initialise_mdns();
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
@ -375,12 +377,12 @@ static void query_mdns_host_with_gethostbyname(char *host)
while (res->h_addr_list[i] != NULL) {
ESP_LOGI(TAG, "gethostbyname: %s resolved to: %s", host,
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_LWIP_IPV4)
res->h_addrtype == AF_INET ? inet_ntoa(*(struct in_addr *) (res->h_addr_list[i])) :
inet6_ntoa(*(struct in6_addr *) (res->h_addr_list[i]))
res->h_addrtype == AF_INET ? inet_ntoa(*(struct in_addr *)(res->h_addr_list[i])) :
inet6_ntoa(*(struct in6_addr *)(res->h_addr_list[i]))
#elif defined(CONFIG_LWIP_IPV6)
inet6_ntoa(*(struct in6_addr *) (res->h_addr_list[i]))
inet6_ntoa(*(struct in6_addr *)(res->h_addr_list[i]))
#else
inet_ntoa(*(struct in_addr *) (res->h_addr_list[i]))
inet_ntoa(*(struct in_addr *)(res->h_addr_list[i]))
#endif
);
i++;

View File

@ -1,4 +1,4 @@
version: "1.4.2"
version: "1.8.0"
description: "Multicast UDP service used to provide local network service and host discovery."
url: "https://github.com/espressif/esp-protocols/tree/master/components/mdns"
issues: "https://github.com/espressif/esp-protocols/issues"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,6 +10,7 @@
extern "C" {
#endif
#include "sdkconfig.h"
#include <esp_netif.h>
#define MDNS_TYPE_A 0x0001
@ -21,6 +22,13 @@ extern "C" {
#define MDNS_TYPE_NSEC 0x002F
#define MDNS_TYPE_ANY 0x00FF
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" )
#else
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#endif
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
/**
* @brief Asynchronous query handle
*/
@ -365,7 +373,7 @@ esp_err_t mdns_service_instance_name_set(const char *service_type, const char *p
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_instance_name_set_for_host(const char *instance_old, const char *service_type, const char *proto, const char *hostname,
const char *instance_name);
const char *instance_name);
/**
* @brief Set service port
@ -399,7 +407,7 @@ esp_err_t mdns_service_port_set(const char *service_type, const char *proto, uin
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_port_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
uint16_t port);
uint16_t port);
/**
* @brief Replace all TXT items for service
@ -474,7 +482,7 @@ esp_err_t mdns_service_txt_item_set(const char *service_type, const char *proto,
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service_type, const char *proto,
const char *key, const char *value, uint8_t value_len);
const char *key, const char *value, uint8_t value_len);
/**
* @brief Set/Add TXT item for service TXT record with hostname
@ -495,7 +503,7 @@ esp_err_t mdns_service_txt_item_set_with_explicit_value_len(const char *service_
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
const char *key, const char *value);
const char *key, const char *value);
/**
* @brief Set/Add TXT item for service TXT record with hostname and txt value length
@ -515,8 +523,8 @@ esp_err_t mdns_service_txt_item_set_for_host(const char *instance, const char *s
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_set_for_host_with_explicit_value_len(const char *instance, const char *service_type, const char *proto,
const char *hostname, const char *key,
const char *value, uint8_t value_len);
const char *hostname, const char *key,
const char *value, uint8_t value_len);
/**
* @brief Remove TXT item for service TXT record
@ -549,7 +557,7 @@ esp_err_t mdns_service_txt_item_remove(const char *service_type, const char *pro
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_txt_item_remove_for_host(const char *instance, const char *service_type, const char *proto, const char *hostname,
const char *key);
const char *key);
/**
* @brief Add a subtype for service.
@ -567,7 +575,7 @@ esp_err_t mdns_service_txt_item_remove_for_host(const char *instance, const char
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
const char *hostname, const char *subtype);
/**
* @brief Remove a subtype for service.
@ -584,7 +592,7 @@ esp_err_t mdns_service_subtype_add_for_host(const char *instance_name, const cha
* - ESP_ERR_NOT_FOUND Service not found
*/
esp_err_t mdns_service_subtype_remove_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, const char *subtype);
const char *hostname, const char *subtype);
/**
* @brief Add multiple subtypes for service at once.
@ -603,7 +611,7 @@ esp_err_t mdns_service_subtype_remove_for_host(const char *instance_name, const
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
/**
* @brief Update subtype for service.
@ -615,6 +623,8 @@ esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_
* @param subtype the pointer of subtype array to add.
* @param num_items number of items in subtype array
*
* @note If `num_items` is 0, then remove all subtypes.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
@ -622,7 +632,7 @@ esp_err_t mdns_service_subtype_add_multiple_items_for_host(const char *instance_
* - ESP_ERR_NO_MEM memory error
*/
esp_err_t mdns_service_subtype_update_multiple_items_for_host(const char *instance_name, const char *service_type, const char *proto,
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
const char *hostname, mdns_subtype_item_t subtype[], uint8_t num_items);
/**
* @brief Remove and free all services from mDNS server
*
@ -676,7 +686,7 @@ bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout,
* NULL otherwise.
*/
mdns_search_once_t *mdns_query_async_new(const char *name, const char *service_type, const char *proto, uint16_t type,
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier);
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier);
/**
* @brief Generic mDNS query
@ -815,7 +825,7 @@ esp_err_t mdns_lookup_delegated_service(const char *instance, const char *servic
* - ESP_ERR_INVALID_ARG parameter error
*/
esp_err_t mdns_lookup_selfhosted_service(const char *instance, const char *service_type, const char *proto, size_t max_results,
mdns_result_t **result);
mdns_result_t **result);
/**
* @brief Query mDNS for A record

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,6 +10,7 @@
#include "mdns.h"
#include "mdns_private.h"
#include "inttypes.h"
#include "mdns_mem_caps.h"
static const char *ip_protocol_str[] = {"V4", "V6", "MAX"};
@ -110,7 +111,7 @@ static void register_mdns_query_a(void)
.argtable = &mdns_query_a_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
#endif /* CONFIG_LWIP_IPV4 */
@ -169,7 +170,7 @@ static void register_mdns_query_aaaa(void)
.argtable = &mdns_query_a_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
#endif /* CONFIG_LWIP_IPV6 */
@ -231,7 +232,7 @@ static void register_mdns_query_srv(void)
.argtable = &mdns_query_srv_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static struct {
@ -293,7 +294,7 @@ static void register_mdns_query_txt(void)
.argtable = &mdns_query_txt_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static struct {
@ -359,7 +360,7 @@ static void register_mdns_query_ptr(void)
.argtable = &mdns_query_ptr_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static struct {
@ -427,7 +428,7 @@ static void register_mdns_query_ip(void)
.argtable = &mdns_query_ip_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static struct {
@ -496,7 +497,7 @@ static void register_mdns_query_svc(void)
.argtable = &mdns_query_svc_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static struct {
@ -513,15 +514,15 @@ static int cmd_mdns_init(int argc, char **argv)
return 1;
}
ESP_ERROR_CHECK( mdns_init() );
ESP_ERROR_CHECK(mdns_init());
if (mdns_init_args.hostname->sval[0]) {
ESP_ERROR_CHECK( mdns_hostname_set(mdns_init_args.hostname->sval[0]) );
ESP_ERROR_CHECK(mdns_hostname_set(mdns_init_args.hostname->sval[0]));
printf("MDNS: Hostname: %s\n", mdns_init_args.hostname->sval[0]);
}
if (mdns_init_args.instance->count) {
ESP_ERROR_CHECK( mdns_instance_name_set(mdns_init_args.instance->sval[0]) );
ESP_ERROR_CHECK(mdns_instance_name_set(mdns_init_args.instance->sval[0]));
printf("MDNS: Instance: %s\n", mdns_init_args.instance->sval[0]);
}
@ -542,7 +543,7 @@ static void register_mdns_init(void)
.argtable = &mdns_init_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_init) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_init));
}
static int cmd_mdns_free(int argc, char **argv)
@ -561,7 +562,7 @@ static void register_mdns_free(void)
.argtable = NULL
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_free) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_free));
}
static struct {
@ -582,7 +583,7 @@ static int cmd_mdns_set_hostname(int argc, char **argv)
return 1;
}
ESP_ERROR_CHECK( mdns_hostname_set(mdns_set_hostname_args.hostname->sval[0]) );
ESP_ERROR_CHECK(mdns_hostname_set(mdns_set_hostname_args.hostname->sval[0]));
return 0;
}
@ -599,7 +600,7 @@ static void register_mdns_set_hostname(void)
.argtable = &mdns_set_hostname_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_set_hostname) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_set_hostname));
}
static struct {
@ -620,7 +621,7 @@ static int cmd_mdns_set_instance(int argc, char **argv)
return 1;
}
ESP_ERROR_CHECK( mdns_instance_name_set(mdns_set_instance_args.instance->sval[0]) );
ESP_ERROR_CHECK(mdns_instance_name_set(mdns_set_instance_args.instance->sval[0]));
return 0;
}
@ -637,14 +638,14 @@ static void register_mdns_set_instance(void)
.argtable = &mdns_set_instance_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_set_instance) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_set_instance));
}
static mdns_txt_item_t *_convert_items(const char **values, int count)
{
int i = 0, e;
const char *value = NULL;
mdns_txt_item_t *items = (mdns_txt_item_t *) malloc(sizeof(mdns_txt_item_t) * count);
mdns_txt_item_t *items = (mdns_txt_item_t *) mdns_mem_malloc(sizeof(mdns_txt_item_t) * count);
if (!items) {
printf("ERROR: No Memory!\n");
goto fail;
@ -661,15 +662,15 @@ static mdns_txt_item_t *_convert_items(const char **values, int count)
}
int var_len = esign - value;
int val_len = strlen(value) - var_len - 1;
char *var = (char *)malloc(var_len + 1);
char *var = (char *)mdns_mem_malloc(var_len + 1);
if (var == NULL) {
printf("ERROR: No Memory!\n");
goto fail;
}
char *val = (char *)malloc(val_len + 1);
char *val = (char *)mdns_mem_malloc(val_len + 1);
if (val == NULL) {
printf("ERROR: No Memory!\n");
free(var);
mdns_mem_free(var);
goto fail;
}
memcpy(var, value, var_len);
@ -685,10 +686,10 @@ static mdns_txt_item_t *_convert_items(const char **values, int count)
fail:
for (e = 0; e < i; e++) {
free((char *)items[e].key);
free((char *)items[e].value);
mdns_mem_free((char *)items[e].key);
mdns_mem_free((char *)items[e].value);
}
free(items);
mdns_mem_free(items);
return NULL;
}
@ -734,9 +735,9 @@ static int cmd_mdns_service_add(int argc, char **argv)
}
}
ESP_ERROR_CHECK( mdns_service_add_for_host(instance, mdns_add_args.service->sval[0], mdns_add_args.proto->sval[0],
host, mdns_add_args.port->ival[0], items, mdns_add_args.txt->count) );
free(items);
ESP_ERROR_CHECK(mdns_service_add_for_host(instance, mdns_add_args.service->sval[0], mdns_add_args.proto->sval[0],
host, mdns_add_args.port->ival[0], items, mdns_add_args.txt->count));
mdns_mem_free(items);
return 0;
}
@ -758,7 +759,7 @@ static void register_mdns_service_add(void)
.argtable = &mdns_add_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_add));
}
static struct {
@ -791,7 +792,7 @@ static int cmd_mdns_service_remove(int argc, char **argv)
host = mdns_remove_args.host->sval[0];
}
ESP_ERROR_CHECK( mdns_service_remove_for_host(instance, mdns_remove_args.service->sval[0], mdns_remove_args.proto->sval[0], host) );
ESP_ERROR_CHECK(mdns_service_remove_for_host(instance, mdns_remove_args.service->sval[0], mdns_remove_args.proto->sval[0], host));
return 0;
}
@ -811,7 +812,7 @@ static void register_mdns_service_remove(void)
.argtable = &mdns_remove_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_remove) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_remove));
}
static struct {
@ -869,7 +870,7 @@ static void register_mdns_service_instance_set(void)
.argtable = &mdns_service_instance_set_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_add));
}
static struct {
@ -927,7 +928,7 @@ static void register_mdns_service_port_set(void)
.argtable = &mdns_service_port_set_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_add) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_add));
}
static struct {
@ -970,8 +971,8 @@ static int cmd_mdns_service_txt_replace(int argc, char **argv)
}
}
ESP_ERROR_CHECK( mdns_service_txt_set_for_host(instance, mdns_txt_replace_args.service->sval[0], mdns_txt_replace_args.proto->sval[0], host, items, mdns_txt_replace_args.txt->count) );
free(items);
ESP_ERROR_CHECK(mdns_service_txt_set_for_host(instance, mdns_txt_replace_args.service->sval[0], mdns_txt_replace_args.proto->sval[0], host, items, mdns_txt_replace_args.txt->count));
mdns_mem_free(items);
return 0;
}
@ -992,7 +993,7 @@ static void register_mdns_service_txt_replace(void)
.argtable = &mdns_txt_replace_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_set) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_txt_set));
}
static struct {
@ -1028,7 +1029,7 @@ static int cmd_mdns_service_txt_set(int argc, char **argv)
printf("MDNS: Service for delegated host: %s\n", host);
}
ESP_ERROR_CHECK( mdns_service_txt_item_set_for_host(instance, mdns_txt_set_args.service->sval[0], mdns_txt_set_args.proto->sval[0], host, mdns_txt_set_args.var->sval[0], mdns_txt_set_args.value->sval[0]) );
ESP_ERROR_CHECK(mdns_service_txt_item_set_for_host(instance, mdns_txt_set_args.service->sval[0], mdns_txt_set_args.proto->sval[0], host, mdns_txt_set_args.var->sval[0], mdns_txt_set_args.value->sval[0]));
return 0;
}
@ -1050,7 +1051,7 @@ static void register_mdns_service_txt_set(void)
.argtable = &mdns_txt_set_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_set) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_txt_set));
}
static struct {
@ -1082,7 +1083,7 @@ static int cmd_mdns_service_txt_remove(int argc, char **argv)
if (mdns_txt_remove_args.host->count && mdns_txt_remove_args.host->sval[0]) {
host = mdns_txt_remove_args.host->sval[0];
}
ESP_ERROR_CHECK( mdns_service_txt_item_remove_for_host(instance, mdns_txt_remove_args.service->sval[0], mdns_txt_remove_args.proto->sval[0], host, mdns_txt_remove_args.var->sval[0]) );
ESP_ERROR_CHECK(mdns_service_txt_item_remove_for_host(instance, mdns_txt_remove_args.service->sval[0], mdns_txt_remove_args.proto->sval[0], host, mdns_txt_remove_args.var->sval[0]));
return 0;
}
@ -1103,7 +1104,7 @@ static void register_mdns_service_txt_remove(void)
.argtable = &mdns_txt_remove_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_txt_remove) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_txt_remove));
}
static int cmd_mdns_service_remove_all(int argc, char **argv)
@ -1122,7 +1123,7 @@ static void register_mdns_service_remove_all(void)
.argtable = NULL
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_free) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_free));
}
#define MDNS_MAX_LOOKUP_RESULTS CONFIG_MDNS_MAX_SERVICES
@ -1189,7 +1190,7 @@ static void register_mdns_lookup_service(void)
.argtable = &mdns_lookup_service_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_lookup_service) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_lookup_service));
}
static struct {
@ -1237,7 +1238,7 @@ static void register_mdns_delegate_host(void)
.argtable = &mdns_delegate_host_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_delegate_host) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_delegate_host));
}
static struct {
@ -1278,7 +1279,7 @@ static void register_mdns_undelegate_host(void)
.argtable = &mdns_undelegate_host_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_undelegate_host) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_undelegate_host));
}
static struct {
@ -1310,7 +1311,7 @@ static int cmd_mdns_service_subtype(int argc, char **argv)
if (mdns_service_subtype_args.host->count && mdns_service_subtype_args.host->sval[0]) {
host = mdns_service_subtype_args.host->sval[0];
}
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host(instance, mdns_service_subtype_args.service->sval[0], mdns_service_subtype_args.proto->sval[0], host, mdns_service_subtype_args.sub->sval[0]) );
ESP_ERROR_CHECK(mdns_service_subtype_add_for_host(instance, mdns_service_subtype_args.service->sval[0], mdns_service_subtype_args.proto->sval[0], host, mdns_service_subtype_args.sub->sval[0]));
return 0;
}
@ -1331,7 +1332,7 @@ static void register_mdns_service_subtype_set(void)
.argtable = &mdns_service_subtype_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_service_sub) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_service_sub));
}
static struct {
@ -1377,7 +1378,7 @@ static void register_mdns_browse(void)
.argtable = &mdns_browse_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_browse) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_browse));
}
static int cmd_mdns_browse_del(int argc, char **argv)
@ -1410,7 +1411,7 @@ static void register_mdns_browse_del(void)
.argtable = &mdns_browse_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_browse_del) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_browse_del));
}
void mdns_console_register(void)

View File

@ -0,0 +1,96 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "sdkconfig.h"
#include "mdns_private.h"
#include "mdns_mem_caps.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#if CONFIG_MDNS_MEMORY_CUSTOM_IMPL
#define ALLOW_WEAK __attribute__((weak))
#else
#define ALLOW_WEAK
#endif
#if CONFIG_MDNS_TASK_CREATE_FROM_SPIRAM
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "SPIRAM"
#endif
#if CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "internal RAM"
#endif
#if CONFIG_MDNS_MEMORY_ALLOC_SPIRAM
#define MDNS_MEMORY_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
#endif
#if CONFIG_MDNS_MEMORY_ALLOC_INTERNAL
#define MDNS_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
// Allocate memory from internal heap as default.
#ifndef MDNS_MEMORY_CAPS
#warning "No memory allocation method defined, using internal memory"
#define MDNS_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
#ifndef MDNS_TASK_MEMORY_CAPS
#define MDNS_TASK_MEMORY_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define MDNS_TASK_MEMORY_LOG "internal RAM"
#endif
void ALLOW_WEAK *mdns_mem_malloc(size_t size)
{
return heap_caps_malloc(size, MDNS_MEMORY_CAPS);
}
void ALLOW_WEAK *mdns_mem_calloc(size_t num, size_t size)
{
return heap_caps_calloc(num, size, MDNS_MEMORY_CAPS);
}
void ALLOW_WEAK mdns_mem_free(void *ptr)
{
heap_caps_free(ptr);
}
char ALLOW_WEAK *mdns_mem_strdup(const char *s)
{
if (!s) {
return NULL;
}
size_t len = strlen(s) + 1;
char *copy = (char *)heap_caps_malloc(len, MDNS_MEMORY_CAPS);
if (copy) {
memcpy(copy, s, len);
}
return copy;
}
char ALLOW_WEAK *mdns_mem_strndup(const char *s, size_t n)
{
if (!s) {
return NULL;
}
size_t len = strnlen(s, n);
char *copy = (char *)heap_caps_malloc(len + 1, MDNS_MEMORY_CAPS);
if (copy) {
memcpy(copy, s, len);
copy[len] = '\0';
}
return copy;
}
void ALLOW_WEAK *mdns_mem_task_malloc(size_t size)
{
ESP_LOGI("mdns_mem", "mDNS task will be created from %s", MDNS_TASK_MEMORY_LOG);
return heap_caps_malloc(size, MDNS_TASK_MEMORY_CAPS);
}
void ALLOW_WEAK mdns_mem_task_free(void *ptr)
{
heap_caps_free(ptr);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -20,6 +20,7 @@
#include "esp_event.h"
#include "mdns_networking.h"
#include "esp_netif_net_stack.h"
#include "mdns_mem_caps.h"
/*
* MDNS Server Networking
@ -143,7 +144,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
pb = pb->next;
this_pb->next = NULL;
mdns_rx_packet_t *packet = (mdns_rx_packet_t *)malloc(sizeof(mdns_rx_packet_t));
mdns_rx_packet_t *packet = (mdns_rx_packet_t *)mdns_mem_malloc(sizeof(mdns_rx_packet_t));
if (!packet) {
HOOK_MALLOC_FAILED;
//missed packet - no memory
@ -188,7 +189,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
bool found = false;
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
if (s_interfaces[i].proto && netif && netif == ip_current_input_netif ()) {
if (s_interfaces[i].proto && netif && netif == ip_current_input_netif()) {
#if LWIP_IPV4
if (packet->src.type == IPADDR_TYPE_V4) {
if ((packet->src.u_addr.ip4.addr & ip_2_ip4(&netif->netmask)->addr) != (ip_2_ip4(&netif->ip_addr)->addr & ip_2_ip4(&netif->netmask)->addr)) {
@ -205,7 +206,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
if (!found || _mdns_send_rx_action(packet) != ESP_OK) {
pbuf_free(this_pb);
free(packet);
mdns_mem_free(packet);
}
}
@ -288,7 +289,7 @@ typedef struct {
static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg)
{
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol);
msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol) == ESP_OK ? ERR_OK : ERR_IF;
return msg->err;
}
@ -338,7 +339,7 @@ static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
msg->err = ERR_IF;
return ERR_IF;
}
esp_err_t err = udp_sendto_if (_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
esp_err_t err = udp_sendto_if(_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
pbuf_free(msg->pbt);
msg->err = err;
return err;
@ -393,5 +394,5 @@ size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
pbuf_free(packet->pb);
free(packet);
mdns_mem_free(packet);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -21,6 +21,7 @@
#include <unistd.h>
#include <sys/param.h>
#include "esp_log.h"
#include "mdns_mem_caps.h"
#if defined(CONFIG_IDF_TARGET_LINUX)
#include <sys/ioctl.h>
@ -87,9 +88,9 @@ size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
void _mdns_packet_free(mdns_rx_packet_t *packet)
{
free(packet->pb->payload);
free(packet->pb);
free(packet);
mdns_mem_free(packet->pb->payload);
mdns_mem_free(packet->pb);
mdns_mem_free(packet);
}
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
@ -297,13 +298,13 @@ void sock_recv_task(void *arg)
inet_to_espaddr(&raddr, &addr, &port);
// Allocate the packet structure and pass it to the mdns main engine
mdns_rx_packet_t *packet = (mdns_rx_packet_t *) calloc(1, sizeof(mdns_rx_packet_t));
struct pbuf *packet_pbuf = calloc(1, sizeof(struct pbuf));
uint8_t *buf = malloc(len);
if (packet == NULL || packet_pbuf == NULL || buf == NULL ) {
free(buf);
free(packet_pbuf);
free(packet);
mdns_rx_packet_t *packet = (mdns_rx_packet_t *) mdns_mem_calloc(1, sizeof(mdns_rx_packet_t));
struct pbuf *packet_pbuf = mdns_mem_calloc(1, sizeof(struct pbuf));
uint8_t *buf = mdns_mem_malloc(len);
if (packet == NULL || packet_pbuf == NULL || buf == NULL) {
mdns_mem_free(buf);
mdns_mem_free(packet_pbuf);
mdns_mem_free(packet);
HOOK_MALLOC_FAILED;
ESP_LOGE(TAG, "Failed to allocate the mdns packet");
continue;
@ -326,9 +327,9 @@ void sock_recv_task(void *arg)
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
if (_mdns_send_rx_action(packet) != ESP_OK) {
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
free(packet->pb->payload);
free(packet->pb);
free(packet);
mdns_mem_free(packet->pb->payload);
mdns_mem_free(packet->pb);
mdns_mem_free(packet);
}
}
}
@ -341,7 +342,7 @@ static void mdns_networking_init(void)
{
if (s_run_sock_recv_task == false) {
s_run_sock_recv_task = true;
xTaskCreate( sock_recv_task, "mdns recv task", 3 * 1024, NULL, 5, NULL );
xTaskCreate(sock_recv_task, "mdns recv task", 3 * 1024, NULL, 5, NULL);
}
}
@ -392,7 +393,7 @@ static int create_socket(esp_netif_t *netif)
}
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
ESP_LOGE(TAG, "Failed setsockopt() to set SO_REUSEADDR. errno=%d: %s\n", errno, strerror(errno));
}
// Bind the socket to any address
@ -419,7 +420,7 @@ static int create_socket(esp_netif_t *netif)
#endif // CONFIG_LWIP_IPV6
struct ifreq ifr;
esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq));
int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq));
if (ret < 0) {
ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface. errno=%d: %s", esp_netif_get_desc(netif), errno, strerror(errno));
goto err;

View File

@ -0,0 +1,54 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import re
import sys
# Configurable prefix for memory functions
MDNS_MEM_PREFIX = 'mdns_mem_' # Change this to modify the prefix
def add_prefix_to_mem_funcs(content):
# List of memory functions to prefix
mem_funcs = [
'malloc',
'calloc',
'free',
'strdup',
'strndup'
]
# Create regex pattern matching the memory functions but not already prefixed ones
pattern = fr'(?<!{MDNS_MEM_PREFIX})(?<![\w])(' + '|'.join(mem_funcs) + r')(?=\s*\()'
# Replace all occurrences with configured prefix
modified = re.sub(pattern, fr'{MDNS_MEM_PREFIX}\1', content)
return modified
def process_file(filename):
try:
# Read the file
with open(filename, 'r') as f:
content = f.read()
# Add prefixes
modified = add_prefix_to_mem_funcs(content)
# Write back to file
with open(filename, 'w') as f:
f.write(modified)
print(f'Successfully processed {filename}')
except Exception as e:
print(f'Error processing {filename}: {str(e)}')
sys.exit(1)
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python script.py <filename>')
sys.exit(1)
process_file(sys.argv[1])

View File

@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Allocate memory.
* @param size Number of bytes to allocate.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_malloc(size_t size);
/**
* @brief Allocate and zero memory.
* @param num Number of elements.
* @param size Size of each element.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_calloc(size_t num, size_t size);
/**
* @brief Free allocated memory.
* @param ptr Pointer to memory to free.
*/
void mdns_mem_free(void *ptr);
/**
* @brief Duplicate a string.
* @param s String to duplicate.
* @return Pointer to duplicated string, or NULL on failure.
*/
char *mdns_mem_strdup(const char *s);
/**
* @brief Duplicate a string with length limit.
* @param s String to duplicate.
* @param n Maximum number of characters to copy.
* @return Pointer to duplicated string, or NULL on failure.
*/
char *mdns_mem_strndup(const char *s, size_t n);
/**
* @brief Allocate memory for mDNS task.
* @param size Number of bytes to allocate.
* @return Pointer to allocated memory, or NULL on failure.
*/
void *mdns_mem_task_malloc(size_t size);
/**
* @brief Free allocated memory for mDNS task.
* @param ptr Pointer to memory to free.
*/
void mdns_mem_task_free(void *ptr);
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -103,12 +103,6 @@
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
#define MDNS_ACTION_QUEUE_LEN CONFIG_MDNS_ACTION_QUEUE_LEN // Maximum actions pending to the server
#define MDNS_TXT_MAX_LEN 1024 // Maximum string length of text data in TXT record
#if defined(CONFIG_LWIP_IPV6) && defined(CONFIG_MDNS_RESPOND_REVERSE_QUERIES)
#define MDNS_NAME_MAX_LEN (64+4) // Need to account for IPv6 reverse queries (64 char address + ".ip6" )
#else
#define MDNS_NAME_MAX_LEN 64 // Maximum string length of hostname, instance, service and proto
#endif
#define MDNS_NAME_BUF_LEN (MDNS_NAME_MAX_LEN+1) // Maximum char buffer size to hold hostname, instance, service or proto
#define MDNS_MAX_PACKET_SIZE 1460 // Maximum size of mDNS outgoing packet
#define MDNS_HEAD_LEN 12

View File

@ -51,7 +51,7 @@ esp_err_t esp_netif_get_ip_info(esp_netif_t *esp_netif, esp_netif_ip_info_t *ip_
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
char addr[20];
struct sockaddr_in *pAddr = (struct sockaddr_in *) tmp->ifa_addr;
inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr) );
inet_ntop(AF_INET, &pAddr->sin_addr, addr, sizeof(addr));
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
ESP_LOGD(TAG, "AF_INET4: %s: %s\n", tmp->ifa_name, addr);
memcpy(&ip_info->ip.addr, &pAddr->sin_addr, 4);
@ -105,7 +105,7 @@ esp_err_t esp_netif_get_ip6_linklocal(esp_netif_t *esp_netif, esp_ip6_addr_t *if
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET6) {
char addr[64];
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)tmp->ifa_addr;
inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr) );
inet_ntop(AF_INET6, &pAddr->sin6_addr, addr, sizeof(addr));
if (strcmp(esp_netif->if_desc, tmp->ifa_name) == 0) {
ESP_LOGD(TAG, "AF_INET6: %s: %s\n", tmp->ifa_name, addr);
memcpy(if_ip6->addr, &pAddr->sin6_addr, 4 * 4);

View File

@ -111,7 +111,7 @@ static void mdns_test_app(esp_netif_t *interface)
.func = exit_console,
.argtable = NULL
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_exit) );
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_exit));
mdns_console_register();
ESP_ERROR_CHECK(esp_console_start_repl(repl));
xEventGroupWaitBits(s_exit_signal, 1, pdTRUE, pdFALSE, portMAX_DELAY);

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -59,7 +59,7 @@ uint32_t xTaskGetTickCount(void)
}
/// Queue mock
QueueHandle_t xQueueCreate( uint32_t uxQueueLength, uint32_t uxItemSize )
QueueHandle_t xQueueCreate(uint32_t uxQueueLength, uint32_t uxItemSize)
{
g_size = uxItemSize;
g_queue = malloc((uxQueueLength) * (uxItemSize));
@ -67,7 +67,7 @@ QueueHandle_t xQueueCreate( uint32_t uxQueueLength, uint32_t uxItemSize )
}
void vQueueDelete( QueueHandle_t xQueue )
void vQueueDelete(QueueHandle_t xQueue)
{
free(xQueue);
}
@ -117,7 +117,46 @@ void esp_log_write(esp_log_level_t level, const char *tag, const char *format, .
{
}
void esp_log(esp_log_config_t config, const char *tag, const char *format, ...)
{
}
uint32_t esp_log_timestamp(void)
{
return 0;
}
void *mdns_mem_malloc(size_t size)
{
return malloc(size);
}
void *mdns_mem_calloc(size_t num, size_t size)
{
return calloc(num, size);
}
void mdns_mem_free(void *ptr)
{
free(ptr);
}
char *mdns_mem_strdup(const char *s)
{
return strdup(s);
}
char *mdns_mem_strndup(const char *s, size_t n)
{
return strndup(s, n);
}
void *mdns_mem_task_malloc(size_t size)
{
return malloc(size);
}
void mdns_mem_task_free(void *ptr)
{
free(ptr);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -38,6 +38,10 @@
#define ESP_ERR_INVALID_RESPONSE 0x108
#define ESP_ERR_INVALID_CRC 0x109
#define MDNS_TASK_MEMORY_LOG "internal RAM"
#define MALLOC_CAP_8BIT (1<<2)
#define MALLOC_CAP_INTERNAL (1<<11)
#define pdTRUE true
#define pdFALSE false
#define pdPASS ( pdTRUE )
@ -62,6 +66,7 @@
#define vSemaphoreDelete(s) free(s)
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U
#define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1)
#define xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) true
#define vTaskDelay(m) usleep((m)*0)
#define esp_random() (rand()%UINT32_MAX)
@ -79,7 +84,8 @@ typedef void *QueueHandle_t;
typedef void *TaskHandle_t;
typedef int BaseType_t;
typedef uint32_t TickType_t;
typedef void *StackType_t;
typedef void *StaticTask_t;
struct udp_pcb {
uint8_t dummy;
@ -111,10 +117,10 @@ uint32_t xTaskGetTickCount(void);
typedef void (*esp_timer_cb_t)(void *arg);
// Queue mock
QueueHandle_t xQueueCreate( uint32_t uxQueueLength,
uint32_t uxItemSize );
QueueHandle_t xQueueCreate(uint32_t uxQueueLength,
uint32_t uxItemSize);
void vQueueDelete( QueueHandle_t xQueue );
void vQueueDelete(QueueHandle_t xQueue);
uint32_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
@ -131,6 +137,6 @@ esp_err_t esp_event_handler_unregister(const char *event_base, int32_t event_id,
TaskHandle_t xTaskGetCurrentTaskHandle(void);
void xTaskNotifyGive(TaskHandle_t task);
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time );
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
#endif //_ESP32_COMPAT_H_

View File

@ -243,7 +243,7 @@ int main(int argc, char **argv)
//
// Note: parameter1 is a file (mangled packet) which caused the crash
file = fopen(argv[1], "r");
assert(file >= 0 );
assert(file >= 0);
len = fread(buf, 1, 1460, file);
fclose(file);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -55,17 +55,17 @@ static void initialise_mdns(void)
char *hostname = generate_hostname();
//initialize mDNS
ESP_ERROR_CHECK( mdns_init() );
ESP_ERROR_CHECK(mdns_init());
//set mDNS hostname (required if you want to advertise services)
ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
ESP_ERROR_CHECK(mdns_hostname_set(hostname));
ESP_LOGI(TAG, "mdns hostname set to: [%s]", hostname);
//set default mDNS instance name
ESP_ERROR_CHECK( mdns_instance_name_set(CONFIG_TEST_MDNS_INSTANCE) );
ESP_ERROR_CHECK(mdns_instance_name_set(CONFIG_TEST_MDNS_INSTANCE));
//initialize service
ESP_ERROR_CHECK( mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, NULL, 0) );
ESP_ERROR_CHECK(mdns_service_add("ESP32-WebServer", "_http", "_tcp", 80, NULL, 0));
#if CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST
char *delegated_hostname;
@ -80,12 +80,12 @@ static void initialise_mdns(void)
addr6.addr.type = ESP_IPADDR_TYPE_V6;
addr4.next = &addr6;
addr6.next = NULL;
ESP_ERROR_CHECK( mdns_delegate_hostname_add(delegated_hostname, &addr4) );
ESP_ERROR_CHECK( mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, NULL, 0) );
ESP_ERROR_CHECK(mdns_delegate_hostname_add(delegated_hostname, &addr4));
ESP_ERROR_CHECK(mdns_service_add_for_host("test0", "_http", "_tcp", delegated_hostname, 1234, NULL, 0));
free(delegated_hostname);
#endif // CONFIG_TEST_MDNS_PUBLISH_DELEGATE_HOST
ESP_ERROR_CHECK( mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server") );
ESP_ERROR_CHECK(mdns_service_subtype_add_for_host("ESP32-WebServer", "_http", "_tcp", NULL, "_server"));
free(hostname);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -45,17 +45,17 @@ static void yield_to_all_priorities(void)
TEST(mdns, api_fails_with_invalid_state)
{
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) );
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_init());
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME));
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE));
TEST_ASSERT_NOT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
}
TEST(mdns, init_deinit)
{
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
yield_to_all_priorities(); // Make sure that mdns task has executed to complete initialization
mdns_free();
esp_event_loop_delete_default();
@ -76,33 +76,33 @@ TEST(mdns, api_fails_with_expected_err)
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr));
yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname
TEST_ASSERT_TRUE(mdns_hostname_exists(MDNS_DELEGATE_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) );
TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) );
TEST_ASSERT_TRUE(mdns_hostname_exists(MDNS_DELEGATE_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES));
TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add_for_host(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME,
MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) );
TEST_ASSERT_TRUE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, serviceTxtData, CONFIG_MDNS_MAX_SERVICES) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1", "value1") );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1") );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) );
MDNS_SERVICE_PORT, serviceTxtData, CONFIG_MDNS_MAX_SERVICES));
TEST_ASSERT_TRUE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, serviceTxtData, CONFIG_MDNS_MAX_SERVICES));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1", "value1"));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_item_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, "key1"));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080));
yield_to_all_priorities(); // to remove the service with the updated txt records
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO));
yield_to_all_priorities(); // Make sure that mdns task has executed to remove the service
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_remove(MDNS_DELEGATE_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_remove(MDNS_DELEGATE_HOSTNAME));
yield_to_all_priorities(); // Make sure that mdns task has executed to remove the hostname
TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove_all() );
TEST_ASSERT_FALSE(mdns_service_exists(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove_all());
yield_to_all_priorities(); // Make sure that mdns task has executed to remove all services
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080) );
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_port_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 8080));
mdns_free();
esp_event_loop_delete_default();
@ -116,7 +116,7 @@ TEST(mdns, query_api_fails_with_expected_err)
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
// check it is not possible to register a service or set an instance without configuring the hostname
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, mdns_instance_name_set(MDNS_INSTANCE));
@ -125,19 +125,19 @@ TEST(mdns, query_api_fails_with_expected_err)
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
TEST_ASSERT_EQUAL(ESP_OK, mdns_instance_name_set(MDNS_INSTANCE));
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_ptr(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, CONFIG_MDNS_MAX_SERVICES, &results) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_ptr(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, CONFIG_MDNS_MAX_SERVICES, &results));
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_srv(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_srv(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results));
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_txt(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_query_txt(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 10, &results));
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_a(MDNS_HOSTNAME, 10, &addr4) );
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_a(MDNS_HOSTNAME, 10, &addr4));
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_aaaa(MDNS_HOSTNAME, 10, &addr6) );
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, mdns_query_aaaa(MDNS_HOSTNAME, 10, &addr6));
mdns_query_results_free(results);
mdns_free();
@ -149,7 +149,7 @@ TEST(mdns, add_remove_service)
mdns_result_t *results = NULL;
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname
@ -170,7 +170,7 @@ TEST(mdns, add_remove_service)
mdns_query_results_free(results);
// Update service properties: instance
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_INSTANCE "1" ));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_INSTANCE "1"));
yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname
TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(MDNS_INSTANCE "1", MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results));
TEST_ASSERT_NOT_EQUAL(NULL, results);
@ -220,9 +220,9 @@ TEST(mdns, add_remove_deleg_service)
mdns_result_t *results = NULL;
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init() );
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr) );
TEST_ASSERT_EQUAL(ESP_OK, mdns_delegate_hostname_add(MDNS_DELEGATE_HOSTNAME, &addr));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add_for_host(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_SERVICE_PORT, NULL, 0));
yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname
@ -243,7 +243,7 @@ TEST(mdns, add_remove_deleg_service)
mdns_query_results_free(results);
// Update service properties: instance
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_INSTANCE "1" ));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_instance_name_set_for_host(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_DELEGATE_HOSTNAME, MDNS_INSTANCE "1"));
yield_to_all_priorities(); // Make sure that mdns task has executed to add the hostname
TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_delegated_service(MDNS_INSTANCE "1", MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results));
TEST_ASSERT_NOT_EQUAL(NULL, results);

View File

@ -20,7 +20,7 @@ mosq_broker_run(&config);
## Memory Footprint Considerations
The broker primarily uses the heap for internal data, with minimal use of static/BSS memory. It consumes approximately 60 kB of program memory.
The broker primarily uses the heap for internal data, with minimal use of static/BSS memory. It consumes approximately 60 kB of program memory and minimum 5kB of stack size.
- **Initial Memory Usage**: ~2 kB of heap on startup
- **Per Client Memory Usage**: ~4 kB of heap for each connected client

View File

@ -0,0 +1,5 @@
# ESP32 mosquitto tests
Mosquitto component doesn't have any tests yet, but we upcycle IDF mqtt tests and run them with the current version of mosquitto.
For that we need to update the IDF test project's `idf_component.yml` file to reference this actual version of mosquitto.
We also need to update some pytest decorators to run only relevant test cases. See the [replacement](./replace_decorators.py) script.

View File

@ -0,0 +1,42 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
# This script replaces the `@pytest` decorators in the test files
# based on the value of the `config` parameter in the `@pytest` decorator.
# to reuse mqtt test cases for mosquitto broker.
import re
import sys
def replace_decorators(in_file: str, out_file: str) -> None:
with open(in_file, 'r') as file:
content = file.read()
# we replace config decorators to differentiate between local mosquitto based tests
pattern = r"@pytest\.mark\.parametrize\(\s*'config'\s*,\s*\[\s*'(.*?)'\s*\]\s*,.*\)"
def replacement(match):
config_value = match.group(1)
if config_value == 'local_broker':
return '@pytest.mark.test_mosquitto'
else:
return '@pytest.mark.test_mqtt'
# Replace occurrences
updated_content = re.sub(pattern, replacement, content)
with open(out_file, 'w') as file:
file.write(updated_content)
# Main function to handle arguments
if __name__ == '__main__':
if len(sys.argv) != 3:
print('Usage: python replace_decorators.py <in_file> <out_file>')
sys.exit(1)
in_file = sys.argv[1]
out_file = sys.argv[2]
replace_decorators(in_file, out_file)
print(f'Replacements completed')

View File

@ -66,13 +66,43 @@ Configuration:
.. note:: If you want to verify the server, then you need to provide a certificate in PEM format, and provide to ``cert_pem`` in :cpp:type:`websocket_client_config_t`. If no certficate is provided then the TLS connection will default to not requiring verification.
PEM certificate for this example could be extracted from an openssl `s_client` command connecting to websocket.org.
In case a host operating system has `openssl` and `sed` packages installed, one could execute the following command to download and save the root or intermediate root certificate to a file (Note for Windows users: Both Linux like environment or Windows native packages may be used).
```
echo "" | openssl s_client -showcerts -connect websocket.org:443 | sed -n "1,/Root/d; /BEGIN/,/END/p" | openssl x509 -outform PEM >websocket_org.pem
```
In case a host operating system has `openssl` and `sed` packages installed, one could execute the following command to download and save the root or intermediate root certificate to a file (Note for Windows users: Both Linux like environment or Windows native packages may be used). ::
echo "" | openssl s_client -showcerts -connect websocket.org:443 \
| sed -n "1,/Root/d; /BEGIN/,/END/p" \
| openssl x509 -outform PEM \
> websocket_org.pem
This command will extract the second certificate in the chain and save it as a pem-file.
Mutual TLS with DS Peripheral
"""""""""""""""""""""""""""""
To leverage the Digital Signature (DS) peripheral on supported targets, use `esp_secure_cert_mgr <https://github.com/espressif/esp_secure_cert_mgr/>`_ to flash an encrypted client certificate. In your project, add the dependency: ::
idf.py add-dependency esp_secure_cert_mgr
Set ``client_cert`` and ``client_ds_data`` in the config struct:
.. code:: c
char *client_cert = NULL;
uint32_t client_cert_len = 0;
esp_err_t err = esp_secure_cert_get_device_cert(&client_cert, &client_cert_len);
assert(err == ESP_OK);
esp_ds_data_ctx_t *ds_data = esp_secure_cert_get_ds_ctx();
assert(ds_data != NULL);
esp_websocket_client_config_t config = {
.uri = "wss://echo.websocket.org",
.cert_pem = (const char *)websocket_org_pem_start,
.client_cert = client_cert,
.client_ds_data = ds_data,
};
.. note:: ``client_cert`` provided by `esp_secure_cert_mgr` is a null-terminated PEM; so ``client_cert_len`` (DER format) should not be set.
Subprotocol
^^^^^^^^^^^
@ -91,14 +121,14 @@ For more options on :cpp:type:`esp_websocket_client_config_t`, please refer to A
Events
------
* `WEBSOCKET_EVENT_BEGIN': The client thread is running.
* `WEBSOCKET_EVENT_BEGIN`: The client thread is running.
* `WEBSOCKET_EVENT_BEFORE_CONNECT`: The client is about to connect.
* `WEBSOCKET_EVENT_CONNECTED`: The client has successfully established a connection to the server. The client is now ready to send and receive data. Contains no event data.
* `WEBSOCKET_EVENT_DATA`: The client has successfully received and parsed a WebSocket frame. The event data contains a pointer to the payload data, the length of the payload data as well as the opcode of the received frame. A message may be fragmented into multiple events if the length exceeds the buffer size. This event will also be posted for non-payload frames, e.g. pong or connection close frames.
* `WEBSOCKET_EVENT_ERROR`: The client has experienced an error. Examples include transport write or read failures.
* `WEBSOCKET_EVENT_DISCONNECTED`: The client has aborted the connection due to the transport layer failing to read data, e.g. because the server is unavailable. Contains no event data.
* `WEBSOCKET_EVENT_CLOSED`: The connection has been closed cleanly.
* `WEBSOCKET_EVENT_FINISH': The client thread is about to exit.
* `WEBSOCKET_EVENT_FINISH`: The client thread is about to exit.
If the client handle is needed in the event handler it can be accessed through the pointer passed to the event handler:

View File

@ -1,8 +1 @@
sphinxcontrib-applehelp==1.0.4
sphinxcontrib_devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.1
sphinxcontrib-serializinghtml==1.1.5
sphinxcontrib-qthelp==1.0.3
breathe==4.35
recommonmark==0.7.1
esp-docs==1.7.1
esp-docs>=2.0.0

View File

@ -0,0 +1,98 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
from __future__ import print_function, unicode_literals
import subprocess
import time
from threading import Event, Thread
import netifaces
def is_esp32(port):
"""
Check if the given port is connected to an ESP32 using esptool.
"""
try:
result = subprocess.run(
['esptool.py', '--port', port, 'chip_id'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True
)
return 'ESP32' in result.stdout
except subprocess.CalledProcessError:
return False
def run_server(server_stop, port):
print('Launching SLIP netif on port: {}'.format(port))
try:
arg_list = ['sudo', 'slattach', '-v', '-L', '-s', '115200','-p', 'slip', port]
p = subprocess.Popen(arg_list, stdout=subprocess.PIPE, bufsize=1)
while not server_stop.is_set():
if p.poll() is not None:
if p.poll() == 16:
print('[SLIP:] Terminated: hang-up received')
break
else:
raise ValueError(
'ENV_TEST_FAILURE: SLIP terminated unexpectedly with {}'
.format(p.poll()))
time.sleep(0.1)
except Exception as e:
print(e)
raise ValueError('ENV_TEST_FAILURE: Error running SLIP netif')
finally:
p.terminate()
print('SLIP netif stopped')
def test_examples_protocol_slip(dut):
# the PPP test env is reused for SLIP test and it uses three ttyUSB's: two for ESP32 board and another one for the ppp server
server_port = None
for i in ['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2']:
if i == dut.serial.port:
print(f'DUT port: {i}')
elif is_esp32(i):
print(f'Some other ESP32: {i}')
else:
print(f'Port for SLIP: {i}')
server_port = i
if server_port is None:
print('ENV_TEST_FAILURE: Cannot locate SLIP port')
raise
# Attach to the SLI netif
server_stop = Event()
t = Thread(target=run_server, args=(server_stop, server_port))
t.start()
try:
# Setups the SLIP interface
ppp_server_timeout = time.time() + 30
while 'sl0' not in netifaces.interfaces():
print(
"SLIP netif hasn't appear yet, list of active netifs:{}"
.format(netifaces.interfaces()))
time.sleep(0.5)
if time.time() > ppp_server_timeout:
raise ValueError(
'ENV_TEST_FAILURE: SLIP netif failed to setup sl0 interface within timeout'
)
# Configure the SLIP interface with IP addresses of both ends
cmd = ['sudo', 'ifconfig', 'sl0', '10.0.0.1', 'dstaddr', '10.0.0.2']
try:
subprocess.run(cmd, check=True)
print('SLIP interface configured successfully.')
except subprocess.CalledProcessError as e:
print(f'Failed to configure SLIP interface: {e}')
# Ping the SLIP interface with 5 packets and 10 seconds timeout
cmd = ['ping', '10.0.0.2', '-c', '5', '-W', '10']
try:
subprocess.run(cmd, check=True)
print('Pinging SLIP interface successfully.')
except subprocess.CalledProcessError as e:
print(f'Failed to ping SLIP interface: {e}')
raise
finally:
server_stop.set()
t.join()

View File

@ -0,0 +1,6 @@
CONFIG_IDF_TARGET="esp32c3"
CONFIG_LWIP_PPP_SUPPORT=y
CONFIG_LWIP_SLIP_SUPPORT=y
CONFIG_EXAMPLE_IPV4=y
CONFIG_EXAMPLE_UART_TX_PIN=6
CONFIG_EXAMPLE_UART_RX_PIN=7

View File

@ -1,2 +1,5 @@
# Override some defaults to enable SLIP
CONFIG_LWIP_SLIP_SUPPORT=y
# Workaround: Enable PPP to let esp_netif know that lwip's netif->state
# will be occupied (by SLIP netif info)
CONFIG_LWIP_PPP_SUPPORT=y

View File

@ -15,6 +15,7 @@ set(EXTRA_COMPONENT_DIRS
../components/console_simple_init
../components/mbedtls_cxx
../components/sock_utils
../components/libwebsockets
../components/mdns)