Compare commits

...

56 Commits

Author SHA1 Message Date
Suren Gabrielyan
3d4712b905 Merge pull request #941 from gabsuren/ws_bump_1.6.0
bump(websocket): 1.5.0 -> 1.6.0
2025-11-14 12:08:52 +04:00
surengab
67188fd7b4 bump(websocket): 1.5.0 -> 1.6.0
1.6.0
Features
- add WEBSOCKET_EVENT_HEADER_RECEIVED (#827) (18f0d028, #715)
- enhance example with docs, pytest setup, and standalone test server     - Add comprehensive README with TOC and quick start     - Add pytest setup and certificate generation scripts     - Add standalone WebSocket test server with TLS support     - Add troubleshooting and multiple testing approaches (cad527d2)
- Add websocket HTTP redirect (ce1560ac)
Bug Fixes
- remove redundant timeout check in client task loop (1e83bee4)
- fix PING timing - enable periodic PING during active traffic (7f424325)
- Update linux build docs on required libs (e52a5757)
- clean up component dependencies - Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration - Add cJSON dependency to target example's idf_component.yml (d665e6f1)
- fix relying on asprintf() to NULL strp on failure (54eb0027)
- Update Remaining Websocket Echo Server (#893) (18faeb3d)
- avoid long stopping time when waiting to auto-reconnect (2432e41d)
- Update Websocket Echo Server (94bd5b07)
Updated
- ci(common): Update test component dir for IDFv6.0 (18418c83)
2025-11-13 16:55:20 +04:00
Suren Gabrielyan
4d7c6848b2 Merge pull request #935 from gabsuren/refactor/websocket_timeout_check_remove
fix(websocket): remove redundant timeout check in client task loop
2025-11-13 15:59:45 +04:00
surengab
1e83bee4fe fix(websocket): remove redundant timeout check in client task loop 2025-11-12 22:00:55 +04:00
Suren Gabrielyan
318bca1657 Merge pull request #930 from gabsuren/fix/ping_unreset
fix(websocket): fix PING timing - enable periodic PING during active traffic (IDFGH-16701)
2025-11-05 10:49:06 +04:00
david-cermak
9e1b9cdd20 Merge pull request #929 from david-cermak/bump/mdns_v1.9
[mdns]:  Bump version to `v1.9`
2025-11-03 12:09:42 +01:00
surengab
7f424325d8 fix(websocket): fix PING timing - enable periodic PING during active traffic 2025-11-03 14:03:30 +04:00
David Cermak
9ef228f247 bump(mdns): 1.8.2 -> 1.9.0
1.9.0
Features
- support null value for boolean txt records (fa96de3b)
Bug Fixes
- Add test case for bool/NULL txt handling (5068f221)
- Temporary fix for build issues on IDF master (0197c994)
- Add tests for delegated answers (487a746d)
- Add fuzzing into mdns CI (af6bb1b5)
- Host test to use hw_support include dir (8bba3a97)
- Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free (63bf7091)
- put srv/txt records in additional section for ptr queries (b7b8c5db)
Updated
- ci(common): Update test component dir for IDFv6.0 (18418c83)
2025-11-03 08:12:38 +01:00
David Cermak
5068f2217e fix(mdns): Add test case for bool/NULL txt handling 2025-11-03 08:11:41 +01:00
david-cermak
1ceb42c5a2 Merge pull request #928 from david-cermak/fix/websocket_linux_docs
fix(websocket): Update linux build docs on required libs
2025-10-31 08:22:58 +01:00
Alexander Salas Bastidas
e52a5757f1 fix(websocket): Update linux build docs on required libs
ESP-IDF linux target expects libbsd headers
Updated instructions for compiling the example on Linux to include the correct path for the executable.
2025-10-30 19:41:31 +01:00
Suren Gabrielyan
732cd29ec0 Merge pull request #925 from gabsuren/ci/fix_json_dependancy
fix(websocket): resolve JSON dependency issues for component and exam…
2025-10-29 20:26:42 +04:00
david-cermak
7a203cf085 Merge pull request #921 from david-cermak/bump/console_ping
bump(console): 1.1.0 -> 1.2.0
2025-10-29 17:04:32 +01:00
surengab
d665e6f18e fix(websocket): clean up component dependencies
- Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration
- Add cJSON dependency to target example's idf_component.yml
2025-10-29 19:19:02 +04:00
David Cermak
2e269640c6 bump(console): 1.1.0 -> 1.2.0
1.2.0
Features
- Add support for interface argument (90ddb04e)
2025-10-27 14:34:59 +01:00
david-cermak
c078c36361 Merge pull request #886 from david-cermak/feat/console_ping_interface
[console_ping]: Add support for interface argument
2025-10-24 15:17:14 +02:00
David Cermak
90ddb04e53 feat(console_ping): Add support for interface argument 2025-10-24 15:12:05 +02:00
david-cermak
cee3bdea9d Merge pull request #822 from tanyanquan/feat/support_null_value_txt
feat(mdns): support null value for boolean txt records
2025-10-24 14:05:47 +02:00
Tan Yan Quan
fa96de3bd7 feat(mdns): support null value for boolean txt records 2025-10-24 16:12:10 +08:00
bryghtlabs-richard
18f0d02806 feat(websocket): add WEBSOCKET_EVENT_HEADER_RECEIVED (#827)
Send a new event for each HTTP header-line received.

Depends on https://github.com/espressif/esp-idf/pull/16119
Closes https://github.com/espressif/esp-protocols/issues/715
2025-10-22 18:25:31 -03:00
david-cermak
bfa604b5f6 Merge pull request #904 from david-cermak/feat/add_tests_v1.9
[mdns]: Add tests for recent feats/fixes
2025-10-22 11:07:59 +02:00
David Cermak
92a31187ff fix(ci): Use python venv for mdns target tests 2025-10-21 17:16:21 +02:00
David Cermak
0197c994ee fix(mdns): Temporary fix for build issues on IDF master 2025-10-21 17:11:46 +02:00
David Cermak
487a746d14 fix(mdns): Add tests for delegated answers 2025-10-21 17:11:46 +02:00
David Cermak
af6bb1b5ee fix(mdns): Add fuzzing into mdns CI 2025-10-21 17:11:46 +02:00
Suren Gabrielyan
f5e62e83e9 Merge pull request #902 from gabsuren/docs/readme_update
[examples]: enhance example with docs, pytest setup, and standalone test server(IDFGH-16585)
2025-10-21 13:07:38 +04:00
surengab
cad527d2fc feat(examples): enhance example with docs, pytest setup, and standalone test server
- Add comprehensive README with TOC and quick start
    - Add pytest setup and certificate generation scripts
    - Add standalone WebSocket test server with TLS support
    - Add troubleshooting and multiple testing approaches
2025-10-21 12:17:00 +04:00
david-cermak
16cc2dcffb Merge pull request #914 from david-cermak/fix/modem_forwardport_patch
[modem]: Forward-port 1.4.1 patches
2025-10-21 07:14:47 +02:00
david-cermak
d622e41a54 Merge pull request #915 from david-cermak/fix/target_tests
[websockets]: Fix target tests
2025-10-21 07:14:28 +02:00
glmfe
ca6e39aac7 ci(common): Ignore ethernet warning. 2025-10-20 16:27:57 +02:00
David Cermak
e45944e143 fix(websocket): Use venv for running target tests 2025-10-20 16:27:22 +02:00
David Cermak
fe657b9737 fix(modem): Ignore more build warnings 2025-10-20 09:35:00 +02:00
David Cermak
453be4cd79 fix(modem): Fix modem console dependencies 2025-10-20 09:34:15 +02:00
David Cermak
018ba58ec5 fix(modem): Address build issues 2025-10-20 09:30:47 +02:00
David Cermak
67c682d911 fix(modem): Fix driver dependency issue on v6.0 2025-10-20 09:30:41 +02:00
Suren Gabrielyan
91915ce1c7 Merge pull request #901 from bryghtlabs-richard/fix/websocket-asprintf-ret-checks
fix(websocket): fix relying on asprintf() to NULL strp on failure (IDFGH-16595)
2025-10-15 14:27:56 +04:00
david-cermak
ae052e5507 Merge pull request #905 from david-cermak/fix/eppp_netif_rx_err
[eppp]: Fix tun netif to (optionally) return errors
2025-10-09 14:03:03 +02:00
David Cermak
44524f5de0 bump(eppp): 1.1.2 -> 1.1.3
1.1.3
Bug Fixes
- Fix test dependency issue on driver (1ace92c2)
- Fix tun netif to (optionally) return errors (7a6cf0f9)
2025-10-09 13:04:46 +02:00
David Cermak
1ace92c279 fix(eppp): Fix test dependency issue on driver 2025-10-09 13:03:06 +02:00
David Cermak
7a6cf0f9c0 fix(eppp): Fix tun netif to (optionally) return errors 2025-10-08 16:20:18 +02:00
Richard Allen
54eb002758 fix(websocket): fix relying on asprintf() to NULL strp on failure
asprintf()'s return value:
    When successful, these functions return the number of bytes
    printed, just like sprintf(3).  On error, -1 is returned, errno is
    set to indicate the error, and the contents of strp are undefined.

Fixes the following:
    error: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
2025-10-03 08:44:00 -05:00
Guilherme Alves Ferreira
318e41b3c3 fix(lws): Update websocket Echo server (#894)
* fix(lws): Update websocket Echo server

- Update Websocket Echo Server, "wss://echo.websocket.events" is no longer available.
2025-10-03 06:17:11 -03:00
david-cermak
6f6237a0cc Merge pull request #813 from tanyanquan/master
fix(mdns): put srv/txt records in additional section for ptr queries
2025-10-01 13:59:03 +02:00
Guilherme Alves Ferreira
18faeb3dfa fix(websocket): Update Remaining Websocket Echo Server (#893) 2025-10-01 07:52:42 -03:00
Guilherme Alves Ferreira
296123c14e Merge pull request #896 from glmfe/fix/ignore-ci-build-warnings
ci(common): Ignore deprecated idf-build-apps parameters
2025-09-30 10:50:22 -03:00
glmfe
4e178f06bd ci(common): Ignore deprecated idf-build-apps parameters
- Added deprecated flash, sign and extract_public_key to global warning list file
2025-09-29 08:21:50 -03:00
Guilherme Alves Ferreira
5ab7e8327e Merge pull request #729 from DazeTechnology/fix/websocket_long_stop
fix(websocket): avoid long stop time when waiting to auto-reconnect (IDFGH-14393)
2025-09-25 10:12:39 -03:00
Paolo Pasinetti
91e7e9fa08 chore(lws): fixed formatting 2025-09-19 15:42:33 +02:00
Paolo Pasinetti
ff5d6021be chore(asio): Fixed formatting 2025-09-19 15:42:33 +02:00
Paolo Pasinetti
2432e41dcb fix(websocket): avoid long stopping time when waiting to auto-reconnect
This commit fixes an issue that occurred when auto-reconnection is enabled,
and the client is disconnected from the server. In this situation, if the
esp_websocket_client_stop() method is called, the caller could remain stuck
waiting for a maximum time equal to half of wait_timeout_ms.

This fix allows the esp_websocket_client_task to be woken up when it is
waiting to reconnect, so it can be closed promptly when requested.
2025-09-19 15:42:33 +02:00
Guilherme Alves Ferreira
870ac91db7 Merge pull request #890 from glmfe/fix/update-ws-echo-server
fix(websocket): Update Websocket Echo Server
2025-09-19 08:35:01 -03:00
glmfe
94bd5b074a fix(websocket): Update Websocket Echo Server 2025-09-19 07:44:12 -03:00
david-cermak
db7baaffba Merge pull request #887 from david-cermak/fix/eppp_uart_deps
[eppp]: Fix uart driver deps with new IDF
2025-09-15 13:41:53 +02:00
David Cermak
1ea93a866b bump(eppp): 1.1.1 -> 1.1.2
1.1.2
Bug Fixes
- Update uart driver deps per IDF > v5.3 (92e14607)
2025-09-15 12:55:14 +02:00
David Cermak
92e1460721 fix(eppp): Update uart driver deps per IDF > v5.3 2025-09-15 12:55:08 +02:00
Tan Yan Quan
b7b8c5dbd7 fix(mdns): put srv/txt records in additional section for ptr queries 2025-05-08 19:09:52 +08:00
58 changed files with 996 additions and 208 deletions

View File

@@ -24,6 +24,11 @@ jobs:
shell: bash
run: |
. ${IDF_PATH}/export.sh
if [[ "${{ matrix.idf_ver }}" == "latest" ]]; then
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'"
else
export EXPECTED_WARNING="warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_GENERIC'"
fi
python -m pip install idf-build-apps
# Build default configs for all targets
python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -d
@@ -71,6 +76,22 @@ jobs:
- name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
working-directory: components/mdns/${{ matrix.test.path }}
run: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -68,3 +68,43 @@ jobs:
diff -q $file /tmp/$file || exit 1
echo "OK"
done
fuzz_test:
if: contains(github.event.pull_request.labels.*.name, 'mdns-fuzz') || github.event_name == 'push'
name: Fuzzer tests for mdns lib
strategy:
matrix:
idf_ver: ["latest"]
runs-on: ubuntu-22.04
container: aflplusplus/aflplusplus
steps:
- name: Checkout esp-protocols
uses: actions/checkout@v4
- name: Checkout ESP-IDF
uses: actions/checkout@v4
with:
repository: espressif/esp-idf
path: idf
submodules: recursive
- name: Install Necessary Libs
run: |
apt-get update -y
apt-get install -y libbsd-dev
- name: Run AFL++
shell: bash
run: |
export IDF_PATH=$GITHUB_WORKSPACE/idf
cd components/mdns/tests/test_afl_fuzz_host/
make fuzz
- name: Upload Crash Artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-crashes
path: components/mdns/tests/test_afl_fuzz_host/out/default/crashes.tar.gz
if-no-files-found: ignore

View File

@@ -15,11 +15,6 @@ jobs:
matrix:
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
include:
- idf_ver: "release-v5.0"
example: "simple_cmux_client"
warning: "Warning: The smallest app partition is nearly full"
runs-on: ubuntu-22.04
container: espressif/idf:${{ matrix.idf_ver }}
steps:
@@ -30,9 +25,9 @@ jobs:
- if: ${{ matrix.skip_config }}
run: rm -f $GITHUB_WORKSPACE/protocols/components/esp_modem/examples/${{ matrix.example }}/sdkconfig.ci.${{ matrix.skip_config }}*
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }}
env:
EXPECTED_WARNING: ${{ matrix.warning }}
shell: bash
env:
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full\nwarning: unknown kconfig symbol 'ESP32_PANIC_PRINT_HALT'"
run: |
. ${IDF_PATH}/export.sh
python -m pip install idf-build-apps

View File

@@ -65,14 +65,27 @@ jobs:
with:
name: websocket_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
path: ${{ env.TEST_DIR }}/ci/
- name: Install Python packages
- name: Run Example Test on target
working-directory: ${{ env.TEST_DIR }}
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: |
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
echo "Installing Python 3.12.6..."
pyenv install -s 3.12.6
fi
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
echo "Creating pyenv virtualenv 'myenv'..."
pyenv virtualenv 3.12.6 myenv
fi
pyenv activate myenv
python --version
pip install --prefer-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool
pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
unzip ci/artifacts.zip -d ci
for dir in `ls -d ci/build_*`; do
rm -rf build sdkconfig.defaults

View File

@@ -56,7 +56,7 @@ if __name__ == '__main__':
build_dir='build_@t_@w',
config_rules_str=args.rules,
build_log_filename='build_log.txt',
size_json_filename='size.json' if not args.linux else None,
size_json_filename=None,
check_warnings=True,
manifest_files=args.manifests,
default_build_targets=SUPPORTED_TARGETS,

View File

@@ -1 +1,7 @@
DeprecationWarning: pkg_resources is deprecated as an API
Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead.
Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead.
Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead.
Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead.
Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead.
warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'

View File

@@ -6,3 +6,4 @@ dpkt
pytest
idf_build_apps
netifaces
esptool>=5.1.0

View File

@@ -28,7 +28,7 @@ posix_event::posix_event()
}
} // namespace asio::detail
extern "C" int pause (void)
extern "C" int pause(void)
{
while (true) {
::sleep(UINT_MAX);

View File

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

View File

@@ -1,5 +1,11 @@
# Changelog
## [1.2.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.2.0)
### Features
- Add support for interface argument ([90ddb04e](https://github.com/espressif/esp-protocols/commit/90ddb04e))
## [1.1.0](https://github.com/espressif/esp-protocols/commits/console_cmd_ping-v1.1.0)
### Features

View File

@@ -48,7 +48,7 @@ For more details refer [IDF Component Manager](https://docs.espressif.com/projec
### ping:
```
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] [-I <n>] <host>
send ICMP ECHO_REQUEST to network hosts
-W, --timeout=<t> Time to wait for a response, in seconds
-i, --interval=<t> Wait interval seconds between sending each packet
@@ -56,6 +56,7 @@ ping [-W <t>] [-i <t>] [-s <n>] [-c <n>] [-Q <n>] [-T <n>] <host>
-c, --count=<n> Stop after sending count packets
-Q, --tos=<n> Set Type of Service related bits in IP datagrams
-T, --ttl=<n> Set Time to Live related bits in IP datagrams
-I, --interface=<n> Set Interface number 0=no-interface selected, >0 netif number + 1 (1 is usually 'lo0')
<host> Host address
getaddrinfo [-f <AF>] [-F <FLAGS>]... [-p <port>] <hostname>
@@ -121,8 +122,8 @@ getaddrinfo -f AF_INET -F AI_PASSIVE www.example.com
ping www.example.com
```
2. To specify additional options, such as timeout, interval, packet size, etc.:
2. To specify additional options, such as timeout, interval, packet size, interface, etc.:
```
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 www.example.com
ping -W 5 -i 1 -s 64 -c 4 -Q 0x10 -T 64 -I 0 www.example.com
```

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -99,6 +99,7 @@ static struct {
struct arg_int *count;
struct arg_int *tos;
struct arg_int *ttl;
struct arg_int *interface;
struct arg_str *host;
struct arg_end *end;
} ping_args;
@@ -137,6 +138,10 @@ static int do_ping_cmd(int argc, char **argv)
config.ttl = (uint32_t)(ping_args.ttl->ival[0]);
}
if (ping_args.interface->count > 0) {
config.interface = (uint32_t)(ping_args.interface->ival[0]);
}
// parse IP address
struct sockaddr_in6 sock_addr6;
ip_addr_t target_addr = {0};
@@ -155,12 +160,12 @@ static int do_ping_cmd(int argc, char **argv)
}
if (res->ai_family == AF_INET) {
#if CONFIG_LWIP_IPV4
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
struct in_addr addr4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
#endif
} else {
#if CONFIG_LWIP_IPV6
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
struct in6_addr addr6 = ((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
#endif
}
@@ -204,6 +209,7 @@ esp_err_t console_cmd_ping_register(void)
ping_args.count = arg_int0("c", "count", "<n>", "Stop after sending count packets");
ping_args.tos = arg_int0("Q", "tos", "<n>", "Set Type of Service related bits in IP datagrams");
ping_args.ttl = arg_int0("T", "ttl", "<n>", "Set Time to Live related bits in IP datagrams");
ping_args.interface = arg_int0("I", "interface", "<n>", "Set Interface number");
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address");
ping_args.end = arg_end(1);
const esp_console_cmd_t ping_cmd = {

View File

@@ -1,4 +1,4 @@
version: 1.1.0
version: 1.2.0
url: https://github.com/espressif/esp-protocols/tree/master/components/console_cmd_ping
description: The component provides a console where the 'ping' command can be executed.
dependencies:

View File

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

View File

@@ -1,5 +1,18 @@
# Changelog
## [1.1.3](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.3)
### Bug Fixes
- Fix test dependency issue on driver ([1ace92c2](https://github.com/espressif/esp-protocols/commit/1ace92c2))
- Fix tun netif to (optionally) return errors ([7a6cf0f9](https://github.com/espressif/esp-protocols/commit/7a6cf0f9))
## [1.1.2](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.2)
### Bug Fixes
- Update uart driver deps per IDF > v5.3 ([92e14607](https://github.com/espressif/esp-protocols/commit/92e14607))
## [1.1.1](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.1)
### Bug Fixes

View File

@@ -1,5 +1,5 @@
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
set(driver_deps esp_driver_gpio esp_driver_spi)
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
else()
set(driver_deps driver)
endif()

View File

@@ -17,9 +17,17 @@
#include "esp_check.h"
#include "esp_idf_version.h"
#if defined(CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS)
typedef esp_err_t esp_netif_recv_ret_t;
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr) expr
#else
typedef void esp_netif_recv_ret_t;
#define ESP_NETIF_OPTIONAL_RETURN_CODE(expr)
#endif // CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS
static const char *TAG = "eppp_tun_netif";
static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
static esp_netif_recv_ret_t tun_input(void *h, void *buffer, unsigned int len, void *eb)
{
__attribute__((unused)) esp_err_t ret = ESP_OK;
struct netif *netif = h;
@@ -31,11 +39,12 @@ static void tun_input(void *h, void *buffer, unsigned int len, void *eb)
ESP_GOTO_ON_FALSE(pbuf_remove_header(p, SIZEOF_ETH_HDR) == 0, ESP_FAIL, err, TAG, "pbuf_remove_header failed");
memcpy(p->payload, buffer, len);
ESP_GOTO_ON_FALSE(netif->input(p, netif) == ERR_OK, ESP_FAIL, err, TAG, "failed to input packet to lwip");
return;
return ESP_NETIF_OPTIONAL_RETURN_CODE(ESP_OK);
err:
if (p) {
pbuf_free(p);
}
return ESP_NETIF_OPTIONAL_RETURN_CODE(ret);
}
static err_t tun_output(struct netif *netif, struct pbuf *p)

View File

@@ -1,4 +1,4 @@
version: 1.1.1
version: 1.1.3
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
dependencies:

View File

@@ -1,4 +1,10 @@
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
set(driver_deps esp_driver_gpio esp_driver_spi esp_driver_uart esp_driver_sdio)
else()
set(driver_deps driver)
endif()
idf_component_register(SRCS app_main.c
INCLUDE_DIRS "."
REQUIRES test_utils
PRIV_REQUIRES unity nvs_flash esp_netif driver esp_event)
PRIV_REQUIRES unity nvs_flash esp_netif esp_event ${driver_deps})

View File

@@ -11,7 +11,12 @@ else()
src/esp_modem_uart.cpp
src/esp_modem_term_uart.cpp
src/esp_modem_netif.cpp)
set(dependencies driver esp_event esp_netif)
set(dependencies esp_event esp_netif)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "5.3")
list(APPEND dependencies esp_driver_uart)
else()
list(APPEND dependencies driver)
endif()
endif()

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
*/
@@ -100,10 +100,10 @@ void wifi_init_softap(void)
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
@@ -120,7 +120,7 @@ void wifi_init_softap(void)
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",

View File

@@ -9,5 +9,4 @@ idf_component_register(SRCS "modem_console_main.cpp"
"${command_dir}/my_module_dce.cpp"
"httpget_handle.c"
"ping_handle.c"
REQUIRES console esp_http_client nvs_flash
INCLUDE_DIRS "." "${command_dir}")

View File

@@ -89,7 +89,7 @@
#define MPPE_SUPPORT 0
#define PPP_MAXIDLEFLAG 0
#define PPP_MAXIDLEFLAG 100
#define PRINTPKT_SUPPORT 1
#define PPP_PROTOCOLNAME 1

View File

@@ -104,7 +104,7 @@ static void ppp_link_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
}
}
static u32_t ppp_output_cb(struct ppp_pcb_s *pcb, const void *data, u32_t len, void *ctx)
static u32_t ppp_output_cb(struct ppp_pcb_s *pcb, u8_t *data, u32_t len, void *ctx)
{
esp_netif_t *netif = (esp_netif_t *)ctx;
if (netif->transmit) {

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.5.0
version: 1.6.0
version_files:
- idf_component.yml

View File

@@ -1,5 +1,28 @@
# Changelog
## [1.6.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.6.0)
### Features
- add WEBSOCKET_EVENT_HEADER_RECEIVED (#827) ([18f0d028](https://github.com/espressif/esp-protocols/commit/18f0d028), [#715](https://github.com/espressif/esp-protocols/issues/715))
- enhance example with docs, pytest setup, and standalone test server - Add comprehensive README with TOC and quick start - Add pytest setup and certificate generation scripts - Add standalone WebSocket test server with TLS support - Add troubleshooting and multiple testing approaches ([cad527d2](https://github.com/espressif/esp-protocols/commit/cad527d2))
- Add websocket HTTP redirect ([ce1560ac](https://github.com/espressif/esp-protocols/commit/ce1560ac))
### Bug Fixes
- remove redundant timeout check in client task loop ([1e83bee4](https://github.com/espressif/esp-protocols/commit/1e83bee4))
- fix PING timing - enable periodic PING during active traffic ([7f424325](https://github.com/espressif/esp-protocols/commit/7f424325))
- Update linux build docs on required libs ([e52a5757](https://github.com/espressif/esp-protocols/commit/e52a5757))
- clean up component dependencies - Remove unused 'json', 'nvs_flash', 'esp_stubs', dependency from Linux build configuration - Add cJSON dependency to target example's idf_component.yml ([d665e6f1](https://github.com/espressif/esp-protocols/commit/d665e6f1))
- fix relying on asprintf() to NULL strp on failure ([54eb0027](https://github.com/espressif/esp-protocols/commit/54eb0027))
- Update Remaining Websocket Echo Server (#893) ([18faeb3d](https://github.com/espressif/esp-protocols/commit/18faeb3d))
- avoid long stopping time when waiting to auto-reconnect ([2432e41d](https://github.com/espressif/esp-protocols/commit/2432e41d))
- Update Websocket Echo Server ([94bd5b07](https://github.com/espressif/esp-protocols/commit/94bd5b07))
### Updated
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
## [1.5.0](https://github.com/espressif/esp-protocols/commits/websocket-v1.5.0)
### Features

View File

@@ -11,7 +11,7 @@ endif()
if(${IDF_TARGET} STREQUAL "linux")
idf_component_register(SRCS "esp_websocket_client.c"
INCLUDE_DIRS "include"
REQUIRES esp-tls tcp_transport http_parser esp_event nvs_flash esp_stubs json
REQUIRES esp-tls tcp_transport http_parser esp_event
PRIV_REQUIRES esp_timer)
else()
idf_component_register(SRCS "esp_websocket_client.c"

View File

@@ -74,6 +74,7 @@ static const char *TAG = "websocket_client";
const static int STOPPED_BIT = BIT0;
const static int CLOSE_FRAME_SENT_BIT = BIT1; // Indicates that a close frame was sent by the client
// and we are waiting for the server to continue with clean close
const static int REQUESTED_STOP_BIT = BIT2; // Indicates that a client stop has been requested
ESP_EVENT_DEFINE_BASE(WEBSOCKET_EVENTS);
@@ -477,11 +478,20 @@ static esp_err_t stop_wait_task(esp_websocket_client_handle_t client)
}
client->run = false;
xEventGroupSetBits(client->status_bits, REQUESTED_STOP_BIT);
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;
}
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
static void websocket_header_hook(void * client, const char * line, int line_len)
{
ESP_LOGD(TAG, "%s header:%.*s", __func__, line_len, line);
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_HEADER_RECEIVED, line, line_len);
}
#endif
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);
@@ -491,6 +501,10 @@ static esp_err_t set_websocket_transport_optional_settings(esp_websocket_client_
.sub_protocol = client->config->subprotocol,
.user_agent = client->config->user_agent,
.headers = client->config->headers,
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
.header_hook = websocket_header_hook,
.header_user_context = client,
#endif
.auth = client->config->auth,
.propagate_control_frames = true
};
@@ -725,10 +739,14 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config, goto _websocket_init_fail);
if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) {
asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME);
if (asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME) < 0) {
client->config->scheme = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
} else if (config->transport == WEBSOCKET_TRANSPORT_OVER_SSL) {
asprintf(&client->config->scheme, WS_OVER_TLS_SCHEME);
if (asprintf(&client->config->scheme, WS_OVER_TLS_SCHEME) < 0) {
client->config->scheme = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
}
@@ -773,7 +791,9 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
}
if (client->config->scheme == NULL) {
asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME);
if (asprintf(&client->config->scheme, WS_OVER_TCP_SCHEME) < 0) {
client->config->scheme = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, goto _websocket_init_fail);
}
@@ -850,26 +870,34 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
}
if (puri.field_data[UF_SCHEMA].len) {
free(client->config->scheme);
asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off);
if (asprintf(&client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off) < 0) {
client->config->scheme = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->scheme, return ESP_ERR_NO_MEM);
}
if (puri.field_data[UF_HOST].len) {
free(client->config->host);
asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off);
if (asprintf(&client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off) < 0) {
client->config->host = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->host, return ESP_ERR_NO_MEM);
}
if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) {
free(client->config->path);
int aret = -1;
if (puri.field_data[UF_QUERY].len == 0) {
asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
aret = asprintf(&client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off);
} else if (puri.field_data[UF_PATH].len == 0) {
asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
aret = asprintf(&client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
} else {
asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
aret = asprintf(&client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off,
puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off);
}
if (aret < 0) {
client->config->path = NULL;
}
ESP_WS_CLIENT_MEM_CHECK(TAG, client->config->path, return ESP_ERR_NO_MEM);
}
@@ -879,7 +907,9 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
if (puri.field_data[UF_USERINFO].len) {
char *user_info = NULL;
asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off);
if (asprintf(&user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off) < 0) {
user_info = NULL;
}
if (user_info) {
char *pass = strchr(user_info, ':');
if (pass) {
@@ -1138,13 +1168,6 @@ static void esp_websocket_client_task(void *pv)
}
}
}
if (read_select == 0) {
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
break;
}
client->ping_tick_ms = _tick_get_ms();
break;
case WEBSOCKET_STATE_WAIT_TIMEOUT:
@@ -1197,10 +1220,12 @@ static void esp_websocket_client_task(void *pv)
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
xSemaphoreGiveRecursive(client->lock);
}
} else {
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_poll_read().");
}
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
// waiting for reconnecting...
vTaskDelay(client->wait_timeout_ms / 2 / portTICK_PERIOD_MS);
// waiting for reconnection or a request to stop the client...
xEventGroupWaitBits(client->status_bits, REQUESTED_STOP_BIT, false, true, client->wait_timeout_ms / 2 / portTICK_PERIOD_MS);
} else if (WEBSOCKET_STATE_CLOSING == client->state &&
(CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits))) {
ESP_LOGD(TAG, " Waiting for TCP connection to be closed by the server");
@@ -1262,7 +1287,7 @@ esp_err_t esp_websocket_client_start(esp_websocket_client_handle_t client)
ESP_LOGE(TAG, "Error create websocket task");
return ESP_FAIL;
}
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT);
xEventGroupClearBits(client->status_bits, STOPPED_BIT | CLOSE_FRAME_SENT_BIT | REQUESTED_STOP_BIT);
ESP_LOGI(TAG, "Started");
return ESP_OK;
}
@@ -1331,6 +1356,7 @@ static esp_err_t esp_websocket_client_close_with_optional_body(esp_websocket_cli
// If could not close gracefully within timeout, stop the client and disconnect
client->run = false;
xEventGroupSetBits(client->status_bits, REQUESTED_STOP_BIT);
xEventGroupWaitBits(client->status_bits, STOPPED_BIT, false, true, portMAX_DELAY);
client->state = WEBSOCKET_STATE_UNKNOW;
return ESP_OK;

View File

@@ -6,29 +6,42 @@ This example demonstrates the ESP websocket client using the `linux` target. It
To compile and execute this example on Linux need to set target `linux`
* Debian/Ubuntu: `sudo apt-get install -y libbsd-dev`
* Fedora/RHEL: `sudo dnf install libbsd-devel`
* Arch: `sudo pacman -S libbsd`
* Alpine: `sudo apk add libbsd-dev`
```
idf.py --preview set-target linux
idf.py build
./websocket.elf
./build/websocket.elf
```
## Example Output
```
I (164532) websocket: [APP] Startup..
I (164532) websocket: [APP] Free memory: 4294967295 bytes
I (164532) websocket: [APP] IDF version: v5.3-dev-1353-gb3f7e2c8a4
I (164538) websocket: Connecting to ws://echo.websocket.events...
W (164538) websocket_client: `reconnect_timeout_ms` is not set, or it is less than or equal to zero, using default time out 10000 (milliseconds)
W (164538) websocket_client: `network_timeout_ms` is not set, or it is less than or equal to zero, using default time out 10000 (milliseconds)
I (165103) websocket: WEBSOCKET_EVENT_CONNECTED
I (165539) websocket: Sending hello 0000
I (165627) websocket: WEBSOCKET_EVENT_DATA
I (165627) websocket: Received opcode=1
W (165627) websocket: Received=hello 0000
W (165627) websocket: Total payload length=10, data_len=10, current payload offset=0
I (76826192) websocket: [APP] Startup..
I (76826193) websocket: [APP] Free memory: 4294967295 bytes
I (76826193) websocket: [APP] IDF version: v6.0-dev-2414-gab3feab1d13
I (76826195) websocket: Connecting to wss://echo.websocket.org...
W (76826195) websocket_client: `reconnect_timeout_ms` is not set, or it is less than or equal to zero, using default time out 10000 (milliseconds)
W (76826195) websocket_client: `network_timeout_ms` is not set, or it is less than or equal to zero, using default time out 10000 (milliseconds)
I (76826195) websocket: WEBSOCKET_EVENT_BEGIN
I (76826196) websocket_client: Started
I (76826294) esp-x509-crt-bundle: Certificate validated
I (76827230) websocket: WEBSOCKET_EVENT_CONNECTED
I (76827239) websocket: WEBSOCKET_EVENT_DATA
I (76827239) websocket: Received opcode=1
W (76827239) websocket: Received=Request served by 4d896d95b55478
W (76827239) websocket: Total payload length=32, data_len=32, current payload offset=0
I (166539) websocket: Sending fragmented message
I (76828198) websocket: Sending hello 0000
I (76828827) websocket: WEBSOCKET_EVENT_DATA
I (76828827) websocket: Received opcode=1
W (76828827) websocket: Received=hello 0000
W (76828827) websocket: Total payload length=10, data_len=10, current payload offset=0
I (76829207) websocket: Sending fragmented text message
```
## Coverage Reporting

View File

@@ -1,5 +1,5 @@
idf_component_register(SRCS "websocket_linux.c"
REQUIRES esp_websocket_client protocol_examples_common)
REQUIRES esp_websocket_client protocol_examples_common esp_netif)
if(CONFIG_GCOV_ENABLED)
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage)

View File

@@ -8,8 +8,27 @@ menu "Host-test config"
config WEBSOCKET_URI
string "Websocket endpoint URI"
default "ws://echo.websocket.events"
default "wss://echo.websocket.org"
help
URL 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 y
help
Enables WebSocket connections over TLS (WSS) with server certificate verification.
The client verifies the server certificate; the server does not require a client certificate.
config WS_OVER_TLS_MUTUAL_AUTH
bool "Enable WebSocket over TLS with Server Client Mutual Authentification"
default n
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
Skip Common Name (CN) check during TLS (WSS) authentication. Use only for testing.
endmenu

View File

@@ -4,13 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_log.h>
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "esp_websocket_client.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_crt_bundle.h"
static const char *TAG = "websocket";
@@ -28,6 +28,11 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
case WEBSOCKET_EVENT_BEGIN:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_BEGIN");
break;
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
case WEBSOCKET_EVENT_HEADER_RECEIVED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_HEADER_RECEIVED: %.*s", data->data_len, data->data_ptr);
break;
#endif
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
break;
@@ -75,6 +80,33 @@ static void websocket_app_start(void)
websocket_cfg.uri = CONFIG_WEBSOCKET_URI;
#if CONFIG_WS_OVER_TLS_MUTUAL_AUTH
/* Configuring client certificates for mutual authentification */
extern const char cacert_start[] asm("_binary_ca_cert_pem_start");
extern const char cert_start[] asm("_binary_client_cert_pem_start");
extern const char cert_end[] asm("_binary_client_cert_pem_end");
extern const char key_start[] asm("_binary_client_key_pem_start");
extern const char key_end[] asm("_binary_client_key_pem_end");
websocket_cfg.cert_pem = cacert_start;
websocket_cfg.client_cert = cert_start;
websocket_cfg.client_cert_len = cert_end - cert_start;
websocket_cfg.client_key = key_start;
websocket_cfg.client_key_len = key_end - key_start;
#elif CONFIG_WS_OVER_TLS_SERVER_AUTH
// Using certificate bundle as default server certificate source
websocket_cfg.crt_bundle_attach = esp_crt_bundle_attach;
// If using a custom certificate it could be added to certificate bundle,
// added to the build similar to client certificates in this examples,
// or read from NVS.
/* extern const char cacert_start[] asm("ADDED_CERTIFICATE"); */
/* websocket_cfg.cert_pem = cacert_start; */
#endif
#if CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK
websocket_cfg.skip_cert_common_name_check = true;
#endif
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri);
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg);
@@ -127,7 +159,6 @@ int main(void)
esp_log_level_set("transport_ws", ESP_LOG_DEBUG);
esp_log_level_set("trans_tcp", ESP_LOG_DEBUG);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());

View File

@@ -3,4 +3,4 @@ CONFIG_IDF_TARGET="linux"
CONFIG_IDF_TARGET_LINUX=y
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
CONFIG_WEBSOCKET_URI="wss://echo.websocket.org"

View File

@@ -2,4 +2,4 @@ CONFIG_IDF_TARGET="linux"
CONFIG_IDF_TARGET_LINUX=y
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
CONFIG_WEBSOCKET_URI="wss://echo.websocket.org"

View File

@@ -2,4 +2,4 @@ CONFIG_IDF_TARGET="linux"
CONFIG_IDF_TARGET_LINUX=y
CONFIG_ESP_EVENT_POST_FROM_ISR=n
CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n
CONFIG_WEBSOCKET_URI="ws://echo.websocket.events"
CONFIG_WEBSOCKET_URI="wss://echo.websocket.org"

View File

@@ -1,6 +1,42 @@
# Websocket Sample application
This example will shows how to set up and communicate over a websocket.
This example shows how to set up and communicate over a websocket.
## Table of Contents
- [Hardware Required](#hardware-required)
- [Configure the project](#configure-the-project)
- [Pre-configured SDK Configurations](#pre-configured-sdk-configurations)
- [Server Certificate Verification](#server-certificate-verification)
- [Generating Self-signed Certificates](#generating-a-self-signed-certificates-with-openssl)
- [Build and Flash](#build-and-flash)
- [Testing with pytest](#testing-with-pytest)
- [Example Output](#example-output)
- [WebSocket Test Server](#websocket-test-server)
- [Python Flask Echo Server](#alternative-python-flask-echo-server)
## Quick Start
1. **Install dependencies:**
```bash
pip install -r esp-protocols/ci/requirements.txt
```
2. **Configure and build:**
```bash
idf.py menuconfig # Configure WiFi/Ethernet and WebSocket URI
idf.py build
```
3. **Flash and monitor:**
```bash
idf.py -p PORT flash monitor
```
4. **Run tests:**
```bash
pytest .
```
## How to Use Example
@@ -15,6 +51,20 @@ This example can be executed on any ESP32 board, the only required interface is
* Configure the websocket endpoint URI under "Example Configuration", if "WEBSOCKET_URI_FROM_STDIN" is selected then the example application will connect to the URI it reads from stdin (used for testing)
* To test a WebSocket client example over TLS, please enable one of the following configurations: `CONFIG_WS_OVER_TLS_MUTUAL_AUTH` or `CONFIG_WS_OVER_TLS_SERVER_AUTH`. See the sections below for more details.
### Pre-configured SDK Configurations
This example includes several pre-configured `sdkconfig.ci.*` files for different testing scenarios:
* **sdkconfig.ci** - Default configuration with WebSocket over Ethernet (IP101 PHY, ESP32, IPv6) and hardcoded URI.
* **sdkconfig.ci.plain_tcp** - WebSocket over plain TCP (no TLS, URI from stdin) using Ethernet (IP101 PHY, ESP32, IPv6).
* **sdkconfig.ci.mutual_auth** - WebSocket with mutual TLS authentication (client/server certificate verification, skips CN check) and URI from stdin.
* **sdkconfig.ci.dynamic_buffer** - WebSocket with dynamic buffer allocation, Ethernet (IP101 PHY, ESP32, IPv6), and hardcoded URI.
Example:
```
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.ci.plain_tcp" build
```
### 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.
@@ -26,7 +76,7 @@ This example can be executed on any ESP32 board, the only required interface is
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
### Generating a self signed Certificates with OpenSSL
### Generating a self signed Certificates with OpenSSL manually
* The example below outlines the process for creating new certificates for both the server and client using OpenSSL, a widely-used command line tool for implementing TLS protocol:
```
@@ -63,6 +113,46 @@ Please see the openssl man pages (man openssl) for more details.
It is **strongly recommended** to not reuse the example certificate in your application;
it is included only for demonstration.
### Certificate Generation Options
#### Option 1: Manual OpenSSL Commands
Follow the step-by-step process in the section above to understand certificate generation.
#### Option 2: Automated Script
**Note:** Test certificates are already available in the example. If you want to regenerate them or create new ones, use the provided `generate_certs.sh` script:
```bash
# Auto-detect local IP address (recommended for network testing)
./generate_certs.sh
# Specify custom hostname or IP address
./generate_certs.sh 192.168.1.100
# Use localhost (for local-only testing)
./generate_certs.sh localhost
```
This script automatically generates all required certificates in the correct directories and cleans up temporary files.
**Important:** The server certificate's Common Name (CN) must match the hostname or IP address that ESP32 clients use to connect. If not specified, the script attempts to auto-detect your local IP address. Certificate verification will fail if there's a mismatch between the CN and the actual connection address.
**CN Mismatch Handling:**
If you encounter certificate verification failures due to CN mismatch, you have two options:
1. **Recommended (Secure):** Regenerate certificates with the correct CN:
```bash
./generate_certs.sh <actual_hostname_or_ip>
```
2. **Testing Only (Less Secure):** Skip CN verification by enabling `CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK=y` in `idf.py menuconfig`.
⚠️ **WARNING:** This option disables an important security check and should **NEVER** be used in production environments. It makes your application vulnerable to man-in-the-middle attacks.
#### Option 3: Online Certificate Generators
- **mkcert**: `install mkcert` then `mkcert -install` and `mkcert localhost`
- **Let's Encrypt**: For production certificates (free, automated renewal)
- **Online generators**: Search for "self-signed certificate generator" online
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
@@ -73,7 +163,36 @@ 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.
See the [ESP-IDF Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Testing with pytest
### Install Dependencies
Before running the pytest tests, you need to install the required Python packages:
```
pip install -r esp-protocols/ci/requirements.txt
```
### Run pytest
After installing the dependencies, you can run the pytest tests:
Run all tests in current directory:
```
pytest .
```
Run specific test file:
```
pytest pytest_websocket.py
```
To specify the target device or serial port, use:
```
pytest --target esp32 --port /dev/ttyUSB0
```
## Example Output
@@ -84,7 +203,7 @@ I (4472) tcpip_adapter: eth ip: 192.168.2.137, mask: 255.255.255.0, gw: 192.168.
I (4472) example_connect: Connected to Ethernet
I (4472) example_connect: IPv4 address: 192.168.2.137
I (4472) example_connect: IPv6 address: fe80:0000:0000:0000:bedd:c2ff:fed4:a92b
I (4482) WEBSOCKET: Connecting to ws://echo.websocket.events...
I (4482) WEBSOCKET: Connecting to wss://echo.websocket.org...
I (5012) WEBSOCKET: WEBSOCKET_EVENT_CONNECTED
I (5492) WEBSOCKET: Sending hello 0000
I (6052) WEBSOCKET: WEBSOCKET_EVENT_DATA
@@ -105,9 +224,101 @@ W (9162) WEBSOCKET: Received=hello 0003
```
## Python Flask echo server
## WebSocket Test Server
By default, the `ws://echo.websocket.events` endpoint is used. You can setup a Python websocket echo server locally and try the `ws://<your-ip>:5000` endpoint. To do this, install Flask-sock Python package
### Standalone Test Server
This example includes a standalone WebSocket test server (`websocket_server.py`) that can be used for testing your ESP32 WebSocket client:
#### Quick Start
**Plain WebSocket (No TLS):**
```bash
# Plain WebSocket server (no encryption)
python websocket_server.py
# Custom port
python websocket_server.py --port 9000
```
**Server-Only Authentication:**
```bash
# TLS WebSocket server (ESP32 verifies server)
python websocket_server.py --tls
# Custom port with TLS
python websocket_server.py --port 9000 --tls
```
**Mutual Authentication:**
```bash
# TLS with client certificate verification (both verify each other)
python websocket_server.py --tls --client-verify
# Custom port with mutual authentication
python websocket_server.py --port 9000 --tls --client-verify
```
#### Server Features
- **Echo functionality** - Automatically echoes back received messages
- **TLS support** - Secure WebSocket (WSS) connections
- **Client certificate verification** - Mutual authentication support
- **Binary and text messages** - Handles both data types
- **Auto IP detection** - Shows connection URL with your local IP
#### Verification Modes
**Plain WebSocket (No TLS):**
- No certificate verification on either side
- Use for local testing or trusted networks
- Configuration: `CONFIG_WS_OVER_TLS_MUTUAL_AUTH=n` and `CONFIG_WS_OVER_TLS_SERVER_AUTH=n`
**Server-Only Authentication (`--tls` without `--client-verify`):**
- ESP32 verifies the server's certificate
- Server does NOT verify the ESP32's certificate
- Use when you trust the client but want to verify the server
- Configuration: `CONFIG_WS_OVER_TLS_SERVER_AUTH=y`
**Mutual Authentication (`--tls --client-verify`):**
- ESP32 verifies the server's certificate
- Server verifies the ESP32's certificate
- Use when both parties need to authenticate each other
- Configuration: `CONFIG_WS_OVER_TLS_MUTUAL_AUTH=y`
#### Usage Examples
**Plain WebSocket (No TLS or Client Verification):**
```bash
# Basic server (port 8080)
python websocket_server.py
# Custom port
python websocket_server.py --port 9000
```
**Server-Only Authentication (ESP32 verifies server, server doesn't verify ESP32):**
```bash
# TLS server without client verification
python websocket_server.py --tls
# Custom port with TLS
python websocket_server.py --port 9000 --tls
```
**Mutual Authentication (Both ESP32 and server verify each other's certificates):**
```bash
# TLS server with client certificate verification
python websocket_server.py --tls --client-verify
# Custom port with mutual authentication
python websocket_server.py --port 9000 --tls --client-verify
```
The server will display the connection URL (e.g., `wss://192.168.1.100:8080`) that you can use in your ESP32 configuration.
### Alternative: Python Flask Echo Server
By default, the `wss://echo.websocket.org` endpoint is used. You can also setup a Python Flask websocket echo server locally and try the `ws://<your-ip>:5000` endpoint. To do this, install Flask-sock Python package
```
pip install flask-sock
@@ -135,3 +346,36 @@ if __name__ == '__main__':
# gunicorn -b 0.0.0.0:5000 --workers 4 --threads 100 module:app
app.run(host="0.0.0.0", debug=True)
```
## Troubleshooting
### Common Issues
**Connection failed:**
- Verify WiFi/Ethernet configuration in `idf.py menuconfig`
- Check if the WebSocket server is running and accessible
- Ensure the URI is correct (use `wss://` for TLS, `ws://` for plain TCP)
**TLS certificate errors:**
- **Certificate verification failed:** The most common cause is CN mismatch. Ensure the server certificate's Common Name matches the hostname/IP you're connecting to:
- Check your connection URI (e.g., if connecting to `wss://192.168.1.100:8080`, the certificate CN must be `192.168.1.100`)
- Regenerate certificates with the correct CN: `./generate_certs.sh <your_hostname_or_ip>`
- For testing only, you can bypass CN check with `CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK=y` (NOT recommended for production)
- Verify certificate files are properly formatted and accessible
- Ensure the CA certificate used to sign the server certificate is loaded on the ESP32
**Build errors:**
- Clean build: `idf.py fullclean`
- Check ESP-IDF version compatibility
- Verify all dependencies are installed
**Test failures:**
- Ensure the device is connected and accessible via the specified port
- Check that the target device matches the configuration (`--target esp32`)
- Verify pytest dependencies are installed correctly
### Getting Help
- Check the [ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/)
- Review the [WebSocket client component documentation](../../README.md)
- Report issues on the [ESP Protocols repository](https://github.com/espressif/esp-protocols)

View File

@@ -0,0 +1,85 @@
#!/bin/bash
# Generate CA, Server, and Client certificates automatically
#
# Usage: ./generate_certs.sh [SERVER_CN]
# SERVER_CN: The Common Name (hostname or IP) for the server certificate.
# This should match the hostname/IP that ESP32 clients will use to connect.
# If not provided, the script will attempt to auto-detect the local IP address.
# Falls back to "localhost" if auto-detection fails.
#
# IMPORTANT: The server certificate's Common Name (CN) must match the hostname or IP address
# that ESP32 clients use to connect. If there's a mismatch, certificate verification will fail
# during the TLS handshake. For production use, always specify the correct hostname/IP.
# Get server hostname/IP from command line argument or auto-detect
if [ -n "$1" ]; then
SERVER_CN="$1"
echo "Using provided SERVER_CN: $SERVER_CN"
else
# Attempt to auto-detect local IP address
# Try multiple methods for better compatibility across different systems
if command -v hostname >/dev/null 2>&1; then
# Try to get IP from hostname command (works on most Unix systems)
SERVER_CN=$(hostname -I 2>/dev/null | awk '{print $1}')
fi
# If the above failed, try ifconfig (macOS and some Linux systems)
if [ -z "$SERVER_CN" ] && command -v ifconfig >/dev/null 2>&1; then
SERVER_CN=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -n1)
fi
# If still empty, try ip command (modern Linux systems)
if [ -z "$SERVER_CN" ] && command -v ip >/dev/null 2>&1; then
SERVER_CN=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -n1)
fi
# Fall back to localhost if auto-detection failed
if [ -z "$SERVER_CN" ]; then
SERVER_CN="localhost"
echo "Warning: Could not auto-detect IP address. Using 'localhost' as SERVER_CN."
echo " If your server runs on a different machine or IP, re-run with: ./generate_certs.sh <hostname_or_ip>"
else
echo "Auto-detected SERVER_CN: $SERVER_CN"
fi
fi
echo "Note: ESP32 clients must connect using: $SERVER_CN"
echo ""
# Create directories if they don't exist
mkdir -p main/certs/server
echo "Generating CA certificate..."
openssl genrsa -out main/certs/ca_key.pem 2048
openssl req -new -x509 -days 3650 -key main/certs/ca_key.pem -out main/certs/ca_cert.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=TestCA"
echo "Generating Server certificate with CN=$SERVER_CN..."
openssl genrsa -out main/certs/server/server_key.pem 2048
openssl req -new -key main/certs/server/server_key.pem -out server_csr.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=$SERVER_CN"
openssl x509 -req -days 3650 -in server_csr.pem -CA main/certs/ca_cert.pem -CAkey main/certs/ca_key.pem -CAcreateserial -out main/certs/server/server_cert.pem
echo "Generating Client certificate..."
openssl genrsa -out main/certs/client_key.pem 2048
openssl req -new -key main/certs/client_key.pem -out client_csr.pem -subj "/C=US/ST=State/L=City/O=Organization/CN=TestClient"
openssl x509 -req -days 3650 -in client_csr.pem -CA main/certs/ca_cert.pem -CAkey main/certs/ca_key.pem -CAcreateserial -out main/certs/client_cert.pem
# Clean up CSR files
rm server_csr.pem client_csr.pem
echo "Certificates generated successfully!"
echo ""
echo "Generated files:"
echo " - main/certs/ca_cert.pem (CA certificate)"
echo " - main/certs/ca_key.pem (CA private key)"
echo " - main/certs/client_cert.pem (Client certificate)"
echo " - main/certs/client_key.pem (Client private key)"
echo " - main/certs/server/server_cert.pem (Server certificate with CN=$SERVER_CN)"
echo " - main/certs/server/server_key.pem (Server private key)"
echo ""
echo "IMPORTANT: Configure ESP32 clients to connect to: $SERVER_CN"
echo " The server certificate is valid for this hostname/IP only."
echo ""
echo "Note: If the CN doesn't match your connection hostname/IP, you have two options:"
echo " 1. Regenerate certificates with correct CN: ./generate_certs.sh <correct_hostname_or_ip>"
echo " 2. Skip CN verification (TESTING ONLY): Enable CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK=y"
echo " WARNING: Option 2 reduces security and should NOT be used in production!"

View File

@@ -16,7 +16,7 @@ menu "Example Configuration"
config WEBSOCKET_URI
string "Websocket endpoint URI"
depends on WEBSOCKET_URI_FROM_STRING
default "wss://echo.websocket.events"
default "wss://echo.websocket.org"
help
URL of websocket endpoint this example connects to and sends echo

View File

@@ -4,5 +4,7 @@ dependencies:
espressif/esp_websocket_client:
version: "^1.0.0"
override_path: "../../../"
espressif/cjson:
version: "^1.7.15"
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

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
*/
@@ -77,6 +77,11 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
case WEBSOCKET_EVENT_BEGIN:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_BEGIN");
break;
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
case WEBSOCKET_EVENT_HEADER_RECEIVED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_HEADER_RECEIVED: %.*s", data->data_len, data->data_ptr);
break;
#endif
case WEBSOCKET_EVENT_CONNECTED:
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
break;
@@ -142,7 +147,11 @@ static void websocket_app_start(void)
#if CONFIG_WEBSOCKET_URI_FROM_STDIN
char line[128];
ESP_LOGI(TAG, "Please enter uri of websocket endpoint");
ESP_LOGI(TAG, "Please enter WebSocket endpoint URI");
ESP_LOGI(TAG, "Examples:");
ESP_LOGI(TAG, " ws://192.168.1.100:8080 (plain WebSocket)");
ESP_LOGI(TAG, " wss://192.168.1.100:8080 (secure WebSocket)");
ESP_LOGI(TAG, " wss://echo.websocket.org (public test server)");
get_string(line, sizeof(line));
websocket_cfg.uri = line;

View File

@@ -1,84 +1,12 @@
# 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
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_mode = 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')
from websocket_server import WebsocketServer, get_my_ip
def test_examples_protocol_websocket(dut):
@@ -228,13 +156,13 @@ def test_examples_protocol_websocket(dut):
if uri_from_stdin:
server_port = 8080
with Websocket(server_port, use_tls, client_verify) as ws:
with WebsocketServer(server_port, use_tls, client_verify) as ws:
if use_tls is True:
uri = 'wss://{}:{}'.format(get_my_ip(), server_port)
else:
uri = 'ws://{}:{}'.format(get_my_ip(), server_port)
print('DUT connecting to {}'.format(uri))
dut.expect('Please enter uri of websocket endpoint', timeout=30)
dut.expect("Please enter WebSocket endpoint URI", timeout=30)
dut.write(uri)
test_echo(dut)
test_recv_long_msg(dut, ws, 2000, 3)

View File

@@ -0,0 +1,148 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import socket
import ssl
from threading import Event, Thread
from SimpleWebSocketServer import (SimpleSSLWebSocketServer,
SimpleWebSocketServer, WebSocket)
def get_my_ip():
"""Get the local IP address of this machine."""
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):
"""WebSocket handler that echoes back received messages."""
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))
class WebsocketServer:
"""WebSocket server for testing purposes."""
def __init__(self, port, use_tls=False, client_verify=False):
self.port = port
self.use_tls = use_tls
self.client_verify = client_verify
self.exit_event = Event()
self.thread = None
self.server = None
def send_data(self, data):
"""Send data to all connected clients."""
if self.server and hasattr(self.server, 'connections'):
for nr, conn in self.server.connections.items():
conn.sendMessage(data)
def run(self):
"""Run the WebSocket server."""
if self.use_tls:
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:
ssl_context.load_verify_locations(cafile='main/certs/ca_cert.pem')
ssl_context.verify_mode = 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)
print(f"WebSocket server starting on port {self.port} (TLS: {self.use_tls}, Client verify: {self.client_verify})")
while not self.exit_event.is_set():
self.server.serveonce()
def start(self):
"""Start the server in a separate thread."""
self.thread = Thread(target=self.run)
self.thread.start()
def stop(self):
"""Stop the server."""
self.exit_event.set()
if self.thread:
self.thread.join(10)
if self.thread.is_alive():
print('Thread cannot be joined', 'orange')
def __enter__(self):
"""Context manager entry."""
self.start()
return self
def __exit__(self, exc_type, exc_value, traceback):
"""Context manager exit."""
self.stop()
def create_websocket_server(port, use_tls=False, client_verify=False):
"""Factory function to create a WebSocket server."""
return WebsocketServer(port, use_tls, client_verify)
def run_forever(port=8080, use_tls=False, client_verify=False):
"""Run WebSocket server forever (for standalone use)."""
print(f"Starting WebSocket server on port {port}")
print(f"TLS enabled: {use_tls}")
print(f"Client verification: {client_verify}")
print(f"Server IP: {get_my_ip()}")
print(f"Connect with-->{'wss' if use_tls else 'ws'}://{get_my_ip()}:{port}")
print("Press Ctrl+C to stop the server")
server = WebsocketServer(port, use_tls, client_verify)
try:
server.start()
# Wait for the server thread to complete or be interrupted
server.thread.join()
except KeyboardInterrupt:
print("\nServer stopped by user")
except Exception as e:
print(f"Server error: {e}")
finally:
server.stop()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="WebSocket Test Server")
parser.add_argument("--port", type=int, default=8080, help="Server port (default: 8080)")
parser.add_argument("--tls", action="store_true", help="Enable TLS/WSS")
parser.add_argument("--client-verify", action="store_true", help="Require client certificate verification")
args = parser.parse_args()
# Usage examples:
# python3 websocket_server.py # Plain WebSocket on port 8080
# python3 websocket_server.py --tls # TLS WebSocket on port 8080
# python3 websocket_server.py --tls --client-verify # TLS with client cert verification
# python3 websocket_server.py --port 9000 --tls # Custom port with TLS
run_forever(port=args.port, use_tls=args.tls, client_verify=args.client_verify)

View File

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

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,6 +14,7 @@
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_idf_version.h"
#include <sys/socket.h>
#include "esp_transport_ws.h"
@@ -21,6 +22,13 @@
extern "C" {
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
// Features supported in 6.0.0
#define WS_TRANSPORT_HEADER_CALLBACK_SUPPORT 1
#else
#define WS_TRANSPORT_HEADER_CALLBACK_SUPPORT 0
#endif
typedef struct esp_websocket_client *esp_websocket_client_handle_t;
ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task events family
@@ -31,6 +39,9 @@ ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task eve
typedef enum {
WEBSOCKET_EVENT_ANY = -1,
WEBSOCKET_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */
#if WS_TRANSPORT_HEADER_CALLBACK_SUPPORT
WEBSOCKET_EVENT_HEADER_RECEIVED,/*!< This event occurs for each pre-upgrade HTTP header */
#endif
WEBSOCKET_EVENT_CONNECTED, /*!< Once the Websocket has been connected to the server, no data exchange has been performed */
WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */

View File

@@ -52,14 +52,14 @@ I (18208) lws-client: LWS minimal ws client echo
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
210112: __lws_lc_tag: ++ [wsicli|0|WS/h1/default/echo.websocket.org] (1)
204800: [wsicli|0|WS/h1/default/echo.websocket.org]: 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
W (20778) lws-client: Received=echo.websocket.org sponsored by Lob.com
I (20968) lws-client: WEBSOCKET_EVENT_DATA

View File

@@ -14,7 +14,7 @@ menu "Example Configuration"
config WEBSOCKET_URI
string "Websocket endpoint URI"
default "echo.websocket.events"
default "echo.websocket.org"
help
URL or IP of websocket endpoint this example connects to and sends echo
config WEBSOCKET_PORT

View File

@@ -14,7 +14,7 @@
*/
extern int __real_mbedtls_ssl_handshake_step(mbedtls_ssl_context *ssl);
int __wrap_mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl )
int __wrap_mbedtls_ssl_handshake_step(mbedtls_ssl_context *ssl)
{
int ret = 0;

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.8.2
version: 1.9.0
version_files:
- idf_component.yml

View File

@@ -1,5 +1,25 @@
# Changelog
## [1.9.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.0)
### Features
- support null value for boolean txt records ([fa96de3b](https://github.com/espressif/esp-protocols/commit/fa96de3b))
### Bug Fixes
- Add test case for bool/NULL txt handling ([5068f221](https://github.com/espressif/esp-protocols/commit/5068f221))
- Temporary fix for build issues on IDF master ([0197c994](https://github.com/espressif/esp-protocols/commit/0197c994))
- Add tests for delegated answers ([487a746d](https://github.com/espressif/esp-protocols/commit/487a746d))
- Add fuzzing into mdns CI ([af6bb1b5](https://github.com/espressif/esp-protocols/commit/af6bb1b5))
- Host test to use hw_support include dir ([8bba3a97](https://github.com/espressif/esp-protocols/commit/8bba3a97))
- Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free ([63bf7091](https://github.com/espressif/esp-protocols/commit/63bf7091))
- put srv/txt records in additional section for ptr queries ([b7b8c5db](https://github.com/espressif/esp-protocols/commit/b7b8c5db))
### Updated
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
## [1.8.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.2)
### Bug Fixes

View File

@@ -12,6 +12,7 @@ CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_PHY_GENERIC=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5

View File

@@ -1,4 +1,4 @@
version: "1.8.2"
version: "1.9.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

@@ -1795,8 +1795,8 @@ static bool _mdns_create_answer_from_service(mdns_tx_packet_t *packet, mdns_serv
// According to RFC6763-section12.1, for DNS-SD, SRV, TXT and all address records
// should be included in additional records.
if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) ||
!_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) ||
!_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) ||
!_mdns_alloc_answer(&packet->additional, MDNS_TYPE_SRV, service, NULL, send_flush, false) ||
!_mdns_alloc_answer(&packet->additional, MDNS_TYPE_TXT, service, NULL, send_flush, false) ||
!_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush,
false) ||
!_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host,
@@ -2656,13 +2656,18 @@ static mdns_txt_linked_item_t *_mdns_allocate_txt(size_t num_items, mdns_txt_ite
mdns_mem_free(new_item);
break;
}
new_item->value = mdns_mem_strdup(txt[i].value);
if (!new_item->value) {
mdns_mem_free((char *)new_item->key);
mdns_mem_free(new_item);
break;
if (txt[i].value) {
new_item->value = mdns_mem_strdup(txt[i].value);
if (!new_item->value) {
mdns_mem_free((char *)new_item->key);
mdns_mem_free(new_item);
break;
}
new_item->value_len = strlen(new_item->value);
} else {
new_item->value = NULL;
new_item->value_len = 0;
}
new_item->value_len = strlen(new_item->value);
new_item->next = new_txt;
new_txt = new_item;
}

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
import re
@@ -92,10 +92,58 @@ class DnsPythonWrapper:
if expect is None:
expect = name
if expected:
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not found in answer section"
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not in answer section"
else:
assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section"
def parse_section(self, response, section: str, rdtype_text: str):
"""Parse a specific response section (answer, authority, additional) for given rdtype.
Returns list of textual records for that rdtype.
"""
out = []
if not response:
return out
rrsets = []
if section == 'answer':
rrsets = response.answer
elif section == 'authority':
rrsets = response.authority
elif section == 'additional':
rrsets = response.additional
else:
raise ValueError('invalid section')
for rr in rrsets:
if dns.rdatatype.to_text(rr.rdtype) != rdtype_text:
continue
for item in rr.items:
full = (
f'{rr.name} {rr.ttl} '
f'{dns.rdataclass.to_text(rr.rdclass)} '
f'{dns.rdatatype.to_text(rr.rdtype)} '
f'{item.to_text()}'
)
out.append(full)
return out
def check_additional(self, response, rdtype_text: str, owner_contains: str, expected: bool = True, expect_substr: str | None = None):
"""Check Additional section for an RR of type rdtype_text whose owner includes owner_contains.
If expect_substr is provided, also require it to appear in the textual RR.
"""
records = self.parse_section(response, 'additional', rdtype_text)
logger.info(f'additional({rdtype_text}): {records}')
def _matches(line: str) -> bool:
in_owner = owner_contains in line
has_val = (expect_substr in line) if expect_substr else True
return in_owner and has_val
found = any(_matches(r) for r in records)
if expected:
assert found, f"Expected {rdtype_text} for {owner_contains} in Additional not found"
else:
assert not found, f"Unexpected {rdtype_text} for {owner_contains} found in Additional"
if __name__ == '__main__':
if len(sys.argv) < 3:

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
@@ -65,6 +65,17 @@ def test_add_service(mdns_console, dig_app):
dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True)
def test_ptr_additional_records_for_service(dig_app):
# Query PTR for the service type and ensure SRV/TXT are in Additional (RFC 6763 §12.1)
resp = dig_app.run_query('_http._tcp.local', query_type='PTR')
# Answer section should have at least one PTR to the instance
answers = dig_app.parse_answer_section(resp, 'PTR')
assert any('test_service._http._tcp.local' in a for a in answers)
# Additional section should include SRV and TXT for the same instance
dig_app.check_additional(resp, 'SRV', 'test_service._http._tcp.local', expected=True)
dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True)
def test_remove_service(mdns_console, dig_app):
mdns_console.send_input('mdns_service_remove _http _tcp')
mdns_console.send_input('mdns_service_lookup _http _tcp')

View File

@@ -1,7 +1,9 @@
#INSTR=off
TEST_NAME=test
FUZZ=afl-fuzz
COMPONENTS_DIR=$(IDF_PATH)/components
COMPILER_ICLUDE_DIR=$(shell echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf)
# Use ESP32 toolchain include path if available, otherwise fall back to system includes for host-based compilation
COMPILER_INCLUDE_DIR=$(shell if command -v xtensa-esp32-elf-gcc >/dev/null 2>&1; then echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf; else echo /usr; fi)
CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversion -Wno-macro-redefined -Wno-int-to-void-pointer-cast -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ \
-I. -I../.. -I../../include -I../../private_include -I ./build/config \
@@ -35,7 +37,7 @@ CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversi
-I$(COMPONENTS_DIR)/xtensa/include \
-I$(COMPONENTS_DIR)/xtensa/esp32/include \
-I$(COMPONENTS_DIR)/esp_hw_support/etm/include \
-I$(COMPILER_ICLUDE_DIR)/include
-I$(COMPILER_INCLUDE_DIR)/include
MDNS_C_DEPENDENCY_INJECTION=-include mdns_di.h
@@ -77,7 +79,18 @@ $(TEST_NAME): $(OBJECTS)
@$(LD) $(OBJECTS) -o $@ $(LDLIBS)
fuzz: $(TEST_NAME)
@$(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME)
# timeout returns 124 if time limit is reached, original return code otherwise
# pass only if: fuzzing was running smoothly until timeout AND no crash found
@timeout 10m $(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME) || \
if [ $$? -eq 124 ]; then \
if [ -n "$$(find out/default/crashes -type f 2>/dev/null)" ]; then \
echo "Crashes found!"; \
tar -czf out/default/crashes.tar.gz -C out/default crashes; \
exit 1; \
fi \
else \
exit 1; \
fi
clean:
@rm -rf *.o *.SYM $(TEST_NAME) out

View File

@@ -55,8 +55,7 @@
#define pdMS_TO_TICKS(a) a
#define xSemaphoreTake(s,d) true
#define xTaskDelete(a)
#define vTaskDelete(a) free(a)
#define vTaskDelete(a) free(NULL)
#define xSemaphoreGive(s)
#define xQueueCreateMutex(s)
#define _mdns_pcb_init(a,b) true
@@ -66,7 +65,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 xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) ((void*)1)
#define vTaskDelay(m) usleep((m)*0)
#define esp_random() (rand()%UINT32_MAX)
@@ -139,4 +138,8 @@ 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);
static inline void xTaskGetStaticBuffers(void *pvTaskBuffer, void *pvStackBuffer, void *pvTaskTCB)
{
}
#endif //_ESP32_COMPAT_H_

View File

@@ -78,30 +78,20 @@ static int mdns_test_service_txt_set(const char *service, const char *proto, ui
static int mdns_test_sub_service_add(const char *sub_name, const char *service_name, const char *proto, uint32_t port)
{
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
// This is expected failure as the service thread is not running
return ESP_FAIL;
}
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
return ESP_FAIL;
}
int ret = mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name);
a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
return ret;
return mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name);
}
static int mdns_test_service_add(const char *service_name, const char *proto, uint32_t port)
{
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
// This is expected failure as the service thread is not running
return ESP_FAIL;
}
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
return ESP_FAIL;
@@ -266,9 +256,6 @@ int main(int argc, char **argv)
}
#ifndef MDNS_NO_SERVICES
mdns_service_remove_all();
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
#endif
ForceTaskDelete();
mdns_free();

View File

@@ -61,6 +61,45 @@ TEST(mdns, init_deinit)
esp_event_loop_delete_default();
}
TEST(mdns, boolean_txt_null_value)
{
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_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));
mdns_txt_item_t txt_data[] = {
{"bool", NULL},
{"key", "value"},
};
const size_t txt_data_count = sizeof(txt_data) / sizeof(txt_data[0]);
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, txt_data, txt_data_count));
yield_to_all_priorities();
TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results));
TEST_ASSERT_NOT_EQUAL(NULL, results);
TEST_ASSERT_NOT_EQUAL(NULL, results->txt);
TEST_ASSERT_EQUAL(txt_data_count, results->txt_count);
bool found_bool = false;
for (size_t i = 0; i < results->txt_count; ++i) {
if (strcmp(results->txt[i].key, "bool") == 0) {
TEST_ASSERT_NOT_EQUAL(NULL, results->txt_value_len);
TEST_ASSERT_EQUAL_UINT8(0, results->txt_value_len[i]);
found_bool = true;
}
}
TEST_ASSERT_TRUE(found_bool);
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO));
mdns_free();
esp_event_loop_delete_default();
}
TEST(mdns, api_fails_with_expected_err)
{
mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL},
@@ -290,6 +329,7 @@ TEST_GROUP_RUNNER(mdns)
RUN_TEST_CASE(mdns, init_deinit)
RUN_TEST_CASE(mdns, add_remove_service)
RUN_TEST_CASE(mdns, add_remove_deleg_service)
RUN_TEST_CASE(mdns, boolean_txt_null_value)
}