mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-06-25 17:31:33 +02:00
Compare commits
451 Commits
websocket-
...
websocket-
Author | SHA1 | Date | |
---|---|---|---|
5c245dbdb5 | |||
134a9a9eee | |||
010f98ca80 | |||
6e4e4fab1d | |||
b6852a0588 | |||
415e04a55f | |||
d1129f3d19 | |||
973837dd66 | |||
36de9afe0c | |||
0d5081b841 | |||
85c7282641 | |||
57afa38f49 | |||
238ee96783 | |||
10e6348e09 | |||
a286634359 | |||
4cdfc857ad | |||
371a32d4ae | |||
dc4e54e99a | |||
d66f9dc158 | |||
4980ac85b7 | |||
626eb52d04 | |||
82b1aa9280 | |||
e8145fcede | |||
9c3e24b6cf | |||
b4d85d6aae | |||
0b102f6286 | |||
06e7baf2c2 | |||
3662c149fb | |||
a243d7e878 | |||
ef1bae5cdd | |||
4cefcd3606 | |||
a177b3d024 | |||
b7e73d25a7 | |||
9b403d477f | |||
d723fb7a5a | |||
9905fe4cf6 | |||
055f051f53 | |||
8dc3e5835c | |||
0cf08fb676 | |||
ac7bf465d2 | |||
2cf7518114 | |||
057a5d2db8 | |||
63bff632df | |||
938ddf16b9 | |||
0c84c9750c | |||
952e5df477 | |||
e3c4391246 | |||
98138189c9 | |||
30dae8f7ed | |||
b76d3fbff9 | |||
f605fdd632 | |||
abbc8d9c5a | |||
a0297743dd | |||
3bcc46276a | |||
f0d0698582 | |||
91262baede | |||
cb2375827b | |||
cc0f2b3cf0 | |||
47d57a5b14 | |||
278b7bef3c | |||
a165452ca1 | |||
55f95fb790 | |||
9b76388163 | |||
0d1d73eb35 | |||
88d3eda156 | |||
622a360e97 | |||
4358c3ceab | |||
f00c3be139 | |||
c05558ba28 | |||
c0c1a65598 | |||
dab12309e2 | |||
789670e8c5 | |||
131613c5cc | |||
919091766d | |||
4e4ab0908d | |||
54e3a85983 | |||
98dfb691b6 | |||
ff7d214f9c | |||
ee6dbbfeaa | |||
a14331ad01 | |||
f3754a3683 | |||
c1176ccdcc | |||
df51e60ac0 | |||
9e83b1eb0e | |||
f82cbfde7b | |||
96d13293ab | |||
3b49d1f559 | |||
3e7591e92a | |||
de830e51d4 | |||
21c0878f0e | |||
a96c890f97 | |||
86d107635b | |||
57672d5add | |||
e8ea8786d2 | |||
142200c7ca | |||
6c47cfe30a | |||
0ae8c3ca33 | |||
5472d5c52f | |||
51a50db0fd | |||
e1660a1806 | |||
84f18bddfb | |||
609594a849 | |||
66e6d4cbf8 | |||
4868689111 | |||
f3ff98bb82 | |||
58a0b57e12 | |||
3fd4391c38 | |||
a16aab6979 | |||
1e0aefd72a | |||
ebf122bd36 | |||
5addf9e885 | |||
6dbfc69627 | |||
128c0a2d87 | |||
1f91d248f5 | |||
8fe2a3a661 | |||
417591b99f | |||
d56b5d90ea | |||
b6b20ad399 | |||
8863ed944d | |||
78001ec871 | |||
4a52cf23f5 | |||
00e7675913 | |||
87c269911d | |||
eb536a74a0 | |||
b720d02fca | |||
3e93ea9b76 | |||
2c764b1b7a | |||
f836ae7f65 | |||
e9a1c26e0c | |||
4b5f24f5b8 | |||
05675c7d63 | |||
30f37c0565 | |||
ddc58e8220 | |||
fa951bf320 | |||
605d1fab73 | |||
58bf2186f7 | |||
d39a4c744e | |||
ec491ec536 | |||
defdfd59b6 | |||
7f42c31252 | |||
94ae672041 | |||
e5a3a3df1d | |||
7710ea9a11 | |||
034c55e18a | |||
ec03fec3d3 | |||
5909e9e54c | |||
82e2a5dcc1 | |||
48e4d4035c | |||
ba3fa24a96 | |||
869f5b75af | |||
ac6dcb6830 | |||
085dbd8c4e | |||
f78e8cfce8 | |||
6cdf5ee074 | |||
fcb5515f1e | |||
9fdbe5f130 | |||
20e6e9e7fe | |||
3c5b13ea0d | |||
c588263b45 | |||
6258edf23b | |||
c8b0d5ea9d | |||
5252b1d801 | |||
4e11cc86fe | |||
7af91ec490 | |||
01256d3e34 | |||
5a2d4eab6d | |||
9de3f534e2 | |||
bcabc8ea16 | |||
dfb27b39cc | |||
076c095aec | |||
7dd0bc1fff | |||
7e82a7cef7 | |||
ae381b779f | |||
d0f4e68c7a | |||
941dc5c42f | |||
525c64915e | |||
f0839d909b | |||
69902ea8e1 | |||
d5001e894f | |||
145a8d2291 | |||
d0bbe880b6 | |||
4a9d55edf7 | |||
be2a924674 | |||
76fcd4128a | |||
fd8499c874 | |||
38b4fe2353 | |||
eb7eeb58d3 | |||
b26606252f | |||
5e087d82d6 | |||
91a3d95f96 | |||
6d6dd2b75e | |||
0611b0cb67 | |||
cf6ed1cc53 | |||
52306e914f | |||
d37ab6dd25 | |||
05dcd8f0ee | |||
5c55ea6e02 | |||
0c71c7bfe1 | |||
4c368c0090 | |||
c2abeff476 | |||
6a92d3253f | |||
af2275341e | |||
b0957e70fd | |||
47c7266103 | |||
40da0d29be | |||
5f6b6f9273 | |||
8a120829e2 | |||
46f28a8011 | |||
5a81eaea3f | |||
2ddaee2b6e | |||
73b1763029 | |||
27fc285000 | |||
93e6efedc7 | |||
bc4cda8ea7 | |||
8a8d58d4dc | |||
402baebfee | |||
9fa25ef3b6 | |||
121b525108 | |||
418fb60dd9 | |||
4049b3b5ed | |||
c8821199a2 | |||
1eb5df9780 | |||
b62b4b3e25 | |||
4d8aec1ad3 | |||
6d649102ab | |||
3ad559bf3f | |||
ab3fa69b8f | |||
c3a5826d60 | |||
cbcbe4ffd7 | |||
adc34309dc | |||
7a8329cb5c | |||
c30617d872 | |||
1e5eeb16ec | |||
22c7c0a195 | |||
eda5d72acf | |||
1623c0e729 | |||
b114ed69de | |||
49a00c2455 | |||
dec1d7dcf8 | |||
2ffd22382d | |||
487287157d | |||
1fe901f70f | |||
2cf9fd8891 | |||
89439e0a9b | |||
becd5d0266 | |||
0d7a30944e | |||
e2653e7fb0 | |||
df6a208f45 | |||
1fdffbbbab | |||
d5566971fb | |||
988d120902 | |||
fd47df3e30 | |||
2cb3a6e35e | |||
ccd48bc9a9 | |||
825652f3e1 | |||
fc53888115 | |||
7635c0479b | |||
d5fe42bffb | |||
e0bc60a586 | |||
9772e49b26 | |||
110f1f652d | |||
ed77d65a82 | |||
6021a88657 | |||
78f71ecdf6 | |||
07f57523c8 | |||
ad67a23097 | |||
078abd8161 | |||
f1f6c5de05 | |||
12fb6d8e15 | |||
2258d5bcef | |||
54f5c6f29c | |||
3319844745 | |||
ac70c9aba8 | |||
123ae8db62 | |||
e24cc7d1ae | |||
9d9aac1569 | |||
ef9a758662 | |||
2a23f355c7 | |||
07399011f7 | |||
2b7d43e1f8 | |||
7bf23a7fe9 | |||
881ca095f0 | |||
d9fa457b4f | |||
4eb3e89841 | |||
74aee42f80 | |||
b5e5a64e7f | |||
6713ffedcc | |||
817c4fd2e8 | |||
8f0dc6d578 | |||
7a3aa26df8 | |||
c7ff8ba0c9 | |||
80ce63d73e | |||
d20666f3a0 | |||
cd3cc0be3b | |||
1734e59057 | |||
662a4ce050 | |||
fb1de80fd7 | |||
1f35e9a728 | |||
573855031d | |||
d1c62628b8 | |||
48b819bbc1 | |||
dfcefc38fd | |||
879a6cdfa3 | |||
53e2aa3241 | |||
3cc64469c1 | |||
248b11bb0a | |||
d52dddfea5 | |||
67d310b988 | |||
901124b7ee | |||
f6ff165be9 | |||
d8d6b35553 | |||
f44c569422 | |||
7dfe14c83d | |||
286c646725 | |||
0582187b9a | |||
28d5b74a00 | |||
c70d527d80 | |||
7cdf96cffa | |||
407875d9c5 | |||
3e753f5e2d | |||
aaba3fc47a | |||
144d4ad1d4 | |||
e431b6b7fe | |||
ed71a239a8 | |||
680bad646f | |||
271665e0cb | |||
7fb6686716 | |||
4912bef740 | |||
181a22ec2b | |||
4172219225 | |||
9a0803ad7e | |||
05ddd5f0e4 | |||
7e3b35efd9 | |||
98d2c1a073 | |||
84cbb1f3cf | |||
4c6818ee97 | |||
c440114d14 | |||
c0f65a6997 | |||
778eaa6ced | |||
25f8656fbc | |||
6ea0ea93fa | |||
d9aec9fc71 | |||
985e69117e | |||
75deebbf03 | |||
fdd27dc9fa | |||
2ec3b558ea | |||
450cbf03cf | |||
34f6d8dd33 | |||
b6efc688b5 | |||
11e4aebefd | |||
90e4babc61 | |||
c546ab8dea | |||
6582b41cd1 | |||
358d26c8a1 | |||
8d08e5ed95 | |||
2ac83d0f27 | |||
98e3171db6 | |||
2f85c075be | |||
b30a7fec27 | |||
7a4fdad16d | |||
b4e57424f9 | |||
2763bcdb8d | |||
dce0b26ef8 | |||
ade4aeffa5 | |||
8cd0e8a501 | |||
9b3b41c3f1 | |||
ea2300753e | |||
81c219d4ee | |||
0c17121ad7 | |||
67173f6770 | |||
fed787f54f | |||
b4ab30b5de | |||
dd714947d6 | |||
39de491597 | |||
19acac76eb | |||
0191d6fcd7 | |||
b26c8665f8 | |||
98069f9ca2 | |||
ad29d34bb6 | |||
9f1be3668e | |||
450c9de67b | |||
c7701d41f8 | |||
b9726db48e | |||
f1ccc4052e | |||
84bd1d7e88 | |||
259d3fc609 | |||
6d99957f2d | |||
7784d002fc | |||
9ebd9852ca | |||
26f00e8ddb | |||
bce7d5231c | |||
ef924f1aa5 | |||
ad8c92db52 | |||
3aa605fe24 | |||
00a72b8920 | |||
907e7ee29e | |||
b367484361 | |||
4a8582f500 | |||
75de31cea3 | |||
4acf639afc | |||
1a1cf71a84 | |||
5924dafd94 | |||
91bb5095f5 | |||
caa4884b3a | |||
441a53c604 | |||
7fbf8e5247 | |||
7346ed9765 | |||
76298ff70e | |||
0875008a46 | |||
a661e51f7e | |||
3ced2d9709 | |||
ba88d7fdbc | |||
4f3c4299d6 | |||
2a4e684848 | |||
541b391c3e | |||
279a3cb6e0 | |||
60bb0eea99 | |||
52de8f1b2a | |||
503218b3ba | |||
38149c8d9b | |||
1984da150d | |||
9e37f537bd | |||
e55f54b69e | |||
6e4d8a19ed | |||
e98cf16859 | |||
be3d2ece55 | |||
15ed885035 | |||
b72a9ae710 | |||
0733ea8ff4 | |||
15cbc9bd50 | |||
bb7f198bea | |||
d3f7ea67fb | |||
6e5bede4c7 | |||
d191a720d1 | |||
cc0d52793b | |||
804a8d5df6 | |||
74040cfd1a | |||
16ea3f7eb8 | |||
1f5eb396c5 | |||
827bebd2bc | |||
745201b188 | |||
ca266cabd0 | |||
e54b240870 | |||
4ccc32ffb5 | |||
91a177edd4 | |||
f303cdc70b | |||
ab8c2da395 | |||
04a7643d67 | |||
8b3d420055 | |||
50b083a58c | |||
5d27b2681a |
36
.github/workflows/build.yml
vendored
36
.github/workflows/build.yml
vendored
@ -1,36 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v4.1", "release-v4.2", "release-v4.3", "release-v4.4"]
|
||||
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
|
||||
idf_target: ["esp32"]
|
||||
exclude:
|
||||
- idf_ver: "release-v4.1"
|
||||
example: modem_console
|
||||
- idf_ver: "release-v4.1"
|
||||
example: ap_to_pppos
|
||||
- idf_ver: "release-v4.1"
|
||||
example: simple_cmux_client
|
||||
- idf_ver: "release-v4.2"
|
||||
example: simple_cmux_client
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_modem/examples/${{ matrix.example }}
|
||||
idf.py build
|
2
.github/workflows/host-test.yml
vendored
2
.github/workflows/host-test.yml
vendored
@ -4,7 +4,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
host_test:
|
||||
name: Build and test
|
||||
name: Build and Test on Host
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:release-v4.3
|
||||
env:
|
||||
|
@ -1,39 +0,0 @@
|
||||
name: Docs and Publish Websockets
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
docs_build:
|
||||
name: Docs-Build-And-Upload
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Generate docs
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip git
|
||||
sudo git clone https://github.com/espressif/esp-idf
|
||||
python -m pip install breathe recommonmark
|
||||
python -m pip install -r esp-idf/docs/requirements.txt
|
||||
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
cd $GITHUB_WORKSPACE/docs
|
||||
touch .nojekyll
|
||||
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a>' >> index.html
|
||||
|
||||
- name: Deploy generated docs.
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.5
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: docs
|
30
.github/workflows/publish-docs-component.yml
vendored
30
.github/workflows/publish-docs-component.yml
vendored
@ -21,20 +21,42 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip
|
||||
python -m pip install breathe recommonmark
|
||||
python -m pip install breathe recommonmark esp-docs
|
||||
cd $GITHUB_WORKSPACE/components/esp_modem/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/esp_modem
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/esp_modem
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/esp_websocket_client
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/mdns/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/mdns/en
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/mdns/zh_CN
|
||||
cp -r html_en/. $GITHUB_WORKSPACE/docs/mdns/en
|
||||
cp -r html_zh_CN/. $GITHUB_WORKSPACE/docs/mdns/zh_CN
|
||||
|
||||
cd $GITHUB_WORKSPACE/components/asio/docs
|
||||
./generate_docs
|
||||
mkdir -p $GITHUB_WORKSPACE/docs/asio
|
||||
cp -r html/. $GITHUB_WORKSPACE/docs/asio
|
||||
|
||||
cd $GITHUB_WORKSPACE/docs
|
||||
touch .nojekyll
|
||||
echo '<a href="esp_modem/index.html">esp-modem</a>' > index.html
|
||||
echo '<a href="esp_modem/index.html">esp-modem</a><br>' > index.html
|
||||
echo '<a href="esp_websocket_client/index.html">esp-websocket-client</a><br>' >> index.html
|
||||
echo '<a href="asio/index.html">ASIO</a><br>' >> index.html
|
||||
echo '<a href="mdns/en/index.html">mDNS_en</a><br>' >> index.html
|
||||
echo '<a href="mdns/zh_CN/index.html">mDNS_zh_CN</a><br>' >> index.html
|
||||
|
||||
|
||||
- name: Upload components to component service
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
with:
|
||||
directories: "components/esp_modem"
|
||||
name: "esp_modem"
|
||||
directories: "components/esp_modem;components/esp_websocket_client;components/mdns;components/asio"
|
||||
namespace: "espressif"
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
|
||||
|
169
.github/workflows/target-test.yml
vendored
Normal file
169
.github/workflows/target-test.yml
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
name: Build and Run Target tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_esp_modem:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v4.1", "release-v4.2", "release-v4.3", "release-v4.4"]
|
||||
example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"]
|
||||
idf_target: ["esp32"]
|
||||
exclude:
|
||||
- idf_ver: "release-v4.1"
|
||||
example: modem_console
|
||||
- idf_ver: "release-v4.1"
|
||||
example: ap_to_pppos
|
||||
- idf_ver: "release-v4.1"
|
||||
example: simple_cmux_client
|
||||
- idf_ver: "release-v4.2"
|
||||
example: simple_cmux_client
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_modem/examples/${{ matrix.example }}
|
||||
idf.py build
|
||||
|
||||
build_mdns:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2", "esp32c3"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/examples/
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
cat sdkconfig.ci.eth_def >> sdkconfig.defaults
|
||||
idf.py build
|
||||
rm sdkconfig.defaults
|
||||
cat sdkconfig.ci.eth_custom_netif >> sdkconfig.defaults
|
||||
idf.py build
|
||||
rm sdkconfig.defaults
|
||||
cat sdkconfig.ci.eth_socket >> sdkconfig.defaults
|
||||
idf.py build
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_apps/
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
idf.py build
|
||||
|
||||
build_asio:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32", "esp32s2"]
|
||||
example: ["asio_chat", "async_request", "socks4", "ssl_client_server", "tcp_echo_server", "udp_echo_server"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
path: esp-protocols
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/asio/examples/${{ matrix.example }}
|
||||
idf.py set-target ${{ matrix.idf_target }}
|
||||
idf.py build
|
||||
|
||||
build_websocket:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
working-directory: components/esp_websocket_client/examples/
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cat sdkconfig.ci >> sdkconfig.defaults
|
||||
idf.py build
|
||||
- name: Merge binaries
|
||||
working-directory: components/esp_websocket_client/examples/build
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: components/esp_websocket_client/examples/build/
|
||||
if-no-files-found: error
|
||||
|
||||
run-target:
|
||||
name: Run Websocket Example Test on target
|
||||
needs: build_websocket
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_target: ["esp32"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
container:
|
||||
image: python:3.7-buster
|
||||
options: --privileged # Privileged mode has access to serial ports
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: components/esp_websocket_client/examples/build/
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
pip install -r $GITHUB_WORKSPACE/components/esp_websocket_client/examples/requirements.txt
|
||||
- name: Download Example Test to target
|
||||
run: python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 components/esp_websocket_client/examples/build/flash_image.bin
|
||||
- name: Run Example Test on target
|
||||
working-directory: components/esp_websocket_client/examples
|
||||
run: |
|
||||
cp sdkconfig.ci sdkconfig.defaults
|
||||
pytest --log-cli-level DEBUG --junit-xml=./test_app_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml --target=${{ matrix.idf_target }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
|
||||
path: examples/*.xml
|
@ -1,4 +1,4 @@
|
||||
name: Build Websockets
|
||||
name: AFL fuzzer compilation test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@ -16,11 +16,15 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
path: esp-protocols
|
||||
- name: Install Necessary Libs
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev
|
||||
- name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
|
||||
env:
|
||||
IDF_TARGET: ${{ matrix.idf_target }}
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/esp_websocket_client/examples/
|
||||
idf.py build
|
||||
cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_afl_fuzz_host/
|
||||
make INSTR=off
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "components/asio/asio"]
|
||||
path = components/asio/asio
|
||||
url = https://github.com/espressif/asio
|
16
README.md
16
README.md
@ -8,3 +8,19 @@
|
||||
|
||||
* Brief introduction [README](components/esp_modem/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_modem/index.html)
|
||||
|
||||
### mDNS
|
||||
|
||||
* Brief introduction [README](components/mdns/README.md)
|
||||
* Full html [documentation(English)](https://espressif.github.io/esp-protocols/mdns/en/index.html)
|
||||
* Full html [documentation(Chinese)](https://espressif.github.io/esp-protocols/mdns/zh_CN/index.html)
|
||||
|
||||
### esp_websocket_client
|
||||
|
||||
* Brief introduction [README](components/esp_websocket_client/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/esp_websocket_client/index.html)
|
||||
|
||||
### ASIO port
|
||||
|
||||
* Brief introduction [README](components/asio/README.md)
|
||||
* Full html [documentation](https://espressif.github.io/esp-protocols/asio/index.html)
|
||||
|
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "connect.c" "stdin_out.c" "addr_from_stdin.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES esp_netif driver esp_eth esp_wifi vfs
|
||||
)
|
322
common_components/protocol_examples_common/Kconfig.projbuild
Normal file
322
common_components/protocol_examples_common/Kconfig.projbuild
Normal file
@ -0,0 +1,322 @@
|
||||
menu "Example Connection Configuration"
|
||||
|
||||
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||
|
||||
config EXAMPLE_CONNECT_WIFI
|
||||
bool "connect using WiFi interface"
|
||||
default y
|
||||
help
|
||||
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
|
||||
Choose this option to connect with WiFi
|
||||
|
||||
if EXAMPLE_CONNECT_WIFI
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config EXAMPLE_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
Can be left blank if the network has no security set.
|
||||
|
||||
choice EXAMPLE_WIFI_SCAN_METHOD
|
||||
prompt "WiFi Scan Method"
|
||||
default EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
|
||||
help
|
||||
WiFi scan method:
|
||||
|
||||
If "Fast" is selected, scan will end after find SSID match AP.
|
||||
|
||||
If "All Channel" is selected, scan will end after scan all the channel.
|
||||
|
||||
config EXAMPLE_WIFI_SCAN_METHOD_FAST
|
||||
bool "Fast"
|
||||
config EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
|
||||
bool "All Channel"
|
||||
endchoice
|
||||
|
||||
menu "WiFi Scan threshold"
|
||||
config EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD
|
||||
int "WiFi minimum rssi"
|
||||
range -127 0
|
||||
|
||||
default -127
|
||||
help
|
||||
The minimum rssi to accept in the scan mode.
|
||||
|
||||
choice EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD
|
||||
prompt "WiFi Scan auth mode threshold"
|
||||
default EXAMPLE_WIFI_AUTH_OPEN
|
||||
help
|
||||
The weakest authmode to accept in the scan mode.
|
||||
|
||||
config EXAMPLE_WIFI_AUTH_OPEN
|
||||
bool "OPEN"
|
||||
config EXAMPLE_WIFI_AUTH_WEP
|
||||
bool "WEP"
|
||||
config EXAMPLE_WIFI_AUTH_WPA_PSK
|
||||
bool "WPA PSK"
|
||||
config EXAMPLE_WIFI_AUTH_WPA2_PSK
|
||||
bool "WPA2 PSK"
|
||||
config EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK
|
||||
bool "WPA WPA2 PSK"
|
||||
config EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE
|
||||
bool "WPA2 ENTERPRISE"
|
||||
config EXAMPLE_WIFI_AUTH_WPA3_PSK
|
||||
bool "WPA3 PSK"
|
||||
config EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK
|
||||
bool "WPA2 WPA3 PSK"
|
||||
config EXAMPLE_WIFI_AUTH_WAPI_PSK
|
||||
bool "WAPI PSK"
|
||||
endchoice
|
||||
endmenu
|
||||
|
||||
choice EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD
|
||||
prompt "WiFi Connect AP Sort Method"
|
||||
default EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
help
|
||||
WiFi connect AP sort method:
|
||||
|
||||
If "Signal" is selected, Sort matched APs in scan list by RSSI.
|
||||
|
||||
If "Security" is selected, Sort matched APs in scan list by security mode.
|
||||
|
||||
config EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
bool "Signal"
|
||||
config EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
|
||||
bool "Security"
|
||||
endchoice
|
||||
endif
|
||||
|
||||
config EXAMPLE_CONNECT_ETHERNET
|
||||
bool "connect using Ethernet interface"
|
||||
default n
|
||||
help
|
||||
Protocol examples can use Wi-Fi and/or Ethernet to connect to the network.
|
||||
Choose this option to connect with Ethernet
|
||||
|
||||
if EXAMPLE_CONNECT_ETHERNET
|
||||
config EXAMPLE_USE_SPI_ETHERNET
|
||||
bool
|
||||
|
||||
choice EXAMPLE_ETHERNET_TYPE
|
||||
prompt "Ethernet Type"
|
||||
default EXAMPLE_USE_INTERNAL_ETHERNET if IDF_TARGET_ESP32
|
||||
default EXAMPLE_USE_W5500
|
||||
help
|
||||
Select which kind of Ethernet will be used in the example.
|
||||
|
||||
config EXAMPLE_USE_INTERNAL_ETHERNET
|
||||
depends on IDF_TARGET_ESP32
|
||||
select ETH_USE_ESP32_EMAC
|
||||
bool "Internal EMAC"
|
||||
help
|
||||
Select internal Ethernet MAC controller.
|
||||
|
||||
config EXAMPLE_USE_DM9051
|
||||
bool "DM9051 Module"
|
||||
select EXAMPLE_USE_SPI_ETHERNET
|
||||
select ETH_USE_SPI_ETHERNET
|
||||
select ETH_SPI_ETHERNET_DM9051
|
||||
help
|
||||
Select external SPI-Ethernet module.
|
||||
|
||||
config EXAMPLE_USE_W5500
|
||||
bool "W5500 Module"
|
||||
select EXAMPLE_USE_SPI_ETHERNET
|
||||
select ETH_USE_SPI_ETHERNET
|
||||
select ETH_SPI_ETHERNET_W5500
|
||||
help
|
||||
Select external SPI-Ethernet module (W5500).
|
||||
|
||||
config EXAMPLE_USE_OPENETH
|
||||
bool "OpenCores Ethernet MAC (EXPERIMENTAL)"
|
||||
select ETH_USE_OPENETH
|
||||
help
|
||||
When this option is enabled, the example is built with support for
|
||||
OpenCores Ethernet MAC, which allows testing the example in QEMU.
|
||||
Note that this option is used for internal testing purposes, and
|
||||
not officially supported. Examples built with this option enabled
|
||||
will not run on a real ESP32 chip.
|
||||
|
||||
endchoice # EXAMPLE_ETHERNET_TYPE
|
||||
|
||||
if EXAMPLE_USE_INTERNAL_ETHERNET
|
||||
choice EXAMPLE_ETH_PHY_MODEL
|
||||
prompt "Ethernet PHY Device"
|
||||
default EXAMPLE_ETH_PHY_IP101
|
||||
help
|
||||
Select the Ethernet PHY device to use in the example.
|
||||
|
||||
config EXAMPLE_ETH_PHY_IP101
|
||||
bool "IP101"
|
||||
help
|
||||
IP101 is a single port 10/100 MII/RMII/TP/Fiber Fast Ethernet Transceiver.
|
||||
Goto http://www.icplus.com.tw/pp-IP101G.html for more information about it.
|
||||
|
||||
config EXAMPLE_ETH_PHY_RTL8201
|
||||
bool "RTL8201/SR8201"
|
||||
help
|
||||
RTL8201F/SR8201F is a single port 10/100Mb Ethernet Transceiver with auto MDIX.
|
||||
Goto http://www.corechip-sz.com/productsview.asp?id=22 for more information about it.
|
||||
|
||||
config EXAMPLE_ETH_PHY_LAN87XX
|
||||
bool "LAN87xx"
|
||||
help
|
||||
Below chips are supported:
|
||||
LAN8710A is a small footprint MII/RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
|
||||
flexPWR® Technology.
|
||||
LAN8720A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX Support.
|
||||
LAN8740A/LAN8741A is a small footprint MII/RMII 10/100 Energy Efficient Ethernet Transceiver
|
||||
with HP Auto-MDIX and flexPWR® Technology.
|
||||
LAN8742A is a small footprint RMII 10/100 Ethernet Transceiver with HP Auto-MDIX and
|
||||
flexPWR® Technology.
|
||||
Goto https://www.microchip.com for more information about them.
|
||||
|
||||
config EXAMPLE_ETH_PHY_DP83848
|
||||
bool "DP83848"
|
||||
help
|
||||
DP83848 is a single port 10/100Mb/s Ethernet Physical Layer Transceiver.
|
||||
Goto http://www.ti.com/product/DP83848J for more information about it.
|
||||
|
||||
config EXAMPLE_ETH_PHY_KSZ80XX
|
||||
bool "KSZ80xx"
|
||||
help
|
||||
With the KSZ80xx series, Microchip offers single-chip 10BASE-T/100BASE-TX
|
||||
Ethernet Physical Layer Tranceivers (PHY).
|
||||
The following chips are supported: KSZ8001, KSZ8021, KSZ8031, KSZ8041,
|
||||
KSZ8051, KSZ8061, KSZ8081, KSZ8091
|
||||
Goto https://www.microchip.com for more information about them.
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_ETH_MDC_GPIO
|
||||
int "SMI MDC GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 23
|
||||
help
|
||||
Set the GPIO number used by SMI MDC.
|
||||
|
||||
config EXAMPLE_ETH_MDIO_GPIO
|
||||
int "SMI MDIO GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 18
|
||||
help
|
||||
Set the GPIO number used by SMI MDIO.
|
||||
endif
|
||||
|
||||
if EXAMPLE_USE_SPI_ETHERNET
|
||||
config EXAMPLE_ETH_SPI_HOST
|
||||
int "SPI Host Number"
|
||||
range 0 2
|
||||
default 1
|
||||
help
|
||||
Set the SPI host used to communicate with the SPI Ethernet Controller.
|
||||
|
||||
config EXAMPLE_ETH_SPI_SCLK_GPIO
|
||||
int "SPI SCLK GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 14
|
||||
help
|
||||
Set the GPIO number used by SPI SCLK.
|
||||
|
||||
config EXAMPLE_ETH_SPI_MOSI_GPIO
|
||||
int "SPI MOSI GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 13
|
||||
help
|
||||
Set the GPIO number used by SPI MOSI.
|
||||
|
||||
config EXAMPLE_ETH_SPI_MISO_GPIO
|
||||
int "SPI MISO GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||
default 12
|
||||
help
|
||||
Set the GPIO number used by SPI MISO.
|
||||
|
||||
config EXAMPLE_ETH_SPI_CS_GPIO
|
||||
int "SPI CS GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 15
|
||||
help
|
||||
Set the GPIO number used by SPI CS.
|
||||
|
||||
config EXAMPLE_ETH_SPI_CLOCK_MHZ
|
||||
int "SPI clock speed (MHz)"
|
||||
range 5 80
|
||||
default 36
|
||||
help
|
||||
Set the clock speed (MHz) of SPI interface.
|
||||
|
||||
config EXAMPLE_ETH_SPI_INT_GPIO
|
||||
int "Interrupt GPIO number"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||
default 4
|
||||
help
|
||||
Set the GPIO number used by the SPI Ethernet module interrupt line.
|
||||
endif # EXAMPLE_USE_SPI_ETHERNET
|
||||
|
||||
config EXAMPLE_ETH_PHY_RST_GPIO
|
||||
int "PHY Reset GPIO number"
|
||||
range -1 ENV_GPIO_OUT_RANGE_MAX
|
||||
default 5
|
||||
help
|
||||
Set the GPIO number used to reset PHY chip.
|
||||
Set to -1 to disable PHY chip hardware reset.
|
||||
|
||||
config EXAMPLE_ETH_PHY_ADDR
|
||||
int "PHY Address"
|
||||
range 0 31 if EXAMPLE_USE_INTERNAL_ETHERNET
|
||||
default 1
|
||||
help
|
||||
Set PHY address according your board schematic.
|
||||
endif # EXAMPLE_CONNECT_ETHERNET
|
||||
|
||||
config EXAMPLE_CONNECT_IPV6
|
||||
bool "Obtain IPv6 address"
|
||||
default y
|
||||
depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET
|
||||
select LWIP_IPV6
|
||||
help
|
||||
By default, examples will wait until IPv4 and IPv6 local link addresses are obtained.
|
||||
Disable this option if the network does not support IPv6.
|
||||
Choose the preferred IPv6 address type if the connection code should wait until other than
|
||||
the local link address gets assigned.
|
||||
Consider enabling IPv6 stateless address autoconfiguration (SLAAC) in the LWIP component.
|
||||
|
||||
if EXAMPLE_CONNECT_IPV6
|
||||
choice EXAMPLE_CONNECT_PREFERRED_IPV6
|
||||
prompt "Preferred IPv6 Type"
|
||||
default EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
|
||||
help
|
||||
Select which kind of IPv6 address the connect logic waits for.
|
||||
|
||||
config EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK
|
||||
bool "Local Link Address"
|
||||
help
|
||||
Blocks until Local link address assigned.
|
||||
|
||||
config EXAMPLE_CONNECT_IPV6_PREF_GLOBAL
|
||||
bool "Global Address"
|
||||
help
|
||||
Blocks until Global address assigned.
|
||||
|
||||
config EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL
|
||||
bool "Site Local Address"
|
||||
help
|
||||
Blocks until Site link address assigned.
|
||||
|
||||
config EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL
|
||||
bool "Unique Local Link Address"
|
||||
help
|
||||
Blocks until Unique local address assigned.
|
||||
|
||||
endchoice
|
||||
|
||||
endif
|
||||
|
||||
|
||||
endmenu
|
68
common_components/protocol_examples_common/addr_from_stdin.c
Normal file
68
common_components/protocol_examples_common/addr_from_stdin.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include <lwip/netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define HOST_IP_SIZE 128
|
||||
|
||||
esp_err_t get_addr_from_stdin(int port, int sock_type, int *ip_protocol, int *addr_family, struct sockaddr_storage *dest_addr)
|
||||
{
|
||||
char host_ip[HOST_IP_SIZE];
|
||||
int len;
|
||||
static bool already_init = false;
|
||||
|
||||
// this function could be called multiple times -> make sure UART init runs only once
|
||||
if (!already_init) {
|
||||
example_configure_stdin_stdout();
|
||||
already_init = true;
|
||||
}
|
||||
|
||||
// ignore empty or LF only string (could receive from DUT class)
|
||||
do {
|
||||
fgets(host_ip, HOST_IP_SIZE, stdin);
|
||||
len = strlen(host_ip);
|
||||
} while (len<=1 && host_ip[0] == '\n');
|
||||
host_ip[len - 1] = '\0';
|
||||
|
||||
struct addrinfo hints, *addr_list, *cur;
|
||||
memset( &hints, 0, sizeof( hints ) );
|
||||
|
||||
// run getaddrinfo() to decide on the IP protocol
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = sock_type;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
if( getaddrinfo( host_ip, NULL, &hints, &addr_list ) != 0 ) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
for( cur = addr_list; cur != NULL; cur = cur->ai_next ) {
|
||||
memcpy(dest_addr, cur->ai_addr, sizeof(*dest_addr));
|
||||
if (cur->ai_family == AF_INET) {
|
||||
*ip_protocol = IPPROTO_IP;
|
||||
*addr_family = AF_INET;
|
||||
// add port number and return on first IPv4 match
|
||||
((struct sockaddr_in*)dest_addr)->sin_port = htons(port);
|
||||
freeaddrinfo( addr_list );
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
#if CONFIG_LWIP_IPV6
|
||||
else if (cur->ai_family == AF_INET6) {
|
||||
*ip_protocol = IPPROTO_IPV6;
|
||||
*addr_family = AF_INET6;
|
||||
// add port and interface number and return on first IPv6 match
|
||||
((struct sockaddr_in6*)dest_addr)->sin6_port = htons(port);
|
||||
((struct sockaddr_in6*)dest_addr)->sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
|
||||
freeaddrinfo( addr_list );
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// no match found
|
||||
freeaddrinfo( addr_list );
|
||||
return ESP_FAIL;
|
||||
}
|
515
common_components/protocol_examples_common/connect.c
Normal file
515
common_components/protocol_examples_common/connect.c
Normal file
@ -0,0 +1,515 @@
|
||||
/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection.
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "protocol_examples_common.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_default.h"
|
||||
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
#include "esp_eth.h"
|
||||
#if CONFIG_ETH_USE_SPI_ETHERNET
|
||||
#include "driver/spi_master.h"
|
||||
#endif // CONFIG_ETH_USE_SPI_ETHERNET
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
#define MAX_IP6_ADDRS_PER_NETIF (5)
|
||||
#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces*2)
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_LOCAL_LINK)
|
||||
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_GLOBAL)
|
||||
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_GLOBAL
|
||||
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_SITE_LOCAL)
|
||||
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_SITE_LOCAL
|
||||
#elif defined(CONFIG_EXAMPLE_CONNECT_IPV6_PREF_UNIQUE_LOCAL)
|
||||
#define EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE ESP_IP6_ADDR_IS_UNIQUE_LOCAL
|
||||
#endif // if-elif CONFIG_EXAMPLE_CONNECT_IPV6_PREF_...
|
||||
|
||||
#else
|
||||
#define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces)
|
||||
#endif
|
||||
|
||||
#define EXAMPLE_DO_CONNECT CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
|
||||
#if CONFIG_EXAMPLE_WIFI_SCAN_METHOD_FAST
|
||||
#define EXAMPLE_WIFI_SCAN_METHOD WIFI_FAST_SCAN
|
||||
#elif CONFIG_EXAMPLE_WIFI_SCAN_METHOD_ALL_CHANNEL
|
||||
#define EXAMPLE_WIFI_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
|
||||
#elif CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
|
||||
#define EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_WIFI_AUTH_OPEN
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WEP
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA_WPA2_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_ENTERPRISE
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_ENTERPRISE
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA3_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
|
||||
#elif CONFIG_EXAMPLE_WIFI_AUTH_WAPI_PSK
|
||||
#define EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
|
||||
#endif
|
||||
|
||||
static int s_active_interfaces = 0;
|
||||
static SemaphoreHandle_t s_semph_get_ip_addrs;
|
||||
static esp_netif_t *s_example_esp_netif = NULL;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
static esp_ip6_addr_t s_ipv6_addr;
|
||||
|
||||
/* types of ipv6 addresses to be displayed on ipv6 events */
|
||||
static const char *s_ipv6_addr_types[] = {
|
||||
"ESP_IP6_ADDR_IS_UNKNOWN",
|
||||
"ESP_IP6_ADDR_IS_GLOBAL",
|
||||
"ESP_IP6_ADDR_IS_LINK_LOCAL",
|
||||
"ESP_IP6_ADDR_IS_SITE_LOCAL",
|
||||
"ESP_IP6_ADDR_IS_UNIQUE_LOCAL",
|
||||
"ESP_IP6_ADDR_IS_IPV4_MAPPED_IPV6"
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example_connect";
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
static esp_netif_t *wifi_start(void);
|
||||
static void wifi_stop(void);
|
||||
#endif
|
||||
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
static esp_netif_t *eth_start(void);
|
||||
static void eth_stop(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Checks the netif description if it contains specified prefix.
|
||||
* All netifs created withing common connect component are prefixed with the module TAG,
|
||||
* so it returns true if the specified netif is owned by this module
|
||||
*/
|
||||
static bool is_our_netif(const char *prefix, esp_netif_t *netif)
|
||||
{
|
||||
return strncmp(prefix, esp_netif_get_desc(netif), strlen(prefix) - 1) == 0;
|
||||
}
|
||||
|
||||
/* set up connection, Wi-Fi and/or Ethernet */
|
||||
static void start(void)
|
||||
{
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
s_example_esp_netif = wifi_start();
|
||||
s_active_interfaces++;
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
s_example_esp_netif = eth_start();
|
||||
s_active_interfaces++;
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI && CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
/* if both intefaces at once, clear out to indicate that multiple netifs are active */
|
||||
s_example_esp_netif = NULL;
|
||||
#endif
|
||||
|
||||
#if EXAMPLE_DO_CONNECT
|
||||
/* create semaphore if at least one interface is active */
|
||||
s_semph_get_ip_addrs = xSemaphoreCreateCounting(NR_OF_IP_ADDRESSES_TO_WAIT_FOR, 0);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* tear down connection, release resources */
|
||||
static void stop(void)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
wifi_stop();
|
||||
s_active_interfaces--;
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
eth_stop();
|
||||
s_active_interfaces--;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if EXAMPLE_DO_CONNECT
|
||||
static esp_ip4_addr_t s_ip_addr;
|
||||
|
||||
static void on_got_ip(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
||||
if (!is_our_netif(TAG, event->esp_netif)) {
|
||||
ESP_LOGW(TAG, "Got IPv4 from another interface \"%s\": ignored", esp_netif_get_desc(event->esp_netif));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
|
||||
memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr));
|
||||
xSemaphoreGive(s_semph_get_ip_addrs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
static void on_got_ipv6(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
|
||||
if (!is_our_netif(TAG, event->esp_netif)) {
|
||||
ESP_LOGW(TAG, "Got IPv6 from another netif: ignored");
|
||||
return;
|
||||
}
|
||||
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip);
|
||||
ESP_LOGI(TAG, "Got IPv6 event: Interface \"%s\" address: " IPV6STR ", type: %s", esp_netif_get_desc(event->esp_netif),
|
||||
IPV62STR(event->ip6_info.ip), s_ipv6_addr_types[ipv6_type]);
|
||||
if (ipv6_type == EXAMPLE_CONNECT_PREFERRED_IPV6_TYPE) {
|
||||
memcpy(&s_ipv6_addr, &event->ip6_info.ip, sizeof(s_ipv6_addr));
|
||||
xSemaphoreGive(s_semph_get_ip_addrs);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
esp_err_t example_connect(void)
|
||||
{
|
||||
#if EXAMPLE_DO_CONNECT
|
||||
if (s_semph_get_ip_addrs != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#endif
|
||||
start();
|
||||
ESP_ERROR_CHECK(esp_register_shutdown_handler(&stop));
|
||||
ESP_LOGI(TAG, "Waiting for IP(s)");
|
||||
for (int i = 0; i < NR_OF_IP_ADDRESSES_TO_WAIT_FOR; ++i) {
|
||||
xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY);
|
||||
}
|
||||
// iterate over active interfaces, and print out IPs of "our" netifs
|
||||
esp_netif_t *netif = NULL;
|
||||
esp_netif_ip_info_t ip;
|
||||
for (int i = 0; i < esp_netif_get_nr_of_ifs(); ++i) {
|
||||
netif = esp_netif_next(netif);
|
||||
if (is_our_netif(TAG, netif)) {
|
||||
ESP_LOGI(TAG, "Connected to %s", esp_netif_get_desc(netif));
|
||||
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
|
||||
|
||||
ESP_LOGI(TAG, "- IPv4 address: " IPSTR, IP2STR(&ip.ip));
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
esp_ip6_addr_t ip6[MAX_IP6_ADDRS_PER_NETIF];
|
||||
int ip6_addrs = esp_netif_get_all_ip6(netif, ip6);
|
||||
for (int j = 0; j < ip6_addrs; ++j) {
|
||||
esp_ip6_addr_type_t ipv6_type = esp_netif_ip6_get_addr_type(&(ip6[j]));
|
||||
ESP_LOGI(TAG, "- IPv6 address: " IPV6STR ", type: %s", IPV62STR(ip6[j]), s_ipv6_addr_types[ipv6_type]);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t example_disconnect(void)
|
||||
{
|
||||
if (s_semph_get_ip_addrs == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
vSemaphoreDelete(s_semph_get_ip_addrs);
|
||||
s_semph_get_ip_addrs = NULL;
|
||||
stop();
|
||||
ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&stop));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
|
||||
static void on_wifi_disconnect(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect...");
|
||||
esp_err_t err = esp_wifi_connect();
|
||||
if (err == ESP_ERR_WIFI_NOT_STARTED) {
|
||||
return;
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
static void on_wifi_connect(void *esp_netif, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
esp_netif_create_ip6_linklocal(esp_netif);
|
||||
}
|
||||
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
static esp_netif_t *wifi_start(void)
|
||||
{
|
||||
char *desc;
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
|
||||
// Prefix the interface description with the module TAG
|
||||
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
|
||||
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
|
||||
esp_netif_config.if_desc = desc;
|
||||
esp_netif_config.route_prio = 128;
|
||||
esp_netif_t *netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config);
|
||||
free(desc);
|
||||
esp_wifi_set_default_wifi_sta_handlers();
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL));
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect, netif));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL));
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = CONFIG_EXAMPLE_WIFI_SSID,
|
||||
.password = CONFIG_EXAMPLE_WIFI_PASSWORD,
|
||||
.scan_method = EXAMPLE_WIFI_SCAN_METHOD,
|
||||
.sort_method = EXAMPLE_WIFI_CONNECT_AP_SORT_METHOD,
|
||||
.threshold.rssi = CONFIG_EXAMPLE_WIFI_SCAN_RSSI_THRESHOLD,
|
||||
.threshold.authmode = EXAMPLE_WIFI_SCAN_AUTH_MODE_THRESHOLD,
|
||||
},
|
||||
};
|
||||
ESP_LOGI(TAG, "Connecting to %s...", wifi_config.sta.ssid);
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_wifi_connect();
|
||||
return netif;
|
||||
}
|
||||
|
||||
static void wifi_stop(void)
|
||||
{
|
||||
esp_netif_t *wifi_netif = get_example_netif_from_desc("sta");
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect));
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip));
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect));
|
||||
#endif
|
||||
esp_err_t err = esp_wifi_stop();
|
||||
if (err == ESP_ERR_WIFI_NOT_INIT) {
|
||||
return;
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
ESP_ERROR_CHECK(esp_wifi_deinit());
|
||||
ESP_ERROR_CHECK(esp_wifi_clear_default_wifi_driver_and_handlers(wifi_netif));
|
||||
esp_netif_destroy(wifi_netif);
|
||||
s_example_esp_netif = NULL;
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
/** Event handler for Ethernet events */
|
||||
static void on_eth_event(void *esp_netif, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
ESP_ERROR_CHECK(esp_netif_create_ip6_linklocal(esp_netif));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
|
||||
static esp_eth_handle_t s_eth_handle = NULL;
|
||||
static esp_eth_mac_t *s_mac = NULL;
|
||||
static esp_eth_phy_t *s_phy = NULL;
|
||||
static esp_eth_netif_glue_handle_t s_eth_glue = NULL;
|
||||
|
||||
static esp_netif_t *eth_start(void)
|
||||
{
|
||||
char *desc;
|
||||
esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH();
|
||||
// Prefix the interface description with the module TAG
|
||||
// Warning: the interface desc is used in tests to capture actual connection details (IP, gw, mask)
|
||||
asprintf(&desc, "%s: %s", TAG, esp_netif_config.if_desc);
|
||||
esp_netif_config.if_desc = desc;
|
||||
esp_netif_config.route_prio = 64;
|
||||
esp_netif_config_t netif_config = {
|
||||
.base = &esp_netif_config,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||
};
|
||||
esp_netif_t *netif = esp_netif_new(&netif_config);
|
||||
assert(netif);
|
||||
free(desc);
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
|
||||
phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;
|
||||
#if CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
esp32_emac_config.smi_mdc_gpio_num = CONFIG_EXAMPLE_ETH_MDC_GPIO;
|
||||
esp32_emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
|
||||
s_mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
#if CONFIG_EXAMPLE_ETH_PHY_IP101
|
||||
s_phy = esp_eth_phy_new_ip101(&phy_config);
|
||||
#elif CONFIG_EXAMPLE_ETH_PHY_RTL8201
|
||||
s_phy = esp_eth_phy_new_rtl8201(&phy_config);
|
||||
#elif CONFIG_EXAMPLE_ETH_PHY_LAN87XX
|
||||
s_phy = esp_eth_phy_new_lan87xx(&phy_config);
|
||||
#elif CONFIG_EXAMPLE_ETH_PHY_DP83848
|
||||
s_phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||
#elif CONFIG_EXAMPLE_ETH_PHY_KSZ80XX
|
||||
s_phy = esp_eth_phy_new_ksz80xx(&phy_config);
|
||||
#endif
|
||||
#elif CONFIG_EXAMPLE_USE_SPI_ETHERNET
|
||||
gpio_install_isr_service(0);
|
||||
spi_device_handle_t spi_handle = NULL;
|
||||
spi_bus_config_t buscfg = {
|
||||
.miso_io_num = CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO,
|
||||
.mosi_io_num = CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO,
|
||||
.sclk_io_num = CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(CONFIG_EXAMPLE_ETH_SPI_HOST, &buscfg, 1));
|
||||
#if CONFIG_EXAMPLE_USE_DM9051
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 1,
|
||||
.address_bits = 7,
|
||||
.mode = 0,
|
||||
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
|
||||
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
|
||||
.queue_size = 20
|
||||
};
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
|
||||
/* dm9051 ethernet driver is based on spi driver */
|
||||
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
|
||||
dm9051_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
|
||||
s_mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
|
||||
s_phy = esp_eth_phy_new_dm9051(&phy_config);
|
||||
#elif CONFIG_EXAMPLE_USE_W5500
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 16, // Actually it's the address phase in W5500 SPI frame
|
||||
.address_bits = 8, // Actually it's the control phase in W5500 SPI frame
|
||||
.mode = 0,
|
||||
.clock_speed_hz = CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ * 1000 * 1000,
|
||||
.spics_io_num = CONFIG_EXAMPLE_ETH_SPI_CS_GPIO,
|
||||
.queue_size = 20
|
||||
};
|
||||
ESP_ERROR_CHECK(spi_bus_add_device(CONFIG_EXAMPLE_ETH_SPI_HOST, &devcfg, &spi_handle));
|
||||
/* w5500 ethernet driver is based on spi driver */
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
||||
w5500_config.int_gpio_num = CONFIG_EXAMPLE_ETH_SPI_INT_GPIO;
|
||||
s_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||
s_phy = esp_eth_phy_new_w5500(&phy_config);
|
||||
#endif
|
||||
#elif CONFIG_EXAMPLE_USE_OPENETH
|
||||
phy_config.autonego_timeout_ms = 100;
|
||||
s_mac = esp_eth_mac_new_openeth(&mac_config);
|
||||
s_phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||
#endif
|
||||
|
||||
// Install Ethernet driver
|
||||
esp_eth_config_t config = ETH_DEFAULT_CONFIG(s_mac, s_phy);
|
||||
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &s_eth_handle));
|
||||
#if !CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET
|
||||
/* The SPI Ethernet module might doesn't have a burned factory MAC address, we cat to set it manually.
|
||||
02:00:00 is a Locally Administered OUI range so should not be used except when testing on a LAN under your control.
|
||||
*/
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_MAC_ADDR, (uint8_t[]) {
|
||||
0x02, 0x00, 0x00, 0x12, 0x34, 0x56
|
||||
}));
|
||||
#endif
|
||||
// combine driver with netif
|
||||
s_eth_glue = esp_eth_new_netif_glue(s_eth_handle);
|
||||
esp_netif_attach(netif, s_eth_glue);
|
||||
|
||||
// Register user defined event handers
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip, NULL));
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event, netif));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6, NULL));
|
||||
#endif
|
||||
|
||||
esp_eth_start(s_eth_handle);
|
||||
return netif;
|
||||
}
|
||||
|
||||
static void eth_stop(void)
|
||||
{
|
||||
esp_netif_t *eth_netif = get_example_netif_from_desc("eth");
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, &on_got_ip));
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_IPV6
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_GOT_IP6, &on_got_ipv6));
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ETHERNET_EVENT_CONNECTED, &on_eth_event));
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handle));
|
||||
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
|
||||
ESP_ERROR_CHECK(esp_eth_driver_uninstall(s_eth_handle));
|
||||
s_eth_handle = NULL;
|
||||
ESP_ERROR_CHECK(s_phy->del(s_phy));
|
||||
ESP_ERROR_CHECK(s_mac->del(s_mac));
|
||||
|
||||
esp_netif_destroy(eth_netif);
|
||||
s_example_esp_netif = NULL;
|
||||
}
|
||||
|
||||
esp_eth_handle_t get_example_eth_handle(void)
|
||||
{
|
||||
return s_eth_handle;
|
||||
}
|
||||
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
|
||||
esp_netif_t *get_example_netif(void)
|
||||
{
|
||||
return s_example_esp_netif;
|
||||
}
|
||||
|
||||
esp_netif_t *get_example_netif_from_desc(const char *desc)
|
||||
{
|
||||
esp_netif_t *netif = NULL;
|
||||
char *expected_desc;
|
||||
asprintf(&expected_desc, "%s: %s", TAG, desc);
|
||||
while ((netif = esp_netif_next(netif)) != NULL) {
|
||||
if (strcmp(esp_netif_get_desc(netif), expected_desc) == 0) {
|
||||
free(expected_desc);
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
free(expected_desc);
|
||||
return netif;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/* Common utilities for socket address input interface:
|
||||
The API get_addr_from_stdin() is mainly used by socket client examples which read IP address from stdin (if configured).
|
||||
This option is typically used in the CI, but could be enabled in the project configuration.
|
||||
In that case this component is used to receive a string that is evaluated and processed to output
|
||||
socket structures to open a connectio
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "lwip/sys.h"
|
||||
#include <lwip/netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/**
|
||||
* @brief Read and evaluate IP address from stdin
|
||||
*
|
||||
* This API reads stdin and parses the input address using getaddrinfo()
|
||||
* to fill in struct sockaddr_storage (for both IPv4 and IPv6) used to open
|
||||
* a socket. IP protocol is guessed from the IP address string.
|
||||
*
|
||||
* @param[in] port port number of expected connection
|
||||
* @param[in] sock_type expected protocol: SOCK_STREAM or SOCK_DGRAM
|
||||
* @param[out] ip_protocol resultant IP protocol: IPPROTO_IP or IPPROTO_IP6
|
||||
* @param[out] addr_family resultant address family: AF_INET or AF_INET6
|
||||
* @param[out] dest_addr sockaddr_storage structure (for both IPv4 and IPv6)
|
||||
* @return ESP_OK on success, ESP_FAIL otherwise
|
||||
*/
|
||||
esp_err_t get_addr_from_stdin(int port, int sock_type,
|
||||
int *ip_protocol,
|
||||
int *addr_family,
|
||||
struct sockaddr_storage *dest_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,93 @@
|
||||
/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection.
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_eth.h"
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
#define EXAMPLE_INTERFACE get_example_netif()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
|
||||
#define EXAMPLE_INTERFACE get_example_netif()
|
||||
#endif
|
||||
|
||||
#if !defined (CONFIG_EXAMPLE_CONNECT_ETHERNET) && !defined (CONFIG_EXAMPLE_CONNECT_WIFI)
|
||||
// This is useful for some tests which do not need a network connection
|
||||
#define EXAMPLE_INTERFACE NULL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure Wi-Fi or Ethernet, connect, wait for IP
|
||||
*
|
||||
* This all-in-one helper function is used in protocols examples to
|
||||
* reduce the amount of boilerplate in the example.
|
||||
*
|
||||
* It is not intended to be used in real world applications.
|
||||
* See examples under examples/wifi/getting_started/ and examples/ethernet/
|
||||
* for more complete Wi-Fi or Ethernet initialization code.
|
||||
*
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*
|
||||
* @return ESP_OK on successful connection
|
||||
*/
|
||||
esp_err_t example_connect(void);
|
||||
|
||||
/**
|
||||
* Counterpart to example_connect, de-initializes Wi-Fi or Ethernet
|
||||
*/
|
||||
esp_err_t example_disconnect(void);
|
||||
|
||||
/**
|
||||
* @brief Configure stdin and stdout to use blocking I/O
|
||||
*
|
||||
* This helper function is used in ASIO examples. It wraps installing the
|
||||
* UART driver and configuring VFS layer to use UART driver for console I/O.
|
||||
*/
|
||||
esp_err_t example_configure_stdin_stdout(void);
|
||||
|
||||
/**
|
||||
* @brief Returns esp-netif pointer created by example_connect()
|
||||
*
|
||||
* @note If multiple interfaces active at once, this API return NULL
|
||||
* In that case the get_example_netif_from_desc() should be used
|
||||
* to get esp-netif pointer based on interface description
|
||||
*/
|
||||
esp_netif_t *get_example_netif(void);
|
||||
|
||||
/**
|
||||
* @brief Returns esp-netif pointer created by example_connect() described by
|
||||
* the supplied desc field
|
||||
*
|
||||
* @param desc Textual interface of created network interface, for example "sta"
|
||||
* indicate default WiFi station, "eth" default Ethernet interface.
|
||||
*
|
||||
*/
|
||||
esp_netif_t *get_example_netif_from_desc(const char *desc);
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
/**
|
||||
* @brief Get the example Ethernet driver handle
|
||||
*
|
||||
* @return esp_eth_handle_t
|
||||
*/
|
||||
esp_eth_handle_t get_example_eth_handle(void);
|
||||
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
29
common_components/protocol_examples_common/stdin_out.c
Normal file
29
common_components/protocol_examples_common/stdin_out.c
Normal file
@ -0,0 +1,29 @@
|
||||
/* Common functions for protocol examples, to configure stdin and stdout.
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "driver/uart.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
esp_err_t example_configure_stdin_stdout(void)
|
||||
{
|
||||
// Initialize VFS & UART so we can use std::cout/cin
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
/* Install UART driver for interrupt-driven reads and writes */
|
||||
ESP_ERROR_CHECK( uart_driver_install( (uart_port_t)CONFIG_ESP_CONSOLE_UART_NUM,
|
||||
256, 0, 0, NULL, 0) );
|
||||
/* Tell VFS to use UART driver */
|
||||
esp_vfs_dev_uart_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
esp_vfs_dev_uart_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
|
||||
/* Move the caret to the beginning of the next line on '\n' */
|
||||
esp_vfs_dev_uart_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
|
||||
return ESP_OK;
|
||||
}
|
45
components/asio/CMakeLists.txt
Normal file
45
components/asio/CMakeLists.txt
Normal file
@ -0,0 +1,45 @@
|
||||
if(NOT CONFIG_LWIP_IPV6 AND NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
# note: the component is still included in the build so it can become visible again in config
|
||||
# without needing to re-run CMake. However no source or header files are built.
|
||||
message(STATUS "IPV6 support is disabled so the asio component will not be built")
|
||||
idf_component_register()
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(asio_sources "asio/asio/src/asio.cpp")
|
||||
|
||||
if(CONFIG_ASIO_SSL_SUPPORT)
|
||||
if(CONFIG_ASIO_USE_ESP_OPENSSL)
|
||||
list(APPEND asio_sources
|
||||
"port/src/asio_ssl_impl.cpp"
|
||||
"port/mbedtls/src/mbedtls_context.cpp"
|
||||
"port/mbedtls/src/mbedtls_engine.cpp")
|
||||
set(asio_priv_includes "port/mbedtls/include")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ASIO_USE_ESP_WOLFSSL)
|
||||
list(APPEND asio_sources
|
||||
"asio/asio/src/asio_ssl.cpp")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${asio_sources}
|
||||
INCLUDE_DIRS "asio/asio/include" "port/include"
|
||||
PRIV_INCLUDE_DIRS ${asio_priv_includes}
|
||||
REQUIRES lwip)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
||||
if(CONFIG_ASIO_SSL_SUPPORT)
|
||||
if(CONFIG_ASIO_USE_ESP_WOLFSSL)
|
||||
idf_component_get_property(wolflib esp-wolfssl COMPONENT_LIB)
|
||||
idf_component_get_property(wolfdir esp-wolfssl COMPONENT_DIR)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC ${wolflib})
|
||||
target_include_directories(${COMPONENT_LIB} PUBLIC ${wolfdir}/wolfssl/wolfssl)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ASIO_USE_ESP_OPENSSL)
|
||||
idf_component_get_property(mbedtls mbedtls COMPONENT_LIB)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC ${mbedtls})
|
||||
endif()
|
||||
endif()
|
36
components/asio/Kconfig
Normal file
36
components/asio/Kconfig
Normal file
@ -0,0 +1,36 @@
|
||||
menu "ESP-ASIO"
|
||||
visible if LWIP_IPV6
|
||||
|
||||
config ASIO_SSL_SUPPORT
|
||||
bool "Enable SSL/TLS support of ASIO"
|
||||
default n
|
||||
help
|
||||
Enable support for basic SSL/TLS features, available for mbedTLS/OpenSSL
|
||||
as well as wolfSSL TLS library.
|
||||
|
||||
choice ASIO_SSL_LIBRARY_CHOICE
|
||||
prompt "Choose SSL/TLS library for ESP-TLS (See help for more Info)"
|
||||
default ASIO_USE_ESP_OPENSSL
|
||||
depends on ASIO_SSL_SUPPORT
|
||||
help
|
||||
The ASIO support multiple backend TLS libraries. Currently the mbedTLS with a thin ESP-OpenSSL
|
||||
port layer (default choice) and WolfSSL are supported.
|
||||
Different TLS libraries may support different features and have different resource
|
||||
usage. Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details.
|
||||
config ASIO_USE_ESP_OPENSSL
|
||||
bool "esp-openssl"
|
||||
config ASIO_USE_ESP_WOLFSSL
|
||||
depends on TLS_STACK_WOLFSSL
|
||||
bool "wolfSSL (License info in wolfSSL directory README)"
|
||||
endchoice
|
||||
|
||||
config ASIO_SSL_BIO_SIZE
|
||||
int "Size of BIO object"
|
||||
default 1024
|
||||
depends on ASIO_SSL_SUPPORT
|
||||
help
|
||||
Size in bytes of SSL-BIO implementation.
|
||||
Reducing the BIO size saves more RAM, but may slow down input output operations due to
|
||||
fragmentation.
|
||||
|
||||
endmenu
|
11
components/asio/README.md
Normal file
11
components/asio/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# ASIO port
|
||||
|
||||
Asio is a cross-platform C++ library, see https://think-async.com/Asio/. It provides a consistent asynchronous model using a modern C++ approach.
|
||||
|
||||
## Examples
|
||||
|
||||
Get started with example test :example:`examples <examples/..>`:
|
||||
|
||||
## Documentation
|
||||
|
||||
* View the full [html documentation](https://espressif.github.io/esp-protocols/asio/index.html)
|
1
components/asio/asio
Submodule
1
components/asio/asio
Submodule
Submodule components/asio/asio added at 58384fb6af
73
components/asio/docs/Doxyfile
Executable file
73
components/asio/docs/Doxyfile
Executable file
@ -0,0 +1,73 @@
|
||||
# This is Doxygen configuration file
|
||||
#
|
||||
# Doxygen provides over 260 configuration statements
|
||||
# To make this file easier to follow,
|
||||
# it contains only statements that are non-default
|
||||
#
|
||||
# NOTE:
|
||||
# It is recommended not to change defaults unless specifically required
|
||||
# Test any changes how they affect generated documentation
|
||||
# Make sure that correct warnings are generated to flag issues with documented code
|
||||
#
|
||||
# For the complete list of configuration statements see:
|
||||
# http://doxygen.nl/manual/config.html
|
||||
|
||||
|
||||
PROJECT_NAME = "ESP Protocols Programming Guide"
|
||||
|
||||
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
|
||||
## to automatically generate API reference list files heder_file.inc
|
||||
## These files are placed in '_inc' directory
|
||||
## and used to include in API reference documentation
|
||||
|
||||
|
||||
## Get warnings for functions that have no documentation for their parameters or return value
|
||||
##
|
||||
WARN_NO_PARAMDOC = YES
|
||||
|
||||
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
|
||||
##
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = \
|
||||
$(ENV_DOXYGEN_DEFINES) \
|
||||
__DOXYGEN__=1 \
|
||||
__attribute__(x)= \
|
||||
_Static_assert()= \
|
||||
IDF_DEPRECATED(X)= \
|
||||
IRAM_ATTR= \
|
||||
configSUPPORT_DYNAMIC_ALLOCATION=1 \
|
||||
configSUPPORT_STATIC_ALLOCATION=1 \
|
||||
configQUEUE_REGISTRY_SIZE=1 \
|
||||
configUSE_RECURSIVE_MUTEXES=1 \
|
||||
configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \
|
||||
configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \
|
||||
configUSE_APPLICATION_TASK_TAG=1 \
|
||||
configTASKLIST_INCLUDE_COREID=1 \
|
||||
"ESP_EVENT_DECLARE_BASE(x)=extern esp_event_base_t x"
|
||||
|
||||
## Do not complain about not having dot
|
||||
##
|
||||
HAVE_DOT = NO
|
||||
|
||||
## Generate XML that is required for Breathe
|
||||
##
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
|
||||
GENERATE_HTML = NO
|
||||
HAVE_DOT = NO
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_MAN = YES
|
||||
GENERATE_RTF = NO
|
||||
|
||||
## Skip distracting progress messages
|
||||
##
|
||||
QUIET = YES
|
||||
|
||||
## Enable Section Tags for conditional documentation
|
||||
##
|
||||
ENABLED_SECTIONS += \
|
||||
DOC_EXCLUDE_HEADER_SECTION \ ## To conditionally remove doc sections from IDF source files without affecting documentation in upstream files.
|
||||
DOC_SINGLE_GROUP ## To conditionally remove groups from the documentation and create a 'flat' document without affecting documentation in upstream files.
|
21
components/asio/docs/conf_common.py
Normal file
21
components/asio/docs/conf_common.py
Normal file
@ -0,0 +1,21 @@
|
||||
from esp_docs.conf_docs import * # noqa: F403,F401
|
||||
|
||||
extensions += ['sphinx_copybutton',
|
||||
# Needed as a trigger for running doxygen
|
||||
'esp_docs.esp_extensions.dummy_build_system',
|
||||
'esp_docs.esp_extensions.run_doxygen',
|
||||
]
|
||||
|
||||
# link roles config
|
||||
github_repo = 'espressif/esp-protocols'
|
||||
|
||||
# context used by sphinx_idf_theme
|
||||
html_context['github_user'] = 'espressif'
|
||||
html_context['github_repo'] = 'esp-protocols'
|
||||
|
||||
# Extra options required by sphinx_idf_theme
|
||||
project_slug = 'esp-idf' # >=5.0
|
||||
versions_url = 'https://github.com/espressif/esp-protocols/docs/docs_versions.js'
|
||||
|
||||
idf_targets = ['esp32']
|
||||
languages = ['en', 'zh_CN']
|
24
components/asio/docs/en/conf.py
Normal file
24
components/asio/docs/en/conf.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-Protocols'
|
||||
copyright = u'2016 - 2022, Espressif Systems (Shanghai) Co., Ltd'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'en'
|
42
components/asio/docs/en/index.rst
Normal file
42
components/asio/docs/en/index.rst
Normal file
@ -0,0 +1,42 @@
|
||||
ASIO port
|
||||
=========
|
||||
|
||||
Overview
|
||||
--------
|
||||
Asio is a cross-platform C++ library, see https://think-async.com/Asio/. It provides a consistent asynchronous model using a modern C++ approach.
|
||||
|
||||
|
||||
ASIO documentation
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Please refer to the original asio documentation at https://think-async.com/Asio/Documentation.
|
||||
Asio also comes with a number of examples which could be find under Documentation/Examples on that web site.
|
||||
|
||||
Supported features
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
ESP platform port currently supports only network asynchronous socket operations; does not support serial port.
|
||||
SSL/TLS support is disabled by default and could be enabled in component configuration menu by choosing TLS library from
|
||||
|
||||
- mbedTLS with OpenSSL translation layer (default option)
|
||||
- wolfSSL
|
||||
|
||||
SSL support is very basic at this stage and it does include following features:
|
||||
|
||||
- Verification callbacks
|
||||
- DH property files
|
||||
- Certificates/private keys file APIs
|
||||
|
||||
Internal asio settings for ESP include
|
||||
|
||||
- EXCEPTIONS are enabled in ASIO if enabled in menuconfig
|
||||
- TYPEID is enabled in ASIO if enabled in menuconfig
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
ESP examples are based on standard asio :example:`examples <../examples>`:
|
||||
|
||||
- :example:`udp_echo_server <../examples/udp_echo_server>`
|
||||
- :example:`tcp_echo_server <../examples/tcp_echo_server>`
|
||||
- :example:`asio_chat <../examples/asio_chat>`
|
||||
- :example:`ssl_client_server <../examples/ssl_client_server>`
|
||||
|
||||
Please refer to the specific example README.md for details
|
27
components/asio/docs/generate_docs
Executable file
27
components/asio/docs/generate_docs
Executable file
@ -0,0 +1,27 @@
|
||||
build-docs --target esp32 --language en
|
||||
|
||||
cp -rf _build/en/esp32/html .
|
||||
rm -rf _build __pycache__
|
||||
|
||||
# Modifes some version and target fields of index.html
|
||||
echo "<script type="text/javascript">
|
||||
window.onload =(function() {
|
||||
var myAnchor = document.getElementById('version-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'latest';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
var myAnchor = document.getElementById('target-select');
|
||||
var mySpan = document.createElement('input');
|
||||
mySpan.setAttribute('type', 'text');
|
||||
mySpan.setAttribute('maxLength', '10');
|
||||
mySpan.value = 'all targets';
|
||||
mySpan.setAttribute('disabled', true);
|
||||
myAnchor.parentNode.replaceChild(mySpan, myAnchor);
|
||||
|
||||
})();
|
||||
</script>" >> html/index.html
|
||||
|
27
components/asio/docs/zh_CN/conf.py
Normal file
27
components/asio/docs/zh_CN/conf.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
import datetime
|
||||
|
||||
current_year = datetime.datetime.now().year
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-IDF 编程指南'
|
||||
copyright = u'2016 - {} 乐鑫信息科技(上海)股份有限公司'.format(current_year)
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'zh_CN'
|
1
components/asio/docs/zh_CN/index.rst
Normal file
1
components/asio/docs/zh_CN/index.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/protocols/asio.rst
|
10
components/asio/examples/asio_chat/CMakeLists.txt
Normal file
10
components/asio/examples/asio_chat/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../.. ../../../../common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_chat)
|
65
components/asio/examples/asio_chat/README.md
Normal file
65
components/asio/examples/asio_chat/README.md
Normal file
@ -0,0 +1,65 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
|
||||
# Asio chat client and server examples
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The application aims to demonstrate a simple use of Asio library in different modes.
|
||||
In project settings it could be configured to run either a Asio chat server, a Asio chat client, or both.
|
||||
|
||||
## How to use example
|
||||
|
||||
The example is configured by default as an Asio chat client.
|
||||
|
||||
Note that the example uses string representation of IP addresses and ports.
|
||||
|
||||
You can find the upstream asio chat implementation [here] https://github.com/chriskohlhoff/asio/tree/master/asio/src/examples/cpp11/chat
|
||||
|
||||
### Asio Client
|
||||
|
||||
In the client mode, the example connects to the configured address, sends the message, which was inserted as an input in the terminal, and receives a response.
|
||||
|
||||
### Asio Server
|
||||
|
||||
In the server mode, Asio chat server with a specified port number is created and being polled till a connection request from the client arrives.
|
||||
Chat server echoes a message (received from any client) to all connected clients.
|
||||
|
||||
## Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
Set following parameters under Example Configuration Options:
|
||||
|
||||
* Set `EXAMPLE_CHAT_SERVER` to use the example as an ASIO chat server
|
||||
* Configure `EXAMPLE_CHAT_SERVER_BIND_PORT` to the port number.
|
||||
|
||||
* Set `EXAMPLE_CHAT_CLIENT` to use the example as an ASIO chat client
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS` to a string representation of the address to connect the client to.
|
||||
* Configure `EXAMPLE_CHAT_CLIENT_CONNECT_PORT` to the port number.
|
||||
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more d etails.
|
||||
|
||||
## Running the example in server mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- Connect to the server using multiple clients, for example using any option below.
|
||||
- build and run asio chat client on your host machine
|
||||
- run chat_client asio example on ESP platform
|
||||
- since chat messages consists of ASCII size and message, it is possible to
|
||||
netcat `nc IP PORT` and type for example ` 4ABC<CR>` to transmit 'ABC\n'
|
||||
|
||||
## Running the example in client mode
|
||||
|
||||
- Configure the example according "Configure the project" section.
|
||||
- Start chat server either on host machine or as another ESP device running chat_server example.
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet.
|
||||
- Receive and send messages to/from other clients on stdin/stdout via serial terminal.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
29
components/asio/examples/asio_chat/example_test.py
Normal file
29
components/asio/examples/asio_chat/example_test.py
Normal file
@ -0,0 +1,29 @@
|
||||
# This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
# Unless required by applicable law or agreed to in writing, this
|
||||
# software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_asio_chat(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
|
||||
msg = 'asio-chat: received hi'
|
||||
dut = env.get_dut('asio_chat', 'examples/protocols/asio/asio_chat')
|
||||
# start the test and expect the client to receive back it's original data
|
||||
dut.start_app()
|
||||
dut.expect(re.compile(r'{}'.format('Waiting for input')), timeout=30)
|
||||
dut.write(msg)
|
||||
dut.write('exit')
|
||||
dut.expect(re.compile(r'{}'.format(msg)), timeout=30)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_chat()
|
2
components/asio/examples/asio_chat/main/CMakeLists.txt
Normal file
2
components/asio/examples/asio_chat/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "asio_chat.cpp"
|
||||
INCLUDE_DIRS ".")
|
39
components/asio/examples/asio_chat/main/Kconfig.projbuild
Normal file
39
components/asio/examples/asio_chat/main/Kconfig.projbuild
Normal file
@ -0,0 +1,39 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_CHAT_SERVER
|
||||
bool "Asio example chat server"
|
||||
default n
|
||||
help
|
||||
This example will setup a chat server, binds it to the specified address
|
||||
and starts listening.
|
||||
|
||||
if EXAMPLE_CHAT_SERVER
|
||||
config EXAMPLE_CHAT_SERVER_BIND_PORT
|
||||
string "Asio example server bind port"
|
||||
default "3344"
|
||||
help
|
||||
Server listener's socket would be bound to this port.
|
||||
endif
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT
|
||||
bool "Asio example chat client"
|
||||
default y
|
||||
help
|
||||
This example will setup an asio chat client.
|
||||
and sends the data.
|
||||
|
||||
if EXAMPLE_CHAT_CLIENT
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS
|
||||
string "Client connection address"
|
||||
default "192.168.0.1"
|
||||
help
|
||||
Client's socket would connect to this address/host.
|
||||
|
||||
config EXAMPLE_CHAT_CLIENT_CONNECT_PORT
|
||||
string "Client connection port"
|
||||
default "3344"
|
||||
help
|
||||
Client's connection port.
|
||||
endif
|
||||
|
||||
endmenu
|
119
components/asio/examples/asio_chat/main/asio_chat.cpp
Normal file
119
components/asio/examples/asio_chat/main/asio_chat.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
/* ASIO chat server client example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "server.hpp"
|
||||
#include "client.hpp"
|
||||
#include <thread>
|
||||
#include <pthread.h>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
static const char *TAG = "asio-chat";
|
||||
|
||||
// This variable is necessary for `python test` execution, it provides synchronisation between server/client(as server should be started before client)
|
||||
std::mutex server_ready;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
static void get_string(char *line, size_t size)
|
||||
{
|
||||
int count = 0;
|
||||
while (count < size) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
line[count] = '\0';
|
||||
break;
|
||||
} else if (c > 0 && c < 127) {
|
||||
line[count] = c;
|
||||
++count;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void start_client(void)
|
||||
{
|
||||
const std::string port(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_PORT);
|
||||
const std::string name(CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS);
|
||||
asio::io_context io_context;
|
||||
char line[128];
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(name, port);
|
||||
chat_client c(io_context, endpoints);
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
#endif
|
||||
std::thread t([&io_context]() { try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during client thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});
|
||||
do {
|
||||
ESP_LOGI(TAG, "CLIENT: Waiting for input");
|
||||
get_string(line, sizeof(line));
|
||||
|
||||
chat_message msg;
|
||||
msg.body_length(std::strlen(line));
|
||||
std::memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
sleep(1);
|
||||
} while (strcmp(line, "exit") != 0);
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
try {
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
asio::io_context io_context;
|
||||
chat_server server(io_context, tcp::endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_CHAT_SERVER_BIND_PORT)));
|
||||
std::thread t = std::thread([&io_context]() { // Chat server starting here
|
||||
try {
|
||||
io_context.run();
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during server thread execution %s", e.what());
|
||||
}
|
||||
catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}});;
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_CLIENT
|
||||
start_client();
|
||||
#endif
|
||||
#ifdef CONFIG_EXAMPLE_CHAT_SERVER
|
||||
t.join();
|
||||
#endif
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGE(TAG, "Exception occured during run %s", e.what());
|
||||
} catch (...) {
|
||||
ESP_LOGE(TAG, "Unknown exception");
|
||||
}
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
91
components/asio/examples/asio_chat/main/chat_message.hpp
Normal file
91
components/asio/examples/asio_chat/main/chat_message.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
//
|
||||
// chat_message.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t header_length = 4;
|
||||
static constexpr std::size_t max_body_length = 512;
|
||||
|
||||
chat_message()
|
||||
: body_length_(0)
|
||||
{
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::size_t length() const
|
||||
{
|
||||
return header_length + body_length_;
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
std::size_t body_length() const
|
||||
{
|
||||
return body_length_;
|
||||
}
|
||||
|
||||
void body_length(std::size_t new_length)
|
||||
{
|
||||
body_length_ = new_length;
|
||||
if (body_length_ > max_body_length)
|
||||
body_length_ = max_body_length;
|
||||
}
|
||||
|
||||
bool decode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::strncat(header, data_, header_length);
|
||||
body_length_ = std::atoi(header);
|
||||
if (body_length_ > max_body_length)
|
||||
{
|
||||
body_length_ = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::sprintf(header, "%4d", static_cast<int>(body_length_));
|
||||
std::memcpy(data_, header, header_length);
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[header_length + max_body_length];
|
||||
std::size_t body_length_;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
126
components/asio/examples/asio_chat/main/client.hpp
Normal file
126
components/asio/examples/asio_chat/main/client.hpp
Normal file
@ -0,0 +1,126 @@
|
||||
//
|
||||
// client.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_CLIENT_HPP
|
||||
#define CHAT_CLIENT_HPP
|
||||
|
||||
#include <deque>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
: io_context_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
do_connect(endpoints);
|
||||
}
|
||||
|
||||
void write(const chat_message& msg)
|
||||
{
|
||||
asio::post(io_context_,
|
||||
[this, msg]()
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
asio::post(io_context_, [this]() { socket_.close(); });
|
||||
}
|
||||
|
||||
private:
|
||||
void do_connect(const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[this](std::error_code ec, asio::ip::tcp::endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_header()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
#endif // CHAT_CLIENT_HPP
|
202
components/asio/examples/asio_chat/main/server.hpp
Normal file
202
components/asio/examples/asio_chat/main/server.hpp
Normal file
@ -0,0 +1,202 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef CHAT_SERVER_HPP
|
||||
#define CHAT_SERVER_HPP
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
extern std::mutex server_ready;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
virtual ~chat_participant() {}
|
||||
virtual void deliver(const chat_message& msg) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<chat_participant> chat_participant_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_room
|
||||
{
|
||||
public:
|
||||
void join(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.insert(participant);
|
||||
for (auto msg: recent_msgs_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
void leave(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.erase(participant);
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
recent_msgs_.push_back(msg);
|
||||
while (recent_msgs_.size() > max_recent_msgs)
|
||||
recent_msgs_.pop_front();
|
||||
|
||||
for (auto participant: participants_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<chat_participant_ptr> participants_;
|
||||
enum { max_recent_msgs = 100 };
|
||||
chat_message_queue recent_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public std::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(asio::ip::tcp::socket socket, chat_room& room)
|
||||
: socket_(std::move(socket)),
|
||||
room_(room)
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
room_.join(shared_from_this());
|
||||
do_read_header();
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read_header()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
ESP_LOGD("asio-chat:", "%s", read_msg_.body());
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::io_context& io_context,
|
||||
const asio::ip::tcp::endpoint& endpoint)
|
||||
: acceptor_(io_context, endpoint)
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(server_ready);
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<chat_session>(std::move(socket), room_)->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
#endif // CHAT_SERVER_HPP
|
6
components/asio/examples/asio_chat/sdkconfig.ci
Normal file
6
components/asio/examples/asio_chat/sdkconfig.ci
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT=y
|
||||
CONFIG_EXAMPLE_CHAT_SERVER=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y
|
||||
CONFIG_EXAMPLE_CHAT_CLIENT_CONNECT_ADDRESS="localhost"
|
2
components/asio/examples/asio_chat/sdkconfig.defaults
Normal file
2
components/asio/examples/asio_chat/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
10
components/asio/examples/async_request/CMakeLists.txt
Normal file
10
components/asio/examples/async_request/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../../ ../../../../common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(async_http_request)
|
52
components/asio/examples/async_request/README.md
Normal file
52
components/asio/examples/async_request/README.md
Normal file
@ -0,0 +1,52 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Async request using ASIO
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The application aims to show how to compose async operations using ASIO to build network protocols and operations.
|
||||
|
||||
# Configure and Building example
|
||||
|
||||
This example doesn't require any configuration, just build it with
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
# Async operations composition and automatic lifetime control
|
||||
|
||||
On this example we compose the operation by starting the next step in the chain inside the completion handler of the
|
||||
previous operation. Also we pass the `Connection` class itself as the parameter of its final handler to be owned by
|
||||
the following operation. This is possible due to the control of lifetime by the usage of `std::shared_ptr`.
|
||||
|
||||
The control of lifetime of the class, done by `std::shared_ptr` usage, guarantee that the data will be available for
|
||||
async operations until it's not needed any more. This makes necessary that all of the async operation class must start
|
||||
its lifetime as a `std::shared_ptr` due to the usage of `std::enable_shared_from_this`.
|
||||
|
||||
|
||||
User creates a shared_ptr──┐
|
||||
of AddressResolution and │
|
||||
ask for resolve. │
|
||||
The handler for the ┌▼─────────────────────┐
|
||||
complete operation is sent│ AddressResolution │ In the completion of resolve a connection is created.
|
||||
└─────────────────┬────┘ AddressResolution is automaticly destroyed since it's
|
||||
│ no longer needed
|
||||
┌─▼────────────────────────────────────┐
|
||||
│ Connection │
|
||||
└──────┬───────────────────────────────┘
|
||||
Http::Session is created once we have a Connection. │
|
||||
Connection is passed to Http::Session that holds it │
|
||||
avoiding it's destruction. │
|
||||
┌─▼───────────────────────────────┐
|
||||
│ Http::Session │
|
||||
└────────┬────────────────────────┘
|
||||
After the HTTP request is │
|
||||
sent the completion handler │
|
||||
is called. │
|
||||
└────►Completion Handler()
|
||||
|
||||
|
||||
The previous diagram shows the process and the life span of each of the tasks in this examples. At each stage the
|
||||
object responsible for the last action inject itself to the completion handler of the next stage for reuse.
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "async_http_request.cpp"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,369 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*
|
||||
* ASIO HTTP request example
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <asio.hpp>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
constexpr auto TAG = "async_request";
|
||||
using asio::ip::tcp;
|
||||
|
||||
namespace {
|
||||
|
||||
void esp_init()
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_log_level_set("async_request", ESP_LOG_DEBUG);
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Simple class to add the resolver to a chain of actions
|
||||
*
|
||||
*/
|
||||
class AddressResolution : public std::enable_shared_from_this<AddressResolution> {
|
||||
public:
|
||||
explicit AddressResolution(asio::io_context &context) : ctx(context), resolver(ctx) {}
|
||||
|
||||
/**
|
||||
* @brief Initiator function for the address resolution
|
||||
*
|
||||
* @tparam CompletionToken callable responsible to use the results.
|
||||
*
|
||||
* @param host Host address
|
||||
* @param port Port for the target, must be number due to a limitation on lwip.
|
||||
*/
|
||||
template<class CompletionToken>
|
||||
void resolve(const std::string &host, const std::string &port, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
resolver.async_resolve(host, port, [self, completion_handler](const asio::error_code & error, tcp::resolver::results_type results) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Failed to resolve: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
completion_handler(self, results);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context &ctx;
|
||||
tcp::resolver resolver;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Connection class
|
||||
*
|
||||
* The lowest level dependency on our asynchronous task, Connection provide an interface to TCP sockets.
|
||||
* A similar class could be provided for a TLS connection.
|
||||
*
|
||||
* @note: All read and write operations are written on an explicit strand, even though an implicit strand
|
||||
* occurs in this example since we run the io context in a single task.
|
||||
*
|
||||
*/
|
||||
class Connection : public std::enable_shared_from_this<Connection> {
|
||||
public:
|
||||
explicit Connection(asio::io_context &context) : ctx(context), strand(context), socket(ctx) {}
|
||||
|
||||
/**
|
||||
* @brief Start the connection
|
||||
*
|
||||
* Async operation to start a connection. As the final act of the process the Connection class pass a
|
||||
* std::shared_ptr of itself to the completion_handler.
|
||||
* Since it uses std::shared_ptr as an automatic control of its lifetime this class must be created
|
||||
* through a std::make_shared call.
|
||||
*
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
* @param host host address
|
||||
* @param port port number - due to a limitation on lwip implementation this should be the number not the
|
||||
* service name typically seen in ASIO examples.
|
||||
*
|
||||
* @note The class could be modified to store the completion handler, as a member variable, instead of
|
||||
* pass it along asynchronous calls to allow the process to run again completely.
|
||||
*
|
||||
*/
|
||||
template<class CompletionToken>
|
||||
void start(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||
{
|
||||
connect(results, completion_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an async write on the socket
|
||||
*
|
||||
* @tparam data
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
*
|
||||
*/
|
||||
template<class DataType, class CompletionToken>
|
||||
void write_async(const DataType &data, CompletionToken &&completion_handler)
|
||||
{
|
||||
asio::async_write(socket, data, asio::bind_executor(strand, completion_handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an async read on the socket
|
||||
*
|
||||
* @tparam data
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
*
|
||||
*/
|
||||
template<class DataBuffer, class CompletionToken>
|
||||
void read_async(DataBuffer &&in_data, CompletionToken &&completion_handler)
|
||||
{
|
||||
asio::async_read(socket, in_data, asio::bind_executor(strand, completion_handler));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<class CompletionToken>
|
||||
void connect(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_connect(socket, results, [self, completion_handler](const asio::error_code & error, [[maybe_unused]] const tcp::endpoint & endpoint) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Failed to connect: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
completion_handler(self);
|
||||
});
|
||||
}
|
||||
asio::io_context &ctx;
|
||||
asio::io_context::strand strand;
|
||||
tcp::socket socket;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
namespace Http {
|
||||
enum class Method { GET };
|
||||
|
||||
/**
|
||||
* @brief Simple HTTP request class
|
||||
*
|
||||
* The user needs to write the request information direct to header and body fields.
|
||||
*
|
||||
* Only GET verb is provided.
|
||||
*
|
||||
*/
|
||||
class Request {
|
||||
public:
|
||||
Request(Method method, std::string host, std::string port, const std::string &target) : host_data(std::move(host)), port_data(std::move(port))
|
||||
{
|
||||
header_data.append("GET ");
|
||||
header_data.append(target);
|
||||
header_data.append(" HTTP/1.1");
|
||||
header_data.append("\r\n");
|
||||
header_data.append("Host: ");
|
||||
header_data.append(host_data);
|
||||
header_data.append("\r\n");
|
||||
header_data.append("\r\n");
|
||||
};
|
||||
|
||||
void set_header_field(std::string const &field)
|
||||
{
|
||||
header_data.append(field);
|
||||
}
|
||||
|
||||
void append_to_body(std::string const &data)
|
||||
{
|
||||
body_data.append(data);
|
||||
};
|
||||
|
||||
const std::string &host() const
|
||||
{
|
||||
return host_data;
|
||||
}
|
||||
|
||||
const std::string &service_port() const
|
||||
{
|
||||
return port_data;
|
||||
}
|
||||
|
||||
const std::string &header() const
|
||||
{
|
||||
return header_data;
|
||||
}
|
||||
|
||||
const std::string &body() const
|
||||
{
|
||||
return body_data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string host_data;
|
||||
std::string port_data;
|
||||
std::string header_data;
|
||||
std::string body_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple HTTP response class
|
||||
*
|
||||
* The response is built from received data and only parsed to split header and body.
|
||||
*
|
||||
* A copy of the received data is kept.
|
||||
*
|
||||
*/
|
||||
struct Response {
|
||||
/**
|
||||
* @brief Construct a response from a contiguous buffer.
|
||||
*
|
||||
* Simple http parsing.
|
||||
*
|
||||
*/
|
||||
template<class DataIt>
|
||||
explicit Response(DataIt data, size_t size)
|
||||
{
|
||||
raw_response = std::string(data, size);
|
||||
|
||||
auto header_last = raw_response.find("\r\n\r\n");
|
||||
if (header_last != std::string::npos) {
|
||||
header = raw_response.substr(0, header_last);
|
||||
}
|
||||
body = raw_response.substr(header_last + 3);
|
||||
}
|
||||
/**
|
||||
* @brief Print response content.
|
||||
*/
|
||||
void print()
|
||||
{
|
||||
ESP_LOGI(TAG, "Header :\n %s", header.c_str());
|
||||
ESP_LOGI(TAG, "Body : \n %s", body.c_str());
|
||||
}
|
||||
|
||||
std::string raw_response;
|
||||
std::string header;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
/** @brief HTTP Session
|
||||
*
|
||||
* Session class to handle HTTP protocol implementation.
|
||||
*
|
||||
*/
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
public:
|
||||
explicit Session(std::shared_ptr<Connection> connection_in) : connection(std::move(connection_in))
|
||||
{
|
||||
}
|
||||
|
||||
template<class CompletionToken>
|
||||
void send_request(const Request &request, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
send_data = { asio::buffer(request.header()), asio::buffer(request.body()) };
|
||||
connection->write_async(send_data, [self, &completion_handler](std::error_code error, std::size_t bytes_transfered) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Request write error: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Bytes Transfered: %d", bytes_transfered);
|
||||
self->get_response(completion_handler);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template<class CompletionToken>
|
||||
void get_response(CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
connection->read_async(asio::buffer(receive_buffer), [self, &completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||
if (error and error.value() != asio::error::eof) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Bytes Received: %d", bytes_received);
|
||||
if (bytes_received == 0) {
|
||||
return;
|
||||
}
|
||||
Response response(std::begin(self->receive_buffer), bytes_received);
|
||||
|
||||
completion_handler(self, response);
|
||||
});
|
||||
}
|
||||
/*
|
||||
* For this example we assumed 2048 to be enough for the receive_buffer
|
||||
*/
|
||||
std::array<char, 2048> receive_buffer;
|
||||
/*
|
||||
* The hardcoded 2 below is related to the type we receive the data to send. We gather the parts from Request, header
|
||||
* and body, to send avoiding the copy.
|
||||
*/
|
||||
std::array<asio::const_buffer, 2> send_data;
|
||||
std::shared_ptr<Connection> connection;
|
||||
};
|
||||
|
||||
/** @brief Execute a fully async HTTP request
|
||||
*
|
||||
* @tparam completion_handler
|
||||
* @param ctx io context
|
||||
* @param request
|
||||
*
|
||||
* @note : We build this function as a simpler interface to compose the operations of connecting to
|
||||
* the address and running the HTTP session. The Http::Session class is injected to the completion handler
|
||||
* for further use.
|
||||
*/
|
||||
template<class CompletionToken>
|
||||
void request_async(asio::io_context &context, const Request &request, CompletionToken &&completion_handler)
|
||||
{
|
||||
/*
|
||||
* The first step is to resolve the address we want to connect to.
|
||||
* The AddressResolution itself is injected to the completion handler.
|
||||
*
|
||||
* This shared_ptr is destroyed by the end of the scope. Pay attention that this is a non blocking function
|
||||
* the lifetime of the object is extended by the resolve call
|
||||
*/
|
||||
std::make_shared<AddressResolution>(context)->resolve(request.host(), request.service_port(),
|
||||
[&context, &request, completion_handler](std::shared_ptr<AddressResolution> resolver, tcp::resolver::results_type results) {
|
||||
/* After resolution we create a Connection.
|
||||
* The completion handler gets a shared_ptr<Connection> to receive the connection, once the
|
||||
* connection process is complete.
|
||||
*/
|
||||
std::make_shared<Connection>(context)->start(results,
|
||||
[&request, completion_handler](std::shared_ptr<Connection> connection) {
|
||||
// Now we create a HTTP::Session and inject the necessary connection.
|
||||
std::make_shared<Session>(connection)->send_request(request, completion_handler);
|
||||
});
|
||||
});
|
||||
}
|
||||
}// namespace Http
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
// Basic initialization of ESP system
|
||||
esp_init();
|
||||
|
||||
asio::io_context io_context;
|
||||
Http::Request request(Http::Method::GET, "www.httpbin.org", "80", "/get");
|
||||
Http::request_async(io_context, request, [](std::shared_ptr<Http::Session> session, Http::Response response) {
|
||||
/*
|
||||
* We only print the response here but could reuse session for other requests.
|
||||
*/
|
||||
response.print();
|
||||
});
|
||||
|
||||
// io_context.run will block until all the tasks on the context are done.
|
||||
io_context.run();
|
||||
ESP_LOGI(TAG, "Context run done");
|
||||
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
10
components/asio/examples/socks4/CMakeLists.txt
Normal file
10
components/asio/examples/socks4/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../.. ../../../../common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_sock4)
|
73
components/asio/examples/socks4/README.md
Normal file
73
components/asio/examples/socks4/README.md
Normal file
@ -0,0 +1,73 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 |
|
||||
| ----------------- | ----- | ----- |
|
||||
|
||||
# Async request using ASIO
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The application aims to show how to connect to a Socks4 proxy using async operations with ASIO. The SOCKS protocol is
|
||||
briefly described by the diagram below.
|
||||
|
||||
┌──────┐ ┌─────┐ ┌──────┐
|
||||
│Client│ │Proxy│ │Target│
|
||||
└──┬───┘ └──┬──┘ └──┬───┘
|
||||
│ │ │
|
||||
│ ╔═╧══════════════╗ │
|
||||
══════════════════════╪════════════════════════╣ Initialization ╠═══╪════════════════════════════════════════════
|
||||
│ ╚═╤══════════════╝ │
|
||||
│ │ │
|
||||
╔══════════════════╗│ │ │
|
||||
║We establish a ░║│ Socket Connection │ │
|
||||
║TCP connection ║│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ > │
|
||||
║and get a socket ║│ │ │
|
||||
╚══════════════════╝│ │ │
|
||||
│ │ │
|
||||
│ ╔═╧══════════════╗ │
|
||||
══════════════════════╪════════════════════════╣ Socks Protocol ╠═══╪════════════════════════════════════════════
|
||||
│ ╚═╤══════════════╝ │
|
||||
│ │ │
|
||||
│ Client Connection Request│ │
|
||||
│ ─────────────────────────> │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ╔════════════╪═══════╤══════════╪════════════════════════════════╗
|
||||
│ ║ TARGET CONNECTION │ │ ║
|
||||
│ ╟────────────────────┘ │ ╔═══════════════════╗ ║
|
||||
│ ║ │ Socket Connection│ ║Proxy establishes ░║ ║
|
||||
│ ║ │ <─ ─ ─ ─ ─ ─ ─ ─ > ║ TCPconnection ║ ║
|
||||
│ ║ │ │ ║ with target host ║ ║
|
||||
│ ╚════════════╪══════════════════╪══╚═══════════════════╝═════════╝
|
||||
│ │ │
|
||||
│ Response packet │ │
|
||||
│ <───────────────────────── │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ ╔═══════╗ │
|
||||
══════════════════════╪══════════════════════════╪══╣ Usage ╠═══════╪════════════════════════════════════════════
|
||||
│ │ ╚═══════╝ │
|
||||
│ │ │
|
||||
╔═════════════════╗│ │ │
|
||||
║Client uses the ░║│ │ │
|
||||
║ socket opened ║│ │ │
|
||||
║ with proxy ║│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
|
||||
║to communicate ║│ │ │
|
||||
║ ║│ │ │
|
||||
╚═════════════════╝┴───┐ ┌──┴──┐ ┌──┴───┐
|
||||
│Client│ │Proxy│ │Target│
|
||||
└──────┘ └─────┘ └──────┘
|
||||
|
||||
|
||||
# Configure and Building example
|
||||
|
||||
This example requires the proxy address to be configured. You can do this using the menuconfig option.
|
||||
Proxy address and port must be configured in order for this example to work.
|
||||
|
||||
If using Linux ssh can be used as a proxy for testing.
|
||||
|
||||
```
|
||||
ssh -N -v -D 0.0.0.0:1080 localhost
|
||||
```
|
||||
# Async operations composition and automatic lifetime control
|
||||
|
||||
For documentation about the structure of this example look into [async\_request README](../async_request/README.md).
|
||||
|
2
components/asio/examples/socks4/main/CMakeLists.txt
Normal file
2
components/asio/examples/socks4/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "socks4.cpp"
|
||||
INCLUDE_DIRS ".")
|
16
components/asio/examples/socks4/main/Kconfig.projbuild
Normal file
16
components/asio/examples/socks4/main/Kconfig.projbuild
Normal file
@ -0,0 +1,16 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PROXY_ADDRESS
|
||||
string "Proxy address"
|
||||
default "myproxy"
|
||||
help
|
||||
Address of the proxy to be used.
|
||||
|
||||
config EXAMPLE_PROXY_SERVICE
|
||||
string "Proxy Service Type"
|
||||
default "myport"
|
||||
help
|
||||
Service type. Due to a limitation of lwip, must
|
||||
be the port number e.g. "1080".
|
||||
|
||||
endmenu
|
393
components/asio/examples/socks4/main/socks4.cpp
Normal file
393
components/asio/examples/socks4/main/socks4.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*
|
||||
*
|
||||
* ASIO Socks4 example
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <asio.hpp>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include "esp_log.h"
|
||||
#include "socks4.hpp"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
constexpr auto TAG = "asio_socks4";
|
||||
using asio::ip::tcp;
|
||||
|
||||
namespace {
|
||||
|
||||
void esp_init()
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_log_level_set("async_request", ESP_LOG_DEBUG);
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Simple class to add the resolver to a chain of actions
|
||||
*
|
||||
*/
|
||||
class AddressResolution : public std::enable_shared_from_this<AddressResolution> {
|
||||
public:
|
||||
explicit AddressResolution(asio::io_context &context) : ctx(context), resolver(ctx) {}
|
||||
|
||||
/**
|
||||
* @brief Initiator function for the address resolution
|
||||
*
|
||||
* @tparam CompletionToken callable responsible to use the results.
|
||||
*
|
||||
* @param host Host address
|
||||
* @param port Port for the target, must be number due to a limitation on lwip.
|
||||
*/
|
||||
template<class CompletionToken>
|
||||
void resolve(const std::string &host, const std::string &port, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
resolver.async_resolve(host, port, [self, completion_handler](const asio::error_code & error, tcp::resolver::results_type results) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Failed to resolve: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
completion_handler(self, results);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context &ctx;
|
||||
tcp::resolver resolver;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Connection class
|
||||
*
|
||||
* The lowest level dependency on our asynchronous task, Connection provide an interface to TCP sockets.
|
||||
* A similar class could be provided for a TLS connection.
|
||||
*
|
||||
* @note: All read and write operations are written on an explicit strand, even though an implicit strand
|
||||
* occurs in this example since we run the io context in a single task.
|
||||
*
|
||||
*/
|
||||
class Connection : public std::enable_shared_from_this<Connection> {
|
||||
public:
|
||||
explicit Connection(asio::io_context &context) : ctx(context), strand(context), socket(ctx) {}
|
||||
|
||||
/**
|
||||
* @brief Start the connection
|
||||
*
|
||||
* Async operation to start a connection. As the final act of the process the Connection class pass a
|
||||
* std::shared_ptr of itself to the completion_handler.
|
||||
* Since it uses std::shared_ptr as an automatic control of its lifetime this class must be created
|
||||
* through a std::make_shared call.
|
||||
*
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
* @param host host address
|
||||
* @param port port number - due to a limitation on lwip implementation this should be the number not the
|
||||
* service name typically seen in ASIO examples.
|
||||
*
|
||||
* @note The class could be modified to store the completion handler, as a member variable, instead of
|
||||
* pass it along asynchronous calls to allow the process to run again completely.
|
||||
*
|
||||
*/
|
||||
template<class CompletionToken>
|
||||
void start(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||
{
|
||||
connect(results, completion_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an async write on the socket
|
||||
*
|
||||
* @tparam data
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
*
|
||||
*/
|
||||
template<class DataType, class CompletionToken>
|
||||
void write_async(const DataType &data, CompletionToken &&completion_handler)
|
||||
{
|
||||
asio::async_write(socket, data, asio::bind_executor(strand, completion_handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an async read on the socket
|
||||
*
|
||||
* @tparam data
|
||||
* @tparam completion_handler A callable to act as the final handler for the process.
|
||||
*
|
||||
*/
|
||||
template<class DataBuffer, class CompletionToken>
|
||||
void read_async(DataBuffer &&in_data, CompletionToken &&completion_handler)
|
||||
{
|
||||
asio::async_read(socket, in_data, asio::bind_executor(strand, completion_handler));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<class CompletionToken>
|
||||
void connect(tcp::resolver::results_type results, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_connect(socket, results, [self, completion_handler](const asio::error_code & error, [[maybe_unused]] const tcp::endpoint & endpoint) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Failed to connect: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
completion_handler(self);
|
||||
});
|
||||
}
|
||||
asio::io_context &ctx;
|
||||
asio::io_context::strand strand;
|
||||
tcp::socket socket;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Socks {
|
||||
|
||||
struct ConnectionData {
|
||||
ConnectionData(socks4::request::command_type cmd, const asio::ip::tcp::endpoint &endpoint,
|
||||
const std::string &user_id) : request(cmd, endpoint, user_id) {};
|
||||
socks4::request request;
|
||||
socks4::reply reply;
|
||||
};
|
||||
|
||||
template<class CompletionToken>
|
||||
void async_connect(asio::io_context &context, std::string proxy, std::string proxy_port, std::string host, std::string port, CompletionToken &&completion_handler)
|
||||
{
|
||||
/*
|
||||
* The first step is to resolve the address of the proxy we want to connect to.
|
||||
* The AddressResolution itself is injected to the completion handler.
|
||||
*/
|
||||
// Resolve proxy
|
||||
std::make_shared<AddressResolution>(context)->resolve(proxy, proxy_port,
|
||||
[&context, host, port, completion_handler](std::shared_ptr<AddressResolution> resolver, tcp::resolver::results_type proxy_resolution) {
|
||||
// We also need to resolve the target host address
|
||||
resolver->resolve(host, port, [&context, proxy_resolution, completion_handler](std::shared_ptr<AddressResolution> resolver, tcp::resolver::results_type host_resolution) {
|
||||
// Make connection with the proxy
|
||||
ESP_LOGI(TAG, "Startig Proxy Connection");
|
||||
std::make_shared<Connection>(context)->start(proxy_resolution,
|
||||
[resolver, host_resolution, completion_handler](std::shared_ptr<Connection> connection) {
|
||||
auto connect_data = std::make_shared<ConnectionData>(socks4::request::connect, *host_resolution, "");
|
||||
ESP_LOGI(TAG, "Sending Request to proxy for host connection.");
|
||||
connection->write_async(connect_data->request.buffers(), [connection, connect_data, completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Proxy request write error: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
connection->read_async(connect_data->reply.buffers(), [connection, connect_data, completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||
if (error) {
|
||||
|
||||
ESP_LOGE(TAG, "Proxy response read error: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
if (!connect_data->reply.success()) {
|
||||
ESP_LOGE(TAG, "Proxy error: %#x", connect_data->reply.status());
|
||||
}
|
||||
completion_handler(connection);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
} // namespace Socks
|
||||
|
||||
namespace Http {
|
||||
enum class Method { GET };
|
||||
|
||||
/**
|
||||
* @brief Simple HTTP request class
|
||||
*
|
||||
* The user needs to write the request information direct to header and body fields.
|
||||
*
|
||||
* Only GET verb is provided.
|
||||
*
|
||||
*/
|
||||
class Request {
|
||||
public:
|
||||
Request(Method method, std::string host, std::string port, const std::string &target) : host_data(std::move(host)), port_data(std::move(port))
|
||||
{
|
||||
header_data.append("GET ");
|
||||
header_data.append(target);
|
||||
header_data.append(" HTTP/1.1");
|
||||
header_data.append("\r\n");
|
||||
header_data.append("Host: ");
|
||||
header_data.append(host_data);
|
||||
header_data.append("\r\n");
|
||||
header_data.append("\r\n");
|
||||
};
|
||||
|
||||
void set_header_field(std::string const &field)
|
||||
{
|
||||
header_data.append(field);
|
||||
}
|
||||
|
||||
void append_to_body(std::string const &data)
|
||||
{
|
||||
body_data.append(data);
|
||||
};
|
||||
|
||||
const std::string &host() const
|
||||
{
|
||||
return host_data;
|
||||
}
|
||||
|
||||
const std::string &service_port() const
|
||||
{
|
||||
return port_data;
|
||||
}
|
||||
|
||||
const std::string &header() const
|
||||
{
|
||||
return header_data;
|
||||
}
|
||||
|
||||
const std::string &body() const
|
||||
{
|
||||
return body_data;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string host_data;
|
||||
std::string port_data;
|
||||
std::string header_data;
|
||||
std::string body_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Simple HTTP response class
|
||||
*
|
||||
* The response is built from received data and only parsed to split header and body.
|
||||
*
|
||||
* A copy of the received data is kept.
|
||||
*
|
||||
*/
|
||||
struct Response {
|
||||
/**
|
||||
* @brief Construct a response from a contiguous buffer.
|
||||
*
|
||||
* Simple http parsing.
|
||||
*
|
||||
*/
|
||||
template<class DataIt>
|
||||
explicit Response(DataIt data, size_t size)
|
||||
{
|
||||
raw_response = std::string(data, size);
|
||||
|
||||
auto header_last = raw_response.find("\r\n\r\n");
|
||||
if (header_last != std::string::npos) {
|
||||
header = raw_response.substr(0, header_last);
|
||||
}
|
||||
body = raw_response.substr(header_last + 3);
|
||||
}
|
||||
/**
|
||||
* @brief Print response content.
|
||||
*/
|
||||
void print()
|
||||
{
|
||||
ESP_LOGI(TAG, "Header :\n %s", header.c_str());
|
||||
ESP_LOGI(TAG, "Body : \n %s", body.c_str());
|
||||
}
|
||||
|
||||
std::string raw_response;
|
||||
std::string header;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
/** @brief HTTP Session
|
||||
*
|
||||
* Session class to handle HTTP protocol implementation.
|
||||
*
|
||||
*/
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
public:
|
||||
explicit Session(std::shared_ptr<Connection> connection_in) : connection(std::move(connection_in))
|
||||
{
|
||||
}
|
||||
|
||||
template<class CompletionToken>
|
||||
void send_request(const Request &request, CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
send_data = { asio::buffer(request.header()), asio::buffer(request.body()) };
|
||||
connection->write_async(send_data, [self, &completion_handler](std::error_code error, std::size_t bytes_transfered) {
|
||||
if (error) {
|
||||
ESP_LOGE(TAG, "Request write error: %s", error.message().c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Bytes Transfered: %d", bytes_transfered);
|
||||
self->get_response(completion_handler);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template<class CompletionToken>
|
||||
void get_response(CompletionToken &&completion_handler)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
connection->read_async(asio::buffer(receive_buffer), [self, &completion_handler](std::error_code error, std::size_t bytes_received) {
|
||||
if (error and error.value() != asio::error::eof) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Bytes Received: %d", bytes_received);
|
||||
if (bytes_received == 0) {
|
||||
return;
|
||||
}
|
||||
Response response(std::begin(self->receive_buffer), bytes_received);
|
||||
|
||||
completion_handler(self, response);
|
||||
});
|
||||
}
|
||||
/*
|
||||
* For this example we assumed 2048 to be enough for the receive_buffer
|
||||
*/
|
||||
std::array<char, 2048> receive_buffer;
|
||||
/*
|
||||
* The hardcoded 2 below is related to the type we receive the data to send. We gather the parts from Request, header
|
||||
* and body, to send avoiding the copy.
|
||||
*/
|
||||
std::array<asio::const_buffer, 2> send_data;
|
||||
std::shared_ptr<Connection> connection;
|
||||
};
|
||||
}// namespace Http
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
// Basic initialization of ESP system
|
||||
esp_init();
|
||||
|
||||
asio::io_context io_context;
|
||||
Http::Request request(Http::Method::GET, "www.httpbin.org", "80", "/get");
|
||||
Socks::async_connect(io_context, CONFIG_EXAMPLE_PROXY_ADDRESS, CONFIG_EXAMPLE_PROXY_SERVICE, request.host(), request.service_port(),
|
||||
[&request](std::shared_ptr<Connection> connection) {
|
||||
// Now we create a HTTP::Session and inject the necessary connection.
|
||||
std::make_shared<Http::Session>(connection)->send_request(request, [](std::shared_ptr<Http::Session> session, Http::Response response) {
|
||||
response.print();
|
||||
});
|
||||
});
|
||||
// io_context.run will block until all the tasks on the context are done.
|
||||
io_context.run();
|
||||
ESP_LOGI(TAG, "Context run done");
|
||||
|
||||
ESP_ERROR_CHECK(example_disconnect());
|
||||
}
|
143
components/asio/examples/socks4/main/socks4.hpp
Normal file
143
components/asio/examples/socks4/main/socks4.hpp
Normal file
@ -0,0 +1,143 @@
|
||||
//
|
||||
// socks4.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCKS4_HPP
|
||||
#define SOCKS4_HPP
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <asio/buffer.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
|
||||
namespace socks4 {
|
||||
|
||||
const unsigned char version = 0x04;
|
||||
|
||||
class request
|
||||
{
|
||||
public:
|
||||
enum command_type
|
||||
{
|
||||
connect = 0x01,
|
||||
bind = 0x02
|
||||
};
|
||||
|
||||
request(command_type cmd, const asio::ip::tcp::endpoint& endpoint,
|
||||
const std::string& user_id)
|
||||
: version_(version),
|
||||
command_(cmd),
|
||||
user_id_(user_id),
|
||||
null_byte_(0)
|
||||
{
|
||||
// Only IPv4 is supported by the SOCKS 4 protocol.
|
||||
if (endpoint.protocol() != asio::ip::tcp::v4())
|
||||
{
|
||||
throw asio::system_error(
|
||||
asio::error::address_family_not_supported);
|
||||
}
|
||||
|
||||
// Convert port number to network byte order.
|
||||
unsigned short port = endpoint.port();
|
||||
port_high_byte_ = (port >> 8) & 0xff;
|
||||
port_low_byte_ = port & 0xff;
|
||||
|
||||
// Save IP address in network byte order.
|
||||
address_ = endpoint.address().to_v4().to_bytes();
|
||||
}
|
||||
|
||||
std::array<asio::const_buffer, 7> buffers() const
|
||||
{
|
||||
return
|
||||
{
|
||||
{
|
||||
asio::buffer(&version_, 1),
|
||||
asio::buffer(&command_, 1),
|
||||
asio::buffer(&port_high_byte_, 1),
|
||||
asio::buffer(&port_low_byte_, 1),
|
||||
asio::buffer(address_),
|
||||
asio::buffer(user_id_),
|
||||
asio::buffer(&null_byte_, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char version_;
|
||||
unsigned char command_;
|
||||
unsigned char port_high_byte_;
|
||||
unsigned char port_low_byte_;
|
||||
asio::ip::address_v4::bytes_type address_;
|
||||
std::string user_id_;
|
||||
unsigned char null_byte_;
|
||||
};
|
||||
|
||||
class reply
|
||||
{
|
||||
public:
|
||||
enum status_type
|
||||
{
|
||||
request_granted = 0x5a,
|
||||
request_failed = 0x5b,
|
||||
request_failed_no_identd = 0x5c,
|
||||
request_failed_bad_user_id = 0x5d
|
||||
};
|
||||
|
||||
reply()
|
||||
: null_byte_(0),
|
||||
status_()
|
||||
{
|
||||
}
|
||||
|
||||
std::array<asio::mutable_buffer, 5> buffers()
|
||||
{
|
||||
return
|
||||
{
|
||||
{
|
||||
asio::buffer(&null_byte_, 1),
|
||||
asio::buffer(&status_, 1),
|
||||
asio::buffer(&port_high_byte_, 1),
|
||||
asio::buffer(&port_low_byte_, 1),
|
||||
asio::buffer(address_)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool success() const
|
||||
{
|
||||
return null_byte_ == 0 && status_ == request_granted;
|
||||
}
|
||||
|
||||
unsigned char status() const
|
||||
{
|
||||
return status_;
|
||||
}
|
||||
|
||||
asio::ip::tcp::endpoint endpoint() const
|
||||
{
|
||||
unsigned short port = port_high_byte_;
|
||||
port = (port << 8) & 0xff00;
|
||||
port = port | port_low_byte_;
|
||||
|
||||
asio::ip::address_v4 address(address_);
|
||||
|
||||
return asio::ip::tcp::endpoint(address, port);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char null_byte_;
|
||||
unsigned char status_;
|
||||
unsigned char port_high_byte_;
|
||||
unsigned char port_low_byte_;
|
||||
asio::ip::address_v4::bytes_type address_;
|
||||
};
|
||||
|
||||
} // namespace socks4
|
||||
|
||||
#endif // SOCKS4_HPP
|
3
components/asio/examples/socks4/sdkconfig.defaults
Normal file
3
components/asio/examples/socks4/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_COMPILER_CXX_RTTI=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0
|
11
components/asio/examples/ssl_client_server/CMakeLists.txt
Normal file
11
components/asio/examples/ssl_client_server/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../.. ../../../../common_components/protocol_examples_common)
|
||||
set(EXCLUDE_COMPONENTS openssl)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_ssl_client_server)
|
88
components/asio/examples/ssl_client_server/README.md
Normal file
88
components/asio/examples/ssl_client_server/README.md
Normal file
@ -0,0 +1,88 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# Asio SSL client/server example
|
||||
|
||||
Simple Asio client and server with SSL/TLS transport
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any ESP platform board. No external connection is required, it is recommended though
|
||||
to connect to internet or a local network via WiFi or Ethernet to easily exercise features of this example.
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
* Enable the ASIO client and set server's host name to examine client's functionality.
|
||||
The ASIO client connects to the configured server and sends default payload string "GET / HTTP/1.1"
|
||||
* Enable the ASIO server to examine server's functionality. The ASIO server listens to connection and echos back what was received.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
### Client connecting to public server
|
||||
|
||||
The below output illustrates the client connecting to a public https server.
|
||||
|
||||
```
|
||||
I (1267) example_connect: Waiting for IP(s)
|
||||
I (2587) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1
|
||||
I (3367) wifi:state: init -> auth (b0)
|
||||
I (3377) wifi:state: auth -> assoc (0)
|
||||
I (3387) wifi:state: assoc -> run (10)
|
||||
I (3397) wifi:security type: 3, phy: bgn, rssi: -49
|
||||
I (3397) wifi:pm start, type: 1
|
||||
I (3457) wifi:AP's beacon interval = 102400 us, DTIM period = 1
|
||||
I (4747) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (5247) esp_netif_handlers: example_connect: sta ip: 192.168.32.69, mask: 255.255.252.0, gw: 192.168.32.3
|
||||
I (5247) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.32.69
|
||||
I (5257) example_connect: Connected to example_connect: sta
|
||||
I (5257) example_connect: - IPv4 address: 192.168.32.69
|
||||
I (5267) example_connect: - IPv6 address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
W (5277) esp32_asio_pthread: pthread_condattr_setclock: not yet supported!
|
||||
W (5297) esp32_asio_pthread: pthread_condattr_setclock: not yet supported!
|
||||
Reply: HTTP/1.1 200 OK
|
||||
D
|
||||
```
|
||||
### Both server and client enabled
|
||||
|
||||
The below output demonstrates the client connecting to the ASIO server via loopback interface, so no WiFi, nor Ethernet connection
|
||||
was established.
|
||||
```
|
||||
I (0) cpu_start: App cpu up.
|
||||
I (495) heap_init: Initializing. RAM available for dynamic allocation:
|
||||
I (502) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
|
||||
I (508) heap_init: At 3FFB5400 len 0002AC00 (171 KiB): DRAM
|
||||
I (515) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
|
||||
I (521) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
|
||||
I (527) heap_init: At 4008BB80 len 00014480 (81 KiB): IRAM
|
||||
I (534) cpu_start: Pro cpu start user code
|
||||
I (556) spi_flash: detected chip: gd
|
||||
I (556) spi_flash: flash io: dio
|
||||
W (556) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
|
||||
I (566) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (600) example_connect: Waiting for IP(s)
|
||||
W (600) esp32_asio_pthread: pthread_condattr_setclock: not yet supported!
|
||||
W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported!
|
||||
W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported!
|
||||
Server received: GET / HTTP/1.1
|
||||
|
||||
|
||||
Reply: GET / HTTP/1.1
|
||||
```
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
16
components/asio/examples/ssl_client_server/example_test.py
Normal file
16
components/asio/examples/ssl_client_server/example_test.py
Normal file
@ -0,0 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32c3'])
|
||||
def test_examples_asio_ssl(env, extra_data):
|
||||
|
||||
dut = env.get_dut('asio_ssl_client_server', 'examples/protocols/asio/ssl_client_server')
|
||||
dut.start_app()
|
||||
|
||||
dut.expect('Reply: GET / HTTP/1.1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_asio_ssl()
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "asio_ssl_main.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES ca.crt server.key srv.crt)
|
@ -0,0 +1,36 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_CLIENT
|
||||
bool "Enable TLS client"
|
||||
default y
|
||||
help
|
||||
Choose this option to use ASIO TLS/SSL client functionality
|
||||
|
||||
config EXAMPLE_PORT
|
||||
string "ASIO port number"
|
||||
default "443"
|
||||
help
|
||||
Port number used by ASIO example.
|
||||
|
||||
config EXAMPLE_SERVER
|
||||
bool "Enable TLS server"
|
||||
default n
|
||||
help
|
||||
Choose this option to use ASIO TLS/SSL server functionality
|
||||
|
||||
config EXAMPLE_SERVER_NAME
|
||||
string "ASIO server name or IP"
|
||||
default "www.google.com"
|
||||
depends on EXAMPLE_CLIENT
|
||||
help
|
||||
Asio example server ip for the ASIO client to connect to.
|
||||
|
||||
config EXAMPLE_CLIENT_VERIFY_PEER
|
||||
bool "Client to verify peer"
|
||||
default n
|
||||
depends on EXAMPLE_CLIENT
|
||||
help
|
||||
This option sets client's mode to verify peer, default is
|
||||
verify-none
|
||||
|
||||
endmenu
|
@ -0,0 +1,272 @@
|
||||
//
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <string>
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "asio.hpp"
|
||||
#include "asio/ssl.hpp"
|
||||
#include "asio/buffer.hpp"
|
||||
#include "esp_pthread.h"
|
||||
|
||||
extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start");
|
||||
extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end");
|
||||
|
||||
extern const unsigned char cacert_pem_start[] asm("_binary_ca_crt_start");
|
||||
extern const unsigned char cacert_pem_end[] asm("_binary_ca_crt_end");
|
||||
|
||||
extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start");
|
||||
extern const unsigned char prvtkey_pem_end[] asm("_binary_server_key_end");
|
||||
|
||||
static const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start);
|
||||
static const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start);
|
||||
static const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start);
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
static const std::size_t max_length = 1024;
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client(asio::io_context &io_context,
|
||||
asio::ssl::context &context,
|
||||
const tcp::resolver::results_type &endpoints)
|
||||
: socket_(io_context, context)
|
||||
{
|
||||
|
||||
#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
socket_.set_verify_mode(asio::ssl::verify_peer);
|
||||
#else
|
||||
socket_.set_verify_mode(asio::ssl::verify_none);
|
||||
#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
|
||||
connect(endpoints);
|
||||
}
|
||||
|
||||
private:
|
||||
void connect(const tcp::resolver::results_type &endpoints)
|
||||
{
|
||||
asio::async_connect(socket_.lowest_layer(), endpoints,
|
||||
[this](const std::error_code & error,
|
||||
const tcp::endpoint & /*endpoint*/) {
|
||||
if (!error) {
|
||||
handshake();
|
||||
} else {
|
||||
std::cout << "Connect failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handshake()
|
||||
{
|
||||
socket_.async_handshake(asio::ssl::stream_base::client,
|
||||
[this](const std::error_code & error) {
|
||||
if (!error) {
|
||||
send_request();
|
||||
} else {
|
||||
std::cout << "Handshake failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void send_request()
|
||||
{
|
||||
size_t request_length = std::strlen(request_);
|
||||
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(request_, request_length),
|
||||
[this](const std::error_code & error, std::size_t length) {
|
||||
if (!error) {
|
||||
receive_response(length);
|
||||
} else {
|
||||
std::cout << "Write failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void receive_response(std::size_t length)
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(reply_, length),
|
||||
[this](const std::error_code & error, std::size_t length) {
|
||||
if (!error) {
|
||||
std::cout << "Reply: ";
|
||||
std::cout.write(reply_, length);
|
||||
std::cout << "\n";
|
||||
} else {
|
||||
std::cout << "Read failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ssl::stream<tcp::socket> socket_;
|
||||
char request_[max_length] = "GET / HTTP/1.1\r\n\r\n";
|
||||
char reply_[max_length];
|
||||
};
|
||||
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
public:
|
||||
Session(tcp::socket socket, asio::ssl::context &context)
|
||||
: socket_(std::move(socket), context)
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_handshake();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_handshake()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_handshake(asio::ssl::stream_base::server,
|
||||
[this, self](const std::error_code & error) {
|
||||
if (!error) {
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
[this, self](const std::error_code & ec, std::size_t length) {
|
||||
if (!ec) {
|
||||
std::cout << "Server received: ";
|
||||
std::cout.write(data_, length);
|
||||
std::cout << std::endl;
|
||||
do_write(length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](const std::error_code & ec,
|
||||
std::size_t /*length*/) {
|
||||
if (!ec) {
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ssl::stream<tcp::socket> socket_;
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
class Server {
|
||||
public:
|
||||
Server(asio::io_context &io_context, unsigned short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
|
||||
context_(asio::ssl::context::tls_server)
|
||||
{
|
||||
context_.set_options(
|
||||
asio::ssl::context::default_workarounds
|
||||
| asio::ssl::context::no_sslv2);
|
||||
context_.use_certificate_chain(server_cert);
|
||||
context_.use_private_key(privkey, asio::ssl::context::pem);
|
||||
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](const std::error_code & error, tcp::socket socket) {
|
||||
if (!error) {
|
||||
std::make_shared<Session>(std::move(socket), context_)->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
asio::ssl::context context_;
|
||||
};
|
||||
|
||||
void set_thread_config(const char *name, int stack, int prio)
|
||||
{
|
||||
auto cfg = esp_pthread_get_default_config();
|
||||
cfg.thread_name = name;
|
||||
cfg.stack_size = stack;
|
||||
cfg.prio = prio;
|
||||
esp_pthread_set_cfg(&cfg);
|
||||
}
|
||||
|
||||
void ssl_server_thread()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
Server s(io_context, 443);
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
void ssl_client_thread()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME;
|
||||
std::string server_port = CONFIG_EXAMPLE_PORT;
|
||||
auto endpoints = resolver.resolve(server_ip, server_port);
|
||||
|
||||
asio::ssl::context ctx(asio::ssl::context::tls_client);
|
||||
#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
ctx.add_certificate_authority(cert_chain);
|
||||
#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER
|
||||
|
||||
Client c(io_context, ctx, endpoints);
|
||||
|
||||
io_context.run();
|
||||
|
||||
}
|
||||
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* This helper function configures blocking UART I/O */
|
||||
ESP_ERROR_CHECK(example_configure_stdin_stdout());
|
||||
std::vector<std::thread> work_threads;
|
||||
|
||||
#if CONFIG_EXAMPLE_SERVER
|
||||
set_thread_config("Server", 16 * 1024, 5);
|
||||
work_threads.emplace_back(std::thread(ssl_server_thread));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
#endif // CONFIG_EXAMPLE_SERVER
|
||||
|
||||
#if CONFIG_EXAMPLE_CLIENT
|
||||
set_thread_config("Client", 16 * 1024, 5);
|
||||
work_threads.emplace_back(ssl_client_thread);
|
||||
#endif // CONFIG_EXAMPLE_CLIENT
|
||||
|
||||
for (auto &t : work_threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
}
|
22
components/asio/examples/ssl_client_server/main/ca.crt
Normal file
22
components/asio/examples/ssl_client_server/main/ca.crt
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkzCCAnugAwIBAgIUNI5wldYysh6rtCzYmda6H414aRswDQYJKoZIhvcNAQEL
|
||||
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMB4X
|
||||
DTIwMDEyMTA5MDk0NloXDTI1MDEyMDA5MDk0NlowWTELMAkGA1UEBhMCQVUxEzAR
|
||||
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
|
||||
IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAyadSpRnIQBVbEAsbpkrKrOMlBOMIUmA8AfNyOYPLfv0Oa5lBiMAV
|
||||
3OQDu5tYyFYKwkCUqq65iAm50fPbSH71w1tkja6nZ1yAIM+TvpMlM/WiFGrhY+Tc
|
||||
kAcLcKUJyPxrv/glzoVslbqUgIhuhCSKA8uk1+ILcn3nWzPcbcowLx31+AHeZj8h
|
||||
bIAdj6vjqxMCFStp4IcA+ikmCk75LCN4vkkifdkebb/ZDNYCZZhpCBnCHyFAjPc4
|
||||
7C+FDVGT3/UUeeTy+Mtn+MqUAhB+W0sPDm1n2h59D4Z/MFm0hl6GQCAKeMJPzssU
|
||||
BBsRm6zoyPQ4VTqG0uwfNNbORyIfKONMUwIDAQABo1MwUTAdBgNVHQ4EFgQUGYLV
|
||||
EkgWzxjpltE6texha7zZVxowHwYDVR0jBBgwFoAUGYLVEkgWzxjpltE6texha7zZ
|
||||
VxowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb2EF4Zg2XWNb
|
||||
eZHnzupCDd9jAhwPqkt7F1OXvxJa/RFUSB9+2izGvikGGhuKY4f0iLuqF+bhExD9
|
||||
sapDcdFO2Suh4J3onbwEvmKvsv56K3xhapYg8WwPofpkVirnkwFjpQXGzrYxPujg
|
||||
BPmSy3psQrhvOr/WH7SefJv2qr4ikaugfE+3enY4PL+C1dSQAuNo1QGgWsZIu0c8
|
||||
TZybNZ13vNVMA+tgj2CM8FR3Etaabwtu3TTcAnO7aoBTix/bLBTuZoczhN8/MhG3
|
||||
GylmDzFI8a6aKxQL3Fi4PsM82hRKWu3gfs39sR1Ci4V22v8uO5EWBPK0QZvDSc1a
|
||||
KwwxI4zA0w==
|
||||
-----END CERTIFICATE-----
|
27
components/asio/examples/ssl_client_server/main/server.key
Normal file
27
components/asio/examples/ssl_client_server/main/server.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAlUCywNhVv4RO2y9h/XGKZ1azzk3jzHpSBzIGO9LoiA8trC/p
|
||||
1ykGaUfYPJllYK4HMhC4fUyE3J7tVL2Eskzl26LNPLbEoaBWZM9NhV3iA1/1EtOu
|
||||
p6umLx+y3sDfvK35YAOUbjdAlBfhnJ4r8h7oTsxl3J5jZ18zgjJnJi2NEFq/yTpO
|
||||
MiwHLWPjy25fDFixfV9UzSvbgt1JaGPmC7c4QkhHzjyp0+ikuvRIw0p9BBNeqBV2
|
||||
da3qBMB5FtodUJTAz6o6OKWbTalLjQi6C1H6z9TnY7IrJBUOy/FWkQH/sEsLdscD
|
||||
hHa1Dz2oT203QjhzyOSfnNF95D/1MdNcMt6l0wIDAQABAoIBAC1JJTOoMFRc48RT
|
||||
myrYQYNbZlEphv3q+2qdfhC2zMFDwbrmCtCy7PQSzYSNkpoEE8DYG/JAvmtmeWJl
|
||||
4pZrCK9ctWM/nWfhC3WpBL97nfEiM20T94F+bn0L5Cz8XqaULv839th+QUTt/hGU
|
||||
WIctY5VNJXcMQ+MAmtNdUbjex1d3iuxiKHUo4nDoZ8digKFNdtdP5B5nlMq5chCL
|
||||
mxNRcsGsx2dDAxbGUapdTVPWHPJKpLOBoSkluDsfd2KZADFU2R1SJpAX9+RYh3HM
|
||||
5FTUdHTUaISxbKkgeDKlEM0lqk2TtGUwCyEj098ewi7Wzsu9w60IplPPUJx5FRG6
|
||||
jp3wzLkCgYEAxKp5T20rf/7ysX7x053I7VCjDXUxAaWOEj1uS3AhOkl0NaZg7Di+
|
||||
y53fWNkcHdkt2n2LqMt/43UgMYq3TVVcq2eunPNF11e1bJw8CjDafwDs4omwwyVn
|
||||
lYhPuB4dK2OAib+vU5Zqpp0kZMoxk2MZVgon8z+s8DW/zmB6aFqAWeUCgYEAwkhC
|
||||
OgmXKMdjOCVy5t2f5UbY8Y9rV3w8eUATuJ47MMwLr4pGYnKoEn9JB4ltWrHv/u5S
|
||||
fOv3tIrrCEvnCoCbOILwCsY5LqTNXgqova8FB6RpMUQCzhDd8LHuvdHv0WMnMzX1
|
||||
3PKuqwh8JS55m4WqZRhzr5BFKG4fHPVs4IcaJVcCgYAzzCaJSdqUKqTnJOUydDNQ
|
||||
ddWMHNqccWs62J0tF0pZHLGT089hSAzQejMyJnSmU+Ykzr4y5e44DUg+ZCelIZ93
|
||||
saYmxlgVwI8THQ8fLADQRIEfpV4996MRmkZM2vmZzOo03Zyi6lIKsga82Rg3lnk8
|
||||
1Q3ynknBNpbfF0AGLhfyFQKBgBYlxJ73HutAJ5hr9HhLBYJOnEaVUehMOlycKGNg
|
||||
bmD2sdJWEgYBChXpurqIORYguLo4EuE4ySkkuPxeIr14wbkkfBbOWBBwKxUwY+IT
|
||||
xKAFZxR9q1AwbgyVTCEJgKw/AGX/HcMNS0omEnjunmBTUYRq0C1QZgHg490aQUor
|
||||
PJjLAoGAevzdTpFlVeuKeYh1oDubGO1LinyXpBv7fPFjl+zu4AVbjojcU6yC4OO6
|
||||
QvqopE6SyAECKy8kAOFcESPsGc9Lta2XUvI203z7pIVlNVEcJ0+90mQh3Mn1U46l
|
||||
sZ49PdRvNwNb5wvkh1UqNsMlGFbRlzMbIk45ou4311kCobowZek=
|
||||
-----END RSA PRIVATE KEY-----
|
18
components/asio/examples/ssl_client_server/main/srv.crt
Normal file
18
components/asio/examples/ssl_client_server/main/srv.crt
Normal file
@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC9DCCAdwCFA1lSIcHwYKdB2UqOrZxZnVgPObTMA0GCSqGSIb3DQEBCwUAMFkx
|
||||
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yMDA2
|
||||
MTIwNjA0MTNaFw0yMjA2MDIwNjA0MTNaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJVAssDYVb+ETtsvYf1ximdW
|
||||
s85N48x6UgcyBjvS6IgPLawv6dcpBmlH2DyZZWCuBzIQuH1MhNye7VS9hLJM5dui
|
||||
zTy2xKGgVmTPTYVd4gNf9RLTrqerpi8fst7A37yt+WADlG43QJQX4ZyeK/Ie6E7M
|
||||
ZdyeY2dfM4IyZyYtjRBav8k6TjIsBy1j48tuXwxYsX1fVM0r24LdSWhj5gu3OEJI
|
||||
R848qdPopLr0SMNKfQQTXqgVdnWt6gTAeRbaHVCUwM+qOjilm02pS40IugtR+s/U
|
||||
52OyKyQVDsvxVpEB/7BLC3bHA4R2tQ89qE9tN0I4c8jkn5zRfeQ/9THTXDLepdMC
|
||||
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAnMYGW+idt37bEE4WPgrRorKWuplR+zHD
|
||||
wJFz53DQzyIZJHmJ2hR5U0jNcHy/nMq7tbdz9LZPrVF4lZJ3TJhnmkOKjMFPCQE8
|
||||
YcmsP3il6eXgtGqg53InOi/uJqEQ9TfM54cbpp6xKbnmpwk4uprISBRQt7u2ZLk2
|
||||
40ED6zgjFPDTYmSjSpb2AN6KUB6PflgVs+4p9ViHNq4U3AlYV/BM0+3G4aMX2wNl
|
||||
ZIpQfOyuaYD5MU50mY+O+gDiiypkpYf6a6S4YJ1sMbavDsP7bW5UMnP0jKYR549q
|
||||
5hF1fdkXq52DfJ9ya2kl3mANFkKssQV+1KCBMxGoeqfakmJfa03xXA==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1400000,
|
|
6
components/asio/examples/ssl_client_server/sdkconfig.ci
Normal file
6
components/asio/examples/ssl_client_server/sdkconfig.ci
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_EXAMPLE_CLIENT=y
|
||||
CONFIG_EXAMPLE_SERVER=y
|
||||
CONFIG_EXAMPLE_SERVER_NAME="localhost"
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=n
|
||||
CONFIG_EXAMPLE_CLIENT_VERIFY_PEER=y
|
@ -0,0 +1,10 @@
|
||||
CONFIG_ASIO_SSL_SUPPORT=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
# Leave some room for larger apps without needing to reduce other features
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
10
components/asio/examples/tcp_echo_server/CMakeLists.txt
Normal file
10
components/asio/examples/tcp_echo_server/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../.. ../../../../common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_tcp_echo_server)
|
22
components/asio/examples/tcp_echo_server/README.md
Normal file
22
components/asio/examples/tcp_echo_server/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# Asio TCP echo server example
|
||||
|
||||
Simple Asio TCP echo server using WiFi STA or Ethernet.
|
||||
|
||||
## Example workflow
|
||||
|
||||
- Wi-Fi or Ethernet connection is established, and IP address is obtained.
|
||||
- Asio TCP server is started on port number defined through the project configuration.
|
||||
- Server receives and echoes back messages transmitted from client.
|
||||
|
||||
## Running the example
|
||||
|
||||
- Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
- Set server port number in menuconfig, "Example configuration".
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- You can now send a TCP message and check it is repeated, for example using netcat `nc IP PORT`.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
@ -0,0 +1,47 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_tcp_server(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Start server
|
||||
3. Test connects to server and sends a test message
|
||||
4. Test evaluates received test message from server
|
||||
5. Test evaluates received test message on server stdout
|
||||
"""
|
||||
test_msg = b'echo message from client to server'
|
||||
dut1 = env.get_dut('tcp_echo_server', 'examples/protocols/asio/tcp_echo_server', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_tcp_echo_server.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_tcp_echo_server_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start test
|
||||
dut1.start_app()
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
if (data == test_msg):
|
||||
print('PASS: Received correct message')
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asi tcp server: {} (expected:{})'.format(data, test_msg))
|
||||
# 5. check the client message appears also on server terminal
|
||||
dut1.expect(test_msg.decode())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_tcp_server()
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "echo_server.cpp"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,9 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PORT
|
||||
string "Asio example port number"
|
||||
default "2222"
|
||||
help
|
||||
Port number used by Asio example.
|
||||
|
||||
endmenu
|
108
components/asio/examples/tcp_echo_server/main/echo_server.cpp
Normal file
108
components/asio/examples/tcp_echo_server/main/echo_server.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "asio.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
data_[length] = 0;
|
||||
std::cout << data_ << std::endl;
|
||||
do_write(length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* This helper function configures blocking UART I/O */
|
||||
ESP_ERROR_CHECK(example_configure_stdin_stdout());
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
|
||||
|
||||
std::cout << "ASIO engine is up and running" << std::endl;
|
||||
|
||||
io_context.run();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
# Leave some room for larger apps without needing to reduce other features
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
10
components/asio/examples/udp_echo_server/CMakeLists.txt
Normal file
10
components/asio/examples/udp_echo_server/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS ../../ ../../../../common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(asio_udp_echo_server)
|
22
components/asio/examples/udp_echo_server/README.md
Normal file
22
components/asio/examples/udp_echo_server/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# Asio UDP echo server example
|
||||
|
||||
Simple Asio UDP echo server using WiFi STA or Ethernet.
|
||||
|
||||
## Example workflow
|
||||
|
||||
- Wi-Fi or Ethernet connection is established, and IP address is obtained.
|
||||
- Asio UDP server is started on port number defined through the project configuration
|
||||
- Server receives and echoes back messages transmitted from client
|
||||
|
||||
## Running the example
|
||||
|
||||
- Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
- Set server port number in menuconfig, "Example configuration".
|
||||
- Run `idf.py -p PORT flash monitor` to build and upload the example to your board and connect to it's serial terminal.
|
||||
- Wait for the board to connect to WiFi or Ethernet (note the IP address).
|
||||
- You can now send a UDP message and check it is repeated, for example using netcat `nc -u IP PORT`.
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
@ -0,0 +1,47 @@
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI_Protocols')
|
||||
def test_examples_protocol_asio_udp_server(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. join AP
|
||||
2. Start server
|
||||
3. Test connects to server and sends a test message
|
||||
4. Test evaluates received test message from server
|
||||
5. Test evaluates received test message on server stdout
|
||||
"""
|
||||
test_msg = b'echo message from client to server'
|
||||
dut1 = env.get_dut('udp_echo_server', 'examples/protocols/asio/udp_echo_server', dut_class=ttfw_idf.ESP32DUT)
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut1.app.binary_path, 'asio_udp_echo_server.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
ttfw_idf.log_performance('asio_udp_echo_server_bin_size', '{}KB'.format(bin_size // 1024))
|
||||
# 1. start test
|
||||
dut1.start_app()
|
||||
# 2. get the server IP address
|
||||
data = dut1.expect(re.compile(r' IPv4 address: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)'), timeout=30)
|
||||
# 3. create tcp client and connect to server
|
||||
dut1.expect('ASIO engine is up and running', timeout=1)
|
||||
cli = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
cli.settimeout(30)
|
||||
cli.connect((data[0], 2222))
|
||||
cli.send(test_msg)
|
||||
data = cli.recv(1024)
|
||||
# 4. check the message received back from the server
|
||||
if (data == test_msg):
|
||||
print('PASS: Received correct message')
|
||||
pass
|
||||
else:
|
||||
print('Failure!')
|
||||
raise ValueError('Wrong data received from asio udp server: {} (expected:{})'.format(data, test_msg))
|
||||
# 5. check the client message appears also on server terminal
|
||||
dut1.expect(test_msg.decode())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_protocol_asio_udp_server()
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "udp_echo_server.cpp"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,9 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_PORT
|
||||
string "Asio example port number"
|
||||
default "2222"
|
||||
help
|
||||
Port number used by Asio example.
|
||||
|
||||
endmenu
|
@ -0,0 +1,90 @@
|
||||
//
|
||||
// async_udp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "asio.hpp"
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: socket_(io_context, udp::endpoint(udp::v4(), port))
|
||||
{
|
||||
do_receive();
|
||||
}
|
||||
|
||||
void do_receive()
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_, max_length), sender_endpoint_,
|
||||
[this](std::error_code ec, std::size_t bytes_recvd)
|
||||
{
|
||||
if (!ec && bytes_recvd > 0)
|
||||
{
|
||||
data_[bytes_recvd] = 0;
|
||||
std::cout << data_ << std::endl;
|
||||
do_send(bytes_recvd);
|
||||
}
|
||||
else
|
||||
{
|
||||
do_receive();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_send(std::size_t length)
|
||||
{
|
||||
socket_.async_send_to(
|
||||
asio::buffer(data_, length), sender_endpoint_,
|
||||
[this](std::error_code /*ec*/, std::size_t bytes /*bytes_sent*/)
|
||||
{
|
||||
do_receive();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
udp::socket socket_;
|
||||
udp::endpoint sender_endpoint_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
esp_netif_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
/* This helper function configures blocking UART I/O */
|
||||
ESP_ERROR_CHECK(example_configure_stdin_stdout());
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(CONFIG_EXAMPLE_PORT));
|
||||
|
||||
std::cout << "ASIO engine is up and running" << std::endl;
|
||||
|
||||
io_context.run();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
# Leave some room for larger apps without needing to reduce other features
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y
|
5
components/asio/idf_component.yml
Normal file
5
components/asio/idf_component.yml
Normal file
@ -0,0 +1,5 @@
|
||||
version: "1.0.2"
|
||||
description: ASIO
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=5.0"
|
45
components/asio/port/include/esp_asio_config.h
Normal file
45
components/asio/port/include/esp_asio_config.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_ASIO_CONFIG_H_
|
||||
#define _ESP_ASIO_CONFIG_H_
|
||||
|
||||
//
|
||||
// Enabling exceptions only when they are enabled in menuconfig
|
||||
//
|
||||
# include <sdkconfig.h>
|
||||
# ifndef CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
# define ASIO_NO_EXCEPTIONS
|
||||
# endif // CONFIG_COMPILER_CXX_EXCEPTIONS
|
||||
|
||||
# ifndef CONFIG_COMPILER_RTTI
|
||||
# define ASIO_NO_TYPEID
|
||||
# endif // CONFIG_COMPILER_RTTI
|
||||
|
||||
//
|
||||
// Use system sockets
|
||||
//
|
||||
# include "sys/socket.h"
|
||||
|
||||
//
|
||||
// Specific ASIO feature flags
|
||||
//
|
||||
# define ASIO_DISABLE_SERIAL_PORT
|
||||
# define ASIO_SEPARATE_COMPILATION
|
||||
# define ASIO_STANDALONE
|
||||
# define ASIO_HAS_PTHREADS
|
||||
# define ASIO_DISABLE_CONCEPTS
|
||||
|
||||
# ifdef CONFIG_ASIO_USE_ESP_OPENSSL
|
||||
# define ASIO_USE_ESP_OPENSSL
|
||||
# define OPENSSL_NO_ENGINE
|
||||
# define ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP
|
||||
# include "openssl_stub.hpp"
|
||||
|
||||
# elif CONFIG_ASIO_USE_ESP_WOLFSSL
|
||||
# define ASIO_USE_WOLFSSL
|
||||
# endif // CONFIG_ASIO_USE_ESP_OPENSSL
|
||||
|
||||
#endif // _ESP_ASIO_CONFIG_H_
|
30
components/asio/port/include/esp_exception.h
Normal file
30
components/asio/port/include/esp_exception.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_EXCEPTION_H_
|
||||
#define _ESP_EXCEPTION_H_
|
||||
|
||||
//
|
||||
// This exception stub is enabled only if exceptions are disabled in menuconfig
|
||||
//
|
||||
#if !defined(CONFIG_COMPILER_CXX_EXCEPTIONS) && defined (ASIO_NO_EXCEPTIONS)
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
//
|
||||
// asio exception stub
|
||||
//
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
template <typename Exception>
|
||||
void throw_exception(const Exception& e)
|
||||
{
|
||||
ESP_LOGE("esp32_asio_exception", "Caught exception: %s!", e.what());
|
||||
abort();
|
||||
}
|
||||
}}
|
||||
#endif // CONFIG_COMPILER_CXX_EXCEPTIONS==1 && defined (ASIO_NO_EXCEPTIONS)
|
||||
|
||||
#endif // _ESP_EXCEPTION_H_
|
46
components/asio/port/include/openssl_stub.hpp
Normal file
46
components/asio/port/include/openssl_stub.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Supply OpenSSL macros and flags for asio-ssl header files
|
||||
//
|
||||
#define OPENSSL_VERSION_NUMBER 0x10100001L
|
||||
|
||||
#define SSL_R_SHORT_READ 219
|
||||
#define SSL_OP_ALL 0
|
||||
#define SSL_OP_SINGLE_DH_USE 0
|
||||
#define SSL_OP_NO_COMPRESSION 0
|
||||
|
||||
#define SSL_OP_NO_SSLv2 0x01000000L
|
||||
#define SSL_OP_NO_SSLv3 0x02000000L
|
||||
#define SSL_OP_NO_TLSv1 0x04000000L
|
||||
|
||||
#define SSL_VERIFY_NONE 0x00
|
||||
#define SSL_VERIFY_PEER 0x01
|
||||
#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
|
||||
#define SSL_VERIFY_CLIENT_ONCE 0x04
|
||||
|
||||
//
|
||||
// Implement asio-ssl layer with these three classes in asio::ssl::mbedtls
|
||||
//
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
class engine;
|
||||
class bio;
|
||||
class shared_ctx;
|
||||
} } } // namespace asio::ssl::mbedtls
|
||||
|
||||
//
|
||||
// Supply OpenSSL types as aliases to mbedtls classes
|
||||
//
|
||||
using X509_STORE_CTX=void;
|
||||
using BIO=asio::ssl::mbedtls::bio;
|
||||
using SSL_CTX=asio::ssl::mbedtls::shared_ctx;
|
||||
using SSL=asio::ssl::mbedtls::engine;
|
113
components/asio/port/mbedtls/include/mbedtls_bio.hpp
Normal file
113
components/asio/port/mbedtls/include/mbedtls_bio.hpp
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "asio/ssl/context_base.hpp"
|
||||
#include "asio/ssl/context.hpp"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
class bio {
|
||||
static constexpr int BIO_SIZE = CONFIG_ASIO_SSL_BIO_SIZE;
|
||||
static constexpr int BIO_FLAGS_READ = 1;
|
||||
static constexpr int BIO_FLAGS_WRITE = 2;
|
||||
|
||||
public:
|
||||
int write(const void *buf, int len)
|
||||
{
|
||||
if (buf == nullptr || len <= 0) {
|
||||
// not an error, just empty operation (as in openssl/bio)
|
||||
return 0;
|
||||
}
|
||||
int remaining = size_ - offset_;
|
||||
if (remaining <= 0) {
|
||||
flags_ |= BIO_FLAGS_WRITE;
|
||||
return -1;
|
||||
}
|
||||
int len_to_write = len > remaining ? remaining : len;
|
||||
std::memcpy(&data_[offset_], buf, len_to_write);
|
||||
offset_ += len_to_write;
|
||||
dlen_ = offset_;
|
||||
if (len_to_write == len) {
|
||||
flags_ &= ~BIO_FLAGS_WRITE;
|
||||
}
|
||||
return len_to_write;
|
||||
}
|
||||
|
||||
int read(void *buf, int len)
|
||||
{
|
||||
if (buf == nullptr || len <= 0) {
|
||||
// not an error, just empty operation (as in openssl/bio)
|
||||
return 0;
|
||||
}
|
||||
int remaining = peer_->dlen_ - peer_->roffset_;
|
||||
if (remaining <= 0) {
|
||||
flags_ |= BIO_FLAGS_READ;
|
||||
return -1;
|
||||
}
|
||||
int len_to_read = remaining > len ? len : remaining;
|
||||
std::memcpy(buf, &peer_->data_[peer_->roffset_], len_to_read);
|
||||
peer_->roffset_ += len_to_read;
|
||||
if (len_to_read == len) {
|
||||
flags_ &= ~BIO_FLAGS_READ;
|
||||
}
|
||||
if (peer_->offset_) {
|
||||
// shift data back to the beginning of the buffer
|
||||
std::memmove(&peer_->data_[0], &peer_->data_[peer_->roffset_], peer_->offset_ - peer_->roffset_);
|
||||
peer_->offset_ -= peer_->roffset_;
|
||||
peer_->roffset_ = 0;
|
||||
peer_->dlen_ = peer_->offset_;
|
||||
}
|
||||
return len_to_read;
|
||||
}
|
||||
|
||||
size_t wpending() const
|
||||
{
|
||||
return dlen_ - roffset_;
|
||||
}
|
||||
|
||||
size_t ctrl_pending()
|
||||
{
|
||||
return peer_->dlen_ - peer_->roffset_;
|
||||
}
|
||||
|
||||
bool should_write() const
|
||||
{
|
||||
return flags_ & BIO_FLAGS_WRITE;
|
||||
}
|
||||
|
||||
bool should_read() const
|
||||
{
|
||||
return flags_ & BIO_FLAGS_READ;
|
||||
}
|
||||
|
||||
static std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>> new_pair(const char* error_location)
|
||||
{
|
||||
auto b1 = std::shared_ptr<bio>(new (std::nothrow) bio);
|
||||
auto b2 = std::shared_ptr<bio>(new (std::nothrow) bio);
|
||||
if (b1 == nullptr || b2 == nullptr) {
|
||||
throw_alloc_failure(error_location);
|
||||
} else {
|
||||
b1->peer_ = b2;
|
||||
b2->peer_ = b1;
|
||||
}
|
||||
return std::make_pair(b1, b2);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<uint8_t, BIO_SIZE> data_ {};
|
||||
size_t size_ {BIO_SIZE};
|
||||
std::shared_ptr<bio> peer_ {nullptr};
|
||||
int dlen_ {0};
|
||||
size_t offset_ {0};
|
||||
size_t roffset_ {0};
|
||||
size_t flags_ {0};
|
||||
};
|
||||
|
||||
} } } // namespace asio::ssl::mbedtls
|
105
components/asio/port/mbedtls/include/mbedtls_context.hpp
Normal file
105
components/asio/port/mbedtls/include/mbedtls_context.hpp
Normal file
@ -0,0 +1,105 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "asio/ssl/context_base.hpp"
|
||||
#include "asio/ssl/context.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace error {
|
||||
|
||||
const asio::error_category& get_mbedtls_category();
|
||||
} // namespace error
|
||||
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
void throw_alloc_failure(const char* location);
|
||||
|
||||
const char *error_message(int error_code);
|
||||
|
||||
enum class container {
|
||||
CERT, CA_CERT, PRIVKEY
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline T* create(const char * location, Args &&... args)
|
||||
{
|
||||
T* t = new (std::nothrow) T(std::forward<Args>(args)...);
|
||||
if (t == nullptr)
|
||||
{
|
||||
throw_alloc_failure(location);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
class context {
|
||||
public:
|
||||
explicit context(context_base::method m): method_(m), options_(0) {}
|
||||
|
||||
const unsigned char *data(container c) const
|
||||
{
|
||||
switch (c) {
|
||||
case container::CERT:
|
||||
return static_cast<const unsigned char *>(cert_chain_.data());
|
||||
case container::CA_CERT:
|
||||
return static_cast<const unsigned char *>(ca_cert_.data());
|
||||
case container::PRIVKEY:
|
||||
return static_cast<const unsigned char *>(private_key_.data());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::size_t size(container c) const
|
||||
{
|
||||
switch (c) {
|
||||
case container::CERT:
|
||||
return cert_chain_.size();
|
||||
case container::CA_CERT:
|
||||
return ca_cert_.size();
|
||||
case container::PRIVKEY:
|
||||
return private_key_.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
context_base::method method_;
|
||||
asio::ssl::context::options options_;
|
||||
const_buffer cert_chain_;
|
||||
const_buffer private_key_;
|
||||
const_buffer ca_cert_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wrapper class around SSL_CTX so we can easily create
|
||||
* a shared pointer to the context without throwing the default exception.
|
||||
* This is useful, as we can use asio::detail::throw_error for allocation errors.
|
||||
*/
|
||||
class shared_ctx {
|
||||
public:
|
||||
static SSL_CTX *create(const char* location, context_base::method m)
|
||||
{
|
||||
auto wrapped = asio::ssl::mbedtls::create<shared_ctx>(location, m);
|
||||
if (wrapped->ctx_ == nullptr)
|
||||
{
|
||||
throw_alloc_failure(location);
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
std::shared_ptr<mbedtls::context> get() const
|
||||
{
|
||||
return ctx_;
|
||||
}
|
||||
|
||||
explicit shared_ctx(context_base::method m)
|
||||
:ctx_(std::shared_ptr<context>(new (std::nothrow) context(m))) { }
|
||||
|
||||
private:
|
||||
std::shared_ptr<mbedtls::context> ctx_;
|
||||
};
|
||||
|
||||
} } } // namespace asio::ssl::mbedtls
|
293
components/asio/port/mbedtls/include/mbedtls_engine.hpp
Normal file
293
components/asio/port/mbedtls/include/mbedtls_engine.hpp
Normal file
@ -0,0 +1,293 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "mbedtls/ssl.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/error.h"
|
||||
#include "mbedtls/esp_debug.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace mbedtls {
|
||||
|
||||
const char *error_message(int error_code)
|
||||
{
|
||||
static char error_buf[100];
|
||||
mbedtls_strerror(error_code, error_buf, sizeof(error_buf));
|
||||
return error_buf;
|
||||
}
|
||||
|
||||
void throw_alloc_failure(const char* location)
|
||||
{
|
||||
asio::error_code ec( MBEDTLS_ERR_SSL_ALLOC_FAILED, asio::error::get_mbedtls_category());
|
||||
asio::detail::throw_error(ec, location);
|
||||
}
|
||||
|
||||
namespace error_codes {
|
||||
|
||||
bool is_error(int ret)
|
||||
{
|
||||
return ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
}
|
||||
|
||||
static bool want_write(int ret)
|
||||
{
|
||||
return ret == MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
}
|
||||
|
||||
static bool want_read(int ret)
|
||||
{
|
||||
return ret == MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
|
||||
} // namespace error_codes
|
||||
|
||||
enum rw_state {
|
||||
IDLE, READING, WRITING, CLOSED
|
||||
};
|
||||
|
||||
class engine {
|
||||
public:
|
||||
explicit engine(std::shared_ptr<context> ctx): ctx_(std::move(ctx)),
|
||||
bio_(bio::new_pair("mbedtls-engine")), state_(IDLE), verify_mode_(0) {}
|
||||
|
||||
void set_verify_mode(asio::ssl::verify_mode mode)
|
||||
{
|
||||
verify_mode_ = mode;
|
||||
}
|
||||
|
||||
bio* ext_bio() const
|
||||
{
|
||||
return bio_.second.get();
|
||||
}
|
||||
|
||||
rw_state get_state() const
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
int shutdown()
|
||||
{
|
||||
int ret = mbedtls_ssl_close_notify(&impl_.ssl_);
|
||||
if (ret) {
|
||||
impl::print_error("mbedtls_ssl_close_notify", ret);
|
||||
}
|
||||
state_ = CLOSED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect()
|
||||
{
|
||||
return handshake(true);
|
||||
}
|
||||
|
||||
int accept()
|
||||
{
|
||||
return handshake(false);
|
||||
}
|
||||
|
||||
int write(const void *buffer, int len)
|
||||
{
|
||||
int ret = impl_.write(buffer, len);
|
||||
state_ = ret == len ? IDLE: WRITING;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int read(void *buffer, int len)
|
||||
{
|
||||
int ret = impl_.read(buffer, len);
|
||||
state_ = ret == len ? IDLE: READING;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
int handshake(bool is_client_not_server)
|
||||
{
|
||||
if (impl_.before_handshake()) {
|
||||
impl_.configure(ctx_.get(), is_client_not_server, impl_verify_mode(is_client_not_server));
|
||||
}
|
||||
return do_handshake();
|
||||
}
|
||||
|
||||
static int bio_read(void *ctx, unsigned char *buf, size_t len)
|
||||
{
|
||||
auto bio = static_cast<BIO*>(ctx);
|
||||
int read = bio->read(buf, len);
|
||||
if (read <= 0 && bio->should_read()) {
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
static int bio_write(void *ctx, const unsigned char *buf, size_t len)
|
||||
{
|
||||
auto bio = static_cast<BIO*>(ctx);
|
||||
int written = bio->write(buf, len);
|
||||
if (written <= 0 && bio->should_write()) {
|
||||
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
int do_handshake()
|
||||
{
|
||||
int ret = 0;
|
||||
mbedtls_ssl_set_bio(&impl_.ssl_, bio_.first.get(), bio_write, bio_read, nullptr);
|
||||
|
||||
while (impl_.ssl_.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) {
|
||||
ret = mbedtls_ssl_handshake_step(&impl_.ssl_);
|
||||
|
||||
if (ret != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
impl::print_error("mbedtls_ssl_handshake_step", ret);
|
||||
}
|
||||
if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
state_ = READING;
|
||||
} else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
state_ = WRITING;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Converts OpenSSL verification mode to mbedtls enum
|
||||
int impl_verify_mode(bool is_client_not_server) const
|
||||
{
|
||||
int mode = MBEDTLS_SSL_VERIFY_UNSET;
|
||||
if (is_client_not_server) {
|
||||
if (verify_mode_ & SSL_VERIFY_PEER)
|
||||
mode = MBEDTLS_SSL_VERIFY_REQUIRED;
|
||||
else if (verify_mode_ == SSL_VERIFY_NONE)
|
||||
mode = MBEDTLS_SSL_VERIFY_NONE;
|
||||
} else {
|
||||
if (verify_mode_ & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
|
||||
mode = MBEDTLS_SSL_VERIFY_REQUIRED;
|
||||
else if (verify_mode_ & SSL_VERIFY_PEER)
|
||||
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
|
||||
else if (verify_mode_ == SSL_VERIFY_NONE)
|
||||
mode = MBEDTLS_SSL_VERIFY_NONE;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
struct impl {
|
||||
static void print_error(const char* function, int error_code)
|
||||
{
|
||||
constexpr const char *TAG="mbedtls-engine-impl";
|
||||
ESP_LOGE(TAG, "%s() returned -0x%04X", function, -error_code);
|
||||
ESP_LOGI(TAG, "-0x%04X: %s", -error_code, error_message(error_code));
|
||||
}
|
||||
|
||||
bool before_handshake() const
|
||||
{
|
||||
return ssl_.MBEDTLS_PRIVATE(state) == 0;
|
||||
}
|
||||
|
||||
int write(const void *buffer, int len)
|
||||
{
|
||||
int ret = mbedtls_ssl_write(&ssl_, static_cast<const unsigned char *>(buffer), len);
|
||||
if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
print_error("mbedtls_ssl_write", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int read(void *buffer, int len)
|
||||
{
|
||||
int ret = mbedtls_ssl_read(&ssl_, static_cast<unsigned char *>(buffer), len);
|
||||
if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
print_error("mbedtls_ssl_read", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
impl()
|
||||
{
|
||||
const unsigned char pers[] = "asio ssl";
|
||||
mbedtls_ssl_init(&ssl_);
|
||||
mbedtls_ssl_config_init(&conf_);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg_);
|
||||
#ifdef CONFIG_MBEDTLS_DEBUG
|
||||
mbedtls_esp_enable_debug_log(&conf_, CONFIG_MBEDTLS_DEBUG_LEVEL);
|
||||
#endif
|
||||
mbedtls_entropy_init(&entropy_);
|
||||
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, pers, sizeof(pers));
|
||||
mbedtls_x509_crt_init(&public_cert_);
|
||||
mbedtls_pk_init(&pk_key_);
|
||||
mbedtls_x509_crt_init(&ca_cert_);
|
||||
}
|
||||
|
||||
bool configure(context *ctx, bool is_client_not_server, int mbedtls_verify_mode)
|
||||
{
|
||||
mbedtls_x509_crt_init(&public_cert_);
|
||||
mbedtls_pk_init(&pk_key_);
|
||||
mbedtls_x509_crt_init(&ca_cert_);
|
||||
int ret = mbedtls_ssl_config_defaults(&conf_, is_client_not_server ? MBEDTLS_SSL_IS_CLIENT: MBEDTLS_SSL_IS_SERVER,
|
||||
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_config_defaults", ret);
|
||||
return false;
|
||||
}
|
||||
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
|
||||
mbedtls_ssl_conf_authmode(&conf_, mbedtls_verify_mode);
|
||||
if (ctx->cert_chain_.size() > 0 && ctx->private_key_.size() > 0) {
|
||||
ret = mbedtls_x509_crt_parse(&public_cert_, ctx->data(container::CERT), ctx->size(container::CERT));
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_x509_crt_parse", ret);
|
||||
return false;
|
||||
}
|
||||
ret = mbedtls_pk_parse_key(&pk_key_, ctx->data(container::PRIVKEY), ctx->size(container::PRIVKEY),
|
||||
nullptr, 0, mbedtls_ctr_drbg_random, &ctr_drbg_);
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_pk_parse_keyfile", ret);
|
||||
return false;
|
||||
}
|
||||
ret = mbedtls_ssl_conf_own_cert(&conf_, &public_cert_, &pk_key_);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_conf_own_cert", ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->ca_cert_.size() > 0) {
|
||||
ret = mbedtls_x509_crt_parse(&ca_cert_, ctx->data(container::CA_CERT), ctx->size(container::CA_CERT));
|
||||
if (ret < 0) {
|
||||
print_error("mbedtls_x509_crt_parse", ret);
|
||||
return false;
|
||||
}
|
||||
mbedtls_ssl_conf_ca_chain(&conf_, &ca_cert_, nullptr);
|
||||
} else {
|
||||
mbedtls_ssl_conf_ca_chain(&conf_, nullptr, nullptr);
|
||||
}
|
||||
ret = mbedtls_ssl_setup(&ssl_, &conf_);
|
||||
if (ret) {
|
||||
print_error("mbedtls_ssl_setup", ret);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
mbedtls_ssl_context ssl_{};
|
||||
mbedtls_entropy_context entropy_{};
|
||||
mbedtls_ctr_drbg_context ctr_drbg_{};
|
||||
mbedtls_ssl_config conf_{};
|
||||
mbedtls_x509_crt public_cert_{};
|
||||
mbedtls_pk_context pk_key_{};
|
||||
mbedtls_x509_crt ca_cert_{};
|
||||
};
|
||||
|
||||
impl impl_{};
|
||||
std::shared_ptr<context> ctx_;
|
||||
std::pair<std::shared_ptr<bio>, std::shared_ptr<bio>> bio_;
|
||||
enum rw_state state_;
|
||||
asio::ssl::verify_mode verify_mode_;
|
||||
};
|
||||
|
||||
} } } // namespace asio::ssl::mbedtls
|
55
components/asio/port/mbedtls/include/mbedtls_error.hpp
Normal file
55
components/asio/port/mbedtls/include/mbedtls_error.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "asio/ssl/error.hpp"
|
||||
#include "asio/ssl/detail/openssl_init.hpp"
|
||||
#include "mbedtls_context.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace error {
|
||||
namespace detail {
|
||||
|
||||
class mbedtls_category : public asio::error_category
|
||||
{
|
||||
public:
|
||||
const char* name() const ASIO_ERROR_CATEGORY_NOEXCEPT
|
||||
{
|
||||
return "asio.ssl";
|
||||
}
|
||||
|
||||
std::string message(int value) const
|
||||
{
|
||||
const char* s = asio::ssl::mbedtls::error_message(value);
|
||||
return s ? s : "asio.mbedtls error";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
const asio::error_category& get_mbedtls_category()
|
||||
{
|
||||
static detail::mbedtls_category instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
const asio::error_category& get_ssl_category()
|
||||
{
|
||||
return asio::error::get_mbedtls_category();
|
||||
}
|
||||
|
||||
} // namespace error
|
||||
|
||||
namespace ssl {
|
||||
namespace error {
|
||||
|
||||
const asio::error_category& get_stream_category()
|
||||
{
|
||||
return asio::error::get_mbedtls_category();
|
||||
}
|
||||
|
||||
} } } // namespace asio::ssl::error
|
6
components/asio/port/mbedtls/include/openssl/conf.h
Normal file
6
components/asio/port/mbedtls/include/openssl/conf.h
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
6
components/asio/port/mbedtls/include/openssl/dh.h
Normal file
6
components/asio/port/mbedtls/include/openssl/dh.h
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
6
components/asio/port/mbedtls/include/openssl/err.h
Normal file
6
components/asio/port/mbedtls/include/openssl/err.h
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
6
components/asio/port/mbedtls/include/openssl/rsa.h
Normal file
6
components/asio/port/mbedtls/include/openssl/rsa.h
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
8
components/asio/port/mbedtls/include/openssl/ssl.h
Normal file
8
components/asio/port/mbedtls/include/openssl/ssl.h
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "openssl_stub.hpp"
|
6
components/asio/port/mbedtls/include/openssl/x509v3.h
Normal file
6
components/asio/port/mbedtls/include/openssl/x509v3.h
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
#pragma once
|
115
components/asio/port/mbedtls/src/mbedtls_context.cpp
Normal file
115
components/asio/port/mbedtls/src/mbedtls_context.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2005 Voipster / Indrek dot Juhani at voipster dot com
|
||||
// SPDX-FileCopyrightText: 2005-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
// SPDX-FileContributor: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "openssl_stub.hpp"
|
||||
#include <cstring>
|
||||
#include "asio/detail/throw_error.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/ssl/context.hpp"
|
||||
#include "asio/ssl/error.hpp"
|
||||
#include "mbedtls_context.hpp"
|
||||
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
|
||||
|
||||
context::context(context::method m)
|
||||
: handle_(0)
|
||||
{
|
||||
handle_ = mbedtls::shared_ctx::create("mbedtls-context", m);
|
||||
set_options(no_compression);
|
||||
}
|
||||
|
||||
context::context(context&& other)
|
||||
{
|
||||
handle_ = other.handle_;
|
||||
other.handle_ = 0;
|
||||
}
|
||||
|
||||
context& context::operator=(context&& other)
|
||||
{
|
||||
context tmp(ASIO_MOVE_CAST(context)(*this));
|
||||
handle_ = other.handle_;
|
||||
other.handle_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
delete handle_;
|
||||
}
|
||||
|
||||
context::native_handle_type context::native_handle()
|
||||
{
|
||||
return handle_;
|
||||
}
|
||||
|
||||
void context::set_options(context::options o)
|
||||
{
|
||||
asio::error_code ec;
|
||||
set_options(o, ec);
|
||||
asio::detail::throw_error(ec, "set_options");
|
||||
}
|
||||
|
||||
ASIO_SYNC_OP_VOID context::set_options(
|
||||
context::options o, asio::error_code& ec)
|
||||
{
|
||||
handle_->get()->options_ = o;
|
||||
ec = asio::error_code();
|
||||
ASIO_SYNC_OP_VOID_RETURN(ec);
|
||||
}
|
||||
|
||||
void context::add_certificate_authority(const const_buffer& ca)
|
||||
{
|
||||
asio::error_code ec;
|
||||
add_certificate_authority(ca, ec);
|
||||
asio::detail::throw_error(ec, "add_certificate_authority");
|
||||
}
|
||||
|
||||
ASIO_SYNC_OP_VOID context::add_certificate_authority(
|
||||
const const_buffer& ca, asio::error_code& ec)
|
||||
{
|
||||
handle_->get()->ca_cert_ = ca;
|
||||
ASIO_SYNC_OP_VOID_RETURN(asio::error_code());
|
||||
}
|
||||
|
||||
void context::use_certificate_chain(const const_buffer& chain)
|
||||
{
|
||||
asio::error_code ec;
|
||||
use_certificate_chain(chain, ec);
|
||||
asio::detail::throw_error(ec, "use_certificate_chain");
|
||||
}
|
||||
|
||||
ASIO_SYNC_OP_VOID context::use_certificate_chain(
|
||||
const const_buffer& chain, asio::error_code& ec)
|
||||
{
|
||||
handle_->get()->cert_chain_ = chain;
|
||||
ASIO_SYNC_OP_VOID_RETURN(asio::error_code());
|
||||
}
|
||||
|
||||
void context::use_private_key(
|
||||
const const_buffer& private_key, context::file_format format)
|
||||
{
|
||||
asio::error_code ec;
|
||||
use_private_key(private_key, format, ec);
|
||||
asio::detail::throw_error(ec, "use_private_key");
|
||||
}
|
||||
|
||||
ASIO_SYNC_OP_VOID context::use_private_key(
|
||||
const const_buffer& private_key, context::file_format format,
|
||||
asio::error_code& ec)
|
||||
{
|
||||
handle_->get()->private_key_ = private_key;
|
||||
ASIO_SYNC_OP_VOID_RETURN(asio::error_code());
|
||||
}
|
||||
|
||||
} // namespace ssl
|
||||
} // namespace asio
|
208
components/asio/port/mbedtls/src/mbedtls_engine.cpp
Normal file
208
components/asio/port/mbedtls/src/mbedtls_engine.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
// SPDX-FileContributor: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "openssl_stub.hpp"
|
||||
#include "asio/detail/throw_error.hpp"
|
||||
#include "asio/error.hpp"
|
||||
#include "asio/ssl/detail/engine.hpp"
|
||||
#include "asio/ssl/error.hpp"
|
||||
#include "asio/ssl/verify_context.hpp"
|
||||
#include "mbedtls_context.hpp"
|
||||
#include "mbedtls_bio.hpp"
|
||||
#include "mbedtls_error.hpp"
|
||||
#include "mbedtls_engine.hpp"
|
||||
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace detail {
|
||||
|
||||
|
||||
engine::engine(SSL_CTX* context)
|
||||
: ssl_(nullptr)
|
||||
{
|
||||
ssl_ = mbedtls::create<mbedtls::engine>("mbedtls-engine", context->get());
|
||||
}
|
||||
|
||||
engine::~engine()
|
||||
{
|
||||
delete ssl_;
|
||||
}
|
||||
|
||||
SSL* engine::native_handle()
|
||||
{
|
||||
return ssl_;
|
||||
}
|
||||
|
||||
asio::error_code engine::set_verify_mode(
|
||||
verify_mode v, asio::error_code& ec)
|
||||
{
|
||||
ssl_->set_verify_mode(v);
|
||||
return {};
|
||||
}
|
||||
|
||||
engine::want engine::handshake(
|
||||
stream_base::handshake_type type, asio::error_code& ec)
|
||||
{
|
||||
return perform((type == asio::ssl::stream_base::client)
|
||||
? &engine::do_connect : &engine::do_accept, 0, 0, ec, 0);
|
||||
}
|
||||
|
||||
engine::want engine::shutdown(asio::error_code& ec)
|
||||
{
|
||||
return perform(&engine::do_shutdown, 0, 0, ec, 0);
|
||||
}
|
||||
|
||||
engine::want engine::write(const asio::const_buffer& data,
|
||||
asio::error_code& ec, std::size_t& bytes_transferred)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
{
|
||||
ec = asio::error_code();
|
||||
return engine::want_nothing;
|
||||
}
|
||||
|
||||
return perform(&engine::do_write,
|
||||
const_cast<void*>(data.data()),
|
||||
data.size(), ec, &bytes_transferred);
|
||||
}
|
||||
|
||||
engine::want engine::read(const asio::mutable_buffer& data,
|
||||
asio::error_code& ec, std::size_t& bytes_transferred)
|
||||
{
|
||||
if (data.size() == 0)
|
||||
{
|
||||
ec = asio::error_code();
|
||||
return engine::want_nothing;
|
||||
}
|
||||
|
||||
return perform(&engine::do_read, data.data(),
|
||||
data.size(), ec, &bytes_transferred);
|
||||
}
|
||||
|
||||
asio::mutable_buffer engine::get_output(
|
||||
const asio::mutable_buffer& data)
|
||||
{
|
||||
int length = ssl_->ext_bio()->read(data.data(), static_cast<int>(data.size()));
|
||||
|
||||
return asio::buffer(data,
|
||||
length > 0 ? static_cast<std::size_t>(length) : 0);
|
||||
}
|
||||
|
||||
asio::const_buffer engine::put_input(
|
||||
const asio::const_buffer& data)
|
||||
{
|
||||
int length = ssl_->ext_bio()->write(data.data(), static_cast<int>(data.size()));
|
||||
|
||||
return asio::buffer(data +
|
||||
(length > 0 ? static_cast<std::size_t>(length) : 0));
|
||||
}
|
||||
|
||||
const asio::error_code& engine::map_error_code(
|
||||
asio::error_code& ec) const
|
||||
{
|
||||
// We only want to map the error::eof code.
|
||||
if (ec != asio::error::eof)
|
||||
return ec;
|
||||
|
||||
// If there's data yet to be read, it's an error.
|
||||
if (ssl_->ext_bio()->wpending())
|
||||
{
|
||||
ec = asio::ssl::error::stream_truncated;
|
||||
return ec;
|
||||
}
|
||||
|
||||
// Otherwise, the peer should have negotiated a proper shutdown.
|
||||
if (ssl_->shutdown() != 0)
|
||||
{
|
||||
ec = asio::ssl::error::stream_truncated;
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
// This is a simplified implementation of a generic ssl io operation
|
||||
// original implementation using openssl's SSL object is in asio/include/asio/ssl/detail/impl/engine.ipp
|
||||
engine::want engine::perform(int (engine::* op)(void*, std::size_t),
|
||||
void* data, std::size_t length, asio::error_code& ec,
|
||||
std::size_t* bytes_transferred)
|
||||
{
|
||||
std::size_t pending_output_before = ssl_->ext_bio()->ctrl_pending();
|
||||
int result = (this->*op)(data, length);
|
||||
|
||||
std::size_t pending_output_after = ssl_->ext_bio()->ctrl_pending();
|
||||
|
||||
if (mbedtls::error_codes::is_error(result))
|
||||
{
|
||||
ec = asio::error_code(result, asio::error::get_mbedtls_category());
|
||||
return pending_output_after > pending_output_before ? want_output : want_nothing;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
return pending_output_after > pending_output_before
|
||||
? want_output : want_nothing;
|
||||
}
|
||||
|
||||
if (result > 0 && bytes_transferred)
|
||||
*bytes_transferred = static_cast<std::size_t>(result);
|
||||
|
||||
if (mbedtls::error_codes::want_write(result))
|
||||
{
|
||||
ec = asio::error_code();
|
||||
return want_output_and_retry;
|
||||
}
|
||||
else if (pending_output_after > pending_output_before)
|
||||
{
|
||||
ec = asio::error_code();
|
||||
return result > 0 ? want_output : want_output_and_retry;
|
||||
}
|
||||
else if (mbedtls::error_codes::want_read(result))
|
||||
{
|
||||
ec = asio::error_code();
|
||||
return want_input_and_retry;
|
||||
}
|
||||
else if (ssl_->get_state() == mbedtls::CLOSED)
|
||||
{
|
||||
ec = asio::error::eof;
|
||||
return want_nothing;
|
||||
}
|
||||
|
||||
ec = asio::error_code();
|
||||
return want_nothing;
|
||||
}
|
||||
|
||||
int engine::do_accept(void*, std::size_t)
|
||||
{
|
||||
return ssl_->accept();
|
||||
}
|
||||
|
||||
int engine::do_connect(void*, std::size_t)
|
||||
{
|
||||
return ssl_->connect();
|
||||
}
|
||||
|
||||
int engine::do_shutdown(void*, std::size_t)
|
||||
{
|
||||
return ssl_->shutdown();
|
||||
}
|
||||
|
||||
int engine::do_read(void* data, std::size_t length)
|
||||
{
|
||||
return ssl_->read(data, length < INT_MAX ? static_cast<int>(length) : INT_MAX);
|
||||
}
|
||||
|
||||
int engine::do_write(void* data, std::size_t length)
|
||||
{
|
||||
return ssl_->write(data, length < INT_MAX ? static_cast<int>(length) : INT_MAX);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ssl
|
||||
} // namespace asio
|
20
components/asio/port/src/asio_ssl_impl.cpp
Normal file
20
components/asio/port/src/asio_ssl_impl.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "asio/detail/config.hpp"
|
||||
#include "asio/ssl/detail/openssl_init.hpp"
|
||||
|
||||
namespace asio {
|
||||
namespace ssl {
|
||||
namespace detail {
|
||||
|
||||
// No OpenSSL in this implementation, instance is nullptr
|
||||
asio::detail::shared_ptr<openssl_init_base::do_init> openssl_init_base::instance()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} } } // namespace asio::ssl::detail
|
@ -11,7 +11,7 @@ else()
|
||||
src/esp_modem_uart.cpp
|
||||
src/esp_modem_term_uart.cpp
|
||||
src/esp_modem_netif.cpp)
|
||||
set(dependencies driver)
|
||||
set(dependencies driver esp_event esp_netif)
|
||||
endif()
|
||||
|
||||
set(srcs ${platform_srcs}
|
||||
@ -34,18 +34,15 @@ idf_component_register(SRCS "${srcs}"
|
||||
PRIV_INCLUDE_DIRS private_include
|
||||
REQUIRES ${dependencies})
|
||||
|
||||
|
||||
# If CMake doesn't know C++17 features, set it manually via comile options
|
||||
# esp-modem component requires C++17 internally, but older CMake (< 3.8, but still supported in IDF)
|
||||
# doesn't support target_compile_features() for cxx_std_17.
|
||||
get_property(cxx_known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
if ("cxx_std_17" IN_LIST cxx_known_features)
|
||||
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
|
||||
else()
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-std=gnu++17")
|
||||
endif()
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
# This is needed for ESP_LOGx() macros, as integer formats differ on ESP32(..) and x64
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES COMPILE_FLAGS -Wno-format)
|
||||
endif()
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
@ -16,6 +16,10 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "network_dce.h"
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#include "esp_mac.h"
|
||||
#include "dhcpserver/dhcpserver.h"
|
||||
#endif
|
||||
|
||||
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
|
||||
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
|
||||
@ -120,6 +124,38 @@ void wifi_init_softap(void)
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
|
||||
}
|
||||
|
||||
void start_network(void)
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
while ((bits & CONNECT_BIT) == 0) {
|
||||
if (!modem_check_sync()) {
|
||||
ESP_LOGE(TAG, "Modem does not respond, maybe in DATA mode? ...exiting network mode");
|
||||
modem_stop_network();
|
||||
if (!modem_check_sync()) {
|
||||
ESP_LOGE(TAG, "Modem does not respond to AT ...restarting");
|
||||
modem_reset();
|
||||
ESP_LOGI(TAG, "Restarted");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!modem_check_signal()) {
|
||||
ESP_LOGI(TAG, "Poor signal ...will check after 5s");
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
continue;
|
||||
}
|
||||
if (!modem_start_network()) {
|
||||
ESP_LOGE(TAG, "Modem could not enter network mode ...will retry after 10s");
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
continue;
|
||||
}
|
||||
bits = xEventGroupWaitBits(event_group, (DISCONNECT_BIT | CONNECT_BIT), pdTRUE, pdFALSE, pdMS_TO_TICKS(30000));
|
||||
if (bits & DISCONNECT_BIT) {
|
||||
ESP_LOGE(TAG, "Modem got disconnected ...retrying");
|
||||
modem_stop_network();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS
|
||||
@ -145,16 +181,7 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL));
|
||||
|
||||
// Start the PPP network and wait for connection
|
||||
modem_start_network();
|
||||
EventBits_t bits;
|
||||
do {
|
||||
bits = xEventGroupWaitBits(event_group, (CONNECT_BIT | DISCONNECT_BIT), pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
if (bits & DISCONNECT_BIT) {
|
||||
ESP_LOGW(TAG, "Modem got disconnected from the PPP server: retrying...");
|
||||
modem_stop_network();
|
||||
modem_start_network();
|
||||
}
|
||||
} while ((bits & CONNECT_BIT) == 0);
|
||||
start_network();
|
||||
|
||||
// Initialize the AP and setup the NAT
|
||||
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
|
||||
@ -168,11 +195,10 @@ void app_main(void)
|
||||
|
||||
// Provide a recovery if disconnection of some kind registered
|
||||
while (true) {
|
||||
bits = xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
EventBits_t bits = xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
if (bits & DISCONNECT_BIT) {
|
||||
ESP_LOGW(TAG, "Modem got disconnected from the PPP server: restarting the network...");
|
||||
modem_stop_network();
|
||||
modem_start_network();
|
||||
start_network();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,12 +47,31 @@ void modem_deinit_network(void)
|
||||
}
|
||||
}
|
||||
|
||||
void modem_start_network()
|
||||
bool modem_start_network()
|
||||
{
|
||||
esp_modem_set_mode(dce, ESP_MODEM_MODE_DATA);
|
||||
return esp_modem_set_mode(dce, ESP_MODEM_MODE_DATA) == ESP_OK;
|
||||
}
|
||||
|
||||
void modem_stop_network()
|
||||
bool modem_stop_network()
|
||||
{
|
||||
esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
|
||||
return esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
|
||||
}
|
||||
|
||||
bool modem_check_sync()
|
||||
{
|
||||
return esp_modem_sync(dce) == ESP_OK;
|
||||
}
|
||||
|
||||
void modem_reset()
|
||||
{
|
||||
esp_modem_reset(dce);
|
||||
}
|
||||
|
||||
bool modem_check_signal()
|
||||
{
|
||||
int rssi, ber;
|
||||
if (esp_modem_get_signal_quality(dce, &rssi, &ber) == ESP_OK) {
|
||||
return rssi != 99 && rssi > 5;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -79,6 +79,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool sync()
|
||||
{
|
||||
return dce_commands::sync(dte.get()) == command_result::OK;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
dce_commands::reset(dte.get());
|
||||
}
|
||||
|
||||
bool check_signal()
|
||||
{
|
||||
int rssi, ber;
|
||||
if (dce_commands::get_signal_quality(dte.get(), rssi, ber) != command_result::OK)
|
||||
return false;
|
||||
return rssi != 99 && rssi > 5;
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool init_sim(const std::string &pin)
|
||||
{
|
||||
// switch to command mode (in case we were in PPP mode)
|
||||
@ -166,14 +184,14 @@ extern "C" esp_err_t modem_init_network(esp_netif_t *netif)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
extern "C" void modem_start_network()
|
||||
extern "C" bool modem_start_network()
|
||||
{
|
||||
dce->set_mode(esp_modem::modem_mode::DATA_MODE);
|
||||
return dce->set_mode(esp_modem::modem_mode::DATA_MODE);
|
||||
}
|
||||
|
||||
extern "C" void modem_stop_network()
|
||||
extern "C" bool modem_stop_network()
|
||||
{
|
||||
dce->set_mode(esp_modem::modem_mode::COMMAND_MODE);
|
||||
return dce->set_mode(esp_modem::modem_mode::COMMAND_MODE);
|
||||
}
|
||||
|
||||
extern "C" void modem_deinit_network()
|
||||
@ -182,4 +200,19 @@ extern "C" void modem_deinit_network()
|
||||
dce = nullptr;
|
||||
}
|
||||
|
||||
extern "C" bool modem_check_sync()
|
||||
{
|
||||
return dce->get_module()->sync();
|
||||
}
|
||||
|
||||
extern "C" void modem_reset()
|
||||
{
|
||||
dce->get_module()->reset();
|
||||
}
|
||||
|
||||
extern "C" bool modem_check_signal()
|
||||
{
|
||||
return dce->get_module()->check_signal();
|
||||
}
|
||||
|
||||
}
|
@ -31,13 +31,18 @@ void modem_deinit_network();
|
||||
/**
|
||||
* @brief Starts the PPP network
|
||||
*/
|
||||
void modem_start_network();
|
||||
bool modem_start_network();
|
||||
|
||||
/**
|
||||
* @brief Stops the PPP network
|
||||
*/
|
||||
void modem_stop_network();
|
||||
bool modem_stop_network();
|
||||
|
||||
bool modem_check_sync();
|
||||
|
||||
void modem_reset();
|
||||
|
||||
bool modem_check_signal();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -5,5 +5,9 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE Threads::Threads)
|
||||
|
||||
target_compile_features(${COMPONENT_LIB} PRIVATE cxx_std_17)
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS ON
|
||||
)
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE "-DCONFIG_IDF_TARGET_LINUX")
|
||||
|
@ -4,5 +4,13 @@ cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../..")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "4.4")
|
||||
if(("${IDF_TARGET}" STREQUAL "esp32s2") OR ("${IDF_TARGET}" STREQUAL "esp32s3"))
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/peripherals/usb/host/cdc/common")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(modem-console)
|
||||
|
@ -1,7 +1,5 @@
|
||||
# Modem console example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example is mainly targets experimenting with a modem device, sending custom commands and switching to PPP mode using esp-console, command line API.
|
||||
@ -16,6 +14,16 @@ that sets up the DCE based on a custom module implemented in the `my_module_dce.
|
||||
`get_module_name()` method supplying a user defined name, but keeps all other commands the same as defined in the `GenericModule`
|
||||
class.
|
||||
|
||||
### USB DTE support
|
||||
|
||||
For USB enabled targets (ESP32-S2 and ESP32-S3), it is possible to connect to the modem device via USB.
|
||||
1. In menuconfig, navigate to `Example Configuration->Type of serial connection to the modem` and choose `USB`.
|
||||
2. Connect the modem USB signals to pin 19 (DATA-) and 20 (DATA+) on your ESP chip.
|
||||
|
||||
USB example uses Quactel BG96 modem device.
|
||||
|
||||
### Supported IDF versions
|
||||
|
||||
This example is only supported from `v4.2`, due to support of the console repl mode.
|
||||
This example is only supported from `v4.2`, due to support of the console repl mode.
|
||||
|
||||
USB example is supported from `v4.4`.
|
@ -0,0 +1,19 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
# USB is supported on S2 and S3 targets and IDF version >= 4.4
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "4.4")
|
||||
if(("${target}" STREQUAL "esp32s2") OR ("${target}" STREQUAL "esp32s3"))
|
||||
|
||||
idf_component_register(SRCS "esp_modem_usb.cpp" "esp_modem_usb_api_target.cpp"
|
||||
REQUIRES cdc_acm_host esp_modem
|
||||
REQUIRED_IDF_TARGETS esp32s2 esp32s3
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
INCLUDE_DIRS "include")
|
||||
|
||||
set_target_properties(${COMPONENT_LIB} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
)
|
||||
|
||||
endif()
|
||||
endif()
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user