mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-23 07:12:32 +01:00
Compare commits
56 Commits
eppp-v1.1.
...
websocket-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d4712b905 | ||
|
|
67188fd7b4 | ||
|
|
4d7c6848b2 | ||
|
|
1e83bee4fe | ||
|
|
318bca1657 | ||
|
|
9e1b9cdd20 | ||
|
|
7f424325d8 | ||
|
|
9ef228f247 | ||
|
|
5068f2217e | ||
|
|
1ceb42c5a2 | ||
|
|
e52a5757f1 | ||
|
|
732cd29ec0 | ||
|
|
7a203cf085 | ||
|
|
d665e6f18e | ||
|
|
2e269640c6 | ||
|
|
c078c36361 | ||
|
|
90ddb04e53 | ||
|
|
cee3bdea9d | ||
|
|
fa96de3bd7 | ||
|
|
18f0d02806 | ||
|
|
bfa604b5f6 | ||
|
|
92a31187ff | ||
|
|
0197c994ee | ||
|
|
487a746d14 | ||
|
|
af6bb1b5ee | ||
|
|
f5e62e83e9 | ||
|
|
cad527d2fc | ||
|
|
16cc2dcffb | ||
|
|
d622e41a54 | ||
|
|
ca6e39aac7 | ||
|
|
e45944e143 | ||
|
|
fe657b9737 | ||
|
|
453be4cd79 | ||
|
|
018ba58ec5 | ||
|
|
67c682d911 | ||
|
|
91915ce1c7 | ||
|
|
ae052e5507 | ||
|
|
44524f5de0 | ||
|
|
1ace92c279 | ||
|
|
7a6cf0f9c0 | ||
|
|
54eb002758 | ||
|
|
318e41b3c3 | ||
|
|
6f6237a0cc | ||
|
|
18faeb3dfa | ||
|
|
296123c14e | ||
|
|
4e178f06bd | ||
|
|
5ab7e8327e | ||
|
|
91e7e9fa08 | ||
|
|
ff5d6021be | ||
|
|
2432e41dcb | ||
|
|
870ac91db7 | ||
|
|
94bd5b074a | ||
|
|
db7baaffba | ||
|
|
1ea93a866b | ||
|
|
92e1460721 | ||
|
|
b7b8c5dbd7 |
21
.github/workflows/mdns__build-target-test.yml
vendored
21
.github/workflows/mdns__build-target-test.yml
vendored
@@ -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
|
||||
|
||||
40
.github/workflows/mdns__host-tests.yml
vendored
40
.github/workflows/mdns__host-tests.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -6,3 +6,4 @@ dpkt
|
||||
pytest
|
||||
idf_build_apps
|
||||
netifaces
|
||||
esptool>=5.1.0
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
#define MPPE_SUPPORT 0
|
||||
|
||||
#define PPP_MAXIDLEFLAG 0
|
||||
#define PPP_MAXIDLEFLAG 100
|
||||
|
||||
#define PRINTPKT_SUPPORT 1
|
||||
#define PPP_PROTOCOLNAME 1
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
85
components/esp_websocket_client/examples/target/generate_certs.sh
Executable file
85
components/esp_websocket_client/examples/target/generate_certs.sh
Executable 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!"
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user