Compare commits
459 commits
Author | SHA1 | Date | |
---|---|---|---|
fba43d5bce | |||
848085226b | |||
e8c187ffd2 | |||
72d0386632 | |||
1d50362a1e | |||
![]() |
49c7e8238b | ||
![]() |
2c2969cdac | ||
![]() |
ad078faaaf | ||
![]() |
227c1a0df2 | ||
![]() |
a2e0dc92ce | ||
![]() |
34d07125e7 | ||
![]() |
82fc4756be | ||
![]() |
fde8c823ac | ||
![]() |
56eef571a0 | ||
![]() |
bc409bbc2e | ||
![]() |
a77e2a7fbf | ||
![]() |
24c216fac5 | ||
![]() |
d6e3346763 | ||
![]() |
0ef3eae50b | ||
![]() |
eaa935a80a | ||
![]() |
1d3cafe179 | ||
![]() |
2f5f1125f7 | ||
![]() |
1f6cbbe7a5 | ||
![]() |
412ff64be9 | ||
![]() |
c4dc102a0b | ||
![]() |
d48be760c0 | ||
![]() |
57f7f54555 | ||
![]() |
e77dafba6f | ||
![]() |
e7673b4c1d | ||
![]() |
eeb76e822a | ||
![]() |
1c08a96b47 | ||
![]() |
c99a99e9b4 | ||
![]() |
de064fd65a | ||
![]() |
a01ad0dd20 | ||
![]() |
9cce1874b0 | ||
![]() |
bce8cca042 | ||
![]() |
da0be64f68 | ||
![]() |
f1c56bc233 | ||
![]() |
f497562a62 | ||
![]() |
0b52d92d12 | ||
![]() |
d37b383edc | ||
![]() |
d05b408cde | ||
![]() |
8d213f8295 | ||
![]() |
91a47f4fab | ||
![]() |
6e1caa5c56 | ||
![]() |
65b68bdc70 | ||
![]() |
ac6c48d98f | ||
![]() |
16a907180c | ||
![]() |
9c9ef9cea2 | ||
![]() |
25e27d76d1 | ||
![]() |
7c0a2ae633 | ||
![]() |
ae688609a0 | ||
![]() |
82adc07c0a | ||
![]() |
c9fb42bb8f | ||
![]() |
7ff8f023d1 | ||
![]() |
800cd40f88 | ||
![]() |
5d06f610a9 | ||
![]() |
2e3ddeedb6 | ||
![]() |
87bb121b47 | ||
![]() |
27e86e2641 | ||
![]() |
8e9751e26e | ||
![]() |
7b227eb333 | ||
![]() |
3459d4fe43 | ||
![]() |
4c5138eade | ||
![]() |
97e36d8901 | ||
![]() |
658c752998 | ||
![]() |
3f683351b3 | ||
![]() |
1d2094996e | ||
![]() |
1bf30c8620 | ||
![]() |
6059c965dd | ||
![]() |
ae4897f58a | ||
![]() |
a9390874c7 | ||
![]() |
b9b100bdfd | ||
![]() |
8829c2f365 | ||
![]() |
f0048acbee | ||
![]() |
62af7934c8 | ||
![]() |
654adaf82a | ||
![]() |
92277ad576 | ||
![]() |
74b4775908 | ||
![]() |
b807ccf261 | ||
![]() |
4b7144f763 | ||
![]() |
7812ffabcc | ||
![]() |
8d79eaf124 | ||
![]() |
65f87efca9 | ||
![]() |
aa816e98a9 | ||
![]() |
f978ea8658 | ||
![]() |
126be3ccf3 | ||
![]() |
16e2f09710 | ||
![]() |
a2210d1bb1 | ||
![]() |
67cfdfaeb2 | ||
![]() |
bf5457af69 | ||
![]() |
d830185df9 | ||
![]() |
e9931680c8 | ||
![]() |
9a51bd0a63 | ||
![]() |
c8af76c07c | ||
![]() |
de4c1303f5 | ||
![]() |
2b180b21ce | ||
![]() |
9586acb560 | ||
![]() |
a7db6e4fd4 | ||
![]() |
3c9924bf73 | ||
![]() |
43f9a8de80 | ||
![]() |
ece243f961 | ||
![]() |
fddebfcfae | ||
![]() |
719f735309 | ||
![]() |
a017c4b0eb | ||
![]() |
af88a889d0 | ||
![]() |
419a6af38d | ||
![]() |
2a9b6f54c6 | ||
![]() |
2d3fc52b73 | ||
![]() |
3038af5b07 | ||
![]() |
caaf26c717 | ||
![]() |
7d466ba98f | ||
![]() |
8f19a3fa9a | ||
![]() |
e052edb14f | ||
![]() |
b9c7f3df92 | ||
![]() |
80d84896f1 | ||
![]() |
3a020c1eab | ||
![]() |
764d0d3b45 | ||
![]() |
020a8a2c38 | ||
![]() |
cd30656e01 | ||
![]() |
865b2439ce | ||
![]() |
b45956fcb9 | ||
![]() |
04e1661411 | ||
![]() |
3647cd8968 | ||
![]() |
bd6fc106d9 | ||
![]() |
94f3981bef | ||
![]() |
d5bb8ecbc6 | ||
![]() |
7cef6fcc7b | ||
![]() |
1820212724 | ||
![]() |
9cf641dece | ||
![]() |
feeca915b9 | ||
![]() |
eedee9c0d3 | ||
![]() |
7aa511d8c5 | ||
![]() |
03acd5a24d | ||
![]() |
b80cef6919 | ||
![]() |
a4161780df | ||
![]() |
3e4a1f3b0e | ||
![]() |
b04708178a | ||
![]() |
5ecec970b2 | ||
![]() |
2519ac9ced | ||
![]() |
5fe9335b36 | ||
![]() |
0b65112ec4 | ||
![]() |
5acbb04b51 | ||
![]() |
422cbf24bd | ||
![]() |
80e3e723e3 | ||
![]() |
95f8328ffc | ||
![]() |
e157fcfe37 | ||
![]() |
b0b10001fe | ||
![]() |
396b42ac6d | ||
![]() |
b9a3b808fb | ||
![]() |
313cbb5350 | ||
![]() |
d237ac1b95 | ||
![]() |
775f7bce09 | ||
![]() |
bfc8a5cb1b | ||
![]() |
5a8fca6f79 | ||
![]() |
da38a17a31 | ||
![]() |
ea8c44d799 | ||
![]() |
613ae7921a | ||
![]() |
3775ac9d2e | ||
![]() |
0e8b3fed12 | ||
![]() |
f63f4e56aa | ||
![]() |
e4a3e8c4d4 | ||
![]() |
876878a2af | ||
![]() |
49a694bd1e | ||
![]() |
b8fb8c5b15 | ||
![]() |
081c9f7678 | ||
![]() |
db8cbb3b61 | ||
![]() |
6232f5a2b7 | ||
![]() |
9db35aa1bf | ||
![]() |
a91ed1fa4c | ||
![]() |
3ed755e52d | ||
![]() |
2b932c500f | ||
![]() |
302f8fad82 | ||
![]() |
0e39e7f5c6 | ||
![]() |
1b5d772c4a | ||
![]() |
2c93b25c73 | ||
![]() |
6055411928 | ||
![]() |
7ad8332838 | ||
![]() |
61376bd734 | ||
![]() |
aae2c24678 | ||
![]() |
5fc2598eac | ||
![]() |
ad5dbda120 | ||
![]() |
862ab64d62 | ||
![]() |
dc15a77dfa | ||
![]() |
cb70b6e633 | ||
![]() |
7f830195d6 | ||
![]() |
e333d1a751 | ||
![]() |
f1ad1c6b1a | ||
![]() |
b490079b47 | ||
![]() |
46f10cade7 | ||
![]() |
1aed8c8127 | ||
![]() |
b49630e515 | ||
![]() |
388c0677ee | ||
![]() |
4d36bc1f46 | ||
![]() |
0d5ca2d87b | ||
![]() |
cce9711653 | ||
![]() |
d39ecd814a | ||
![]() |
2203a5f019 | ||
![]() |
e5150b7cd9 | ||
![]() |
0d8b11d250 | ||
![]() |
3de2e9aa8a | ||
![]() |
658b86ed9f | ||
![]() |
7bc6383759 | ||
![]() |
e02a999863 | ||
![]() |
a0581a926b | ||
![]() |
27115c3258 | ||
![]() |
341f280662 | ||
![]() |
714ba5c9df | ||
![]() |
faef0b047f | ||
![]() |
ff2f5f601d | ||
![]() |
73b0147b40 | ||
![]() |
640620dbdc | ||
![]() |
67db15a84a | ||
![]() |
a663aefebd | ||
![]() |
99154ae223 | ||
![]() |
afafc135cd | ||
![]() |
4e9d13bf50 | ||
![]() |
9ac452a87d | ||
![]() |
5366a766f4 | ||
![]() |
34255ff54e | ||
![]() |
5c00887054 | ||
![]() |
d6de3de732 | ||
![]() |
113d623957 | ||
![]() |
0c55e5ad4c | ||
![]() |
6072829df6 | ||
![]() |
33d373c300 | ||
![]() |
9556ad77fb | ||
![]() |
948408b36a | ||
![]() |
8e9871d995 | ||
![]() |
93ac0b671c | ||
![]() |
65a67d72f1 | ||
![]() |
44d2664ece | ||
![]() |
1d9d04ff9d | ||
![]() |
1b43ed1cba | ||
![]() |
a8a484eb53 | ||
![]() |
b3b6bcdf64 | ||
![]() |
bd3d8d9de0 | ||
![]() |
93f904f601 | ||
![]() |
8c07636dfd | ||
![]() |
87aff27ab1 | ||
![]() |
a3bbb0a374 | ||
![]() |
673e3aa549 | ||
![]() |
93bc409ca1 | ||
![]() |
0e24969f53 | ||
![]() |
5a1f0b7c4f | ||
![]() |
715847cc8c | ||
![]() |
b13bff84fa | ||
![]() |
5494128faf | ||
![]() |
3846986851 | ||
![]() |
efbd6c1b50 | ||
![]() |
3eae5b1363 | ||
![]() |
0c40f0b7bd | ||
![]() |
5451457738 | ||
![]() |
6b3b32e98a | ||
![]() |
08d36dbe43 | ||
![]() |
a903d73ab7 | ||
![]() |
2e4ac9defe | ||
![]() |
48be844e5b | ||
![]() |
265db34286 | ||
![]() |
cfb076c8a0 | ||
![]() |
15820da8ba | ||
![]() |
f95f689146 | ||
![]() |
5a6764f218 | ||
![]() |
4cee34c0d0 | ||
![]() |
b94091130b | ||
![]() |
f3c6fac3cb | ||
![]() |
e8e448df3e | ||
![]() |
350af2394c | ||
![]() |
e7c7794cd4 | ||
![]() |
1e7f5b5b2b | ||
![]() |
d58d749b30 | ||
![]() |
d7b6c04aaf | ||
![]() |
8ff7dce46d | ||
![]() |
248b089ad4 | ||
![]() |
83af0716c5 | ||
![]() |
1da0197798 | ||
![]() |
9039b7c9c6 | ||
![]() |
db04a40b5c | ||
![]() |
7d59122b5f | ||
![]() |
8487279e64 | ||
![]() |
2643775f18 | ||
![]() |
e83860d1bc | ||
![]() |
91a821c793 | ||
![]() |
f6de7465ec | ||
![]() |
e1cdb1f328 | ||
![]() |
3031666dc7 | ||
![]() |
6c484b2b31 | ||
![]() |
2a30cd8ba7 | ||
![]() |
a4148a13ba | ||
![]() |
df46d8827c | ||
![]() |
93846135f2 | ||
![]() |
5b74d7108b | ||
![]() |
516001db8a | ||
![]() |
946bfa2f18 | ||
![]() |
3db1ce0953 | ||
![]() |
3160605462 | ||
![]() |
dcf5a83ee4 | ||
![]() |
37e8ee2c00 | ||
![]() |
c5f6d180dd | ||
![]() |
b06665b851 | ||
![]() |
ad07d95026 | ||
![]() |
79d2038fdf | ||
![]() |
3ec7c1ab21 | ||
![]() |
813b019bd1 | ||
![]() |
a5514d2b2b | ||
![]() |
0c254aba87 | ||
![]() |
713aab17f8 | ||
![]() |
33b40e0ae4 | ||
![]() |
0724f8982a | ||
![]() |
53bbc6be33 | ||
![]() |
66916394ed | ||
![]() |
2b30c82b9e | ||
![]() |
a76e9aad11 | ||
![]() |
09f59ba3b8 | ||
![]() |
1f37ec0be9 | ||
![]() |
a798db0e2b | ||
![]() |
fb4397f841 | ||
![]() |
2befafca77 | ||
![]() |
0ed131e6a7 | ||
![]() |
0060af62c5 | ||
![]() |
121d5d5940 | ||
![]() |
a552de400a | ||
![]() |
2f7bd10487 | ||
![]() |
939436b793 | ||
![]() |
00ffebfd24 | ||
![]() |
41d1326da0 | ||
![]() |
be525cb624 | ||
![]() |
ae8915861a | ||
![]() |
1d5bd23268 | ||
![]() |
74fddbc09e | ||
![]() |
97cd393649 | ||
![]() |
c2488dd4a3 | ||
![]() |
fa73d31558 | ||
![]() |
4ff8e866d5 | ||
![]() |
b1934d6b64 | ||
![]() |
dbb6c34cf2 | ||
![]() |
278e520479 | ||
![]() |
c32a22c20b | ||
![]() |
dd3e5619cb | ||
![]() |
aebf187b98 | ||
![]() |
2639b276f4 | ||
![]() |
46ee0713de | ||
![]() |
6bc92f7592 | ||
![]() |
7e4c67e29d | ||
![]() |
78ad6f9705 | ||
![]() |
c7b536764f | ||
![]() |
8c35e14965 | ||
![]() |
b119f2c471 | ||
![]() |
91ed21d487 | ||
![]() |
7e1c8876e6 | ||
![]() |
fac510fbc2 | ||
![]() |
95f1cd6ea8 | ||
![]() |
a1ce8feba2 | ||
![]() |
dba00cff6f | ||
![]() |
462e449cd6 | ||
![]() |
d30597f728 | ||
![]() |
7594bc585c | ||
![]() |
491fd183f8 | ||
![]() |
413631632b | ||
![]() |
5a90bb36d1 | ||
![]() |
f9421f084b | ||
![]() |
505a3fc1fc | ||
![]() |
d286e990f0 | ||
![]() |
52d82ad83a | ||
![]() |
5a0b5299d2 | ||
![]() |
4afeefbb3c | ||
![]() |
1c70181ca2 | ||
![]() |
89cb55ea58 | ||
![]() |
bb04883097 | ||
![]() |
ce68d38794 | ||
![]() |
4a2dd2bdaa | ||
![]() |
af8f2d3616 | ||
![]() |
28d820ba0a | ||
![]() |
c86488be9c | ||
![]() |
11afda24ed | ||
![]() |
07d4cf7628 | ||
![]() |
10828b1ea0 | ||
![]() |
8528de580a | ||
![]() |
001b3010a6 | ||
![]() |
79326e4f07 | ||
![]() |
fcf5b2c25a | ||
![]() |
dfacbc2071 | ||
![]() |
1799b02a43 | ||
![]() |
c83afc66e6 | ||
![]() |
904a9c0920 | ||
![]() |
028551271e | ||
![]() |
e67ae24d41 | ||
![]() |
6f3f2df7ca | ||
![]() |
1673554c6b | ||
![]() |
f66b84778c | ||
![]() |
46b04f4ba4 | ||
![]() |
df2dc99c14 | ||
![]() |
d3fb641cdb | ||
![]() |
9c2a7dd58b | ||
![]() |
4f99ccd6a8 | ||
![]() |
4e55c1c452 | ||
![]() |
5fd9ad2dc3 | ||
![]() |
7cff6b0cf9 | ||
![]() |
8b5eaa0534 | ||
![]() |
a9843c3c26 | ||
![]() |
796a5edb6c | ||
![]() |
fc995df480 | ||
![]() |
7597ac3766 | ||
![]() |
d383b94dbf | ||
![]() |
8bea6c144e | ||
![]() |
495342c2ae | ||
![]() |
bc451afb30 | ||
![]() |
49f8029677 | ||
![]() |
c52a6267ab | ||
![]() |
91166ecb2a | ||
![]() |
93f05d6eff | ||
![]() |
4f267c515e | ||
![]() |
66a402cc96 | ||
![]() |
4a0db7fbf1 | ||
![]() |
12adb39542 | ||
![]() |
f85a1d42d8 | ||
![]() |
d1859ad4fd | ||
![]() |
217dfe7c6b | ||
![]() |
69e4433f4e | ||
![]() |
6750365393 | ||
![]() |
59687e04e0 | ||
![]() |
98df6722ea | ||
![]() |
4efc35db58 | ||
![]() |
9b484ff507 | ||
![]() |
7379e5ad6b | ||
![]() |
dee2d68b9c | ||
![]() |
ca3013ca38 | ||
![]() |
b4673d871b | ||
![]() |
a3cc4a368e | ||
![]() |
0186490fad | ||
![]() |
37b994cc69 | ||
![]() |
b8d3f51ebd | ||
![]() |
14cc31fe7d | ||
![]() |
43b3141f93 | ||
![]() |
d78c93254b | ||
![]() |
04ed8d2673 | ||
![]() |
138c8523f9 | ||
![]() |
3ea4fcf8a5 | ||
![]() |
8cb341314a | ||
![]() |
d38f630519 | ||
![]() |
f06967fa84 | ||
![]() |
cad115bf6b | ||
![]() |
f5058fa9b7 | ||
![]() |
8d63fbb6aa | ||
![]() |
4046239bc8 | ||
![]() |
f8fad0d350 | ||
![]() |
32eaf83f24 | ||
![]() |
df5c143b89 | ||
![]() |
d6e8459c07 | ||
![]() |
f994a4f728 | ||
![]() |
6d25763d10 | ||
![]() |
ec5e0e3e51 | ||
![]() |
c0c217bd5a | ||
![]() |
41909eac97 | ||
![]() |
77a71e94d1 | ||
![]() |
ea5638cb2c | ||
![]() |
3aaf38d3d8 | ||
![]() |
ae817cc993 | ||
68b2b3f269 |
556 changed files with 81401 additions and 32600 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -1,4 +1,12 @@
|
|||
#Ignore build files
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
build
|
||||
cmake_install.cmake
|
||||
lws-minimal*
|
||||
Makefile
|
||||
.cproject
|
||||
.project
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
|
@ -38,3 +46,7 @@ ar-lib
|
|||
libwebsockets.pc
|
||||
build/
|
||||
*.swp
|
||||
doc
|
||||
/build2/
|
||||
/build3/
|
||||
/cov-int/
|
||||
|
|
23
.travis.yml
23
.travis.yml
|
@ -4,26 +4,35 @@ env:
|
|||
global:
|
||||
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
|
||||
matrix:
|
||||
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON"
|
||||
- LWS_METHOD=default
|
||||
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON"
|
||||
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON"
|
||||
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON"
|
||||
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
|
||||
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
|
||||
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
|
||||
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=libev CMAKE_ARGS="-DLWS_WITH_LIBEV=ON"
|
||||
- LWS_METHOD=noipv6 CMAKE_ARGS="-DLWS_IPV6=OFF"
|
||||
- LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF"
|
||||
- LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON"
|
||||
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
|
||||
- LWS_METHOD=nologs CMAKE_ARGS="-DLWS_WITH_NO_LOGS=ON"
|
||||
- LWS_METHOD=smp CMAKE_ARGS="-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- LWS_METHOD=nows CMAKE_ARGS="-DLWS_ROLE_WS=0"
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
language: generic
|
||||
install:
|
||||
- ./travis_install.sh
|
||||
- ./scripts/travis_install.sh
|
||||
# - ./travis-tool.sh github_package jimhester/covr
|
||||
|
||||
#after_success:
|
||||
# - Rscript -e 'covr::coveralls()'
|
||||
|
||||
script:
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "osx" ]; then mkdir build && cd build && cmake -DOPENSSL_ROOT_DIR="/usr/local/opt/openssl" $CMAKE_ARGS .. && cmake --build .; else if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "linux" ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi ; fi
|
||||
- ./scripts/travis_control.sh
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
|
|
1059
CMakeLists.txt
1059
CMakeLists.txt
File diff suppressed because it is too large
Load diff
14
LICENSE
14
LICENSE
|
@ -33,19 +33,21 @@ to get original sources with the liberal terms.
|
|||
|
||||
Original liberal license retained
|
||||
|
||||
- lib/sha-1.c - 3-clause BSD license retained, link to original
|
||||
- lib/misc/sha-1.c - 3-clause BSD license retained, link to original
|
||||
- win32port/zlib - ZLIB license (see zlib.h)
|
||||
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
|
||||
|
||||
Relicensed to libwebsocket license
|
||||
|
||||
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
|
||||
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
|
||||
link to original Public Domain version
|
||||
- lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
|
||||
- lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
|
||||
link to original Public Domain version
|
||||
|
||||
Public Domain (CC-zero) to simplify reuse
|
||||
|
||||
- test-server/*.c
|
||||
- test-server/*.h
|
||||
- test-apps/*.c
|
||||
- test-apps/*.h
|
||||
- minimal-examples/*
|
||||
- lwsws/*
|
||||
|
||||
------ end of exceptions
|
||||
|
|
1
Makefile.projbuild
Normal file
1
Makefile.projbuild
Normal file
|
@ -0,0 +1 @@
|
|||
CPPFLAGS += -I$(BUILD_DIR_BASE)/libwebsockets/include
|
469
README.build.md
469
README.build.md
|
@ -1,469 +0,0 @@
|
|||
Notes about building lws
|
||||
========================
|
||||
|
||||
|
||||
@section cm Introduction to CMake
|
||||
|
||||
CMake is a multi-platform build tool that can generate build files for many
|
||||
different target platforms. See more info at http://www.cmake.org
|
||||
|
||||
CMake also allows/recommends you to do "out of source"-builds, that is,
|
||||
the build files are separated from your sources, so there is no need to
|
||||
create elaborate clean scripts to get a clean source tree, instead you
|
||||
simply remove your build directory.
|
||||
|
||||
Libwebsockets has been tested to build successfully on the following platforms
|
||||
with SSL support (for OpenSSL/wolfSSL/BoringSSL):
|
||||
|
||||
- Windows (Visual Studio)
|
||||
- Windows (MinGW)
|
||||
- Linux (x86 and ARM)
|
||||
- OSX
|
||||
- NetBSD
|
||||
|
||||
|
||||
@section build1 Building the library and test apps
|
||||
|
||||
The project settings used by CMake to generate the platform specific build
|
||||
files is called [CMakeLists.txt](CMakeLists.txt). CMake then uses one of its "Generators" to
|
||||
output a Visual Studio project or Make file for instance. To see a list of
|
||||
the available generators for your platform, simply run the "cmake" command.
|
||||
|
||||
Note that by default OpenSSL will be linked, if you don't want SSL support
|
||||
see below on how to toggle compile options.
|
||||
|
||||
|
||||
@section bu Building on Unix:
|
||||
|
||||
1. Install CMake 2.8 or greater: http://cmake.org/cmake/resources/software.html
|
||||
(Most Unix distributions comes with a packaged version also)
|
||||
|
||||
2. Install OpenSSL.
|
||||
|
||||
3. Generate the build files (default is Make files):
|
||||
```
|
||||
$ cd /path/to/src
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
```
|
||||
|
||||
4. Finally you can build using the generated Makefile:
|
||||
```
|
||||
$ make && sudo make install
|
||||
```
|
||||
**NOTE**: The `build/`` directory can have any name and be located anywhere
|
||||
on your filesystem, and that the argument `..` given to cmake is simply
|
||||
the source directory of **libwebsockets** containing the [CMakeLists.txt](CMakeLists.txt)
|
||||
project file. All examples in this file assumes you use ".."
|
||||
|
||||
**NOTE2**:
|
||||
A common option you may want to give is to set the install path, same
|
||||
as --prefix= with autotools. It defaults to /usr/local.
|
||||
You can do this by, eg
|
||||
```
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .
|
||||
```
|
||||
|
||||
**NOTE3**:
|
||||
On machines that want libraries in lib64, you can also add the
|
||||
following to the cmake line
|
||||
```
|
||||
-DLIB_SUFFIX=64
|
||||
```
|
||||
|
||||
**NOTE4**:
|
||||
If you are building against a non-distro OpenSSL (eg, in order to get
|
||||
access to ALPN support only in newer OpenSSL versions) the nice way to
|
||||
express that in one cmake command is eg,
|
||||
```
|
||||
$ cmake .. -DOPENSSL_ROOT_DIR=/usr/local/ssl \
|
||||
-DCMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE=/usr/local/ssl \
|
||||
-DLWS_WITH_HTTP2=1
|
||||
```
|
||||
|
||||
When you run the test apps using non-distro SSL, you have to force them
|
||||
to use your libs, not the distro ones
|
||||
```
|
||||
$ LD_LIBRARY_PATH=/usr/local/ssl/lib libwebsockets-test-server --ssl
|
||||
```
|
||||
|
||||
To get it to build on latest openssl (2016-04-10) it needed this approach
|
||||
```
|
||||
cmake .. -DLWS_WITH_HTTP2=1 -DLWS_OPENSSL_INCLUDE_DIRS=/usr/local/include/openssl -DLWS_OPENSSL_LIBRARIES="/usr/local/lib64/libssl.so;/usr/local/lib64/libcrypto.so"
|
||||
```
|
||||
|
||||
Mac users have reported
|
||||
|
||||
```
|
||||
$ export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2k; cmake ..; make -j4
|
||||
```
|
||||
|
||||
worked for them when using "homebrew" OpenSSL
|
||||
|
||||
**NOTE5**:
|
||||
To build with debug info and _DEBUG for lower priority debug messages
|
||||
compiled in, use
|
||||
```
|
||||
$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
|
||||
```
|
||||
|
||||
**NOTE6**
|
||||
To build on Solaris the linker needs to be informed to use lib socket
|
||||
and libnsl, and only builds in 64bit mode.
|
||||
|
||||
```bash
|
||||
$ cmake .. -DCMAKE_C_FLAGS=-m64 -DCMAKE_EXE_LINKER_FLAGS="-lsocket -lnsl"
|
||||
```
|
||||
|
||||
4. Finally you can build using the generated Makefile:
|
||||
|
||||
```bash
|
||||
$ make
|
||||
```
|
||||
|
||||
@section lcap Linux Capabilities
|
||||
|
||||
On Linux, lws now lets you retain selected root capabilities when dropping
|
||||
privileges. If libcap-dev or similar package is installed providing
|
||||
sys/capabilities.h, and libcap or similar package is installed providing
|
||||
libcap.so, CMake will enable the capability features.
|
||||
|
||||
The context creation info struct .caps[] and .count_caps members can then
|
||||
be set by user code to enable selected root capabilities to survive the
|
||||
transition to running under an unprivileged user.
|
||||
|
||||
@section cmq Quirk of cmake
|
||||
|
||||
When changing cmake options, for some reason the only way to get it to see the
|
||||
changes sometimes is delete the contents of your build directory and do the
|
||||
cmake from scratch.
|
||||
|
||||
deleting build/CMakeCache.txt may be enough.
|
||||
|
||||
|
||||
@section cmw Building on Windows (Visual Studio)
|
||||
|
||||
1. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
|
||||
|
||||
2. Install OpenSSL binaries. http://www.openssl.org/related/binaries.html
|
||||
|
||||
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
|
||||
|
||||
**NOTE2**:
|
||||
Be sure that OPENSSL_CONF environment variable is defined and points at
|
||||
<OpenSSL install location>\bin\openssl.cfg
|
||||
|
||||
3. Generate the Visual studio project by opening the Visual Studio cmd prompt:
|
||||
|
||||
```
|
||||
cd <path to src>
|
||||
md build
|
||||
cd build
|
||||
cmake -G "Visual Studio 10" ..
|
||||
```
|
||||
|
||||
(**NOTE**: There is also a cmake-gui available on Windows if you prefer that)
|
||||
|
||||
**NOTE2**:
|
||||
See this link to find out the version number corresponding to your Visual Studio edition:
|
||||
http://superuser.com/a/194065
|
||||
|
||||
4. Now you should have a generated Visual Studio Solution in your
|
||||
`<path to src>/build` directory, which can be used to build.
|
||||
|
||||
5. Some additional deps may be needed
|
||||
|
||||
- iphlpapi.lib
|
||||
- psapi.lib
|
||||
- userenv.lib
|
||||
|
||||
6. If you're using libuv, you must make sure to compile libuv with the same multithread-dll / Mtd attributes as libwebsockets itself
|
||||
|
||||
|
||||
@section cmwmgw Building on Windows (MinGW)
|
||||
|
||||
1. Install MinGW: http://sourceforge.net/projects/mingw/files
|
||||
|
||||
(**NOTE**: Preferably in the default location C:\MinGW)
|
||||
|
||||
2. Fix up MinGW headers
|
||||
|
||||
a) If still necessary, sdd the following lines to C:\MinGW\include\winsock2.h:
|
||||
```
|
||||
#if(_WIN32_WINNT >= 0x0600)
|
||||
|
||||
typedef struct pollfd {
|
||||
|
||||
SOCKET fd;
|
||||
SHORT events;
|
||||
SHORT revents;
|
||||
|
||||
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
|
||||
|
||||
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
|
||||
|
||||
#endif // (_WIN32_WINNT >= 0x0600)
|
||||
```
|
||||
|
||||
Update crtdefs.h line 47 to say:
|
||||
|
||||
```
|
||||
typedef __int64 ssize_t;
|
||||
```
|
||||
|
||||
b) Create C:\MinGW\include\mstcpip.h and copy and paste the content from following link into it:
|
||||
|
||||
https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/include/mstcpip.h
|
||||
|
||||
3. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
|
||||
|
||||
4. Install OpenSSL binaries. http://www.openssl.org/related/binaries.html
|
||||
|
||||
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
|
||||
|
||||
**NOTE2**:
|
||||
Be sure that OPENSSL_CONF environment variable is defined and points at
|
||||
<OpenSSL install location>\bin\openssl.cfg
|
||||
|
||||
5. Generate the build files (default is Make files) using MSYS shell:
|
||||
```
|
||||
$ cd /drive/path/to/src
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW ..
|
||||
```
|
||||
(**NOTE**: The `build/`` directory can have any name and be located anywhere
|
||||
on your filesystem, and that the argument `..` given to cmake is simply
|
||||
the source directory of **libwebsockets** containing the [CMakeLists.txt](CMakeLists.txt)
|
||||
project file. All examples in this file assumes you use "..")
|
||||
|
||||
**NOTE2**:
|
||||
To generate build files allowing to create libwebsockets binaries with debug information
|
||||
set the CMAKE_BUILD_TYPE flag to DEBUG:
|
||||
```
|
||||
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW -DCMAKE_BUILD_TYPE=DEBUG ..
|
||||
```
|
||||
6. Finally you can build using the generated Makefile and get the results deployed into your MinGW installation:
|
||||
|
||||
```
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
|
||||
@section optee Building for OP-TEE
|
||||
|
||||
OP-TEE is a "Secure World" Trusted Execution Environment.
|
||||
|
||||
Although lws is only part of the necessary picture to have an https-enabled
|
||||
TA, it does support OP-TEE as a platform and if you provide the other
|
||||
pieces, does work very well.
|
||||
|
||||
Select it in cmake with `-DLWS_PLAT_OPTEE=1`
|
||||
|
||||
|
||||
@section cmco Setting compile options
|
||||
|
||||
To set compile time flags you can either use one of the CMake gui applications
|
||||
or do it via the command line.
|
||||
|
||||
@subsection cmcocl Command line
|
||||
|
||||
To list available options (omit the H if you don't want the help text):
|
||||
|
||||
cmake -LH ..
|
||||
|
||||
Then to set an option and build (for example turn off SSL support):
|
||||
|
||||
cmake -DLWS_WITH_SSL=0 ..
|
||||
or
|
||||
cmake -DLWS_WITH_SSL:BOOL=OFF ..
|
||||
|
||||
@subsection cmcoug Unix GUI
|
||||
|
||||
If you have a curses-enabled build you simply type:
|
||||
(not all packages include this, my debian install does not for example).
|
||||
|
||||
ccmake
|
||||
|
||||
@subsection cmcowg Windows GUI
|
||||
|
||||
On windows CMake comes with a gui application:
|
||||
Start -> Programs -> CMake -> CMake (cmake-gui)
|
||||
|
||||
|
||||
@section wolf wolfSSL/CyaSSL replacement for OpenSSL
|
||||
|
||||
wolfSSL/CyaSSL is a lightweight SSL library targeted at embedded systems:
|
||||
https://www.wolfssl.com/wolfSSL/Products-wolfssl.html
|
||||
|
||||
It contains a OpenSSL compatibility layer which makes it possible to pretty
|
||||
much link to it instead of OpenSSL, giving a much smaller footprint.
|
||||
|
||||
**NOTE**: wolfssl needs to be compiled using the `--enable-opensslextra` flag for
|
||||
this to work.
|
||||
|
||||
@section wolf1 Compiling libwebsockets with wolfSSL
|
||||
|
||||
```
|
||||
cmake .. -DLWS_USE_WOLFSSL=1 \
|
||||
-DLWS_WOLFSSL_INCLUDE_DIRS=/path/to/wolfssl \
|
||||
-DLWS_WOLFSSL_LIBRARIES=/path/to/wolfssl/wolfssl.a ..
|
||||
```
|
||||
|
||||
**NOTE**: On windows use the .lib file extension for `LWS_WOLFSSL_LIBRARIES` instead.
|
||||
|
||||
@section cya Compiling libwebsockets with CyaSSL
|
||||
|
||||
```
|
||||
cmake .. -DLWS_USE_CYASSL=1 \
|
||||
-DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \
|
||||
-DLWS_CYASSL_LIBRARIES=/path/to/wolfssl/cyassl.a ..
|
||||
```
|
||||
|
||||
**NOTE**: On windows use the .lib file extension for `LWS_CYASSL_LIBRARIES` instead.
|
||||
|
||||
@section esp32 Building for ESP32
|
||||
|
||||
Step 1, get ESP-IDF with lws integrated as a component
|
||||
|
||||
```
|
||||
$ git clone --int --recursive https://github.com/lws-team/lws-esp-idf
|
||||
```
|
||||
|
||||
Step 2: Get Application including the test plugins
|
||||
|
||||
```
|
||||
$ git clone https://github.com/lws-team/lws-esp32
|
||||
```
|
||||
|
||||
Set your IDF_PATH to point to the esp-idf you downloaded in 1)
|
||||
|
||||
There's docs for how to build the lws-esp32 test app and reproduce it in the README.md here
|
||||
|
||||
https://github.com/lws-team/lws-esp32/blob/master/README.md
|
||||
|
||||
|
||||
@section extplugins Building plugins outside of lws itself
|
||||
|
||||
The directory ./plugin-standalone/ shows how easy it is to create plugins
|
||||
outside of lws itself. First build lws itself with -DLWS_WITH_PLUGINS,
|
||||
then use the same flow to build the standalone plugin
|
||||
```
|
||||
cd ./plugin-standalone
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make && sudo make install
|
||||
```
|
||||
|
||||
if you changed the default plugin directory when you built lws, you must
|
||||
also give the same arguments to cmake here (eg,
|
||||
` -DCMAKE_INSTALL_PREFIX:PATH=/usr/something/else...` )
|
||||
|
||||
Otherwise if you run lwsws or libwebsockets-test-server-v2.0, it will now
|
||||
find the additional plugin "libprotocol_example_standalone.so"
|
||||
```
|
||||
lwsts[21257]: Plugins:
|
||||
lwsts[21257]: libprotocol_dumb_increment.so
|
||||
lwsts[21257]: libprotocol_example_standalone.so
|
||||
lwsts[21257]: libprotocol_lws_mirror.so
|
||||
lwsts[21257]: libprotocol_lws_server_status.so
|
||||
lwsts[21257]: libprotocol_lws_status.so
|
||||
```
|
||||
If you have multiple vhosts, you must enable plugins at the vhost
|
||||
additionally, discovered plugins are not enabled automatically for security
|
||||
reasons. You do this using info->pvo or for lwsws, in the JSON config.
|
||||
|
||||
|
||||
@section http2rp Reproducing HTTP2.0 tests
|
||||
|
||||
You must have built and be running lws against a version of openssl that has
|
||||
ALPN / NPN. Most distros still have older versions. You'll know it's right by
|
||||
seeing
|
||||
```
|
||||
lwsts[4752]: Compiled with OpenSSL support
|
||||
lwsts[4752]: Using SSL mode
|
||||
lwsts[4752]: HTTP2 / ALPN enabled
|
||||
```
|
||||
at lws startup.
|
||||
|
||||
For non-SSL HTTP2.0 upgrade
|
||||
```
|
||||
$ nghttp -nvasu http://localhost:7681/test.htm
|
||||
```
|
||||
For SSL / ALPN HTTP2.0 upgrade
|
||||
```
|
||||
$ nghttp -nvas https://localhost:7681/test.html
|
||||
```
|
||||
|
||||
@section cross Cross compiling
|
||||
|
||||
To enable cross-compiling **libwebsockets** using CMake you need to create
|
||||
a "Toolchain file" that you supply to CMake when generating your build files.
|
||||
CMake will then use the cross compilers and build paths specified in this file
|
||||
to look for dependencies and such.
|
||||
|
||||
**Libwebsockets** includes an example toolchain file [cross-arm-linux-gnueabihf.cmake](cross-arm-linux-gnueabihf.cmake)
|
||||
you can use as a starting point.
|
||||
|
||||
The commandline to configure for cross with this would look like
|
||||
```
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
|
||||
-DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_SSL=0
|
||||
```
|
||||
The example shows how to build with no external cross lib dependencies, you
|
||||
need to provide the cross libraries otherwise.
|
||||
|
||||
**NOTE**: start from an EMPTY build directory if you had a non-cross build in there
|
||||
before the settings will be cached and your changes ignored.
|
||||
|
||||
Additional information on cross compilation with CMake:
|
||||
http://www.vtk.org/Wiki/CMake_Cross_Compiling
|
||||
|
||||
@section mem Memory efficiency
|
||||
|
||||
Embedded server-only configuration without extensions (ie, no compression
|
||||
on websocket connections), but with full v13 websocket features and http
|
||||
server, built on ARM Cortex-A9:
|
||||
|
||||
Update at 8dac94d (2013-02-18)
|
||||
```
|
||||
$ ./configure --without-client --without-extensions --disable-debug --without-daemonize
|
||||
|
||||
Context Creation, 1024 fd limit[2]: 16720 (includes 12 bytes per fd)
|
||||
Per-connection [3]: 72 bytes, +1328 during headers
|
||||
|
||||
.text .rodata .data .bss
|
||||
11512 2784 288 4
|
||||
```
|
||||
This shows the impact of the major configuration with/without options at
|
||||
13ba5bbc633ea962d46d using Ubuntu ARM on a PandaBoard ES.
|
||||
|
||||
These are accounting for static allocations from the library elf, there are
|
||||
additional dynamic allocations via malloc. These are a bit old now but give
|
||||
the right idea for relative "expense" of features.
|
||||
|
||||
Static allocations, ARM9
|
||||
|
||||
| | .text | .rodata | .data | .bss |
|
||||
|--------------------------------|---------|---------|-------|------|
|
||||
| All (no without) | 35024 | 9940 | 336 | 4104 |
|
||||
| without client | 25684 | 7144 | 336 | 4104 |
|
||||
| without client, exts | 21652 | 6288 | 288 | 4104 |
|
||||
| without client, exts, debug[1] | 19756 | 3768 | 288 | 4104 |
|
||||
| without server | 30304 | 8160 | 336 | 4104 |
|
||||
| without server, exts | 25382 | 7204 | 288 | 4104 |
|
||||
| without server, exts, debug[1] | 23712 | 4256 | 288 | 4104 |
|
||||
|
||||
[1] `--disable-debug` only removes messages below `lwsl_notice`. Since that is
|
||||
the default logging level the impact is not noticeable, error, warn and notice
|
||||
logs are all still there.
|
||||
|
||||
[2] `1024` fd per process is the default limit (set by ulimit) in at least Fedora
|
||||
and Ubuntu. You can make significant savings tailoring this to actual expected
|
||||
peak fds, ie, at a limit of `20`, context creation allocation reduces to `4432 +
|
||||
240 = 4672`)
|
||||
|
||||
[3] known header content is freed after connection establishment
|
|
@ -1,34 +0,0 @@
|
|||
ESP8266 lws port
|
||||
----------------
|
||||
|
||||
lws can now work well on the ESP8266.
|
||||
|
||||
You should get the ESP8266 Espressif SDK-based project here
|
||||
|
||||
https://github.com/lws-team/esplws
|
||||
|
||||
which includes lws as an "app" in the build. The project provides full AP-based setup over the web, and once the device has been configured to associate to a local AP, a separate station vhost with the lws test protocols.
|
||||
|
||||
Instructions for building that are here
|
||||
|
||||
https://github.com/lws-team/esplws/blob/master/README.md
|
||||
|
||||
There are also instructions there for how to remove the test apps from the build and customize your own station content.
|
||||
|
||||
|
||||
Information about lws integration on ESP8266
|
||||
--------------------------------------------
|
||||
|
||||
The following existing lws features are used to make a nice integration:
|
||||
|
||||
- vhosts: there are separate vhosts for the configuration AP mode and the normal station mode.
|
||||
|
||||
- file_ops: the lws file operations are overridden and handled by a ROMFS parser
|
||||
|
||||
- mounts: mounts are used to serve files automatically from the ROMFS
|
||||
|
||||
- plugins: standalone protocol plugins are included into the build, so there are clean individual implementations for each protocol, while everything is statically linked
|
||||
|
||||
- lws stability and security features like bytewise parsers, sophisticated timeouts, http/1.1 keepalive support
|
||||
|
||||
|
135
README.md
135
README.md
|
@ -2,19 +2,91 @@
|
|||
[](https://ci.appveyor.com/project/lws-team/libwebsockets)
|
||||
[](https://scan.coverity.com/projects/3576)
|
||||
|
||||
libwebsockets
|
||||
-------------
|
||||

|
||||
|
||||
News
|
||||
----
|
||||
|
||||
v2.3 is out... see the changelog https://github.com/warmcat/libwebsockets/blob/v2.3-stable/changelog
|
||||
## v3.0.0 released
|
||||
|
||||
See the changelog for info https://github.com/warmcat/libwebsockets/blob/v3.0-stable/changelog
|
||||
|
||||
## Major CI improvements for QA
|
||||
|
||||
The Travis build of lws done on every commit now runs
|
||||
|
||||
Tests|Count|Explanation
|
||||
---|---|---
|
||||
Build / Linux / gcc|14|-Wall -Werror cmake config variants
|
||||
Build / Mac / Clang|14|-Wall -Werror cmake config variants
|
||||
Build / Windows / MSVC|7|default
|
||||
Selftests|openssl:33, mbedtls:33|minimal examples built and run against each other and remote server
|
||||
attack.sh|225|Correctness, robustness and security tests for http parser
|
||||
Autobahn Server|480|Testing lws ws client, including permessage-deflate
|
||||
Autobahn Client|480|Testing lws ws server, including permaessage-deflate
|
||||
h2spec|openssl:146, mbedtls:146|Http/2 server compliance suite (in strict mode)
|
||||
h2load|openssl:6, mbedtls:6|Http/2 server load tool (checks 10K / 100K in h1 and h2, at 1, 10, 100 concurrency)
|
||||
h2load SMP|6|Http/2 and http/1.1 server load checks on SMP server build
|
||||
|
||||
The over 1,500 tests run on every commit take most of an hour to complete.
|
||||
If any problems are found, it breaks the travis build, generating an email.
|
||||
|
||||
Current master passes all the tests and these new CI arrangements will help
|
||||
keep it that way.
|
||||
|
||||
## Lws has the first official ws-over-h2 server support
|
||||
|
||||

|
||||
|
||||
There's a new standard on the RFC track that enables multiplexing ws connections
|
||||
over an http/2 link. Compared to making individual tcp and tls connections for
|
||||
each ws link back to the same server, this makes your site start up radically
|
||||
faster, and since all the connections are in one tls tunnel, with considerable memory
|
||||
reduction serverside.
|
||||
|
||||
To enable it on master you just need -DLWS_WITH_HTTP2=1 at cmake. No changes to
|
||||
existing code are necessary for either http/2 (if you use the official header creation
|
||||
apis if you return your own headers, as shown in the test apps for several versions)
|
||||
or to take advantage of ws-over-h2. When built with http/2 support, it automatically
|
||||
falls back to http/1 and traditional ws upgrade if that's all the client can handle.
|
||||
|
||||
Currently only Chrome Canary v67 supports this ws-over-h2 encapsulation (chrome
|
||||
must be started with `--enable-websocket-over-http2` switch to enable it currently)
|
||||
but the other browsers will catch up soon.
|
||||
|
||||
## New "minimal examples"
|
||||
|
||||
https://github.com/warmcat/libwebsockets/tree/master/minimal-examples
|
||||
|
||||
These are like the test apps, but focus on doing one thing, the best way, with the minimum amount of code. For example the minimal-http-server serves the cwd on http/1 or http/2 in 50 LOC. Same thing with tls is just three more lines.
|
||||
|
||||
They build standalone, so it's easier to copy them directly to start your own project; they
|
||||
are CC0 licensed (public domain) to facilitate that.
|
||||
|
||||
## Windows binary builds
|
||||
|
||||
32- and 64-bit Windows binary builds are available via Appveyor. Visit [lws on Appveyor](https://ci.appveyor.com/project/lws-team/libwebsockets),
|
||||
click on a build, the ARTIFACTS, and unzip the zip file at `C:\Program Files (x86)/libwebsockets`.
|
||||
|
||||
## Latest Stable
|
||||
|
||||
- v2.4.2 is out... HTTP/2 server support and mbedTLS as a TLS backend.
|
||||
|
||||
see the changelog https://github.com/warmcat/libwebsockets/blob/v2.4-stable/changelog
|
||||
|
||||
Please note the additional READMEs have moved to ./READMEs/
|
||||
|
||||
## ESP32 is supported
|
||||
|
||||
ESP32 is now supported in lws! Download the
|
||||
|
||||
- factory https://github.com/warmcat/lws-esp32-factory and
|
||||
- test server app https://github.com/warmcat/lws-esp32-test-server-demos
|
||||
|
||||
The ESP32 stuff has my dynamic mbedtls buffer allocation patches applied,
|
||||
which reduce allocation for small payload TLS links by around 26KiB per connection.
|
||||
|
||||
## Support
|
||||
|
||||
This is the libwebsockets C library for lightweight websocket clients and
|
||||
servers. For support, visit
|
||||
|
@ -33,60 +105,3 @@ You can get the latest version of the library from git:
|
|||
|
||||
Doxygen API docs for master: https://libwebsockets.org/lws-api-doc-master/html/index.html
|
||||
|
||||
|
||||
After libwebsockets 1.3, tags will be signed using a key corresponding to this public key
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
mQINBFRe35QBEADZA7snW7MoEXkT2deDYZeggVD3694dg1o5G4q36NWjC8Pn/b2V
|
||||
d+L9Nmw8ydKIv8PLJW762rnveQpPYRqCRD8X4bVTYzYz3qsOl5BrYf6cuVn0ZrPB
|
||||
13TVRg+NZwUaVxc7O+tdOvvEBdA9OCIygctPNK9Nyh53xs5gPHhghZrKVrt0xM1A
|
||||
2LYsgoHmMBCCY25SHb1nuapvhA3LvuJb4cNNVRCukCoA6yx0uhSEz2AUPJSLqnZ9
|
||||
XnNBMKq+1a9C+y7jo4O78upTTmuOmRmNEVAu7pxCSUXDrNa87T8n6vFkV/MiW8nv
|
||||
VmhppKJrKPJ0KxJF9b7uG6eKosfoK2PKyE7pAoDN1fuNyBTB0dkFAwyTCN8hmhOg
|
||||
z71QrCltotq/AxSCsKzgFkDBL7D3KUM10QR5kmznjcm8tFWHoSttPR334z/1Yepf
|
||||
ATqH/tfYydW42qeeHgKjfeegnlI65nTDtwYW6lSqZsXg+/ABg0ki9m5HA6l713ig
|
||||
gRbVHSNkiz56O+UOqBtfcJZBc8QZqqixq8rbP2Is0HBBEtD+aFMuKx/sQ3ULkQs2
|
||||
8dZ5qsGTBT/xHmqpHJsIFX/jwjY5zeEiFbnO5bMH7YLmkjynVsn5zxTyXKQJe29C
|
||||
Uq0Yd9+JpDhHnZoiz/1hIIBsr89Z4Yy6c59YNJ3yJEOast0ODERcKSaUAQARAQAB
|
||||
tC9BbmR5IEdyZWVuIChMaW5hcm8ga2V5KSA8YW5keS5ncmVlbkBsaW5hcm8ub3Jn
|
||||
PokCPQQTAQoAJwUCVF7flAIbAwUJBaOagAULCQgHAwUVCgkICwUWAwIBAAIeAQIX
|
||||
gAAKCRA8ZxoDS3lTexApD/9WT7JWy3tK33OIACYV40XwLEhRam4Xku4rhtsoIeJK
|
||||
P0k/wa7J2PpceX6gKV+QBsOx3UbUfpqZ/Mu7ff3M0J6W87XpKRROAmP43zyiBkmM
|
||||
A6v0pJXozknmCU28p3DuLC8spVDFg9N52xV7Qb+9TDHcTYiVi4swKYuDEuHBC/qa
|
||||
M69+ANgsGbrMFRypxtU7OEhls3AEo3Cq03xD8QvLjFyPpYp1f0vNRFm2Jjgm2CRe
|
||||
YLVsCGxG35Dz7DpJHekHNxje6xsZ2w9Q38M0rLQ0ICOVQ+E1Dir3hwmZQWASzMMi
|
||||
+R0P+MVYpVt5y7KtiLywJ4BzNogS7gY3wQxksJOFA1uuk5h/hO54a361mcdA0Ta5
|
||||
HHhGKRw87lVjEQSaRjFZmHFClB+Sb8MuWR51JTzVS5HtJlcNqcWhF63vZ8bZ7b6y
|
||||
Aj8cXNjH6ULXyX3QnTUWXX/QU3an3yh8iPONWOGP5d5Hi/qejHGIhP2L5H+h05CP
|
||||
aZQYFLjjebYgEHijuA28eKWsBsoBPFSLpLloHTDkiycgFdV2AkQcxZN9ZElAqURP
|
||||
xUkEIscQg3YhExGiVEtaxBp1/p/WctMxs5HNoi0Oc97ZUcKvSCz9FDGXX9wYBpRf
|
||||
gzjNn055Xn4QyxBDnp5DrYT0ft/8BEnRK0JP6z3gNfnhOxZo4XA+M6w4Hjh3tI2A
|
||||
3rkCDQRUXt+UARAA0yHmONtW3L1HpvWFR+VgVNHa1HBWWk7lMsI6ajeiUK/lN3F/
|
||||
+vNbux46bPj/sNT9twbWmYhv6c0yVzCpmv5M5ztefS7mW/zPNLJmCmH32kAvVFr1
|
||||
Z90R/X+Z1Uh8wCCU72S2pSIXQFza3LF53pbpKi5m1F2icYcx+35egAvvZVZtcrMu
|
||||
TjHUa+N9mFKxa7tb5PI8Lv93nRLwB7aKkp5PKy9Yvse0jACrAAGeIpI73H467/wO
|
||||
ujermKlyPOOv+Lpjd7kedWKdaweitva7FVI20K/afn4AwCI8HJUIqVbil0Yrg9Le
|
||||
M1TRsRydzMQQejsb/cWi3fQ3U3HxvSJijKltckPMqjJaXbqmrLz3FOA5Km0ciIOB
|
||||
WW0Qq0WREcS3rc5FHU29duS9OAieAWFYyLDieug4nQ29KQE6I0lMqLnz8vWYtbmw
|
||||
6AHk9i2GsXOZiPnztuADgt9o9Os8fm7ZiacA1LISl86P7wpFk+Gf4LRvv8Fk08NV
|
||||
b2K1BY4YC9KP+AynyYQmxmyB1YQCh/dZHiD4ikGKttHAy4ZsMW6IRL5bRP0Z97pA
|
||||
lyBtXP0cGTJtuPt2feh0zaiA7blZ/IDXkB1UqH6jnTa71d1FeNKtVFi8FhPIREN6
|
||||
Rc5imyRxubZEgsxhdjqGgdT5k6Qr42SewAN391ygutpgGizGQtTwzvmKa0UAEQEA
|
||||
AYkCJQQYAQoADwUCVF7flAIbDAUJBaOagAAKCRA8ZxoDS3lTewuBD/9/rakAMCRC
|
||||
+WmbUVpCbJSWP5ViH87Xko4ku437gq56whcGjQpxfCYt8oeVgS8fZetUOHs2gspJ
|
||||
CEc8TYLUFntfyt2AzKU29oQoizPm33W9S1u7aRGWsVVutd2sqUaQUDsl9z35+Ka9
|
||||
YcWoATJSWBgnhSAmNcM60OG0P5qrZloTlbRSlDZTSZT3RvY4JWtWCubGsjEpXO4h
|
||||
ZqbKCu3KgV/6NOuTLciriSOZ/iyva3WsCP2S8mRRvma7x04oMTEWX80zozTCK8gG
|
||||
XqqS9eDhCkRbdmMyUQbHIhc/ChYchO5+fQ1o0zMS5cv6xgkhWI3NJRUkNdXolH9a
|
||||
5F9q4CmCTcdEZkqpnjsLNiQLIENfHbgC0A5IjR6YgN6qAP8ZJ5hBgyTfyKkwB7bW
|
||||
DcCnuoC9R79hkI8nWkoRVou9tdzKxo0bGR6O4CfLj+4d3hpWkv9Rw7Xxygo5JOqN
|
||||
4cNZGtHkmIFFk9fSXul5rkjfF/XmThIwoI8aHSBZ7j3IMtmkKVkBjNjiTfbgW8RT
|
||||
XIIR+QQdVLOyJqq+NZC/SrKVQITg0ToYJutRTUJViqyz5b3psJo5o2SW6jcexQpE
|
||||
cX6tdPyGz3o0aywfJ9dcN6izleSV1gYmXmIoS0cQyezVqTUkT8C12zeRB7mtWsDa
|
||||
+AWJGq/WfB7N6pPh8S/XMW4e6ptuUodjiA==
|
||||
=HV8t
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
|
250
READMEs/README-plugin-sshd-base.md
Normal file
250
READMEs/README-plugin-sshd-base.md
Normal file
|
@ -0,0 +1,250 @@
|
|||
ssh-base Plugin
|
||||
================
|
||||
|
||||
## Introduction
|
||||
|
||||
lws-ssh-base is a protcol plugin for libwebsockets that implements a
|
||||
generic, abstract, ssh server.
|
||||
|
||||
- very small footprint in code and memory, takes up small part of ESP32
|
||||
|
||||
- written with security in mind: valgrind and Coverity -clean
|
||||
|
||||
- binds to one or more vhosts, that controls listen port(s)
|
||||
|
||||
- all IO and settings abstracted through a single "ops" struct from user code
|
||||
|
||||
- each instance on a vhost has its own "ops" struct, defining server keys,
|
||||
auth method and functions to implement IO and other operations
|
||||
|
||||
- The plugin has no built-in behaviours like check ~/.ssh/authorized_keys,
|
||||
treat auth usernames as system usernames, or spawn the user's shell.
|
||||
Everything potentially dangerous is left to the user ops code to decide
|
||||
how to handle. It's NOT like sshd where running it implies it will accept
|
||||
existing keys for any system user, will spawn a shell, etc, unless you
|
||||
implement those parts in the ops callbacks.
|
||||
|
||||
- The plugin requires extra code around it in the form of the ops struct
|
||||
handlers. So it's role is something like an abstract base class for an ssh
|
||||
server. All the crypto, protocol sequencing and state machine are inside,
|
||||
but all the IO except the network connection is outside.
|
||||
|
||||
- Built as part of libwebsockets, like all plugins may be dynamically loaded
|
||||
at runtime or built statically. Test app `libwebsockets-test-sshd` provided
|
||||
|
||||
- Uses hash and RSA functions from either mbedTLS or OpenSSL automatically,
|
||||
according to which library libwebsockets was built for
|
||||
|
||||
To maintain its small size, it implements a single "best of breed" crypto for
|
||||
the following functions:
|
||||
|
||||
|Function|Crypto|
|
||||
|---|---|
|
||||
|KEX|curve25519-sha256@libssh.org|
|
||||
|Server host key|ssh-rsa (4096b)|
|
||||
|Encryption|chacha20-poly1305@openssh.com|
|
||||
|Compression|None|
|
||||
|
||||
## License
|
||||
|
||||
lws-ssh-base is Free Software, available under libwebsocket's LGPLv2 +
|
||||
static linking exception license.
|
||||
|
||||
The crypto parts are available elsewhere under a BSD license. But for
|
||||
simplicity the whole plugin is under LGPLv2.
|
||||
|
||||
## Generating your own keys
|
||||
|
||||
```
|
||||
$ ssh-keygen -t rsa -b 4096 -f mykeys
|
||||
```
|
||||
|
||||
will ask for a passphrase and generate the private key in `mykeys` and the
|
||||
public key in `mykeys.pub`. If you already have a suitable RSA key you use
|
||||
with ssh, you can just use that directly.
|
||||
|
||||
lws installs a test keypair in /usr[/local]/share/libwebsockets-test-server
|
||||
that the test apps will accept.
|
||||
|
||||
## Example code
|
||||
|
||||
1) There's a working example app `libwebsockets-test-sshd` included that
|
||||
spawns a bash shell when an ssh client authenticates. The username used on
|
||||
the remote ssh has no meaning, it spawns the shell under the credentials of
|
||||
"lws-test-sshd" was run under. It accepts the lws ssh test key which is
|
||||
installed into /usr[/local]/share/libwebsockets-test-server.
|
||||
|
||||
Start the server like this (it wants root only because the server key is stored
|
||||
in /etc)
|
||||
|
||||
```
|
||||
$ sudo libwebsockets-test-sshd
|
||||
```
|
||||
|
||||
Connect to it using the test private key like this
|
||||
|
||||
```
|
||||
$ ssh -p 2200 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys anyuser@127.0.0.1
|
||||
```
|
||||
|
||||
2) There's also a working example plugin `lws-sshd-demo` that "subclasses" the
|
||||
abstract `lws-ssh-base` plugin to make a protocol which can be used from,
|
||||
eg, lwsws. For an lwsws vhost that listens on port 2222 and responds with
|
||||
the lws-sshd-demo ssh server, the related config is:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "sshd",
|
||||
"port": "2222",
|
||||
"onlyraw": "1",
|
||||
"ws-protocols": [{
|
||||
"lws-ssh-base": {
|
||||
"status": "ok",
|
||||
"ops-from": "lws-sshd-demo"
|
||||
},
|
||||
"lws-sshd-demo": {
|
||||
"status": "ok",
|
||||
"raw": "1"
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Integration to other apps
|
||||
|
||||
### Step 0: Build and install libwebsockets
|
||||
|
||||
For the `libwebsockets-test-sshd` example, you will need CMake options
|
||||
`LWS_WITH_CGI`, since it uses lws helpers to spawn a shell.
|
||||
|
||||
lws-ssh-base itself doesn't require CGI support in libwebsockets.
|
||||
|
||||
### Step 1: make the code available in your app
|
||||
|
||||
Include `lws-plugin-ssh-base` in your app, either as a runtime plugin or by using
|
||||
the lws static include scheme.
|
||||
|
||||
To bring in the whole of the ssh-base plugin
|
||||
into your app in one step, statically, just include
|
||||
`plugins/ssh-base/include/lws-plugin-sshd-static-build-includes.h`, you can see
|
||||
an example of this in `./test-apps/test-sshd.c`.
|
||||
|
||||
### Step 2: define your `struct lws_ssh_ops`
|
||||
|
||||
`plugins/ssh-base/include/lws-plugin-ssh.h` defines
|
||||
`struct lws_ssh_ops` which is used for all customization and integration
|
||||
of the plugin per vhost. Eg,
|
||||
|
||||
```
|
||||
static const struct lws_ssh_ops ssh_ops = {
|
||||
.channel_create = ssh_ops_channel_create,
|
||||
.channel_destroy = ssh_ops_channel_destroy,
|
||||
.tx_waiting = ssh_ops_tx_waiting,
|
||||
.tx = ssh_ops_tx,
|
||||
.rx = ssh_ops_rx,
|
||||
.get_server_key = ssh_ops_get_server_key,
|
||||
.set_server_key = ssh_ops_set_server_key,
|
||||
.set_env = ssh_ops_set_env,
|
||||
.pty_req = ssh_ops_pty_req,
|
||||
.child_process_io = ssh_ops_child_process_io,
|
||||
.child_process_terminated = ssh_ops_child_process_terminated,
|
||||
.exec = ssh_ops_exec,
|
||||
.shell = ssh_ops_shell,
|
||||
.is_pubkey_authorized = ssh_ops_is_pubkey_authorized,
|
||||
.banner = ssh_ops_banner,
|
||||
.disconnect_reason = ssh_ops_disconnect_reason,
|
||||
.server_string = "SSH-2.0-Libwebsockets",
|
||||
.api_version = 1,
|
||||
};
|
||||
```
|
||||
The `ssh_ops_...()` functions are your implementations for the operations
|
||||
needed by the plugin for your purposes.
|
||||
|
||||
### Step 3: enable `lws-ssh-base` protocol to a vhost and configure using pvo
|
||||
|
||||
A pointer to your struct lws_ssh_ops is passed into the vhost instance of the
|
||||
protocol using per-vhost options
|
||||
|
||||
```
|
||||
static const struct lws_protocol_vhost_options pvo_ssh_ops = {
|
||||
NULL,
|
||||
NULL,
|
||||
"ops",
|
||||
(void *)&ssh_ops
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_ssh = {
|
||||
NULL,
|
||||
&pvo_ssh_ops,
|
||||
"lws-sshd-base",
|
||||
"" /* ignored, just matches the protocol name above */
|
||||
};
|
||||
|
||||
...
|
||||
info.port = 22;
|
||||
info.options = LWS_SERVER_OPTION_ONLY_RAW;
|
||||
info.vhost_name = "sshd";
|
||||
info.protocols = protocols_sshd;
|
||||
info.pvo = &pvo_ssh;
|
||||
|
||||
vh_sshd = lws_create_vhost(context, &info);
|
||||
```
|
||||
|
||||
There are two possible pvos supported, "ops", shown above, directly passes the
|
||||
ops structure in using the value on the "ops" pvo.
|
||||
|
||||
To support other protocols that want to provide ops to lws-ssh-base themselves
|
||||
for a particular vhost, you can also provide a pvo `"ops-from"` whose value is
|
||||
the name of the protocol also enabled on this vhost, whose protocol ".user"
|
||||
pointer points to the ops struct lws-ssh-base should use.
|
||||
|
||||
## Integration to other plugins
|
||||
|
||||
A worked example of using the abstract `lws-ssh-base` plugin from another
|
||||
plugin that provides the ops struct is in `./plugins/protocol_lws_sshd_demo`.
|
||||
|
||||
The key points to note
|
||||
|
||||
- the plugin sets the ops struct for the vhost instantiation of `lws-ssh-base`
|
||||
by passing a pointer to the ops struct in its `lws_protocols` struct `user`
|
||||
member.
|
||||
|
||||
- the config for the vhost tells `lws-ssh-base` to pick up the ops struct
|
||||
pointer using an "ops-from" pvo that indicates the protocol name.
|
||||
|
||||
```
|
||||
"lws-ssh-base": {
|
||||
"status": "ok",
|
||||
"ops-from": "lws-sshd-demo"
|
||||
},
|
||||
```
|
||||
|
||||
- the config for the vhost tells lws this vhost only serves RAW (ie, no http)
|
||||
|
||||
```
|
||||
{
|
||||
"name": "sshd",
|
||||
"port": "2222",
|
||||
"onlyraw": "1",
|
||||
...
|
||||
```
|
||||
|
||||
- the config for the vhost marks the protocol that uses `lws-ssh-base`, not
|
||||
`lws-ssh-base` itself, as the protocol to be served for raw connections
|
||||
|
||||
```
|
||||
"lws-sshd-demo": {
|
||||
"status": "ok",
|
||||
"raw": "1"
|
||||
...
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
You can have the vhost it binds to listen on a nonstandard port. The ssh
|
||||
commandline app cane be told to connect to a non-22 port with
|
||||
`ssh -p portnum user@hostname`
|
||||
|
||||
|
711
READMEs/README.build.md
Normal file
711
READMEs/README.build.md
Normal file
|
@ -0,0 +1,711 @@
|
|||
Notes about building lws
|
||||
========================
|
||||
|
||||
|
||||
@section cm Introduction to CMake
|
||||
|
||||
CMake is a multi-platform build tool that can generate build files for many
|
||||
different target platforms. See more info at http://www.cmake.org
|
||||
|
||||
CMake also allows/recommends you to do "out of source"-builds, that is,
|
||||
the build files are separated from your sources, so there is no need to
|
||||
create elaborate clean scripts to get a clean source tree, instead you
|
||||
simply remove your build directory.
|
||||
|
||||
Libwebsockets has been tested to build successfully on the following platforms
|
||||
with SSL support (for OpenSSL/wolfSSL/BoringSSL):
|
||||
|
||||
- Windows (Visual Studio)
|
||||
- Windows (MinGW)
|
||||
- Linux (x86 and ARM)
|
||||
- OSX
|
||||
- NetBSD
|
||||
|
||||
|
||||
@section build1 Building the library and test apps
|
||||
|
||||
The project settings used by CMake to generate the platform specific build
|
||||
files is called [CMakeLists.txt](../CMakeLists.txt). CMake then uses one of its "Generators" to
|
||||
output a Visual Studio project or Make file for instance. To see a list of
|
||||
the available generators for your platform, simply run the "cmake" command.
|
||||
|
||||
Note that by default OpenSSL will be linked, if you don't want SSL support
|
||||
see below on how to toggle compile options.
|
||||
|
||||
|
||||
@section bu Building on Unix:
|
||||
|
||||
1. Install CMake 2.8 or greater: http://cmake.org/cmake/resources/software.html
|
||||
(Most Unix distributions comes with a packaged version also)
|
||||
|
||||
2. Install OpenSSL.
|
||||
|
||||
3. Generate the build files (default is Make files):
|
||||
```
|
||||
$ cd /path/to/src
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
```
|
||||
|
||||
4. Finally you can build using the generated Makefile:
|
||||
```
|
||||
$ make && sudo make install
|
||||
```
|
||||
**NOTE**: The `build/`` directory can have any name and be located anywhere
|
||||
on your filesystem, and that the argument `..` given to cmake is simply
|
||||
the source directory of **libwebsockets** containing the [CMakeLists.txt](../CMakeLists.txt)
|
||||
project file. All examples in this file assumes you use ".."
|
||||
|
||||
**NOTE2**:
|
||||
A common option you may want to give is to set the install path, same
|
||||
as --prefix= with autotools. It defaults to /usr/local.
|
||||
You can do this by, eg
|
||||
```
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .
|
||||
```
|
||||
|
||||
**NOTE3**:
|
||||
On machines that want libraries in lib64, you can also add the
|
||||
following to the cmake line
|
||||
```
|
||||
-DLIB_SUFFIX=64
|
||||
```
|
||||
|
||||
**NOTE4**:
|
||||
If you are building against a non-distro OpenSSL (eg, in order to get
|
||||
access to ALPN support only in newer OpenSSL versions) the nice way to
|
||||
express that in one cmake command is eg,
|
||||
```
|
||||
$ cmake .. -DOPENSSL_ROOT_DIR=/usr/local/ssl \
|
||||
-DCMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE=/usr/local/ssl \
|
||||
-DLWS_WITH_HTTP2=1
|
||||
```
|
||||
|
||||
When you run the test apps using non-distro SSL, you have to force them
|
||||
to use your libs, not the distro ones
|
||||
```
|
||||
$ LD_LIBRARY_PATH=/usr/local/ssl/lib libwebsockets-test-server --ssl
|
||||
```
|
||||
|
||||
To get it to build on latest openssl (2016-04-10) it needed this approach
|
||||
```
|
||||
cmake .. -DLWS_WITH_HTTP2=1 -DLWS_OPENSSL_INCLUDE_DIRS=/usr/local/include/openssl -DLWS_OPENSSL_LIBRARIES="/usr/local/lib64/libssl.so;/usr/local/lib64/libcrypto.so"
|
||||
```
|
||||
|
||||
Mac users have reported
|
||||
|
||||
```
|
||||
$ export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2k; cmake ..; make -j4
|
||||
```
|
||||
|
||||
worked for them when using "homebrew" OpenSSL
|
||||
|
||||
**NOTE5**:
|
||||
To build with debug info and _DEBUG for lower priority debug messages
|
||||
compiled in, use
|
||||
```
|
||||
$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
|
||||
```
|
||||
|
||||
**NOTE6**
|
||||
To build on Solaris the linker needs to be informed to use lib socket
|
||||
and libnsl, and only builds in 64bit mode.
|
||||
|
||||
```bash
|
||||
$ cmake .. -DCMAKE_C_FLAGS=-m64 -DCMAKE_EXE_LINKER_FLAGS="-lsocket -lnsl"
|
||||
```
|
||||
|
||||
4. Finally you can build using the generated Makefile:
|
||||
|
||||
```bash
|
||||
$ make
|
||||
```
|
||||
|
||||
@section lcap Linux Capabilities
|
||||
|
||||
On Linux, lws now lets you retain selected root capabilities when dropping
|
||||
privileges. If libcap-dev or similar package is installed providing
|
||||
sys/capabilities.h, and libcap or similar package is installed providing
|
||||
libcap.so, CMake will enable the capability features.
|
||||
|
||||
The context creation info struct .caps[] and .count_caps members can then
|
||||
be set by user code to enable selected root capabilities to survive the
|
||||
transition to running under an unprivileged user.
|
||||
|
||||
@section cmq Quirk of cmake
|
||||
|
||||
When changing cmake options, for some reason the only way to get it to see the
|
||||
changes sometimes is delete the contents of your build directory and do the
|
||||
cmake from scratch.
|
||||
|
||||
deleting build/CMakeCache.txt may be enough.
|
||||
|
||||
|
||||
@section cmw Building on Windows (Visual Studio)
|
||||
|
||||
1. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
|
||||
|
||||
2. Install OpenSSL binaries. https://wiki.openssl.org/index.php/Binaries
|
||||
|
||||
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
|
||||
|
||||
**NOTE2**:
|
||||
Be sure that OPENSSL_CONF environment variable is defined and points at
|
||||
<OpenSSL install location>\bin\openssl.cfg
|
||||
|
||||
3. Generate the Visual studio project by opening the Visual Studio cmd prompt:
|
||||
|
||||
```
|
||||
cd <path to src>
|
||||
md build
|
||||
cd build
|
||||
cmake -G "Visual Studio 10" ..
|
||||
```
|
||||
|
||||
(**NOTE**: There is also a cmake-gui available on Windows if you prefer that)
|
||||
|
||||
**NOTE2**:
|
||||
See this link to find out the version number corresponding to your Visual Studio edition:
|
||||
http://superuser.com/a/194065
|
||||
|
||||
4. Now you should have a generated Visual Studio Solution in your
|
||||
`<path to src>/build` directory, which can be used to build.
|
||||
|
||||
5. Some additional deps may be needed
|
||||
|
||||
- iphlpapi.lib
|
||||
- psapi.lib
|
||||
- userenv.lib
|
||||
|
||||
6. If you're using libuv, you must make sure to compile libuv with the same multithread-dll / Mtd attributes as libwebsockets itself
|
||||
|
||||
|
||||
@section cmwmgw Building on Windows (MinGW)
|
||||
|
||||
1. Install MinGW: http://sourceforge.net/projects/mingw/files
|
||||
|
||||
(**NOTE**: Preferably in the default location C:\MinGW)
|
||||
|
||||
2. Fix up MinGW headers
|
||||
|
||||
a) If still necessary, sdd the following lines to C:\MinGW\include\winsock2.h:
|
||||
```
|
||||
#if(_WIN32_WINNT >= 0x0600)
|
||||
|
||||
typedef struct pollfd {
|
||||
|
||||
SOCKET fd;
|
||||
SHORT events;
|
||||
SHORT revents;
|
||||
|
||||
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
|
||||
|
||||
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
|
||||
|
||||
#endif // (_WIN32_WINNT >= 0x0600)
|
||||
```
|
||||
|
||||
Update crtdefs.h line 47 to say:
|
||||
|
||||
```
|
||||
typedef __int64 ssize_t;
|
||||
```
|
||||
|
||||
b) Create C:\MinGW\include\mstcpip.h and copy and paste the content from following link into it:
|
||||
|
||||
https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/include/mstcpip.h
|
||||
|
||||
3. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
|
||||
|
||||
4. Install OpenSSL binaries. https://wiki.openssl.org/index.php/Binaries
|
||||
|
||||
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
|
||||
|
||||
**NOTE2**:
|
||||
Be sure that OPENSSL_CONF environment variable is defined and points at
|
||||
<OpenSSL install location>\bin\openssl.cfg
|
||||
|
||||
5. Generate the build files (default is Make files) using MSYS shell:
|
||||
```
|
||||
$ cd /drive/path/to/src
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW ..
|
||||
```
|
||||
(**NOTE**: The `build/`` directory can have any name and be located anywhere
|
||||
on your filesystem, and that the argument `..` given to cmake is simply
|
||||
the source directory of **libwebsockets** containing the [CMakeLists.txt](../CMakeLists.txt)
|
||||
project file. All examples in this file assumes you use "..")
|
||||
|
||||
**NOTE2**:
|
||||
To generate build files allowing to create libwebsockets binaries with debug information
|
||||
set the CMAKE_BUILD_TYPE flag to DEBUG:
|
||||
```
|
||||
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW -DCMAKE_BUILD_TYPE=DEBUG ..
|
||||
```
|
||||
6. Finally you can build using the generated Makefile and get the results deployed into your MinGW installation:
|
||||
|
||||
```
|
||||
$ make
|
||||
$ make install
|
||||
```
|
||||
|
||||
@section distro Selecting CMake options useful for distros
|
||||
|
||||
Distro packagers should select the CMake option "LWS_WITH_DISTRO_RECOMMENDED",
|
||||
which selects common additional options like support for various event libraries,
|
||||
plugins and lwsws.
|
||||
|
||||
@section ssllib Choosing Your TLS Poison
|
||||
|
||||
- If you are really restricted on memory, code size, or don't care about TLS
|
||||
speed, mbedTLS is a good choice: `cmake .. -DLWS_WITH_MBEDTLS=1`
|
||||
|
||||
- If cpu and memory is not super restricted and you care about TLS speed,
|
||||
OpenSSL or a directly compatible variant like Boring SSL is a good choice.
|
||||
|
||||
Just building lws against stock Fedora OpenSSL or stock Fedora mbedTLS, for
|
||||
SSL handhake mbedTLS takes ~36ms and OpenSSL takes ~1ms on the same x86_64
|
||||
build machine here, with everything else the same. Over the 144 connections of
|
||||
h2spec compliance testing for example, this ends up completing in 400ms for
|
||||
OpenSSL and 5.5sec for mbedTLS on x86_64. In other words mbedTLS is very slow
|
||||
compared to OpenSSL under the (fairly typical) conditions I tested it.
|
||||
|
||||
This isn't an inefficiency in the mbedtls interface implementation, it's just
|
||||
mbedTLS doing the crypto much slower than OpenSSL, which has accelerated
|
||||
versions of common crypto operations it automatically uses for platforms
|
||||
supporting it. As of Oct 2017 mbedTLS itself has no such optimizations for any
|
||||
platform that I could find. It's just pure C running on the CPU.
|
||||
|
||||
Lws supports both almost the same, so instead of taking my word for it you are
|
||||
invited to try it both ways and see which the results (including, eg, binary
|
||||
size and memory usage as well as speed) suggest you use.
|
||||
|
||||
NOTE: one major difference with mbedTLS is it does not load the system trust
|
||||
store by default. That has advantages and disadvantages, but the disadvantage
|
||||
is you must provide the CA cert to lws built against mbedTLS for it to be able
|
||||
to validate it, ie, use -A with the test client. The minimal test clients
|
||||
have the CA cert for warmcat.com and libwebsockets.org and use it if they see
|
||||
they were built with mbedTLS.
|
||||
|
||||
@section optee Building for OP-TEE
|
||||
|
||||
OP-TEE is a "Secure World" Trusted Execution Environment.
|
||||
|
||||
Although lws is only part of the necessary picture to have an https-enabled
|
||||
TA, it does support OP-TEE as a platform and if you provide the other
|
||||
pieces, does work very well.
|
||||
|
||||
Select it in cmake with `-DLWS_PLAT_OPTEE=1`
|
||||
|
||||
|
||||
@section cmco Setting compile options
|
||||
|
||||
To set compile time flags you can either use one of the CMake gui applications
|
||||
or do it via the command line.
|
||||
|
||||
@subsection cmcocl Command line
|
||||
|
||||
To list available options (omit the H if you don't want the help text):
|
||||
|
||||
cmake -LH ..
|
||||
|
||||
Then to set an option and build (for example turn off SSL support):
|
||||
|
||||
cmake -DLWS_WITH_SSL=0 ..
|
||||
or
|
||||
cmake -DLWS_WITH_SSL:BOOL=OFF ..
|
||||
|
||||
@subsection cmcoug Unix GUI
|
||||
|
||||
If you have a curses-enabled build you simply type:
|
||||
(not all packages include this, my debian install does not for example).
|
||||
|
||||
ccmake
|
||||
|
||||
@subsection cmcowg Windows GUI
|
||||
|
||||
On windows CMake comes with a gui application:
|
||||
Start -> Programs -> CMake -> CMake (cmake-gui)
|
||||
|
||||
|
||||
@section wolf wolfSSL/CyaSSL replacement for OpenSSL
|
||||
|
||||
wolfSSL/CyaSSL is a lightweight SSL library targeted at embedded systems:
|
||||
https://www.wolfssl.com/wolfSSL/Products-wolfssl.html
|
||||
|
||||
It contains a OpenSSL compatibility layer which makes it possible to pretty
|
||||
much link to it instead of OpenSSL, giving a much smaller footprint.
|
||||
|
||||
**NOTE**: wolfssl needs to be compiled using the `--enable-opensslextra` flag for
|
||||
this to work.
|
||||
|
||||
@section wolf1 Compiling libwebsockets with wolfSSL
|
||||
|
||||
```
|
||||
cmake .. -DLWS_WITH_WOLFSSL=1 \
|
||||
-DLWS_WOLFSSL_INCLUDE_DIRS=/path/to/wolfssl \
|
||||
-DLWS_WOLFSSL_LIBRARIES=/path/to/wolfssl/wolfssl.a ..
|
||||
```
|
||||
|
||||
**NOTE**: On windows use the .lib file extension for `LWS_WOLFSSL_LIBRARIES` instead.
|
||||
|
||||
@section cya Compiling libwebsockets with CyaSSL
|
||||
|
||||
```
|
||||
cmake .. -DLWS_WITH_CYASSL=1 \
|
||||
-DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \
|
||||
-DLWS_CYASSL_LIBRARIES=/path/to/wolfssl/cyassl.a ..
|
||||
```
|
||||
|
||||
**NOTE**: On windows use the .lib file extension for `LWS_CYASSL_LIBRARIES` instead.
|
||||
|
||||
@section esp32 Building for ESP32
|
||||
|
||||
Building for ESP32 requires the ESP-IDF framework. It can be built under Linux, OSX or Windows (MSYS2).
|
||||
|
||||
1. Install ESP-IDF, follow the getting started guide here - http://esp-idf.readthedocs.io/en/latest/get-started/
|
||||
2. Set ESP-IDF to last known working version (assuming ESP-IDF is in `~/esp/esp-idf`) :
|
||||
```
|
||||
cd ~/esp/esp-idf
|
||||
git checkout 0c50b65a34cd6b3954f7435193411a88adb49cb0
|
||||
git submodule update --recursive
|
||||
```
|
||||
3. Add `libwebsockets` as a submodule in the `components` folder of your ESP-IDF project:
|
||||
```
|
||||
git submodule add https://github.com/warmcat/libwebsockets.git components/libwebsockets
|
||||
```
|
||||
4. If on Windows (MSYS2) you will need to install CMake in the MSYS2 environment:
|
||||
```
|
||||
pacman -S mingw-w64-i686-cmake
|
||||
```
|
||||
If you're on Linux or OSX ensure CMake version is at least 3.7.
|
||||
|
||||
@section extplugins Building plugins outside of lws itself
|
||||
|
||||
The directory ./plugin-standalone/ shows how easy it is to create plugins
|
||||
outside of lws itself. First build lws itself with -DLWS_WITH_PLUGINS,
|
||||
then use the same flow to build the standalone plugin
|
||||
```
|
||||
cd ./plugin-standalone
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make && sudo make install
|
||||
```
|
||||
|
||||
if you changed the default plugin directory when you built lws, you must
|
||||
also give the same arguments to cmake here (eg,
|
||||
` -DCMAKE_INSTALL_PREFIX:PATH=/usr/something/else...` )
|
||||
|
||||
Otherwise if you run lwsws or libwebsockets-test-server-v2.0, it will now
|
||||
find the additional plugin "libprotocol_example_standalone.so"
|
||||
```
|
||||
lwsts[21257]: Plugins:
|
||||
lwsts[21257]: libprotocol_dumb_increment.so
|
||||
lwsts[21257]: libprotocol_example_standalone.so
|
||||
lwsts[21257]: libprotocol_lws_mirror.so
|
||||
lwsts[21257]: libprotocol_lws_server_status.so
|
||||
lwsts[21257]: libprotocol_lws_status.so
|
||||
```
|
||||
If you have multiple vhosts, you must enable plugins at the vhost
|
||||
additionally, discovered plugins are not enabled automatically for security
|
||||
reasons. You do this using info->pvo or for lwsws, in the JSON config.
|
||||
|
||||
|
||||
@section http2rp Reproducing HTTP/2 tests
|
||||
|
||||
Enable `-DLWS_WITH_HTTP2=1` in cmake to build with http/2 support enabled.
|
||||
|
||||
You must have built and be running lws against a version of openssl that has
|
||||
ALPN. At the time of writing, recent distros have started upgrading to OpenSSL
|
||||
1.1+ that supports this already. You'll know it's right by seeing
|
||||
|
||||
```
|
||||
lwsts[4752]: Compiled with OpenSSL support
|
||||
lwsts[4752]: Using SSL mode
|
||||
lwsts[4752]: HTTP2 / ALPN enabled
|
||||
```
|
||||
at lws startup.
|
||||
|
||||
Recent Firefox and Chrome also support HTTP/2 by ALPN, so these should just work
|
||||
with the test server running in -s / ssl mode.
|
||||
|
||||
For testing with nghttp client:
|
||||
|
||||
```
|
||||
$ nghttp -nvas https://localhost:7681/test.html
|
||||
```
|
||||
|
||||
Testing with h2spec (https://github.com/summerwind/h2spec)
|
||||
|
||||
```
|
||||
$ h2spec -h 127.0.0.1 -p 7681 -t -k -v -o 1
|
||||
```
|
||||
|
||||
```
|
||||
145 tests, 145 passed, 0 skipped, 0 failed
|
||||
|
||||
```
|
||||
|
||||
@section coverage Automated Coverage Testing
|
||||
|
||||
./test-apps/attack.sh contains scripted tests that are the basis
|
||||
of the automated test coverage assessment available for gcc and clang.
|
||||
|
||||
To reproduce
|
||||
|
||||
$ cd build
|
||||
$ cmake .. -DLWS_WITH_GCOV=1 -DCMAKE_BUILD_TYPE=DEBUG
|
||||
$ ../scripts/build-gcov.sh
|
||||
$ ../test-apps/attack.sh
|
||||
$ ../scripts/gcov.sh
|
||||
...
|
||||
Lines executed:51.24% of 8279
|
||||
|
||||
@section windowsprebuilt Using Windows binary builds on Appveyor
|
||||
|
||||
The CI builds on Appveyor now produce usable binary outputs. Visit
|
||||
|
||||
[lws on Appveyor](https://ci.appveyor.com/project/lws-team/libwebsockets)
|
||||
|
||||
and select one of the builds, then click on ARTIFACTS at the top right. The zip file
|
||||
want to be unpacked into `C:\Program Files (x86)/libwebsockets`, after that, you should be able to run the test server, by running it from `bin/Release/libwebsockets-test-server.exe` and opening a browser on http://127.0.0.1:7681
|
||||
|
||||
@section cross Cross compiling
|
||||
|
||||
To enable cross-compiling **libwebsockets** using CMake you need to create
|
||||
a "Toolchain file" that you supply to CMake when generating your build files.
|
||||
CMake will then use the cross compilers and build paths specified in this file
|
||||
to look for dependencies and such.
|
||||
|
||||
**Libwebsockets** includes an example toolchain file [cross-arm-linux-gnueabihf.cmake](../contrib/cross-arm-linux-gnueabihf.cmake)
|
||||
you can use as a starting point.
|
||||
|
||||
The commandline to configure for cross with this would look like
|
||||
```
|
||||
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr/lib/my-cross-root \
|
||||
-DCMAKE_TOOLCHAIN_FILE=../contrib/cross-arm-linux-gnueabihf.cmake \
|
||||
-DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_SSL=0 \
|
||||
-DLWS_WITH_ZIP_FOPS=0 -DLWS_WITH_ZLIB=0
|
||||
```
|
||||
The example shows how to build with no external cross lib dependencies, you
|
||||
need to provide the cross libraries otherwise.
|
||||
|
||||
**NOTE**: start from an EMPTY build directory if you had a non-cross build in there
|
||||
before the settings will be cached and your changes ignored.
|
||||
Delete `build/CMakeCache.txt` at least before trying a new cmake config
|
||||
to ensure you are really building the options you think you are.
|
||||
|
||||
Additional information on cross compilation with CMake:
|
||||
http://www.vtk.org/Wiki/CMake_Cross_Compiling
|
||||
|
||||
@section cross_example Complex Cross compiling example
|
||||
|
||||
Here are step by step instructions for cross-building the external projects needed for lws with lwsws + mbedtls as an example.
|
||||
|
||||
In the example, my toolchain lives in `/projects/aist-tb/arm-tc` and is named `arm-linux-gnueabihf`. So you will need to adapt those to where your toolchain lives and its name where you see them here.
|
||||
|
||||
Likewise I do all this in /tmp but it has no special meaning, you can adapt that to somewhere else.
|
||||
|
||||
All "foreign" cross-built binaries are sent into `/tmp/cross` so they cannot be confused for 'native' x86_64 stuff on your host machine in /usr/[local/]....
|
||||
|
||||
## Prepare the cmake toolchain file
|
||||
|
||||
1) `cd /tmp`
|
||||
|
||||
2) `wget -O mytoolchainfile https://raw.githubusercontent.com/warmcat/libwebsockets/master/contrib/cross-arm-linux-gnueabihf.cmake`
|
||||
|
||||
3) Edit `/tmp/mytoolchainfile` adapting `CROSS_PATH`, `CMAKE_C_COMPILER` and `CMAKE_CXX_COMPILER` to reflect your toolchain install dir and path to your toolchain C and C++ compilers respectively. For my case:
|
||||
|
||||
```
|
||||
set(CROSS_PATH /projects/aist-tb/arm-tc/)
|
||||
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-gcc")
|
||||
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-g++")
|
||||
```
|
||||
|
||||
## 1/4: Building libuv cross:
|
||||
|
||||
1) `export PATH=/projects/aist-tb/arm-tc/bin:$PATH` Notice there is a **/bin** on the end of the toolchain path
|
||||
|
||||
2) `cd /tmp ; mkdir cross` we will put the cross-built libs in /tmp/cross
|
||||
|
||||
3) `git clone https://github.com/libuv/libuv.git` get libuv
|
||||
|
||||
4) `cd libuv`
|
||||
|
||||
5) `./autogen.sh`
|
||||
|
||||
```
|
||||
+ libtoolize --copy
|
||||
libtoolize: putting auxiliary files in '.'.
|
||||
libtoolize: copying file './ltmain.sh'
|
||||
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
|
||||
libtoolize: copying file 'm4/libtool.m4'
|
||||
libtoolize: copying file 'm4/ltoptions.m4'
|
||||
libtoolize: copying file 'm4/ltsugar.m4'
|
||||
libtoolize: copying file 'm4/ltversion.m4'
|
||||
libtoolize: copying file 'm4/lt~obsolete.m4'
|
||||
+ aclocal -I m4
|
||||
+ autoconf
|
||||
+ automake --add-missing --copy
|
||||
configure.ac:38: installing './ar-lib'
|
||||
configure.ac:25: installing './compile'
|
||||
configure.ac:22: installing './config.guess'
|
||||
configure.ac:22: installing './config.sub'
|
||||
configure.ac:21: installing './install-sh'
|
||||
configure.ac:21: installing './missing'
|
||||
Makefile.am: installing './depcomp'
|
||||
```
|
||||
If it has problems, you will need to install `automake`, `libtool` etc.
|
||||
|
||||
6) `./configure --host=arm-linux-gnueabihf --prefix=/tmp/cross`
|
||||
|
||||
7) `make && make install` this will install to `/tmp/cross/...`
|
||||
|
||||
8) `file /tmp/cross/lib/libuv.so.1.0.0` Check it's really built for ARM
|
||||
```
|
||||
/tmp/cross/lib/libuv.so.1.0.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=cdde0bc945e51db6001a9485349c035baaec2b46, with debug_info, not stripped
|
||||
```
|
||||
|
||||
## 2/4: Building zlib cross
|
||||
|
||||
1) `cd /tmp`
|
||||
|
||||
2) `git clone https://github.com/madler/zlib.git`
|
||||
|
||||
3) `CC=arm-linux-gnueabihf-gcc ./configure --prefix=/tmp/cross`
|
||||
```
|
||||
Checking for shared library support...
|
||||
Building shared library libz.so.1.2.11 with arm-linux-gnueabihf-gcc.
|
||||
Checking for size_t... Yes.
|
||||
Checking for off64_t... Yes.
|
||||
Checking for fseeko... Yes.
|
||||
Checking for strerror... Yes.
|
||||
Checking for unistd.h... Yes.
|
||||
Checking for stdarg.h... Yes.
|
||||
Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf().
|
||||
Checking for vsnprintf() in stdio.h... Yes.
|
||||
Checking for return value of vsnprintf()... Yes.
|
||||
Checking for attribute(visibility) support... Yes.
|
||||
```
|
||||
|
||||
4) `make && make install`
|
||||
```
|
||||
arm-linux-gnueabihf-gcc -O3 -D_LARGEFILE64_SOURCE=1 -DHAVE_HIDDEN -I. -c -o example.o test/example.c
|
||||
...
|
||||
rm -f /tmp/cross/include/zlib.h /tmp/cross/include/zconf.h
|
||||
cp zlib.h zconf.h /tmp/cross/include
|
||||
chmod 644 /tmp/cross/include/zlib.h /tmp/cross/include/zconf.h
|
||||
```
|
||||
|
||||
5) `file /tmp/cross/lib/libz.so.1.2.11` This is just to confirm we built an ARM lib as expected
|
||||
```
|
||||
/tmp/cross/lib/libz.so.1.2.11: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=6f8ffef84389b1417d2fd1da1bd0c90f748f300d, with debug_info, not stripped
|
||||
```
|
||||
|
||||
## 3/4: Building mbedtls cross
|
||||
|
||||
1) `cd /tmp`
|
||||
|
||||
2) `git clone https://github.com/ARMmbed/mbedtls.git`
|
||||
|
||||
3) `cd mbedtls ; mkdir build ; cd build`
|
||||
|
||||
3) `cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile -DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross -DCMAKE_BUILD_TYPE=RELEASE -DUSE_SHARED_MBEDTLS_LIBRARY=1` mbedtls also uses cmake, so you can simply reuse the toolchain file you used for libwebsockets. That is why you shouldn't put project-specific options in the toolchain file, it should just describe the toolchain.
|
||||
|
||||
4) `make && make install`
|
||||
|
||||
5) `file /tmp/cross/lib/libmbedcrypto.so.2.6.0`
|
||||
```
|
||||
/tmp/cross/lib/libmbedcrypto.so.2.6.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=bcca195e78bd4fd2fb37f36ab7d72d477d609d87, with debug_info, not stripped
|
||||
```
|
||||
|
||||
## 4/4: Building libwebsockets with everything
|
||||
|
||||
1) `cd /tmp`
|
||||
|
||||
2) `git clone ssh://git@github.com/warmcat/libwebsockets`
|
||||
|
||||
3) `cd libwebsockets ; mkdir build ; cd build`
|
||||
|
||||
4) (this is all one line on the commandline)
|
||||
```
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross \
|
||||
-DLWS_WITH_LWSWS=1 \
|
||||
-DLWS_WITH_MBEDTLS=1 \
|
||||
-DLWS_MBEDTLS_LIBRARIES="/tmp/cross/lib/libmbedcrypto.so;/tmp/cross/lib/libmbedtls.so;/tmp/cross/lib/libmbedx509.so" \
|
||||
-DLWS_MBEDTLS_INCLUDE_DIRS=/tmp/cross/include \
|
||||
-DLWS_LIBUV_LIBRARIES=/tmp/cross/lib/libuv.so \
|
||||
-DLWS_LIBUV_INCLUDE_DIRS=/tmp/cross/include \
|
||||
-DLWS_ZLIB_LIBRARIES=/tmp/cross/lib/libz.so \
|
||||
-DLWS_ZLIB_INCLUDE_DIRS=/tmp/cross/include
|
||||
```
|
||||
|
||||
3) `make && make install`
|
||||
|
||||
4) `file /tmp/cross/lib/libwebsockets.so.11`
|
||||
```
|
||||
/tmp/cross/lib/libwebsockets.so.11: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=81e59c6534f8e9629a9fc9065c6e955ce96ca690, with debug_info, not stripped
|
||||
```
|
||||
|
||||
5) `arm-linux-gnueabihf-objdump -p /tmp/cross/lib/libwebsockets.so.11 | grep NEEDED` Confirm that the lws library was linked against everything we expect (libm / libc are provided by your toolchain)
|
||||
```
|
||||
NEEDED libz.so.1
|
||||
NEEDED libmbedcrypto.so.0
|
||||
NEEDED libmbedtls.so.10
|
||||
NEEDED libmbedx509.so.0
|
||||
NEEDED libuv.so.1
|
||||
NEEDED libm.so.6
|
||||
NEEDED libc.so.6
|
||||
```
|
||||
|
||||
You will also find the lws test apps in `/tmp/cross/bin`... to run lws on the target you will need to copy the related things from /tmp/cross... all the .so from /tmp/cross/lib and anything from /tmp/cross/bin you want.
|
||||
|
||||
@section mem Memory efficiency
|
||||
|
||||
Embedded server-only configuration without extensions (ie, no compression
|
||||
on websocket connections), but with full v13 websocket features and http
|
||||
server, built on ARM Cortex-A9:
|
||||
|
||||
Update at 8dac94d (2013-02-18)
|
||||
```
|
||||
$ ./configure --without-client --without-extensions --disable-debug --without-daemonize
|
||||
|
||||
Context Creation, 1024 fd limit[2]: 16720 (includes 12 bytes per fd)
|
||||
Per-connection [3]: 72 bytes, +1328 during headers
|
||||
|
||||
.text .rodata .data .bss
|
||||
11512 2784 288 4
|
||||
```
|
||||
This shows the impact of the major configuration with/without options at
|
||||
13ba5bbc633ea962d46d using Ubuntu ARM on a PandaBoard ES.
|
||||
|
||||
These are accounting for static allocations from the library elf, there are
|
||||
additional dynamic allocations via malloc. These are a bit old now but give
|
||||
the right idea for relative "expense" of features.
|
||||
|
||||
Static allocations, ARM9
|
||||
|
||||
| | .text | .rodata | .data | .bss |
|
||||
|--------------------------------|---------|---------|-------|------|
|
||||
| All (no without) | 35024 | 9940 | 336 | 4104 |
|
||||
| without client | 25684 | 7144 | 336 | 4104 |
|
||||
| without client, exts | 21652 | 6288 | 288 | 4104 |
|
||||
| without client, exts, debug[1] | 19756 | 3768 | 288 | 4104 |
|
||||
| without server | 30304 | 8160 | 336 | 4104 |
|
||||
| without server, exts | 25382 | 7204 | 288 | 4104 |
|
||||
| without server, exts, debug[1] | 23712 | 4256 | 288 | 4104 |
|
||||
|
||||
[1] `--disable-debug` only removes messages below `lwsl_notice`. Since that is
|
||||
the default logging level the impact is not noticeable, error, warn and notice
|
||||
logs are all still there.
|
||||
|
||||
[2] `1024` fd per process is the default limit (set by ulimit) in at least Fedora
|
||||
and Ubuntu. You can make significant savings tailoring this to actual expected
|
||||
peak fds, ie, at a limit of `20`, context creation allocation reduces to `4432 +
|
||||
240 = 4672`)
|
||||
|
||||
[3] known header content is freed after connection establishment
|
|
@ -1,70 +1,39 @@
|
|||
Notes about coding with lws
|
||||
===========================
|
||||
|
||||
@section dae Daemonization
|
||||
@section era Old lws and lws v2.0
|
||||
|
||||
There's a helper api `lws_daemonize` built by default that does everything you
|
||||
need to daemonize well, including creating a lock file. If you're making
|
||||
what's basically a daemon, just call this early in your init to fork to a
|
||||
headless background process and exit the starting process.
|
||||
Originally lws only supported the "manual" method of handling everything in the
|
||||
user callback found in test-server.c / test-server-http.c.
|
||||
|
||||
Notice stdout, stderr, stdin are all redirected to /dev/null to enforce your
|
||||
daemon is headless, so you'll need to sort out alternative logging, by, eg,
|
||||
syslog.
|
||||
Since v2.0, the need for most or all of this manual boilerplate has been
|
||||
eliminated: the protocols[0] http stuff is provided by a generic lib export
|
||||
`lws_callback_http_dummy()`. You can serve parts of your filesystem at part of
|
||||
the URL space using mounts, the dummy http callback will do the right thing.
|
||||
|
||||
It's much preferred to use the "automated" v2.0 type scheme, because it's less
|
||||
code and it's easier to support.
|
||||
|
||||
@section conns Maximum number of connections
|
||||
You can see an example of the new way in test-server-v2.0.c.
|
||||
|
||||
The maximum number of connections the library can deal with is decided when
|
||||
it starts by querying the OS to find out how many file descriptors it is
|
||||
allowed to open (1024 on Fedora for example). It then allocates arrays that
|
||||
allow up to that many connections, minus whatever other file descriptors are
|
||||
in use by the user code.
|
||||
If you just need generic serving capability, without the need to integrate lws
|
||||
to some other app, consider not writing any server code at all, and instead use
|
||||
the generic server `lwsws`, and writing your special user code in a standalone
|
||||
"plugin". The server is configured for mounts etc using JSON, see
|
||||
./READMEs/README.lwsws.md.
|
||||
|
||||
If you want to restrict that allocation, or increase it, you can use ulimit or
|
||||
similar to change the available number of file descriptors, and when restarted
|
||||
**libwebsockets** will adapt accordingly.
|
||||
Although the "plugins" are dynamically loaded if you use lwsws or lws built
|
||||
with libuv, actually they may perfectly well be statically included if that
|
||||
suits your situation better, eg, ESP32 test server, where the platform does
|
||||
not support processes or dynamic loading, just #includes the plugins
|
||||
one after the other and gets the same benefit from the same code.
|
||||
|
||||
Isolating and collating the protocol code in one place also makes it very easy
|
||||
to maintain and understand.
|
||||
|
||||
@section evtloop Libwebsockets is singlethreaded
|
||||
|
||||
Libwebsockets works in a serialized event loop, in a single thread.
|
||||
|
||||
Directly performing websocket actions from other threads is not allowed.
|
||||
Aside from the internal data being inconsistent in `forked()` processes,
|
||||
the scope of a `wsi` (`struct websocket`) can end at any time during service
|
||||
with the socket closing and the `wsi` freed.
|
||||
|
||||
Websocket write activities should only take place in the
|
||||
`LWS_CALLBACK_SERVER_WRITEABLE` callback as described below.
|
||||
|
||||
[This network-programming necessity to link the issue of new data to
|
||||
the peer taking the previous data is not obvious to all users so let's
|
||||
repeat that in other words:
|
||||
|
||||
***ONLY DO LWS_WRITE FROM THE WRITEABLE CALLBACK***
|
||||
|
||||
There is another network-programming truism that surprises some people which
|
||||
is if the sink for the data cannot accept more:
|
||||
|
||||
***YOU MUST PERFORM RX FLOW CONTROL***
|
||||
|
||||
See the mirror protocol implementations for example code.
|
||||
|
||||
Only live connections appear in the user callbacks, so this removes any
|
||||
possibility of trying to used closed and freed wsis.
|
||||
|
||||
If you need to service other socket or file descriptors as well as the
|
||||
websocket ones, you can combine them together with the websocket ones
|
||||
in one poll loop, see "External Polling Loop support" below, and
|
||||
still do it all in one thread / process context.
|
||||
|
||||
If you insist on trying to use it from multiple threads, take special care if
|
||||
you might simultaneously create more than one context from different threads.
|
||||
|
||||
SSL_library_init() is called from the context create api and it also is not
|
||||
reentrant. So at least create the contexts sequentially.
|
||||
|
||||
So it if highly recommended you put your protocol-specific code into the
|
||||
form of a "plugin" at the source level, even if you have no immediate plan to
|
||||
use it dynamically-loaded.
|
||||
|
||||
@section writeable Only send data when socket writeable
|
||||
|
||||
|
@ -72,10 +41,10 @@ You should only send data on a websocket connection from the user callback
|
|||
`LWS_CALLBACK_SERVER_WRITEABLE` (or `LWS_CALLBACK_CLIENT_WRITEABLE` for
|
||||
clients).
|
||||
|
||||
If you want to send something, do not just send it but request a callback
|
||||
If you want to send something, do NOT just send it but request a callback
|
||||
when the socket is writeable using
|
||||
|
||||
- `lws_callback_on_writable(context, wsi)` for a specific `wsi`, or
|
||||
- `lws_callback_on_writable(wsi)` for a specific `wsi`, or
|
||||
|
||||
- `lws_callback_on_writable_all_protocol(protocol)` for all connections
|
||||
using that protocol to get a callback when next writeable.
|
||||
|
@ -87,6 +56,36 @@ in the ...WRITEABLE callback.
|
|||
|
||||
See the test server code for an example of how to do this.
|
||||
|
||||
Otherwise evolved libs like libuv get this wrong, they will allow you to "send"
|
||||
anything you want but it only uses up your local memory (and costs you
|
||||
memcpys) until the socket can actually accept it. It is much better to regulate
|
||||
your send action by the downstream peer readiness to take new data in the first
|
||||
place, avoiding all the wasted buffering.
|
||||
|
||||
Libwebsockets' concept is that the downstream peer is truly the boss, if he,
|
||||
or our connection to him, cannot handle anything new, we should not generate
|
||||
anything new for him. This is how unix shell piping works, you may have
|
||||
`cat a.txt | grep xyz > remote", but actually that does not cat anything from
|
||||
a.txt while remote cannot accept anything new.
|
||||
|
||||
@section oneper Only one lws_write per WRITEABLE callback
|
||||
|
||||
From v2.5, lws strictly enforces only one lws_write() per WRITEABLE callback.
|
||||
|
||||
You will receive a message about "Illegal back-to-back write of ... detected"
|
||||
if there is a second lws_write() before returning to the event loop.
|
||||
|
||||
This is because with http/2, the state of the network connection carrying a
|
||||
wsi is unrelated to any state of the wsi. The situation on http/1 where a
|
||||
new request implied a new tcp connection and new SSL buffer, so you could
|
||||
assume some window for writes is no longer true. Any lws_write() can fail
|
||||
and be buffered for completion by lws; it will be auto-completed by the
|
||||
event loop.
|
||||
|
||||
Note that if you are handling your own http responses, writing the headers
|
||||
needs to be done with a separate lws_write() from writing any payload. That
|
||||
means after writing the headers you must call `lws_callback_on_writable(wsi)`
|
||||
and send any payload from the writable callback.
|
||||
|
||||
@section otherwr Do not rely on only your own WRITEABLE requests appearing
|
||||
|
||||
|
@ -100,6 +99,176 @@ came it really is time to send something.
|
|||
It's quite possible you get an 'extra' writeable callback at any time and
|
||||
just need to `return 0` and wait for the expected callback later.
|
||||
|
||||
@section dae Daemonization
|
||||
|
||||
There's a helper api `lws_daemonize` built by default that does everything you
|
||||
need to daemonize well, including creating a lock file. If you're making
|
||||
what's basically a daemon, just call this early in your init to fork to a
|
||||
headless background process and exit the starting process.
|
||||
|
||||
Notice stdout, stderr, stdin are all redirected to /dev/null to enforce your
|
||||
daemon is headless, so you'll need to sort out alternative logging, by, eg,
|
||||
syslog via `lws_set_log_level(..., lwsl_emit_syslog)`.
|
||||
|
||||
@section conns Maximum number of connections
|
||||
|
||||
The maximum number of connections the library can deal with is decided when
|
||||
it starts by querying the OS to find out how many file descriptors it is
|
||||
allowed to open (1024 on Fedora for example). It then allocates arrays that
|
||||
allow up to that many connections, minus whatever other file descriptors are
|
||||
in use by the user code.
|
||||
|
||||
If you want to restrict that allocation, or increase it, you can use ulimit or
|
||||
similar to change the available number of file descriptors, and when restarted
|
||||
**libwebsockets** will adapt accordingly.
|
||||
|
||||
@section peer_limits optional LWS_WITH_PEER_LIMITS
|
||||
|
||||
If you select `LWS_WITH_PEER_LIMITS` at cmake, then lws will track peer IPs
|
||||
and monitor how many connections and ah resources they are trying to use
|
||||
at one time. You can choose to limit these at context creation time, using
|
||||
`info.ip_limit_ah` and `info.ip_limit_wsi`.
|
||||
|
||||
Note that although the ah limit is 'soft', ie, the connection will just wait
|
||||
until the IP is under the ah limit again before attaching a new ah, the
|
||||
wsi limit is 'hard', lws will drop any additional connections from the
|
||||
IP until it's under the limit again.
|
||||
|
||||
If you use these limits, you should consider multiple clients may simultaneously
|
||||
try to access the site through NAT, etc. So the limits should err on the side
|
||||
of being generous, while still making it impossible for one IP to exhaust
|
||||
all the server resources.
|
||||
|
||||
@section evtloop Libwebsockets is singlethreaded
|
||||
|
||||
Libwebsockets works in a serialized event loop, in a single thread. It supports
|
||||
not only the default poll() backend, but libuv, libev, and libevent event loop
|
||||
libraries that also take this locking-free, nonblocking event loop approach that
|
||||
is not threadsafe. There are several advantages to this technique, but one
|
||||
disadvantage, it doesn't integrate easily if there are multiple threads that
|
||||
want to use libwebsockets.
|
||||
|
||||
However integration to multithreaded apps is possible if you follow some guidelines.
|
||||
|
||||
1) Aside from two APIs, directly calling lws apis from other threads is not allowed.
|
||||
|
||||
2) If you want to keep a list of live wsi, you need to use lifecycle callbacks on
|
||||
the protocol in the service thread to manage the list, with your own locking.
|
||||
Typically you use an ESTABLISHED callback to add ws wsi to your list and a CLOSED
|
||||
callback to remove them.
|
||||
|
||||
3) LWS regulates your write activity by being able to let you know when you may
|
||||
write more on a connection. That reflects the reality that you cannot succeed to
|
||||
send data to a peer that has no room for it, so you should not generate or buffer
|
||||
write data until you know the peer connection can take more.
|
||||
|
||||
Other libraries pretend that the guy doing the writing is the boss who decides
|
||||
what happens, and absorb as much as you want to write to local buffering. That does
|
||||
not scale to a lot of connections, because it will exhaust your memory and waste
|
||||
time copying data around in memory needlessly.
|
||||
|
||||
The truth is the receiver, along with the network between you, is the boss who
|
||||
decides what will happen. If he stops accepting data, no data will move. LWS is
|
||||
designed to reflect that.
|
||||
|
||||
If you have something to send, you call `lws_callback_on_writable()` on the
|
||||
connection, and when it is writeable, you will get a `LWS_CALLBACK_SERVER_WRITEABLE`
|
||||
callback, where you should generate the data to send and send it with `lws_write()`.
|
||||
|
||||
You cannot send data using `lws_write()` outside of the WRITEABLE callback.
|
||||
|
||||
4) For multithreaded apps, this corresponds to a need to be able to provoke the
|
||||
`lws_callback_on_writable()` action and to wake the service thread from its event
|
||||
loop wait (sleeping in `poll()` or `epoll()` or whatever). The rules above
|
||||
mean directly sending data on the connection from another thread is out of the
|
||||
question.
|
||||
|
||||
Therefore the two apis mentioned above that may be used from another thread are
|
||||
|
||||
- For LWS using the default poll() event loop, `lws_callback_on_writable()`
|
||||
|
||||
- For LWS using libuv/libev/libevent event loop, `lws_cancel_service()`
|
||||
|
||||
If you are using the default poll() event loop, one "foreign thread" at a time may
|
||||
call `lws_callback_on_writable()` directly for a wsi. You need to use your own
|
||||
locking around that to serialize multiple thread access to it.
|
||||
|
||||
If you implement LWS_CALLBACK_GET_THREAD_ID in protocols[0], then LWS will detect
|
||||
when it has been called from a foreign thread and automatically use
|
||||
`lws_cancel_service()` to additionally wake the service loop from its wait.
|
||||
|
||||
For libuv/libev/libevent event loop, they cannot handle being called from other
|
||||
threads. So there is a slightly different scheme, you may call `lws_cancel_service()`
|
||||
to force the event loop to end immediately. This then broadcasts a callback (in the
|
||||
service thread context) `LWS_CALLBACK_EVENT_WAIT_CANCELLED`, to all protocols on all
|
||||
vhosts, where you can perform your own locking and walk a list of wsi that need
|
||||
`lws_callback_on_writable()` calling on them.
|
||||
|
||||
`lws_cancel_service()` is very cheap to call.
|
||||
|
||||
5) The obverse of this truism about the receiver being the boss is the case where
|
||||
we are receiving. If we get into a situation we actually can't usefully
|
||||
receive any more, perhaps because we are passing the data on and the guy we want
|
||||
to send to can't receive any more, then we should "turn off RX" by using the
|
||||
RX flow control API, `lws_rx_flow_control(wsi, 0)`. When something happens where we
|
||||
can accept more RX, (eg, we learn our onward connection is writeable) we can call
|
||||
it again to re-enable it on the incoming wsi.
|
||||
|
||||
LWS stops calling back about RX immediately you use flow control to disable RX, it
|
||||
buffers the data internally if necessary. So you will only see RX when you can
|
||||
handle it. When flow control is disabled, LWS stops taking new data in... this makes
|
||||
the situation known to the sender by TCP "backpressure", the tx window fills and the
|
||||
sender finds he cannot write any more to the connection.
|
||||
|
||||
See the mirror protocol implementations for example code.
|
||||
|
||||
If you need to service other socket or file descriptors as well as the
|
||||
websocket ones, you can combine them together with the websocket ones
|
||||
in one poll loop, see "External Polling Loop support" below, and
|
||||
still do it all in one thread / process context. If the need is less
|
||||
architectural, you can also create RAW mode client and serving sockets; this
|
||||
is how the lws plugin for the ssh server works.
|
||||
|
||||
@section anonprot Working without a protocol name
|
||||
|
||||
Websockets allows connections to negotiate without a protocol name...
|
||||
in that case by default it will bind to the first protocol in your
|
||||
vhost protocols[] array.
|
||||
|
||||
You can tell the vhost to use a different protocol by attaching a
|
||||
pvo (per-vhost option) to the
|
||||
|
||||
```
|
||||
/*
|
||||
* this sets a per-vhost, per-protocol option name:value pair
|
||||
* the effect is to set this protocol to be the default one for the vhost,
|
||||
* ie, selected if no Protocol: header is sent with the ws upgrade.
|
||||
*/
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_opt = {
|
||||
NULL,
|
||||
NULL,
|
||||
"default",
|
||||
"1"
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo = {
|
||||
NULL,
|
||||
&pvo_opt,
|
||||
"my-protocol",
|
||||
""
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
context_info.pvo = &pvo;
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Will select "my-protocol" from your protocol list (even if it came
|
||||
in by plugin) as being the target of client connections that don't
|
||||
specify a protocol.
|
||||
|
||||
@section closing Closing connections from the user side
|
||||
|
||||
|
@ -220,10 +389,9 @@ external polling array. That's needed if **libwebsockets** will
|
|||
cooperate with an existing poll array maintained by another
|
||||
server.
|
||||
|
||||
Four callbacks `LWS_CALLBACK_ADD_POLL_FD`, `LWS_CALLBACK_DEL_POLL_FD`,
|
||||
`LWS_CALLBACK_SET_MODE_POLL_FD` and `LWS_CALLBACK_CLEAR_MODE_POLL_FD`
|
||||
appear in the callback for protocol 0 and allow interface code to
|
||||
manage socket descriptors in other poll loops.
|
||||
Three callbacks `LWS_CALLBACK_ADD_POLL_FD`, `LWS_CALLBACK_DEL_POLL_FD`
|
||||
and `LWS_CALLBACK_CHANGE_MODE_POLL_FD` appear in the callback for protocol 0
|
||||
and allow interface code to manage socket descriptors in other poll loops.
|
||||
|
||||
You can pass all pollfds that need service to `lws_service_fd()`, even
|
||||
if the socket or file does not belong to **libwebsockets** it is safe.
|
||||
|
@ -244,6 +412,26 @@ reflecting the real event:
|
|||
- use LWS_POLLHUP / LWS_POLLIN / LWS_POLLOUT from libwebsockets.h to avoid
|
||||
losing windows compatibility
|
||||
|
||||
You also need to take care about "forced service" somehow... these are cases
|
||||
where the network event was consumed, incoming data was all read, for example,
|
||||
but the work arising from it was not completed. There will not be any more
|
||||
network event to trigger the remaining work, Eg, we read compressed data, but
|
||||
we did not use up all the decompressed data before returning to the event loop
|
||||
because we had to write some of it.
|
||||
|
||||
Lws provides an API to determine if anyone is waiting for forced service,
|
||||
`lws_service_adjust_timeout(context, 1, tsi)`, normally tsi is 0. If it returns
|
||||
0, then at least one connection has pending work you can get done by calling
|
||||
`lws_service_tsi(context, -1, tsi)`, again normally tsi is 0.
|
||||
|
||||
For eg, the default poll() event loop, or libuv/ev/event, lws does this
|
||||
checking for you and handles it automatically. But in the external polling
|
||||
loop case, you must do it explicitly. Handling it after every normal service
|
||||
triggered by the external poll fd should be enough, since the situations needing
|
||||
it are initially triggered by actual network events.
|
||||
|
||||
An example of handling it is shown in the test-server code specific to
|
||||
external polling.
|
||||
|
||||
@section cpp Using with in c++ apps
|
||||
|
||||
|
@ -251,7 +439,7 @@ The library is ready for use by C++ apps. You can get started quickly by
|
|||
copying the test server
|
||||
|
||||
```
|
||||
$ cp test-server/test-server.c test.cpp
|
||||
$ cp test-apps/test-server.c test.cpp
|
||||
```
|
||||
|
||||
and building it in C++ like this
|
||||
|
@ -278,6 +466,75 @@ isn't processed by user code before then should be copied out for later.
|
|||
For HTTP connections that don't upgrade, header info remains available the
|
||||
whole time.
|
||||
|
||||
@section http2compat Code Requirements for HTTP/2 compatibility
|
||||
|
||||
Websocket connections only work over http/1, so there is nothing special to do
|
||||
when you want to enable -DLWS_WITH_HTTP2=1.
|
||||
|
||||
The internal http apis already follow these requirements and are compatible with
|
||||
http/2 already. So if you use stuff like mounts and serve stuff out of the
|
||||
filesystem, there's also nothing special to do.
|
||||
|
||||
However if you are getting your hands dirty with writing response headers, or
|
||||
writing bulk data over http/2, you need to observe these rules so that it will
|
||||
work over both http/1.x and http/2 the same.
|
||||
|
||||
1) LWS_PRE requirement applies on ALL lws_write(). For http/1, you don't have
|
||||
to take care of LWS_PRE for http data, since it is just sent straight out.
|
||||
For http/2, it will write up to LWS_PRE bytes behind the buffer start to create
|
||||
the http/2 frame header.
|
||||
|
||||
This has implications if you treated the input buffer to lws_write() as const...
|
||||
it isn't any more with http/2, up to 9 bytes behind the buffer will be trashed.
|
||||
|
||||
2) Headers are encoded using a sophisticated scheme in http/2. The existing
|
||||
header access apis are already made compatible for incoming headers,
|
||||
for outgoing headers you must:
|
||||
|
||||
- observe the LWS_PRE buffer requirement mentioned above
|
||||
|
||||
- Use `lws_add_http_header_status()` to add the transaction status (200 etc)
|
||||
|
||||
- use lws apis `lws_add_http_header_by_name()` and `lws_add_http_header_by_token()`
|
||||
to put the headers into the buffer (these will translate what is actually
|
||||
written to the buffer depending on if the connection is in http/2 mode or not)
|
||||
|
||||
- use the `lws api lws_finalize_http_header()` api after adding the last
|
||||
response header
|
||||
|
||||
- write the header using lws_write(..., `LWS_WRITE_HTTP_HEADERS`);
|
||||
|
||||
3) http/2 introduces per-stream transmit credit... how much more you can send
|
||||
on a stream is decided by the peer. You start off with some amount, as the
|
||||
stream sends stuff lws will reduce your credit accordingly, when it reaches
|
||||
zero, you must not send anything further until lws receives "more credit" for
|
||||
that stream the peer. Lws will suppress writable callbacks if you hit 0 until
|
||||
more credit for the stream appears, and lws built-in file serving (via mounts
|
||||
etc) already takes care of observing the tx credit restrictions. However if
|
||||
you write your own code that wants to send http data, you must consult the
|
||||
`lws_get_peer_write_allowance()` api to find out the state of your tx credit.
|
||||
For http/1, it will always return (size_t)-1, ie, no limit.
|
||||
|
||||
This is orthogonal to the question of how much space your local side's kernel
|
||||
will make to buffer your send data on that connection. So although the result
|
||||
from `lws_get_peer_write_allowance()` is "how much you can send" logically,
|
||||
and may be megabytes if the peer allows it, you should restrict what you send
|
||||
at one time to whatever your machine will generally accept in one go, and
|
||||
further reduce that amount if `lws_get_peer_write_allowance()` returns
|
||||
something smaller. If it returns 0, you should not consume or send anything
|
||||
and return having asked for callback on writable, it will only come back when
|
||||
more tx credit has arrived for your stream.
|
||||
|
||||
4) Header names with captital letters are illegal in http/2. Header names in
|
||||
http/1 are case insensitive. So if you generate headers by name, change all
|
||||
your header name strings to lower-case to be compatible both ways.
|
||||
|
||||
5) Chunked Transfer-encoding is illegal in http/2, http/2 peers will actively
|
||||
reject it. Lws takes care of removing the header and converting CGIs that
|
||||
emit chunked into unchunked automatically for http/2 connections.
|
||||
|
||||
If you follow these rules, your code will automatically work with both http/1.x
|
||||
and http/2.
|
||||
|
||||
@section ka TCP Keepalive
|
||||
|
||||
|
@ -321,6 +578,20 @@ if left `NULL`, then the "DEFAULT" set of ciphers are all possible to select.
|
|||
You can also set it to `"ALL"` to allow everything (including insecure ciphers).
|
||||
|
||||
|
||||
@section sslcerts Passing your own cert information direct to SSL_CTX
|
||||
|
||||
For most users it's enough to pass the SSL certificate and key information by
|
||||
giving filepaths to the info.ssl_cert_filepath and info.ssl_private_key_filepath
|
||||
members when creating the vhost.
|
||||
|
||||
If you want to control that from your own code instead, you can do so by leaving
|
||||
the related info members NULL, and setting the info.options flag
|
||||
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX at vhost creation time. That will create
|
||||
the vhost SSL_CTX without any certificate, and allow you to use the callback
|
||||
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS to add your certificate to
|
||||
the SSL_CTX directly. The vhost SSL_CTX * is in the user parameter in that
|
||||
callback.
|
||||
|
||||
@section clientasync Async nature of client connections
|
||||
|
||||
When you call `lws_client_connect_info(..)` and get a `wsi` back, it does not
|
||||
|
@ -337,7 +608,7 @@ other reasons, if any of that happens you'll get a
|
|||
After attempting the connection and getting back a non-`NULL` `wsi` you should
|
||||
loop calling `lws_service()` until one of the above callbacks occurs.
|
||||
|
||||
As usual, see [test-client.c](test-server/test-client.c) for example code.
|
||||
As usual, see [test-client.c](../test-apps/test-client.c) for example code.
|
||||
|
||||
Notice that the client connection api tries to progress the connection
|
||||
somewhat before returning. That means it's possible to get callbacks like
|
||||
|
@ -472,7 +743,9 @@ callbacks on the named protocol
|
|||
|
||||
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
|
||||
|
||||
`protocol-lws-raw-test` plugin provides a method for testing this with
|
||||
The minimal example `raw/minimal-raw-file` demonstrates how to use it.
|
||||
|
||||
`protocol-lws-raw-test` plugin also provides a method for testing this with
|
||||
`libwebsockets-test-server-v2.0`:
|
||||
|
||||
The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
|
||||
|
@ -556,6 +829,46 @@ and in another window, connect to it using the test client
|
|||
The connection should succeed, and text typed in the netcat window (including a CRLF)
|
||||
will be received in the client.
|
||||
|
||||
@section rawudp RAW UDP socket integration
|
||||
|
||||
Lws provides an api to create, optionally bind, and adopt a RAW UDP
|
||||
socket (RAW here means an uninterpreted normal UDP socket, not a
|
||||
"raw socket").
|
||||
|
||||
```
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi);
|
||||
```
|
||||
|
||||
`flags` should be `LWS_CAUDP_BIND` if the socket will receive packets.
|
||||
|
||||
The callbacks `LWS_CALLBACK_RAW_ADOPT`, `LWS_CALLBACK_RAW_CLOSE`,
|
||||
`LWS_CALLBACK_RAW_RX` and `LWS_CALLBACK_RAW_WRITEABLE` apply to the
|
||||
wsi. But UDP is different than TCP in some fundamental ways.
|
||||
|
||||
For receiving on a UDP connection, data becomes available at
|
||||
`LWS_CALLBACK_RAW_RX` as usual, but because there is no specific
|
||||
connection with UDP, it is necessary to also get the source address of
|
||||
the data separately, using `struct lws_udp * lws_get_udp(wsi)`.
|
||||
You should take a copy of the `struct lws_udp` itself (not the
|
||||
pointer) and save it for when you want to write back to that peer.
|
||||
|
||||
Writing is also a bit different for UDP. By default, the system has no
|
||||
idea about the receiver state and so asking for a `callback_on_writable()`
|
||||
always believes that the socket is writeable... the callback will
|
||||
happen next time around the event loop.
|
||||
|
||||
With UDP, there is no single "connection". You need to write with sendto() and
|
||||
direct the packets to a specific destination. To return packets to a
|
||||
peer who sent something earlier and you copied his `struct lws_udp`, you
|
||||
use the .sa and .salen members as the last two parameters of the sendto().
|
||||
|
||||
The kernel may not accept to buffer / write everything you wanted to send.
|
||||
So you are responsible to watch the result of sendto() and resend the
|
||||
unsent part next time (which may involve adding new protocol headers to
|
||||
the remainder depending on what you are doing).
|
||||
|
||||
@section ecdh ECDH Support
|
||||
|
||||
ECDH Certs are now supported. Enable the CMake option
|
||||
|
@ -598,7 +911,7 @@ Returning nonzero from the callback will close the wsi.
|
|||
|
||||
SMP support is integrated into LWS without any internal threading. It's
|
||||
very simple to use, libwebsockets-test-server-pthread shows how to do it,
|
||||
use -j <n> argument there to control the number of service threads up to 32.
|
||||
use -j n argument there to control the number of service threads up to 32.
|
||||
|
||||
Two new members are added to the info struct
|
||||
|
||||
|
@ -628,35 +941,74 @@ You can set fd_limit_per_thread to a nonzero number to control this manually, eg
|
|||
the overall supported fd limit is less than the process allowance.
|
||||
|
||||
You can control the context basic data allocation for multithreading from Cmake
|
||||
using -DLWS_MAX_SMP=, if not given it's set to 32. The serv_buf allocation
|
||||
using -DLWS_MAX_SMP=, if not given it's set to 1. The serv_buf allocation
|
||||
for the threads (currently 4096) is made at runtime only for active threads.
|
||||
|
||||
Because lws will limit the requested number of actual threads supported
|
||||
according to LWS_MAX_SMP, there is an api lws_get_count_threads(context) to
|
||||
discover how many threads were actually allowed when the context was created.
|
||||
|
||||
It's required to implement locking in the user code in the same way that
|
||||
libwebsockets-test-server-pthread does it, for the FD locking callbacks.
|
||||
See the test-server-pthreads.c sample for how to use.
|
||||
|
||||
There is no knowledge or dependency in lws itself about pthreads. How the
|
||||
locking is implemented is entirely up to the user code.
|
||||
@section smplocking SMP Locking Helpers
|
||||
|
||||
Lws provide a set of pthread mutex helpers that reduce to no code or
|
||||
variable footprint in the case that LWS_MAX_SMP == 1.
|
||||
|
||||
Define your user mutex like this
|
||||
|
||||
```
|
||||
lws_pthread_mutex(name);
|
||||
```
|
||||
|
||||
If LWS_MAX_SMP > 1, this produces `pthread_mutex_t name;`. In the case
|
||||
LWS_MAX_SMP == 1, it produces nothing.
|
||||
|
||||
Likewise these helpers for init, destroy, lock and unlock
|
||||
|
||||
|
||||
@section libevuv Libev / Libuv support
|
||||
```
|
||||
void lws_pthread_mutex_init(pthread_mutex_t *lock)
|
||||
void lws_pthread_mutex_destroy(pthread_mutex_t *lock)
|
||||
void lws_pthread_mutex_lock(pthread_mutex_t *lock)
|
||||
void lws_pthread_mutex_unlock(pthread_mutex_t *lock)
|
||||
```
|
||||
|
||||
resolve to nothing if LWS_MAX_SMP == 1, otherwise produce the equivalent
|
||||
pthread api.
|
||||
|
||||
pthreads is required in lws only if LWS_MAX_SMP > 1.
|
||||
|
||||
|
||||
@section libevuv libev / libuv / libevent support
|
||||
|
||||
You can select either or both
|
||||
|
||||
-DLWS_WITH_LIBEV=1
|
||||
-DLWS_WITH_LIBUV=1
|
||||
-DLWS_WITH_LIBEVENT=1
|
||||
|
||||
at cmake configure-time. The user application may use one of the
|
||||
context init options flags
|
||||
|
||||
LWS_SERVER_OPTION_LIBEV
|
||||
LWS_SERVER_OPTION_LIBUV
|
||||
LWS_SERVER_OPTION_LIBEVENT
|
||||
|
||||
to indicate it will use either of the event libraries.
|
||||
to indicate it will use one of the event libraries at runtime.
|
||||
|
||||
libev has some problems, its headers conflict with libevent, they both define
|
||||
critical constants like EV_READ to different values. Attempts
|
||||
to discuss clearing that up with libevent and libev did not get anywhere useful.
|
||||
|
||||
In addition building anything with libev using gcc spews warnings, the
|
||||
maintainer is aware of this for many years, and blames gcc. We worked
|
||||
around this by disabling -Werror on the parts of lws that use libev.
|
||||
|
||||
For these reasons and the response I got trying to raise these issues with
|
||||
them, if you have a choice about event loop, I would gently encourage you
|
||||
to avoid libev. Where lws uses an event loop itself, eg in lwsws, we use
|
||||
libuv.
|
||||
|
||||
@section extopts Extension option control from user code
|
||||
|
||||
|
@ -728,7 +1080,41 @@ prepare the client SSL context for the vhost after creating the vhost, since
|
|||
this is not normally done if the vhost was set up to listen / serve. Call
|
||||
the api lws_init_vhost_client_ssl() to also allow client SSL on the vhost.
|
||||
|
||||
@section clipipe Pipelining Client Requests to same host
|
||||
|
||||
If you are opening more client requests to the same host and port, you
|
||||
can give the flag LCCSCF_PIPELINE on `info.ssl_connection` to indicate
|
||||
you wish to pipeline them.
|
||||
|
||||
Without the flag, the client connections will occur concurrently using a
|
||||
socket and tls wrapper if requested for each connection individually.
|
||||
That is fast, but resource-intensive.
|
||||
|
||||
With the flag, lws will queue subsequent client connections on the first
|
||||
connection to the same host and port. When it has confirmed from the
|
||||
first connection that pipelining / keep-alive is supported by the server,
|
||||
it lets the queued client pipeline connections send their headers ahead
|
||||
of time to create a pipeline of requests on the server side.
|
||||
|
||||
In this way only one tcp connection and tls wrapper is required to transfer
|
||||
all the transactions sequentially. It takes a little longer but it
|
||||
can make a significant difference to resources on both sides.
|
||||
|
||||
If lws learns from the first response header that keepalive is not possible,
|
||||
then it marks itself with that information and detaches any queued clients
|
||||
to make their own individual connections as a fallback.
|
||||
|
||||
Lws can also intelligently combine multiple ongoing client connections to
|
||||
the same host and port into a single http/2 connection with multiple
|
||||
streams if the server supports it.
|
||||
|
||||
Unlike http/1 pipelining, with http/2 the client connections all occur
|
||||
simultaneously using h2 stream multiplexing inside the one tcp + tls
|
||||
connection.
|
||||
|
||||
You can turn off the h2 client support either by not building lws with
|
||||
`-DLWS_WITH_HTTP2=1` or giving the `LCCSCF_NOT_H2` flag in the client
|
||||
connection info struct `ssl_connection` member.
|
||||
|
||||
@section vhosts Using lws vhosts
|
||||
|
||||
|
@ -888,6 +1274,15 @@ This allocation is only deleted / replaced when the connection accesses a
|
|||
URL region with a different protocol (or the default protocols[0] if no
|
||||
CALLBACK area matches it).
|
||||
|
||||
This "binding connection to a protocol" lifecycle in managed by
|
||||
`LWS_CALLBACK_HTTP_BIND_PROTOCOL` and `LWS_CALLBACK_HTTP_DROP_PROTOCOL`.
|
||||
Because of HTTP/1.1 connection pipelining, one connection may perform
|
||||
many transactions, each of which may map to different URLs and need
|
||||
binding to different protocols. So these messages are used to
|
||||
create the binding of the wsi to your protocol including any
|
||||
allocations, and to destroy the binding, at which point you should
|
||||
destroy any related allocations.
|
||||
|
||||
@section BINDTODEV SO_BIND_TO_DEVICE
|
||||
|
||||
The .bind_iface flag in the context / vhost creation struct lets you
|
||||
|
@ -930,7 +1325,7 @@ also add this to your own html easily
|
|||
|
||||
- include lws-common.js from your HEAD section
|
||||
|
||||
<script src="/lws-common.js"></script>
|
||||
\<script src="/lws-common.js">\</script>
|
||||
|
||||
- dim the page during initialization, in a script section on your page
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
ESP32 Support
|
||||
=============
|
||||
|
||||
See \ref esp32 for details on how to build lws as a component in an ESP-IDF project.
|
||||
|
||||
Lws provides a "factory" application
|
||||
|
||||
https://github.com/warmcat/lws-esp32-factory
|
||||
|
@ -21,3 +23,15 @@ Factory Reset or Uninitialized|Factory|AP: ESP_012345|80|http://192.168.4.1|fact
|
|||
User configuration|Factory|AP: config-model-serial|443|https://192.168.4.1|index.html - user set up his AP information
|
||||
Operation|OTA|Station only|443|https://model-serial.local|OTA application
|
||||
|
||||
## Basic Auth
|
||||
|
||||
The lws-esp32-test-server-demos app also demos basic auth.
|
||||
|
||||
On a normal platform this is done by binding a mount to a text file somewhere in the filesystem, which
|
||||
contains user:password information one per line.
|
||||
|
||||
On ESP32 there is not necessarily any generic VFS in use. So instead, the basic auth lookup is bound to
|
||||
a given nvs domain, where the username is the key and the password the value. main/main.c in the test
|
||||
demos app shows how to both make the mount use basic auth, and how to set a user:password combination
|
||||
using nvs.
|
||||
|
|
@ -79,6 +79,12 @@ on port 7681, non-SSL is provided. To set it up
|
|||
# sudo lwsws
|
||||
```
|
||||
|
||||
@section lwswsacme Using Letsencrypt or other ACME providers
|
||||
|
||||
Lws supports automatic provisioning and renewal of TLS certificates.
|
||||
|
||||
See ./READMEs/README.plugin-acme.md for examples of how to set it up on an lwsws vhost.
|
||||
|
||||
@section lwsogo Other Global Options
|
||||
|
||||
- `reject-service-keywords` allows you to return an HTTP error code and message of your choice
|
||||
|
@ -182,7 +188,7 @@ Vhosts can select which plugins they want to offer and give them per-vhost setti
|
|||
```
|
||||
|
||||
The "x":"y" parameters like "status":"ok" are made available to the protocol during its per-vhost
|
||||
LWS_CALLBACK_PROTOCOL_INIT (@in is a pointer to a linked list of struct lws_protocol_vhost_options
|
||||
LWS_CALLBACK_PROTOCOL_INIT (in is a pointer to a linked list of struct lws_protocol_vhost_options
|
||||
containing the name and value pointers).
|
||||
|
||||
To indicate that a protocol should be used when no Protocol: header is sent
|
||||
|
@ -196,6 +202,18 @@ by the client, you can use "default": "1"
|
|||
}]
|
||||
```
|
||||
|
||||
Similarly, if your vhost is serving a raw protocol, you can mark the protocol
|
||||
to be selected using "raw": "1"
|
||||
```
|
||||
"ws-protocols": [{
|
||||
"warmcat-timezoom": {
|
||||
"status": "ok",
|
||||
"raw": "1"
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
See also "rawonly" below.
|
||||
|
||||
@section lwswsovo Lwsws Other vhost options
|
||||
|
||||
|
@ -205,7 +223,7 @@ by the client, you can use "default": "1"
|
|||
|
||||
- `keeplive-timeout` (in secs) defaults to 60 for lwsws, it may be set as a vhost option
|
||||
|
||||
- `interface` lets you specify which network interface to listen on, if not given listens on all
|
||||
- `interface` lets you specify which network interface to listen on, if not given listens on all. If the network interface is not usable (eg, ethernet cable out) it will be logged at startup with such vhost not listening, and lws will poll for it and bind a listen socket to the interface if and when it becomes available.
|
||||
|
||||
- "`unix-socket`": "1" causes the unix socket specified in the interface option to be used instead of an INET socket
|
||||
|
||||
|
@ -257,6 +275,8 @@ recommended vhost headers for good client security are
|
|||
|
||||
```
|
||||
|
||||
- "`rawonly`": "on" This vhost only serves a raw protocol, disable HTTP on it
|
||||
|
||||
@section lwswsm Lwsws Mounts
|
||||
|
||||
Where mounts are given in the vhost definition, then directory contents may
|
||||
|
@ -321,7 +341,7 @@ provide them using "pmo"
|
|||
}]
|
||||
}
|
||||
|
||||
2) When using a cgi:// protcol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
|
||||
2) When using a cgi:// protocol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
|
||||
```
|
||||
{
|
||||
"mountpoint": "/git",
|
||||
|
@ -416,6 +436,17 @@ The file should be readable by lwsws, and for a little bit of extra security not
|
|||
have a file suffix, so lws would reject to serve it even if it could find it on
|
||||
a mount.
|
||||
|
||||
@section lwswscc Requiring a Client Cert on a vhost
|
||||
|
||||
You can make a vhost insist to get a client certificate from the peer before
|
||||
allowing the connection with
|
||||
|
||||
```
|
||||
"client-cert-required": "1"
|
||||
```
|
||||
|
||||
the connection will only proceed if the client certificate was signed by the
|
||||
same CA as the server has been told to trust.
|
||||
|
||||
@section lwswspl Lwsws Plugins
|
||||
|
||||
|
@ -590,7 +621,7 @@ this will give nice backtraces in lwsws itself and in plugins, if they were buil
|
|||
|
||||
@section lwswsvgd Running lwsws under valgrind
|
||||
|
||||
You can just run lwsws under galgrind as usual and get valid results. However the results / analysis part of valgrind runs
|
||||
You can just run lwsws under valgrind as usual and get valid results. However the results / analysis part of valgrind runs
|
||||
after the plugins have removed themselves, this means valgrind backtraces into plugin code is opaque, without
|
||||
source-level info because the dynamic library is gone.
|
||||
|
180
READMEs/README.plugin-acme.md
Normal file
180
READMEs/README.plugin-acme.md
Normal file
|
@ -0,0 +1,180 @@
|
|||
lws-acme-client Plugin
|
||||
======================
|
||||
|
||||
## Introduction
|
||||
|
||||
lws-acme-client is a protcol plugin for libwebsockets that implements an
|
||||
ACME client able to communicate with let's encrypt and other certificate
|
||||
providers.
|
||||
|
||||
It implements `tls-sni-01` challenge, and is able to provision tls certificates
|
||||
"from thin air" that are accepted by all the major browsers. It also manages
|
||||
re-requesting the certificate when it only has two weeks left to run.
|
||||
|
||||
It works with both the OpenSSL and mbedTLS backends.
|
||||
|
||||
## Overview for use
|
||||
|
||||
You need to:
|
||||
|
||||
- Provide name resolution to the IP with your server, ie, myserver.com needs to
|
||||
resolve to the IP that hosts your server
|
||||
|
||||
- Enable port forwarding / external firewall access to your port, usually 443
|
||||
|
||||
- Enable the "lws-acme-client" plugin on the vhosts you want it to manage
|
||||
certs for
|
||||
|
||||
- Add per-vhost options describing what should be in the certificate
|
||||
|
||||
After that the plugin will sort everything else out.
|
||||
|
||||
## Example lwsws setup
|
||||
|
||||
```
|
||||
"vhosts": [ {
|
||||
"name": "home.warmcat.com",
|
||||
"port": "443",
|
||||
"host-ssl-cert": "/etc/lwsws/acme/home.warmcat.com.crt.pem",
|
||||
"host-ssl-key": "/etc/lwsws/acme/home.warmcat.com.key.pem",
|
||||
"ignore-missing-cert": "1",
|
||||
"access-log": "/var/log/lwsws/test-access-log",
|
||||
"ws-protocols": [{
|
||||
"lws-acme-client": {
|
||||
"auth-path": "/etc/lwsws/acme/auth.jwk",
|
||||
"cert-path": "/etc/lwsws/acme/home.warmcat.com.crt.pem",
|
||||
"key-path": "/etc/lwsws/acme/home.warmcat.com.key.pem",
|
||||
"directory-url": "https://acme-staging.api.letsencrypt.org/directory",
|
||||
"country": "TW",
|
||||
"state": "Taipei",
|
||||
"locality": "Xiaobitan",
|
||||
"organization": "Crash Barrier Ltd",
|
||||
"common-name": "home.warmcat.com",
|
||||
"email": "andy@warmcat.com"
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
## Required PVOs
|
||||
|
||||
Notice that the `"host-ssl-cert"` and `"host-ssl-key"` entries have the same
|
||||
meaning as usual, they point to your certificate and private key. However
|
||||
because the ACME plugin can provision these, you should also mark the vhost with
|
||||
`"ignore-missing-cert" : "1"`, so lwsws will ignore what will initially be
|
||||
missing certificate / keys on that vhost, and will set about creating the
|
||||
necessary certs and keys instead of erroring out.
|
||||
|
||||
You must make sure the directories mentioned here exist, lws doesn't create them
|
||||
for you. They should be 0700 root:root, even if you drop lws privileges.
|
||||
|
||||
If you are implementing support in code, this corresponds to making sure the
|
||||
vhost creating `info.options` has the `LWS_SERVER_OPTION_IGNORE_MISSING_CERT`
|
||||
bit set.
|
||||
|
||||
Similarly, in code, the each of the per-vhost options shown above can be
|
||||
provided in a linked-list of structs at vhost creation time. See
|
||||
`./test-apps/test-server-v2.0.c` for example code for providing pvos.
|
||||
|
||||
### auth-path
|
||||
|
||||
This is where the plugin will store the auth keys it generated.
|
||||
|
||||
### cert-path
|
||||
|
||||
Where the plugin will store the certificate file. Should match `host-ssl-cert`
|
||||
that the vhost wants to use.
|
||||
|
||||
The path should include at least one 0700 root:root directory.
|
||||
|
||||
### key-path
|
||||
|
||||
Where the plugin will store the certificate keys. Again it should match
|
||||
`host-ssl-key` the vhost is trying to use.
|
||||
|
||||
The path should include at least one 0700 root:root directory.
|
||||
|
||||
### directory-url
|
||||
|
||||
This defines the URL of the certification server you will get your
|
||||
certificates from. For let's encrypt, they have a "practice" one
|
||||
|
||||
- `https://acme-staging.api.letsencrypt.org/directory`
|
||||
|
||||
and they have a "real" one
|
||||
|
||||
- `https://acme-v01.api.letsencrypt.org/directory`
|
||||
|
||||
the main difference is the CA certificate for the real one is in most browsers
|
||||
already, but the staging one's CA certificate isn't. The staging server will
|
||||
also let you abuse it more in terms of repeated testing etc.
|
||||
|
||||
It's recommended you confirm expected operation with the staging directory-url,
|
||||
and then switch to the "real" URL.
|
||||
|
||||
### common-name
|
||||
|
||||
Your server DNS name, like "libwebsockets.org". The remote ACME server will
|
||||
use this to find your server to perform the SNI challenges.
|
||||
|
||||
### email
|
||||
|
||||
The contact email address for the certificate.
|
||||
|
||||
## Optional PVOs
|
||||
|
||||
These are not included in the cert by letsencrypt
|
||||
|
||||
### country
|
||||
|
||||
Two-letter country code for the certificate
|
||||
|
||||
### state
|
||||
|
||||
State "or province" for the certificate
|
||||
|
||||
### locality
|
||||
|
||||
Locality for the certificate
|
||||
|
||||
### organization
|
||||
|
||||
Your company name
|
||||
|
||||
## Security / Key storage considerations
|
||||
|
||||
The `lws-acme-client` plugin is able to provision and update your certificate
|
||||
and keys in an entirely root-only storage environment, even though lws runs
|
||||
as a different uid / gid with no privileges to access the storage dir.
|
||||
|
||||
It does this by opening and holding two WRONLY fds on "update paths" inside the
|
||||
root directory structure for each cert and key it manages; these are the normal
|
||||
cert and key paths with `.upd` appended. If during the time the server is up
|
||||
the certs become within two weeks of expiry, the `lws-acme-client` plugin will
|
||||
negotiate new certs and write them to the file descriptors.
|
||||
|
||||
Next time the server starts, if it sees `.upd` cert and keys, it will back up
|
||||
the old ones and copy them into place as the new ones, before dropping privs.
|
||||
|
||||
To also handle the long-uptime server case, lws will update the vhost with the
|
||||
new certs using in-memory temporary copies of the cert and key after updating
|
||||
the cert.
|
||||
|
||||
In this way the cert and key live in root-only storage but the vhost is kept up
|
||||
to date dynamically with any cert changes as well.
|
||||
|
||||
## Multiple vhosts using same cert
|
||||
|
||||
In the case you have multiple vhosts using of the same cert, just attach
|
||||
the `lws-acme-client` plugin to one instance. When the cert updates, all the
|
||||
vhosts are informed and vhosts using the same filepath to access the cert will
|
||||
be able to update their cert.
|
||||
|
||||
## Implementation point
|
||||
|
||||
You will need to remove the auth keys when switching from OpenSSL to
|
||||
mbedTLS. They will be regenerated automatically. It's the file at this
|
||||
path:
|
||||
|
||||
```
|
||||
"auth-path": "/etc/lwsws/acme/auth.jwk",
|
||||
```
|
|
@ -2,7 +2,7 @@ Overview of lws test apps
|
|||
=========================
|
||||
|
||||
Are you building a client? You just need to look at the test client
|
||||
[libwebsockets-test-client](test-server/test-client.c).
|
||||
[libwebsockets-test-client](../test-apps/test-client.c).
|
||||
|
||||
If you are building a standalone server, there are three choices, in order of
|
||||
preferability.
|
||||
|
@ -13,12 +13,12 @@ Lws provides a generic web server app that can be configured with JSON
|
|||
config files. https://libwebsockets.org itself uses this method.
|
||||
|
||||
With lwsws handling the serving part, you only need to write an lws protocol
|
||||
plugin. See [plugin-standalone](plugin-standalone) for an example of how
|
||||
plugin. See [plugin-standalone](../plugin-standalone) for an example of how
|
||||
to do that outside lws itself, using lws public apis.
|
||||
|
||||
$ cmake .. -DLWS_WITH_LWSWS=1
|
||||
|
||||
See [README.lwsws.md](README.lwsws.md) for information on how to configure
|
||||
See [README.lwsws.md](../READMEs/README.lwsws.md) for information on how to configure
|
||||
lwsws.
|
||||
|
||||
NOTE this method implies libuv is used by lws, to provide crossplatform
|
||||
|
@ -28,11 +28,13 @@ implementations of timers, dynamic lib loading etc for plugins and lwsws.
|
|||
|
||||
This method lets you configure web serving in code, instead of using lwsws.
|
||||
|
||||
Plugins are still used, which implies libuv needed.
|
||||
Plugins are still used, but you have a choice whether to dynamically load
|
||||
them or statically include them. In this example, they are dynamically
|
||||
loaded.
|
||||
|
||||
$ cmake .. -DLWS_WITH_PLUGINS=1
|
||||
|
||||
See [test-server-v2.0.c](test-server/test-server-v2.0.c)
|
||||
See [test-server-v2.0.c](../test-apps/test-server-v2.0.c)
|
||||
|
||||
3) protocols in the server app
|
||||
|
||||
|
@ -43,13 +45,23 @@ combined code is all squidged together and is much less maintainable.
|
|||
This method is still supported in lws but all ongoing and future work is
|
||||
being done in protocol plugins only.
|
||||
|
||||
You can simply include the plugin contents and have it buit statically into
|
||||
your server, just define this before including the plugin source
|
||||
|
||||
```
|
||||
#define LWS_PLUGIN_STATIC
|
||||
```
|
||||
|
||||
This gets you most of the advantages without needing dynamic loading +
|
||||
libuv.
|
||||
|
||||
|
||||
Notes about lws test apps
|
||||
=========================
|
||||
|
||||
@section tsb Testing server with a browser
|
||||
|
||||
If you run [libwebsockets-test-server](test-server/test-server.c) and point your browser
|
||||
If you run [libwebsockets-test-server](../test-apps/test-server.c) and point your browser
|
||||
(eg, Chrome) to
|
||||
|
||||
http://127.0.0.1:7681
|
||||
|
@ -74,7 +86,7 @@ terminates.
|
|||
|
||||
To stop the daemon, do
|
||||
```
|
||||
$ kill cat /tmp/.lwsts-lock
|
||||
$ kill \`cat /tmp/.lwsts-lock\`
|
||||
```
|
||||
If it finds a stale lock (the pid mentioned in the file does not exist
|
||||
any more) it will delete the lock and create a new one during startup.
|
||||
|
@ -82,6 +94,60 @@ any more) it will delete the lock and create a new one during startup.
|
|||
If the lock is valid, the daemon will exit with a note on stderr that
|
||||
it was already running.
|
||||
|
||||
@section clicert Testing Client Certs
|
||||
|
||||
Here is a very quick way to create a CA, and a client and server cert from it,
|
||||
for testing.
|
||||
|
||||
```
|
||||
$ cp -rp ./scripts/client-ca /tmp
|
||||
$ cd /tmp/client-ca
|
||||
$ ./create-ca.sh
|
||||
$ ./create-server-cert.sh server
|
||||
$ ./create-client-cert.sh client
|
||||
```
|
||||
|
||||
The last step wants an export password, you will need this password again to
|
||||
import the p12 format certificate into your browser.
|
||||
|
||||
This will get you the following
|
||||
|
||||
|name|function|
|
||||
|----|--------|
|
||||
|ca.pem|Your Certificate Authority cert|
|
||||
|ca.key|Private key for the CA cert|
|
||||
|client.pem|Client certificate, signed by your CA|
|
||||
|client.key|Client private key|
|
||||
|client.p12|combined client.pem + client.key in p12 format for browsers|
|
||||
|server.pem|Server cert, signed by your CA|
|
||||
|server.key|Server private key|
|
||||
|
||||
You can confirm yourself the client and server certs are signed by the CA.
|
||||
|
||||
```
|
||||
$ openssl verify -verbose -trusted ca.pem server.pem
|
||||
$ openssl verify -verbose -trusted ca.pem client.pem
|
||||
```
|
||||
|
||||
Import the client.p12 file into your browser. In FFOX57 it's
|
||||
|
||||
- preferences
|
||||
- Privacy & Security
|
||||
- Certificates | View Certificates
|
||||
- Certificate Manager | Your Certificates | Import...
|
||||
- Enter the password you gave when creating client1.p12
|
||||
- Click OK.
|
||||
|
||||
You can then run the test server like this:
|
||||
|
||||
```
|
||||
$ libwebsockets-test-server -s -A ca.pem -K server.key -C server.pem -v
|
||||
```
|
||||
|
||||
When you connect your browser to https://localhost:7681 after accepting the
|
||||
selfsigned server cert, your browser will pop up a prompt to send the server
|
||||
your client cert (the -v switch enables this). The server will only accept
|
||||
a client cert that has been signed by ca.pem.
|
||||
|
||||
@section sssl Using SSL on the server side
|
||||
|
||||
|
@ -100,7 +166,7 @@ certificates in the browser and the connection will proceed
|
|||
in first https and then websocket wss, acting exactly the
|
||||
same.
|
||||
|
||||
[test-server.c](test-server/test-server.c) is all that is needed to use libwebsockets for
|
||||
[test-server.c](../test-apps/test-server.c) is all that is needed to use libwebsockets for
|
||||
serving both the script html over http and websockets.
|
||||
|
||||
@section lwstsdynvhost Dynamic Vhosts
|
||||
|
@ -111,6 +177,28 @@ to toggle the creation and destruction of an identical second vhost on port + 1.
|
|||
This is intended as a test and demonstration for how to bring up and remove
|
||||
vhosts dynamically.
|
||||
|
||||
@section unixskt Testing Unix Socket Server support
|
||||
|
||||
Start the test server with -U and the path to create the unix domain socket
|
||||
|
||||
```
|
||||
$ libwebsockets-test-server -U /tmp/uds
|
||||
```
|
||||
|
||||
On exit, lws will delete the socket inode.
|
||||
|
||||
To test the client side, eg
|
||||
|
||||
```
|
||||
$ nc -C -U /tmp/uds -i 30
|
||||
```
|
||||
|
||||
and type
|
||||
|
||||
`GET / HTTP/1.1`
|
||||
|
||||
followed by two ENTER. The contents of test.html should be returned.
|
||||
|
||||
@section wscl Testing websocket client support
|
||||
|
||||
If you run the test server as described above, you can also
|
||||
|
@ -150,30 +238,6 @@ For those two options libuv is needed to support the protocol plugins, if
|
|||
that's not possible then the other variations with their own protocol code
|
||||
should be considered.
|
||||
|
||||
|
||||
@section echo Testing simple echo
|
||||
|
||||
You can test against `echo.websockets.org` as a sanity test like
|
||||
this (the client connects to port `80` by default):
|
||||
|
||||
```
|
||||
$ libwebsockets-test-echo --client echo.websocket.org
|
||||
```
|
||||
|
||||
This echo test is of limited use though because it doesn't
|
||||
negotiate any protocol. You can run the same test app as a
|
||||
local server, by default on localhost:7681
|
||||
```
|
||||
$ libwebsockets-test-echo
|
||||
```
|
||||
and do the echo test against the local echo server
|
||||
```
|
||||
$ libwebsockets-test-echo --client localhost --port 7681
|
||||
```
|
||||
If you add the `--ssl` switch to both the client and server, you can also test
|
||||
with an encrypted link.
|
||||
|
||||
|
||||
@section tassl Testing SSL on the client side
|
||||
|
||||
To test SSL/WSS client action, just run the client test with
|
||||
|
@ -348,37 +412,25 @@ treatment to the other app during that call.
|
|||
|
||||
@section autobahn Autobahn Test Suite
|
||||
|
||||
Lws can be tested against the autobahn websocket fuzzer.
|
||||
Lws can be tested against the autobahn websocket fuzzer in both client and
|
||||
server modes
|
||||
|
||||
1) pip install autobahntestsuite
|
||||
|
||||
2) wstest -m fuzzingserver
|
||||
2) From your build dir: cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1 && make
|
||||
|
||||
3) Run tests like this
|
||||
3) ../scripts/autobahn-test.sh
|
||||
|
||||
libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=20&agent=libwebsockets" -v -d 65535 -n 1
|
||||
4) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
|
||||
|
||||
(this runs test 20)
|
||||
|
||||
4) In a browser, go here
|
||||
|
||||
http://localhost:8080/test_browser.html
|
||||
|
||||
fill in "libwebsockets" in "User Agent Identifier" and press "Update Reports (Manual)"
|
||||
|
||||
5) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
|
||||
|
||||
file:///projects/libwebsockets/reports/clients/index.html
|
||||
file:///projects/libwebsockets/build/reports/clients/index.html
|
||||
|
||||
to see the results
|
||||
|
||||
|
||||
@section autobahnnotes Autobahn Test Notes
|
||||
|
||||
1) Autobahn tests the user code + lws implementation. So to get the same
|
||||
results, you need to follow test-echo.c in terms of user implementation.
|
||||
|
||||
2) Two of the tests make no sense for Libwebsockets to support and we fail them.
|
||||
1) Two of the tests make no sense for Libwebsockets to support and we fail them.
|
||||
|
||||
- Tests 2.10 + 2.11: sends multiple pings on one connection. Lws policy is to
|
||||
only allow one active ping in flight on each connection, the rest are dropped.
|
||||
|
@ -386,5 +438,7 @@ The autobahn test itself admits this is not part of the standard, just someone's
|
|||
random opinion about how they think a ws server should act. So we will fail
|
||||
this by design and it is no problem about RFC6455 compliance.
|
||||
|
||||
|
||||
2) Currently two parts of autobahn are broken and we skip them
|
||||
|
||||
https://github.com/crossbario/autobahn-testsuite/issues/71
|
||||
|
20
READMEs/mainpage.md
Normal file
20
READMEs/mainpage.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
##Libwebsockets API introduction
|
||||
|
||||
Libwebsockets covers a lot of interesting features for people making embedded servers or clients
|
||||
|
||||
- HTTP(S) serving and client operation
|
||||
- HTTP/2 support for serving
|
||||
- WS(S) serving and client operation
|
||||
- HTTP(S) apis for file transfer and upload
|
||||
- HTTP 1 + 2 POST form handling (including multipart / file upload)
|
||||
- cookie-based sessions
|
||||
- account management (including registration, email verification, lost pw etc)
|
||||
- strong SSL / TLS PFS support (A+ on SSLlabs test)
|
||||
- ssh server integration
|
||||
- serving gzipped files directly from inside zip files, without conversion
|
||||
- support for linux, bsd, windows etc... and very small nonlinux targets like ESP32
|
||||
|
||||
You can browse by api category <a href="modules.html">here</a>
|
||||
|
||||
A collection of READMEs for build, coding, lwsws etc are <a href="pages.html">here</a>
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
Release Checklist
|
||||
-----------------
|
||||
|
||||
0) QA
|
||||
1) QA
|
||||
|
||||
a) ab
|
||||
a) ab / h2load / h2spec
|
||||
|
||||
$ ab -n 100000 -c 200 http://localhost:7681/
|
||||
$ ab -n 100000 -c 200 https://127.0.0.1:7681/
|
||||
$ h2load -n 100000 -c 200 https://localhost:7681
|
||||
$ h2spec -h 127.0.0.1 -p 7681 -t -k -o 1
|
||||
|
||||
b) coverity
|
||||
|
||||
|
@ -16,24 +18,6 @@ Release Checklist
|
|||
|
||||
d) valgrind test servers + client + browser
|
||||
|
||||
e) attack.sh
|
||||
|
||||
$ ./test-server/attack.sh
|
||||
|
||||
f) Autobahn
|
||||
|
||||
$ wstest -m fuzzingserver &
|
||||
$ ./autobahn-test.sh
|
||||
|
||||
Force update by browser using agent "libwebsockets"
|
||||
http://localhost:8080/test_browser.html
|
||||
|
||||
rsync -av ./reports/* root@warmcat.com:/var/www/libwebsockets.org
|
||||
|
||||
1) api
|
||||
|
||||
$ cp build/doc/* .
|
||||
|
||||
2) soname bump?
|
||||
|
||||
a) We need one if we added / changed / removed apis
|
||||
|
@ -42,7 +26,7 @@ Release Checklist
|
|||
|
||||
set(SOVERSION "6")
|
||||
|
||||
libwebsockets.spec
|
||||
scripts/libwebsockets.spec
|
||||
|
||||
-/%{_libdir}/libwebsockets.so.6
|
||||
+/%{_libdir}/libwebsockets.so.7
|
||||
|
@ -69,23 +53,20 @@ Release Checklist
|
|||
|
||||
a) rpm version bump to match CMake one
|
||||
|
||||
libwebsockets.spec
|
||||
scripts/libwebsockets.spec
|
||||
|
||||
Version: 1.6.0
|
||||
|
||||
b) Summarize changelog
|
||||
|
||||
libwebsockets.spec
|
||||
scripts/libwebsockets.spec
|
||||
|
||||
%changelog
|
||||
* Sun Jan 17 2016 Andrew Cooks <acooks@linux.com> 1.6.4-1
|
||||
- Bump version to 1.6.4
|
||||
- MINOR fix xyz
|
||||
|
||||
6) update api docs
|
||||
|
||||
$ cmake ..
|
||||
$ cp doc/* ..
|
||||
6) Announce latest version on README.md
|
||||
|
||||
7) signed tag
|
||||
|
||||
|
@ -97,6 +78,6 @@ Release Checklist
|
|||
|
||||
b) final CI check, if fail delete tag, kill pushed tags, restart flow
|
||||
|
||||
8) website
|
||||
9) website
|
||||
|
||||
a) update latest tag for release branch
|
47
appveyor.yml
47
appveyor.yml
|
@ -1,5 +1,8 @@
|
|||
environment:
|
||||
matrix:
|
||||
- LWS_METHOD: x64
|
||||
CMAKE_ARGS: -DCMAKE_GENERATOR_PLATFORM=x64 -DLWS_WITH_HTTP2=1 -DLWS_WITH_PLUGINS=1 -DLIBUV_INCLUDE_DIRS=C:\assets\libuv64\include -DLIBUV_LIBRARIES=C:\assets\libuv64\libuv.lib
|
||||
|
||||
- LWS_METHOD: lwsws
|
||||
CMAKE_ARGS: -DLWS_WITH_LWSWS=1 -DSQLITE3_INCLUDE_DIRS=C:\assets\sqlite3 -DSQLITE3_LIBRARIES=C:\assets\sqlite3\sqlite3.lib -DLIBUV_INCLUDE_DIRS=C:\assets\libuv\include -DLIBUV_LIBRARIES=C:\assets\libuv\libuv.lib
|
||||
|
||||
|
@ -16,21 +19,25 @@ environment:
|
|||
|
||||
- LWS_METHOD: nossl
|
||||
CMAKE_ARGS: -DLWS_WITH_SSL=OFF
|
||||
|
||||
install:
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/win-libuv.zip
|
||||
- mkdir c:\assets
|
||||
- mkdir c:\assets\libuv
|
||||
- 7z x -oc:\assets\libuv win-libuv.zip
|
||||
# - appveyor DownloadFile https://slproweb.com/download/Win32OpenSSL-1_0_2h.exe
|
||||
# - appveyor DownloadFile https://libwebsockets.org:444/Win32OpenSSL-1_0_2L.exe
|
||||
# - Win32OpenSSL-1_0_2L.exe /silent /verysilent /sp- /suppressmsgboxes
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/win-libuv64.zip
|
||||
- mkdir c:\assets\libuv64
|
||||
- 7z x -oc:\assets\libuv64 win-libuv64.zip
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/nsis-3.0rc1-setup.exe
|
||||
- cmd /c start /wait nsis-3.0rc1-setup.exe /S /D=C:\nsis
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/sqlite-dll-win32-x86-3130000.zip
|
||||
- mkdir c:\assets\sqlite3
|
||||
- 7z x -oc:\assets\sqlite3 sqlite-dll-win32-x86-3130000.zip
|
||||
- SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;c:\nsis;%PATH%
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
|
||||
build_script:
|
||||
- md build
|
||||
|
@ -38,21 +45,33 @@ build_script:
|
|||
- cmake -DCMAKE_BUILD_TYPE=Release %CMAKE_ARGS% ..
|
||||
- cmake --build . --config Release
|
||||
|
||||
# TODO: Keeps breaking Windows build, should be rewritten using CPack properly instead...
|
||||
after_build:
|
||||
- 7z a lws.zip %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.lib %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.exp %APPVEYOR_BUILD_FOLDER%\build\bin\Release\websockets.dll %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h %APPVEYOR_BUILD_FOLDER%\build\lws_config.h %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe
|
||||
# - cd ..
|
||||
# - cd win32port
|
||||
# - makensis -DVERSION=%APPVEYOR_BUILD_VERSION% libwebsockets.nsi
|
||||
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- mkdir staging
|
||||
- mkdir staging\include
|
||||
- cp -r %APPVEYOR_BUILD_FOLDER%\build\bin %APPVEYOR_BUILD_FOLDER%\build\lib staging
|
||||
- if EXIST staging\bin\share mv staging\bin\share staging
|
||||
- if NOT EXIST staging\share\libwebsockets-test-server mkdir staging\share\libwebsockets-test-server
|
||||
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem staging\share\libwebsockets-test-server
|
||||
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem staging\share\libwebsockets-test-server
|
||||
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\lws_config.h cp %APPVEYOR_BUILD_FOLDER%\build\lws_config.h staging\include
|
||||
- cp %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h staging\include
|
||||
- 7z a build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip %APPVEYOR_BUILD_FOLDER%\staging\*
|
||||
|
||||
artifacts:
|
||||
- path: lws.zip
|
||||
name: lws.zip
|
||||
type: Zip
|
||||
- path: build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip
|
||||
|
||||
#cache:
|
||||
# - C:\OpenSSL-Win32
|
||||
deploy:
|
||||
- provider: BinTray
|
||||
username: lws-team
|
||||
api_key:
|
||||
secure: nDpZ7P/wrk98DwJPMC6KpCC23QrVP8f3RxvKzBaqOmb9LiVrg1IyO1cc5vcgShZC
|
||||
subject: lws-team
|
||||
repo: libwebsockets
|
||||
package: windows
|
||||
publish: true
|
||||
override: true
|
||||
explode: false
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -u
|
||||
|
||||
N=1
|
||||
OS=`uname`
|
||||
|
||||
for i in '1.1.1' '1.1.2' '1.1.3' '1.1.4' '1.1.5' '1.1.6' '1.1.7' '1.1.8' '1.2.1' '1.2.2' '1.2.3' '1.2.4' '1.2.5' '1.2.6' '1.2.7' '1.2.8' '2.1' '2.2' '2.3' '2.4' '2.5' '2.6' '2.7' '2.8' '2.9' '2.10' '2.11' '3.1' '3.2' '3.3' '3.4' '3.5' '3.6' '3.7' '4.1.1' '4.1.2' '4.1.3' '4.1.4' '4.1.5' '4.2.1' '4.2.2' '4.2.3' '4.2.4' '4.2.5' '5.1' '5.2' '5.3' '5.4' '5.5' '5.6' '5.7' '5.8' '5.9' '5.10' '5.11' '5.12' '5.13' '5.14' '5.15' '5.16' '5.17' '5.18' '5.19' '5.20' '6.1.1' '6.1.2' '6.1.3' '6.2.1' '6.2.2' '6.2.3' '6.2.4' '6.3.1' '6.3.2' '6.4.1' '6.4.2' '6.4.3' '6.4.4' '6.5.1' '6.5.2' '6.5.3' '6.5.4' '6.5.5' '6.6.1' '6.6.2' '6.6.3' '6.6.4' '6.6.5' '6.6.6' '6.6.7' '6.6.8' '6.6.9' '6.6.10' '6.6.11' '6.7.1' '6.7.2' '6.7.3' '6.7.4' '6.8.1' '6.8.2' '6.9.1' '6.9.2' '6.9.3' '6.9.4' '6.10.1' '6.10.2' '6.10.3' '6.11.1' '6.11.2' '6.11.3' '6.11.4' '6.11.5' '6.12.1' '6.12.2' '6.12.3' '6.12.4' '6.12.5' '6.12.6' '6.12.7' '6.12.8' '6.13.1' '6.13.2' '6.13.3' '6.13.4' '6.13.5' '6.14.1' '6.14.2' '6.14.3' '6.14.4' '6.14.5' '6.14.6' '6.14.7' '6.14.8' '6.14.9' '6.14.10' '6.15.1' '6.16.1' '6.16.2' '6.16.3' '6.17.1' '6.17.2' '6.17.3' '6.17.4' '6.17.5' '6.18.1' '6.18.2' '6.18.3' '6.18.4' '6.18.5' '6.19.1' '6.19.2' '6.19.3' '6.19.4' '6.19.5' '6.20.1' '6.20.2' '6.20.3' '6.20.4' '6.20.5' '6.20.6' '6.20.7' '6.21.1' '6.21.2' '6.21.3' '6.21.4' '6.21.5' '6.21.6' '6.21.7' '6.21.8' '6.22.1' '6.22.2' '6.22.3' '6.22.4' '6.22.5' '6.22.6' '6.22.7' '6.22.8' '6.22.9' '6.22.10' '6.22.11' '6.22.12' '6.22.13' '6.22.14' '6.22.15' '6.22.16' '6.22.17' '6.22.18' '6.22.19' '6.22.20' '6.22.21' '6.22.22' '6.22.23' '6.22.24' '6.22.25' '6.22.26' '6.22.27' '6.22.28' '6.22.29' '6.22.30' '6.22.31' '6.22.32' '6.22.33' '6.22.34' '6.23.1' '6.23.2' '6.23.3' '6.23.4' '6.23.5' '6.23.6' '6.23.7' '7.1.1' '7.1.2' '7.1.3' '7.1.4' '7.1.5' '7.1.6' '7.3.1' '7.3.2' '7.3.3' '7.3.4' '7.3.5' '7.3.6' '7.5.1' '7.7.1' '7.7.2' '7.7.3' '7.7.4' '7.7.5' '7.7.6' '7.7.7' '7.7.8' '7.7.9' '7.7.10' '7.7.11' '7.7.12' '7.7.13' '7.9.1' '7.9.2' '7.9.3' '7.9.4' '7.9.5' '7.9.6' '7.9.7' '7.9.8' '7.9.9' '7.9.10' '7.9.11' '7.9.12' '7.9.13' '7.13.1' '7.13.2' '9.1.1' '9.1.2' '9.1.3' '9.1.4' '9.1.5' '9.1.6' '9.2.1' '9.2.2' '9.2.3' '9.2.4' '9.2.5' '9.2.6' '9.3.1' '9.3.2' '9.3.3' '9.3.4' '9.3.5' '9.3.6' '9.3.7' '9.3.8' '9.3.9' '9.4.1' '9.4.2' '9.4.3' '9.4.4' '9.4.5' '9.4.6' '9.4.7' '9.4.8' '9.4.9' '9.5.1' '9.5.2' '9.5.3' '9.5.4' '9.5.5' '9.5.6' '9.6.1' '9.6.2' '9.6.3' '9.6.4' '9.6.5' '9.6.6' '9.7.1' '9.7.2' '9.7.3' '9.7.4' '9.7.5' '9.7.6' '9.8.1' '9.8.2' '9.8.3' '9.8.4' '9.8.5' '9.8.6' '10.1.1' '12.1.1' '12.1.2' '12.1.3' '12.1.4' '12.1.5' '12.1.6' '12.1.7' '12.1.8' '12.1.9' '12.1.10' '12.1.11' '12.1.12' '12.1.13' '12.1.14' '12.1.15' '12.1.16' '12.1.17' '12.1.18' '12.2.1' '12.2.2' '12.2.3' '12.2.4' '12.2.5' '12.2.6' '12.2.7' '12.2.8' '12.2.9' '12.2.10' '12.2.11' '12.2.12' '12.2.13' '12.2.14' '12.2.15' '12.2.16' '12.2.17' '12.2.18' '12.3.1' '12.3.2' '12.3.3' '12.3.4' '12.3.5' '12.3.6' '12.3.7' '12.3.8' '12.3.9' '12.3.10' '12.3.11' '12.3.12' '12.3.13' '12.3.14' '12.3.15' '12.3.16' '12.3.17' '12.3.18' '12.4.1' '12.4.2' '12.4.3' '12.4.4' '12.4.5' '12.4.6' '12.4.7' '12.4.8' '12.4.9' '12.4.10' '12.4.11' '12.4.12' '12.4.13' '12.4.14' '12.4.15' '12.4.16' '12.4.17' '12.4.18' '12.5.1' '12.5.2' '12.5.3' '12.5.4' '12.5.5' '12.5.6' '12.5.7' '12.5.8' '12.5.9' '12.5.10' '12.5.11' '12.5.12' '12.5.13' '12.5.14' '12.5.15' '12.5.16' '12.5.17' '12.5.18' '13.1.1' '13.1.2' '13.1.3' '13.1.4' '13.1.5' '13.1.6' '13.1.7' '13.1.8' '13.1.9' '13.1.10' '13.1.11' '13.1.12' '13.1.13' '13.1.14' '13.1.15' '13.1.16' '13.1.17' '13.1.18' '13.2.1' '13.2.2' '13.2.3' '13.2.4' '13.2.5' '13.2.6' '13.2.7' '13.2.8' '13.2.9' '13.2.10' '13.2.11' '13.2.12' '13.2.13' '13.2.14' '13.2.15' '13.2.16' '13.2.17' '13.2.18' '13.3.1' '13.3.2' '13.3.3' '13.3.4' '13.3.5' '13.3.6' '13.3.7' '13.3.8' '13.3.9' '13.3.10' '13.3.11' '13.3.12' '13.3.13' '13.3.14' '13.3.15' '13.3.16' '13.3.17' '13.3.18' '13.4.1' '13.4.2' '13.4.3' '13.4.4' '13.4.5' '13.4.6' '13.4.7' '13.4.8' '13.4.9' '13.4.10' '13.4.11' '13.4.12' '13.4.13' '13.4.14' '13.4.15' '13.4.16' '13.4.17' '13.4.18' '13.5.1' '13.5.2' '13.5.3' '13.5.4' '13.5.5' '13.5.6' '13.5.7' '13.5.8' '13.5.9' '13.5.10' '13.5.11' '13.5.12' '13.5.13' '13.5.14' '13.5.15' '13.5.16' '13.5.17' '13.5.18' '13.6.1' '13.6.2' '13.6.3' '13.6.4' '13.6.5' '13.6.6' '13.6.7' '13.6.8' '13.6.9' '13.6.10' '13.6.11' '13.6.12' '13.6.13' '13.6.14' '13.6.15' '13.6.16' '13.6.17' '13.6.18' '13.7.1' '13.7.2' '13.7.3' '13.7.4' '13.7.5' '13.7.6' '13.7.7' '13.7.8' '13.7.9' '13.7.10' '13.7.11' '13.7.12' '13.7.13' '13.7.14' '13.7.15' '13.7.16' '13.7.17' '13.7.18' ; do
|
||||
libwebsockets-test-echo --client 127.0.0.1 --port 9001 -u "/runCase?case=$N&agent=libwebsockets" -v -n 1 &
|
||||
|
||||
C=99
|
||||
while [ $C -gt 8 ] ; do
|
||||
if [ $OS=SunOS ] ; then
|
||||
C=`ps -ef | grep libwebsockets-test-echo | wc -l`
|
||||
else
|
||||
C=`ps fax | grep libwebsockets-test-echo | wc -l`
|
||||
fi
|
||||
if [ $C -gt 8 ] ; then
|
||||
sleep 1s
|
||||
fi
|
||||
done
|
||||
|
||||
N=$(( $N + 1 ))
|
||||
done
|
||||
|
||||
echo "waiting for forks to complete..."
|
||||
|
||||
while [ 1 ] ; do
|
||||
if [ $OS=SunOS ] ; then
|
||||
n=`ps -ef | grep libwebsocket | grep -v grep | wc -l`
|
||||
else
|
||||
n=`ps fax | grep libwebsocket | grep -v grep | wc -l`
|
||||
fi
|
||||
echo "$n forks running..."
|
||||
if [ $n -eq 0 ] ; then
|
||||
echo "Completed"
|
||||
exit 0
|
||||
fi
|
||||
sleep 1s
|
||||
done
|
||||
|
200
changelog
200
changelog
|
@ -1,6 +1,206 @@
|
|||
Changelog
|
||||
---------
|
||||
|
||||
v3.0.0
|
||||
======
|
||||
|
||||
- CHANGE: Clients used to call LWS_CALLBACK_CLOSED same as servers...
|
||||
LWS_CALLBACK_CLIENT_CLOSED has been introduced and is called for clients
|
||||
now.
|
||||
|
||||
- CHANGE: LWS_CALLBACK_CLIENT_CONNECTION_ERROR used to only be directed at
|
||||
protocols[0]. However in many cases, the protocol to bind to was provided
|
||||
at client connection info time and the wsi bound accordingly. In those
|
||||
cases, CONNECTION_ERROR is directed at the bound protocol, not protcols[0]
|
||||
any more.
|
||||
|
||||
- CHANGE: CMAKE: the following cmake defaults have changed with this version:
|
||||
|
||||
- LWS_WITH_ZIP_FOPS: now defaults OFF
|
||||
- LWS_WITH_RANGES: now defaults OFF
|
||||
- LWS_WITH_ZLIB: now defaults OFF
|
||||
- LWS_WITHOUT_EXTENSIONS: now defaults ON
|
||||
|
||||
- CHANGE: REMOVED: lws_alloc_vfs_file() (read a file to malloc buffer)
|
||||
|
||||
- CHANGE: REMOVED: lws_read() (no longer useful outside of lws internals)
|
||||
|
||||
- CHANGE: REMOVED: ESP8266... ESP32 is now within the same price range and much
|
||||
more performant
|
||||
|
||||
- CHANGE: soname bump... don't forget to `ldconfig`
|
||||
|
||||
- NEW: all event libraries support "foreign" loop integration where lws itself
|
||||
if just a temporary user of the loop unrelated to the actual loop lifecycle.
|
||||
|
||||
See `minimal-http-server-eventlib-foreign` for example code demonstrating
|
||||
this for all the event libraries.
|
||||
|
||||
Internal loop in lws is also supported and demonstrated by
|
||||
`minimal-http-server-eventlib`.
|
||||
|
||||
- NEW: ws-over-h2 support. This is a new RFC-on-the-way supported by Chrome
|
||||
and shortly firefox that allows ws connections to be multiplexed back to the
|
||||
server on the same tcp + tls wrapper h2 connection that the html and scripts
|
||||
came in on. This is hugely faster that discrete connections.
|
||||
|
||||
- NEW: UDP socket adoption and related event callbacks
|
||||
|
||||
- NEW: Multi-client connection binding, queuing and pipelining support.
|
||||
|
||||
Lws detects multiple client connections to the same server and port, and
|
||||
optimizes how it handles them according to the server type and provided
|
||||
flags. For http/1.0, all occur with individual parallel connections. For
|
||||
http/1.1, you can enable keepalive pipelining, so the connections occur
|
||||
sequentially on a single network connection. For http/2, they all occur
|
||||
as parallel streams within a single h2 network connection.
|
||||
|
||||
See minimal-http-client-multi for example code.
|
||||
|
||||
- NEW: High resolution timer API for wsi, get a callback on your wsi with
|
||||
LWS_CALLBACK_TIMER, set and reset the timer with lws_set_timer_usecs(wsi, us)
|
||||
Actual resolution depends on event backend. Works with all backends, poll,
|
||||
libuv, libevent, and libev.
|
||||
|
||||
- NEW: Protocols can arrange vhost-protocol instance specific callbacks with
|
||||
second resolution using `lws_timed_callback_vh_protocol()`
|
||||
|
||||
- NEW: ACME client plugin for self-service TLS certificates
|
||||
|
||||
- NEW: RFC7517 JSON Web Keys RFC7638 JWK thumbprint, and RFC7515 JSON Web
|
||||
signatures support
|
||||
|
||||
- NEW: lws_cancel_service() now provides a generic way to synchronize events
|
||||
from other threads, which appear as a LWS_CALLBACK_EVENT_WAIT_CANCELLED
|
||||
callback on all protocols. This is compatible with all the event libraries.
|
||||
|
||||
- NEW: support BSD poll() where changes to the poll wait while waiting are
|
||||
undone.
|
||||
|
||||
- NEW: Introduce generic hash, hmac and RSA apis that operate the same
|
||||
regardless of OpenSSL or mbedTLS tls backend
|
||||
|
||||
- NEW: Introduce X509 element query api that works the same regardless of
|
||||
OpenSSL or mbedTLS tls backend
|
||||
|
||||
- NEW: Introduce over 30 "minimal examples" in ./minimal-examples... these
|
||||
replace most of the old test servers
|
||||
|
||||
- test-echo -> minimal-ws-server-echo and minimal-ws-client-echo
|
||||
|
||||
- test-server-libuv / -libevent / -libev ->
|
||||
minimal-https-server-eventlib / -eventlib-foreign / -eventlib-demos
|
||||
|
||||
- test-server-v2.0 -> folded into all the minimal servers
|
||||
|
||||
- test-server direct http serving -> minimal-http-server-dynamic
|
||||
|
||||
The minimal examples allow individual standalone build using their own
|
||||
small CMakeLists.txt.
|
||||
|
||||
- NEW: lws now detects any back-to-back writes that did not go through the
|
||||
event loop inbetween and reports them. This will flag any possibility of
|
||||
failure rather than wait until the problem happens.
|
||||
|
||||
- NEW: CMake has LWS_WITH_DISTRO_RECOMMENDED to select features that are
|
||||
appropriate for distros
|
||||
|
||||
- NEW: Optional vhost URL `error_document_404` if given causes a redirect there
|
||||
instead of serve the default 404 page.
|
||||
|
||||
- NEW: lws_strncpy() wrapper guarantees NUL in copied string even if it was
|
||||
truncated to fit.
|
||||
|
||||
- NEW: for client connections, local protocol binding name can be separated
|
||||
from the ws subprotocol name if needed, using .local_protocol_name
|
||||
|
||||
- NEW: Automatic detection of time discontiguities
|
||||
|
||||
- NEW: Applies TCP_USER_TIMEOUT for Linux tcp keepalive where available
|
||||
|
||||
- QA: 1600 tests run on each commit in Travis CI, including almost all
|
||||
Autobahn in client and server mode, various h2load tests, h2spec, attack.sh
|
||||
the minimal example selftests and others.
|
||||
|
||||
- QA: fix small warnings introduced on gcc8.x (eg, Fedora 28)
|
||||
|
||||
- QA: Add most of -Wextra on gcc (-Wsign-compare, -Wignored-qualifiers,
|
||||
-Wtype-limits, -Wuninitialized)
|
||||
|
||||
- QA: clean out warnings on windows
|
||||
|
||||
- QA: pass all 146 h2spec tests now on strict
|
||||
|
||||
- QA: introduce 35 selftests that operate different minimal examples against
|
||||
each other and confirm the results.
|
||||
|
||||
- QA: LWS_WITH_MINIMAL_EXAMPLES allows mass build of all relevant minimal-
|
||||
examples with the LWS build, for CI and to make all the example binaries
|
||||
available from the lws build dir ./bin
|
||||
|
||||
- REFACTOR: the lws source directory layout in ./lib has been radically
|
||||
improved, and there are now README.md files in selected subdirs with extra
|
||||
documentation of interest to people working on lws itself.
|
||||
|
||||
- REFACTOR: pipelined transactions return to the event loop before starting the
|
||||
next part.
|
||||
|
||||
- REFACTOR: TLS: replace all TLS library constants with generic LWS ones and
|
||||
adapt all the TLS library code to translate to these common ones.
|
||||
|
||||
Isolated all the tls-related private stuff in `./lib/tls/private.h`, and all
|
||||
the mbedTLS stuff in `./lib/tls/mbedtls` + openSSL stuff in
|
||||
`./lib/tls/openssl`
|
||||
|
||||
- REFACTOR: the various kinds of wsi possible with lws have been extracted
|
||||
from the main code and isolated into "roles" in `./lib/roles` which
|
||||
communicate with the core code via an ops struct. Everything related to
|
||||
ah is migrated to the http role.
|
||||
|
||||
wsi modes are eliminated and replaced by the ops pointer for the role the
|
||||
wsi is performing. Generic states for wsi are available to control the
|
||||
lifecycle using core code.
|
||||
|
||||
Adding new "roles" is now much easier with the changes and ops struct to
|
||||
plug into.
|
||||
|
||||
- REFACTOR: reduce four different kinds of buffer management in lws into a
|
||||
generic scatter-gather struct lws_buflist.
|
||||
|
||||
- REFACTOR: close notifications go through event loop
|
||||
|
||||
|
||||
v2.4.0
|
||||
======
|
||||
|
||||
- HTTP/2 server support is now mature and usable! LWS_WITH_HTTP2=1 enables it.
|
||||
Uses ALPN to serve HTTP/2, HTTP/1 and ws[s] connections all from the same
|
||||
listen port seamlessly. (Requires ALPN-capable OpenSSL 1.1 or mbedTLS).
|
||||
|
||||
- LWS_WITH_MBEDTLS=1 at CMake now builds and works against mbedTLS instead of
|
||||
OpenSSL. Most things work identically, although on common targets where
|
||||
OpenSSL has acceleration, mbedTLS is many times slower in operation. However
|
||||
it is a lot smaller codewise.
|
||||
|
||||
- Generic hash apis introduced that work the same on mbedTLS or OpenSSL backend
|
||||
|
||||
- LWS_WITH_PEER_LIMITS tracks IPs across all vhosts and allows restrictions on
|
||||
both the number of simultaneous connections and wsi in use for any single IP
|
||||
|
||||
- lws_ring apis provide a generic single- or multi-tail ringbuffer... mirror
|
||||
protocol now uses this. Features include ring elements may be sized to fit
|
||||
structs in the ringbuffer, callback when no tail any longer needs an element
|
||||
and it can be deleted, and zerocopy options to write new members directly
|
||||
into the ringbuffer, and use the ringbuffer element by address too.
|
||||
|
||||
- abstract ssh 2 server plugin included, with both plugin and standalone
|
||||
demos provided. You can bind the plugin to a vhost and also serve full-
|
||||
strength ssh from the vhost. IO from the ssh server is controlled by an
|
||||
"ops" struct of callbacks for tx, rx, auth etc.
|
||||
|
||||
- Many fixes, cleanups, source refactors and other improvements.
|
||||
|
||||
|
||||
v2.3.0
|
||||
======
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ rm -rf build_tree
|
|||
mkdir build_tree
|
||||
cd build_tree
|
||||
cmake -DCMAKE_INSTALL_PREFIX=%{rpmprefix} ../%{srcdirname}
|
||||
make
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
cd ../build_tree
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
|
||||
#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share"
|
||||
|
||||
#cmakedefine LWS_ROLE_H1
|
||||
#cmakedefine LWS_ROLE_WS
|
||||
#cmakedefine LWS_ROLE_RAW
|
||||
#cmakedefine LWS_ROLE_H2
|
||||
#cmakedefine LWS_ROLE_CGI
|
||||
|
||||
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
|
||||
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
|
||||
#cmakedefine USE_WOLFSSL
|
||||
|
@ -15,11 +21,10 @@
|
|||
/* Also define to 1 (in addition to USE_WOLFSSL) when using the
|
||||
(older) CyaSSL library */
|
||||
#cmakedefine USE_OLD_CYASSL
|
||||
#cmakedefine LWS_USE_BORINGSSL
|
||||
#cmakedefine LWS_WITH_BORINGSSL
|
||||
|
||||
#cmakedefine LWS_USE_MBEDTLS
|
||||
#cmakedefine LWS_USE_POLARSSL
|
||||
#cmakedefine LWS_WITH_ESP8266
|
||||
#cmakedefine LWS_WITH_MBEDTLS
|
||||
#cmakedefine LWS_WITH_POLARSSL
|
||||
#cmakedefine LWS_WITH_ESP32
|
||||
|
||||
#cmakedefine LWS_WITH_PLUGINS
|
||||
|
@ -37,8 +42,9 @@
|
|||
/* The current git commit hash that we're building from */
|
||||
#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
|
||||
|
||||
/* Build with OpenSSL support */
|
||||
/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
|
||||
#cmakedefine LWS_OPENSSL_SUPPORT
|
||||
#cmakedefine LWS_WITH_TLS
|
||||
|
||||
/* The client should load and trust CA root certs it finds in the OS */
|
||||
#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS
|
||||
|
@ -47,25 +53,31 @@
|
|||
#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}"
|
||||
|
||||
/* Turn off websocket extensions */
|
||||
#cmakedefine LWS_NO_EXTENSIONS
|
||||
#cmakedefine LWS_WITHOUT_EXTENSIONS
|
||||
|
||||
/* notice if client or server gone */
|
||||
#cmakedefine LWS_WITHOUT_SERVER
|
||||
#cmakedefine LWS_WITHOUT_CLIENT
|
||||
|
||||
#cmakedefine LWS_WITH_POLL
|
||||
|
||||
/* Enable libev io loop */
|
||||
#cmakedefine LWS_USE_LIBEV
|
||||
#cmakedefine LWS_WITH_LIBEV
|
||||
|
||||
/* Enable libuv io loop */
|
||||
#cmakedefine LWS_USE_LIBUV
|
||||
#cmakedefine LWS_WITH_LIBUV
|
||||
|
||||
/* Enable libevent io loop */
|
||||
#cmakedefine LWS_USE_LIBEVENT
|
||||
#cmakedefine LWS_WITH_LIBEVENT
|
||||
|
||||
/* Build with support for ipv6 */
|
||||
#cmakedefine LWS_USE_IPV6
|
||||
#cmakedefine LWS_WITH_IPV6
|
||||
|
||||
/* Build with support for UNIX domain socket */
|
||||
#cmakedefine LWS_USE_UNIX_SOCK
|
||||
#cmakedefine LWS_WITH_UNIX_SOCK
|
||||
|
||||
/* Build with support for HTTP2 */
|
||||
#cmakedefine LWS_USE_HTTP2
|
||||
#cmakedefine LWS_WITH_HTTP2
|
||||
|
||||
/* Turn on latency measuring code */
|
||||
#cmakedefine LWS_LATENCY
|
||||
|
@ -92,8 +104,12 @@
|
|||
#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
#cmakedefine LWS_HAVE_SSL_CTX_set1_param
|
||||
#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host
|
||||
#cmakedefine LWS_HAVE_RSA_SET0_KEY
|
||||
#cmakedefine LWS_HAVE_X509_get_key_usage
|
||||
#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate
|
||||
|
||||
#cmakedefine LWS_HAVE_UV_VERSION_H
|
||||
#cmakedefine LWS_HAVE_PTHREAD_H
|
||||
|
||||
/* CGI apis */
|
||||
#cmakedefine LWS_WITH_CGI
|
||||
|
@ -112,6 +128,7 @@
|
|||
#cmakedefine LWS_WITH_SERVER_STATUS
|
||||
|
||||
#cmakedefine LWS_WITH_STATEFUL_URLDECODE
|
||||
#cmakedefine LWS_WITH_PEER_LIMITS
|
||||
|
||||
/* Maximum supported service threads */
|
||||
#define LWS_MAX_SMP ${LWS_MAX_SMP}
|
||||
|
@ -143,11 +160,22 @@
|
|||
#cmakedefine LWS_HAVE__ATOI64
|
||||
#cmakedefine LWS_HAVE__STAT32I64
|
||||
|
||||
#cmakedefine LWS_WITH_JWS
|
||||
#cmakedefine LWS_WITH_ACME
|
||||
#cmakedefine LWS_WITH_SELFTESTS
|
||||
|
||||
#cmakedefine LWS_HAVE_MALLOC_H
|
||||
|
||||
#cmakedefine LWS_HAVE_PIPE2
|
||||
|
||||
/* OpenSSL various APIs */
|
||||
|
||||
#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
|
||||
#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
|
||||
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
|
||||
#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
|
||||
#cmakedefine LWS_HAVE_SSL_get0_alpn_selected
|
||||
#cmakedefine LWS_HAVE_SSL_set_alpn_protos
|
||||
|
||||
#cmakedefine LWS_HAS_INTPTR_T
|
||||
|
|
@ -28,12 +28,6 @@
|
|||
/* Define to 1 if you have the <in6addr.h> header file. */
|
||||
#cmakedefine LWS_HAVE_IN6ADDR_H
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#cmakedefine LWS_HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `ssl' library (-lssl). */
|
||||
//#cmakedefine LWS_HAVE_LIBSSL
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#cmakedefine LWS_HAVE_MALLOC
|
||||
|
@ -87,6 +81,8 @@
|
|||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine LWS_HAVE_UNISTD_H
|
||||
|
||||
#cmakedefine LWS_HAVE_TCP_USER_TIMEOUT
|
||||
|
||||
/* Define to 1 if you have the `vfork' function. */
|
||||
#cmakedefine LWS_HAVE_VFORK
|
||||
|
40
component.mk
40
component.mk
|
@ -1,19 +1,20 @@
|
|||
COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
|
||||
COMPONENT_DEPENDS := mbedtls openssl
|
||||
#COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
|
||||
|
||||
COMPONENT_OWNBUILDTARGET:= 1
|
||||
COMPONENT_OWNBUILDTARGET := 1
|
||||
|
||||
CROSS_PATH1:=$(shell which xtensa-esp32-elf-gcc )
|
||||
CROSS_PATH:= $(shell dirname $(CROSS_PATH1) )/..
|
||||
CROSS_PATH1 := $(shell which xtensa-esp32-elf-gcc )
|
||||
CROSS_PATH := $(shell dirname $(CROSS_PATH1) )/..
|
||||
|
||||
#-DLWS_USE_BORINGSSL=1 \
|
||||
# -DOPENSSL_ROOT_DIR="${PWD}/../../boringssl" \
|
||||
# -DOPENSSL_LIBRARIES="${PWD}/../../boringssl/build/ssl/libssl.a;${PWD}/../../boringssl/build/crypto/libcrypto.a" \
|
||||
# -DOPENSSL_INCLUDE_DIRS="${PWD}/../../boringssl/include" \
|
||||
|
||||
# -DNDEBUG=1 after cflags
|
||||
# -DOPENSSL_LIBRARIES=x \
|
||||
# -DCOMPONENT_PATH=$(COMPONENT_PATH) \
|
||||
# detect MSYS2 environment and set generator flag if found
|
||||
# also set executable extension to .exe so that tools can be properly found
|
||||
# and disable bundled zlib
|
||||
MSYS_VERSION = $(if $(findstring Msys, $(shell uname -o)),$(word 1, $(subst ., ,$(shell uname -r))),0)
|
||||
ifneq ($(MSYS_VERSION),0)
|
||||
MSYS_FLAGS = -DLWS_WITH_BUNDLED_ZLIB=0 -DEXECUTABLE_EXT=.exe -G'MSYS Makefiles'
|
||||
endif
|
||||
|
||||
# -DNDEBUG=1 after cflags stops debug etc being built
|
||||
.PHONY: build
|
||||
build:
|
||||
cd $(COMPONENT_BUILD_DIR) ; \
|
||||
|
@ -22,13 +23,19 @@ build:
|
|||
-DIDF_PATH=$(IDF_PATH) \
|
||||
-DCROSS_PATH=$(CROSS_PATH) \
|
||||
-DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/cross-esp32.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/contrib/cross-esp32.cmake \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DOPENSSL_INCLUDE_DIR=${IDF_PATH}/components/openssl/include \
|
||||
-DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \
|
||||
-DLWS_WITH_STATS=0 \
|
||||
-DLWS_WITH_HTTP2=1 \
|
||||
-DLWS_WITH_RANGES=1 \
|
||||
-DLWS_WITH_ACME=1 \
|
||||
-DLWS_WITH_ZLIB=1 \
|
||||
-DLWS_WITH_ZIP_FOPS=1 \
|
||||
-DZLIB_LIBRARY=$(BUILD_DIR_BASE)/zlib/libzlib.a \
|
||||
-DZLIB_INCLUDE_DIR=$(COMPONENT_PATH)/../zlib \
|
||||
-DLWS_WITH_ESP32=1 ;\
|
||||
-DLWS_WITH_ESP32=1 \
|
||||
$(MSYS_FLAGS) ; \
|
||||
make && \
|
||||
cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a
|
||||
|
||||
|
@ -36,6 +43,3 @@ clean: myclean
|
|||
|
||||
myclean:
|
||||
rm -rf ./build
|
||||
|
||||
INCLUDES := $(INCLUDES) -I build/
|
||||
|
||||
|
|
|
@ -20,7 +20,13 @@ LGPL2 / GPL2 at your choice.
|
|||
Installation
|
||||
------------
|
||||
|
||||
The author provides an easy way to install the various tools he provides:
|
||||
The abi monitoring stuff is now packaged in, eg, fedora, which is a lot
|
||||
easier than using the helper script.
|
||||
|
||||
```
|
||||
# dnf install abi-tracker vtable-dumper
|
||||
|
||||
Otherwise, the author provides an "easy way" to install the various tools he provides:
|
||||
|
||||
git clone https://github.com/lvc/installer
|
||||
cd installer
|
||||
|
|
|
@ -101,7 +101,7 @@ PATH=$TOOLCHAIN_PATH:$PATH cmake \
|
|||
-DLWS_WITHOUT_DAEMONIZE=ON \
|
||||
-DLWS_WITHOUT_TESTAPPS=ON \
|
||||
-DLWS_IPV6=OFF \
|
||||
-DLWS_USE_BUNDLED_ZLIB=OFF \
|
||||
-DLWS_WITH_BUNDLED_ZLIB=OFF \
|
||||
-DLWS_WITH_SSL=ON \
|
||||
-DLWS_WITH_HTTP2=ON \
|
||||
-DLWS_OPENSSL_LIBRARIES="$TOOLCHAIN_PATH/../lib/libssl.a;$TOOLCHAIN_PATH/../lib/libcrypto.a" \
|
||||
|
|
|
@ -10,14 +10,15 @@
|
|||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
|
||||
# Name of C compiler.
|
||||
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc")
|
||||
set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar")
|
||||
set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib")
|
||||
set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld")
|
||||
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc${EXECUTABLE_EXT}")
|
||||
set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar${EXECUTABLE_EXT}")
|
||||
set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib${EXECUTABLE_EXT}")
|
||||
set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld${EXECUTABLE_EXT}")
|
||||
|
||||
SET(CMAKE_C_FLAGS "-nostdlib -Wall -Werror \
|
||||
-I${BUILD_DIR_BASE}/include \
|
||||
-I${IDF_PATH}/components/mdns/include \
|
||||
-I${IDF_PATH}/components/heap/include \
|
||||
-I${IDF_PATH}/components/driver/include \
|
||||
-I${IDF_PATH}/components/spi_flash/include \
|
||||
-I${IDF_PATH}/components/nvs_flash/include \
|
||||
|
@ -29,6 +30,8 @@ SET(CMAKE_C_FLAGS "-nostdlib -Wall -Werror \
|
|||
-I${IDF_PATH}/components/bootloader_support/include/ \
|
||||
-I${IDF_PATH}/components/app_update/include/ \
|
||||
-I$(IDF_PATH)/components/soc/esp32/include/ \
|
||||
-I$(IDF_PATH)/components/soc/include/ \
|
||||
-I$(IDF_PATH)/components/vfs/include/ \
|
||||
${LWS_C_FLAGS} -Os \
|
||||
-I${IDF_PATH}/components/nvs_flash/test_nvs_host \
|
||||
-I${IDF_PATH}/components/freertos/include" CACHE STRING "" FORCE)
|
|
@ -25,13 +25,13 @@ CMAKE_OPTIONS += -DLWS_WITHOUT_TESTAPPS=$(if $(CONFIG_PACKAGE_libwebsockets-exam
|
|||
|
||||
# for wolfssl, define these in addition to LWS_OPENSSL_SUPPORT and
|
||||
# edit package/libs/wolfssl/Makefile to include --enable-opensslextra
|
||||
# CMAKE_OPTIONS += -DLWS_USE_WOLFSSL=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITH_WOLFSSL=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WOLFSSL_LIBRARIES=$(STAGING_DIR)/usr/lib/libwolfssl.so
|
||||
# CMAKE_OPTIONS += -DLWS_WOLFSSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include
|
||||
|
||||
# for cyassl, define these in addition to LWS_OPENSSL_SUPPORT and
|
||||
# edit package/libs/wolfssl/Makefile to include --enable-opensslextra
|
||||
# CMAKE_OPTIONS += -DLWS_USE_CYASSL=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITH_CYASSL=ON
|
||||
# CMAKE_OPTIONS += -DLWS_CYASSL_LIBRARIES=$(STAGING_DIR)/usr/lib/libcyassl.so
|
||||
# CMAKE_OPTIONS += -DLWS_CYASSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include
|
||||
|
29
contrib/cross-w32.cmake
Normal file
29
contrib/cross-w32.cmake
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# CMake Toolchain file for crosscompiling on 32bit Windows platforms.
|
||||
#
|
||||
# This can be used when running cmake in the following way:
|
||||
# cd build/
|
||||
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w32.cmake -DLWS_WITH_SSL=0
|
||||
#
|
||||
|
||||
set(CROSS_PATH /opt/mingw32)
|
||||
|
||||
# Target operating system name.
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Name of C compiler.
|
||||
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-gcc")
|
||||
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-g++")
|
||||
set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-windres")
|
||||
set(CMAKE_C_FLAGS "-Wno-error")
|
||||
|
||||
# Where to look for the target environment. (More paths can be added here)
|
||||
set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}")
|
||||
|
||||
# Adjust the default behavior of the FIND_XXX() commands:
|
||||
# search programs in the host environment only.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
# Search headers and libraries in the target environment only.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
29
contrib/cross-w64.cmake
Normal file
29
contrib/cross-w64.cmake
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# CMake Toolchain file for crosscompiling on 64bit Windows platforms.
|
||||
#
|
||||
# This can be used when running cmake in the following way:
|
||||
# cd build/
|
||||
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w64.cmake -DLWS_WITH_SSL=0
|
||||
#
|
||||
|
||||
set(CROSS_PATH /opt/mingw64)
|
||||
|
||||
# Target operating system name.
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Name of C compiler.
|
||||
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-gcc")
|
||||
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-g++")
|
||||
set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-windres")
|
||||
set(CMAKE_C_FLAGS "-Wno-error")
|
||||
|
||||
# Where to look for the target environment. (More paths can be added here)
|
||||
set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}")
|
||||
|
||||
# Adjust the default behavior of the FIND_XXX() commands:
|
||||
# search programs in the host environment only.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
# Search headers and libraries in the target environment only.
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
BIN
doc-assets/lws-overview.png
Normal file
BIN
doc-assets/lws-overview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 545 KiB |
BIN
doc-assets/lws-smp-example.png
Normal file
BIN
doc-assets/lws-smp-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
BIN
doc-assets/lws-smp-ov.png
Normal file
BIN
doc-assets/lws-smp-ov.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
8
lib/.gitignore
vendored
8
lib/.gitignore
vendored
|
@ -1,8 +0,0 @@
|
|||
#Ignore build files
|
||||
Makefile
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
.libs
|
||||
.deps
|
||||
|
14
lib/README.md
Normal file
14
lib/README.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
## Library sources layout
|
||||
|
||||
Code that goes in the libwebsockets library itself lives down ./lib
|
||||
|
||||
Path|Sources
|
||||
---|---
|
||||
lib/core|Core lws code related to generic fd and wsi servicing and management
|
||||
lib/event-libs|Code containing optional event-lib specific adaptations
|
||||
lib/misc|Code for various mostly optional miscellaneous features
|
||||
lib/plat|Platform-specific adaptation code
|
||||
lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc
|
||||
lib/tls|Code supporting the various TLS libraries
|
||||
libwebsockets.h|Public API header for the whole of lws
|
||||
|
83
lib/alloc.c
83
lib/alloc.c
|
@ -1,83 +0,0 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
|
||||
#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000
|
||||
|
||||
void *__attribute__((weak))
|
||||
TEE_Malloc(uint32_t size, uint32_t hint)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void *__attribute__((weak))
|
||||
TEE_Realloc(void *buffer, uint32_t newSize)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void __attribute__((weak))
|
||||
TEE_Free(void *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void *lws_realloc(void *ptr, size_t size)
|
||||
{
|
||||
return TEE_Realloc(ptr, size);
|
||||
}
|
||||
|
||||
void *lws_malloc(size_t size)
|
||||
{
|
||||
return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
|
||||
}
|
||||
|
||||
void lws_free(void *p)
|
||||
{
|
||||
TEE_Free(p);
|
||||
}
|
||||
|
||||
void *lws_zalloc(size_t size)
|
||||
{
|
||||
void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
|
||||
if (ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
|
||||
{
|
||||
(void)cb;
|
||||
}
|
||||
#else
|
||||
|
||||
static void *_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if (size)
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
return (void *)TEE_Realloc(ptr, size);
|
||||
#else
|
||||
return (void *)realloc(ptr, size);
|
||||
#endif
|
||||
else if (ptr)
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *(*_lws_realloc)(void *ptr, size_t size) = _realloc;
|
||||
|
||||
void *lws_realloc(void *ptr, size_t size)
|
||||
{
|
||||
return _lws_realloc(ptr, size);
|
||||
}
|
||||
|
||||
void *lws_zalloc(size_t size)
|
||||
{
|
||||
void *ptr = _lws_realloc(NULL, size);
|
||||
if (ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
|
||||
{
|
||||
_lws_realloc = cb;
|
||||
}
|
||||
#endif
|
1402
lib/client.c
1402
lib/client.c
File diff suppressed because it is too large
Load diff
92
lib/core/alloc.c
Normal file
92
lib/core/alloc.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include "core/private.h"
|
||||
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
|
||||
#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000
|
||||
|
||||
void *__attribute__((weak))
|
||||
TEE_Malloc(uint32_t size, uint32_t hint)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void *__attribute__((weak))
|
||||
TEE_Realloc(void *buffer, uint32_t newSize)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void __attribute__((weak))
|
||||
TEE_Free(void *buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void *lws_realloc(void *ptr, size_t size, const char *reason)
|
||||
{
|
||||
return TEE_Realloc(ptr, size);
|
||||
}
|
||||
|
||||
void *lws_malloc(size_t size, const char *reason)
|
||||
{
|
||||
return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
|
||||
}
|
||||
|
||||
void lws_free(void *p)
|
||||
{
|
||||
TEE_Free(p);
|
||||
}
|
||||
|
||||
void *lws_zalloc(size_t size, const char *reason)
|
||||
{
|
||||
void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
|
||||
if (ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
|
||||
{
|
||||
(void)cb;
|
||||
}
|
||||
#else
|
||||
|
||||
static void *_realloc(void *ptr, size_t size, const char *reason)
|
||||
{
|
||||
if (size) {
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__,
|
||||
(unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size);
|
||||
#else
|
||||
lwsl_debug("%s: size %lu: %s\n", __func__,
|
||||
(unsigned long)size, reason);
|
||||
#endif
|
||||
#if defined(LWS_PLAT_OPTEE)
|
||||
return (void *)TEE_Realloc(ptr, size);
|
||||
#else
|
||||
return (void *)realloc(ptr, size);
|
||||
#endif
|
||||
}
|
||||
if (ptr)
|
||||
free(ptr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc;
|
||||
|
||||
void *lws_realloc(void *ptr, size_t size, const char *reason)
|
||||
{
|
||||
return _lws_realloc(ptr, size, reason);
|
||||
}
|
||||
|
||||
void *lws_zalloc(size_t size, const char *reason)
|
||||
{
|
||||
void *ptr = _lws_realloc(NULL, size, reason);
|
||||
if (ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
|
||||
{
|
||||
_lws_realloc = cb;
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
3452
lib/core/libwebsockets.c
Normal file
3452
lib/core/libwebsockets.c
Normal file
File diff suppressed because it is too large
Load diff
308
lib/core/output.c
Normal file
308
lib/core/output.c
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
/*
|
||||
* notice this returns number of bytes consumed, or -1
|
||||
*/
|
||||
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
size_t real_len = len;
|
||||
unsigned int n;
|
||||
|
||||
// lwsl_hexdump_err(buf, len);
|
||||
|
||||
/*
|
||||
* Detect if we got called twice without going through the
|
||||
* event loop to handle pending. This would be caused by either
|
||||
* back-to-back writes in one WRITABLE (illegal) or calling lws_write()
|
||||
* from outside the WRITABLE callback (illegal).
|
||||
*/
|
||||
if (wsi->could_have_pending) {
|
||||
lwsl_hexdump_level(LLL_ERR, buf, len);
|
||||
lwsl_err("** %p: vh: %s, prot: %s, role %s: "
|
||||
"Illegal back-to-back write of %lu detected...\n",
|
||||
wsi, wsi->vhost->name, wsi->protocol->name,
|
||||
wsi->role_ops->name,
|
||||
(unsigned long)len);
|
||||
// assert(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
/* just ignore sends after we cleared the truncation buffer */
|
||||
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
|
||||
return (int)len;
|
||||
|
||||
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
|
||||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
|
||||
lwsl_hexdump_level(LLL_ERR, buf, len);
|
||||
lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n"
|
||||
" It's illegal to do an lws_write outside of\n"
|
||||
" the writable callback: fix your code\n",
|
||||
wsi, wsi->vhost->name, wsi->protocol->name,
|
||||
(unsigned long)len);
|
||||
assert(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
|
||||
lwsl_warn("** error invalid sock but expected to send\n");
|
||||
|
||||
/* limit sending */
|
||||
if (wsi->protocol->tx_packet_size)
|
||||
n = (int)wsi->protocol->tx_packet_size;
|
||||
else {
|
||||
n = (int)wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = context->pt_serv_buf_size;
|
||||
}
|
||||
n += LWS_PRE + 4;
|
||||
if (n > len)
|
||||
n = (int)len;
|
||||
|
||||
/* nope, send it on the socket directly */
|
||||
lws_latency_pre(context, wsi);
|
||||
n = lws_ssl_capable_write(wsi, buf, n);
|
||||
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
|
||||
|
||||
/* something got written, it can have been truncated now */
|
||||
wsi->could_have_pending = 1;
|
||||
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
/* we're going to close, let close know sends aren't possible */
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
/*
|
||||
* nothing got sent, not fatal. Retry the whole thing later,
|
||||
* ie, implying treat it was a truncated send so it gets
|
||||
* retried
|
||||
*/
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* we were already handling a truncated send?
|
||||
*/
|
||||
if (wsi->trunc_len) {
|
||||
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
|
||||
wsi->trunc_offset += n;
|
||||
wsi->trunc_len -= n;
|
||||
|
||||
if (!wsi->trunc_len) {
|
||||
lwsl_info("** %p partial send completed\n", wsi);
|
||||
/* done with it, but don't free it */
|
||||
n = (int)real_len;
|
||||
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
|
||||
lwsl_info("** %p signalling to close now\n", wsi);
|
||||
return -1; /* retry closing now */
|
||||
}
|
||||
}
|
||||
/* always callback on writeable */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if ((unsigned int)n == real_len)
|
||||
/* what we just sent went out cleanly */
|
||||
return n;
|
||||
|
||||
/*
|
||||
* Newly truncated send. Buffer the remainder (it will get
|
||||
* first priority next time the socket is writable).
|
||||
*/
|
||||
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
|
||||
(unsigned long)real_len);
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
|
||||
|
||||
/*
|
||||
* - if we still have a suitable malloc lying around, use it
|
||||
* - or, if too small, reallocate it
|
||||
* - or, if no buffer, create it
|
||||
*/
|
||||
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
|
||||
lws_free(wsi->trunc_alloc);
|
||||
|
||||
wsi->trunc_alloc_len = (unsigned int)(real_len - n);
|
||||
wsi->trunc_alloc = lws_malloc(real_len - n,
|
||||
"truncated send alloc");
|
||||
if (!wsi->trunc_alloc) {
|
||||
lwsl_err("truncated send: unable to malloc %lu\n",
|
||||
(unsigned long)(real_len - n));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
wsi->trunc_offset = 0;
|
||||
wsi->trunc_len = (unsigned int)(real_len - n);
|
||||
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
|
||||
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
/* stash original destination for fulfilling UDP partials */
|
||||
wsi->udp->sa_pending = wsi->udp->sa;
|
||||
wsi->udp->salen_pending = wsi->udp->salen;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* since something buffered, force it to get another chance to send */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return (int)real_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol wp)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
struct lws_write_passthru pas;
|
||||
|
||||
pas.buf = buf;
|
||||
pas.len = len;
|
||||
pas.wp = wp;
|
||||
pas.wsi = wsi;
|
||||
|
||||
if (wsi->parent->protocol->callback(wsi->parent,
|
||||
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
|
||||
wsi->parent->user_space,
|
||||
(void *)&pas, 0))
|
||||
return 1;
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
|
||||
|
||||
if ((int)len < 0) {
|
||||
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
|
||||
(int)len, (unsigned long)len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
wsi->http.access_log.sent += len;
|
||||
#endif
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.tx += len;
|
||||
|
||||
assert(wsi->role_ops);
|
||||
if (!wsi->role_ops->write_role_protocol)
|
||||
return lws_issue_raw(wsi, buf, len);
|
||||
|
||||
return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n = 0;
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
wsi->udp->salen = sizeof(wsi->udp->sa);
|
||||
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
|
||||
&wsi->udp->sa, &wsi->udp->salen);
|
||||
#endif
|
||||
} else
|
||||
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
|
||||
|
||||
if (n >= 0) {
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK ||
|
||||
LWS_ERRNO == LWS_EINTR)
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
|
||||
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
#if !defined(LWS_WITH_ESP32)
|
||||
if (wsi->trunc_len)
|
||||
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
|
||||
else
|
||||
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
|
||||
#endif
|
||||
} else
|
||||
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
|
||||
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
|
||||
if (n >= 0)
|
||||
return n;
|
||||
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK ||
|
||||
LWS_ERRNO == LWS_EINTR) {
|
||||
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
|
||||
lws_set_blocking_send(wsi);
|
||||
}
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
|
||||
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
|
||||
len, wsi->desc.sockfd, n, LWS_ERRNO);
|
||||
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending_no_ssl(struct lws *wsi)
|
||||
{
|
||||
(void)wsi;
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
return 100;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -19,21 +19,31 @@
|
|||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
int
|
||||
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
||||
{
|
||||
#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && !defined(LWS_WITH_LIBEVENT)
|
||||
volatile struct lws_context_per_thread *vpt;
|
||||
#endif
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws_context *context;
|
||||
int ret = 0, pa_events = 1;
|
||||
struct lws_pollfd *pfd;
|
||||
int sampled_tid, tid;
|
||||
|
||||
if (!wsi || wsi->position_in_fds_table < 0)
|
||||
if (!wsi)
|
||||
return 0;
|
||||
|
||||
if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
|
||||
assert(wsi->position_in_fds_table == LWS_NO_FDS_POS ||
|
||||
wsi->position_in_fds_table >= 0);
|
||||
|
||||
if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
|
||||
return 0;
|
||||
|
||||
if (((volatile struct lws *)wsi)->handling_pollout &&
|
||||
!_and && _or == LWS_POLLOUT) {
|
||||
/*
|
||||
* Happening alongside service thread handling POLLOUT.
|
||||
* The danger is when he is finished, he will disable POLLOUT,
|
||||
|
@ -42,7 +52,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
* Instead of changing the fds, inform the service thread
|
||||
* what happened, and ask it to leave POLLOUT active on exit
|
||||
*/
|
||||
wsi->leave_pollout_active = 1;
|
||||
((volatile struct lws *)wsi)->leave_pollout_active = 1;
|
||||
/*
|
||||
* by definition service thread is not in poll wait, so no need
|
||||
* to cancel service
|
||||
|
@ -55,45 +65,106 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
|
||||
context = wsi->context;
|
||||
pt = &context->pt[(int)wsi->tsi];
|
||||
assert(wsi->position_in_fds_table >= 0 &&
|
||||
wsi->position_in_fds_table < pt->fds_count);
|
||||
|
||||
assert(wsi->position_in_fds_table < (int)pt->fds_count);
|
||||
|
||||
#if !defined(LWS_WITH_LIBUV) && \
|
||||
!defined(LWS_WITH_LIBEV) && \
|
||||
!defined(LWS_WITH_LIBEVENT)
|
||||
/*
|
||||
* This only applies when we use the default poll() event loop.
|
||||
*
|
||||
* BSD can revert pa->events at any time, when the kernel decides to
|
||||
* exit from poll(). We can't protect against it using locking.
|
||||
*
|
||||
* Therefore we must check first if the service thread is in poll()
|
||||
* wait; if so, we know we must be being called from a foreign thread,
|
||||
* and we must keep a strictly ordered list of changes we made instead
|
||||
* of trying to apply them, since when poll() exits, which may happen
|
||||
* at any time it would revert our changes.
|
||||
*
|
||||
* The plat code will apply them when it leaves the poll() wait
|
||||
* before doing anything else.
|
||||
*/
|
||||
|
||||
vpt = (volatile struct lws_context_per_thread *)pt;
|
||||
|
||||
vpt->foreign_spinlock = 1;
|
||||
lws_memory_barrier();
|
||||
|
||||
if (vpt->inside_poll) {
|
||||
struct lws_foreign_thread_pollfd *ftp, **ftp1;
|
||||
/*
|
||||
* We are certainly a foreign thread trying to change events
|
||||
* while the service thread is in the poll() wait.
|
||||
*
|
||||
* Create a list of changes to be applied after poll() exit,
|
||||
* instead of trying to apply them now.
|
||||
*/
|
||||
ftp = lws_malloc(sizeof(*ftp), "ftp");
|
||||
if (!ftp) {
|
||||
vpt->foreign_spinlock = 0;
|
||||
lws_memory_barrier();
|
||||
ret = -1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ftp->_and = _and;
|
||||
ftp->_or = _or;
|
||||
ftp->fd_index = wsi->position_in_fds_table;
|
||||
ftp->next = NULL;
|
||||
|
||||
/* place at END of list to maintain order */
|
||||
ftp1 = (struct lws_foreign_thread_pollfd **)
|
||||
&vpt->foreign_pfd_list;
|
||||
while (*ftp1)
|
||||
ftp1 = &((*ftp1)->next);
|
||||
|
||||
*ftp1 = ftp;
|
||||
vpt->foreign_spinlock = 0;
|
||||
lws_memory_barrier();
|
||||
lws_cancel_service_pt(wsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
vpt->foreign_spinlock = 0;
|
||||
lws_memory_barrier();
|
||||
#endif
|
||||
|
||||
pfd = &pt->fds[wsi->position_in_fds_table];
|
||||
pa->fd = wsi->desc.sockfd;
|
||||
lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, pa->fd, pfd->events, (pfd->events & ~_and) | _or);
|
||||
pa->prev_events = pfd->events;
|
||||
pa->events = pfd->events = (pfd->events & ~_and) | _or;
|
||||
|
||||
//lwsl_notice("%s: wsi %p, posin %d. from %d -> %d\n", __func__, wsi, wsi->position_in_fds_table, pa->prev_events, pa->events);
|
||||
|
||||
|
||||
if (wsi->http2_substream)
|
||||
return 0;
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
|
||||
wsi->user_space, (void *)pa, 0)) {
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
|
||||
wsi->user_space, (void *)pa, 0)) {
|
||||
ret = -1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (_and & LWS_POLLIN) {
|
||||
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
|
||||
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
|
||||
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ);
|
||||
}
|
||||
if (_or & LWS_POLLIN) {
|
||||
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
}
|
||||
if (_and & LWS_POLLOUT) {
|
||||
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
|
||||
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
|
||||
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
|
||||
}
|
||||
if (_or & LWS_POLLOUT) {
|
||||
lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
|
||||
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
|
||||
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
|
||||
if (context->event_loop_ops->io) {
|
||||
if (_and & LWS_POLLIN)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_STOP | LWS_EV_READ);
|
||||
|
||||
if (_or & LWS_POLLIN)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
if (_and & LWS_POLLOUT)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_STOP | LWS_EV_WRITE);
|
||||
|
||||
if (_or & LWS_POLLOUT)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_START | LWS_EV_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -103,20 +174,16 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
* ... and the service thread is waiting ...
|
||||
* then cancel it to force a restart with our changed events
|
||||
*/
|
||||
#if LWS_POSIX
|
||||
pa_events = pa->prev_events != pa->events;
|
||||
#endif
|
||||
|
||||
if (pa_events) {
|
||||
|
||||
if (lws_plat_change_pollfd(context, wsi, pfd)) {
|
||||
lwsl_info("%s failed\n", __func__);
|
||||
ret = -1;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
sampled_tid = context->service_tid;
|
||||
if (sampled_tid) {
|
||||
if (sampled_tid && wsi->vhost) {
|
||||
tid = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
if (tid == -1) {
|
||||
|
@ -127,34 +194,39 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
|
|||
lws_cancel_service_pt(wsi);
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
/*
|
||||
* Enable or disable listen sockets on this pt globally...
|
||||
* it's modulated according to the pt having space for a new accept.
|
||||
*/
|
||||
static void
|
||||
lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
|
||||
lws_accept_modulation(struct lws_context *context,
|
||||
struct lws_context_per_thread *pt, int allow)
|
||||
{
|
||||
// multithread listen seems broken
|
||||
#if 0
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
struct lws_pollargs pa1;
|
||||
|
||||
while (vh) {
|
||||
if (allow)
|
||||
_lws_change_pollfd(pt->wsi_listening,
|
||||
if (vh->lserv_wsi) {
|
||||
if (allow)
|
||||
_lws_change_pollfd(vh->lserv_wsi,
|
||||
0, LWS_POLLIN, &pa1);
|
||||
else
|
||||
_lws_change_pollfd(pt->wsi_listening,
|
||||
else
|
||||
_lws_change_pollfd(vh->lserv_wsi,
|
||||
LWS_POLLIN, 0, &pa1);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
||||
__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
@ -170,54 +242,46 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
|
||||
if (wsi->desc.sockfd >= context->max_fds) {
|
||||
lwsl_err("Socket fd %d is too high (%d)\n",
|
||||
wsi->desc.sockfd, context->max_fds);
|
||||
#if !defined(_WIN32)
|
||||
if (wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) {
|
||||
lwsl_err("Socket fd %d is too high (%d) offset %d\n",
|
||||
wsi->desc.sockfd, context->max_fds, lws_plat_socket_offset());
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(wsi);
|
||||
assert(wsi->vhost);
|
||||
assert(wsi->event_pipe || wsi->vhost);
|
||||
assert(lws_socket_is_valid(wsi->desc.sockfd));
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 1))
|
||||
return -1;
|
||||
|
||||
lws_pt_lock(pt);
|
||||
pt->count_conns++;
|
||||
insert_wsi(context, wsi);
|
||||
#if defined(LWS_WITH_ESP8266)
|
||||
if (wsi->position_in_fds_table == -1)
|
||||
#endif
|
||||
wsi->position_in_fds_table = pt->fds_count;
|
||||
|
||||
// lwsl_notice("%s: %p: setting posinfds %d\n", __func__, wsi, wsi->position_in_fds_table);
|
||||
wsi->position_in_fds_table = pt->fds_count;
|
||||
|
||||
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
|
||||
#if LWS_POSIX
|
||||
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
|
||||
#else
|
||||
pt->fds[wsi->position_in_fds_table].events = 0; // LWS_POLLIN;
|
||||
#endif
|
||||
pa.events = pt->fds[pt->fds_count].events;
|
||||
|
||||
lws_plat_insert_socket_into_fds(context, wsi);
|
||||
|
||||
/* external POLL support via protocol 0 */
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
#ifndef LWS_NO_SERVER
|
||||
/* if no more room, defeat accepts on this thread */
|
||||
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
|
||||
lws_accept_modulation(pt, 0);
|
||||
lws_accept_modulation(context, pt, 0);
|
||||
#endif
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *)&pa, 1))
|
||||
ret = -1;
|
||||
|
||||
|
@ -225,15 +289,13 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
}
|
||||
|
||||
int
|
||||
remove_wsi_socket_from_fds(struct lws *wsi)
|
||||
__remove_wsi_socket_from_fds(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
|
||||
#if !defined(LWS_WITH_ESP8266)
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
struct lws *end_wsi;
|
||||
int v;
|
||||
#endif
|
||||
int m, ret = 0;
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
|
@ -241,14 +303,16 @@ remove_wsi_socket_from_fds(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
|
||||
if (wsi->desc.sockfd > context->max_fds) {
|
||||
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, context->max_fds);
|
||||
#if !defined(_WIN32)
|
||||
if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
|
||||
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
|
||||
context->max_fds);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *)&pa, 1))
|
||||
return -1;
|
||||
|
||||
|
@ -257,53 +321,89 @@ remove_wsi_socket_from_fds(struct lws *wsi)
|
|||
/* the guy who is to be deleted's slot index in pt->fds */
|
||||
m = wsi->position_in_fds_table;
|
||||
|
||||
#if !defined(LWS_WITH_ESP8266)
|
||||
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
|
||||
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
|
||||
/* these are the only valid possibilities for position_in_fds_table */
|
||||
assert(m == LWS_NO_FDS_POS || (m >= 0 &&
|
||||
(unsigned int)m < pt->fds_count));
|
||||
|
||||
lws_pt_lock(pt);
|
||||
if (context->event_loop_ops->io)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE |
|
||||
LWS_EV_PREPARE_DELETION);
|
||||
|
||||
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
|
||||
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
|
||||
pt->fds_count, pt->fds[pt->fds_count].fd);
|
||||
|
||||
/* have the last guy take up the now vacant slot */
|
||||
pt->fds[m] = pt->fds[pt->fds_count - 1];
|
||||
#endif
|
||||
/* this decrements pt->fds_count */
|
||||
lws_plat_delete_socket_from_fds(context, wsi, m);
|
||||
#if !defined(LWS_WITH_ESP8266)
|
||||
v = (int) pt->fds[m].fd;
|
||||
/* end guy's "position in fds table" is now the deletion guy's old one */
|
||||
end_wsi = wsi_from_fd(context, v);
|
||||
if (!end_wsi) {
|
||||
lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", (int)pt->fds[m].fd, m, pt->fds_count);
|
||||
assert(0);
|
||||
} else
|
||||
end_wsi->position_in_fds_table = m;
|
||||
if (m != LWS_NO_FDS_POS) {
|
||||
|
||||
/* deletion guy's lws_lookup entry needs nuking */
|
||||
delete_from_fd(context, wsi->desc.sockfd);
|
||||
/* removed wsi has no position any more */
|
||||
wsi->position_in_fds_table = -1;
|
||||
/* have the last guy take up the now vacant slot */
|
||||
pt->fds[m] = pt->fds[pt->fds_count - 1];
|
||||
/* this decrements pt->fds_count */
|
||||
lws_plat_delete_socket_from_fds(context, wsi, m);
|
||||
v = (int) pt->fds[m].fd;
|
||||
/* end guy's "position in fds table" is now the deletion guy's old one */
|
||||
end_wsi = wsi_from_fd(context, v);
|
||||
if (!end_wsi) {
|
||||
lwsl_err("no wsi for fd %d at pos %d, pt->fds_count=%d\n",
|
||||
(int)pt->fds[m].fd, m, pt->fds_count);
|
||||
assert(0);
|
||||
} else
|
||||
end_wsi->position_in_fds_table = m;
|
||||
|
||||
/* deletion guy's lws_lookup entry needs nuking */
|
||||
delete_from_fd(context, wsi->desc.sockfd);
|
||||
|
||||
/* removed wsi has no position any more */
|
||||
wsi->position_in_fds_table = LWS_NO_FDS_POS;
|
||||
}
|
||||
|
||||
/* remove also from external POLL support via protocol 0 */
|
||||
if (lws_socket_is_valid(wsi->desc.sockfd))
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
#ifndef LWS_NO_SERVER
|
||||
if (!context->being_destroyed)
|
||||
/* if this made some room, accept connects on this thread */
|
||||
if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
|
||||
lws_accept_modulation(pt, 1);
|
||||
#endif
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 1))
|
||||
if (lws_socket_is_valid(wsi->desc.sockfd) && wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
if (!context->being_destroyed &&
|
||||
/* if this made some room, accept connects on this thread */
|
||||
(unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
|
||||
lws_accept_modulation(context, pt, 1);
|
||||
#endif
|
||||
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 1))
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
__lws_change_pollfd(struct lws *wsi, int _and, int _or)
|
||||
{
|
||||
struct lws_context *context;
|
||||
struct lws_pollargs pa;
|
||||
int ret = 0;
|
||||
|
||||
if (!wsi || (!wsi->protocol && !wsi->event_pipe) ||
|
||||
wsi->position_in_fds_table == LWS_NO_FDS_POS)
|
||||
return 0;
|
||||
|
||||
context = lws_get_context(wsi);
|
||||
if (!context)
|
||||
return 1;
|
||||
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
return -1;
|
||||
|
||||
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
|
||||
if (wsi->vhost &&
|
||||
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -311,29 +411,13 @@ int
|
|||
lws_change_pollfd(struct lws *wsi, int _and, int _or)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws_context *context;
|
||||
struct lws_pollargs pa;
|
||||
int ret = 0;
|
||||
|
||||
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
|
||||
return 1;
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
context = lws_get_context(wsi);
|
||||
if (!context)
|
||||
return 1;
|
||||
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
return -1;
|
||||
|
||||
pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
lws_pt_lock(pt);
|
||||
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
|
||||
lws_pt_lock(pt, __func__);
|
||||
ret = __lws_change_pollfd(wsi, _and, _or);
|
||||
lws_pt_unlock(pt);
|
||||
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
|
||||
wsi->user_space, (void *) &pa, 0))
|
||||
ret = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -342,20 +426,25 @@ LWS_VISIBLE int
|
|||
lws_callback_on_writable(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
#ifdef LWS_USE_HTTP2
|
||||
struct lws *network_wsi, *wsi2;
|
||||
int already;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
if (wsi->state == LWSS_SHUTDOWN)
|
||||
if (lwsi_state(wsi) == LRS_SHUTDOWN)
|
||||
return 0;
|
||||
|
||||
if (wsi->socket_is_permanently_unusable)
|
||||
return 0;
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
int n = lws_callback_on_writable(wsi->parent);
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->active_writable_req_us) {
|
||||
wsi->active_writable_req_us = time_in_microseconds();
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
|
||||
}
|
||||
#endif
|
||||
n = lws_callback_on_writable(wsi->parent);
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
|
@ -363,74 +452,35 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
return 1;
|
||||
}
|
||||
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!wsi->active_writable_req_us) {
|
||||
wsi->active_writable_req_us = time_in_microseconds();
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LWS_USE_HTTP2
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
|
||||
if (wsi->mode != LWSCM_HTTP2_SERVING)
|
||||
goto network_sock;
|
||||
|
||||
if (wsi->u.http2.requested_POLLOUT) {
|
||||
lwsl_info("already pending writable\n");
|
||||
return 1;
|
||||
if (wsi->role_ops->callback_on_writable) {
|
||||
if (wsi->role_ops->callback_on_writable(wsi))
|
||||
return 1;
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
}
|
||||
|
||||
if (wsi->u.http2.tx_credit <= 0) {
|
||||
/*
|
||||
* other side is not able to cope with us sending
|
||||
* anything so no matter if we have POLLOUT on our side.
|
||||
*
|
||||
* Delay waiting for our POLLOUT until peer indicates he has
|
||||
* space for more using tx window command in http2 layer
|
||||
*/
|
||||
lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi,
|
||||
wsi->u.http2.tx_credit);
|
||||
wsi->u.http2.waiting_tx_credit = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
network_wsi = lws_http2_get_network_wsi(wsi);
|
||||
already = network_wsi->u.http2.requested_POLLOUT;
|
||||
|
||||
/* mark everybody above him as requesting pollout */
|
||||
|
||||
wsi2 = wsi;
|
||||
while (wsi2) {
|
||||
wsi2->u.http2.requested_POLLOUT = 1;
|
||||
lwsl_info("mark %p pending writable\n", wsi2);
|
||||
wsi2 = wsi2->u.http2.parent_wsi;
|
||||
}
|
||||
|
||||
/* for network action, act only on the network wsi */
|
||||
|
||||
wsi = network_wsi;
|
||||
if (already)
|
||||
return 1;
|
||||
network_sock:
|
||||
#endif
|
||||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
return 1;
|
||||
|
||||
if (wsi->position_in_fds_table < 0) {
|
||||
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
|
||||
if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
|
||||
lwsl_debug("%s: failed to find socket %d\n", __func__,
|
||||
wsi->desc.sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
|
||||
if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* stitch protocol choice into the vh protocol linked list
|
||||
* We always insert ourselves at the start of the list
|
||||
|
@ -443,20 +493,16 @@ network_sock:
|
|||
void
|
||||
lws_same_vh_protocol_insert(struct lws *wsi, int n)
|
||||
{
|
||||
//lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n",
|
||||
// __func__,
|
||||
// wsi->vhost->same_vh_protocol_list[n],
|
||||
// wsi->same_vh_protocol_prev);
|
||||
|
||||
if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) {
|
||||
lws_same_vh_protocol_remove(wsi);
|
||||
lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
|
||||
}
|
||||
|
||||
wsi->same_vh_protocol_prev = /* guy who points to us */
|
||||
&wsi->vhost->same_vh_protocol_list[n];
|
||||
wsi->same_vh_protocol_next = /* old first guy is our next */
|
||||
wsi->vhost->same_vh_protocol_list[n];
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
|
||||
wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
|
||||
/* old first guy is our next */
|
||||
wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[n];
|
||||
/* we become the new first guy */
|
||||
wsi->vhost->same_vh_protocol_list[n] = wsi;
|
||||
|
||||
|
@ -464,6 +510,10 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
|
|||
/* old first guy points back to us now */
|
||||
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
||||
&wsi->same_vh_protocol_next;
|
||||
|
||||
wsi->on_same_vh_list = 1;
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -478,6 +528,11 @@ lws_same_vh_protocol_remove(struct lws *wsi)
|
|||
*/
|
||||
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
|
||||
|
||||
if (!wsi->vhost || !wsi->on_same_vh_list)
|
||||
return;
|
||||
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
|
||||
if (wsi->same_vh_protocol_prev) {
|
||||
assert (*(wsi->same_vh_protocol_prev) == wsi);
|
||||
lwsl_info("have prev %p, setting him to our next %p\n",
|
||||
|
@ -489,13 +544,15 @@ lws_same_vh_protocol_remove(struct lws *wsi)
|
|||
}
|
||||
|
||||
/* our next should point back to our prev */
|
||||
if (wsi->same_vh_protocol_next) {
|
||||
if (wsi->same_vh_protocol_next)
|
||||
wsi->same_vh_protocol_next->same_vh_protocol_prev =
|
||||
wsi->same_vh_protocol_prev;
|
||||
}
|
||||
|
||||
wsi->same_vh_protocol_prev = NULL;
|
||||
wsi->same_vh_protocol_next = NULL;
|
||||
wsi->on_same_vh_list = 0;
|
||||
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
|
||||
|
||||
|
@ -507,7 +564,6 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
|
|||
|
||||
if (protocol < vhost->protocols ||
|
||||
protocol >= (vhost->protocols + vhost->count_protocols)) {
|
||||
|
||||
lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n",
|
||||
__func__, protocol, vhost->protocols, vhost,
|
||||
(vhost->protocols + vhost->count_protocols));
|
||||
|
@ -516,19 +572,14 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
|
|||
}
|
||||
|
||||
wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols];
|
||||
//lwsl_notice("%s: protocol %p, start wsi %p\n", __func__, protocol, wsi);
|
||||
while (wsi) {
|
||||
//lwsl_notice("%s: protocol %p, this wsi %p (wsi->protocol=%p)\n",
|
||||
// __func__, protocol, wsi, wsi->protocol);
|
||||
assert(wsi->protocol == protocol);
|
||||
assert(*wsi->same_vh_protocol_prev == wsi);
|
||||
if (wsi->same_vh_protocol_next) {
|
||||
// lwsl_err("my next says %p\n", wsi->same_vh_protocol_next);
|
||||
// lwsl_err("my next's prev says %p\n",
|
||||
// wsi->same_vh_protocol_next->same_vh_protocol_prev);
|
||||
assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == &wsi->same_vh_protocol_next);
|
||||
}
|
||||
//lwsl_notice(" apv: %p\n", wsi);
|
||||
if (wsi->same_vh_protocol_next)
|
||||
assert(wsi->same_vh_protocol_next->
|
||||
same_vh_protocol_prev ==
|
||||
&wsi->same_vh_protocol_next);
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
wsi = wsi->same_vh_protocol_next;
|
||||
}
|
||||
|
@ -540,9 +591,14 @@ LWS_VISIBLE int
|
|||
lws_callback_on_writable_all_protocol(const struct lws_context *context,
|
||||
const struct lws_protocols *protocol)
|
||||
{
|
||||
struct lws_vhost *vhost = context->vhost_list;
|
||||
struct lws_vhost *vhost;
|
||||
int n;
|
||||
|
||||
if (!context)
|
||||
return 0;
|
||||
|
||||
vhost = context->vhost_list;
|
||||
|
||||
while (vhost) {
|
||||
for (n = 0; n < vhost->count_protocols; n++)
|
||||
if (protocol->callback ==
|
1756
lib/core/private.h
Normal file
1756
lib/core/private.h
Normal file
File diff suppressed because it is too large
Load diff
987
lib/core/service.c
Normal file
987
lib/core/service.c
Normal file
|
@ -0,0 +1,987 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
int
|
||||
lws_callback_as_writeable(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int n, m;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (wsi->active_writable_req_us) {
|
||||
uint64_t ul = time_in_microseconds() -
|
||||
wsi->active_writable_req_us;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_WRITABLE_DELAY, ul);
|
||||
lws_stats_atomic_max(wsi->context, pt,
|
||||
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
|
||||
wsi->active_writable_req_us = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
|
||||
|
||||
m = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, (enum lws_callback_reasons) n,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
||||
{
|
||||
volatile struct lws *vwsi = (volatile struct lws *)wsi;
|
||||
int n;
|
||||
|
||||
//lwsl_notice("%s: %p\n", __func__, wsi);
|
||||
|
||||
vwsi->leave_pollout_active = 0;
|
||||
vwsi->handling_pollout = 1;
|
||||
/*
|
||||
* if another thread wants POLLOUT on us, from here on while
|
||||
* handling_pollout is set, he will only set leave_pollout_active.
|
||||
* If we are going to disable POLLOUT, we will check that first.
|
||||
*/
|
||||
wsi->could_have_pending = 0; /* clear back-to-back write detection */
|
||||
|
||||
/*
|
||||
* user callback is lowest priority to get these notifications
|
||||
* actually, since other pending things cannot be disordered
|
||||
*
|
||||
* Priority 1: pending truncated sends are incomplete ws fragments
|
||||
* If anything else sent first the protocol would be
|
||||
* corrupted.
|
||||
*/
|
||||
|
||||
if (wsi->trunc_len) {
|
||||
//lwsl_notice("%s: completing partial\n", __func__);
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0) {
|
||||
lwsl_info("%s signalling to close\n", __func__);
|
||||
goto bail_die;
|
||||
}
|
||||
/* leave POLLOUT active either way */
|
||||
goto bail_ok;
|
||||
} else
|
||||
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
goto bail_die; /* retry closing now */
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
/*
|
||||
* A cgi master's wire protocol remains h1 or h2. He is just getting
|
||||
* his data from his child cgis.
|
||||
*/
|
||||
if (wsi->http.cgi) {
|
||||
/* also one shot */
|
||||
if (pollfd)
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
return 1;
|
||||
}
|
||||
goto user_service_go_again;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if we got here, we should have wire protocol ops set on the wsi */
|
||||
assert(wsi->role_ops);
|
||||
|
||||
if (!wsi->role_ops->handle_POLLOUT)
|
||||
goto bail_ok;
|
||||
|
||||
switch ((wsi->role_ops->handle_POLLOUT)(wsi)) {
|
||||
case LWS_HP_RET_BAIL_OK:
|
||||
goto bail_ok;
|
||||
case LWS_HP_RET_BAIL_DIE:
|
||||
goto bail_die;
|
||||
case LWS_HP_RET_USER_SERVICE:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* one shot */
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
vwsi->handling_pollout = 0;
|
||||
vwsi->leave_pollout_active = 0;
|
||||
|
||||
return lws_callback_as_writeable(wsi);
|
||||
}
|
||||
|
||||
if (pollfd) {
|
||||
int eff = vwsi->leave_pollout_active;
|
||||
|
||||
if (!eff) {
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
goto bail_die;
|
||||
}
|
||||
}
|
||||
|
||||
vwsi->handling_pollout = 0;
|
||||
|
||||
/* cannot get leave_pollout_active set after the above */
|
||||
if (!eff && wsi->leave_pollout_active) {
|
||||
/*
|
||||
* got set inbetween sampling eff and clearing
|
||||
* handling_pollout, force POLLOUT on
|
||||
*/
|
||||
lwsl_debug("leave_pollout_active\n");
|
||||
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
goto bail_die;
|
||||
}
|
||||
}
|
||||
|
||||
vwsi->leave_pollout_active = 0;
|
||||
}
|
||||
|
||||
if (lwsi_role_client(wsi) &&
|
||||
!wsi->hdr_parsing_completed &&
|
||||
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS &&
|
||||
lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY
|
||||
)
|
||||
goto bail_ok;
|
||||
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
user_service_go_again:
|
||||
#endif
|
||||
|
||||
if (wsi->role_ops->perform_user_POLLOUT) {
|
||||
if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1)
|
||||
goto bail_die;
|
||||
else
|
||||
goto bail_ok;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: %p: non mux: wsistate 0x%x, ops %s\n", __func__, wsi,
|
||||
wsi->wsistate, wsi->role_ops->name);
|
||||
|
||||
vwsi = (volatile struct lws *)wsi;
|
||||
vwsi->leave_pollout_active = 0;
|
||||
|
||||
n = lws_callback_as_writeable(wsi);
|
||||
vwsi->handling_pollout = 0;
|
||||
|
||||
if (vwsi->leave_pollout_active)
|
||||
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
|
||||
|
||||
return n;
|
||||
|
||||
/*
|
||||
* since these don't disable the POLLOUT, they are always doing the
|
||||
* right thing for leave_pollout_active whether it was set or not.
|
||||
*/
|
||||
|
||||
bail_ok:
|
||||
vwsi->handling_pollout = 0;
|
||||
vwsi->leave_pollout_active = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
bail_die:
|
||||
vwsi->handling_pollout = 0;
|
||||
vwsi->leave_pollout_active = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
__lws_service_timeout_check(struct lws *wsi, time_t sec)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int n = 0;
|
||||
|
||||
(void)n;
|
||||
|
||||
/*
|
||||
* if we went beyond the allowed time, kill the
|
||||
* connection
|
||||
*/
|
||||
if (wsi->dll_timeout.prev &&
|
||||
lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) >
|
||||
wsi->pending_timeout_limit) {
|
||||
|
||||
if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
|
||||
wsi->position_in_fds_table >= 0)
|
||||
n = pt->fds[wsi->position_in_fds_table].events;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
|
||||
|
||||
/* no need to log normal idle keepalive timeout */
|
||||
if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
|
||||
"(did hdr %d, ah %p, wl %d, pfd "
|
||||
"events %d) %llu vs %llu\n",
|
||||
(void *)wsi, wsi->pending_timeout,
|
||||
wsi->hdr_parsing_completed, wsi->http.ah,
|
||||
pt->http.ah_wait_list_length, n,
|
||||
(unsigned long long)sec,
|
||||
(unsigned long long)wsi->pending_timeout_limit);
|
||||
#if defined(LWS_WITH_CGI)
|
||||
if (wsi->http.cgi)
|
||||
lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary);
|
||||
#endif
|
||||
#else
|
||||
lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi,
|
||||
wsi->pending_timeout);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Since he failed a timeout, he already had a chance to do
|
||||
* something and was unable to... that includes situations like
|
||||
* half closed connections. So process this "failed timeout"
|
||||
* close as a violent death and don't try to do protocol
|
||||
* cleanup like flush partials.
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
|
||||
wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space,
|
||||
(void *)"Timed out waiting SSL", 21);
|
||||
|
||||
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
uint8_t *buffered;
|
||||
size_t blen;
|
||||
int ret = 0, m;
|
||||
|
||||
/* his RX is flowcontrolled, don't send remaining now */
|
||||
blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered);
|
||||
if (blen) {
|
||||
if (buf >= buffered && buf + len <= buffered + blen) {
|
||||
/* rxflow while we were spilling prev rxflow */
|
||||
lwsl_info("%s: staying in rxflow buf\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
/* a new rxflow, buffer it and warn caller */
|
||||
|
||||
m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n);
|
||||
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) {
|
||||
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
|
||||
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this is used by the platform service code to stop us waiting for network
|
||||
* activity in poll() when we have something that already needs service
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
/* Figure out if we really want to wait in poll()
|
||||
* We only need to wait if really nothing already to do and we have
|
||||
* to wait for something from network
|
||||
*/
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/* 1) if we know we are draining rx ext, do not wait in poll */
|
||||
if (pt->ws.rx_draining_ext_list)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* 2) if we know we have non-network pending data, do not wait in poll */
|
||||
|
||||
if (pt->context->tls_ops &&
|
||||
pt->context->tls_ops->fake_POLLIN_for_buffered)
|
||||
if (pt->context->tls_ops->fake_POLLIN_for_buffered(pt))
|
||||
return 0;
|
||||
|
||||
/* 3) If there is any wsi with rxflow buffered and in a state to process
|
||||
* it, we should not wait in poll
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
|
||||
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
|
||||
|
||||
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION)
|
||||
return 0;
|
||||
|
||||
} lws_end_foreach_dll(d);
|
||||
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
/*
|
||||
* POLLIN said there is something... we must read it, and either use it; or
|
||||
* if other material already in the buflist append it and return the buflist
|
||||
* head material.
|
||||
*/
|
||||
int
|
||||
lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_tokens *ebuf)
|
||||
{
|
||||
int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL);
|
||||
|
||||
ebuf->token = (char *)pt->serv_buf;
|
||||
ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
|
||||
wsi->context->pt_serv_buf_size);
|
||||
|
||||
if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior)
|
||||
goto get_from_buflist;
|
||||
|
||||
if (ebuf->len <= 0)
|
||||
return 0;
|
||||
|
||||
/* nothing in buflist already? Then just use what we read */
|
||||
|
||||
if (!prior)
|
||||
return 0;
|
||||
|
||||
/* stash what we read */
|
||||
|
||||
n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token,
|
||||
ebuf->len);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
if (n) {
|
||||
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
|
||||
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
|
||||
}
|
||||
|
||||
/* get the first buflist guy in line */
|
||||
|
||||
get_from_buflist:
|
||||
|
||||
ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
|
||||
(uint8_t **)&ebuf->token);
|
||||
|
||||
return 1; /* came from buflist */
|
||||
}
|
||||
|
||||
int
|
||||
lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
|
||||
int buffered)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int m;
|
||||
|
||||
/* it's in the buflist; we didn't use any */
|
||||
|
||||
if (!used && buffered)
|
||||
return 0;
|
||||
|
||||
if (used && buffered) {
|
||||
m = lws_buflist_use_segment(&wsi->buflist, used);
|
||||
lwsl_info("%s: draining rxflow: used %d, next %d\n",
|
||||
__func__, used, m);
|
||||
if (m)
|
||||
return 0;
|
||||
|
||||
lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
|
||||
lws_dll_lws_remove(&wsi->dll_buflist);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* any remainder goes on the buflist */
|
||||
|
||||
if (used != ebuf->len) {
|
||||
m = lws_buflist_append_segment(&wsi->buflist,
|
||||
(uint8_t *)ebuf->token + used,
|
||||
ebuf->len - used);
|
||||
if (m < 0)
|
||||
return 1; /* OOM */
|
||||
if (m) {
|
||||
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
|
||||
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt)
|
||||
{
|
||||
struct lws_pollfd pfd;
|
||||
|
||||
if (!pt->dll_head_buflist.next)
|
||||
return;
|
||||
|
||||
/*
|
||||
* service all guys with pending rxflow that reached a state they can
|
||||
* accept the pending data
|
||||
*/
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
pt->dll_head_buflist.next) {
|
||||
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
|
||||
|
||||
pfd.events = LWS_POLLIN;
|
||||
pfd.revents = LWS_POLLIN;
|
||||
pfd.fd = -1;
|
||||
|
||||
lwsl_debug("%s: rxflow processing: %p 0x%x\n", __func__, wsi,
|
||||
wsi->wsistate);
|
||||
|
||||
if (!lws_is_flowcontrolled(wsi) &&
|
||||
lwsi_state(wsi) != LRS_DEFERRING_ACTION &&
|
||||
(wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) ==
|
||||
LWS_HPI_RET_PLEASE_CLOSE_ME)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"close_and_handled");
|
||||
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
/*
|
||||
* guys that need POLLIN service again without waiting for network action
|
||||
* can force POLLIN here if not flowcontrolled, so they will get service.
|
||||
*
|
||||
* Return nonzero if anybody got their POLLIN faked
|
||||
*/
|
||||
int
|
||||
lws_service_flag_pending(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
struct lws *wsi, *wsi_next;
|
||||
#endif
|
||||
int forced = 0;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
/*
|
||||
* 1) If there is any wsi with a buflist and in a state to process
|
||||
* it, we should not wait in poll
|
||||
*/
|
||||
|
||||
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
|
||||
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
|
||||
|
||||
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) {
|
||||
forced = 1;
|
||||
break;
|
||||
}
|
||||
} lws_end_foreach_dll(d);
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
forced |= role_ops_ws.service_flag_pending(context, tsi);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
/*
|
||||
* 2) For all guys with buffered SSL read data already saved up, if they
|
||||
* are not flowcontrolled, fake their POLLIN status so they'll get
|
||||
* service to use up the buffered incoming data, even though their
|
||||
* network socket may have nothing
|
||||
*/
|
||||
wsi = pt->tls.pending_read_list;
|
||||
while (wsi) {
|
||||
wsi_next = wsi->tls.pending_read_list_next;
|
||||
pt->fds[wsi->position_in_fds_table].revents |=
|
||||
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
|
||||
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
|
||||
forced = 1;
|
||||
/*
|
||||
* he's going to get serviced now, take him off the
|
||||
* list of guys with buffered SSL. If he still has some
|
||||
* at the end of the service, he'll get put back on the
|
||||
* list then.
|
||||
*/
|
||||
__lws_ssl_remove_wsi_from_buffered_list(wsi);
|
||||
}
|
||||
|
||||
wsi = wsi_next;
|
||||
}
|
||||
#endif
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
return forced;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_service_periodic_checks(struct lws_context *context,
|
||||
struct lws_pollfd *pollfd, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
lws_sockfd_type our_fd = 0, tmp_fd;
|
||||
struct lws *wsi;
|
||||
int timed_out = 0;
|
||||
time_t now;
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
struct allocated_headers *ah;
|
||||
int m;
|
||||
#endif
|
||||
|
||||
if (!context->protocol_init_done)
|
||||
if (lws_protocol_init(context))
|
||||
return -1;
|
||||
|
||||
time(&now);
|
||||
|
||||
/*
|
||||
* handle case that system time was uninitialized when lws started
|
||||
* at boot, and got initialized a little later
|
||||
*/
|
||||
if (context->time_up < 1464083026 && now > 1464083026)
|
||||
context->time_up = now;
|
||||
|
||||
if (context->last_timeout_check_s &&
|
||||
now - context->last_timeout_check_s > 100) {
|
||||
/*
|
||||
* There has been a discontiguity. Any stored time that is
|
||||
* less than context->time_discontiguity should have context->
|
||||
* time_fixup added to it.
|
||||
*
|
||||
* Some platforms with no RTC will experience this as a normal
|
||||
* event when ntp sets their clock, but we can have started
|
||||
* long before that with a 0-based unix time.
|
||||
*/
|
||||
|
||||
context->time_discontiguity = now;
|
||||
context->time_fixup = now - context->last_timeout_check_s;
|
||||
|
||||
lwsl_notice("time discontiguity: at old time %llus, "
|
||||
"new time %llus: +%llus\n",
|
||||
(unsigned long long)context->last_timeout_check_s,
|
||||
(unsigned long long)context->time_discontiguity,
|
||||
(unsigned long long)context->time_fixup);
|
||||
|
||||
context->last_timeout_check_s = now - 1;
|
||||
}
|
||||
|
||||
if (!lws_compare_time_t(context, context->last_timeout_check_s, now))
|
||||
return 0;
|
||||
|
||||
context->last_timeout_check_s = now;
|
||||
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (!tsi && now - context->last_dump > 10) {
|
||||
lws_stats_log_dump(context);
|
||||
context->last_dump = now;
|
||||
}
|
||||
#endif
|
||||
|
||||
lws_plat_service_periodic(context);
|
||||
lws_check_deferred_free(context, 0);
|
||||
|
||||
#if defined(LWS_WITH_PEER_LIMITS)
|
||||
lws_peer_cull_peer_wait_list(context);
|
||||
#endif
|
||||
|
||||
/* retire unused deprecated context */
|
||||
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
|
||||
#if !defined(_WIN32)
|
||||
if (context->deprecated && !context->count_wsi_allocated) {
|
||||
lwsl_notice("%s: ending deprecated context\n", __func__);
|
||||
kill(getpid(), SIGINT);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/* global timeout check once per second */
|
||||
|
||||
if (pollfd)
|
||||
our_fd = pollfd->fd;
|
||||
|
||||
/*
|
||||
* Phase 1: check every wsi on the timeout check list
|
||||
*/
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
context->pt[tsi].dll_head_timeout.next) {
|
||||
wsi = lws_container_of(d, struct lws, dll_timeout);
|
||||
tmp_fd = wsi->desc.sockfd;
|
||||
if (__lws_service_timeout_check(wsi, now)) {
|
||||
/* he did time out... */
|
||||
if (tmp_fd == our_fd)
|
||||
/* it was the guy we came to service! */
|
||||
timed_out = 1;
|
||||
/* he's gone, no need to mark as handled */
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
/*
|
||||
* Phase 2: double-check active ah timeouts independent of wsi
|
||||
* timeout status
|
||||
*/
|
||||
|
||||
ah = pt->http.ah_list;
|
||||
while (ah) {
|
||||
int len;
|
||||
char buf[256];
|
||||
const unsigned char *c;
|
||||
|
||||
if (!ah->in_use || !ah->wsi || !ah->assigned ||
|
||||
(ah->wsi->vhost &&
|
||||
lws_compare_time_t(context, now, ah->assigned) <
|
||||
ah->wsi->vhost->timeout_secs_ah_idle + 360)) {
|
||||
ah = ah->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* a single ah session somehow got held for
|
||||
* an unreasonable amount of time.
|
||||
*
|
||||
* Dump info on the connection...
|
||||
*/
|
||||
wsi = ah->wsi;
|
||||
buf[0] = '\0';
|
||||
#if !defined(LWS_PLAT_OPTEE)
|
||||
lws_get_peer_simple(wsi, buf, sizeof(buf));
|
||||
#else
|
||||
buf[0] = '\0';
|
||||
#endif
|
||||
lwsl_notice("ah excessive hold: wsi %p\n"
|
||||
" peer address: %s\n"
|
||||
" ah pos %u\n",
|
||||
wsi, buf, ah->pos);
|
||||
buf[0] = '\0';
|
||||
m = 0;
|
||||
do {
|
||||
c = lws_token_to_string(m);
|
||||
if (!c)
|
||||
break;
|
||||
if (!(*c))
|
||||
break;
|
||||
|
||||
len = lws_hdr_total_length(wsi, m);
|
||||
if (!len || len > (int)sizeof(buf) - 1) {
|
||||
m++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lws_hdr_copy(wsi, buf,
|
||||
sizeof buf, m) > 0) {
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
|
||||
lwsl_notice(" %s = %s\n",
|
||||
(const char *)c, buf);
|
||||
}
|
||||
m++;
|
||||
} while (1);
|
||||
|
||||
/* explicitly detach the ah */
|
||||
lws_header_table_detach(wsi, 0);
|
||||
|
||||
/* ... and then drop the connection */
|
||||
|
||||
m = 0;
|
||||
if (wsi->desc.sockfd == our_fd) {
|
||||
m = timed_out;
|
||||
|
||||
/* it was the guy we came to service! */
|
||||
timed_out = 1;
|
||||
}
|
||||
|
||||
if (!m) /* if he didn't already timeout */
|
||||
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"excessive ah");
|
||||
|
||||
ah = pt->http.ah_list;
|
||||
}
|
||||
#endif
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
#if 0
|
||||
{
|
||||
char s[300], *p = s;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
p += sprintf(p, " %7lu (%5d), ",
|
||||
context->pt[n].count_conns,
|
||||
context->pt[n].fds_count);
|
||||
|
||||
lwsl_notice("load: %s\n", s);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Phase 3: vhost / protocol timer callbacks
|
||||
*/
|
||||
|
||||
wsi = NULL;
|
||||
lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
|
||||
struct lws_timed_vh_protocol *nx;
|
||||
if (v->timed_vh_protocol_list) {
|
||||
lws_start_foreach_ll(struct lws_timed_vh_protocol *,
|
||||
q, v->timed_vh_protocol_list) {
|
||||
if (now >= q->time) {
|
||||
if (!wsi)
|
||||
wsi = lws_zalloc(sizeof(*wsi), "cbwsi");
|
||||
wsi->context = context;
|
||||
wsi->vhost = v;
|
||||
wsi->protocol = q->protocol;
|
||||
lwsl_debug("timed cb: vh %s, protocol %s, reason %d\n", v->name, q->protocol->name, q->reason);
|
||||
q->protocol->callback(wsi, q->reason, NULL, NULL, 0);
|
||||
nx = q->next;
|
||||
lws_timed_callback_remove(v, q);
|
||||
q = nx;
|
||||
continue; /* we pointed ourselves to the next from the now-deleted guy */
|
||||
}
|
||||
} lws_end_foreach_ll(q, next);
|
||||
}
|
||||
} lws_end_foreach_ll(v, vhost_next);
|
||||
if (wsi)
|
||||
lws_free(wsi);
|
||||
|
||||
/*
|
||||
* Phase 4: check for unconfigured vhosts due to required
|
||||
* interface missing before
|
||||
*/
|
||||
|
||||
lws_context_lock(context);
|
||||
lws_start_foreach_llp(struct lws_vhost **, pv,
|
||||
context->no_listener_vhost_list) {
|
||||
struct lws_vhost *v = *pv;
|
||||
lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name);
|
||||
if (_lws_vhost_init_server(NULL, *pv) == 0) {
|
||||
/* became happy */
|
||||
lwsl_notice("vh %s: became connected\n", v->name);
|
||||
*pv = v->no_listener_vhost_list;
|
||||
v->no_listener_vhost_list = NULL;
|
||||
break;
|
||||
}
|
||||
} lws_end_foreach_llp(pv, no_listener_vhost_list);
|
||||
lws_context_unlock(context);
|
||||
|
||||
/*
|
||||
* Phase 5: role periodic checks
|
||||
*/
|
||||
#if defined(LWS_ROLE_WS)
|
||||
role_ops_ws.periodic_checks(context, tsi, now);
|
||||
#endif
|
||||
#if defined(LWS_ROLE_CGI)
|
||||
role_ops_cgi.periodic_checks(context, tsi, now);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Phase 6: check the remaining cert lifetime daily
|
||||
*/
|
||||
|
||||
if (context->tls_ops &&
|
||||
context->tls_ops->periodic_housekeeping)
|
||||
context->tls_ops->periodic_housekeeping(context, now);
|
||||
|
||||
return timed_out;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
||||
int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws *wsi;
|
||||
|
||||
if (!context || context->being_destroyed1)
|
||||
return -1;
|
||||
|
||||
/* the socket we came to service timed out, nothing to do */
|
||||
if (lws_service_periodic_checks(context, pollfd, tsi) || !pollfd)
|
||||
return 0;
|
||||
|
||||
/* no, here to service a socket descriptor */
|
||||
wsi = wsi_from_fd(context, pollfd->fd);
|
||||
if (!wsi)
|
||||
/* not lws connection ... leave revents alone and return */
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* so that caller can tell we handled, past here we need to
|
||||
* zero down pollfd->revents after handling
|
||||
*/
|
||||
|
||||
/* handle session socket closed */
|
||||
|
||||
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
|
||||
(pollfd->revents & LWS_POLLHUP)) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
lwsl_debug("Session Socket %p (fd=%d) dead\n",
|
||||
(void *)wsi, pollfd->fd);
|
||||
|
||||
goto close_and_handled;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->sock_send_blocking = FALSE;
|
||||
#endif
|
||||
|
||||
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
|
||||
(pollfd->revents & LWS_POLLHUP)) {
|
||||
lwsl_debug("pollhup\n");
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
goto close_and_handled;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_TLS)
|
||||
if (lwsi_state(wsi) == LRS_SHUTDOWN &&
|
||||
lws_is_ssl(wsi) && wsi->tls.ssl) {
|
||||
switch (__lws_tls_shutdown(wsi)) {
|
||||
case LWS_SSL_CAPABLE_DONE:
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
goto close_and_handled;
|
||||
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
goto handled;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
wsi->could_have_pending = 0; /* clear back-to-back write detection */
|
||||
|
||||
/* okay, what we came here to do... */
|
||||
|
||||
/* if we got here, we should have wire protocol ops set on the wsi */
|
||||
assert(wsi->role_ops);
|
||||
|
||||
// lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name,
|
||||
// wsi->wsistate);
|
||||
|
||||
switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) {
|
||||
case LWS_HPI_RET_WSI_ALREADY_DIED:
|
||||
return 1;
|
||||
case LWS_HPI_RET_HANDLED:
|
||||
break;
|
||||
case LWS_HPI_RET_PLEASE_CLOSE_ME:
|
||||
close_and_handled:
|
||||
lwsl_debug("%p: Close and handled\n", wsi);
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"close_and_handled");
|
||||
#if defined(_DEBUG) && defined(LWS_WITH_LIBUV)
|
||||
/*
|
||||
* confirm close has no problem being called again while
|
||||
* it waits for libuv service to complete the first async
|
||||
* close
|
||||
*/
|
||||
if (context->event_loop_ops == &event_loop_ops_uv)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"close_and_handled uv repeat test");
|
||||
#endif
|
||||
/*
|
||||
* pollfd may point to something else after the close
|
||||
* due to pollfd swapping scheme on delete on some platforms
|
||||
* we can't clear revents now because it'd be the wrong guy's
|
||||
* revents
|
||||
*/
|
||||
return 1;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
#if defined(LWS_WITH_TLS)
|
||||
handled:
|
||||
#endif
|
||||
pollfd->revents = 0;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
__lws_hrtimer_service(pt);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
|
||||
{
|
||||
return lws_service_fd_tsi(context, pollfd, 0);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_service(struct lws_context *context, int timeout_ms)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int n;
|
||||
|
||||
if (!context)
|
||||
return 1;
|
||||
|
||||
pt->inside_service = 1;
|
||||
|
||||
if (context->event_loop_ops->run_pt) {
|
||||
/* we are configured for an event loop */
|
||||
context->event_loop_ops->run_pt(context, 0);
|
||||
|
||||
pt->inside_service = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
n = lws_plat_service(context, timeout_ms);
|
||||
|
||||
pt->inside_service = 0;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
int n;
|
||||
|
||||
pt->inside_service = 1;
|
||||
|
||||
if (context->event_loop_ops->run_pt) {
|
||||
/* we are configured for an event loop */
|
||||
context->event_loop_ops->run_pt(context, tsi);
|
||||
|
||||
pt->inside_service = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
n = _lws_plat_service_tsi(context, timeout_ms, tsi);
|
||||
|
||||
pt->inside_service = 0;
|
||||
|
||||
return n;
|
||||
}
|
124
lib/event-libs/README.md
Normal file
124
lib/event-libs/README.md
Normal file
|
@ -0,0 +1,124 @@
|
|||
## Information for new event lib implementers
|
||||
|
||||
### Introduction
|
||||
|
||||
By default lws has built-in support for POSIX poll() as the event loop.
|
||||
|
||||
However either to get access to epoll() or other platform specific better
|
||||
poll waits, or to integrate with existing applications already using a
|
||||
specific event loop, it can be desirable for lws to use another external
|
||||
event library, like libuv, libevent or libev.
|
||||
|
||||
### Code placement
|
||||
|
||||
The code specific to the event library should live in `./lib/event-libs/**lib name**`
|
||||
|
||||
### Allowing control over enabling event libs
|
||||
|
||||
All event libs should add a cmake define `LWS_WITH_**lib name**` and make its build
|
||||
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
|
||||
as well so user builds can understand if the event lib is available in the lws build it is
|
||||
trying to bind to.
|
||||
|
||||
If the event lib is disabled in cmake, nothing in its directory is built or referenced.
|
||||
|
||||
### Event loop ops struct
|
||||
|
||||
The event lib support is defined by `struct lws_event_loop_ops` in `lib/private/libwebsockets.h`,
|
||||
each event lib support instantiates one of these and fills in the appropriate ops
|
||||
callbacks to perform its job. By convention that lives in
|
||||
`./lib/event-libs/**lib name**/**lib_name**.c`.
|
||||
|
||||
### Private event lib declarations
|
||||
|
||||
Truly private declarations for the event lib can go in the event-libs directory as you like.
|
||||
However when the declarations must be accessible to other things in lws build, eg,
|
||||
the event lib support adds members to `struct lws` when enabled, they should be in the
|
||||
event lib supporr directory in a file `private.h`.
|
||||
|
||||
Search for "bring in event libs private declarations" in `./lib/private-libwebsockets.h
|
||||
and add your private event lib support file there following the style used for the other
|
||||
event libs, eg,
|
||||
|
||||
```
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
#include "event-libs/libuv/private.h"
|
||||
#endif
|
||||
```
|
||||
|
||||
If the event lib support is disabled at cmake, nothing from its private.h should be used anywhere.
|
||||
|
||||
### Integrating event lib assets to lws
|
||||
|
||||
If your event lib needs special storage in lws objects, that's no problem. But to keep
|
||||
things sane, there are some rules.
|
||||
|
||||
- declare a "container struct" in your private.h for everything, eg, the libuv event
|
||||
lib support need to add its own assets in the perthread struct, it declares in its private.h
|
||||
|
||||
```
|
||||
struct lws_pt_eventlibs_libuv {
|
||||
uv_loop_t *io_loop;
|
||||
uv_signal_t signals[8];
|
||||
uv_timer_t timeout_watcher;
|
||||
uv_timer_t hrtimer;
|
||||
uv_idle_t idle;
|
||||
};
|
||||
```
|
||||
|
||||
- add your event lib content in one place in the related lws struct, protected by `#if defined(LWS_WITH_**lib name**)`,
|
||||
eg, again for LWS_WITH_LIBUV
|
||||
|
||||
```
|
||||
struct lws_context_per_thread {
|
||||
|
||||
...
|
||||
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
struct lws_pt_eventlibs_libuv uv;
|
||||
#endif
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### Adding to lws available event libs list
|
||||
|
||||
Edit the NULL-terminated array `available_event_libs` at the top of `./lib/context.c` to include
|
||||
a pointer to your new event lib support's ops struct, following the style already there.
|
||||
|
||||
```
|
||||
const struct lws_event_loop_ops *available_event_libs[] = {
|
||||
#if defined(LWS_WITH_POLL)
|
||||
&event_loop_ops_poll,
|
||||
#endif
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
&event_loop_ops_uv,
|
||||
#endif
|
||||
...
|
||||
```
|
||||
|
||||
This is used to provide a list of avilable configured backends.
|
||||
|
||||
### Enabling event lib adoption
|
||||
|
||||
You need to add a `LWS_SERVER_OPTION...` flag as necessary in `./lib/libwebsockets.h`
|
||||
`enum lws_context_options`, and follow the existing code in `lws_create_context()`
|
||||
to convert the flag into binding your ops struct to the context.
|
||||
|
||||
### Implementation of the event lib bindings
|
||||
|
||||
Study eg libuv implementation, using the available ops in the struct lws_event_loop_ops
|
||||
as a guide.
|
||||
|
||||
### Destruction
|
||||
|
||||
Ending the event loop is generally a bit tricky, because if the event loop is internal
|
||||
to the lws context, you cannot destroy it while the event loop is running.
|
||||
|
||||
Don't add special exports... we tried that, it's a huge mess. The same user code should be able
|
||||
work with any of the event loops including poll.
|
||||
|
||||
The solution we found was hide the different processing necessary for the different cases in
|
||||
lws_destroy_context(). To help with that there are ops available at two different places in
|
||||
the context destroy processing.
|
||||
|
378
lib/event-libs/libev/libev.c
Normal file
378
lib/event-libs/libev/libev.c
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
static void
|
||||
lws_ev_hrtimer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents)
|
||||
{
|
||||
struct lws_context_per_thread *pt =
|
||||
(struct lws_context_per_thread *)watcher->data;
|
||||
lws_usec_t us;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT) {
|
||||
ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0);
|
||||
ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer);
|
||||
}
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_ev_idle_cb(struct ev_loop *loop, struct ev_idle *handle, int revents)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(handle,
|
||||
struct lws_context_per_thread, ev.idle);
|
||||
lws_usec_t us;
|
||||
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
*/
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* -1 timeout means just do forced service */
|
||||
_lws_plat_service_tsi(pt->context, -1, pt->tid);
|
||||
/* still somebody left who wants forced service? */
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
|
||||
/* yes... come back again later */
|
||||
return;
|
||||
}
|
||||
|
||||
/* account for hrtimer */
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT) {
|
||||
ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0);
|
||||
ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer);
|
||||
}
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
/* there is nobody who needs service forcing, shut down idle */
|
||||
ev_idle_stop(loop, handle);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws_io_watcher *lws_io = lws_container_of(watcher,
|
||||
struct lws_io_watcher, ev.watcher);
|
||||
struct lws_context *context = lws_io->context;
|
||||
struct lws_pollfd eventfd;
|
||||
struct lws *wsi;
|
||||
|
||||
if (revents & EV_ERROR)
|
||||
return;
|
||||
|
||||
eventfd.fd = watcher->fd;
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = EV_NONE;
|
||||
|
||||
if (revents & EV_READ) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & EV_WRITE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
|
||||
wsi = wsi_from_fd(context, watcher->fd);
|
||||
pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
lws_service_fd_tsi(context, &eventfd, (int)wsi->tsi);
|
||||
|
||||
ev_idle_start(pt->ev.io_loop, &pt->ev.idle);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
|
||||
{
|
||||
struct lws_context *context = watcher->data;
|
||||
|
||||
if (context->eventlib_signal_cb) {
|
||||
context->eventlib_signal_cb((void *)watcher, watcher->signum);
|
||||
|
||||
return;
|
||||
}
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev.watcher;
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
const char *backend_name;
|
||||
struct ev_loop *loop = (struct ev_loop *)_loop;
|
||||
int status = 0;
|
||||
int backend;
|
||||
|
||||
lwsl_info("%s: loop %p\n", __func__, _loop);
|
||||
|
||||
if (!loop)
|
||||
loop = ev_loop_new(0);
|
||||
else
|
||||
context->pt[tsi].event_loop_foreign = 1;
|
||||
|
||||
if (!loop) {
|
||||
lwsl_err("%s: creating event base failed\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pt->ev.io_loop = loop;
|
||||
|
||||
/*
|
||||
* Initialize the accept w_accept with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*/
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
vh->w_accept.context = context;
|
||||
|
||||
ev_io_init(&vh->w_accept.ev.watcher, lws_accept_cb,
|
||||
vh->lserv_wsi->desc.sockfd, EV_READ);
|
||||
ev_io_start(loop, &vh->w_accept.ev.watcher);
|
||||
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* Register the signal watcher unless it's a foreign loop */
|
||||
if (!context->pt[tsi].event_loop_foreign) {
|
||||
ev_signal_init(w_sigint, lws_ev_sigint_cb, SIGINT);
|
||||
w_sigint->data = context;
|
||||
ev_signal_start(loop, w_sigint);
|
||||
}
|
||||
|
||||
backend = ev_backend(loop);
|
||||
switch (backend) {
|
||||
case EVBACKEND_SELECT:
|
||||
backend_name = "select";
|
||||
break;
|
||||
case EVBACKEND_POLL:
|
||||
backend_name = "poll";
|
||||
break;
|
||||
case EVBACKEND_EPOLL:
|
||||
backend_name = "epoll";
|
||||
break;
|
||||
case EVBACKEND_KQUEUE:
|
||||
backend_name = "kqueue";
|
||||
break;
|
||||
case EVBACKEND_DEVPOLL:
|
||||
backend_name = "/dev/poll";
|
||||
break;
|
||||
case EVBACKEND_PORT:
|
||||
backend_name = "Solaris 10 \"port\"";
|
||||
break;
|
||||
default:
|
||||
backend_name = "Unknown libev backend";
|
||||
break;
|
||||
}
|
||||
|
||||
lwsl_info(" libev backend: %s\n", backend_name);
|
||||
(void)backend_name;
|
||||
|
||||
ev_timer_init(&pt->ev.hrtimer, lws_ev_hrtimer_cb, 0, 0);
|
||||
pt->ev.hrtimer.data = pt;
|
||||
|
||||
ev_idle_init(&pt->ev.idle, lws_ev_idle_cb);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_destroy_pt_ev(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi)
|
||||
ev_io_stop(pt->ev.io_loop, &vh->w_accept.ev.watcher);
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* static assets */
|
||||
|
||||
ev_timer_stop(pt->ev.io_loop, &pt->ev.hrtimer);
|
||||
ev_idle_stop(pt->ev.io_loop, &pt->ev.idle);
|
||||
|
||||
if (!pt->event_loop_foreign) {
|
||||
ev_signal_stop(pt->ev.io_loop, &pt->w_sigint.ev.watcher);
|
||||
|
||||
ev_loop_destroy(pt->ev.io_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_context_ev(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
int n;
|
||||
|
||||
context->eventlib_signal_cb = info->signal_cb;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
context->pt[n].w_sigint.context = context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_accept_ev(struct lws *wsi)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (wsi->role_ops->file_handle)
|
||||
fd = wsi->desc.filefd;
|
||||
else
|
||||
fd = wsi->desc.sockfd;
|
||||
|
||||
wsi->w_read.context = wsi->context;
|
||||
wsi->w_write.context = wsi->context;
|
||||
|
||||
ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ);
|
||||
ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_io_ev(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!pt->ev.io_loop)
|
||||
return;
|
||||
|
||||
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
|
||||
|
||||
if (flags & LWS_EV_START) {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
ev_io_start(pt->ev.io_loop, &wsi->w_write.ev.watcher);
|
||||
if (flags & LWS_EV_READ)
|
||||
ev_io_start(pt->ev.io_loop, &wsi->w_read.ev.watcher);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher);
|
||||
if (flags & LWS_EV_READ)
|
||||
ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elops_run_pt_ev(struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].ev.io_loop)
|
||||
ev_run(context->pt[tsi].ev.io_loop, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
elops_destroy_context2_ev(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, m;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
int budget = 1000;
|
||||
|
||||
pt = &context->pt[n];
|
||||
|
||||
/* only for internal loops... */
|
||||
|
||||
if (pt->event_loop_foreign || !pt->ev.io_loop)
|
||||
continue;
|
||||
|
||||
if (!context->finalize_destroy_after_internal_loops_stopped) {
|
||||
ev_break(pt->ev.io_loop, EVBREAK_ONE);
|
||||
continue;
|
||||
}
|
||||
while (budget-- &&
|
||||
(m = ev_run(pt->ev.io_loop, 0)))
|
||||
;
|
||||
|
||||
ev_loop_destroy(pt->ev.io_loop);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_vhost_listen_wsi_ev(struct lws *wsi)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (!wsi) {
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wsi->w_read.context = wsi->context;
|
||||
wsi->w_write.context = wsi->context;
|
||||
|
||||
if (wsi->role_ops->file_handle)
|
||||
fd = wsi->desc.filefd;
|
||||
else
|
||||
fd = wsi->desc.sockfd;
|
||||
|
||||
ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ);
|
||||
ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE);
|
||||
|
||||
elops_io_ev(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_destroy_wsi_ev(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher);
|
||||
ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher);
|
||||
}
|
||||
|
||||
struct lws_event_loop_ops event_loop_ops_ev = {
|
||||
/* name */ "libev",
|
||||
/* init_context */ elops_init_context_ev,
|
||||
/* destroy_context1 */ NULL,
|
||||
/* destroy_context2 */ elops_destroy_context2_ev,
|
||||
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_ev,
|
||||
/* init_pt */ elops_init_pt_ev,
|
||||
/* wsi_logical_close */ NULL,
|
||||
/* check_client_connect_ok */ NULL,
|
||||
/* close_handle_manually */ NULL,
|
||||
/* accept */ elops_accept_ev,
|
||||
/* io */ elops_io_ev,
|
||||
/* run_pt */ elops_run_pt_ev,
|
||||
/* destroy_pt */ elops_destroy_pt_ev,
|
||||
/* destroy wsi */ elops_destroy_wsi_ev,
|
||||
|
||||
/* periodic_events_available */ 0,
|
||||
};
|
53
lib/event-libs/libev/private.h
Normal file
53
lib/event-libs/libev/private.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_WITH_LIBEV
|
||||
*/
|
||||
|
||||
#include <ev.h>
|
||||
|
||||
#define LWS_EV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \
|
||||
{ (_x)->data = _ctx; \
|
||||
_ctx->count_event_loop_static_asset_handles++; }
|
||||
#define LWS_EV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \
|
||||
((struct lws_context *)(_x)->data)))
|
||||
#define LWS_EV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
|
||||
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \
|
||||
count_event_loop_static_asset_handles))
|
||||
|
||||
struct lws_pt_eventlibs_libev {
|
||||
struct ev_loop *io_loop;
|
||||
struct ev_timer hrtimer;
|
||||
struct ev_idle idle;
|
||||
};
|
||||
|
||||
struct lws_io_watcher_libev {
|
||||
ev_io watcher;
|
||||
};
|
||||
|
||||
struct lws_signal_watcher_libev {
|
||||
ev_signal watcher;
|
||||
};
|
||||
|
||||
struct lws_context_eventlibs_libev {
|
||||
int placeholder;
|
||||
};
|
||||
|
||||
extern struct lws_event_loop_ops event_loop_ops_ev;
|
412
lib/event-libs/libevent/libevent.c
Normal file
412
lib/event-libs/libevent/libevent.c
Normal file
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
static void
|
||||
lws_event_hrtimer_cb(int fd, short event, void *p)
|
||||
{
|
||||
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
|
||||
struct timeval tv;
|
||||
lws_usec_t us;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT) {
|
||||
tv.tv_sec = us / 1000000;
|
||||
tv.tv_usec = us - (tv.tv_sec * 1000000);
|
||||
evtimer_add(pt->event.hrtimer, &tv);
|
||||
}
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_event_idle_timer_cb(int fd, short event, void *p)
|
||||
{
|
||||
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
|
||||
struct timeval tv;
|
||||
lws_usec_t us;
|
||||
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
*/
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* -1 timeout means just do forced service */
|
||||
_lws_plat_service_tsi(pt->context, -1, pt->tid);
|
||||
/* still somebody left who wants forced service? */
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* yes... come back again later */
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1000;
|
||||
evtimer_add(pt->event.idle_timer, &tv);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* account for hrtimer */
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT) {
|
||||
tv.tv_sec = us / 1000000;
|
||||
tv.tv_usec = us - (tv.tv_sec * 1000000);
|
||||
evtimer_add(pt->event.hrtimer, &tv);
|
||||
}
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = (struct lws_io_watcher *)ctx;
|
||||
struct lws_context *context = lws_io->context;
|
||||
struct lws_context_per_thread *pt;
|
||||
struct lws_pollfd eventfd;
|
||||
struct timeval tv;
|
||||
struct lws *wsi;
|
||||
|
||||
if (revents & EV_TIMEOUT)
|
||||
return;
|
||||
|
||||
/* !!! EV_CLOSED doesn't exist in libevent2 */
|
||||
#if LIBEVENT_VERSION_NUMBER < 0x02000000
|
||||
if (revents & EV_CLOSED) {
|
||||
event_del(lws_io->event.watcher);
|
||||
event_free(lws_io->event.watcher);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
eventfd.fd = sock_fd;
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = 0;
|
||||
if (revents & EV_READ) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & EV_WRITE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
|
||||
wsi = wsi_from_fd(context, sock_fd);
|
||||
pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
lws_service_fd_tsi(context, &eventfd, wsi->tsi);
|
||||
|
||||
/* set the idle timer for 1ms ahead */
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1000;
|
||||
evtimer_add(pt->event.idle_timer, &tv);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx)
|
||||
{
|
||||
struct lws_context_per_thread *pt = ctx;
|
||||
struct event *signal = (struct event *)ctx;
|
||||
|
||||
if (pt->context->eventlib_signal_cb) {
|
||||
pt->context->eventlib_signal_cb((void *)(lws_intptr_t)sock_fd,
|
||||
event_get_signal(signal));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!pt->event_loop_foreign)
|
||||
event_base_loopbreak(pt->event.io_loop);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
elops_init_pt_event(struct lws_context *context, void *_loop, int tsi)
|
||||
{
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
struct event_base *loop = (struct event_base *)_loop;
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
lwsl_info("%s: loop %p\n", __func__, _loop);
|
||||
|
||||
if (!loop)
|
||||
loop = event_base_new();
|
||||
else
|
||||
context->pt[tsi].event_loop_foreign = 1;
|
||||
|
||||
if (!loop) {
|
||||
lwsl_err("%s: creating event base failed\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pt->event.io_loop = loop;
|
||||
|
||||
/*
|
||||
* Initialize all events with the listening sockets
|
||||
* and register a callback for read operations
|
||||
*/
|
||||
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
vh->lserv_wsi->w_read.event.watcher = event_new(
|
||||
loop, vh->lserv_wsi->desc.sockfd,
|
||||
(EV_READ | EV_PERSIST), lws_event_cb,
|
||||
&vh->lserv_wsi->w_read);
|
||||
event_add(vh->lserv_wsi->w_read.event.watcher, NULL);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* static event loop objects */
|
||||
|
||||
pt->event.hrtimer = event_new(loop, -1, EV_PERSIST,
|
||||
lws_event_hrtimer_cb, pt);
|
||||
|
||||
pt->event.idle_timer = event_new(loop, -1, EV_PERSIST,
|
||||
lws_event_idle_timer_cb, pt);
|
||||
|
||||
/* Register the signal watcher unless it's a foreign loop */
|
||||
|
||||
if (pt->event_loop_foreign)
|
||||
return 0;
|
||||
|
||||
pt->w_sigint.event.watcher = evsignal_new(loop, SIGINT,
|
||||
lws_event_sigint_cb, pt);
|
||||
event_add(pt->w_sigint.event.watcher, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_context_event(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
int n;
|
||||
|
||||
context->eventlib_signal_cb = info->signal_cb;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
context->pt[n].w_sigint.context = context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_accept_event(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt;
|
||||
int fd;
|
||||
|
||||
wsi->w_read.context = context;
|
||||
wsi->w_write.context = context;
|
||||
|
||||
// Initialize the event
|
||||
pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->role_ops->file_handle)
|
||||
fd = wsi->desc.filefd;
|
||||
else
|
||||
fd = wsi->desc.sockfd;
|
||||
|
||||
wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd,
|
||||
(EV_READ | EV_PERSIST), lws_event_cb, &wsi->w_read);
|
||||
wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd,
|
||||
(EV_WRITE | EV_PERSIST), lws_event_cb, &wsi->w_write);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_io_event(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!pt->event.io_loop || wsi->context->being_destroyed)
|
||||
return;
|
||||
|
||||
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
|
||||
|
||||
if (flags & LWS_EV_START) {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
event_add(wsi->w_write.event.watcher, NULL);
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
event_add(wsi->w_read.event.watcher, NULL);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
event_del(wsi->w_write.event.watcher);
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
event_del(wsi->w_read.event.watcher);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elops_run_pt_event(struct lws_context *context, int tsi)
|
||||
{
|
||||
/* Run / Dispatch the event_base loop */
|
||||
if (context->pt[tsi].event.io_loop)
|
||||
event_base_dispatch(context->pt[tsi].event.io_loop);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_destroy_pt_event(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
|
||||
lwsl_info("%s\n", __func__);
|
||||
|
||||
if (!pt->event.io_loop)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Free all events with the listening sockets
|
||||
*/
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
event_free(vh->lserv_wsi->w_read.event.watcher);
|
||||
vh->lserv_wsi->w_read.event.watcher = NULL;
|
||||
event_free(vh->lserv_wsi->w_write.event.watcher);
|
||||
vh->lserv_wsi->w_write.event.watcher = NULL;
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
event_free(pt->event.hrtimer);
|
||||
event_free(pt->event.idle_timer);
|
||||
|
||||
if (!pt->event_loop_foreign) {
|
||||
event_del(pt->w_sigint.event.watcher);
|
||||
event_free(pt->w_sigint.event.watcher);
|
||||
|
||||
event_base_free(pt->event.io_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
elops_destroy_wsi_event(struct lws *wsi)
|
||||
{
|
||||
if (!wsi)
|
||||
return;
|
||||
|
||||
if (wsi->w_read.event.watcher)
|
||||
event_free(wsi->w_read.event.watcher);
|
||||
|
||||
if (wsi->w_write.event.watcher)
|
||||
event_free(wsi->w_write.event.watcher);
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_vhost_listen_wsi_event(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int fd;
|
||||
|
||||
if (!wsi) {
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
wsi->w_read.context = wsi->context;
|
||||
wsi->w_write.context = wsi->context;
|
||||
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (wsi->role_ops->file_handle)
|
||||
fd = wsi->desc.filefd;
|
||||
else
|
||||
fd = wsi->desc.sockfd;
|
||||
|
||||
wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd,
|
||||
(EV_READ | EV_PERSIST),
|
||||
lws_event_cb, &wsi->w_read);
|
||||
wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd,
|
||||
(EV_WRITE | EV_PERSIST),
|
||||
lws_event_cb, &wsi->w_write);
|
||||
|
||||
elops_io_event(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_destroy_context2_event(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, m;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
int budget = 1000;
|
||||
|
||||
pt = &context->pt[n];
|
||||
|
||||
/* only for internal loops... */
|
||||
|
||||
if (pt->event_loop_foreign || !pt->event.io_loop)
|
||||
continue;
|
||||
|
||||
if (!context->finalize_destroy_after_internal_loops_stopped) {
|
||||
event_base_loopexit(pt->event.io_loop, NULL);
|
||||
continue;
|
||||
}
|
||||
while (budget-- &&
|
||||
(m = event_base_loop(pt->event.io_loop, EVLOOP_NONBLOCK)))
|
||||
;
|
||||
#if 0
|
||||
if (m) {
|
||||
lwsl_err("%s: tsi %d: NOT everything closed\n",
|
||||
__func__, n);
|
||||
event_base_dump_events(pt->event.io_loop, stderr);
|
||||
} else
|
||||
lwsl_debug("%s: %d: everything closed OK\n", __func__, n);
|
||||
#endif
|
||||
event_base_free(pt->event.io_loop);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_event_loop_ops event_loop_ops_event = {
|
||||
/* name */ "libevent",
|
||||
/* init_context */ elops_init_context_event,
|
||||
/* destroy_context1 */ NULL,
|
||||
/* destroy_context2 */ elops_destroy_context2_event,
|
||||
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_event,
|
||||
/* init_pt */ elops_init_pt_event,
|
||||
/* wsi_logical_close */ NULL,
|
||||
/* check_client_connect_ok */ NULL,
|
||||
/* close_handle_manually */ NULL,
|
||||
/* accept */ elops_accept_event,
|
||||
/* io */ elops_io_event,
|
||||
/* run_pt */ elops_run_pt_event,
|
||||
/* destroy_pt */ elops_destroy_pt_event,
|
||||
/* destroy wsi */ elops_destroy_wsi_event,
|
||||
|
||||
/* periodic_events_available */ 0,
|
||||
};
|
44
lib/event-libs/libevent/private.h
Normal file
44
lib/event-libs/libevent/private.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_WITH_LIBEVENT
|
||||
*/
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
struct lws_pt_eventlibs_libevent {
|
||||
struct event_base *io_loop;
|
||||
struct event *hrtimer;
|
||||
struct event *idle_timer;
|
||||
};
|
||||
|
||||
struct lws_io_watcher_libevent {
|
||||
struct event *watcher;
|
||||
};
|
||||
|
||||
struct lws_signal_watcher_libevent {
|
||||
struct event *watcher;
|
||||
};
|
||||
|
||||
struct lws_context_eventlibs_libevent {
|
||||
int placeholder;
|
||||
};
|
||||
|
||||
extern struct lws_event_loop_ops event_loop_ops_event;
|
929
lib/event-libs/libuv/libuv.c
Normal file
929
lib/event-libs/libuv/libuv.c
Normal file
|
@ -0,0 +1,929 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
static void
|
||||
lws_uv_hrtimer_cb(uv_timer_t *timer
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(timer,
|
||||
struct lws_context_per_thread, uv.hrtimer);
|
||||
lws_usec_t us;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT)
|
||||
uv_timer_start(&pt->uv.hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_idle(uv_idle_t *handle
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(handle,
|
||||
struct lws_context_per_thread, uv.idle);
|
||||
lws_usec_t us;
|
||||
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
*/
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* -1 timeout means just do forced service */
|
||||
_lws_plat_service_tsi(pt->context, -1, pt->tid);
|
||||
/* still somebody left who wants forced service? */
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
|
||||
/* yes... come back again later */
|
||||
return;
|
||||
}
|
||||
|
||||
/* account for hrtimer */
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
us = __lws_hrtimer_service(pt);
|
||||
if (us != LWS_HRTIMER_NOWAIT)
|
||||
uv_timer_start(&pt->uv.hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
/* there is nobody who needs service forcing, shut down idle */
|
||||
uv_idle_stop(handle);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_io_cb(uv_poll_t *watcher, int status, int revents)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = lws_container_of(watcher,
|
||||
struct lws_io_watcher, uv.watcher);
|
||||
struct lws *wsi = lws_container_of(lws_io, struct lws, w_read);
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
eventfd.fd = watcher->socket;
|
||||
#else
|
||||
eventfd.fd = watcher->io_watcher.fd;
|
||||
#endif
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = 0;
|
||||
|
||||
if (status < 0) {
|
||||
/*
|
||||
* At this point status will be an UV error, like UV_EBADF,
|
||||
* we treat all errors as LWS_POLLHUP
|
||||
*
|
||||
* You might want to return; instead of servicing the fd in
|
||||
* some cases */
|
||||
if (status == UV_EAGAIN)
|
||||
return;
|
||||
|
||||
eventfd.events |= LWS_POLLHUP;
|
||||
eventfd.revents |= LWS_POLLHUP;
|
||||
} else {
|
||||
if (revents & UV_READABLE) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & UV_WRITABLE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
}
|
||||
lws_service_fd_tsi(context, &eventfd, wsi->tsi);
|
||||
|
||||
uv_idle_start(&pt->uv.idle, lws_uv_idle);
|
||||
}
|
||||
|
||||
/*
|
||||
* This does not actually stop the event loop. The reason is we have to pass
|
||||
* libuv handle closures through its event loop. So this tries to close all
|
||||
* wsi, and set a flag; when all the wsi closures are finalized then we
|
||||
* actually stop the libuv event loops.
|
||||
*/
|
||||
static void
|
||||
lws_libuv_stop(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, m;
|
||||
|
||||
lwsl_err("%s\n", __func__);
|
||||
|
||||
if (context->requested_kill) {
|
||||
lwsl_err("%s: ignoring\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
context->requested_kill = 1;
|
||||
|
||||
m = context->count_threads;
|
||||
context->being_destroyed = 1;
|
||||
|
||||
/*
|
||||
* Phase 1: start the close of every dynamic uv handle
|
||||
*/
|
||||
|
||||
while (m--) {
|
||||
pt = &context->pt[m];
|
||||
|
||||
if (pt->pipe_wsi) {
|
||||
uv_poll_stop(&pt->pipe_wsi->w_read.uv.watcher);
|
||||
lws_destroy_event_pipe(pt->pipe_wsi);
|
||||
pt->pipe_wsi = NULL;
|
||||
}
|
||||
|
||||
for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
|
||||
struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
|
||||
|
||||
if (!wsi)
|
||||
continue;
|
||||
lws_close_free_wsi(wsi,
|
||||
LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, __func__
|
||||
/* no protocol close */);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_info("%s: started closing all wsi\n", __func__);
|
||||
|
||||
/* we cannot have completed... there are at least the cancel pipes */
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_signal_handler(uv_signal_t *watcher, int signum)
|
||||
{
|
||||
struct lws_context *context = watcher->data;
|
||||
|
||||
if (context->eventlib_signal_cb) {
|
||||
context->eventlib_signal_cb((void *)watcher, signum);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lwsl_err("internal signal handler caught signal %d\n", signum);
|
||||
lws_libuv_stop(watcher->data);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_timeout_cb(uv_timer_t *timer
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(timer,
|
||||
struct lws_context_per_thread, uv.timeout_watcher);
|
||||
|
||||
if (pt->context->requested_kill)
|
||||
return;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
lws_service_fd_tsi(pt->context, NULL, pt->tid);
|
||||
}
|
||||
|
||||
static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP };
|
||||
|
||||
/*
|
||||
* Closing Phase 2: Close callback for a static UV asset
|
||||
*/
|
||||
|
||||
static void
|
||||
lws_uv_close_cb_sa(uv_handle_t *handle)
|
||||
{
|
||||
struct lws_context *context =
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(handle);
|
||||
int n;
|
||||
|
||||
lwsl_info("%s: sa left %d: dyn left: %d\n", __func__,
|
||||
context->count_event_loop_static_asset_handles,
|
||||
context->count_wsi_allocated);
|
||||
|
||||
/* any static assets left? */
|
||||
|
||||
if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) ||
|
||||
context->count_wsi_allocated)
|
||||
return;
|
||||
|
||||
/*
|
||||
* That's it... all wsi were down, and now every
|
||||
* static asset lws had a UV handle for is down.
|
||||
*
|
||||
* Stop the loop so we can get out of here.
|
||||
*/
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
struct lws_context_per_thread *pt = &context->pt[n];
|
||||
|
||||
if (pt->uv.io_loop && !pt->event_loop_foreign)
|
||||
uv_stop(pt->uv.io_loop);
|
||||
}
|
||||
|
||||
if (!context->pt[0].event_loop_foreign) {
|
||||
lwsl_info("%s: calling lws_context_destroy2\n", __func__);
|
||||
lws_context_destroy2(context);
|
||||
}
|
||||
|
||||
lwsl_info("%s: all done\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* These must be called by protocols that want to use libuv objects directly...
|
||||
*
|
||||
* .... when the libuv object is created...
|
||||
*/
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_static_refcount_add(uv_handle_t *h, struct lws_context *context)
|
||||
{
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(h, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* ... and in the close callback when the object is closed.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_static_refcount_del(uv_handle_t *h)
|
||||
{
|
||||
lws_uv_close_cb_sa(h);
|
||||
}
|
||||
|
||||
|
||||
static void lws_uv_close_cb(uv_handle_t *handle)
|
||||
{
|
||||
}
|
||||
|
||||
static void lws_uv_walk_cb(uv_handle_t *handle, void *arg)
|
||||
{
|
||||
if (!uv_is_closing(handle))
|
||||
uv_close(handle, lws_uv_close_cb);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_close_all_handles_in_loop(uv_loop_t *loop)
|
||||
{
|
||||
uv_walk(loop, lws_uv_walk_cb, NULL);
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_stop_without_kill(const struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].uv.io_loop)
|
||||
uv_stop(context->pt[tsi].uv.io_loop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
LWS_VISIBLE uv_loop_t *
|
||||
lws_uv_getloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].uv.io_loop)
|
||||
return context->pt[tsi].uv.io_loop;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_libuv_closewsi_m(uv_handle_t* handle)
|
||||
{
|
||||
lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data;
|
||||
|
||||
compatible_close(sockfd);
|
||||
}
|
||||
|
||||
int
|
||||
lws_libuv_check_watcher_active(struct lws *wsi)
|
||||
{
|
||||
uv_handle_t *h = (void *)&wsi->w_read.uv.watcher;
|
||||
|
||||
return uv_is_active(h);
|
||||
}
|
||||
|
||||
|
||||
#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0)
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_plugins_init(struct lws_context *context, const char * const *d)
|
||||
{
|
||||
struct lws_plugin_capability lcaps;
|
||||
struct lws_plugin *plugin;
|
||||
lws_plugin_init_func initfunc;
|
||||
int m, ret = 0;
|
||||
void *v;
|
||||
uv_dirent_t dent;
|
||||
uv_fs_t req;
|
||||
char path[256];
|
||||
uv_lib_t lib;
|
||||
int pofs = 0;
|
||||
|
||||
#if defined(__MINGW32__) || !defined(WIN32)
|
||||
pofs = 3;
|
||||
#endif
|
||||
|
||||
lib.errmsg = NULL;
|
||||
lib.handle = NULL;
|
||||
|
||||
uv_loop_init(&context->uv.loop);
|
||||
|
||||
lwsl_notice(" Plugins:\n");
|
||||
|
||||
while (d && *d) {
|
||||
|
||||
lwsl_notice(" Scanning %s\n", *d);
|
||||
m =uv_fs_scandir(&context->uv.loop, &req, *d, 0, NULL);
|
||||
if (m < 1) {
|
||||
lwsl_err("Scandir on %s failed\n", *d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
|
||||
if (strlen(dent.name) < 7)
|
||||
continue;
|
||||
|
||||
lwsl_notice(" %s\n", dent.name);
|
||||
|
||||
lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d,
|
||||
dent.name);
|
||||
if (uv_dlopen(path, &lib)) {
|
||||
uv_dlerror(&lib);
|
||||
lwsl_err("Error loading DSO: %s\n", lib.errmsg);
|
||||
uv_dlclose(&lib);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* we could open it, can we get his init function? */
|
||||
|
||||
#if !defined(WIN32) && !defined(__MINGW32__)
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
|
||||
dent.name + pofs /* snip lib... */);
|
||||
path[m - 3] = '\0'; /* snip the .so */
|
||||
#else
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
|
||||
dent.name + pofs);
|
||||
path[m - 4] = '\0'; /* snip the .dll */
|
||||
#endif
|
||||
if (uv_dlsym(&lib, path, &v)) {
|
||||
uv_dlerror(&lib);
|
||||
lwsl_err("Failed to get %s on %s: %s", path,
|
||||
dent.name, lib.errmsg);
|
||||
uv_dlclose(&lib);
|
||||
goto bail;
|
||||
}
|
||||
initfunc = (lws_plugin_init_func)v;
|
||||
lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
|
||||
m = initfunc(context, &lcaps);
|
||||
if (m) {
|
||||
lwsl_err("Init %s failed %d\n", dent.name, m);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
plugin = lws_malloc(sizeof(*plugin), "plugin");
|
||||
if (!plugin) {
|
||||
uv_dlclose(&lib);
|
||||
lwsl_err("OOM\n");
|
||||
goto bail;
|
||||
}
|
||||
plugin->list = context->plugin_list;
|
||||
context->plugin_list = plugin;
|
||||
lws_strncpy(plugin->name, dent.name, sizeof(plugin->name));
|
||||
plugin->lib = lib;
|
||||
plugin->caps = lcaps;
|
||||
context->plugin_protocol_count += lcaps.count_protocols;
|
||||
context->plugin_extension_count += lcaps.count_extensions;
|
||||
|
||||
continue;
|
||||
|
||||
skip:
|
||||
uv_dlclose(&lib);
|
||||
}
|
||||
bail:
|
||||
uv_fs_req_cleanup(&req);
|
||||
d++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_plugins_destroy(struct lws_context *context)
|
||||
{
|
||||
struct lws_plugin *plugin = context->plugin_list, *p;
|
||||
lws_plugin_destroy_func func;
|
||||
char path[256];
|
||||
int pofs = 0;
|
||||
void *v;
|
||||
int m;
|
||||
|
||||
#if defined(__MINGW32__) || !defined(WIN32)
|
||||
pofs = 3;
|
||||
#endif
|
||||
|
||||
if (!plugin)
|
||||
return 0;
|
||||
|
||||
while (plugin) {
|
||||
p = plugin;
|
||||
|
||||
#if !defined(WIN32) && !defined(__MINGW32__)
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s",
|
||||
plugin->name + pofs);
|
||||
path[m - 3] = '\0';
|
||||
#else
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s",
|
||||
plugin->name + pofs);
|
||||
path[m - 4] = '\0';
|
||||
#endif
|
||||
|
||||
if (uv_dlsym(&plugin->lib, path, &v)) {
|
||||
uv_dlerror(&plugin->lib);
|
||||
lwsl_err("Failed to get %s on %s: %s", path,
|
||||
plugin->name, plugin->lib.errmsg);
|
||||
} else {
|
||||
func = (lws_plugin_destroy_func)v;
|
||||
m = func(context);
|
||||
if (m)
|
||||
lwsl_err("Destroying %s failed %d\n",
|
||||
plugin->name, m);
|
||||
}
|
||||
|
||||
uv_dlclose(&p->lib);
|
||||
plugin = p->list;
|
||||
p->list = NULL;
|
||||
free(p);
|
||||
}
|
||||
|
||||
context->plugin_list = NULL;
|
||||
|
||||
while (uv_loop_close(&context->uv.loop))
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
elops_init_context_uv(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
int n;
|
||||
|
||||
context->eventlib_signal_cb = info->signal_cb;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
context->pt[n].w_sigint.context = context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_destroy_context1_uv(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, m;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
int budget = 10000;
|
||||
pt = &context->pt[n];
|
||||
|
||||
/* only for internal loops... */
|
||||
|
||||
if (!pt->event_loop_foreign) {
|
||||
|
||||
while (budget-- && (m = uv_run(pt->uv.io_loop,
|
||||
UV_RUN_NOWAIT)))
|
||||
;
|
||||
if (m)
|
||||
lwsl_err("%s: tsi %d: not all closed\n",
|
||||
__func__, n);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* call destroy2 if internal loop */
|
||||
return !context->pt[0].event_loop_foreign;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_destroy_context2_uv(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, internal = 0;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
pt = &context->pt[n];
|
||||
|
||||
/* only for internal loops... */
|
||||
|
||||
if (!pt->event_loop_foreign && pt->uv.io_loop) {
|
||||
internal = 1;
|
||||
if (!context->finalize_destroy_after_internal_loops_stopped)
|
||||
uv_stop(pt->uv.io_loop);
|
||||
else {
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
uv_loop_close(pt->uv.io_loop);
|
||||
#endif
|
||||
lws_free_set_NULL(pt->uv.io_loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_wsi_logical_close_uv(struct lws *wsi)
|
||||
{
|
||||
if (wsi->parent_carries_io || !lws_socket_is_valid(wsi->desc.sockfd))
|
||||
return 0;
|
||||
|
||||
if (wsi->listener || wsi->event_pipe) {
|
||||
lwsl_debug("%s: %p: %d %d stop listener / pipe poll\n",
|
||||
__func__, wsi, wsi->listener, wsi->event_pipe);
|
||||
uv_poll_stop(&wsi->w_read.uv.watcher);
|
||||
}
|
||||
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
|
||||
/*
|
||||
* libuv has to do his own close handle processing asynchronously
|
||||
*/
|
||||
lws_libuv_closehandle(wsi);
|
||||
|
||||
return 1; /* do not complete the wsi close, uv close cb will do it */
|
||||
}
|
||||
|
||||
static int
|
||||
elops_check_client_connect_ok_uv(struct lws *wsi)
|
||||
{
|
||||
if (lws_libuv_check_watcher_active(wsi)) {
|
||||
lwsl_warn("Waiting for libuv watcher to close\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_close_handle_manually_uv(struct lws *wsi)
|
||||
{
|
||||
uv_handle_t *h = (void *)&wsi->w_read.uv.watcher;
|
||||
|
||||
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
|
||||
h->data = (void *)(lws_intptr_t)wsi->desc.sockfd;
|
||||
/* required to defer actual deletion until libuv has processed it */
|
||||
uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi_m);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_accept_uv(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
wsi->w_read.context = wsi->context;
|
||||
if (wsi->role_ops->file_handle)
|
||||
uv_poll_init(pt->uv.io_loop, &wsi->w_read.uv.watcher,
|
||||
(int)(long long)wsi->desc.filefd);
|
||||
else
|
||||
uv_poll_init_socket(pt->uv.io_loop, &wsi->w_read.uv.watcher,
|
||||
wsi->desc.sockfd);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_io_uv(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws_io_watcher *w = &wsi->w_read;
|
||||
int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE);
|
||||
|
||||
lwsl_debug("%s: %p: %d\n", __func__, wsi, flags);
|
||||
|
||||
/* w->context is set after the loop is initialized */
|
||||
|
||||
if (!pt->uv.io_loop || !w->context) {
|
||||
lwsl_info("%s: no io loop yet\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)))) {
|
||||
lwsl_err("%s: assert: flags %d", __func__, flags);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (flags & LWS_EV_START) {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
current_events |= UV_WRITABLE;
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
current_events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&w->uv.watcher, current_events, lws_io_cb);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
current_events &= ~UV_WRITABLE;
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
current_events &= ~UV_READABLE;
|
||||
|
||||
if (!(current_events & (UV_READABLE | UV_WRITABLE)))
|
||||
uv_poll_stop(&w->uv.watcher);
|
||||
else
|
||||
uv_poll_start(&w->uv.watcher, current_events,
|
||||
lws_io_cb);
|
||||
}
|
||||
|
||||
w->actual_events = current_events;
|
||||
}
|
||||
|
||||
static int
|
||||
elops_init_vhost_listen_wsi_uv(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n;
|
||||
|
||||
if (!wsi)
|
||||
return 0;
|
||||
if (wsi->w_read.context)
|
||||
return 0;
|
||||
|
||||
pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
if (!pt->uv.io_loop)
|
||||
return 0;
|
||||
|
||||
wsi->w_read.context = wsi->context;
|
||||
n = uv_poll_init_socket(pt->uv.io_loop,
|
||||
&wsi->w_read.uv.watcher, wsi->desc.sockfd);
|
||||
if (n) {
|
||||
lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
|
||||
n, (void *)(lws_intptr_t)wsi->desc.sockfd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elops_run_pt_uv(struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].uv.io_loop)
|
||||
uv_run(context->pt[tsi].uv.io_loop, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
elops_destroy_pt_uv(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
int m, ns;
|
||||
|
||||
lwsl_info("%s: %d\n", __func__, tsi);
|
||||
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
|
||||
return;
|
||||
|
||||
if (!pt->uv.io_loop)
|
||||
return;
|
||||
|
||||
if (pt->event_loop_destroy_processing_done)
|
||||
return;
|
||||
|
||||
pt->event_loop_destroy_processing_done = 1;
|
||||
|
||||
if (!pt->event_loop_foreign) {
|
||||
uv_signal_stop(&pt->w_sigint.uv.watcher);
|
||||
|
||||
ns = ARRAY_SIZE(sigs);
|
||||
if (lws_check_opt(context->options,
|
||||
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
|
||||
ns = 2;
|
||||
|
||||
for (m = 0; m < ns; m++) {
|
||||
uv_signal_stop(&pt->uv.signals[m]);
|
||||
uv_close((uv_handle_t *)&pt->uv.signals[m],
|
||||
lws_uv_close_cb_sa);
|
||||
}
|
||||
} else
|
||||
lwsl_debug("%s: not closing pt signals\n", __func__);
|
||||
|
||||
uv_timer_stop(&pt->uv.timeout_watcher);
|
||||
uv_close((uv_handle_t *)&pt->uv.timeout_watcher, lws_uv_close_cb_sa);
|
||||
uv_timer_stop(&pt->uv.hrtimer);
|
||||
uv_close((uv_handle_t *)&pt->uv.hrtimer, lws_uv_close_cb_sa);
|
||||
|
||||
uv_idle_stop(&pt->uv.idle);
|
||||
uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa);
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to be called after vhosts have been defined.
|
||||
*
|
||||
* If later, after server start, another vhost is added, this must be
|
||||
* called again to bind the vhost
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
elops_init_pt_uv(struct lws_context *context, void *_loop, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
int status = 0, n, ns, first = 1;
|
||||
uv_loop_t *loop = (uv_loop_t *)_loop;
|
||||
|
||||
if (!pt->uv.io_loop) {
|
||||
if (!loop) {
|
||||
loop = lws_malloc(sizeof(*loop), "libuv loop");
|
||||
if (!loop) {
|
||||
lwsl_err("OOM\n");
|
||||
return -1;
|
||||
}
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
uv_loop_init(loop);
|
||||
#else
|
||||
lwsl_err("This libuv is too old to work...\n");
|
||||
return 1;
|
||||
#endif
|
||||
pt->event_loop_foreign = 0;
|
||||
} else {
|
||||
lwsl_notice(" Using foreign event loop...\n");
|
||||
pt->event_loop_foreign = 1;
|
||||
}
|
||||
|
||||
pt->uv.io_loop = loop;
|
||||
uv_idle_init(loop, &pt->uv.idle);
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.idle, context);
|
||||
|
||||
|
||||
ns = ARRAY_SIZE(sigs);
|
||||
if (lws_check_opt(context->options,
|
||||
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
|
||||
ns = 2;
|
||||
|
||||
if (!pt->event_loop_foreign) {
|
||||
assert(ns <= (int)ARRAY_SIZE(pt->uv.signals));
|
||||
for (n = 0; n < ns; n++) {
|
||||
uv_signal_init(loop, &pt->uv.signals[n]);
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.signals[n],
|
||||
context);
|
||||
pt->uv.signals[n].data = pt->context;
|
||||
uv_signal_start(&pt->uv.signals[n],
|
||||
lws_uv_signal_handler, sigs[n]);
|
||||
}
|
||||
}
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
/*
|
||||
* Initialize the accept wsi read watcher with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*
|
||||
* We have to do it here because the uv loop(s) are not
|
||||
* initialized until after context creation.
|
||||
*/
|
||||
while (vh) {
|
||||
if (elops_init_vhost_listen_wsi_uv(vh->lserv_wsi) == -1)
|
||||
return -1;
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
return status;
|
||||
|
||||
uv_timer_init(pt->uv.io_loop, &pt->uv.timeout_watcher);
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.timeout_watcher, context);
|
||||
uv_timer_start(&pt->uv.timeout_watcher, lws_uv_timeout_cb, 10, 1000);
|
||||
|
||||
uv_timer_init(pt->uv.io_loop, &pt->uv.hrtimer);
|
||||
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.hrtimer, context);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_libuv_closewsi(uv_handle_t* handle)
|
||||
{
|
||||
struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) -
|
||||
(char *)(&n->w_read.uv.watcher));
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int lspd = 0, m;
|
||||
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
|
||||
/*
|
||||
* We get called back here for every wsi that closes
|
||||
*/
|
||||
|
||||
if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) {
|
||||
lspd = 1;
|
||||
context->deprecation_pending_listen_close_count--;
|
||||
if (!context->deprecation_pending_listen_close_count)
|
||||
lspd = 2;
|
||||
}
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
__lws_close_free_wsi_final(wsi);
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (lspd == 2 && context->deprecation_cb) {
|
||||
lwsl_notice("calling deprecation callback\n");
|
||||
context->deprecation_cb();
|
||||
}
|
||||
|
||||
lwsl_info("%s: sa left %d: dyn left: %d (rk %d)\n", __func__,
|
||||
context->count_event_loop_static_asset_handles,
|
||||
context->count_wsi_allocated, context->requested_kill);
|
||||
|
||||
/*
|
||||
* eventually, we closed all the wsi...
|
||||
*/
|
||||
|
||||
if (context->requested_kill && !context->count_wsi_allocated) {
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
|
||||
/*
|
||||
* Start Closing Phase 2: close of static handles
|
||||
*/
|
||||
|
||||
lwsl_info("%s: all lws dynamic handles down, closing static\n",
|
||||
__func__);
|
||||
|
||||
for (m = 0; m < context->count_threads; m++)
|
||||
elops_destroy_pt_uv(context, m);
|
||||
|
||||
/* protocols may have initialized libuv objects */
|
||||
|
||||
while (vh) {
|
||||
lws_vhost_destroy1(vh);
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (!context->count_event_loop_static_asset_handles &&
|
||||
context->pt[0].event_loop_foreign) {
|
||||
lwsl_info("%s: call lws_context_destroy2\n", __func__);
|
||||
lws_context_destroy2(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_closehandle(struct lws *wsi)
|
||||
{
|
||||
if (wsi->told_event_loop_closed) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
lwsl_debug("%s: %p\n", __func__, wsi);
|
||||
|
||||
wsi->told_event_loop_closed = 1;
|
||||
|
||||
/* required to defer actual deletion until libuv has processed it */
|
||||
uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi);
|
||||
}
|
||||
|
||||
struct lws_event_loop_ops event_loop_ops_uv = {
|
||||
/* name */ "libuv",
|
||||
/* init_context */ elops_init_context_uv,
|
||||
/* destroy_context1 */ elops_destroy_context1_uv,
|
||||
/* destroy_context2 */ elops_destroy_context2_uv,
|
||||
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uv,
|
||||
/* init_pt */ elops_init_pt_uv,
|
||||
/* wsi_logical_close */ elops_wsi_logical_close_uv,
|
||||
/* check_client_connect_ok */ elops_check_client_connect_ok_uv,
|
||||
/* close_handle_manually */ elops_close_handle_manually_uv,
|
||||
/* accept */ elops_accept_uv,
|
||||
/* io */ elops_io_uv,
|
||||
/* run_pt */ elops_run_pt_uv,
|
||||
/* destroy_pt */ elops_destroy_pt_uv,
|
||||
/* destroy wsi */ NULL,
|
||||
|
||||
/* periodic_events_available */ 0,
|
||||
};
|
71
lib/event-libs/libuv/private.h
Normal file
71
lib/event-libs/libuv/private.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_WITH_LIBUV
|
||||
*/
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
/*
|
||||
* libuv's async destroy cb means that asking to close something doesn't mean
|
||||
* you can destroy it or parent things until after the close completes.
|
||||
*
|
||||
* So we must reference-count creation and close completions with libuv.
|
||||
*
|
||||
* All "static" (per-pt or per-context) uv handles must
|
||||
*
|
||||
* - have their .data set to point to the context
|
||||
*
|
||||
* - contribute to context->uv_count_static_asset_handles
|
||||
* counting
|
||||
*/
|
||||
#define LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \
|
||||
{ uv_handle_t *_uht = (uv_handle_t *)(_x); _uht->data = _ctx; \
|
||||
_ctx->count_event_loop_static_asset_handles++; }
|
||||
#define LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \
|
||||
((struct lws_context *)((uv_handle_t *)((_x)->data)))
|
||||
#define LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
|
||||
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \
|
||||
count_event_loop_static_asset_handles))
|
||||
|
||||
struct lws_pt_eventlibs_libuv {
|
||||
uv_loop_t *io_loop;
|
||||
uv_signal_t signals[8];
|
||||
uv_timer_t timeout_watcher;
|
||||
uv_timer_t hrtimer;
|
||||
uv_idle_t idle;
|
||||
};
|
||||
|
||||
struct lws_context_eventlibs_libuv {
|
||||
uv_loop_t loop;
|
||||
};
|
||||
|
||||
struct lws_io_watcher_libuv {
|
||||
uv_poll_t watcher;
|
||||
};
|
||||
|
||||
struct lws_signal_watcher_libuv {
|
||||
uv_signal_t watcher;
|
||||
};
|
||||
|
||||
extern struct lws_event_loop_ops event_loop_ops_uv;
|
||||
|
||||
LWS_VISIBLE uv_loop_t *
|
||||
lws_uv_getloop(struct lws_context *context, int tsi);
|
43
lib/event-libs/poll/poll.c
Normal file
43
lib/event-libs/poll/poll.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_ROLE_WS
|
||||
*/
|
||||
|
||||
#include <core/private.h>
|
||||
|
||||
struct lws_event_loop_ops event_loop_ops_poll = {
|
||||
/* name */ "poll",
|
||||
/* init_context */ NULL,
|
||||
/* destroy_context1 */ NULL,
|
||||
/* destroy_context2 */ NULL,
|
||||
/* init_vhost_listen_wsi */ NULL,
|
||||
/* init_pt */ NULL,
|
||||
/* wsi_logical_close */ NULL,
|
||||
/* check_client_connect_ok */ NULL,
|
||||
/* close_handle_manually */ NULL,
|
||||
/* accept */ NULL,
|
||||
/* io */ NULL,
|
||||
/* run */ NULL,
|
||||
/* destroy_pt */ NULL,
|
||||
/* destroy wsi */ NULL,
|
||||
|
||||
/* periodic_events_available */ 1,
|
||||
};
|
23
lib/event-libs/poll/private.h
Normal file
23
lib/event-libs/poll/private.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
extern struct lws_event_loop_ops event_loop_ops_poll;
|
74
lib/event-libs/private.h
Normal file
74
lib/event-libs/private.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h
|
||||
*/
|
||||
|
||||
struct lws_event_loop_ops {
|
||||
const char *name;
|
||||
/* event loop-specific context init during context creation */
|
||||
int (*init_context)(struct lws_context *context,
|
||||
const struct lws_context_creation_info *info);
|
||||
/* called during lws_destroy_context */
|
||||
int (*destroy_context1)(struct lws_context *context);
|
||||
/* called during lws_destroy_context2 */
|
||||
int (*destroy_context2)(struct lws_context *context);
|
||||
/* init vhost listening wsi */
|
||||
int (*init_vhost_listen_wsi)(struct lws *wsi);
|
||||
/* init the event loop for a pt */
|
||||
int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
|
||||
/* called at end of first phase of close_free_wsi() */
|
||||
int (*wsi_logical_close)(struct lws *wsi);
|
||||
/* return nonzero if client connect not allowed */
|
||||
int (*check_client_connect_ok)(struct lws *wsi);
|
||||
/* close handle manually */
|
||||
void (*close_handle_manually)(struct lws *wsi);
|
||||
/* event loop accept processing */
|
||||
void (*accept)(struct lws *wsi);
|
||||
/* control wsi active events */
|
||||
void (*io)(struct lws *wsi, int flags);
|
||||
/* run the event loop for a pt */
|
||||
void (*run_pt)(struct lws_context *context, int tsi);
|
||||
/* called before pt is destroyed */
|
||||
void (*destroy_pt)(struct lws_context *context, int tsi);
|
||||
/* called just before wsi is freed */
|
||||
void (*destroy_wsi)(struct lws *wsi);
|
||||
|
||||
unsigned int periodic_events_available:1;
|
||||
};
|
||||
|
||||
/* bring in event libs private declarations */
|
||||
|
||||
#if defined(LWS_WITH_POLL)
|
||||
#include "event-libs/poll/private.h"
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_LIBUV)
|
||||
#include "event-libs/libuv/private.h"
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_LIBEVENT)
|
||||
#include "event-libs/libevent/private.h"
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_LIBEV)
|
||||
#include "event-libs/libev/private.h"
|
||||
#endif
|
||||
|
262
lib/handshake.c
262
lib/handshake.c
|
@ -1,262 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
/*
|
||||
* -04 of the protocol (actually the 80th version) has a radically different
|
||||
* handshake. The 04 spec gives the following idea
|
||||
*
|
||||
* The handshake from the client looks as follows:
|
||||
*
|
||||
* GET /chat HTTP/1.1
|
||||
* Host: server.example.com
|
||||
* Upgrade: websocket
|
||||
* Connection: Upgrade
|
||||
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||
* Sec-WebSocket-Origin: http://example.com
|
||||
* Sec-WebSocket-Protocol: chat, superchat
|
||||
* Sec-WebSocket-Version: 4
|
||||
*
|
||||
* The handshake from the server looks as follows:
|
||||
*
|
||||
* HTTP/1.1 101 Switching Protocols
|
||||
* Upgrade: websocket
|
||||
* Connection: Upgrade
|
||||
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
|
||||
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
|
||||
* Sec-WebSocket-Protocol: chat
|
||||
*/
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
* into multiple fragments. They may contain unknown headers with arbitrary
|
||||
* argument lengths. So, we parse using a single-character at a time state
|
||||
* machine that is completely independent of packet size.
|
||||
*
|
||||
* Returns <0 for error or length of chars consumed from buf (up to len)
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
unsigned char *last_char, *oldbuf = buf;
|
||||
lws_filepos_t body_chunk_len;
|
||||
size_t n;
|
||||
|
||||
lwsl_debug("%s: incoming len %d state %d\n", __func__, (int)len, wsi->state);
|
||||
|
||||
switch (wsi->state) {
|
||||
#ifdef LWS_USE_HTTP2
|
||||
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
|
||||
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
|
||||
case LWSS_HTTP2_ESTABLISHED:
|
||||
n = 0;
|
||||
while (n < len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
|
||||
lws_rxflow_cache(wsi, buf, n, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
if (lws_http2_parser(wsi, buf[n++])) {
|
||||
lwsl_debug("%s: http2_parser bailed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case LWSS_HTTP_ISSUING_FILE:
|
||||
return 0;
|
||||
|
||||
case LWSS_CLIENT_HTTP_ESTABLISHED:
|
||||
break;
|
||||
|
||||
case LWSS_HTTP:
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
/* fallthru */
|
||||
|
||||
case LWSS_HTTP_HEADERS:
|
||||
if (!wsi->u.hdr.ah) {
|
||||
lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
|
||||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
|
||||
last_char = buf;
|
||||
if (lws_handshake_server(wsi, &buf, (size_t)len))
|
||||
/* Handshake indicates this session is done. */
|
||||
goto bail;
|
||||
|
||||
/* we might have transitioned to RAW */
|
||||
if (wsi->mode == LWSCM_RAW)
|
||||
/* we gave the read buffer to RAW handler already */
|
||||
goto read_ok;
|
||||
|
||||
/*
|
||||
* It's possible that we've exhausted our data already, or
|
||||
* rx flow control has stopped us dealing with this early,
|
||||
* but lws_handshake_server doesn't update len for us.
|
||||
* Figure out how much was read, so that we can proceed
|
||||
* appropriately:
|
||||
*/
|
||||
len -= (buf - last_char);
|
||||
lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
/* More header content on the way */
|
||||
goto read_ok;
|
||||
|
||||
switch (wsi->state) {
|
||||
case LWSS_HTTP:
|
||||
case LWSS_HTTP_HEADERS:
|
||||
goto read_ok;
|
||||
case LWSS_HTTP_ISSUING_FILE:
|
||||
goto read_ok;
|
||||
case LWSS_HTTP_BODY:
|
||||
wsi->u.http.content_remain =
|
||||
wsi->u.http.content_length;
|
||||
if (wsi->u.http.content_remain)
|
||||
goto http_postbody;
|
||||
|
||||
/* there is no POST content */
|
||||
goto postbody_completion;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSS_HTTP_BODY:
|
||||
http_postbody:
|
||||
while (len && wsi->u.http.content_remain) {
|
||||
/* Copy as much as possible, up to the limit of:
|
||||
* what we have in the read buffer (len)
|
||||
* remaining portion of the POST body (content_remain)
|
||||
*/
|
||||
body_chunk_len = min(wsi->u.http.content_remain,len);
|
||||
wsi->u.http.content_remain -= body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->cgi) {
|
||||
struct lws_cgi_args args;
|
||||
|
||||
args.ch = LWS_STDIN;
|
||||
args.stdwsi = &wsi->cgi->stdwsi[0];
|
||||
args.data = buf;
|
||||
args.len = body_chunk_len;
|
||||
|
||||
/* returns how much used */
|
||||
n = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
|
||||
wsi->user_space,
|
||||
(void *)&args, 0);
|
||||
if ((int)n < 0)
|
||||
goto bail;
|
||||
} else {
|
||||
#endif
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
|
||||
buf, (size_t)body_chunk_len);
|
||||
if (n)
|
||||
goto bail;
|
||||
n = (size_t)body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
}
|
||||
#endif
|
||||
buf += n;
|
||||
|
||||
if (wsi->u.http.content_remain) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
wsi->context->timeout_secs);
|
||||
break;
|
||||
}
|
||||
/* he sent all the content in time */
|
||||
postbody_completion:
|
||||
#ifdef LWS_WITH_CGI
|
||||
/* if we're running a cgi, we can't let him off the hook just because he sent his POST data */
|
||||
if (wsi->cgi)
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, wsi->context->timeout_secs);
|
||||
else
|
||||
#endif
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (!wsi->cgi)
|
||||
#endif
|
||||
{
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY_COMPLETION,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSS_ESTABLISHED:
|
||||
case LWSS_AWAITING_CLOSE_ACK:
|
||||
case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
|
||||
case LWSS_SHUTDOWN:
|
||||
if (lws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
switch (wsi->mode) {
|
||||
case LWSCM_WS_SERVING:
|
||||
|
||||
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
|
||||
lwsl_info("interpret_incoming_packet has bailed\n");
|
||||
goto bail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
|
||||
break;
|
||||
}
|
||||
|
||||
read_ok:
|
||||
/* Nothing more to do for now */
|
||||
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
|
||||
|
||||
return buf - oldbuf;
|
||||
|
||||
bail:
|
||||
//lwsl_notice("closing connection at lws_read bail:\n");
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
|
||||
return -1;
|
||||
}
|
704
lib/hpack.c
704
lib/hpack.c
|
@ -1,704 +0,0 @@
|
|||
/*
|
||||
* lib/hpack.c
|
||||
*
|
||||
* Copyright (C) 2014 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
/*
|
||||
* Official static header table for HPACK
|
||||
* +-------+-----------------------------+---------------+
|
||||
| 1 | :authority | |
|
||||
| 2 | :method | GET |
|
||||
| 3 | :method | POST |
|
||||
| 4 | :path | / |
|
||||
| 5 | :path | /index.html |
|
||||
| 6 | :scheme | http |
|
||||
| 7 | :scheme | https |
|
||||
| 8 | :status | 200 |
|
||||
| 9 | :status | 204 |
|
||||
| 10 | :status | 206 |
|
||||
| 11 | :status | 304 |
|
||||
| 12 | :status | 400 |
|
||||
| 13 | :status | 404 |
|
||||
| 14 | :status | 500 |
|
||||
| 15 | accept-charset | |
|
||||
| 16 | accept-encoding | gzip, deflate |
|
||||
| 17 | accept-language | |
|
||||
| 18 | accept-ranges | |
|
||||
| 19 | accept | |
|
||||
| 20 | access-control-allow-origin | |
|
||||
| 21 | age | |
|
||||
| 22 | allow | |
|
||||
| 23 | authorization | |
|
||||
| 24 | cache-control | |
|
||||
| 25 | content-disposition | |
|
||||
| 26 | content-encoding | |
|
||||
| 27 | content-language | |
|
||||
| 28 | content-length | |
|
||||
| 29 | content-location | |
|
||||
| 30 | content-range | |
|
||||
| 31 | content-type | |
|
||||
| 32 | cookie | |
|
||||
| 33 | date | |
|
||||
| 34 | etag | |
|
||||
| 35 | expect | |
|
||||
| 36 | expires | |
|
||||
| 37 | from | |
|
||||
| 38 | host | |
|
||||
| 39 | if-match | |
|
||||
| 40 | if-modified-since | |
|
||||
| 41 | if-none-match | |
|
||||
| 42 | if-range | |
|
||||
| 43 | if-unmodified-since | |
|
||||
| 44 | last-modified | |
|
||||
| 45 | link | |
|
||||
| 46 | location | |
|
||||
| 47 | max-forwards | |
|
||||
| 48 | proxy-authenticate | |
|
||||
| 49 | proxy-authorization | |
|
||||
| 50 | range | |
|
||||
| 51 | referer | |
|
||||
| 52 | refresh | |
|
||||
| 53 | retry-after | |
|
||||
| 54 | server | |
|
||||
| 55 | set-cookie | |
|
||||
| 56 | strict-transport-security | |
|
||||
| 57 | transfer-encoding | |
|
||||
| 58 | user-agent | |
|
||||
| 59 | vary | |
|
||||
| 60 | via | |
|
||||
| 61 | www-authenticate | |
|
||||
+-------+-----------------------------+---------------+
|
||||
*/
|
||||
|
||||
static const unsigned char static_token[] = {
|
||||
0,
|
||||
WSI_TOKEN_HTTP_COLON_AUTHORITY,
|
||||
WSI_TOKEN_HTTP_COLON_METHOD,
|
||||
WSI_TOKEN_HTTP_COLON_METHOD,
|
||||
WSI_TOKEN_HTTP_COLON_PATH,
|
||||
WSI_TOKEN_HTTP_COLON_PATH,
|
||||
WSI_TOKEN_HTTP_COLON_SCHEME,
|
||||
WSI_TOKEN_HTTP_COLON_SCHEME,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
WSI_TOKEN_HTTP_ACCEPT_CHARSET,
|
||||
WSI_TOKEN_HTTP_ACCEPT_ENCODING,
|
||||
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE,
|
||||
WSI_TOKEN_HTTP_ACCEPT_RANGES,
|
||||
WSI_TOKEN_HTTP_ACCEPT,
|
||||
WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
WSI_TOKEN_HTTP_AGE,
|
||||
WSI_TOKEN_HTTP_ALLOW,
|
||||
WSI_TOKEN_HTTP_AUTHORIZATION,
|
||||
WSI_TOKEN_HTTP_CACHE_CONTROL,
|
||||
WSI_TOKEN_HTTP_CONTENT_DISPOSITION,
|
||||
WSI_TOKEN_HTTP_CONTENT_ENCODING,
|
||||
WSI_TOKEN_HTTP_CONTENT_LANGUAGE,
|
||||
WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
WSI_TOKEN_HTTP_CONTENT_LOCATION,
|
||||
WSI_TOKEN_HTTP_CONTENT_RANGE,
|
||||
WSI_TOKEN_HTTP_CONTENT_TYPE,
|
||||
WSI_TOKEN_HTTP_COOKIE,
|
||||
WSI_TOKEN_HTTP_DATE,
|
||||
WSI_TOKEN_HTTP_ETAG,
|
||||
WSI_TOKEN_HTTP_EXPECT,
|
||||
WSI_TOKEN_HTTP_EXPIRES,
|
||||
WSI_TOKEN_HTTP_FROM,
|
||||
WSI_TOKEN_HOST,
|
||||
WSI_TOKEN_HTTP_IF_MATCH,
|
||||
WSI_TOKEN_HTTP_IF_MODIFIED_SINCE,
|
||||
WSI_TOKEN_HTTP_IF_NONE_MATCH,
|
||||
WSI_TOKEN_HTTP_IF_RANGE,
|
||||
WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE,
|
||||
WSI_TOKEN_HTTP_LAST_MODIFIED,
|
||||
WSI_TOKEN_HTTP_LINK,
|
||||
WSI_TOKEN_HTTP_LOCATION,
|
||||
WSI_TOKEN_HTTP_MAX_FORWARDS,
|
||||
WSI_TOKEN_HTTP_PROXY_AUTHENTICATE,
|
||||
WSI_TOKEN_HTTP_PROXY_AUTHORIZATION,
|
||||
WSI_TOKEN_HTTP_RANGE,
|
||||
WSI_TOKEN_HTTP_REFERER,
|
||||
WSI_TOKEN_HTTP_REFRESH,
|
||||
WSI_TOKEN_HTTP_RETRY_AFTER,
|
||||
WSI_TOKEN_HTTP_SERVER,
|
||||
WSI_TOKEN_HTTP_SET_COOKIE,
|
||||
WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY,
|
||||
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
||||
WSI_TOKEN_HTTP_USER_AGENT,
|
||||
WSI_TOKEN_HTTP_VARY,
|
||||
WSI_TOKEN_HTTP_VIA,
|
||||
WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
|
||||
};
|
||||
|
||||
/* some of the entries imply values as well as header names */
|
||||
|
||||
static const char * const http2_canned[] = {
|
||||
"",
|
||||
"",
|
||||
"GET",
|
||||
"POST",
|
||||
"/",
|
||||
"/index.html",
|
||||
"http",
|
||||
"https",
|
||||
"200",
|
||||
"204",
|
||||
"206",
|
||||
"304",
|
||||
"400",
|
||||
"404",
|
||||
"500",
|
||||
"",
|
||||
"gzip, deflate"
|
||||
};
|
||||
|
||||
/* see minihuf.c */
|
||||
|
||||
#include "huftable.h"
|
||||
|
||||
static int huftable_decode(int pos, char c)
|
||||
{
|
||||
int q = pos + !!c;
|
||||
|
||||
if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */
|
||||
return lextable[q] | 0x8000;
|
||||
|
||||
return pos + (lextable[q] << 1);
|
||||
}
|
||||
|
||||
static int lws_hpack_update_table_size(struct lws *wsi, int idx)
|
||||
{
|
||||
lwsl_info("hpack set table size %d\n", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
|
||||
{
|
||||
struct allocated_headers * ah = wsi->u.http2.http.ah;
|
||||
|
||||
if (!hdr_token_idx) {
|
||||
lwsl_err("%s: zero hdr_token_idx\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ah->nfrag >= ARRAY_SIZE(ah->frag_index)) {
|
||||
lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ah->frags[ah->nfrag].offset = ah->pos;
|
||||
ah->frags[ah->nfrag].len = 0;
|
||||
ah->frags[ah->nfrag].nfrag = 0;
|
||||
|
||||
ah->frag_index[hdr_token_idx] = ah->nfrag;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lws_frag_append(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
struct allocated_headers * ah = wsi->u.http2.http.ah;
|
||||
|
||||
ah->data[ah->pos++] = c;
|
||||
ah->frags[ah->nfrag].len++;
|
||||
|
||||
return ah->pos >= wsi->context->max_http_header_data;
|
||||
}
|
||||
|
||||
static int lws_frag_end(struct lws *wsi)
|
||||
{
|
||||
if (lws_frag_append(wsi, 0))
|
||||
return 1;
|
||||
|
||||
wsi->u.http2.http.ah->nfrag++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lws_dump_header(struct lws *wsi, int hdr)
|
||||
{
|
||||
char s[200];
|
||||
int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
|
||||
s[len] = '\0';
|
||||
lwsl_info(" hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
|
||||
}
|
||||
|
||||
static int
|
||||
lws_token_from_index(struct lws *wsi, int index, char **arg, int *len)
|
||||
{
|
||||
struct hpack_dynamic_table *dyn;
|
||||
|
||||
/* dynamic table only belongs to network wsi */
|
||||
|
||||
wsi = lws_http2_get_network_wsi(wsi);
|
||||
|
||||
dyn = wsi->u.http2.hpack_dyn_table;
|
||||
|
||||
if (index < ARRAY_SIZE(static_token))
|
||||
return static_token[index];
|
||||
|
||||
if (!dyn)
|
||||
return 0;
|
||||
|
||||
index -= ARRAY_SIZE(static_token);
|
||||
if (index >= dyn->num_entries)
|
||||
return 0;
|
||||
|
||||
if (arg && len) {
|
||||
*arg = dyn->args + dyn->entries[index].arg_offset;
|
||||
*len = dyn->entries[index].arg_len;
|
||||
}
|
||||
|
||||
return dyn->entries[index].token;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
|
||||
{
|
||||
struct hpack_dynamic_table *dyn;
|
||||
int ret = 1;
|
||||
|
||||
wsi = lws_http2_get_network_wsi(wsi);
|
||||
dyn = wsi->u.http2.hpack_dyn_table;
|
||||
|
||||
if (!dyn) {
|
||||
dyn = lws_zalloc(sizeof(*dyn));
|
||||
if (!dyn)
|
||||
return 1;
|
||||
wsi->u.http2.hpack_dyn_table = dyn;
|
||||
|
||||
dyn->args = lws_malloc(1024);
|
||||
if (!dyn->args)
|
||||
goto bail1;
|
||||
dyn->args_length = 1024;
|
||||
dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20);
|
||||
if (!dyn->entries)
|
||||
goto bail2;
|
||||
dyn->num_entries = 20;
|
||||
}
|
||||
|
||||
if (dyn->next == dyn->num_entries)
|
||||
return 1;
|
||||
|
||||
if (dyn->args_length - dyn->pos < len)
|
||||
return 1;
|
||||
|
||||
dyn->entries[dyn->next].token = token;
|
||||
dyn->entries[dyn->next].arg_offset = dyn->pos;
|
||||
if (len)
|
||||
memcpy(dyn->args + dyn->pos, arg, len);
|
||||
dyn->entries[dyn->next].arg_len = len;
|
||||
|
||||
lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
|
||||
__func__, dyn->next, token, lws_token_to_string(token), len);
|
||||
|
||||
dyn->pos += len;
|
||||
dyn->next++;
|
||||
|
||||
return 0;
|
||||
|
||||
bail2:
|
||||
lws_free(dyn->args);
|
||||
bail1:
|
||||
lws_free(dyn);
|
||||
wsi->u.http2.hpack_dyn_table = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lws_write_indexed_hdr(struct lws *wsi, int idx)
|
||||
{
|
||||
const char *p;
|
||||
int tok = lws_token_from_index(wsi, idx, NULL, 0);
|
||||
|
||||
lwsl_info("writing indexed hdr %d (tok %d '%s')\n", idx, tok,
|
||||
lws_token_to_string(tok));
|
||||
|
||||
if (lws_frag_start(wsi, tok))
|
||||
return 1;
|
||||
|
||||
if (idx < ARRAY_SIZE(http2_canned)) {
|
||||
p = http2_canned[idx];
|
||||
while (*p)
|
||||
if (lws_frag_append(wsi, *p++))
|
||||
return 1;
|
||||
}
|
||||
if (lws_frag_end(wsi))
|
||||
return 1;
|
||||
|
||||
lws_dump_header(wsi, tok);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_hpack_interpret(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
unsigned int prev;
|
||||
unsigned char c1;
|
||||
int n;
|
||||
|
||||
lwsl_debug(" state %d\n", wsi->u.http2.hpack);
|
||||
|
||||
switch (wsi->u.http2.hpack) {
|
||||
case HPKS_OPT_PADDING:
|
||||
wsi->u.http2.padding = c;
|
||||
lwsl_info("padding %d\n", c);
|
||||
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
|
||||
wsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
|
||||
wsi->u.http2.hpack_m = 4;
|
||||
} else
|
||||
wsi->u.http2.hpack = HPKS_TYPE;
|
||||
break;
|
||||
case HKPS_OPT_E_DEPENDENCY:
|
||||
wsi->u.http2.hpack_e_dep <<= 8;
|
||||
wsi->u.http2.hpack_e_dep |= c;
|
||||
if (! --wsi->u.http2.hpack_m) {
|
||||
lwsl_info("hpack_e_dep = 0x%x\n", wsi->u.http2.hpack_e_dep);
|
||||
wsi->u.http2.hpack = HKPS_OPT_WEIGHT;
|
||||
}
|
||||
break;
|
||||
case HKPS_OPT_WEIGHT:
|
||||
/* weight */
|
||||
wsi->u.http2.hpack = HPKS_TYPE;
|
||||
break;
|
||||
|
||||
case HPKS_TYPE:
|
||||
|
||||
if (wsi->u.http2.count > (wsi->u.http2.length - wsi->u.http2.padding)) {
|
||||
lwsl_info("padding eat\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c & 0x80) { /* indexed header field only */
|
||||
/* just a possibly-extended integer */
|
||||
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_7;
|
||||
lwsl_debug("HKPS_TYPE setting header_index %d\n", c & 0x7f);
|
||||
wsi->u.http2.header_index = c & 0x7f;
|
||||
if ((c & 0x7f) == 0x7f) {
|
||||
wsi->u.http2.hpack_len = c & 0x7f;
|
||||
wsi->u.http2.hpack_m = 0;
|
||||
wsi->u.http2.hpack = HPKS_IDX_EXT;
|
||||
break;
|
||||
}
|
||||
lwsl_debug("HKPS_TYPE: %d\n", c & 0x7f);
|
||||
if (lws_write_indexed_hdr(wsi, c & 0x7f))
|
||||
return 1;
|
||||
/* stay at same state */
|
||||
break;
|
||||
}
|
||||
if (c & 0x40) { /* literal header incr idx */
|
||||
/*
|
||||
* [possibly-extended hdr idx (6) | new literal hdr name]
|
||||
* H + possibly-extended value length
|
||||
* literal value
|
||||
*/
|
||||
lwsl_debug("HKPS_TYPE 2 setting header_index %d\n", 0);
|
||||
wsi->u.http2.header_index = 0;
|
||||
if (c == 0x40) { /* literal name */
|
||||
wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE_INCR;
|
||||
wsi->u.http2.value = 0;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
break;
|
||||
}
|
||||
/* indexed name */
|
||||
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR;
|
||||
if ((c & 0x3f) == 0x3f) {
|
||||
wsi->u.http2.hpack_len = c & 0x3f;
|
||||
wsi->u.http2.hpack_m = 0;
|
||||
wsi->u.http2.hpack = HPKS_IDX_EXT;
|
||||
break;
|
||||
}
|
||||
lwsl_debug("HKPS_TYPE 3 setting header_index %d\n", c & 0x3f);
|
||||
wsi->u.http2.header_index = c & 0x3f;
|
||||
wsi->u.http2.value = 1;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
break;
|
||||
}
|
||||
switch(c & 0xf0) {
|
||||
case 0x10: /* literal header never index */
|
||||
case 0: /* literal header without indexing */
|
||||
/*
|
||||
* follows 0x40 except 4-bit hdr idx
|
||||
* and don't add to index
|
||||
*/
|
||||
if (c == 0) { /* literal name */
|
||||
wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
wsi->u.http2.value = 0;
|
||||
break;
|
||||
}
|
||||
//lwsl_debug("indexed\n");
|
||||
/* indexed name */
|
||||
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_4_VALUE;
|
||||
wsi->u.http2.header_index = 0;
|
||||
if ((c & 0xf) == 0xf) {
|
||||
wsi->u.http2.hpack_len = c & 0xf;
|
||||
wsi->u.http2.hpack_m = 0;
|
||||
wsi->u.http2.hpack = HPKS_IDX_EXT;
|
||||
break;
|
||||
}
|
||||
//lwsl_err("HKPS_TYPE 5 setting header_index %d\n", c & 0xf);
|
||||
wsi->u.http2.header_index = c & 0xf;
|
||||
wsi->u.http2.value = 1;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
case 0x30: /* header table size update */
|
||||
/* possibly-extended size value (5) */
|
||||
wsi->u.http2.hpack_type = HPKT_SIZE_5;
|
||||
if ((c & 0x1f) == 0x1f) {
|
||||
wsi->u.http2.hpack_len = c & 0x1f;
|
||||
wsi->u.http2.hpack_m = 0;
|
||||
wsi->u.http2.hpack = HPKS_IDX_EXT;
|
||||
break;
|
||||
}
|
||||
lws_hpack_update_table_size(wsi, c & 0x1f);
|
||||
/* stay at HPKS_TYPE state */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HPKS_IDX_EXT:
|
||||
wsi->u.http2.hpack_len += (c & 0x7f) << wsi->u.http2.hpack_m;
|
||||
wsi->u.http2.hpack_m += 7;
|
||||
if (!(c & 0x80)) {
|
||||
switch (wsi->u.http2.hpack_type) {
|
||||
case HPKT_INDEXED_HDR_7:
|
||||
//lwsl_err("HKPS_IDX_EXT hdr idx %d\n", wsi->u.http2.hpack_len);
|
||||
if (lws_write_indexed_hdr(wsi, wsi->u.http2.hpack_len))
|
||||
return 1;
|
||||
wsi->u.http2.hpack = HPKS_TYPE;
|
||||
break;
|
||||
default:
|
||||
// lwsl_err("HKPS_IDX_EXT setting header_index %d\n",
|
||||
// wsi->u.http2.hpack_len);
|
||||
wsi->u.http2.header_index = wsi->u.http2.hpack_len;
|
||||
wsi->u.http2.value = 1;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HPKS_HLEN: /* [ H | 7+ ] */
|
||||
wsi->u.http2.huff = !!(c & 0x80);
|
||||
wsi->u.http2.hpack_pos = 0;
|
||||
wsi->u.http2.hpack_len = c & 0x7f;
|
||||
if (wsi->u.http2.hpack_len < 0x7f) {
|
||||
pre_data:
|
||||
if (wsi->u.http2.value) {
|
||||
if (wsi->u.http2.header_index)
|
||||
if (lws_frag_start(wsi, lws_token_from_index(wsi,
|
||||
wsi->u.http2.header_index,
|
||||
NULL, NULL))) {
|
||||
// lwsl_notice("%s: hlen failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
|
||||
wsi->u.http2.hpack = HPKS_DATA;
|
||||
break;
|
||||
}
|
||||
wsi->u.http2.hpack_m = 0;
|
||||
wsi->u.http2.hpack = HPKS_HLEN_EXT;
|
||||
break;
|
||||
|
||||
case HPKS_HLEN_EXT:
|
||||
wsi->u.http2.hpack_len += (c & 0x7f) <<
|
||||
wsi->u.http2.hpack_m;
|
||||
wsi->u.http2.hpack_m += 7;
|
||||
if (!(c & 0x80))
|
||||
goto pre_data;
|
||||
|
||||
break;
|
||||
|
||||
case HPKS_DATA:
|
||||
for (n = 0; n < 8; n++) {
|
||||
if (wsi->u.http2.huff) {
|
||||
prev = wsi->u.http2.hpack_pos;
|
||||
wsi->u.http2.hpack_pos = huftable_decode(
|
||||
wsi->u.http2.hpack_pos,
|
||||
(c >> 7) & 1);
|
||||
c <<= 1;
|
||||
if (wsi->u.http2.hpack_pos == 0xffff)
|
||||
return 1;
|
||||
if (!(wsi->u.http2.hpack_pos & 0x8000))
|
||||
continue;
|
||||
c1 = wsi->u.http2.hpack_pos & 0x7fff;
|
||||
wsi->u.http2.hpack_pos = 0;
|
||||
|
||||
if (!c1 && prev == HUFTABLE_0x100_PREV)
|
||||
; /* EOT */
|
||||
} else {
|
||||
n = 8;
|
||||
c1 = c;
|
||||
}
|
||||
if (wsi->u.http2.value) { /* value */
|
||||
if (wsi->u.http2.header_index)
|
||||
if (lws_frag_append(wsi, c1))
|
||||
return 1;
|
||||
} else { /* name */
|
||||
if (lws_parse(wsi, c1))
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
if (--wsi->u.http2.hpack_len == 0) {
|
||||
|
||||
switch (wsi->u.http2.hpack_type) {
|
||||
case HPKT_LITERAL_HDR_VALUE_INCR:
|
||||
case HPKT_INDEXED_HDR_6_VALUE_INCR: // !!!
|
||||
if (lws_hpack_add_dynamic_header(wsi,
|
||||
lws_token_from_index(wsi,
|
||||
wsi->u.http2.header_index,
|
||||
NULL, NULL), NULL, 0))
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
n = 8;
|
||||
if (wsi->u.http2.value) {
|
||||
if (lws_frag_end(wsi))
|
||||
return 1;
|
||||
// lwsl_err("data\n");
|
||||
lws_dump_header(wsi, lws_token_from_index(
|
||||
wsi, wsi->u.http2.header_index,
|
||||
NULL, NULL));
|
||||
if (wsi->u.http2.count + wsi->u.http2.padding ==
|
||||
wsi->u.http2.length)
|
||||
wsi->u.http2.hpack = HKPS_OPT_DISCARD_PADDING;
|
||||
else
|
||||
wsi->u.http2.hpack = HPKS_TYPE;
|
||||
} else { /* name */
|
||||
//if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
|
||||
|
||||
wsi->u.http2.value = 1;
|
||||
wsi->u.http2.hpack = HPKS_HLEN;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HKPS_OPT_DISCARD_PADDING:
|
||||
lwsl_info("eating padding %x\n", c);
|
||||
if (! --wsi->u.http2.padding)
|
||||
wsi->u.http2.hpack = HPKS_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lws_http2_num(int starting_bits, unsigned long num,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
int mask = (1 << starting_bits) - 1;
|
||||
|
||||
if (num < mask) {
|
||||
*((*p)++) |= num;
|
||||
return *p >= end;
|
||||
}
|
||||
|
||||
*((*p)++) |= mask;
|
||||
if (*p >= end)
|
||||
return 1;
|
||||
|
||||
num -= mask;
|
||||
while (num >= 128) {
|
||||
*((*p)++) = 0x80 | (num & 0x7f);
|
||||
if (*p >= end)
|
||||
return 1;
|
||||
num >>= 7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_add_http2_header_by_name(struct lws *wsi,
|
||||
const unsigned char *name,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
int len;
|
||||
|
||||
lwsl_info("%s: %p %s:%s\n", __func__, *p, name, value);
|
||||
|
||||
len = strlen((char *)name);
|
||||
if (len)
|
||||
if (name[len - 1] == ':')
|
||||
len--;
|
||||
|
||||
if (end - *p < len + length + 8)
|
||||
return 1;
|
||||
|
||||
*((*p)++) = 0; /* not indexed, literal name */
|
||||
|
||||
**p = 0; /* non-HUF */
|
||||
if (lws_http2_num(7, len, p, end))
|
||||
return 1;
|
||||
memcpy(*p, name, len);
|
||||
*p += len;
|
||||
|
||||
*(*p) = 0; /* non-HUF */
|
||||
if (lws_http2_num(7, length, p, end))
|
||||
return 1;
|
||||
|
||||
memcpy(*p, value, length);
|
||||
*p += length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_add_http2_header_by_token(struct lws *wsi, enum lws_token_indexes token,
|
||||
const unsigned char *value, int length,
|
||||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
const unsigned char *name;
|
||||
|
||||
name = lws_token_to_string(token);
|
||||
if (!name)
|
||||
return 1;
|
||||
|
||||
return lws_add_http2_header_by_name(wsi, name, value, length, p, end);
|
||||
}
|
||||
|
||||
int lws_add_http2_header_status(struct lws *wsi,
|
||||
unsigned int code, unsigned char **p,
|
||||
unsigned char *end)
|
||||
{
|
||||
unsigned char status[10];
|
||||
int n;
|
||||
|
||||
wsi->u.http2.send_END_STREAM = !!(code >= 400);
|
||||
|
||||
n = sprintf((char *)status, "%u", code);
|
||||
if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS,
|
||||
status, n, p, end))
|
||||
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
536
lib/http2.c
536
lib/http2.c
|
@ -1,536 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
const struct http2_settings lws_http2_default_settings = { {
|
||||
0,
|
||||
/* LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE */ 4096,
|
||||
/* LWS_HTTP2_SETTINGS__ENABLE_PUSH */ 1,
|
||||
/* LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS */ 100,
|
||||
/* LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE */ 65535,
|
||||
/* LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE */ 16384,
|
||||
/* LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE */ ~0,
|
||||
}};
|
||||
|
||||
|
||||
void lws_http2_init(struct http2_settings *settings)
|
||||
{
|
||||
memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings));
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
|
||||
{
|
||||
do {
|
||||
if (wsi->u.http2.my_stream_id == sid)
|
||||
return wsi;
|
||||
|
||||
wsi = wsi->u.http2.next_child_wsi;
|
||||
} while (wsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
|
||||
unsigned int sid)
|
||||
{
|
||||
struct lws *wsi = lws_create_new_server_wsi(vhost);
|
||||
|
||||
if (!wsi)
|
||||
return NULL;
|
||||
|
||||
/* no more children allowed by parent */
|
||||
if (parent_wsi->u.http2.child_count + 1 ==
|
||||
parent_wsi->u.http2.peer_settings.setting[
|
||||
LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS])
|
||||
goto bail;
|
||||
lws_http2_init(&wsi->u.http2.peer_settings);
|
||||
lws_http2_init(&wsi->u.http2.my_settings);
|
||||
wsi->u.http2.stream_id = sid;
|
||||
wsi->u.http2.my_stream_id = sid;
|
||||
|
||||
wsi->u.http2.parent_wsi = parent_wsi;
|
||||
wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi;
|
||||
parent_wsi->u.http2.next_child_wsi = wsi;
|
||||
parent_wsi->u.http2.child_count++;
|
||||
|
||||
wsi->u.http2.my_priority = 16;
|
||||
wsi->u.http2.tx_credit = 65535;
|
||||
|
||||
wsi->state = LWSS_HTTP2_ESTABLISHED;
|
||||
wsi->mode = parent_wsi->mode;
|
||||
|
||||
wsi->protocol = &vhost->protocols[0];
|
||||
if (lws_ensure_user_space(wsi))
|
||||
goto bail;
|
||||
|
||||
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
|
||||
parent_wsi, wsi, sid, wsi->user_space);
|
||||
|
||||
return wsi;
|
||||
|
||||
bail:
|
||||
vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
|
||||
NULL, NULL, 0);
|
||||
lws_free(wsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lws_remove_server_child_wsi(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
struct lws **w = &wsi->u.http2.parent_wsi;
|
||||
do {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.http2.next_child_wsi;
|
||||
(wsi->u.http2.parent_wsi)->u.http2.child_count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
w = &((*w)->u.http2.next_child_wsi);
|
||||
} while (*w);
|
||||
|
||||
lwsl_err("%s: can't find %p\n", __func__, wsi);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_http2_interpret_settings_payload(struct http2_settings *settings,
|
||||
unsigned char *buf, int len)
|
||||
{
|
||||
unsigned int a, b;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (len < LWS_HTTP2_SETTINGS_LENGTH)
|
||||
return 1;
|
||||
|
||||
while (len >= LWS_HTTP2_SETTINGS_LENGTH) {
|
||||
a = (buf[0] << 8) | buf[1];
|
||||
if (a < LWS_HTTP2_SETTINGS__COUNT) {
|
||||
b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5];
|
||||
settings->setting[a] = b;
|
||||
lwsl_info("http2 settings %d <- 0x%x\n", a, b);
|
||||
}
|
||||
len -= LWS_HTTP2_SETTINGS_LENGTH;
|
||||
buf += LWS_HTTP2_SETTINGS_LENGTH;
|
||||
}
|
||||
|
||||
if (len)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws *lws_http2_get_network_wsi(struct lws *wsi)
|
||||
{
|
||||
while (wsi->u.http2.parent_wsi)
|
||||
wsi = wsi->u.http2.parent_wsi;
|
||||
|
||||
return wsi;
|
||||
}
|
||||
|
||||
int lws_http2_frame_write(struct lws *wsi, int type, int flags,
|
||||
unsigned int sid, unsigned int len, unsigned char *buf)
|
||||
{
|
||||
struct lws *wsi_eff = lws_http2_get_network_wsi(wsi);
|
||||
unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
|
||||
int n;
|
||||
|
||||
*p++ = len >> 16;
|
||||
*p++ = len >> 8;
|
||||
*p++ = len;
|
||||
*p++ = type;
|
||||
*p++ = flags;
|
||||
*p++ = sid >> 24;
|
||||
*p++ = sid >> 16;
|
||||
*p++ = sid >> 8;
|
||||
*p++ = sid;
|
||||
|
||||
lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d, tx_credit=%d\n",
|
||||
__func__, wsi, wsi_eff, type, flags, sid, len,
|
||||
wsi->u.http2.tx_credit);
|
||||
|
||||
if (type == LWS_HTTP2_FRAME_TYPE_DATA) {
|
||||
if (wsi->u.http2.tx_credit < len)
|
||||
lwsl_err("%s: %p: sending payload len %d"
|
||||
" but tx_credit only %d!\n", __func__, wsi, len,
|
||||
wsi->u.http2.tx_credit);
|
||||
wsi->u.http2.tx_credit -= len;
|
||||
}
|
||||
|
||||
n = lws_issue_raw(wsi_eff, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH],
|
||||
len + LWS_HTTP2_FRAME_HEADER_LENGTH);
|
||||
if (n >= LWS_HTTP2_FRAME_HEADER_LENGTH)
|
||||
return n - LWS_HTTP2_FRAME_HEADER_LENGTH;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void lws_http2_settings_write(struct lws *wsi, int n, unsigned char *buf)
|
||||
{
|
||||
*buf++ = n >> 8;
|
||||
*buf++ = n;
|
||||
*buf++ = wsi->u.http2.my_settings.setting[n] >> 24;
|
||||
*buf++ = wsi->u.http2.my_settings.setting[n] >> 16;
|
||||
*buf++ = wsi->u.http2.my_settings.setting[n] >> 8;
|
||||
*buf = wsi->u.http2.my_settings.setting[n];
|
||||
}
|
||||
|
||||
static const char * https_client_preface =
|
||||
"PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a";
|
||||
|
||||
int
|
||||
lws_http2_parser(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
struct lws *swsi;
|
||||
int n;
|
||||
|
||||
switch (wsi->state) {
|
||||
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
|
||||
if (https_client_preface[wsi->u.http2.count++] != c)
|
||||
return 1;
|
||||
|
||||
if (!https_client_preface[wsi->u.http2.count]) {
|
||||
lwsl_info("http2: %p: established\n", wsi);
|
||||
wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS;
|
||||
wsi->u.http2.count = 0;
|
||||
wsi->u.http2.tx_credit = 65535;
|
||||
|
||||
/*
|
||||
* we must send a settings frame -- empty one is OK...
|
||||
* that must be the first thing sent by server
|
||||
* and the peer must send a SETTINGS with ACK flag...
|
||||
*/
|
||||
|
||||
lws_set_protocol_write_pending(wsi,
|
||||
LWS_PPS_HTTP2_MY_SETTINGS);
|
||||
}
|
||||
break;
|
||||
|
||||
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
|
||||
case LWSS_HTTP2_ESTABLISHED:
|
||||
if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { // payload
|
||||
wsi->u.http2.count++;
|
||||
wsi->u.http2.stream_wsi->u.http2.count = wsi->u.http2.count;
|
||||
/* applies to wsi->u.http2.stream_wsi which may be wsi*/
|
||||
switch(wsi->u.http2.type) {
|
||||
case LWS_HTTP2_FRAME_TYPE_SETTINGS:
|
||||
wsi->u.http2.stream_wsi->u.http2.one_setting[wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH] = c;
|
||||
if (wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH == LWS_HTTP2_SETTINGS_LENGTH - 1)
|
||||
if (lws_http2_interpret_settings_payload(
|
||||
&wsi->u.http2.stream_wsi->u.http2.peer_settings,
|
||||
wsi->u.http2.one_setting,
|
||||
LWS_HTTP2_SETTINGS_LENGTH))
|
||||
return 1;
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
|
||||
case LWS_HTTP2_FRAME_TYPE_HEADERS:
|
||||
lwsl_info(" %02X\n", c);
|
||||
if (!wsi->u.http2.stream_wsi->u.hdr.ah)
|
||||
if (lws_header_table_attach(wsi->u.http2.stream_wsi, 0)) {
|
||||
lwsl_err("%s: Failed to get ah\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
if (lws_hpack_interpret(wsi->u.http2.stream_wsi, c)) {
|
||||
lwsl_notice("%s: lws_hpack_interpret failed\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_GOAWAY:
|
||||
if (wsi->u.http2.count >= 5 && wsi->u.http2.count <= 8) {
|
||||
wsi->u.http2.hpack_e_dep <<= 8;
|
||||
wsi->u.http2.hpack_e_dep |= c;
|
||||
if (wsi->u.http2.count == 8) {
|
||||
lwsl_info("goaway err 0x%x\n", wsi->u.http2.hpack_e_dep);
|
||||
}
|
||||
}
|
||||
wsi->u.http2.GOING_AWAY = 1;
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_DATA:
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_PRIORITY:
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_RST_STREAM:
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE:
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_PING:
|
||||
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
|
||||
} else { /* they're sending us a ping request */
|
||||
if (wsi->u.http2.count > 8)
|
||||
return 1;
|
||||
wsi->u.http2.ping_payload[wsi->u.http2.count - 1] = c;
|
||||
}
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
|
||||
wsi->u.http2.hpack_e_dep <<= 8;
|
||||
wsi->u.http2.hpack_e_dep |= c;
|
||||
break;
|
||||
}
|
||||
if (wsi->u.http2.count != wsi->u.http2.length)
|
||||
break;
|
||||
|
||||
/* end of frame */
|
||||
|
||||
wsi->u.http2.frame_state = 0;
|
||||
wsi->u.http2.count = 0;
|
||||
swsi = wsi->u.http2.stream_wsi;
|
||||
/* set our initial window size */
|
||||
if (!wsi->u.http2.initialized) {
|
||||
wsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
|
||||
lwsl_info("initial tx credit on master conn %p: %d\n", wsi, wsi->u.http2.tx_credit);
|
||||
wsi->u.http2.initialized = 1;
|
||||
}
|
||||
switch (wsi->u.http2.type) {
|
||||
case LWS_HTTP2_FRAME_TYPE_HEADERS:
|
||||
/* service the http request itself */
|
||||
lwsl_info("servicing initial http request, wsi=%p, stream wsi=%p\n", wsi, wsi->u.http2.stream_wsi);
|
||||
n = lws_http_action(swsi);
|
||||
(void)n;
|
||||
lwsl_info(" action result %d\n", n);
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_PING:
|
||||
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
|
||||
} else { /* they're sending us a ping request */
|
||||
lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_PONG);
|
||||
}
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
|
||||
wsi->u.http2.hpack_e_dep &= ~(1 << 31);
|
||||
if ((lws_intptr_t)swsi->u.http2.tx_credit + (lws_intptr_t)wsi->u.http2.hpack_e_dep > (~(1 << 31)))
|
||||
return 1; /* actually need to close swsi not the whole show */
|
||||
swsi->u.http2.tx_credit += wsi->u.http2.hpack_e_dep;
|
||||
if (swsi->u.http2.waiting_tx_credit && swsi->u.http2.tx_credit > 0) {
|
||||
lwsl_info("%s: %p: waiting_tx_credit -> wait on writeable\n", __func__, wsi);
|
||||
swsi->u.http2.waiting_tx_credit = 0;
|
||||
lws_callback_on_writable(swsi);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (wsi->u.http2.frame_state++) {
|
||||
case 0:
|
||||
wsi->u.http2.length = c;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
wsi->u.http2.length <<= 8;
|
||||
wsi->u.http2.length |= c;
|
||||
break;
|
||||
case 3:
|
||||
wsi->u.http2.type = c;
|
||||
break;
|
||||
case 4:
|
||||
wsi->u.http2.flags = c;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
wsi->u.http2.stream_id <<= 8;
|
||||
wsi->u.http2.stream_id |= c;
|
||||
break;
|
||||
}
|
||||
if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { /* frame header complete */
|
||||
lwsl_info("frame: type 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n",
|
||||
wsi->u.http2.type, wsi->u.http2.flags, wsi->u.http2.stream_id, wsi->u.http2.length);
|
||||
wsi->u.http2.count = 0;
|
||||
|
||||
wsi->u.http2.stream_wsi = wsi;
|
||||
if (wsi->u.http2.stream_id)
|
||||
wsi->u.http2.stream_wsi = lws_http2_wsi_from_id(wsi, wsi->u.http2.stream_id);
|
||||
|
||||
switch (wsi->u.http2.type) {
|
||||
case LWS_HTTP2_FRAME_TYPE_SETTINGS:
|
||||
/* nonzero sid on settings is illegal */
|
||||
if (wsi->u.http2.stream_id)
|
||||
return 1;
|
||||
|
||||
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
|
||||
} else
|
||||
/* non-ACK coming in means we must ACK it */
|
||||
lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_PING:
|
||||
if (wsi->u.http2.stream_id)
|
||||
return 1;
|
||||
if (wsi->u.http2.length != 8)
|
||||
return 1;
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
|
||||
if (wsi->u.http2.END_HEADERS)
|
||||
return 1;
|
||||
goto update_end_headers;
|
||||
|
||||
case LWS_HTTP2_FRAME_TYPE_HEADERS:
|
||||
lwsl_info("LWS_HTTP2_FRAME_TYPE_HEADERS: stream_id = %d\n", wsi->u.http2.stream_id);
|
||||
if (!wsi->u.http2.stream_id)
|
||||
return 1;
|
||||
if (!wsi->u.http2.stream_wsi) {
|
||||
wsi->u.http2.stream_wsi =
|
||||
lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id);
|
||||
wsi->u.http2.stream_wsi->http2_substream = 1;
|
||||
}
|
||||
|
||||
/* END_STREAM means after servicing this, close the stream */
|
||||
wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
|
||||
lwsl_info("%s: headers END_STREAM = %d\n",__func__, wsi->u.http2.END_STREAM);
|
||||
update_end_headers:
|
||||
/* no END_HEADERS means CONTINUATION must come */
|
||||
wsi->u.http2.END_HEADERS = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_HEADERS);
|
||||
|
||||
swsi = wsi->u.http2.stream_wsi;
|
||||
if (!swsi)
|
||||
return 1;
|
||||
|
||||
|
||||
/* prepare the hpack parser at the right start */
|
||||
|
||||
swsi->u.http2.flags = wsi->u.http2.flags;
|
||||
swsi->u.http2.length = wsi->u.http2.length;
|
||||
swsi->u.http2.END_STREAM = wsi->u.http2.END_STREAM;
|
||||
|
||||
if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PADDED)
|
||||
swsi->u.http2.hpack = HPKS_OPT_PADDING;
|
||||
else
|
||||
if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
|
||||
swsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
|
||||
swsi->u.http2.hpack_m = 4;
|
||||
} else
|
||||
swsi->u.http2.hpack = HPKS_TYPE;
|
||||
lwsl_info("initial hpack state %d\n", swsi->u.http2.hpack);
|
||||
break;
|
||||
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
|
||||
if (wsi->u.http2.length != 4)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
if (wsi->u.http2.length == 0)
|
||||
wsi->u.http2.frame_state = 0;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
|
||||
struct lws *swsi;
|
||||
int n, m = 0;
|
||||
|
||||
lwsl_debug("%s: %p: %d\n", __func__, wsi, wsi->pps);
|
||||
|
||||
switch (wsi->pps) {
|
||||
case LWS_PPS_HTTP2_MY_SETTINGS:
|
||||
for (n = 1; n < LWS_HTTP2_SETTINGS__COUNT; n++)
|
||||
if (wsi->u.http2.my_settings.setting[n] != lws_http2_default_settings.setting[n]) {
|
||||
lws_http2_settings_write(wsi, n,
|
||||
&settings[LWS_PRE + m]);
|
||||
m += sizeof(wsi->u.http2.one_setting);
|
||||
}
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
|
||||
0, LWS_HTTP2_STREAM_ID_MASTER, m,
|
||||
&settings[LWS_PRE]);
|
||||
if (n != m) {
|
||||
lwsl_info("send %d %d\n", n, m);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case LWS_PPS_HTTP2_ACK_SETTINGS:
|
||||
/* send ack ... always empty */
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
|
||||
1, LWS_HTTP2_STREAM_ID_MASTER, 0,
|
||||
&settings[LWS_PRE]);
|
||||
if (n) {
|
||||
lwsl_err("ack tells %d\n", n);
|
||||
return 1;
|
||||
}
|
||||
/* this is the end of the preface dance then? */
|
||||
if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) {
|
||||
wsi->state = LWSS_HTTP2_ESTABLISHED;
|
||||
|
||||
wsi->u.http.fop_fd = NULL;
|
||||
|
||||
if (lws_is_ssl(lws_http2_get_network_wsi(wsi))) {
|
||||
lwsl_info("skipping nonexistent ssl upgrade headers\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* we need to treat the headers from this upgrade
|
||||
* as the first job. These need to get
|
||||
* shifted to stream ID 1
|
||||
*/
|
||||
lwsl_info("%s: setting up sid 1\n", __func__);
|
||||
|
||||
swsi = wsi->u.http2.stream_wsi =
|
||||
lws_create_server_child_wsi(wsi->vhost, wsi, 1);
|
||||
/* pass on the initial headers to SID 1 */
|
||||
swsi->u.http.ah = wsi->u.http.ah;
|
||||
wsi->u.http.ah = NULL;
|
||||
|
||||
lwsl_info("%s: inherited headers %p\n", __func__, swsi->u.http.ah);
|
||||
swsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
|
||||
lwsl_info("initial tx credit on conn %p: %d\n", swsi, swsi->u.http2.tx_credit);
|
||||
swsi->u.http2.initialized = 1;
|
||||
/* demanded by HTTP2 */
|
||||
swsi->u.http2.END_STREAM = 1;
|
||||
lwsl_info("servicing initial http request\n");
|
||||
return lws_http_action(swsi);
|
||||
}
|
||||
break;
|
||||
case LWS_PPS_HTTP2_PONG:
|
||||
memcpy(&settings[LWS_PRE], wsi->u.http2.ping_payload, 8);
|
||||
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_PING,
|
||||
LWS_HTTP2_FLAG_SETTINGS_ACK,
|
||||
LWS_HTTP2_STREAM_ID_MASTER, 8,
|
||||
&settings[LWS_PRE]);
|
||||
if (n != 8) {
|
||||
lwsl_info("send %d %d\n", n, m);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
|
||||
{
|
||||
do {
|
||||
wsi = wsi->u.http2.next_child_wsi;
|
||||
if (!wsi)
|
||||
return NULL;
|
||||
} while (n--);
|
||||
|
||||
return wsi;
|
||||
}
|
232
lib/lejp.h
232
lib/lejp.h
|
@ -1,232 +0,0 @@
|
|||
#include "libwebsockets.h"
|
||||
struct lejp_ctx;
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
|
||||
#endif
|
||||
#define LEJP_FLAG_WS_KEEP 64
|
||||
#define LEJP_FLAG_WS_COMMENTLINE 32
|
||||
|
||||
enum lejp_states {
|
||||
LEJP_IDLE = 0,
|
||||
LEJP_MEMBERS = 1,
|
||||
LEJP_M_P = 2,
|
||||
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
|
||||
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
|
||||
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
|
||||
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
|
||||
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
|
||||
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
|
||||
LEJP_MP_DELIM = 9,
|
||||
LEJP_MP_VALUE = 10,
|
||||
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
|
||||
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
|
||||
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
|
||||
LEJP_MP_COMMA_OR_END = 14,
|
||||
LEJP_MP_ARRAY_END = 15,
|
||||
};
|
||||
|
||||
enum lejp_reasons {
|
||||
LEJP_CONTINUE = -1,
|
||||
LEJP_REJECT_IDLE_NO_BRACE = -2,
|
||||
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
|
||||
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
|
||||
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
|
||||
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
|
||||
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
|
||||
LEJP_REJECT_ILLEGAL_HEX = -8,
|
||||
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
|
||||
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
|
||||
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
|
||||
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
|
||||
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
|
||||
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
|
||||
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
|
||||
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
|
||||
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
|
||||
LEJP_REJECT_STACK_OVERFLOW = -18,
|
||||
LEJP_REJECT_MP_DELIM_ISTACK = -19,
|
||||
LEJP_REJECT_NUM_TOO_LONG = -20,
|
||||
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
|
||||
LEJP_REJECT_UNKNOWN = -22,
|
||||
LEJP_REJECT_CALLBACK = -23
|
||||
};
|
||||
|
||||
#define LEJP_FLAG_CB_IS_VALUE 64
|
||||
|
||||
enum lejp_callbacks {
|
||||
LEJPCB_CONSTRUCTED = 0,
|
||||
LEJPCB_DESTRUCTED = 1,
|
||||
|
||||
LEJPCB_START = 2,
|
||||
LEJPCB_COMPLETE = 3,
|
||||
LEJPCB_FAILED = 4,
|
||||
|
||||
LEJPCB_PAIR_NAME = 5,
|
||||
|
||||
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
|
||||
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
|
||||
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
|
||||
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
|
||||
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
|
||||
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
|
||||
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
|
||||
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
|
||||
|
||||
LEJPCB_ARRAY_START = 14,
|
||||
LEJPCB_ARRAY_END = 15,
|
||||
|
||||
LEJPCB_OBJECT_START = 16,
|
||||
LEJPCB_OBJECT_END = 17
|
||||
};
|
||||
|
||||
/**
|
||||
* _lejp_callback() - User parser actions
|
||||
* \param ctx: LEJP context
|
||||
* \param reason: Callback reason
|
||||
*
|
||||
* Your user callback is associated with the context at construction time,
|
||||
* and receives calls as the parsing progresses.
|
||||
*
|
||||
* All of the callbacks may be ignored and just return 0.
|
||||
*
|
||||
* The reasons it might get called, found in @reason, are:
|
||||
*
|
||||
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
|
||||
* perform one-time allocation for the life of the context.
|
||||
*
|
||||
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
|
||||
* allocations at construction-time, you can free them now
|
||||
*
|
||||
* LEJPCB_START: Parsing is beginning at the first byte of input
|
||||
*
|
||||
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
|
||||
* positive return code from lejp_parse indicating the
|
||||
* amount of unused bytes left in the input buffer
|
||||
*
|
||||
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
|
||||
* returned from lejp_parse
|
||||
*
|
||||
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
|
||||
* this callback occurs. You can find the new name at
|
||||
* the end of ctx->path[]
|
||||
*
|
||||
* LEJPCB_VAL_TRUE: The "true" value appeared
|
||||
*
|
||||
* LEJPCB_VAL_FALSE: The "false" value appeared
|
||||
*
|
||||
* LEJPCB_VAL_NULL: The "null" value appeared
|
||||
*
|
||||
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
|
||||
*
|
||||
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
|
||||
*
|
||||
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
|
||||
*
|
||||
* LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
|
||||
* ctx->buf, which is as much as we can buffer, so we are
|
||||
* spilling it. If all your strings are less than
|
||||
* LEJP_STRING_CHUNK - 1 bytes, you will never see this
|
||||
* callback.
|
||||
*
|
||||
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
|
||||
* string is in ctx->buf.
|
||||
*
|
||||
* LEJPCB_ARRAY_START: An array started
|
||||
*
|
||||
* LEJPCB_ARRAY_END: An array ended
|
||||
*
|
||||
* LEJPCB_OBJECT_START: An object started
|
||||
*
|
||||
* LEJPCB_OBJECT_END: An object ended
|
||||
*/
|
||||
LWS_EXTERN char _lejp_callback(struct lejp_ctx *ctx, char reason);
|
||||
|
||||
typedef char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
|
||||
|
||||
#ifndef LEJP_MAX_DEPTH
|
||||
#define LEJP_MAX_DEPTH 12
|
||||
#endif
|
||||
#ifndef LEJP_MAX_INDEX_DEPTH
|
||||
#define LEJP_MAX_INDEX_DEPTH 5
|
||||
#endif
|
||||
#ifndef LEJP_MAX_PATH
|
||||
#define LEJP_MAX_PATH 128
|
||||
#endif
|
||||
#ifndef LEJP_STRING_CHUNK
|
||||
/* must be >= 30 to assemble floats */
|
||||
#define LEJP_STRING_CHUNK 255
|
||||
#endif
|
||||
|
||||
enum num_flags {
|
||||
LEJP_SEEN_MINUS = (1 << 0),
|
||||
LEJP_SEEN_POINT = (1 << 1),
|
||||
LEJP_SEEN_POST_POINT = (1 << 2),
|
||||
LEJP_SEEN_EXP = (1 << 3)
|
||||
};
|
||||
|
||||
struct _lejp_stack {
|
||||
char s; /* lejp_state stack*/
|
||||
char p; /* path length */
|
||||
char i; /* index array length */
|
||||
char b; /* user bitfield */
|
||||
};
|
||||
|
||||
struct lejp_ctx {
|
||||
|
||||
/* sorted by type for most compact alignment
|
||||
*
|
||||
* pointers
|
||||
*/
|
||||
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason);
|
||||
void *user;
|
||||
const char * const *paths;
|
||||
|
||||
/* arrays */
|
||||
|
||||
struct _lejp_stack st[LEJP_MAX_DEPTH];
|
||||
unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
||||
unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
|
||||
char path[LEJP_MAX_PATH];
|
||||
char buf[LEJP_STRING_CHUNK];
|
||||
|
||||
/* int */
|
||||
|
||||
unsigned int line;
|
||||
|
||||
/* short */
|
||||
|
||||
unsigned short uni;
|
||||
|
||||
/* char */
|
||||
|
||||
unsigned char npos;
|
||||
unsigned char dcount;
|
||||
unsigned char f;
|
||||
unsigned char sp; /* stack head */
|
||||
unsigned char ipos; /* index stack depth */
|
||||
unsigned char ppos;
|
||||
unsigned char count_paths;
|
||||
unsigned char path_match;
|
||||
unsigned char path_match_len;
|
||||
unsigned char wildcount;
|
||||
};
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lejp_construct(struct lejp_ctx *ctx,
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
|
||||
const char * const *paths, unsigned char paths_count);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lejp_destruct(struct lejp_ctx *ctx);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lejp_change_callback(struct lejp_ctx *ctx,
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason));
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);
|
796
lib/lextable.h
796
lib/lextable.h
|
@ -1,796 +0,0 @@
|
|||
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
|
||||
0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
|
||||
0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
|
||||
0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
|
||||
0x63 /* 'c' */, 0x66, 0x00 /* (to 0x0072 state 23) */,
|
||||
0x75 /* 'u' */, 0x87, 0x00 /* (to 0x0096 state 34) */,
|
||||
0x73 /* 's' */, 0x9D, 0x00 /* (to 0x00AF state 48) */,
|
||||
0x0D /* '.' */, 0xD6, 0x00 /* (to 0x00EB state 68) */,
|
||||
0x61 /* 'a' */, 0x2E, 0x01 /* (to 0x0146 state 129) */,
|
||||
0x69 /* 'i' */, 0x6D, 0x01 /* (to 0x0188 state 163) */,
|
||||
0x64 /* 'd' */, 0x16, 0x02 /* (to 0x0234 state 265) */,
|
||||
0x72 /* 'r' */, 0x1F, 0x02 /* (to 0x0240 state 270) */,
|
||||
0x3A /* ':' */, 0x50, 0x02 /* (to 0x0274 state 299) */,
|
||||
0x65 /* 'e' */, 0xDC, 0x02 /* (to 0x0303 state 409) */,
|
||||
0x66 /* 'f' */, 0xF8, 0x02 /* (to 0x0322 state 425) */,
|
||||
0x6C /* 'l' */, 0x1A, 0x03 /* (to 0x0347 state 458) */,
|
||||
0x6D /* 'm' */, 0x3D, 0x03 /* (to 0x036D state 484) */,
|
||||
0x74 /* 't' */, 0xAC, 0x03 /* (to 0x03DF state 578) */,
|
||||
0x76 /* 'v' */, 0xC7, 0x03 /* (to 0x03FD state 606) */,
|
||||
0x77 /* 'w' */, 0xD4, 0x03 /* (to 0x040D state 614) */,
|
||||
0x78 /* 'x' */, 0xFB, 0x03 /* (to 0x0437 state 650) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0041: 2 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
|
||||
/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
|
||||
/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
|
||||
0x72 /* 'r' */, 0x92, 0x01 /* (to 0x01DA state 211) */,
|
||||
0x61 /* 'a' */, 0xD4, 0x03 /* (to 0x041F state 631) */,
|
||||
0x75 /* 'u' */, 0xD6, 0x03 /* (to 0x0424 state 635) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0052: 6 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0053: 7 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
|
||||
/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
|
||||
/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
|
||||
0x72 /* 'r' */, 0x4E, 0x00 /* (to 0x00A8 state 42) */,
|
||||
0x08, /* fail */
|
||||
/* pos 005e: 11 */ 0xF4 /* 't' -> */,
|
||||
/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0060: 13 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0061: 14 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0062: 15 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
|
||||
/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
|
||||
/* pos 0066: 18 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 19) */,
|
||||
0x74 /* 't' */, 0xBC, 0x00 /* (to 0x0125 state 110) */,
|
||||
0x08, /* fail */
|
||||
/* pos 006d: 19 */ 0xF3 /* 's' -> */,
|
||||
/* pos 006e: 20 */ 0xF4 /* 't' -> */,
|
||||
/* pos 006f: 21 */ 0xBA /* ':' -> */,
|
||||
/* pos 0070: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
|
||||
/* pos 0072: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0079 state 24) */,
|
||||
0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01E7 state 217) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0079: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0080 state 25) */,
|
||||
0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0203 state 243) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0080: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0087 state 26) */,
|
||||
0x74 /* 't' */, 0x86, 0x01 /* (to 0x0209 state 248) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0087: 26 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0088: 27 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0089: 28 */ 0xF4 /* 't' -> */,
|
||||
/* pos 008a: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0091 state 30) */,
|
||||
0x20 /* ' ' */, 0xCC, 0x03 /* (to 0x0459 state 675) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0091: 30 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0092: 31 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0093: 32 */ 0xBA /* ':' -> */,
|
||||
/* pos 0094: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
|
||||
/* pos 0096: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A0 state 35) */,
|
||||
0x73 /* 's' */, 0x59, 0x03 /* (to 0x03F2 state 596) */,
|
||||
0x72 /* 'r' */, 0x91, 0x03 /* (to 0x042D state 642) */,
|
||||
0x08, /* fail */
|
||||
/* pos 00a0: 35 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 00a1: 36 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 00a2: 37 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 00a3: 38 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 00a4: 39 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 00a5: 40 */ 0xBA /* ':' -> */,
|
||||
/* pos 00a6: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
|
||||
/* pos 00a8: 42 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 00a9: 43 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 00aa: 44 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 00ab: 45 */ 0xEE /* 'n' -> */,
|
||||
/* pos 00ac: 46 */ 0xBA /* ':' -> */,
|
||||
/* pos 00ad: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
|
||||
/* pos 00af: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B6 state 49) */,
|
||||
0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C5 state 553) */,
|
||||
0x08, /* fail */
|
||||
/* pos 00b6: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C0 state 50) */,
|
||||
0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B5 state 539) */,
|
||||
0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BB state 544) */,
|
||||
0x08, /* fail */
|
||||
/* pos 00c0: 50 */ 0xAD /* '-' -> */,
|
||||
/* pos 00c1: 51 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 00c2: 52 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 00c3: 53 */ 0xE2 /* 'b' -> */,
|
||||
/* pos 00c4: 54 */ 0xF3 /* 's' -> */,
|
||||
/* pos 00c5: 55 */ 0xEF /* 'o' -> */,
|
||||
/* pos 00c6: 56 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 00c7: 57 */ 0xEB /* 'k' -> */,
|
||||
/* pos 00c8: 58 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 00c9: 59 */ 0xF4 /* 't' -> */,
|
||||
/* pos 00ca: 60 */ 0xAD /* '-' -> */,
|
||||
/* pos 00cb: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E4 state 62) */,
|
||||
0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00EE state 70) */,
|
||||
0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FA state 81) */,
|
||||
0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010C state 88) */,
|
||||
0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0116 state 97) */,
|
||||
0x6E /* 'n' */, 0x44, 0x00 /* (to 0x011E state 104) */,
|
||||
0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0263 state 284) */,
|
||||
0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026C state 292) */,
|
||||
0x08, /* fail */
|
||||
/* pos 00e4: 62 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 00e5: 63 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 00e6: 64 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 00e7: 65 */ 0xF4 /* 't' -> */,
|
||||
/* pos 00e8: 66 */ 0xBA /* ':' -> */,
|
||||
/* pos 00e9: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
|
||||
/* pos 00eb: 68 */ 0x8A /* '.' -> */,
|
||||
/* pos 00ec: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
|
||||
/* pos 00ee: 70 */ 0xF8 /* 'x' -> */,
|
||||
/* pos 00ef: 71 */ 0xF4 /* 't' -> */,
|
||||
/* pos 00f0: 72 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 00f1: 73 */ 0xEE /* 'n' -> */,
|
||||
/* pos 00f2: 74 */ 0xF3 /* 's' -> */,
|
||||
/* pos 00f3: 75 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 00f4: 76 */ 0xEF /* 'o' -> */,
|
||||
/* pos 00f5: 77 */ 0xEE /* 'n' -> */,
|
||||
/* pos 00f6: 78 */ 0xF3 /* 's' -> */,
|
||||
/* pos 00f7: 79 */ 0xBA /* ':' -> */,
|
||||
/* pos 00f8: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
|
||||
/* pos 00fa: 81 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 00fb: 82 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 00fc: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0106 state 84) */,
|
||||
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0109 state 86) */,
|
||||
0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0261 state 283) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0106: 84 */ 0xBA /* ':' -> */,
|
||||
/* pos 0107: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
|
||||
/* pos 0109: 86 */ 0xBA /* ':' -> */,
|
||||
/* pos 010a: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
|
||||
/* pos 010c: 88 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 010d: 89 */ 0xEF /* 'o' -> */,
|
||||
/* pos 010e: 90 */ 0xF4 /* 't' -> */,
|
||||
/* pos 010f: 91 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0110: 92 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0111: 93 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0112: 94 */ 0xEC /* 'l' -> */,
|
||||
/* pos 0113: 95 */ 0xBA /* ':' -> */,
|
||||
/* pos 0114: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
|
||||
/* pos 0116: 97 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0117: 98 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0118: 99 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0119: 100 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 011a: 101 */ 0xF4 /* 't' -> */,
|
||||
/* pos 011b: 102 */ 0xBA /* ':' -> */,
|
||||
/* pos 011c: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
|
||||
/* pos 011e: 104 */ 0xEF /* 'o' -> */,
|
||||
/* pos 011f: 105 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0120: 106 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0121: 107 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0122: 108 */ 0xBA /* ':' -> */,
|
||||
/* pos 0123: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
|
||||
/* pos 0125: 110 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0126: 111 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 0127: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x012E state 113) */,
|
||||
0x32 /* '2' */, 0x10, 0x00 /* (to 0x013A state 118) */,
|
||||
0x08, /* fail */
|
||||
/* pos 012e: 113 */ 0xB1 /* '1' -> */,
|
||||
/* pos 012f: 114 */ 0xAE /* '.' -> */,
|
||||
/* pos 0130: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x0137 state 116) */,
|
||||
0x30 /* '0' */, 0x15, 0x03 /* (to 0x0448 state 660) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0137: 116 */ 0xA0 /* ' ' -> */,
|
||||
/* pos 0138: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
|
||||
/* pos 013a: 118 */ 0xAD /* '-' -> */,
|
||||
/* pos 013b: 119 */ 0xF3 /* 's' -> */,
|
||||
/* pos 013c: 120 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 013d: 121 */ 0xF4 /* 't' -> */,
|
||||
/* pos 013e: 122 */ 0xF4 /* 't' -> */,
|
||||
/* pos 013f: 123 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0140: 124 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0141: 125 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 0142: 126 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0143: 127 */ 0xBA /* ':' -> */,
|
||||
/* pos 0144: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
|
||||
/* pos 0146: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0153 state 130) */,
|
||||
0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F5 state 230) */,
|
||||
0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02C9 state 358) */,
|
||||
0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02CD state 361) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0153: 130 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0154: 131 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0155: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015C state 133) */,
|
||||
0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0166 state 136) */,
|
||||
0x08, /* fail */
|
||||
/* pos 015c: 133 */ 0xF4 /* 't' -> */,
|
||||
/* pos 015d: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0164 state 135) */,
|
||||
0x2D /* '-' */, 0x59, 0x00 /* (to 0x01B9 state 192) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0164: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
|
||||
/* pos 0166: 136 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0167: 137 */ 0xAD /* '-' -> */,
|
||||
/* pos 0168: 138 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0169: 139 */ 0xEF /* 'o' -> */,
|
||||
/* pos 016a: 140 */ 0xEE /* 'n' -> */,
|
||||
/* pos 016b: 141 */ 0xF4 /* 't' -> */,
|
||||
/* pos 016c: 142 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 016d: 143 */ 0xEF /* 'o' -> */,
|
||||
/* pos 016e: 144 */ 0xEC /* 'l' -> */,
|
||||
/* pos 016f: 145 */ 0xAD /* '-' -> */,
|
||||
/* pos 0170: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0177 state 147) */,
|
||||
0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BB state 345) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0177: 147 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0178: 148 */ 0xF1 /* 'q' -> */,
|
||||
/* pos 0179: 149 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 017a: 150 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 017b: 151 */ 0xF3 /* 's' -> */,
|
||||
/* pos 017c: 152 */ 0xF4 /* 't' -> */,
|
||||
/* pos 017d: 153 */ 0xAD /* '-' -> */,
|
||||
/* pos 017e: 154 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 017f: 155 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0180: 156 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0181: 157 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0182: 158 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0183: 159 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0184: 160 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0185: 161 */ 0xBA /* ':' -> */,
|
||||
/* pos 0186: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
|
||||
/* pos 0188: 163 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 0189: 164 */ 0xAD /* '-' -> */,
|
||||
/* pos 018a: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x0197 state 166) */,
|
||||
0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01AD state 181) */,
|
||||
0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x032E state 435) */,
|
||||
0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0335 state 441) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0197: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x019E state 167) */,
|
||||
0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x0328 state 430) */,
|
||||
0x08, /* fail */
|
||||
/* pos 019e: 167 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 019f: 168 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01a0: 169 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 01a1: 170 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01a2: 171 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 01a3: 172 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 01a4: 173 */ 0xAD /* '-' -> */,
|
||||
/* pos 01a5: 174 */ 0xF3 /* 's' -> */,
|
||||
/* pos 01a6: 175 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01a7: 176 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01a8: 177 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 01a9: 178 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 01aa: 179 */ 0xBA /* ':' -> */,
|
||||
/* pos 01ab: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
|
||||
/* pos 01ad: 181 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01ae: 182 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01af: 183 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 01b0: 184 */ 0xAD /* '-' -> */,
|
||||
/* pos 01b1: 185 */ 0xED /* 'm' -> */,
|
||||
/* pos 01b2: 186 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 01b3: 187 */ 0xF4 /* 't' -> */,
|
||||
/* pos 01b4: 188 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 01b5: 189 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 01b6: 190 */ 0xBA /* ':' -> */,
|
||||
/* pos 01b7: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
|
||||
/* pos 01b9: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C6 state 193) */,
|
||||
0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D0 state 202) */,
|
||||
0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AA state 330) */,
|
||||
0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B3 state 338) */,
|
||||
0x08, /* fail */
|
||||
/* pos 01c6: 193 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01c7: 194 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 01c8: 195 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01c9: 196 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 01ca: 197 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01cb: 198 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01cc: 199 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 01cd: 200 */ 0xBA /* ':' -> */,
|
||||
/* pos 01ce: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
|
||||
/* pos 01d0: 202 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 01d1: 203 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01d2: 204 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 01d3: 205 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 01d4: 206 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 01d5: 207 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 01d6: 208 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 01d7: 209 */ 0xBA /* ':' -> */,
|
||||
/* pos 01d8: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
|
||||
/* pos 01da: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E1 state 212) */,
|
||||
0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037B state 497) */,
|
||||
0x08, /* fail */
|
||||
/* pos 01e1: 212 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 01e2: 213 */ 0xED /* 'm' -> */,
|
||||
/* pos 01e3: 214 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 01e4: 215 */ 0xBA /* ':' -> */,
|
||||
/* pos 01e5: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
|
||||
/* pos 01e7: 217 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 01e8: 218 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 01e9: 219 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 01ea: 220 */ 0xAD /* '-' -> */,
|
||||
/* pos 01eb: 221 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 01ec: 222 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01ed: 223 */ 0xEE /* 'n' -> */,
|
||||
/* pos 01ee: 224 */ 0xF4 /* 't' -> */,
|
||||
/* pos 01ef: 225 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 01f0: 226 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01f1: 227 */ 0xEC /* 'l' -> */,
|
||||
/* pos 01f2: 228 */ 0xBA /* ':' -> */,
|
||||
/* pos 01f3: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
|
||||
/* pos 01f5: 230 */ 0xF4 /* 't' -> */,
|
||||
/* pos 01f6: 231 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 01f7: 232 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01f8: 233 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 01f9: 234 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01fa: 235 */ 0xFA /* 'z' -> */,
|
||||
/* pos 01fb: 236 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 01fc: 237 */ 0xF4 /* 't' -> */,
|
||||
/* pos 01fd: 238 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 01fe: 239 */ 0xEF /* 'o' -> */,
|
||||
/* pos 01ff: 240 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0200: 241 */ 0xBA /* ':' -> */,
|
||||
/* pos 0201: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
|
||||
/* pos 0203: 243 */ 0xEB /* 'k' -> */,
|
||||
/* pos 0204: 244 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0205: 245 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0206: 246 */ 0xBA /* ':' -> */,
|
||||
/* pos 0207: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
|
||||
/* pos 0209: 248 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 020a: 249 */ 0xEE /* 'n' -> */,
|
||||
/* pos 020b: 250 */ 0xF4 /* 't' -> */,
|
||||
/* pos 020c: 251 */ 0xAD /* '-' -> */,
|
||||
/* pos 020d: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x021D state 253) */,
|
||||
0x74 /* 't' */, 0x1E, 0x00 /* (to 0x022E state 260) */,
|
||||
0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D3 state 366) */,
|
||||
0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E0 state 378) */,
|
||||
0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FC state 403) */,
|
||||
0x08, /* fail */
|
||||
/* pos 021d: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0227 state 254) */,
|
||||
0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02EA state 387) */,
|
||||
0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F3 state 395) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0227: 254 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0228: 255 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 0229: 256 */ 0xF4 /* 't' -> */,
|
||||
/* pos 022a: 257 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 022b: 258 */ 0xBA /* ':' -> */,
|
||||
/* pos 022c: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
|
||||
/* pos 022e: 260 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 022f: 261 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 0230: 262 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0231: 263 */ 0xBA /* ':' -> */,
|
||||
/* pos 0232: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
|
||||
/* pos 0234: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023B state 266) */,
|
||||
0x65 /* 'e' */, 0xF0, 0x01 /* (to 0x0427 state 637) */,
|
||||
0x08, /* fail */
|
||||
/* pos 023b: 266 */ 0xF4 /* 't' -> */,
|
||||
/* pos 023c: 267 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 023d: 268 */ 0xBA /* ':' -> */,
|
||||
/* pos 023e: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
|
||||
/* pos 0240: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0247 state 271) */,
|
||||
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x024D state 276) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0247: 271 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0248: 272 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 0249: 273 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 024a: 274 */ 0xBA /* ':' -> */,
|
||||
/* pos 024b: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
|
||||
/* pos 024d: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0254 state 277) */,
|
||||
0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AA state 529) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0254: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025B state 278) */,
|
||||
0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A4 state 524) */,
|
||||
0x08, /* fail */
|
||||
/* pos 025b: 278 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 025c: 279 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 025d: 280 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 025e: 281 */ 0xBA /* ':' -> */,
|
||||
/* pos 025f: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
|
||||
/* pos 0261: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
|
||||
/* pos 0263: 284 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0264: 285 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0265: 286 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0266: 287 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0267: 288 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0268: 289 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0269: 290 */ 0xBA /* ':' -> */,
|
||||
/* pos 026a: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
|
||||
/* pos 026c: 292 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 026d: 293 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 026e: 294 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 026f: 295 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0270: 296 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0271: 297 */ 0xBA /* ':' -> */,
|
||||
/* pos 0272: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
|
||||
/* pos 0274: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0281 state 300) */,
|
||||
0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028B state 309) */,
|
||||
0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0292 state 315) */,
|
||||
0x73 /* 's' */, 0x1A, 0x00 /* (to 0x0297 state 319) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0281: 300 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 0282: 301 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0283: 302 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 0284: 303 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0285: 304 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0286: 305 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0287: 306 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0288: 307 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 0289: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
|
||||
/* pos 028b: 309 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 028c: 310 */ 0xF4 /* 't' -> */,
|
||||
/* pos 028d: 311 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 028e: 312 */ 0xEF /* 'o' -> */,
|
||||
/* pos 028f: 313 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0290: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
|
||||
/* pos 0292: 315 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0293: 316 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0294: 317 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 0295: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
|
||||
/* pos 0297: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x029E state 320) */,
|
||||
0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A4 state 325) */,
|
||||
0x08, /* fail */
|
||||
/* pos 029e: 320 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 029f: 321 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02a0: 322 */ 0xED /* 'm' -> */,
|
||||
/* pos 02a1: 323 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02a2: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
|
||||
/* pos 02a4: 325 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02a5: 326 */ 0xF4 /* 't' -> */,
|
||||
/* pos 02a6: 327 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 02a7: 328 */ 0xF3 /* 's' -> */,
|
||||
/* pos 02a8: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
|
||||
/* pos 02aa: 330 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 02ab: 331 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02ac: 332 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 02ad: 333 */ 0xF3 /* 's' -> */,
|
||||
/* pos 02ae: 334 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02af: 335 */ 0xF4 /* 't' -> */,
|
||||
/* pos 02b0: 336 */ 0xBA /* ':' -> */,
|
||||
/* pos 02b1: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
|
||||
/* pos 02b3: 338 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02b4: 339 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02b5: 340 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02b6: 341 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02b7: 342 */ 0xF3 /* 's' -> */,
|
||||
/* pos 02b8: 343 */ 0xBA /* ':' -> */,
|
||||
/* pos 02b9: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
|
||||
/* pos 02bb: 345 */ 0xEC /* 'l' -> */,
|
||||
/* pos 02bc: 346 */ 0xEC /* 'l' -> */,
|
||||
/* pos 02bd: 347 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02be: 348 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 02bf: 349 */ 0xAD /* '-' -> */,
|
||||
/* pos 02c0: 350 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02c1: 351 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 02c2: 352 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02c3: 353 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02c4: 354 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02c5: 355 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02c6: 356 */ 0xBA /* ':' -> */,
|
||||
/* pos 02c7: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
|
||||
/* pos 02c9: 358 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02ca: 359 */ 0xBA /* ':' -> */,
|
||||
/* pos 02cb: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
|
||||
/* pos 02cd: 361 */ 0xEC /* 'l' -> */,
|
||||
/* pos 02ce: 362 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02cf: 363 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 02d0: 364 */ 0xBA /* ':' -> */,
|
||||
/* pos 02d1: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
|
||||
/* pos 02d3: 366 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02d4: 367 */ 0xF3 /* 's' -> */,
|
||||
/* pos 02d5: 368 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 02d6: 369 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02d7: 370 */ 0xF3 /* 's' -> */,
|
||||
/* pos 02d8: 371 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02d9: 372 */ 0xF4 /* 't' -> */,
|
||||
/* pos 02da: 373 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02db: 374 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02dc: 375 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02dd: 376 */ 0xBA /* ':' -> */,
|
||||
/* pos 02de: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
|
||||
/* pos 02e0: 378 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02e1: 379 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 02e2: 380 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02e3: 381 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 02e4: 382 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02e5: 383 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02e6: 384 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02e7: 385 */ 0xBA /* ':' -> */,
|
||||
/* pos 02e8: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
|
||||
/* pos 02ea: 387 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02eb: 388 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02ec: 389 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 02ed: 390 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02ee: 391 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02ef: 392 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 02f0: 393 */ 0xBA /* ':' -> */,
|
||||
/* pos 02f1: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
|
||||
/* pos 02f3: 395 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 02f4: 396 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02f5: 397 */ 0xF4 /* 't' -> */,
|
||||
/* pos 02f6: 398 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 02f7: 399 */ 0xEF /* 'o' -> */,
|
||||
/* pos 02f8: 400 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02f9: 401 */ 0xBA /* ':' -> */,
|
||||
/* pos 02fa: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
|
||||
/* pos 02fc: 403 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 02fd: 404 */ 0xEE /* 'n' -> */,
|
||||
/* pos 02fe: 405 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 02ff: 406 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0300: 407 */ 0xBA /* ':' -> */,
|
||||
/* pos 0301: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
|
||||
/* pos 0303: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030A state 410) */,
|
||||
0x78 /* 'x' */, 0x09, 0x00 /* (to 0x030F state 414) */,
|
||||
0x08, /* fail */
|
||||
/* pos 030a: 410 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 030b: 411 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 030c: 412 */ 0xBA /* ':' -> */,
|
||||
/* pos 030d: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
|
||||
/* pos 030f: 414 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 0310: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0317 state 416) */,
|
||||
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031C state 420) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0317: 416 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0318: 417 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0319: 418 */ 0xBA /* ':' -> */,
|
||||
/* pos 031a: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
|
||||
/* pos 031c: 420 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 031d: 421 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 031e: 422 */ 0xF3 /* 's' -> */,
|
||||
/* pos 031f: 423 */ 0xBA /* ':' -> */,
|
||||
/* pos 0320: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
|
||||
/* pos 0322: 425 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0323: 426 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0324: 427 */ 0xED /* 'm' -> */,
|
||||
/* pos 0325: 428 */ 0xBA /* ':' -> */,
|
||||
/* pos 0326: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
|
||||
/* pos 0328: 430 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0329: 431 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 032a: 432 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 032b: 433 */ 0xBA /* ':' -> */,
|
||||
/* pos 032c: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
|
||||
/* pos 032e: 435 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 032f: 436 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0330: 437 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 0331: 438 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0332: 439 */ 0xBA /* ':' -> */,
|
||||
/* pos 0333: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
|
||||
/* pos 0335: 441 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0336: 442 */ 0xED /* 'm' -> */,
|
||||
/* pos 0337: 443 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0338: 444 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0339: 445 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 033a: 446 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 033b: 447 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 033c: 448 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 033d: 449 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 033e: 450 */ 0xAD /* '-' -> */,
|
||||
/* pos 033f: 451 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0340: 452 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0341: 453 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0342: 454 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0343: 455 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0344: 456 */ 0xBA /* ':' -> */,
|
||||
/* pos 0345: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
|
||||
/* pos 0347: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0351 state 459) */,
|
||||
0x69 /* 'i' */, 0x15, 0x00 /* (to 0x035F state 472) */,
|
||||
0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0364 state 476) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0351: 459 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0352: 460 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0353: 461 */ 0xAD /* '-' -> */,
|
||||
/* pos 0354: 462 */ 0xED /* 'm' -> */,
|
||||
/* pos 0355: 463 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0356: 464 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0357: 465 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0358: 466 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 0359: 467 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 035a: 468 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 035b: 469 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 035c: 470 */ 0xBA /* ':' -> */,
|
||||
/* pos 035d: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
|
||||
/* pos 035f: 472 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0360: 473 */ 0xEB /* 'k' -> */,
|
||||
/* pos 0361: 474 */ 0xBA /* ':' -> */,
|
||||
/* pos 0362: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
|
||||
/* pos 0364: 476 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0365: 477 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0366: 478 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0367: 479 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0368: 480 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0369: 481 */ 0xEE /* 'n' -> */,
|
||||
/* pos 036a: 482 */ 0xBA /* ':' -> */,
|
||||
/* pos 036b: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
|
||||
/* pos 036d: 484 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 036e: 485 */ 0xF8 /* 'x' -> */,
|
||||
/* pos 036f: 486 */ 0xAD /* '-' -> */,
|
||||
/* pos 0370: 487 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 0371: 488 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0372: 489 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0373: 490 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 0374: 491 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0375: 492 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0376: 493 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0377: 494 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0378: 495 */ 0xBA /* ':' -> */,
|
||||
/* pos 0379: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
|
||||
/* pos 037b: 497 */ 0xF8 /* 'x' -> */,
|
||||
/* pos 037c: 498 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 037d: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0384 state 500) */,
|
||||
0x20 /* ' ' */, 0xB5, 0x00 /* (to 0x0435 state 649) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0384: 500 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0385: 501 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 0386: 502 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0387: 503 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 0388: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x038F state 505) */,
|
||||
0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x0399 state 514) */,
|
||||
0x08, /* fail */
|
||||
/* pos 038f: 505 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0390: 506 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0391: 507 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0392: 508 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0393: 509 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0394: 510 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0395: 511 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0396: 512 */ 0xBA /* ':' -> */,
|
||||
/* pos 0397: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
|
||||
/* pos 0399: 514 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 039a: 515 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 039b: 516 */ 0xFA /* 'z' -> */,
|
||||
/* pos 039c: 517 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 039d: 518 */ 0xF4 /* 't' -> */,
|
||||
/* pos 039e: 519 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 039f: 520 */ 0xEF /* 'o' -> */,
|
||||
/* pos 03a0: 521 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03a1: 522 */ 0xBA /* ':' -> */,
|
||||
/* pos 03a2: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
|
||||
/* pos 03a4: 524 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03a5: 525 */ 0xF3 /* 's' -> */,
|
||||
/* pos 03a6: 526 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 03a7: 527 */ 0xBA /* ':' -> */,
|
||||
/* pos 03a8: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
|
||||
/* pos 03aa: 529 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03ab: 530 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 03ac: 531 */ 0xAD /* '-' -> */,
|
||||
/* pos 03ad: 532 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 03ae: 533 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 03af: 534 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03b0: 535 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03b1: 536 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03b2: 537 */ 0xBA /* ':' -> */,
|
||||
/* pos 03b3: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
|
||||
/* pos 03b5: 539 */ 0xF6 /* 'v' -> */,
|
||||
/* pos 03b6: 540 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03b7: 541 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03b8: 542 */ 0xBA /* ':' -> */,
|
||||
/* pos 03b9: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
|
||||
/* pos 03bb: 544 */ 0xAD /* '-' -> */,
|
||||
/* pos 03bc: 545 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 03bd: 546 */ 0xEF /* 'o' -> */,
|
||||
/* pos 03be: 547 */ 0xEF /* 'o' -> */,
|
||||
/* pos 03bf: 548 */ 0xEB /* 'k' -> */,
|
||||
/* pos 03c0: 549 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 03c1: 550 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03c2: 551 */ 0xBA /* ':' -> */,
|
||||
/* pos 03c3: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
|
||||
/* pos 03c5: 553 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03c6: 554 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 03c7: 555 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 03c8: 556 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03c9: 557 */ 0xAD /* '-' -> */,
|
||||
/* pos 03ca: 558 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03cb: 559 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03cc: 560 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 03cd: 561 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03ce: 562 */ 0xF3 /* 's' -> */,
|
||||
/* pos 03cf: 563 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 03d0: 564 */ 0xEF /* 'o' -> */,
|
||||
/* pos 03d1: 565 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03d2: 566 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03d3: 567 */ 0xAD /* '-' -> */,
|
||||
/* pos 03d4: 568 */ 0xF3 /* 's' -> */,
|
||||
/* pos 03d5: 569 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03d6: 570 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 03d7: 571 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 03d8: 572 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03d9: 573 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 03da: 574 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03db: 575 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 03dc: 576 */ 0xBA /* ':' -> */,
|
||||
/* pos 03dd: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
|
||||
/* pos 03df: 578 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03e0: 579 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 03e1: 580 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03e2: 581 */ 0xF3 /* 's' -> */,
|
||||
/* pos 03e3: 582 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 03e4: 583 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03e5: 584 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03e6: 585 */ 0xAD /* '-' -> */,
|
||||
/* pos 03e7: 586 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03e8: 587 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03e9: 588 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 03ea: 589 */ 0xEF /* 'o' -> */,
|
||||
/* pos 03eb: 590 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 03ec: 591 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 03ed: 592 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03ee: 593 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 03ef: 594 */ 0xBA /* ':' -> */,
|
||||
/* pos 03f0: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
|
||||
/* pos 03f2: 596 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03f3: 597 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 03f4: 598 */ 0xAD /* '-' -> */,
|
||||
/* pos 03f5: 599 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 03f6: 600 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 03f7: 601 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 03f8: 602 */ 0xEE /* 'n' -> */,
|
||||
/* pos 03f9: 603 */ 0xF4 /* 't' -> */,
|
||||
/* pos 03fa: 604 */ 0xBA /* ':' -> */,
|
||||
/* pos 03fb: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
|
||||
/* pos 03fd: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0404 state 607) */,
|
||||
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0409 state 611) */,
|
||||
0x08, /* fail */
|
||||
/* pos 0404: 607 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0405: 608 */ 0xF9 /* 'y' -> */,
|
||||
/* pos 0406: 609 */ 0xBA /* ':' -> */,
|
||||
/* pos 0407: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
|
||||
/* pos 0409: 611 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 040a: 612 */ 0xBA /* ':' -> */,
|
||||
/* pos 040b: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
|
||||
/* pos 040d: 614 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 040e: 615 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 040f: 616 */ 0xAD /* '-' -> */,
|
||||
/* pos 0410: 617 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0411: 618 */ 0xF5 /* 'u' -> */,
|
||||
/* pos 0412: 619 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0413: 620 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 0414: 621 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0415: 622 */ 0xEE /* 'n' -> */,
|
||||
/* pos 0416: 623 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0417: 624 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0418: 625 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0419: 626 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 041a: 627 */ 0xF4 /* 't' -> */,
|
||||
/* pos 041b: 628 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 041c: 629 */ 0xBA /* ':' -> */,
|
||||
/* pos 041d: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
|
||||
/* pos 041f: 631 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0420: 632 */ 0xE3 /* 'c' -> */,
|
||||
/* pos 0421: 633 */ 0xE8 /* 'h' -> */,
|
||||
/* pos 0422: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
|
||||
/* pos 0424: 635 */ 0xF4 /* 't' -> */,
|
||||
/* pos 0425: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
|
||||
/* pos 0427: 637 */ 0xEC /* 'l' -> */,
|
||||
/* pos 0428: 638 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0429: 639 */ 0xF4 /* 't' -> */,
|
||||
/* pos 042a: 640 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 042b: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
|
||||
/* pos 042d: 642 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 042e: 643 */ 0xAD /* '-' -> */,
|
||||
/* pos 042f: 644 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0430: 645 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0431: 646 */ 0xE7 /* 'g' -> */,
|
||||
/* pos 0432: 647 */ 0xF3 /* 's' -> */,
|
||||
/* pos 0433: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
|
||||
/* pos 0435: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
|
||||
/* pos 0437: 650 */ 0xAD /* '-' -> */,
|
||||
/* pos 0438: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x043F state 652) */,
|
||||
0x66 /* 'f' */, 0x10, 0x00 /* (to 0x044B state 662) */,
|
||||
0x08, /* fail */
|
||||
/* pos 043f: 652 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0440: 653 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 0441: 654 */ 0xEC /* 'l' -> */,
|
||||
/* pos 0442: 655 */ 0xAD /* '-' -> */,
|
||||
/* pos 0443: 656 */ 0xE9 /* 'i' -> */,
|
||||
/* pos 0444: 657 */ 0xF0 /* 'p' -> */,
|
||||
/* pos 0445: 658 */ 0xBA /* ':' -> */,
|
||||
/* pos 0446: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
|
||||
/* pos 0448: 660 */ 0xA0 /* ' ' -> */,
|
||||
/* pos 0449: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
|
||||
/* pos 044b: 662 */ 0xEF /* 'o' -> */,
|
||||
/* pos 044c: 663 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 044d: 664 */ 0xF7 /* 'w' -> */,
|
||||
/* pos 044e: 665 */ 0xE1 /* 'a' -> */,
|
||||
/* pos 044f: 666 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0450: 667 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0451: 668 */ 0xE5 /* 'e' -> */,
|
||||
/* pos 0452: 669 */ 0xE4 /* 'd' -> */,
|
||||
/* pos 0453: 670 */ 0xAD /* '-' -> */,
|
||||
/* pos 0454: 671 */ 0xE6 /* 'f' -> */,
|
||||
/* pos 0455: 672 */ 0xEF /* 'o' -> */,
|
||||
/* pos 0456: 673 */ 0xF2 /* 'r' -> */,
|
||||
/* pos 0457: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
|
||||
/* pos 0459: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
|
||||
/* total size 1115 bytes */
|
233
lib/libev.c
233
lib/libev.c
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
void lws_feature_status_libev(struct lws_context_creation_info *info)
|
||||
{
|
||||
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEV))
|
||||
lwsl_notice("libev support compiled in and enabled\n");
|
||||
else
|
||||
lwsl_notice("libev support compiled in but disabled\n");
|
||||
}
|
||||
|
||||
static void
|
||||
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = lws_container_of(watcher,
|
||||
struct lws_io_watcher, ev_watcher);
|
||||
struct lws_context *context = lws_io->context;
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
if (revents & EV_ERROR)
|
||||
return;
|
||||
|
||||
eventfd.fd = watcher->fd;
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = EV_NONE;
|
||||
if (revents & EV_READ) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & EV_WRITE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
lws_service_fd(context, &eventfd);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
|
||||
{
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint,
|
||||
lws_ev_signal_cb_t *cb)
|
||||
{
|
||||
context->use_ev_sigint = use_ev_sigint;
|
||||
if (cb)
|
||||
context->lws_ev_sigint_cb = cb;
|
||||
else
|
||||
context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
|
||||
{
|
||||
struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher;
|
||||
struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher;
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
const char * backend_name;
|
||||
int status = 0;
|
||||
int backend;
|
||||
|
||||
if (!loop)
|
||||
loop = ev_loop_new(0);
|
||||
else
|
||||
context->pt[tsi].ev_loop_foreign = 1;
|
||||
|
||||
context->pt[tsi].io_loop_ev = loop;
|
||||
|
||||
/*
|
||||
* Initialize the accept w_accept with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*/
|
||||
while (vh) {
|
||||
if (vh->lserv_wsi) {
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
ev_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->desc.sockfd,
|
||||
EV_READ);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
ev_io_start(context->pt[tsi].io_loop_ev, w_accept);
|
||||
|
||||
/* Register the signal watcher unless the user says not to */
|
||||
if (context->use_ev_sigint) {
|
||||
ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT);
|
||||
ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint);
|
||||
}
|
||||
backend = ev_backend(loop);
|
||||
|
||||
switch (backend) {
|
||||
case EVBACKEND_SELECT:
|
||||
backend_name = "select";
|
||||
break;
|
||||
case EVBACKEND_POLL:
|
||||
backend_name = "poll";
|
||||
break;
|
||||
case EVBACKEND_EPOLL:
|
||||
backend_name = "epoll";
|
||||
break;
|
||||
case EVBACKEND_KQUEUE:
|
||||
backend_name = "kqueue";
|
||||
break;
|
||||
case EVBACKEND_DEVPOLL:
|
||||
backend_name = "/dev/poll";
|
||||
break;
|
||||
case EVBACKEND_PORT:
|
||||
backend_name = "Solaris 10 \"port\"";
|
||||
break;
|
||||
default:
|
||||
backend_name = "Unknown libev backend";
|
||||
break;
|
||||
}
|
||||
|
||||
lwsl_notice(" libev backend: %s\n", backend_name);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
lws_libev_destroyloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV))
|
||||
return;
|
||||
|
||||
if (!pt->io_loop_ev)
|
||||
return;
|
||||
|
||||
ev_io_stop(pt->io_loop_ev, &pt->w_accept.ev_watcher);
|
||||
if (context->use_ev_sigint)
|
||||
ev_signal_stop(pt->io_loop_ev,
|
||||
&pt->w_sigint.ev_watcher);
|
||||
if (!pt->ev_loop_foreign)
|
||||
ev_loop_destroy(pt->io_loop_ev);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(new_wsi);
|
||||
struct ev_io *r = &new_wsi->w_read.ev_watcher;
|
||||
struct ev_io *w = &new_wsi->w_write.ev_watcher;
|
||||
int fd;
|
||||
|
||||
if (!LWS_LIBEV_ENABLED(context))
|
||||
return;
|
||||
|
||||
if (new_wsi->mode == LWSCM_RAW_FILEDESC)
|
||||
fd = desc.filefd;
|
||||
else
|
||||
fd = desc.sockfd;
|
||||
|
||||
new_wsi->w_read.context = context;
|
||||
new_wsi->w_write.context = context;
|
||||
ev_io_init(r, lws_accept_cb, fd, EV_READ);
|
||||
ev_io_init(w, lws_accept_cb, fd, EV_WRITE);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libev_io(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!LWS_LIBEV_ENABLED(context))
|
||||
return;
|
||||
|
||||
if (!pt->io_loop_ev)
|
||||
return;
|
||||
|
||||
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
|
||||
|
||||
if (flags & LWS_EV_START) {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher);
|
||||
if (flags & LWS_EV_READ)
|
||||
ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher);
|
||||
if (flags & LWS_EV_READ)
|
||||
ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher);
|
||||
}
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_libev_init_fd_table(struct lws_context *context)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!LWS_LIBEV_ENABLED(context))
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++) {
|
||||
context->pt[n].w_accept.context = context;
|
||||
context->pt[n].w_sigint.context = context;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libev_run(const struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context))
|
||||
ev_run(context->pt[tsi].io_loop_ev, 0);
|
||||
}
|
249
lib/libevent.c
249
lib/libevent.c
|
@ -1,249 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
void lws_feature_status_libevent(struct lws_context_creation_info *info)
|
||||
{
|
||||
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEVENT))
|
||||
lwsl_notice("libevent support compiled in and enabled\n");
|
||||
else
|
||||
lwsl_notice("libevent support compiled in but disabled\n");
|
||||
}
|
||||
|
||||
static void
|
||||
lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = (struct lws_io_watcher *)ctx;
|
||||
struct lws_context *context = lws_io->context;
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
if (revents & EV_TIMEOUT)
|
||||
return;
|
||||
|
||||
/* !!! EV_CLOSED doesn't exist in libevent2 */
|
||||
#if LIBEVENT_VERSION_NUMBER < 0x02000000
|
||||
if (revents & EV_CLOSED)
|
||||
{
|
||||
event_del(lws_io->event_watcher);
|
||||
event_free(lws_io->event_watcher);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
eventfd.fd = sock_fd;
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = 0;
|
||||
if (revents & EV_READ)
|
||||
{
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & EV_WRITE)
|
||||
{
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
lws_service_fd(context, &eventfd);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx)
|
||||
{
|
||||
struct lws_context_per_thread *pt = ctx;
|
||||
if (!pt->ev_loop_foreign)
|
||||
event_base_loopbreak(pt->io_loop_event_base);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint,
|
||||
lws_event_signal_cb_t *cb)
|
||||
{
|
||||
context->use_ev_sigint = use_event_sigint;
|
||||
if (cb)
|
||||
context->lws_event_sigint_cb = cb;
|
||||
else
|
||||
context->lws_event_sigint_cb = &lws_event_sigint_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_event_initloop(struct lws_context *context, struct event_base *loop,
|
||||
int tsi)
|
||||
{
|
||||
if (!loop)
|
||||
{
|
||||
context->pt[tsi].io_loop_event_base = event_base_new();
|
||||
}
|
||||
else
|
||||
{
|
||||
context->pt[tsi].ev_loop_foreign = 1;
|
||||
context->pt[tsi].io_loop_event_base = loop;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize all events with the listening sockets
|
||||
* and register a callback for read operations
|
||||
*/
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
while (vh)
|
||||
{
|
||||
if (vh->lserv_wsi)
|
||||
{
|
||||
vh->lserv_wsi->w_read.context = context;
|
||||
vh->lserv_wsi->w_read.event_watcher = event_new(
|
||||
loop,
|
||||
vh->lserv_wsi->desc.sockfd,
|
||||
(EV_READ | EV_PERSIST),
|
||||
lws_event_cb,
|
||||
&vh->lserv_wsi->w_read);
|
||||
event_add(vh->lserv_wsi->w_read.event_watcher, NULL);
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
/* Register the signal watcher unless the user says not to */
|
||||
if (context->use_ev_sigint)
|
||||
{
|
||||
struct event *w_sigint = evsignal_new(loop, SIGINT,
|
||||
context->lws_event_sigint_cb, &context->pt[tsi]);
|
||||
context->pt[tsi].w_sigint.event_watcher = w_sigint;
|
||||
event_add(w_sigint, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_libevent_destroyloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT))
|
||||
return;
|
||||
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
if (!pt->io_loop_event_base)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Free all events with the listening sockets
|
||||
*/
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
while (vh)
|
||||
{
|
||||
if (vh->lserv_wsi)
|
||||
{
|
||||
event_free(vh->lserv_wsi->w_read.event_watcher);
|
||||
vh->lserv_wsi->w_read.event_watcher = NULL;
|
||||
}
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (context->use_ev_sigint)
|
||||
event_free(pt->w_sigint.event_watcher);
|
||||
if (!pt->ev_loop_foreign)
|
||||
event_base_free(pt->io_loop_event_base);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(new_wsi);
|
||||
if (!LWS_LIBEVENT_ENABLED(context))
|
||||
return;
|
||||
|
||||
new_wsi->w_read.context = context;
|
||||
new_wsi->w_write.context = context;
|
||||
|
||||
// Initialize the event
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)new_wsi->tsi];
|
||||
int fd;
|
||||
if (new_wsi->mode == LWSCM_RAW_FILEDESC)
|
||||
fd = desc.filefd;
|
||||
else
|
||||
fd = desc.sockfd;
|
||||
new_wsi->w_read.event_watcher = event_new(pt->io_loop_event_base, fd,
|
||||
(EV_READ | EV_PERSIST), lws_event_cb, &new_wsi->w_read);
|
||||
new_wsi->w_write.event_watcher = event_new(pt->io_loop_event_base, fd,
|
||||
(EV_WRITE | EV_PERSIST), lws_event_cb, &new_wsi->w_write);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libevent_io(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
|
||||
if (!LWS_LIBEVENT_ENABLED(context))
|
||||
return;
|
||||
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
if (!pt->io_loop_event_base || context->being_destroyed)
|
||||
return;
|
||||
|
||||
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
|
||||
|
||||
if (flags & LWS_EV_START)
|
||||
{
|
||||
if (flags & LWS_EV_WRITE)
|
||||
{
|
||||
event_add(wsi->w_write.event_watcher, NULL);
|
||||
}
|
||||
if (flags & LWS_EV_READ)
|
||||
{
|
||||
event_add(wsi->w_read.event_watcher, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags & LWS_EV_WRITE)
|
||||
{
|
||||
event_del(wsi->w_write.event_watcher);
|
||||
}
|
||||
if (flags & LWS_EV_READ)
|
||||
{
|
||||
event_del(wsi->w_read.event_watcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_libevent_init_fd_table(struct lws_context *context)
|
||||
{
|
||||
if (!LWS_LIBEVENT_ENABLED(context))
|
||||
return 0;
|
||||
|
||||
int n;
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
{
|
||||
context->pt[n].w_sigint.context = context;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libevent_run(const struct lws_context *context, int tsi)
|
||||
{
|
||||
// Run/Dispatch the event_base loop
|
||||
if (context->pt[tsi].io_loop_event_base && LWS_LIBEVENT_ENABLED(context))
|
||||
event_base_dispatch(context->pt[tsi].io_loop_event_base);
|
||||
}
|
723
lib/libuv.c
723
lib/libuv.c
|
@ -1,723 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
void
|
||||
lws_feature_status_libuv(struct lws_context_creation_info *info)
|
||||
{
|
||||
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV))
|
||||
lwsl_notice("libuv support compiled in and enabled\n");
|
||||
else
|
||||
lwsl_notice("libuv support compiled in but disabled\n");
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_idle(uv_idle_t *handle
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(handle,
|
||||
struct lws_context_per_thread, uv_idle);
|
||||
|
||||
// lwsl_debug("%s\n", __func__);
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
*/
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* -1 timeout means just do forced service */
|
||||
_lws_plat_service_tsi(pt->context, -1, pt->tid);
|
||||
/* still somebody left who wants forced service? */
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
|
||||
/* yes... come back again later */
|
||||
// lwsl_debug("%s: done again\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* there is nobody who needs service forcing, shut down idle */
|
||||
uv_idle_stop(handle);
|
||||
|
||||
//lwsl_debug("%s: done stop\n", __func__);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_io_cb(uv_poll_t *watcher, int status, int revents)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = lws_container_of(watcher,
|
||||
struct lws_io_watcher, uv_watcher);
|
||||
struct lws *wsi = lws_container_of(lws_io, struct lws, w_read);
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
eventfd.fd = watcher->socket;
|
||||
#else
|
||||
eventfd.fd = watcher->io_watcher.fd;
|
||||
#endif
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = 0;
|
||||
|
||||
if (status < 0) {
|
||||
/* at this point status will be an UV error, like UV_EBADF,
|
||||
we treat all errors as LWS_POLLHUP */
|
||||
|
||||
/* you might want to return; instead of servicing the fd in some cases */
|
||||
if (status == UV_EAGAIN)
|
||||
return;
|
||||
|
||||
eventfd.events |= LWS_POLLHUP;
|
||||
eventfd.revents |= LWS_POLLHUP;
|
||||
} else {
|
||||
if (revents & UV_READABLE) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & UV_WRITABLE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
}
|
||||
lws_service_fd(context, &eventfd);
|
||||
|
||||
uv_idle_start(&context->pt[(int)wsi->tsi].uv_idle, lws_uv_idle);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_uv_sigint_cb(uv_signal_t *watcher, int signum)
|
||||
{
|
||||
lwsl_err("internal signal handler caught signal %d\n", signum);
|
||||
lws_libuv_stop(watcher->data);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
|
||||
uv_signal_cb cb)
|
||||
{
|
||||
context->use_ev_sigint = use_uv_sigint;
|
||||
if (cb)
|
||||
context->lws_uv_sigint_cb = cb;
|
||||
else
|
||||
context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_timeout_cb(uv_timer_t *timer
|
||||
#if UV_VERSION_MAJOR == 0
|
||||
, int status
|
||||
#endif
|
||||
)
|
||||
{
|
||||
struct lws_context_per_thread *pt = lws_container_of(timer,
|
||||
struct lws_context_per_thread, uv_timeout_watcher);
|
||||
|
||||
if (pt->context->requested_kill)
|
||||
return;
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
lws_service_fd_tsi(pt->context, NULL, pt->tid);
|
||||
}
|
||||
|
||||
static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP };
|
||||
|
||||
int
|
||||
lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n;
|
||||
|
||||
if (!LWS_LIBUV_ENABLED(vh->context))
|
||||
return 0;
|
||||
if (!wsi)
|
||||
wsi = vh->lserv_wsi;
|
||||
if (!wsi)
|
||||
return 0;
|
||||
if (wsi->w_read.context)
|
||||
return 0;
|
||||
|
||||
pt = &vh->context->pt[(int)wsi->tsi];
|
||||
if (!pt->io_loop_uv)
|
||||
return 0;
|
||||
|
||||
wsi->w_read.context = vh->context;
|
||||
n = uv_poll_init_socket(pt->io_loop_uv,
|
||||
&wsi->w_read.uv_watcher, wsi->desc.sockfd);
|
||||
if (n) {
|
||||
lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
|
||||
n, (void *)(lws_intptr_t)wsi->desc.sockfd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This needs to be called after vhosts have been defined.
|
||||
*
|
||||
* If later, after server start, another vhost is added, this must be
|
||||
* called again to bind the vhost
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
struct lws_vhost *vh = context->vhost_list;
|
||||
int status = 0, n, ns, first = 1;
|
||||
|
||||
if (!pt->io_loop_uv) {
|
||||
if (!loop) {
|
||||
loop = lws_malloc(sizeof(*loop));
|
||||
if (!loop) {
|
||||
lwsl_err("OOM\n");
|
||||
return -1;
|
||||
}
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
uv_loop_init(loop);
|
||||
#else
|
||||
lwsl_err("This libuv is too old to work...\n");
|
||||
return 1;
|
||||
#endif
|
||||
pt->ev_loop_foreign = 0;
|
||||
} else {
|
||||
lwsl_notice(" Using foreign event loop...\n");
|
||||
pt->ev_loop_foreign = 1;
|
||||
}
|
||||
|
||||
pt->io_loop_uv = loop;
|
||||
uv_idle_init(loop, &pt->uv_idle);
|
||||
|
||||
ns = ARRAY_SIZE(sigs);
|
||||
if (lws_check_opt(context->options,
|
||||
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
|
||||
ns = 2;
|
||||
|
||||
if (pt->context->use_ev_sigint) {
|
||||
assert(ns <= ARRAY_SIZE(pt->signals));
|
||||
for (n = 0; n < ns; n++) {
|
||||
uv_signal_init(loop, &pt->signals[n]);
|
||||
pt->signals[n].data = pt->context;
|
||||
uv_signal_start(&pt->signals[n],
|
||||
context->lws_uv_sigint_cb, sigs[n]);
|
||||
}
|
||||
}
|
||||
} else
|
||||
first = 0;
|
||||
|
||||
/*
|
||||
* Initialize the accept wsi read watcher with all the listening sockets
|
||||
* and register a callback for read operations
|
||||
*
|
||||
* We have to do it here because the uv loop(s) are not
|
||||
* initialized until after context creation.
|
||||
*/
|
||||
while (vh) {
|
||||
if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1)
|
||||
return -1;
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
|
||||
uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb,
|
||||
10, 1000);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void lws_uv_close_cb(uv_handle_t *handle)
|
||||
{
|
||||
//lwsl_err("%s: handle %p\n", __func__, handle);
|
||||
}
|
||||
|
||||
static void lws_uv_walk_cb(uv_handle_t *handle, void *arg)
|
||||
{
|
||||
if (!uv_is_closing(handle))
|
||||
uv_close(handle, lws_uv_close_cb);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_close_all_handles_in_loop(uv_loop_t *loop)
|
||||
{
|
||||
uv_walk(loop, lws_uv_walk_cb, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_destroyloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
// struct lws_context *ctx;
|
||||
int m, budget = 100, ns;
|
||||
|
||||
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
|
||||
return;
|
||||
|
||||
if (!pt->io_loop_uv)
|
||||
return;
|
||||
|
||||
lwsl_notice("%s: closing signals + timers context %p\n", __func__, context);
|
||||
|
||||
if (context->use_ev_sigint) {
|
||||
uv_signal_stop(&pt->w_sigint.uv_watcher);
|
||||
|
||||
ns = ARRAY_SIZE(sigs);
|
||||
if (lws_check_opt(context->options, LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
|
||||
ns = 2;
|
||||
|
||||
for (m = 0; m < ns; m++) {
|
||||
uv_signal_stop(&pt->signals[m]);
|
||||
uv_close((uv_handle_t *)&pt->signals[m], lws_uv_close_cb);
|
||||
}
|
||||
}
|
||||
|
||||
uv_timer_stop(&pt->uv_timeout_watcher);
|
||||
uv_close((uv_handle_t *)&pt->uv_timeout_watcher, lws_uv_close_cb);
|
||||
|
||||
uv_idle_stop(&pt->uv_idle);
|
||||
uv_close((uv_handle_t *)&pt->uv_idle, lws_uv_close_cb);
|
||||
|
||||
if (pt->ev_loop_foreign)
|
||||
return;
|
||||
|
||||
while (budget-- && uv_run(pt->io_loop_uv, UV_RUN_NOWAIT))
|
||||
;
|
||||
|
||||
lwsl_notice("%s: closing all loop handles context %p\n", __func__, context);
|
||||
|
||||
uv_stop(pt->io_loop_uv);
|
||||
|
||||
uv_walk(pt->io_loop_uv, lws_uv_walk_cb, NULL);
|
||||
|
||||
while (uv_run(pt->io_loop_uv, UV_RUN_NOWAIT))
|
||||
;
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
m = uv_loop_close(pt->io_loop_uv);
|
||||
if (m == UV_EBUSY)
|
||||
lwsl_err("%s: uv_loop_close: UV_EBUSY\n", __func__);
|
||||
#endif
|
||||
lws_free(pt->io_loop_uv);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!LWS_LIBUV_ENABLED(context))
|
||||
return;
|
||||
|
||||
lwsl_debug("%s: new wsi %p\n", __func__, wsi);
|
||||
|
||||
wsi->w_read.context = context;
|
||||
if (wsi->mode == LWSCM_RAW_FILEDESC)
|
||||
uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
|
||||
(int)desc.filefd);
|
||||
else
|
||||
uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher,
|
||||
desc.sockfd);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_io(struct lws *wsi, int flags)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
int current_events = wsi->w_read.uv_watcher.events &
|
||||
(UV_READABLE | UV_WRITABLE);
|
||||
#else
|
||||
int current_events = wsi->w_read.uv_watcher.io_watcher.pevents &
|
||||
(UV_READABLE | UV_WRITABLE);
|
||||
#endif
|
||||
struct lws_io_watcher *w = &wsi->w_read;
|
||||
|
||||
if (!LWS_LIBUV_ENABLED(context))
|
||||
return;
|
||||
|
||||
// lwsl_notice("%s: wsi: %p, flags:0x%x\n", __func__, wsi, flags);
|
||||
|
||||
// w->context is set after the loop is initialized
|
||||
|
||||
if (!pt->io_loop_uv || !w->context) {
|
||||
lwsl_info("%s: no io loop yet\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((flags & (LWS_EV_START | LWS_EV_STOP)) &&
|
||||
(flags & (LWS_EV_READ | LWS_EV_WRITE)))) {
|
||||
lwsl_err("%s: assert: flags %d", __func__, flags);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (flags & LWS_EV_START) {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
current_events |= UV_WRITABLE;
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
current_events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&w->uv_watcher, current_events, lws_io_cb);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
current_events &= ~UV_WRITABLE;
|
||||
|
||||
if (flags & LWS_EV_READ)
|
||||
current_events &= ~UV_READABLE;
|
||||
|
||||
if (!(current_events & (UV_READABLE | UV_WRITABLE)))
|
||||
uv_poll_stop(&w->uv_watcher);
|
||||
else
|
||||
uv_poll_start(&w->uv_watcher, current_events,
|
||||
lws_io_cb);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
lws_libuv_init_fd_table(struct lws_context *context)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!LWS_LIBUV_ENABLED(context))
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
context->pt[n].w_sigint.context = context;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_run(const struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
|
||||
uv_run(context->pt[tsi].io_loop_uv, 0);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_stop_without_kill(const struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
|
||||
uv_stop(context->pt[tsi].io_loop_uv);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_libuv_kill(const struct lws_context *context)
|
||||
{
|
||||
int n;
|
||||
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
for (n = 0; n < context->count_threads; n++)
|
||||
if (context->pt[n].io_loop_uv &&
|
||||
LWS_LIBUV_ENABLED(context) )//&&
|
||||
//!context->pt[n].ev_loop_foreign)
|
||||
uv_stop(context->pt[n].io_loop_uv);
|
||||
}
|
||||
|
||||
/*
|
||||
* This does not actually stop the event loop. The reason is we have to pass
|
||||
* libuv handle closures through its event loop. So this tries to close all
|
||||
* wsi, and set a flag; when all the wsi closures are finalized then we
|
||||
* actually stop the libuv event loops.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_libuv_stop(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt;
|
||||
int n, m;
|
||||
|
||||
if (context->requested_kill)
|
||||
return;
|
||||
|
||||
context->requested_kill = 1;
|
||||
|
||||
m = context->count_threads;
|
||||
context->being_destroyed = 1;
|
||||
|
||||
while (m--) {
|
||||
pt = &context->pt[m];
|
||||
|
||||
for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
|
||||
struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
|
||||
|
||||
if (!wsi)
|
||||
continue;
|
||||
lws_close_free_wsi(wsi,
|
||||
LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
|
||||
/* no protocol close */);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_info("%s: feels everything closed\n", __func__);
|
||||
if (context->count_wsi_allocated == 0)
|
||||
lws_libuv_kill(context);
|
||||
}
|
||||
|
||||
LWS_VISIBLE uv_loop_t *
|
||||
lws_uv_getloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
|
||||
return context->pt[tsi].io_loop_uv;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_libuv_closewsi(uv_handle_t* handle)
|
||||
{
|
||||
struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) -
|
||||
(char *)(&n->w_read.uv_watcher));
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
int lspd = 0;
|
||||
|
||||
if (wsi->mode == LWSCM_SERVER_LISTENER &&
|
||||
wsi->context->deprecated) {
|
||||
lspd = 1;
|
||||
context->deprecation_pending_listen_close_count--;
|
||||
if (!context->deprecation_pending_listen_close_count)
|
||||
lspd = 2;
|
||||
}
|
||||
|
||||
lws_close_free_wsi_final(wsi);
|
||||
|
||||
if (lspd == 2 && context->deprecation_cb) {
|
||||
lwsl_notice("calling deprecation callback\n");
|
||||
context->deprecation_cb();
|
||||
}
|
||||
|
||||
//lwsl_notice("%s: ctx %p: wsi left %d\n", __func__, context, context->count_wsi_allocated);
|
||||
|
||||
if (context->requested_kill && context->count_wsi_allocated == 0)
|
||||
lws_libuv_kill(context);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_closehandle(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
|
||||
/* required to defer actual deletion until libuv has processed it */
|
||||
uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi);
|
||||
|
||||
if (context->requested_kill && context->count_wsi_allocated == 0)
|
||||
lws_libuv_kill(context);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_libuv_closewsi_m(uv_handle_t* handle)
|
||||
{
|
||||
lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data;
|
||||
|
||||
compatible_close(sockfd);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_closehandle_manually(struct lws *wsi)
|
||||
{
|
||||
uv_handle_t *h = (void *)&wsi->w_read.uv_watcher;
|
||||
|
||||
h->data = (void *)(lws_intptr_t)wsi->desc.sockfd;
|
||||
/* required to defer actual deletion until libuv has processed it */
|
||||
uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi_m);
|
||||
}
|
||||
|
||||
int
|
||||
lws_libuv_check_watcher_active(struct lws *wsi)
|
||||
{
|
||||
uv_handle_t *h = (void *)&wsi->w_read.uv_watcher;
|
||||
|
||||
return uv_is_active(h);
|
||||
}
|
||||
|
||||
|
||||
#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0)
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_plugins_init(struct lws_context *context, const char * const *d)
|
||||
{
|
||||
struct lws_plugin_capability lcaps;
|
||||
struct lws_plugin *plugin;
|
||||
lws_plugin_init_func initfunc;
|
||||
int m, ret = 0;
|
||||
void *v;
|
||||
uv_dirent_t dent;
|
||||
uv_fs_t req;
|
||||
char path[256];
|
||||
uv_lib_t lib;
|
||||
int pofs = 0;
|
||||
|
||||
#if defined(__MINGW32__) || !defined(WIN32)
|
||||
pofs = 3;
|
||||
#endif
|
||||
|
||||
lib.errmsg = NULL;
|
||||
lib.handle = NULL;
|
||||
|
||||
uv_loop_init(&context->pu_loop);
|
||||
|
||||
lwsl_notice(" Plugins:\n");
|
||||
|
||||
while (d && *d) {
|
||||
|
||||
lwsl_notice(" Scanning %s\n", *d);
|
||||
m =uv_fs_scandir(&context->pu_loop, &req, *d, 0, NULL);
|
||||
if (m < 1) {
|
||||
lwsl_err("Scandir on %s failed\n", *d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
|
||||
if (strlen(dent.name) < 7)
|
||||
continue;
|
||||
|
||||
lwsl_notice(" %s\n", dent.name);
|
||||
|
||||
lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name);
|
||||
if (uv_dlopen(path, &lib)) {
|
||||
uv_dlerror(&lib);
|
||||
lwsl_err("Error loading DSO: %s\n", lib.errmsg);
|
||||
uv_dlclose(&lib);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* we could open it, can we get his init function? */
|
||||
|
||||
#if !defined(WIN32) && !defined(__MINGW32__)
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
|
||||
dent.name + pofs /* snip lib... */);
|
||||
path[m - 3] = '\0'; /* snip the .so */
|
||||
#else
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
|
||||
dent.name + pofs);
|
||||
path[m - 4] = '\0'; /* snip the .dll */
|
||||
#endif
|
||||
if (uv_dlsym(&lib, path, &v)) {
|
||||
uv_dlerror(&lib);
|
||||
lwsl_err("Failed to get %s on %s: %s", path,
|
||||
dent.name, lib.errmsg);
|
||||
uv_dlclose(&lib);
|
||||
goto bail;
|
||||
}
|
||||
initfunc = (lws_plugin_init_func)v;
|
||||
lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
|
||||
m = initfunc(context, &lcaps);
|
||||
if (m) {
|
||||
lwsl_err("Initializing %s failed %d\n", dent.name, m);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
plugin = lws_malloc(sizeof(*plugin));
|
||||
if (!plugin) {
|
||||
uv_dlclose(&lib);
|
||||
lwsl_err("OOM\n");
|
||||
goto bail;
|
||||
}
|
||||
plugin->list = context->plugin_list;
|
||||
context->plugin_list = plugin;
|
||||
strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1);
|
||||
plugin->name[sizeof(plugin->name) - 1] = '\0';
|
||||
plugin->lib = lib;
|
||||
plugin->caps = lcaps;
|
||||
context->plugin_protocol_count += lcaps.count_protocols;
|
||||
context->plugin_extension_count += lcaps.count_extensions;
|
||||
|
||||
continue;
|
||||
|
||||
skip:
|
||||
uv_dlclose(&lib);
|
||||
}
|
||||
bail:
|
||||
uv_fs_req_cleanup(&req);
|
||||
d++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_plugins_destroy(struct lws_context *context)
|
||||
{
|
||||
struct lws_plugin *plugin = context->plugin_list, *p;
|
||||
lws_plugin_destroy_func func;
|
||||
char path[256];
|
||||
void *v;
|
||||
int m;
|
||||
int pofs = 0;
|
||||
|
||||
#if defined(__MINGW32__) || !defined(WIN32)
|
||||
pofs = 3;
|
||||
#endif
|
||||
|
||||
if (!plugin)
|
||||
return 0;
|
||||
|
||||
// lwsl_notice("%s\n", __func__);
|
||||
|
||||
while (plugin) {
|
||||
p = plugin;
|
||||
|
||||
#if !defined(WIN32) && !defined(__MINGW32__)
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + pofs);
|
||||
path[m - 3] = '\0';
|
||||
#else
|
||||
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + pofs);
|
||||
path[m - 4] = '\0';
|
||||
#endif
|
||||
|
||||
if (uv_dlsym(&plugin->lib, path, &v)) {
|
||||
uv_dlerror(&plugin->lib);
|
||||
lwsl_err("Failed to get %s on %s: %s", path,
|
||||
plugin->name, plugin->lib.errmsg);
|
||||
} else {
|
||||
func = (lws_plugin_destroy_func)v;
|
||||
m = func(context);
|
||||
if (m)
|
||||
lwsl_err("Destroying %s failed %d\n",
|
||||
plugin->name, m);
|
||||
}
|
||||
|
||||
uv_dlclose(&p->lib);
|
||||
plugin = p->list;
|
||||
p->list = NULL;
|
||||
free(p);
|
||||
}
|
||||
|
||||
context->plugin_list = NULL;
|
||||
|
||||
while (uv_loop_close(&context->pu_loop))
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
3687
lib/libwebsockets.c
3687
lib/libwebsockets.c
File diff suppressed because it is too large
Load diff
3408
lib/libwebsockets.h
3408
lib/libwebsockets.h
File diff suppressed because it is too large
Load diff
|
@ -1,700 +0,0 @@
|
|||
#include "private-libwebsockets.h"
|
||||
|
||||
#include "ip_addr.h"
|
||||
|
||||
/* forced into this because new espconn accepted callbacks carry no context ptr */
|
||||
static struct lws_context *hacky_context;
|
||||
static unsigned int time_high, ot;
|
||||
|
||||
/*
|
||||
* included from libwebsockets.c for esp8266 builds
|
||||
*/
|
||||
|
||||
unsigned long long time_in_microseconds(void)
|
||||
{
|
||||
unsigned int t = system_get_time();
|
||||
|
||||
if (ot > t)
|
||||
time_high++;
|
||||
ot = t;
|
||||
|
||||
return (((long long)time_high) << 32) | t;
|
||||
}
|
||||
|
||||
int gettimeofday(struct timeval *tv, void *tz)
|
||||
{
|
||||
unsigned long long t = time_in_microseconds();
|
||||
|
||||
tv->tv_sec = t / 1000000;
|
||||
tv->tv_usec = t % 1000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t time(time_t *tloc)
|
||||
{
|
||||
unsigned long long t = time_in_microseconds();
|
||||
|
||||
if (tloc)
|
||||
*tloc = t / 1000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_get_random(struct lws_context *context, void *buf, int len)
|
||||
{
|
||||
// return read(context->fd_random, (char *)buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_send_pipe_choked(struct lws *wsi)
|
||||
{
|
||||
return wsi->pending_send_completion;
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < context->max_fds; n++)
|
||||
if (context->connpool[n] == fd)
|
||||
return (struct lws *)context->connpool[n + context->max_fds];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
//lwsl_notice("%s: wsi %p: len %d\n", __func__, wsi, len);
|
||||
|
||||
wsi->pending_send_completion++;
|
||||
espconn_send(wsi->desc.sockfd, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
while(1) ;
|
||||
}
|
||||
|
||||
void exit(int n)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void _sint(void *s)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
insert_wsi(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
(void)context;
|
||||
(void)wsi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
|
||||
{
|
||||
(void)context;
|
||||
(void)fd;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tm *localtime(const time_t *timep)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
struct tm *localtime_r(const time_t *timep, struct tm *t)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int atoi(const char *s)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (*s && (*s >= '0' && *s <= '9'))
|
||||
n = (n * 10) + ((*s++) - '0');
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#undef isxdigit
|
||||
int isxdigit(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return 1;
|
||||
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return 1;
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
char a, b;
|
||||
while (*s1 && *s2) {
|
||||
a = *s1++;
|
||||
b = *s2++;
|
||||
|
||||
if (a == b)
|
||||
continue;
|
||||
|
||||
if (a >= 'a' && a <= 'z')
|
||||
a -= 'a' - 'A';
|
||||
if (b >= 'a' && b <= 'z')
|
||||
b -= 'a' - 'A';
|
||||
|
||||
if (a != b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_poll_listen_fd(struct lws_pollfd *fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service_pt(struct lws *wsi)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service(struct lws_context *context)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
||||
{
|
||||
extern void output_redirect(const char *str);
|
||||
output_redirect(line);
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_check_connection_error(struct lws *wsi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_service(struct lws_context *context, int timeout_ms)
|
||||
{
|
||||
// return _lws_plat_service_tsi(context, timeout_ms, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
esp8266_find_free_conn(struct lws_context *context)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < context->max_fds; n++)
|
||||
if (!context->connpool[n]) {
|
||||
lwsl_info(" using connpool %d\n", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
lwsl_err("%s: no free conns\n", __func__);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_sockfd_type
|
||||
esp8266_create_tcp_listen_socket(struct lws_vhost *vh)
|
||||
{
|
||||
int n = esp8266_find_free_conn(vh->context);
|
||||
struct espconn *conn;
|
||||
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
conn = lws_zalloc(sizeof *conn);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
vh->context->connpool[n] = conn;
|
||||
|
||||
conn->type = ESPCONN_TCP;
|
||||
conn->state = ESPCONN_NONE;
|
||||
conn->proto.tcp = &vh->tcp;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
const char *
|
||||
lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen)
|
||||
{
|
||||
unsigned char *p = wsi->desc.sockfd->proto.tcp->remote_ip;
|
||||
|
||||
lws_snprintf(name, namelen, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
//lwsl_notice("%s\n", __func__);
|
||||
|
||||
if (!wsi->context->rxd)
|
||||
return 0;
|
||||
|
||||
if (len < wsi->context->rxd_len)
|
||||
lwsl_err("trunc read (%d vs %d)\n", len, wsi->context->rxd_len);
|
||||
else
|
||||
len = wsi->context->rxd_len;
|
||||
|
||||
ets_memcpy(buf, wsi->context->rxd, len);
|
||||
|
||||
wsi->context->rxd = NULL;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
cb_1Hz(void *arg)
|
||||
{
|
||||
struct lws_context *context = arg;
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
struct lws *wsi;
|
||||
struct lws_pollfd *pollfd;
|
||||
int n;
|
||||
|
||||
/* Service any ah that has pending rx */
|
||||
for (n = 0; n < context->max_http_header_pool; n++)
|
||||
if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) {
|
||||
wsi = pt->ah_pool[n].wsi;
|
||||
pollfd = &pt->fds[wsi->position_in_fds_table];
|
||||
if (pollfd->events & LWS_POLLIN) {
|
||||
pollfd->revents |= LWS_POLLIN;
|
||||
lws_service_fd(context, pollfd);
|
||||
}
|
||||
}
|
||||
|
||||
/* handle timeouts */
|
||||
|
||||
lws_service_fd(context, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
esp8266_cb_rx(void *arg, char *data, unsigned short len)
|
||||
{
|
||||
struct espconn *conn = arg;
|
||||
struct lws *wsi = conn->reverse;
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[0];
|
||||
struct lws_pollfd pollfd;
|
||||
int n = 0;
|
||||
|
||||
/*
|
||||
* if we're doing HTTP headers, and we have no ah, check if there is
|
||||
* a free ah, if not, have to buffer it
|
||||
*/
|
||||
if (!wsi->hdr_parsing_completed && !wsi->u.hdr.ah) {
|
||||
for (n = 0; n < wsi->context->max_http_header_pool; n++)
|
||||
if (!pt->ah_pool[n].in_use)
|
||||
break;
|
||||
|
||||
n = n == wsi->context->max_http_header_pool;
|
||||
}
|
||||
|
||||
if (!(pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN) || n) {
|
||||
wsi->premature_rx = realloc(wsi->premature_rx,
|
||||
wsi->prem_rx_size + len);
|
||||
if (!wsi->premature_rx)
|
||||
return;
|
||||
os_memcpy((char *)wsi->premature_rx + wsi->prem_rx_size, data, len);
|
||||
wsi->prem_rx_size += len;
|
||||
// lwsl_notice("%s: wsi %p: len %d BUFFERING\n", __func__, wsi, len);
|
||||
|
||||
if (n) /* we know it will fail, but we will get on the wait list */
|
||||
n = lws_header_table_attach(wsi, 0);
|
||||
|
||||
(void)n;
|
||||
return;
|
||||
}
|
||||
|
||||
//lwsl_err("%s: wsi %p. len %d\n", __func__, wsi, len);
|
||||
|
||||
pollfd.fd = arg;
|
||||
pollfd.events = LWS_POLLIN;
|
||||
pollfd.revents = LWS_POLLIN;
|
||||
|
||||
wsi->context->rxd = data;
|
||||
wsi->context->rxd_len = len;
|
||||
|
||||
lws_service_fd(lws_get_context(wsi), &pollfd);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
esp8266_cb_sent(void *arg)
|
||||
{
|
||||
struct espconn *conn = arg;
|
||||
struct lws *wsi = conn->reverse;
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
// lwsl_err("%s: wsi %p (psc %d) wsi->position_in_fds_table=%d\n", __func__, wsi, wsi->pending_send_completion, wsi->position_in_fds_table);
|
||||
|
||||
wsi->pending_send_completion--;
|
||||
if (wsi->close_is_pending_send_completion &&
|
||||
!wsi->pending_send_completion &&
|
||||
!lws_partial_buffered(wsi)) {
|
||||
lwsl_notice("doing delayed close\n");
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
}
|
||||
|
||||
if (pt->fds[wsi->position_in_fds_table].events & LWS_POLLOUT) {
|
||||
struct lws_pollfd pollfd;
|
||||
|
||||
pollfd.fd = arg;
|
||||
pollfd.events = LWS_POLLOUT;
|
||||
pollfd.revents = LWS_POLLOUT;
|
||||
|
||||
// lwsl_notice("informing POLLOUT\n");
|
||||
|
||||
lws_service_fd(lws_get_context(wsi), &pollfd);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
esp8266_cb_disconnected(void *arg)
|
||||
{
|
||||
struct espconn *conn = arg;
|
||||
struct lws *wsi = conn->reverse;
|
||||
int n;
|
||||
|
||||
lwsl_notice("%s: %p\n", __func__, wsi);
|
||||
|
||||
for (n = 0; n < hacky_context->max_fds; n++)
|
||||
if (hacky_context->connpool[n] == arg) {
|
||||
hacky_context->connpool[n] = NULL;
|
||||
lwsl_info(" freed connpool %d\n", n);
|
||||
}
|
||||
|
||||
if (wsi) {
|
||||
conn->reverse = NULL;
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
lwsl_notice("closed ok\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
esp8266_cb_recon(void *arg, signed char err)
|
||||
{
|
||||
struct espconn *conn = arg;
|
||||
|
||||
lwsl_err("%s: wsi %p. err %d\n", __func__, conn->reverse, err);
|
||||
|
||||
conn->state = ESPCONN_CLOSE;
|
||||
|
||||
esp8266_cb_disconnected(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* there is no reliable indication of which listen socket we were accepted on.
|
||||
*/
|
||||
|
||||
static void
|
||||
esp8266_cb_connect(void *arg)
|
||||
{
|
||||
struct espconn *cs = arg;
|
||||
// struct ip_addr *ipa = (struct ip_addr *)cs->proto.tcp->remote_ip;
|
||||
struct lws_vhost *vh = hacky_context->vhost_list;
|
||||
// struct ip_info info;
|
||||
struct lws *wsi;
|
||||
int n;
|
||||
|
||||
lwsl_notice("%s: (wsi coming): %p\n", __func__, cs->reverse);
|
||||
#if 0
|
||||
wifi_get_ip_info(0, &info);
|
||||
if (ip_addr_netcmp(ipa, &info.ip, &info.netmask)) {
|
||||
/* we are on the same subnet as the AP, ie, connected to AP */
|
||||
while (vh && strcmp(vh->name, "ap"))
|
||||
vh = vh->vhost_next;
|
||||
} else
|
||||
while (vh && !strcmp(vh->name, "ap"))
|
||||
vh = vh->vhost_next;
|
||||
|
||||
if (!vh)
|
||||
goto bail;
|
||||
#endif
|
||||
n = esp8266_find_free_conn(hacky_context);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
hacky_context->connpool[n] = cs;
|
||||
|
||||
espconn_recv_hold(cs);
|
||||
|
||||
wsi = lws_adopt_socket_vhost(vh, cs);
|
||||
if (!wsi)
|
||||
goto bail;
|
||||
|
||||
lwsl_err("%s: wsi %p (using free_conn %d): vh %s\n", __func__, wsi, n, vh->name);
|
||||
|
||||
espconn_regist_recvcb(cs, esp8266_cb_rx);
|
||||
espconn_regist_reconcb(cs, esp8266_cb_recon);
|
||||
espconn_regist_disconcb(cs, esp8266_cb_disconnected);
|
||||
espconn_regist_sentcb(cs, esp8266_cb_sent);
|
||||
|
||||
espconn_set_opt(cs, ESPCONN_NODELAY | ESPCONN_REUSEADDR);
|
||||
espconn_regist_time(cs, 7200, 1);
|
||||
|
||||
return;
|
||||
|
||||
bail:
|
||||
lwsl_err("%s: bailed]n", __func__);
|
||||
espconn_disconnect(cs);
|
||||
}
|
||||
|
||||
void
|
||||
esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi)
|
||||
{
|
||||
fd->proto.tcp->local_port = port;
|
||||
fd->reverse = wsi;
|
||||
|
||||
hacky_context = wsi->context;
|
||||
|
||||
espconn_regist_connectcb(fd, esp8266_cb_connect);
|
||||
/* hmmm it means, listen() + accept() */
|
||||
espconn_accept(fd);
|
||||
|
||||
espconn_tcp_set_max_con_allow(fd, 10);
|
||||
}
|
||||
|
||||
void
|
||||
esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi)
|
||||
{
|
||||
int n;
|
||||
|
||||
fd->reverse = wsi;
|
||||
|
||||
for (n = 0; n < wsi->context->max_fds ; n++)
|
||||
if (wsi->context->connpool[n] == wsi->desc.sockfd)
|
||||
wsi->position_in_fds_table = n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_context_early_init(void)
|
||||
{
|
||||
espconn_tcp_set_max_con(12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_context_early_destroy(struct lws_context *context)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_context_late_destroy(struct lws_context *context)
|
||||
{
|
||||
#if 0
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int m = context->count_threads;
|
||||
|
||||
if (context->lws_lookup)
|
||||
lws_free(context->lws_lookup);
|
||||
|
||||
while (m--) {
|
||||
close(pt->dummy_pipe_fds[0]);
|
||||
close(pt->dummy_pipe_fds[1]);
|
||||
pt++;
|
||||
}
|
||||
#endif
|
||||
// close(context->fd_random);
|
||||
}
|
||||
|
||||
/* cast a struct sockaddr_in6 * into addr for ipv6 */
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
||||
size_t addrlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
context->connpool[wsi->position_in_fds_table + context->max_fds] = (lws_sockfd_type)wsi;
|
||||
wsi->desc.sockfd->reverse = wsi;
|
||||
pt->fds_count++;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_delete_socket_from_fds(struct lws_context *context,
|
||||
struct lws *wsi, int m)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < wsi->context->max_fds; n++)
|
||||
if (wsi->context->connpool[n] == wsi->desc.sockfd) {
|
||||
wsi->context->connpool[n] = NULL;
|
||||
wsi->context->connpool[n + wsi->context->max_fds] = NULL;
|
||||
lwsl_notice(" freed connpool %d\n", n);
|
||||
}
|
||||
|
||||
wsi->desc.sockfd->reverse = NULL;
|
||||
pt->fds_count--;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_service_periodic(struct lws_context *context)
|
||||
{
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_change_pollfd(struct lws_context *context,
|
||||
struct lws *wsi, struct lws_pollfd *pfd)
|
||||
{
|
||||
void *p;
|
||||
|
||||
//lwsl_notice("%s: %p: wsi->pift=%d, events %d\n",
|
||||
// __func__, wsi, wsi->position_in_fds_table, pfd->events);
|
||||
|
||||
if (pfd->events & LWS_POLLIN) {
|
||||
if (wsi->premature_rx) {
|
||||
lwsl_notice("replaying buffered rx: wsi %p\n", wsi);
|
||||
p = wsi->premature_rx;
|
||||
wsi->premature_rx = NULL;
|
||||
esp8266_cb_rx(wsi->desc.sockfd,
|
||||
(char *)p + wsi->prem_rx_pos,
|
||||
wsi->prem_rx_size - wsi->prem_rx_pos);
|
||||
wsi->prem_rx_size = 0;
|
||||
wsi->prem_rx_pos = 0;
|
||||
lws_free(p);
|
||||
}
|
||||
if (espconn_recv_unhold(wsi->desc.sockfd) < 0)
|
||||
return -1;
|
||||
} else
|
||||
if (espconn_recv_hold(wsi->desc.sockfd) < 0)
|
||||
return -1;
|
||||
|
||||
if (!(pfd->events & LWS_POLLOUT))
|
||||
return 0;
|
||||
|
||||
if (!wsi->pending_send_completion) {
|
||||
pfd->revents |= LWS_POLLOUT;
|
||||
|
||||
// lwsl_notice("doing POLLOUT\n");
|
||||
lws_service_fd(lws_get_context(wsi), pfd);
|
||||
} //else
|
||||
//lwsl_notice("pending sc\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE const char *
|
||||
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
|
||||
{
|
||||
// return inet_ntop(af, src, dst, cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_inet_pton(int af, const char *src, void *dst)
|
||||
{
|
||||
//return inet_pton(af, src, dst);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_init(struct lws_context *context,
|
||||
struct lws_context_creation_info *info)
|
||||
{
|
||||
// struct lws_context_per_thread *pt = &context->pt[0];
|
||||
// int n = context->count_threads, fd;
|
||||
|
||||
/* master context has the global fd lookup array */
|
||||
context->connpool = lws_zalloc(sizeof(struct espconn *) *
|
||||
context->max_fds * 2);
|
||||
if (context->connpool == NULL) {
|
||||
lwsl_err("OOM on lws_lookup array for %d connections\n",
|
||||
context->max_fds);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice(" mem: platform fd map: %5u bytes\n",
|
||||
sizeof(struct espconn *) * context->max_fds);
|
||||
// fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
|
||||
|
||||
// context->fd_random = fd;
|
||||
// if (context->fd_random < 0) {
|
||||
// lwsl_err("Unable to open random device %s %d\n",
|
||||
// SYSTEM_RANDOM_FILEPATH, context->fd_random);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
os_memset(&context->to_timer, 0, sizeof(os_timer_t));
|
||||
os_timer_disarm(&context->to_timer);
|
||||
os_timer_setfn(&context->to_timer, (os_timer_func_t *)cb_1Hz, context);
|
||||
os_timer_arm(&context->to_timer, 1000, 1);
|
||||
|
||||
if (!lws_libev_init_fd_table(context) &&
|
||||
!lws_libuv_init_fd_table(context) &&
|
||||
!lws_libevent_init_fd_table(context)) {
|
||||
/* otherwise libev handled it instead */
|
||||
#if 0
|
||||
while (n--) {
|
||||
if (pipe(pt->dummy_pipe_fds)) {
|
||||
lwsl_err("Unable to create pipe\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* use the read end of pipe as first item */
|
||||
pt->fds[0].fd = pt->dummy_pipe_fds[0];
|
||||
pt->fds[0].events = LWS_POLLIN;
|
||||
pt->fds[0].revents = 0;
|
||||
pt->fds_count = 1;
|
||||
pt++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
if (info->plugin_dirs)
|
||||
lws_plat_plugins_init(context, info->plugin_dirs);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -40,15 +40,18 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
|
||||
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
|
||||
static int
|
||||
_lws_b64_encode_string(const char *encode, const char *in, int in_len,
|
||||
char *out, int out_size)
|
||||
{
|
||||
unsigned char triple[3];
|
||||
int i;
|
||||
|
@ -89,26 +92,47 @@ lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
|
|||
return done;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
|
||||
{
|
||||
return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
|
||||
{
|
||||
return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns length of decoded string in out, or -1 if out was too small
|
||||
* according to out_size
|
||||
*
|
||||
* Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
|
||||
* the first NUL in the input.
|
||||
*/
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_decode_string(const char *in, char *out, int out_size)
|
||||
static int
|
||||
_lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
|
||||
{
|
||||
int len, i, c = 0, done = 0;
|
||||
unsigned char v, quad[4];
|
||||
|
||||
while (*in) {
|
||||
while (in_len && *in) {
|
||||
|
||||
len = 0;
|
||||
for (i = 0; i < 4 && *in; i++) {
|
||||
for (i = 0; i < 4 && in_len && *in; i++) {
|
||||
|
||||
v = 0;
|
||||
c = 0;
|
||||
while (*in && !v) {
|
||||
while (in_len && *in && !v) {
|
||||
c = v = *in++;
|
||||
in_len--;
|
||||
/* support the url base64 variant too */
|
||||
if (v == '-')
|
||||
c = v = '+';
|
||||
if (v == '_')
|
||||
c = v = '/';
|
||||
v = (v < 43 || v > 122) ? 0 : decode[v - 43];
|
||||
if (v)
|
||||
v = (v == '$') ? 0 : v - 61;
|
||||
|
@ -131,7 +155,7 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
|
|||
* bytes." (wikipedia)
|
||||
*/
|
||||
|
||||
if (!*in && c == '=')
|
||||
if ((!in_len || !*in) && c == '=')
|
||||
len--;
|
||||
|
||||
if (len >= 2)
|
||||
|
@ -152,6 +176,18 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
|
|||
return done;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_decode_string(const char *in, char *out, int out_size)
|
||||
{
|
||||
return _lws_b64_decode_string(in, -1, out, out_size);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
|
||||
{
|
||||
return _lws_b64_decode_string(in, in_len, out, out_size);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int
|
||||
lws_b64_selftest(void)
|
|
@ -22,7 +22,7 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
int pid_daemon;
|
||||
static char *lock_path;
|
||||
|
@ -126,7 +126,7 @@ lws_daemonize(const char *_lock_path)
|
|||
}
|
||||
|
||||
n = strlen(_lock_path) + 1;
|
||||
lock_path = lws_malloc(n);
|
||||
lock_path = lws_malloc(n, "daemonize lock");
|
||||
if (!lock_path) {
|
||||
fprintf(stderr, "Out of mem in lws_daemonize\n");
|
||||
return 1;
|
|
@ -1,8 +1,3 @@
|
|||
/*
|
||||
* downloaded from
|
||||
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
|
||||
*/
|
||||
#if !LWS_HAVE_GETIFADDRS
|
||||
/*
|
||||
* Copyright (c) 2000 - 2001 Kungliga Tekniska H<EFBFBD>gskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
|
@ -34,6 +29,10 @@
|
|||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* originally downloaded from
|
||||
*
|
||||
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -44,7 +43,7 @@
|
|||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
#ifdef LWS_HAVE_SYS_SOCKIO_H
|
||||
#include <sys/sockio.h>
|
||||
|
@ -84,7 +83,7 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
|
|||
|
||||
buf_size = 8192;
|
||||
for (;;) {
|
||||
buf = lws_zalloc(buf_size);
|
||||
buf = lws_zalloc(buf_size, "getifaddrs2");
|
||||
if (buf == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto error_out;
|
||||
|
@ -136,12 +135,12 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
|
|||
goto error_out;
|
||||
}
|
||||
|
||||
*end = lws_malloc(sizeof(**end));
|
||||
*end = lws_malloc(sizeof(**end), "getifaddrs");
|
||||
|
||||
(*end)->ifa_next = NULL;
|
||||
(*end)->ifa_name = strdup(ifr->ifr_name);
|
||||
(*end)->ifa_flags = ifreq.ifr_flags;
|
||||
(*end)->ifa_addr = lws_malloc(salen);
|
||||
(*end)->ifa_addr = lws_malloc(salen, "getifaddrs");
|
||||
memcpy((*end)->ifa_addr, sa, salen);
|
||||
(*end)->ifa_netmask = NULL;
|
||||
|
||||
|
@ -149,12 +148,12 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
|
|||
/* fix these when we actually need them */
|
||||
if (ifreq.ifr_flags & IFF_BROADCAST) {
|
||||
(*end)->ifa_broadaddr =
|
||||
lws_malloc(sizeof(ifr->ifr_broadaddr));
|
||||
lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs");
|
||||
memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
|
||||
sizeof(ifr->ifr_broadaddr));
|
||||
} else if (ifreq.ifr_flags & IFF_POINTOPOINT) {
|
||||
(*end)->ifa_dstaddr =
|
||||
lws_malloc(sizeof(ifr->ifr_dstaddr));
|
||||
lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs");
|
||||
memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
|
||||
sizeof(ifr->ifr_dstaddr));
|
||||
} else
|
||||
|
@ -269,4 +268,3 @@ main()
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
323
lib/misc/jws/jwk.c
Normal file
323
lib/misc/jws/jwk.c
Normal file
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Key support
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char * const jwk_tok[] = {
|
||||
"e", "n", "d", "p", "q", "dp", "dq", "qi", "kty", "k",
|
||||
};
|
||||
|
||||
static int
|
||||
_lws_jwk_set_element(struct lws_genrsa_element *e, char *in, int len)
|
||||
{
|
||||
int dec_size = ((len * 3) / 4) + 4, n;
|
||||
|
||||
e->buf = lws_malloc(dec_size, "jwk");
|
||||
if (!e->buf)
|
||||
return -1;
|
||||
|
||||
n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
e->len = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cb_lws_jwk {
|
||||
struct lws_jwk *s;
|
||||
char *b64;
|
||||
int b64max;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static signed char
|
||||
cb_jwk(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
|
||||
struct lws_jwk *s = cbs->s;
|
||||
int idx;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_START)
|
||||
cbs->pos = 0;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case JWK_KTY:
|
||||
lws_strncpy(s->keytype, ctx->buf, sizeof(s->keytype));
|
||||
if (!strcmp(ctx->buf, "oct")) {
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RSA")) {
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
|
||||
case JWK_KEY:
|
||||
// if (strcmp(s->keytype, "oct"))
|
||||
// return -1;
|
||||
idx = JWK_KEY_E;
|
||||
goto read_element1;
|
||||
|
||||
case JWK_KEY_N:
|
||||
case JWK_KEY_E:
|
||||
case JWK_KEY_D:
|
||||
case JWK_KEY_P:
|
||||
case JWK_KEY_Q:
|
||||
case JWK_KEY_DP:
|
||||
case JWK_KEY_DQ:
|
||||
case JWK_KEY_QI:
|
||||
idx = ctx->path_match - 1;
|
||||
goto read_element;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
read_element:
|
||||
/* kty is no longer first in lex order */
|
||||
// if (strcmp(s->keytype, "RSA"))
|
||||
// return -1;
|
||||
|
||||
read_element1:
|
||||
|
||||
if (cbs->pos + ctx->npos >= cbs->b64max)
|
||||
return -1;
|
||||
|
||||
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
|
||||
cbs->pos += ctx->npos;
|
||||
|
||||
if (reason == LEJPCB_VAL_STR_CHUNK)
|
||||
return 0;
|
||||
|
||||
if (_lws_jwk_set_element(&s->el.e[idx], cbs->b64, cbs->pos) < 0) {
|
||||
lws_jwk_destroy_genrsa_elements(&s->el);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_import(struct lws_jwk *s, const char *in, size_t len)
|
||||
{
|
||||
struct lejp_ctx jctx;
|
||||
struct cb_lws_jwk cbs;
|
||||
const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
|
||||
char b64[b64max];
|
||||
int m;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
cbs.s = s;
|
||||
cbs.b64 = b64;
|
||||
cbs.b64max = b64max;
|
||||
cbs.pos = 0;
|
||||
lejp_construct(&jctx, cb_jwk, &cbs, jwk_tok, ARRAY_SIZE(jwk_tok));
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
|
||||
lejp_destruct(&jctx);
|
||||
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: parse got %d\n", __func__, m);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_jwk_destroy(struct lws_jwk *s)
|
||||
{
|
||||
lws_jwk_destroy_genrsa_elements(&s->el);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
|
||||
{
|
||||
char *start = p, *end = &p[len - 1];
|
||||
int n, m, limit = LWS_COUNT_RSA_ELEMENTS;
|
||||
|
||||
/* RFC7638 lexicographic order requires
|
||||
* RSA: e -> kty -> n
|
||||
* oct: k -> kty
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{");
|
||||
|
||||
if (!strcmp(s->keytype, "oct")) {
|
||||
if (!s->el.e[JWK_KEY_E].buf)
|
||||
return -1;
|
||||
|
||||
p += lws_snprintf(p, end - p, "\"k\":\"");
|
||||
n = lws_jws_base64_enc((const char *)s->el.e[JWK_KEY_E].buf,
|
||||
s->el.e[JWK_KEY_E].len, p,
|
||||
end - p - 4);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: enc failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
p += n;
|
||||
|
||||
p += lws_snprintf(p, end - p, "\",\"kty\":\"%s\"}", s->keytype);
|
||||
|
||||
return p - start;
|
||||
}
|
||||
|
||||
if (!strcmp(s->keytype, "RSA")) {
|
||||
if (!s->el.e[JWK_KEY_E].buf ||
|
||||
!s->el.e[JWK_KEY_N].buf ||
|
||||
(private && (!s->el.e[JWK_KEY_D].buf ||
|
||||
!s->el.e[JWK_KEY_P].buf ||
|
||||
!s->el.e[JWK_KEY_Q].buf))
|
||||
) {
|
||||
lwsl_notice("%s: not enough elements filled\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!private)
|
||||
limit = JWK_KEY_N + 1;
|
||||
|
||||
for (n = 0; n < limit; n++) {
|
||||
if (!s->el.e[n].buf)
|
||||
continue;
|
||||
lwsl_info("%d: len %d\n", n, s->el.e[n].len);
|
||||
|
||||
if (n)
|
||||
p += lws_snprintf(p, end - p, ",");
|
||||
p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
|
||||
m = lws_jws_base64_enc((const char *)s->el.e[n].buf,
|
||||
s->el.e[n].len, p,
|
||||
end - p - 4);
|
||||
if (m < 0) {
|
||||
lwsl_notice("%s: enc fail inlen %d outlen %d\n",
|
||||
__func__, (int)s->el.e[n].len,
|
||||
lws_ptr_diff(end, p) - 4);
|
||||
return -1;
|
||||
}
|
||||
p += m;
|
||||
*p++ = '\"';
|
||||
|
||||
if (!n) /* RFC7638 lexicographic order */
|
||||
p += lws_snprintf(p, end - p, ",\"kty\":\"%s\"",
|
||||
s->keytype);
|
||||
}
|
||||
|
||||
p += lws_snprintf(p, end - p, "}");
|
||||
|
||||
return p - start;
|
||||
}
|
||||
|
||||
lwsl_err("%s: unknown key type %s\n", __func__, s->keytype);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
|
||||
{
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
int tmpsize = 2536, n;
|
||||
char *tmp;
|
||||
|
||||
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
|
||||
|
||||
n = lws_jwk_export(s, 0, tmp, tmpsize);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
|
||||
goto bail;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, tmp, n)) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
|
||||
goto bail;
|
||||
}
|
||||
lws_free(tmp);
|
||||
|
||||
if (lws_genhash_destroy(&hash_ctx, digest32))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(tmp);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_load(struct lws_jwk *s, const char *filename)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-load");
|
||||
int n;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_plat_read_file(filename, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
n = lws_jwk_import(s, buf, n);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jwk_save(struct lws_jwk *s, const char *filename)
|
||||
{
|
||||
int buflen = 4096;
|
||||
char *buf = lws_malloc(buflen, "jwk-save");
|
||||
int n, m;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_jwk_export(s, 1, buf, buflen);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
m = lws_plat_write_file(filename, buf, n);
|
||||
|
||||
lws_free(buf);
|
||||
if (m)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
642
lib/misc/jws/jws.c
Normal file
642
lib/misc/jws/jws.c
Normal file
|
@ -0,0 +1,642 @@
|
|||
/*
|
||||
* libwebsockets - JSON Web Signature support
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
/*
|
||||
* JSON Web Signature is defined in RFC7515
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc7515
|
||||
*
|
||||
* It's basically a way to wrap some JSON with a JSON "header" describing the
|
||||
* crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
|
||||
*
|
||||
* The signature stays with the content, it serves a different purpose than eg
|
||||
* a TLS tunnel to transfer it.
|
||||
*
|
||||
* RFC7518 (JSON Web Algorithms) says for the "alg" names
|
||||
*
|
||||
* | HS256 | HMAC using SHA-256 | Required |
|
||||
* | HS384 | HMAC using SHA-384 | Optional |
|
||||
* | HS512 | HMAC using SHA-512 | Optional |
|
||||
* | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
|
||||
* | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-384 | |
|
||||
* | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
|
||||
* | | SHA-512 | |
|
||||
* | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
|
||||
* | ES384 | ECDSA using P-384 and SHA-384 | Optional |
|
||||
* | ES512 | ECDSA using P-521 and SHA-512 | Optional |
|
||||
*
|
||||
* Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
|
||||
* currently. The "Recommended+" just means it is recommended but will likely
|
||||
* be "very recommended" soon.
|
||||
*
|
||||
* We support HS256/384/512 for symmetric crypto, but the choice for the
|
||||
* asymmetric crypto isn't as easy to make.
|
||||
*
|
||||
* Normally you'd choose the EC option but these are defined to use the
|
||||
* "NIST curves" (RFC7518 3.4) which are believed to be insecure.
|
||||
*
|
||||
* https://safecurves.cr.yp.to/
|
||||
*
|
||||
* For that reason we implement RS256/384/512 for asymmetric.
|
||||
*/
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
static const char
|
||||
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
|
||||
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
|
||||
*test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
|
||||
" \"http://example.com/is_root\":true}",
|
||||
*test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
|
||||
"ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
|
||||
*key_jwk = "{\"kty\":\"oct\",\r\n"
|
||||
" \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
|
||||
"Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
|
||||
*hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
||||
/* the key from worked example in RFC7515 A-1, as a JWK */
|
||||
*rfc7515_rsa_key =
|
||||
"{\"kty\":\"RSA\","
|
||||
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
|
||||
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
|
||||
"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
|
||||
"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
|
||||
"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
|
||||
"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
|
||||
"\"e\":\"AQAB\","
|
||||
"\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
|
||||
"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
|
||||
"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
|
||||
"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
|
||||
"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
|
||||
"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
|
||||
"\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
|
||||
"YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
|
||||
"BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
|
||||
"\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
|
||||
"ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
|
||||
"-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
|
||||
"\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
|
||||
"CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
|
||||
"34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
|
||||
"\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
|
||||
"7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
|
||||
"NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
|
||||
"\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
|
||||
"y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
|
||||
"W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
|
||||
"}",
|
||||
*rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
|
||||
"eyJhbGciOiJSUzI1NiJ9"
|
||||
".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
|
||||
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
|
||||
".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
|
||||
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
|
||||
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
|
||||
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
|
||||
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
|
||||
"p0igcN_IoypGlUPQGe77Rw";
|
||||
#endif
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = lws_b64_encode_string_url(in, in_len, out, out_max - 1);
|
||||
if (n < 0)
|
||||
return n; /* too large for output buffer */
|
||||
|
||||
/* trim the terminal = */
|
||||
while (n && out[n - 1] == '=')
|
||||
n--;
|
||||
|
||||
out[n] = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
|
||||
char *end)
|
||||
{
|
||||
int n, len = (end - *p) - 1;
|
||||
char *p_entry = *p;
|
||||
|
||||
if (len < 3)
|
||||
return -1;
|
||||
|
||||
if (!first)
|
||||
*(*p)++ = '.';
|
||||
|
||||
n = lws_jws_base64_enc(in, in_len, *p, len - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
*p += n;
|
||||
|
||||
return (*p) - p_entry;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_jws_find_sig(const char *in, size_t len)
|
||||
{
|
||||
const char *p = in + len - 1;
|
||||
|
||||
while (len--)
|
||||
if (*p == '.')
|
||||
return (p + 1) - in;
|
||||
else
|
||||
p--;
|
||||
|
||||
lwsl_notice("%s failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static const char * const jhdr_tok[] = {
|
||||
"typ",
|
||||
"alg",
|
||||
};
|
||||
enum enum_jhdr_tok {
|
||||
JHP_TYP,
|
||||
JHP_ALG
|
||||
};
|
||||
struct cb_hdr_s {
|
||||
enum lws_genhash_types hash_type;
|
||||
enum lws_genhmac_types hmac_type;
|
||||
char alg[10];
|
||||
int is_rsa:1;
|
||||
};
|
||||
|
||||
static signed char
|
||||
cb_hdr(struct lejp_ctx *ctx, char reason)
|
||||
{
|
||||
struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
|
||||
|
||||
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
|
||||
return 0;
|
||||
|
||||
switch (ctx->path_match - 1) {
|
||||
case JHP_TYP: /* it is optional */
|
||||
if (strcmp(ctx->buf, "JWT"))
|
||||
return -1;
|
||||
break;
|
||||
case JHP_ALG:
|
||||
lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
|
||||
if (!strcmp(ctx->buf, "HS256")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS384")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "HS512")) {
|
||||
s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS256")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA256;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS384")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA384;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(ctx->buf, "RS512")) {
|
||||
s->hash_type = LWS_GENHASH_TYPE_SHA512;
|
||||
s->is_rsa = 1;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk)
|
||||
{
|
||||
int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct cb_hdr_s args;
|
||||
struct lejp_ctx jctx;
|
||||
char buf[2048];
|
||||
|
||||
/* 1) there has to be a signature */
|
||||
|
||||
if (sig_pos < 0)
|
||||
return -1;
|
||||
|
||||
/* 2) find length of first, hdr, block */
|
||||
|
||||
while (in[pos] != '.' && pos < (int)len)
|
||||
pos++;
|
||||
if (pos == (int)len)
|
||||
return -1;
|
||||
|
||||
/* 3) Decode the header block */
|
||||
|
||||
n = lws_b64_decode_string_len(in, pos, buf, sizeof(buf) - 1);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
/* 4) Require either:
|
||||
* typ: JWT (if present) and alg: HS256/384/512
|
||||
* typ: JWT (if present) and alg: RS256/384/512
|
||||
*/
|
||||
|
||||
args.alg[0] = '\0';
|
||||
args.is_rsa = 0;
|
||||
lejp_construct(&jctx, cb_hdr, &args, jhdr_tok, ARRAY_SIZE(jhdr_tok));
|
||||
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
|
||||
lejp_destruct(&jctx);
|
||||
if (m < 0) {
|
||||
lwsl_notice("parse got %d: alg %s\n", m, args.alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 5) decode the B64URL signature part into buf / m */
|
||||
|
||||
m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
|
||||
buf, sizeof(buf) - 1);
|
||||
|
||||
if (args.is_rsa) {
|
||||
|
||||
/* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
|
||||
|
||||
/* 6(RSA): compute the hash of the payload into "digest" */
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, args.hash_type))
|
||||
return -1;
|
||||
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (lws_genhash_destroy(&hash_ctx, digest))
|
||||
return -1;
|
||||
|
||||
h_len = lws_genhash_size(args.hash_type);
|
||||
|
||||
if (lws_genrsa_create(&rsactx, &jwk->el)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
|
||||
(uint8_t *)buf, m);
|
||||
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lwsl_notice("decrypt fail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SHA256/384/512 HMAC */
|
||||
|
||||
h_len = lws_genhmac_size(args.hmac_type);
|
||||
if (m < 0 || m != h_len)
|
||||
return -1;
|
||||
|
||||
/* 6) compute HMAC over payload */
|
||||
|
||||
if (lws_genhmac_init(&ctx, args.hmac_type, jwk->el.e[JWK_KEY_E].buf,
|
||||
jwk->el.e[JWK_KEY_E].len))
|
||||
return -1;
|
||||
|
||||
if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
|
||||
lws_genhmac_destroy(&ctx, NULL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
if (lws_genhmac_destroy(&ctx, digest))
|
||||
return -1;
|
||||
|
||||
/* 7) Compare the computed and decoded hashes */
|
||||
|
||||
if (memcmp(digest, buf, h_len)) {
|
||||
lwsl_notice("digest mismatch\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
|
||||
size_t pay_len, char *b64_sig, size_t sig_len,
|
||||
enum lws_genhash_types hash_type, struct lws_jwk *jwk)
|
||||
{
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
struct lws_genhash_ctx hash_ctx;
|
||||
struct lws_genrsa_ctx rsactx;
|
||||
uint8_t *buf;
|
||||
int n;
|
||||
|
||||
if (lws_genhash_init(&hash_ctx, hash_type))
|
||||
return -1;
|
||||
|
||||
if (b64_hdr) {
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_hdr, hdr_len))
|
||||
goto hash_fail;
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)".", 1))
|
||||
goto hash_fail;
|
||||
}
|
||||
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_pay, pay_len))
|
||||
goto hash_fail;
|
||||
|
||||
if (lws_genhash_destroy(&hash_ctx, digest))
|
||||
return -1;
|
||||
|
||||
if (!strcmp(jwk->keytype, "RSA")) {
|
||||
if (lws_genrsa_create(&rsactx, &jwk->el)) {
|
||||
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = jwk->el.e[JWK_KEY_N].len;
|
||||
buf = lws_malloc(n, "jws sign");
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
|
||||
lws_genrsa_destroy(&rsactx);
|
||||
if (n < 0) {
|
||||
lws_free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
|
||||
lws_free(buf);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if (!strcmp(jwk->keytype, "oct"))
|
||||
return lws_jws_base64_enc((char *)digest,
|
||||
lws_genhash_size(hash_type),
|
||||
b64_sig, sig_len);
|
||||
|
||||
/* unknown key type */
|
||||
|
||||
return -1;
|
||||
|
||||
hash_fail:
|
||||
lws_genhash_destroy(&hash_ctx, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
|
||||
const char *nonce, char *out, size_t out_len)
|
||||
{
|
||||
char *buf, *start, *p, *end, *p1, *end1, *b64_hdr, *b64_pay;
|
||||
int n, b64_hdr_len, b64_pay_len;
|
||||
|
||||
/*
|
||||
* This buffer is local to the function, the actual output
|
||||
* is prepared into vhd->buf. Only the plaintext protected header
|
||||
* (which contains the public key, 512 bytes for 4096b) goes in
|
||||
* here temporarily.
|
||||
*/
|
||||
n = LWS_PRE + 2048;
|
||||
buf = malloc(n);
|
||||
if (!buf) {
|
||||
lwsl_notice("%s: malloc %d failed\n", __func__, n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = start = buf + LWS_PRE;
|
||||
end = buf + n - LWS_PRE - 1;
|
||||
|
||||
/*
|
||||
* temporary JWS protected header plaintext
|
||||
*/
|
||||
|
||||
p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
|
||||
n = lws_jwk_export(jwk, 0, p, end - p);
|
||||
if (n < 0) {
|
||||
lwsl_notice("failed to export jwk\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p += n;
|
||||
p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce);
|
||||
|
||||
/*
|
||||
* prepare the signed outer JSON with all the parts in
|
||||
*/
|
||||
|
||||
p1 = out;
|
||||
end1 = out + out_len - 1;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
|
||||
b64_hdr = p1;
|
||||
n = lws_jws_base64_enc(start, p - start, p1, end1 - p1);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: failed to encode protected\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
b64_hdr_len = n;
|
||||
p1 += n;
|
||||
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
|
||||
b64_pay = p1;
|
||||
n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
|
||||
if (n < 0) {
|
||||
lwsl_notice("%s: failed to encode payload\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
b64_pay_len = n;
|
||||
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
|
||||
|
||||
/*
|
||||
* taking the b64 protected header and the b64 payload, sign them
|
||||
* and place the signature into the packet
|
||||
*/
|
||||
n = lws_jws_sign_from_b64(b64_hdr, b64_hdr_len, b64_pay, b64_pay_len,
|
||||
p1, end1 - p1, LWS_GENHASH_TYPE_SHA256, jwk);
|
||||
if (n < 0) {
|
||||
lwsl_notice("sig gen failed\n");
|
||||
|
||||
goto bail;
|
||||
}
|
||||
p1 += n;
|
||||
p1 += lws_snprintf(p1, end1 - p1, "\"}");
|
||||
|
||||
free(buf);
|
||||
|
||||
return p1 - out;
|
||||
|
||||
bail:
|
||||
free(buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
/*
|
||||
* These are the inputs and outputs from the worked example in RFC7515
|
||||
* Appendix A.1.
|
||||
*
|
||||
* 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
|
||||
* a fixed BASE64URL result.
|
||||
*
|
||||
* 2) has a fixed header + payload and is signed with a key given in JWK format
|
||||
*/
|
||||
int
|
||||
lws_jws_selftest(void)
|
||||
{
|
||||
struct lws_genhmac_ctx ctx;
|
||||
struct lws_jwk jwk;
|
||||
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
|
||||
uint8_t digest[LWS_GENHASH_LARGEST];
|
||||
int n;
|
||||
|
||||
/* Test 1: SHA256 on RFC7515 worked example */
|
||||
|
||||
/* 1.1: decode the JWK oct key */
|
||||
|
||||
if (lws_jwk_import(&jwk, key_jwk, strlen(key_jwk)) < 0) {
|
||||
lwsl_notice("Failed to decode JWK test key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 1.2: create JWS known hdr + known payload */
|
||||
|
||||
n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
if (strcmp(buf, test1_enc))
|
||||
goto bail;
|
||||
|
||||
enc_ptr = p + 1; /* + 1 skips the . */
|
||||
n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
if (strcmp(enc_ptr, test2_enc))
|
||||
goto bail;
|
||||
|
||||
/* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
|
||||
|
||||
if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
|
||||
jwk.el.e[JWK_KEY_E].buf, jwk.el.e[JWK_KEY_E].len))
|
||||
goto bail;
|
||||
if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
|
||||
goto bail_destroy_hmac;
|
||||
lws_genhmac_destroy(&ctx, digest);
|
||||
|
||||
/* 1.4: append a base64 encode of the computed HMAC digest */
|
||||
|
||||
enc_ptr = p + 1; /* + 1 skips the . */
|
||||
n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
if (strcmp(enc_ptr, hash_enc)) /* check against known B64URL hash */
|
||||
goto bail;
|
||||
|
||||
/* 1.5: Check we can agree the signature matches the payload */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 0) {
|
||||
lwsl_notice("confirm sig failed\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk); /* finished with the key from the first test */
|
||||
|
||||
/* Test 2: RSA256 on RFC7515 worked example */
|
||||
|
||||
/* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
|
||||
|
||||
if (lws_jwk_import(&jwk, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
|
||||
lwsl_notice("Failed to read JWK key\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
/* 2.2: check the signature on the test packet from RFC7515 A-1 */
|
||||
|
||||
if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
|
||||
&jwk) < 0) {
|
||||
lwsl_notice("confirm rsa sig failed\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* 2.3: generate our own signature for a copy of the test packet */
|
||||
|
||||
memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
|
||||
|
||||
/* set p to second . */
|
||||
p = strchr(buf + 1, '.');
|
||||
p1 = strchr(p + 1, '.');
|
||||
|
||||
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
|
||||
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
|
||||
LWS_GENHASH_TYPE_SHA256, &jwk);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
|
||||
puts(buf);
|
||||
|
||||
/* 2.4: confirm our signature can be verified */
|
||||
|
||||
if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
|
||||
lwsl_notice("confirm rsa sig 2 failed\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
lws_jwk_destroy(&jwk);
|
||||
|
||||
/* end */
|
||||
|
||||
lwsl_notice("%s: selftest OK\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
bail_destroy_hmac:
|
||||
lws_genhmac_destroy(&ctx, NULL);
|
||||
|
||||
bail:
|
||||
lws_jwk_destroy(&jwk);
|
||||
bail2:
|
||||
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
|
@ -1,14 +1,26 @@
|
|||
/*
|
||||
* Lightweight Embedded JSON Parser
|
||||
*
|
||||
* Copyright (C) 2013 Andy Green <andy@warmcat.com>
|
||||
* This code is licensed under LGPL 2.1
|
||||
* http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
* Copyright (C) 2013-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include "lejp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
|
@ -25,7 +37,7 @@
|
|||
|
||||
void
|
||||
lejp_construct(struct lejp_ctx *ctx,
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
|
||||
signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
|
||||
const char * const *paths, unsigned char count_paths)
|
||||
{
|
||||
ctx->st[0].s = 0;
|
||||
|
@ -86,7 +98,7 @@ lejp_destruct(struct lejp_ctx *ctx)
|
|||
|
||||
void
|
||||
lejp_change_callback(struct lejp_ctx *ctx,
|
||||
char (*callback)(struct lejp_ctx *ctx, char reason))
|
||||
signed char (*callback)(struct lejp_ctx *ctx, char reason))
|
||||
{
|
||||
ctx->callback(ctx, LEJPCB_DESTRUCTED);
|
||||
ctx->callback = callback;
|
||||
|
@ -148,7 +160,8 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
|
|||
|
||||
n = ctx->wild[wildcard];
|
||||
|
||||
while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
|
||||
while (--len && n < ctx->ppos &&
|
||||
(n == ctx->wild[wildcard] || ctx->path[n] != '.'))
|
||||
*dest++ = ctx->path[n++];
|
||||
|
||||
*dest = '\0';
|
||||
|
@ -186,7 +199,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
|
||||
while (len--) {
|
||||
c = *json++;
|
||||
|
||||
s = ctx->st[ctx->sp].s;
|
||||
|
||||
/* skip whitespace unless we should care */
|
||||
|
@ -411,6 +423,26 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
}
|
||||
goto add_stack_level;
|
||||
|
||||
case ']':
|
||||
/* pop */
|
||||
ctx->sp--;
|
||||
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
|
||||
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
|
||||
goto reject;
|
||||
}
|
||||
/* drop the path [n] bit */
|
||||
ctx->ppos = ctx->st[ctx->sp - 1].p;
|
||||
ctx->ipos = ctx->st[ctx->sp - 1].i;
|
||||
ctx->path[ctx->ppos] = '\0';
|
||||
if (ctx->path_match &&
|
||||
ctx->ppos <= ctx->path_match_len)
|
||||
/*
|
||||
* we shrank the path to be
|
||||
* smaller than the matching point
|
||||
*/
|
||||
ctx->path_match = 0;
|
||||
goto array_end;
|
||||
|
||||
case 't': /* true */
|
||||
ctx->uni = 0;
|
||||
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
|
||||
|
@ -444,7 +476,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
goto append_npos;
|
||||
}
|
||||
if (c == '.') {
|
||||
if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
|
||||
if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
|
||||
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
|
||||
goto reject;
|
||||
}
|
||||
|
@ -582,8 +614,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
goto reject;
|
||||
}
|
||||
/* drop the path [n] bit */
|
||||
ctx->ppos = ctx->st[ctx->sp - 1].p;
|
||||
ctx->ipos = ctx->st[ctx->sp - 1].i;
|
||||
if (ctx->sp) {
|
||||
ctx->ppos = ctx->st[ctx->sp - 1].p;
|
||||
ctx->ipos = ctx->st[ctx->sp - 1].i;
|
||||
}
|
||||
ctx->path[ctx->ppos] = '\0';
|
||||
if (ctx->path_match &&
|
||||
ctx->ppos <= ctx->path_match_len)
|
||||
|
@ -609,8 +643,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
}
|
||||
/* pop */
|
||||
ctx->sp--;
|
||||
ctx->ppos = ctx->st[ctx->sp - 1].p;
|
||||
ctx->ipos = ctx->st[ctx->sp - 1].i;
|
||||
if (ctx->sp) {
|
||||
ctx->ppos = ctx->st[ctx->sp - 1].p;
|
||||
ctx->ipos = ctx->st[ctx->sp - 1].i;
|
||||
}
|
||||
ctx->path[ctx->ppos] = '\0';
|
||||
if (ctx->path_match &&
|
||||
ctx->ppos <= ctx->path_match_len)
|
||||
|
@ -631,6 +667,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
|
|||
goto reject;
|
||||
|
||||
case LEJP_MP_ARRAY_END:
|
||||
array_end:
|
||||
ctx->path[ctx->ppos] = '\0';
|
||||
if (c == ',') {
|
||||
/* increment this stack level's index */
|
294
lib/misc/lws-ring.c
Normal file
294
lib/misc/lws-ring.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* libwebsockets - lws-ring multi-tail abstract ringbuffer api
|
||||
*
|
||||
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_ring *
|
||||
lws_ring_create(size_t element_len, size_t count,
|
||||
void (*destroy_element)(void *))
|
||||
{
|
||||
struct lws_ring *ring = lws_malloc(sizeof(*ring), "ring create");
|
||||
|
||||
if (!ring)
|
||||
return NULL;
|
||||
|
||||
ring->buflen = (uint32_t)(count * element_len);
|
||||
ring->element_len = (uint32_t)element_len;
|
||||
ring->head = 0;
|
||||
ring->oldest_tail = 0;
|
||||
ring->destroy_element = destroy_element;
|
||||
|
||||
ring->buf = lws_malloc(ring->buflen, "ring buf");
|
||||
if (!ring->buf) {
|
||||
lws_free(ring);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ring_destroy(struct lws_ring *ring)
|
||||
{
|
||||
if (ring->destroy_element)
|
||||
while (ring->oldest_tail != ring->head) {
|
||||
ring->destroy_element((uint8_t *)ring->buf +
|
||||
ring->oldest_tail);
|
||||
ring->oldest_tail =
|
||||
(ring->oldest_tail + ring->element_len) %
|
||||
ring->buflen;
|
||||
}
|
||||
if (ring->buf)
|
||||
lws_free_set_NULL(ring->buf);
|
||||
|
||||
lws_free(ring);
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_ring_get_count_free_elements(struct lws_ring *ring)
|
||||
{
|
||||
int f;
|
||||
|
||||
/*
|
||||
* possible ringbuf patterns
|
||||
*
|
||||
* h == t
|
||||
* |--------t***h---|
|
||||
* |**h-----------t*|
|
||||
* |t**************h|
|
||||
* |*****ht*********|
|
||||
*/
|
||||
if (ring->head == ring->oldest_tail)
|
||||
f = ring->buflen - ring->element_len;
|
||||
else
|
||||
if (ring->head < ring->oldest_tail)
|
||||
f = (ring->oldest_tail - ring->head) -
|
||||
ring->element_len;
|
||||
else
|
||||
f = (ring->buflen - ring->head) + ring->oldest_tail -
|
||||
ring->element_len;
|
||||
|
||||
if (f < 2)
|
||||
return 0;
|
||||
|
||||
return f / ring->element_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail)
|
||||
{ int f;
|
||||
|
||||
if (!tail)
|
||||
tail = &ring->oldest_tail;
|
||||
/*
|
||||
* possible ringbuf patterns
|
||||
*
|
||||
* h == t
|
||||
* |--------t***h---|
|
||||
* |**h-----------t*|
|
||||
* |t**************h|
|
||||
* |*****ht*********|
|
||||
*/
|
||||
if (ring->head == *tail)
|
||||
f = 0;
|
||||
else
|
||||
if (ring->head > *tail)
|
||||
f = (ring->head - *tail);
|
||||
else
|
||||
f = (ring->buflen - *tail) + ring->head;
|
||||
|
||||
return f / ring->element_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
|
||||
size_t *bytes)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* n is how many bytes the whole fifo can take */
|
||||
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
|
||||
|
||||
if (!n)
|
||||
return 1;
|
||||
|
||||
if (ring->head + n > ring->buflen) {
|
||||
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
|
||||
*bytes = ring->buflen - ring->head;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
|
||||
*bytes = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ring_bump_head(struct lws_ring *ring, size_t bytes)
|
||||
{
|
||||
ring->head = (ring->head + (uint32_t)bytes) % ring->buflen;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count)
|
||||
{
|
||||
const uint8_t *osrc = src;
|
||||
int m, n;
|
||||
|
||||
/* n is how many bytes the whole fifo can take */
|
||||
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
|
||||
|
||||
/* restrict n to how much we want to insert */
|
||||
if ((uint32_t)n > max_count * ring->element_len)
|
||||
n = (int)(max_count * ring->element_len);
|
||||
|
||||
/*
|
||||
* n is legal to insert, but as an optimization we can cut the
|
||||
* insert into one or two memcpys, depending on if it wraps
|
||||
*/
|
||||
if (ring->head + n > ring->buflen) {
|
||||
|
||||
/*
|
||||
* He does wrap. The first memcpy should take us up to
|
||||
* the end of the buffer
|
||||
*/
|
||||
|
||||
m = ring->buflen - ring->head;
|
||||
memcpy(((uint8_t *)ring->buf) + ring->head, src, m);
|
||||
/* we know it will wrap exactly back to zero */
|
||||
ring->head = 0;
|
||||
|
||||
/* adapt the second memcpy for what we already did */
|
||||
|
||||
src = ((uint8_t *)src) + m;
|
||||
n -= m;
|
||||
}
|
||||
|
||||
memcpy(((uint8_t *)ring->buf) + ring->head, src, n);
|
||||
ring->head = (ring->head + n) % ring->buflen;
|
||||
|
||||
return (((uint8_t *)src + n) - osrc) / ring->element_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN size_t
|
||||
lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
|
||||
size_t max_count)
|
||||
{
|
||||
uint8_t *odest = dest;
|
||||
void *orig_tail = tail;
|
||||
uint32_t fake_tail;
|
||||
int m, n;
|
||||
|
||||
if (!tail) {
|
||||
fake_tail = ring->oldest_tail;
|
||||
tail = &fake_tail;
|
||||
}
|
||||
|
||||
/* n is how many bytes the whole fifo has for us */
|
||||
n = (int)(lws_ring_get_count_waiting_elements(ring, tail) *
|
||||
ring->element_len);
|
||||
|
||||
/* restrict n to how much we want to insert */
|
||||
if ((size_t)n > max_count * ring->element_len)
|
||||
n = (int)(max_count * ring->element_len);
|
||||
|
||||
if (!dest) {
|
||||
*tail = ((*tail) + n) % ring->buflen;
|
||||
if (!orig_tail) /* single tail */
|
||||
lws_ring_update_oldest_tail(ring, *tail);
|
||||
|
||||
return n / ring->element_len;
|
||||
}
|
||||
if (*tail + n > ring->buflen) {
|
||||
|
||||
/*
|
||||
* He does wrap. The first memcpy should take us up to
|
||||
* the end of the buffer
|
||||
*/
|
||||
|
||||
m = ring->buflen - *tail;
|
||||
memcpy(dest, ((uint8_t *)ring->buf) + *tail, m);
|
||||
/* we know it will wrap exactly back to zero */
|
||||
*tail = 0;
|
||||
|
||||
/* adapt the second memcpy for what we already did */
|
||||
|
||||
dest = ((uint8_t *)dest) + m;
|
||||
n -= m;
|
||||
}
|
||||
|
||||
memcpy(dest, ((uint8_t *)ring->buf) + *tail, n);
|
||||
|
||||
*tail = ((*tail) + n) % ring->buflen;
|
||||
if (!orig_tail) /* single tail */
|
||||
lws_ring_update_oldest_tail(ring, *tail);
|
||||
|
||||
return (((uint8_t *)dest + n) - odest) / ring->element_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const void *
|
||||
lws_ring_get_element(struct lws_ring *ring, uint32_t *tail)
|
||||
{
|
||||
if (!tail)
|
||||
tail = &ring->oldest_tail;
|
||||
|
||||
if (*tail == ring->head)
|
||||
return NULL;
|
||||
|
||||
return ((uint8_t *)ring->buf) + *tail;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail)
|
||||
{
|
||||
if (!ring->destroy_element) {
|
||||
ring->oldest_tail = tail;
|
||||
return;
|
||||
}
|
||||
|
||||
while (ring->oldest_tail != tail) {
|
||||
ring->destroy_element((uint8_t *)ring->buf + ring->oldest_tail);
|
||||
ring->oldest_tail = (ring->oldest_tail + ring->element_len) %
|
||||
ring->buflen;
|
||||
}
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN uint32_t
|
||||
lws_ring_get_oldest_tail(struct lws_ring *ring)
|
||||
{
|
||||
return ring->oldest_tail;
|
||||
}
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_ring_dump(struct lws_ring *ring, uint32_t *tail)
|
||||
{
|
||||
if (tail == NULL)
|
||||
tail = &ring->oldest_tail;
|
||||
lwsl_notice("ring %p: buflen %u, elem_len %u, head %u, oldest_tail %u\n"
|
||||
" free_elems: %u; for tail %u, waiting elements: %u\n",
|
||||
ring, ring->buflen, ring->element_len, ring->head,
|
||||
ring->oldest_tail,
|
||||
(int)lws_ring_get_count_free_elements(ring), *tail,
|
||||
(int)lws_ring_get_count_waiting_elements(ring, tail));
|
||||
}
|
281
lib/misc/peer-limits.c
Normal file
281
lib/misc/peer-limits.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* libwebsockets - peer limits tracking
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "core/private.h"
|
||||
|
||||
/* requires context->lock */
|
||||
static void
|
||||
__lws_peer_remove_from_peer_wait_list(struct lws_context *context,
|
||||
struct lws_peer *peer)
|
||||
{
|
||||
struct lws_peer *df;
|
||||
|
||||
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
|
||||
if (*p == peer) {
|
||||
df = *p;
|
||||
|
||||
*p = df->peer_wait_list;
|
||||
df->peer_wait_list = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
} lws_end_foreach_llp(p, peer_wait_list);
|
||||
}
|
||||
|
||||
/* requires context->lock */
|
||||
static void
|
||||
__lws_peer_add_to_peer_wait_list(struct lws_context *context,
|
||||
struct lws_peer *peer)
|
||||
{
|
||||
__lws_peer_remove_from_peer_wait_list(context, peer);
|
||||
|
||||
peer->peer_wait_list = context->peer_wait_list;
|
||||
context->peer_wait_list = peer;
|
||||
}
|
||||
|
||||
|
||||
struct lws_peer *
|
||||
lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
|
||||
{
|
||||
struct lws_context *context = vhost->context;
|
||||
socklen_t rlen = 0;
|
||||
void *q;
|
||||
uint8_t *q8;
|
||||
struct lws_peer *peer;
|
||||
uint32_t hash = 0;
|
||||
int n, af = AF_INET;
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (LWS_IPV6_ENABLED(vhost)) {
|
||||
af = AF_INET6;
|
||||
}
|
||||
#endif
|
||||
rlen = sizeof(addr);
|
||||
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen))
|
||||
/* eg, udp doesn't have to have a peer */
|
||||
return NULL;
|
||||
|
||||
if (af == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
q = &s->sin_addr;
|
||||
rlen = sizeof(s->sin_addr);
|
||||
} else
|
||||
#ifdef LWS_WITH_IPV6
|
||||
{
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
||||
q = &s->sin6_addr;
|
||||
rlen = sizeof(s->sin6_addr);
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
q8 = q;
|
||||
for (n = 0; n < (int)rlen; n++)
|
||||
hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n];
|
||||
|
||||
hash = hash % context->pl_hash_elements;
|
||||
|
||||
lws_context_lock(context); /* <====================================== */
|
||||
|
||||
lws_start_foreach_ll(struct lws_peer *, peerx,
|
||||
context->pl_hash_table[hash]) {
|
||||
if (peerx->af == af && !memcmp(q, peerx->addr, rlen)) {
|
||||
lws_context_unlock(context); /* === */
|
||||
return peerx;
|
||||
}
|
||||
} lws_end_foreach_ll(peerx, next);
|
||||
|
||||
lwsl_info("%s: creating new peer\n", __func__);
|
||||
|
||||
peer = lws_zalloc(sizeof(*peer), "peer");
|
||||
if (!peer) {
|
||||
lws_context_unlock(context); /* === */
|
||||
lwsl_err("%s: OOM for new peer\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->count_peers++;
|
||||
peer->next = context->pl_hash_table[hash];
|
||||
peer->hash = hash;
|
||||
peer->af = af;
|
||||
context->pl_hash_table[hash] = peer;
|
||||
memcpy(peer->addr, q, rlen);
|
||||
time(&peer->time_created);
|
||||
/*
|
||||
* On creation, the peer has no wsi attached, so is created on the
|
||||
* wait list. When a wsi is added it is removed from the wait list.
|
||||
*/
|
||||
time(&peer->time_closed_all);
|
||||
__lws_peer_add_to_peer_wait_list(context, peer);
|
||||
|
||||
lws_context_unlock(context); /* ====================================> */
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
/* requires context->lock */
|
||||
static int
|
||||
__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer)
|
||||
{
|
||||
lws_start_foreach_llp(struct lws_peer **, p,
|
||||
context->pl_hash_table[peer->hash]) {
|
||||
if (*p == peer) {
|
||||
struct lws_peer *df = *p;
|
||||
*p = df->next;
|
||||
lws_free(df);
|
||||
context->count_peers--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
} lws_end_foreach_llp(p, next);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
lws_peer_cull_peer_wait_list(struct lws_context *context)
|
||||
{
|
||||
struct lws_peer *df;
|
||||
time_t t;
|
||||
|
||||
time(&t);
|
||||
|
||||
if (context->next_cull && t < context->next_cull)
|
||||
return;
|
||||
|
||||
lws_context_lock(context); /* <====================================== */
|
||||
|
||||
context->next_cull = t + 5;
|
||||
|
||||
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
|
||||
if (t - (*p)->time_closed_all > 10) {
|
||||
df = *p;
|
||||
|
||||
/* remove us from the peer wait list */
|
||||
*p = df->peer_wait_list;
|
||||
df->peer_wait_list = NULL;
|
||||
|
||||
__lws_peer_destroy(context, df);
|
||||
continue; /* we already point to next, if any */
|
||||
}
|
||||
} lws_end_foreach_llp(p, peer_wait_list);
|
||||
|
||||
lws_context_unlock(context); /* ====================================> */
|
||||
}
|
||||
|
||||
void
|
||||
lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
|
||||
struct lws *wsi)
|
||||
{
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
lws_context_lock(context); /* <====================================== */
|
||||
|
||||
peer->count_wsi++;
|
||||
wsi->peer = peer;
|
||||
__lws_peer_remove_from_peer_wait_list(context, peer);
|
||||
|
||||
lws_context_unlock(context); /* ====================================> */
|
||||
}
|
||||
|
||||
void
|
||||
lws_peer_dump_from_wsi(struct lws *wsi)
|
||||
{
|
||||
struct lws_peer *peer;
|
||||
|
||||
if (!wsi || !wsi->peer)
|
||||
return;
|
||||
|
||||
peer = wsi->peer;
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d, ah %d/%d\n", __func__,
|
||||
wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi,
|
||||
peer->http.count_ah, peer->http.total_ah);
|
||||
#else
|
||||
lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d\n", __func__,
|
||||
wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
|
||||
{
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
lws_context_lock(context); /* <====================================== */
|
||||
|
||||
assert(peer->count_wsi);
|
||||
peer->count_wsi--;
|
||||
|
||||
if (!peer->count_wsi
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
&& !peer->http.count_ah
|
||||
#endif
|
||||
) {
|
||||
/*
|
||||
* in order that we can accumulate peer activity correctly
|
||||
* allowing for periods when the peer has no connections,
|
||||
* we don't synchronously destroy the peer when his last
|
||||
* wsi closes. Instead we mark the time his last wsi
|
||||
* closed and add him to a peer_wait_list to be reaped
|
||||
* later if no further activity is coming.
|
||||
*/
|
||||
time(&peer->time_closed_all);
|
||||
__lws_peer_add_to_peer_wait_list(context, peer);
|
||||
}
|
||||
|
||||
lws_context_unlock(context); /* ====================================> */
|
||||
}
|
||||
|
||||
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
|
||||
int
|
||||
lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer)
|
||||
{
|
||||
if (!peer)
|
||||
return 0;
|
||||
|
||||
if (context->ip_limit_ah && peer->http.count_ah >= context->ip_limit_ah) {
|
||||
lwsl_info("peer reached ah limit %d, deferring\n",
|
||||
context->ip_limit_ah);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
|
||||
{
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
lws_context_lock(context); /* <====================================== */
|
||||
assert(peer->http.count_ah);
|
||||
peer->http.count_ah--;
|
||||
lws_context_unlock(context); /* ====================================> */
|
||||
}
|
||||
#endif
|
|
@ -142,6 +142,9 @@ romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
|
|||
cp++;
|
||||
}
|
||||
|
||||
while (*p && *p == '/' && p[1] == '/')
|
||||
p++;
|
||||
|
||||
if (!*cp && (!*p || *p == '/') &&
|
||||
(ntohl(next_be) & 7) == RFST_HARDLINK) {
|
||||
set_cache(i, sizeof(*i));
|
||||
|
@ -162,6 +165,9 @@ romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
|
|||
if (!*p && *cp == '/')
|
||||
return NULL;
|
||||
|
||||
while (*p && *p == '/' && p[1] == '/')
|
||||
p++;
|
||||
|
||||
if (*p == '/' && !*cp) {
|
||||
set_cache(i, sizeof(*i));
|
||||
switch (ntohl(ci->next) & 7) {
|
|
@ -32,7 +32,7 @@
|
|||
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
#ifdef LWS_HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
|
@ -45,7 +45,7 @@ struct sha1_ctxt {
|
|||
} h;
|
||||
union {
|
||||
unsigned char b8[8];
|
||||
u_int64_t b64[1];
|
||||
uint64_t b64[1];
|
||||
} c;
|
||||
union {
|
||||
unsigned char b8[64];
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SMTP support for libwebsockets
|
||||
*
|
||||
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2016-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -19,7 +19,7 @@
|
|||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
static unsigned int
|
||||
lwsgs_now_secs(void)
|
||||
|
@ -93,7 +93,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
|
|||
email->estate = LGSSMTP_SENT_HELO;
|
||||
break;
|
||||
case LGSSMTP_SENT_HELO:
|
||||
n = sprintf(email->content, "MAIL FROM: <%s>\n", email->email_from);
|
||||
n = sprintf(email->content, "MAIL FROM: <%s>\n",
|
||||
email->email_from);
|
||||
email->estate = LGSSMTP_SENT_FROM;
|
||||
break;
|
||||
case LGSSMTP_SENT_FROM:
|
||||
|
@ -105,7 +106,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
|
|||
email->estate = LGSSMTP_SENT_DATA;
|
||||
break;
|
||||
case LGSSMTP_SENT_DATA:
|
||||
if (email->on_get_body(email, email->content, email->max_content_size))
|
||||
if (email->on_get_body(email, email->content,
|
||||
email->max_content_size))
|
||||
return;
|
||||
n = strlen(email->content);
|
||||
email->estate = LGSSMTP_SENT_BODY;
|
||||
|
@ -208,7 +210,7 @@ uv_timeout_cb_email(uv_timer_t *w
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content)
|
||||
{
|
||||
email->content = lws_malloc(max_content);
|
||||
email->content = lws_malloc(max_content, "email content");
|
||||
if (!email->content)
|
||||
return 1;
|
||||
|
||||
|
@ -238,4 +240,3 @@ lws_email_destroy(struct lws_email *email)
|
|||
uv_timer_stop(&email->timeout_email);
|
||||
uv_close((uv_handle_t *)&email->timeout_email, NULL);
|
||||
}
|
||||
|
871
lib/output.c
871
lib/output.c
|
@ -1,871 +0,0 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
static int
|
||||
lws_0405_frame_mask_generate(struct lws *wsi)
|
||||
{
|
||||
#if 0
|
||||
wsi->u.ws.mask[0] = 0;
|
||||
wsi->u.ws.mask[1] = 0;
|
||||
wsi->u.ws.mask[2] = 0;
|
||||
wsi->u.ws.mask[3] = 0;
|
||||
#else
|
||||
int n;
|
||||
/* fetch the per-frame nonce */
|
||||
|
||||
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
|
||||
if (n != 4) {
|
||||
lwsl_parser("Unable to read from random device %s %d\n",
|
||||
SYSTEM_RANDOM_FILEPATH, n);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
/* start masking from first byte of masking key buffer */
|
||||
wsi->u.ws.mask_idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len)
|
||||
{
|
||||
unsigned char *buf = (unsigned char *)vbuf;
|
||||
unsigned int n, m, start;
|
||||
char line[80];
|
||||
char *p;
|
||||
|
||||
lwsl_parser("\n");
|
||||
|
||||
for (n = 0; n < len;) {
|
||||
start = n;
|
||||
p = line;
|
||||
|
||||
p += sprintf(p, "%04X: ", start);
|
||||
|
||||
for (m = 0; m < 16 && n < len; m++)
|
||||
p += sprintf(p, "%02X ", buf[n++]);
|
||||
while (m++ < 16)
|
||||
p += sprintf(p, " ");
|
||||
|
||||
p += sprintf(p, " ");
|
||||
|
||||
for (m = 0; m < 16 && (start + m) < len; m++) {
|
||||
if (buf[start + m] >= ' ' && buf[start + m] < 127)
|
||||
*p++ = buf[start + m];
|
||||
else
|
||||
*p++ = '.';
|
||||
}
|
||||
while (m++ < 16)
|
||||
*p++ = ' ';
|
||||
|
||||
*p++ = '\n';
|
||||
*p = '\0';
|
||||
lwsl_debug("%s", line);
|
||||
}
|
||||
lwsl_debug("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* notice this returns number of bytes consumed, or -1
|
||||
*/
|
||||
|
||||
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
||||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
size_t real_len = len;
|
||||
unsigned int n;
|
||||
int m;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
/* just ignore sends after we cleared the truncation buffer */
|
||||
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
|
||||
!wsi->trunc_len)
|
||||
return len;
|
||||
|
||||
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
|
||||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
|
||||
char dump[20];
|
||||
strncpy(dump, (char *)buf, sizeof(dump) - 1);
|
||||
dump[sizeof(dump) - 1] = '\0';
|
||||
#if defined(LWS_WITH_ESP8266)
|
||||
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n",
|
||||
wsi, (unsigned long)len, dump);
|
||||
#else
|
||||
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
|
||||
" It's illegal to do an lws_write outside of\n"
|
||||
" the writable callback: fix your code",
|
||||
wsi, (unsigned long)len, dump);
|
||||
#endif
|
||||
assert(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */ {
|
||||
n = m;
|
||||
goto handle_truncated_send;
|
||||
}
|
||||
|
||||
if (!lws_socket_is_valid(wsi->desc.sockfd))
|
||||
lwsl_warn("** error invalid sock but expected to send\n");
|
||||
|
||||
/* limit sending */
|
||||
if (wsi->protocol->tx_packet_size)
|
||||
n = wsi->protocol->tx_packet_size;
|
||||
else {
|
||||
n = wsi->protocol->rx_buffer_size;
|
||||
if (!n)
|
||||
n = context->pt_serv_buf_size;
|
||||
}
|
||||
n += LWS_PRE + 4;
|
||||
if (n > len)
|
||||
n = len;
|
||||
#if defined(LWS_WITH_ESP8266)
|
||||
if (wsi->pending_send_completion) {
|
||||
n = 0;
|
||||
goto handle_truncated_send;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* nope, send it on the socket directly */
|
||||
lws_latency_pre(context, wsi);
|
||||
n = lws_ssl_capable_write(wsi, buf, n);
|
||||
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
|
||||
|
||||
//lwsl_notice("lws_ssl_capable_write: %d\n", n);
|
||||
|
||||
switch (n) {
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
/* we're going to close, let close know sends aren't possible */
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
/* nothing got sent, not fatal, retry the whole thing later */
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
handle_truncated_send:
|
||||
/*
|
||||
* we were already handling a truncated send?
|
||||
*/
|
||||
if (wsi->trunc_len) {
|
||||
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
|
||||
wsi->trunc_offset += n;
|
||||
wsi->trunc_len -= n;
|
||||
|
||||
if (!wsi->trunc_len) {
|
||||
lwsl_info("***** %p partial send completed\n", wsi);
|
||||
/* done with it, but don't free it */
|
||||
n = real_len;
|
||||
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
|
||||
lwsl_info("***** %p signalling to close now\n", wsi);
|
||||
return -1; /* retry closing now */
|
||||
}
|
||||
}
|
||||
/* always callback on writeable */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
if ((unsigned int)n == real_len)
|
||||
/* what we just sent went out cleanly */
|
||||
return n;
|
||||
|
||||
/*
|
||||
* Newly truncated send. Buffer the remainder (it will get
|
||||
* first priority next time the socket is writable)
|
||||
*/
|
||||
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
|
||||
(unsigned long)real_len);
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
|
||||
|
||||
/*
|
||||
* - if we still have a suitable malloc lying around, use it
|
||||
* - or, if too small, reallocate it
|
||||
* - or, if no buffer, create it
|
||||
*/
|
||||
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
|
||||
lws_free(wsi->trunc_alloc);
|
||||
|
||||
wsi->trunc_alloc_len = real_len - n;
|
||||
wsi->trunc_alloc = lws_malloc(real_len - n);
|
||||
if (!wsi->trunc_alloc) {
|
||||
lwsl_err("truncated send: unable to malloc %lu\n",
|
||||
(unsigned long)(real_len - n));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
wsi->trunc_offset = 0;
|
||||
wsi->trunc_len = real_len - n;
|
||||
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
|
||||
|
||||
/* since something buffered, force it to get another chance to send */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return real_len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol wp)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
|
||||
unsigned char is_masked_bit = 0;
|
||||
unsigned char *dropmask = NULL;
|
||||
struct lws_tokens eff_buf;
|
||||
int pre = 0, n;
|
||||
size_t orig_len = len;
|
||||
|
||||
if (wsi->parent_carries_io) {
|
||||
struct lws_write_passthru pas;
|
||||
|
||||
pas.buf = buf;
|
||||
pas.len = len;
|
||||
pas.wp = wp;
|
||||
pas.wsi = wsi;
|
||||
|
||||
if (wsi->parent->protocol->callback(wsi->parent,
|
||||
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
|
||||
wsi->parent->user_space,
|
||||
(void *)&pas, 0))
|
||||
return 1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
|
||||
|
||||
if ((int)len < 0) {
|
||||
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
|
||||
(int)len, (unsigned long)len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
|
||||
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
wsi->access_log.sent += len;
|
||||
#endif
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.tx += len;
|
||||
|
||||
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
|
||||
/* remove us from the list */
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
|
||||
// lwsl_notice("%s: TX EXT DRAINING: Remove from list\n", __func__);
|
||||
wsi->u.ws.tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
if (*w == wsi) {
|
||||
*w = wsi->u.ws.tx_draining_ext_list;
|
||||
break;
|
||||
}
|
||||
w = &((*w)->u.ws.tx_draining_ext_list);
|
||||
}
|
||||
wsi->u.ws.tx_draining_ext_list = NULL;
|
||||
wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
|
||||
LWS_WRITE_CONTINUATION;
|
||||
|
||||
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
|
||||
}
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
if (wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL ||
|
||||
wp == LWS_WRITE_HTTP_HEADERS)
|
||||
goto send_raw;
|
||||
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
||||
if (wsi->state != LWSS_ESTABLISHED &&
|
||||
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
|
||||
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
|
||||
wp != LWS_WRITE_CLOSE))
|
||||
return 0;
|
||||
|
||||
/* if we are continuing a frame that already had its header done */
|
||||
|
||||
if (wsi->u.ws.inside_frame) {
|
||||
lwsl_debug("INSIDE FRAME\n");
|
||||
goto do_more_inside_frame;
|
||||
}
|
||||
|
||||
wsi->u.ws.clean_buffer = 1;
|
||||
|
||||
/*
|
||||
* give a chance to the extensions to modify payload
|
||||
* the extension may decide to produce unlimited payload erratically
|
||||
* (eg, compression extension), so we require only that if he produces
|
||||
* something, it will be a complete fragment of the length known at
|
||||
* the time (just the fragment length known), and if he has
|
||||
* more we will come back next time he is writeable and allow him to
|
||||
* produce more fragments until he's drained.
|
||||
*
|
||||
* This allows what is sent each time it is writeable to be limited to
|
||||
* a size that can be sent without partial sends or blocking, allows
|
||||
* interleaving of control frames and other connection service.
|
||||
*/
|
||||
eff_buf.token = (char *)buf;
|
||||
eff_buf.token_len = len;
|
||||
|
||||
switch ((int)wp) {
|
||||
case LWS_WRITE_PING:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_CLOSE:
|
||||
break;
|
||||
default:
|
||||
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
|
||||
if (n && eff_buf.token_len) {
|
||||
lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
|
||||
/* extension requires further draining */
|
||||
wsi->u.ws.tx_draining_ext = 1;
|
||||
wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list;
|
||||
pt->tx_draining_ext_list = wsi;
|
||||
/* we must come back to do more */
|
||||
lws_callback_on_writable(wsi);
|
||||
/*
|
||||
* keep a copy of the write type for the overall
|
||||
* action that has provoked generation of these
|
||||
* fragments, so the last guy can use its FIN state.
|
||||
*/
|
||||
wsi->u.ws.tx_draining_stashed_wp = wp;
|
||||
/* this is definitely not actually the last fragment
|
||||
* because the extension asserted he has more coming
|
||||
* So make sure this intermediate one doesn't go out
|
||||
* with a FIN.
|
||||
*/
|
||||
wp |= LWS_WRITE_NO_FIN;
|
||||
}
|
||||
|
||||
if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
|
||||
wsi->u.ws.stashed_write_pending = 0;
|
||||
wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* an extension did something we need to keep... for example, if
|
||||
* compression extension, it has already updated its state according
|
||||
* to this being issued
|
||||
*/
|
||||
if ((char *)buf != eff_buf.token) {
|
||||
/*
|
||||
* ext might eat it, but not have anything to issue yet.
|
||||
* In that case we have to follow his lead, but stash and
|
||||
* replace the write type that was lost here the first time.
|
||||
*/
|
||||
if (len && !eff_buf.token_len) {
|
||||
if (!wsi->u.ws.stashed_write_pending)
|
||||
wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
|
||||
wsi->u.ws.stashed_write_pending = 1;
|
||||
return len;
|
||||
}
|
||||
/*
|
||||
* extension recreated it:
|
||||
* need to buffer this if not all sent
|
||||
*/
|
||||
wsi->u.ws.clean_buffer = 0;
|
||||
}
|
||||
|
||||
buf = (unsigned char *)eff_buf.token;
|
||||
len = eff_buf.token_len;
|
||||
|
||||
lwsl_debug("%p / %d\n", buf, (int)len);
|
||||
|
||||
if (!buf) {
|
||||
lwsl_err("null buf (%d)\n", (int)len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (wsi->ietf_spec_revision) {
|
||||
case 13:
|
||||
if (masked7) {
|
||||
pre += 4;
|
||||
dropmask = &buf[0 - pre];
|
||||
is_masked_bit = 0x80;
|
||||
}
|
||||
|
||||
switch (wp & 0xf) {
|
||||
case LWS_WRITE_TEXT:
|
||||
n = LWSWSOPC_TEXT_FRAME;
|
||||
break;
|
||||
case LWS_WRITE_BINARY:
|
||||
n = LWSWSOPC_BINARY_FRAME;
|
||||
break;
|
||||
case LWS_WRITE_CONTINUATION:
|
||||
n = LWSWSOPC_CONTINUATION;
|
||||
break;
|
||||
|
||||
case LWS_WRITE_CLOSE:
|
||||
n = LWSWSOPC_CLOSE;
|
||||
break;
|
||||
case LWS_WRITE_PING:
|
||||
n = LWSWSOPC_PING;
|
||||
break;
|
||||
case LWS_WRITE_PONG:
|
||||
n = LWSWSOPC_PONG;
|
||||
break;
|
||||
default:
|
||||
lwsl_warn("lws_write: unknown write opc / wp\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(wp & LWS_WRITE_NO_FIN))
|
||||
n |= 1 << 7;
|
||||
|
||||
if (len < 126) {
|
||||
pre += 2;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
|
||||
} else {
|
||||
if (len < 65536) {
|
||||
pre += 4;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = 126 | is_masked_bit;
|
||||
buf[-pre + 2] = (unsigned char)(len >> 8);
|
||||
buf[-pre + 3] = (unsigned char)len;
|
||||
} else {
|
||||
pre += 10;
|
||||
buf[-pre] = n;
|
||||
buf[-pre + 1] = 127 | is_masked_bit;
|
||||
#if defined __LP64__
|
||||
buf[-pre + 2] = (len >> 56) & 0x7f;
|
||||
buf[-pre + 3] = len >> 48;
|
||||
buf[-pre + 4] = len >> 40;
|
||||
buf[-pre + 5] = len >> 32;
|
||||
#else
|
||||
buf[-pre + 2] = 0;
|
||||
buf[-pre + 3] = 0;
|
||||
buf[-pre + 4] = 0;
|
||||
buf[-pre + 5] = 0;
|
||||
#endif
|
||||
buf[-pre + 6] = (unsigned char)(len >> 24);
|
||||
buf[-pre + 7] = (unsigned char)(len >> 16);
|
||||
buf[-pre + 8] = (unsigned char)(len >> 8);
|
||||
buf[-pre + 9] = (unsigned char)len;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
do_more_inside_frame:
|
||||
|
||||
/*
|
||||
* Deal with masking if we are in client -> server direction and
|
||||
* the wp demands it
|
||||
*/
|
||||
|
||||
if (masked7) {
|
||||
if (!wsi->u.ws.inside_frame)
|
||||
if (lws_0405_frame_mask_generate(wsi)) {
|
||||
lwsl_err("frame mask generation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* in v7, just mask the payload
|
||||
*/
|
||||
if (dropmask) { /* never set if already inside frame */
|
||||
for (n = 4; n < (int)len + 4; n++)
|
||||
dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
|
||||
(wsi->u.ws.mask_idx++) & 3];
|
||||
|
||||
/* copy the frame nonce into place */
|
||||
memcpy(dropmask, wsi->u.ws.mask, 4);
|
||||
}
|
||||
}
|
||||
|
||||
send_raw:
|
||||
switch ((int)wp) {
|
||||
case LWS_WRITE_CLOSE:
|
||||
/* lwsl_hexdump(&buf[-pre], len); */
|
||||
case LWS_WRITE_HTTP:
|
||||
case LWS_WRITE_HTTP_FINAL:
|
||||
case LWS_WRITE_HTTP_HEADERS:
|
||||
case LWS_WRITE_PONG:
|
||||
case LWS_WRITE_PING:
|
||||
#ifdef LWS_USE_HTTP2
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING) {
|
||||
unsigned char flags = 0;
|
||||
|
||||
n = LWS_HTTP2_FRAME_TYPE_DATA;
|
||||
if (wp == LWS_WRITE_HTTP_HEADERS) {
|
||||
n = LWS_HTTP2_FRAME_TYPE_HEADERS;
|
||||
flags = LWS_HTTP2_FLAG_END_HEADERS;
|
||||
if (wsi->u.http2.send_END_STREAM)
|
||||
flags |= LWS_HTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
|
||||
if ((wp == LWS_WRITE_HTTP ||
|
||||
wp == LWS_WRITE_HTTP_FINAL) &&
|
||||
wsi->u.http.content_length) {
|
||||
wsi->u.http.content_remain -= len;
|
||||
lwsl_info("%s: content_remain = %llu\n", __func__,
|
||||
(unsigned long long)wsi->u.http.content_remain);
|
||||
if (!wsi->u.http.content_remain) {
|
||||
lwsl_info("%s: selecting final write mode\n", __func__);
|
||||
wp = LWS_WRITE_HTTP_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
|
||||
lwsl_info("%s: setting END_STREAM\n", __func__);
|
||||
flags |= LWS_HTTP2_FLAG_END_STREAM;
|
||||
}
|
||||
|
||||
return lws_http2_frame_write(wsi, n, flags,
|
||||
wsi->u.http2.my_stream_id, len, buf);
|
||||
}
|
||||
#endif
|
||||
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* give any active extensions a chance to munge the buffer
|
||||
* before send. We pass in a pointer to an lws_tokens struct
|
||||
* prepared with the default buffer and content length that's in
|
||||
* there. Rather than rewrite the default buffer, extensions
|
||||
* that expect to grow the buffer can adapt .token to
|
||||
* point to their own per-connection buffer in the extension
|
||||
* user allocation. By default with no extensions or no
|
||||
* extension callback handling, just the normal input buffer is
|
||||
* used then so it is efficient.
|
||||
*
|
||||
* callback returns 1 in case it wants to spill more buffers
|
||||
*
|
||||
* This takes care of holding the buffer if send is incomplete, ie,
|
||||
* if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
|
||||
* the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
|
||||
* return to the user code how much OF THE USER BUFFER was consumed.
|
||||
*/
|
||||
|
||||
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
|
||||
wsi->u.ws.inside_frame = 1;
|
||||
if (n <= 0)
|
||||
return n;
|
||||
|
||||
if (n == (int)len + pre) {
|
||||
/* everything in the buffer was handled (or rebuffered...) */
|
||||
wsi->u.ws.inside_frame = 0;
|
||||
return orig_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* it is how many bytes of user buffer got sent... may be < orig_len
|
||||
* in which case callback when writable has already been arranged
|
||||
* and user code can call lws_write() again with the rest
|
||||
* later.
|
||||
*/
|
||||
|
||||
return n - pre;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
struct lws_process_html_args args;
|
||||
lws_filepos_t amount, poss;
|
||||
unsigned char *p;
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
unsigned char finished = 0;
|
||||
#endif
|
||||
int n, m;
|
||||
|
||||
// lwsl_notice("%s (trunc len %d)\n", __func__, wsi->trunc_len);
|
||||
|
||||
while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) {
|
||||
|
||||
if (wsi->trunc_len) {
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
||||
wsi->trunc_offset,
|
||||
wsi->trunc_len) < 0) {
|
||||
lwsl_info("%s: closing\n", __func__);
|
||||
goto file_had_it;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wsi->u.http.filepos == wsi->u.http.filelen)
|
||||
goto all_sent;
|
||||
|
||||
n = 0;
|
||||
|
||||
p = pt->serv_buf;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
|
||||
|
||||
lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start);
|
||||
|
||||
if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
|
||||
wsi->u.http.range.start -
|
||||
wsi->u.http.filepos) < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->u.http.filepos = wsi->u.http.range.start;
|
||||
|
||||
if (wsi->u.http.range.count_ranges > 1) {
|
||||
n = lws_snprintf((char *)p, context->pt_serv_buf_size,
|
||||
"_lws\x0d\x0a"
|
||||
"Content-Type: %s\x0d\x0a"
|
||||
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
|
||||
"\x0d\x0a",
|
||||
wsi->u.http.multipart_content_type,
|
||||
wsi->u.http.range.start,
|
||||
wsi->u.http.range.end,
|
||||
wsi->u.http.range.extent);
|
||||
p += n;
|
||||
}
|
||||
|
||||
wsi->u.http.range.budget = wsi->u.http.range.end -
|
||||
wsi->u.http.range.start + 1;
|
||||
wsi->u.http.range.inside = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
poss = context->pt_serv_buf_size - n;
|
||||
|
||||
/*
|
||||
* if there is a hint about how much we will do well to send at one time,
|
||||
* restrict ourselves to only trying to send that.
|
||||
*/
|
||||
if (wsi->protocol->tx_packet_size && poss > wsi->protocol->tx_packet_size)
|
||||
poss = wsi->protocol->tx_packet_size;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.count_ranges) {
|
||||
if (wsi->u.http.range.count_ranges > 1)
|
||||
poss -= 7; /* allow for final boundary */
|
||||
if (poss > wsi->u.http.range.budget)
|
||||
poss = wsi->u.http.range.budget;
|
||||
}
|
||||
#endif
|
||||
if (wsi->sending_chunked) {
|
||||
/* we need to drop the chunk size in here */
|
||||
p += 10;
|
||||
/* allow for the chunk to grow by 128 in translation */
|
||||
poss -= 10 + 128;
|
||||
}
|
||||
|
||||
if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
|
||||
goto file_had_it; /* caller will close */
|
||||
|
||||
//lwsl_notice("amount %ld\n", amount);
|
||||
|
||||
if (wsi->sending_chunked)
|
||||
n = (int)amount;
|
||||
else
|
||||
n = (p - pt->serv_buf) + (int)amount;
|
||||
if (n) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
context->timeout_secs);
|
||||
|
||||
if (wsi->sending_chunked) {
|
||||
args.p = (char *)p;
|
||||
args.len = n;
|
||||
args.max_len = (unsigned int)poss + 128;
|
||||
args.final = wsi->u.http.filepos + n ==
|
||||
wsi->u.http.filelen;
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
|
||||
LWS_CALLBACK_PROCESS_HTML,
|
||||
wsi->user_space, &args, 0) < 0)
|
||||
goto file_had_it;
|
||||
n = args.len;
|
||||
p = (unsigned char *)args.p;
|
||||
} else
|
||||
p = pt->serv_buf;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.send_ctr + 1 ==
|
||||
wsi->u.http.range.count_ranges && // last range
|
||||
wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
|
||||
wsi->u.http.range.budget - amount == 0) {// final part
|
||||
n += lws_snprintf((char *)pt->serv_buf + n, 6,
|
||||
"_lws\x0d\x0a"); // append trailing boundary
|
||||
lwsl_debug("added trailing boundary\n");
|
||||
}
|
||||
#endif
|
||||
m = lws_write(wsi, p, n,
|
||||
wsi->u.http.filepos == wsi->u.http.filelen ?
|
||||
LWS_WRITE_HTTP_FINAL :
|
||||
LWS_WRITE_HTTP
|
||||
);
|
||||
if (m < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->u.http.filepos += amount;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->u.http.range.count_ranges >= 1) {
|
||||
wsi->u.http.range.budget -= amount;
|
||||
if (wsi->u.http.range.budget == 0) {
|
||||
lwsl_notice("range budget exhausted\n");
|
||||
wsi->u.http.range.inside = 0;
|
||||
wsi->u.http.range.send_ctr++;
|
||||
|
||||
if (lws_ranges_next(&wsi->u.http.range) < 1) {
|
||||
finished = 1;
|
||||
goto all_sent;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m != n) {
|
||||
/* adjust for what was not sent */
|
||||
if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
|
||||
m - n) ==
|
||||
(unsigned long)-1)
|
||||
goto file_had_it;
|
||||
}
|
||||
}
|
||||
all_sent:
|
||||
if ((!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen)
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
|| finished)
|
||||
#else
|
||||
)
|
||||
#endif
|
||||
{
|
||||
wsi->state = LWSS_HTTP;
|
||||
/* we might be in keepalive, so close it off here */
|
||||
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
||||
|
||||
lwsl_debug("file completed\n");
|
||||
|
||||
if (wsi->protocol->callback)
|
||||
/* ignore callback returned value */
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL, 0) < 0)
|
||||
return -1;
|
||||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
}
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0; /* indicates further processing must be done */
|
||||
|
||||
file_had_it:
|
||||
lws_vfs_file_close(&wsi->u.http.fop_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if LWS_POSIX
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
int n;
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
|
||||
if (n >= 0) {
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
return n;
|
||||
}
|
||||
#if LWS_POSIX
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK ||
|
||||
LWS_ERRNO == LWS_EINTR)
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
#endif
|
||||
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
#if LWS_POSIX
|
||||
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
|
||||
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
|
||||
if (n >= 0)
|
||||
return n;
|
||||
|
||||
if (LWS_ERRNO == LWS_EAGAIN ||
|
||||
LWS_ERRNO == LWS_EWOULDBLOCK ||
|
||||
LWS_ERRNO == LWS_EINTR) {
|
||||
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
|
||||
lws_set_blocking_send(wsi);
|
||||
}
|
||||
|
||||
return LWS_SSL_CAPABLE_MORE_SERVICE;
|
||||
}
|
||||
#else
|
||||
(void)n;
|
||||
(void)wsi;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
// !!!
|
||||
#endif
|
||||
|
||||
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", len, wsi->desc.sockfd, n, LWS_ERRNO);
|
||||
return LWS_SSL_CAPABLE_ERROR;
|
||||
}
|
||||
#endif
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_pending_no_ssl(struct lws *wsi)
|
||||
{
|
||||
(void)wsi;
|
||||
#if defined(LWS_WITH_ESP32)
|
||||
return 100;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
1634
lib/parsers.c
1634
lib/parsers.c
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,32 @@
|
|||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
/*
|
||||
* included from libwebsockets.c for OPTEE builds
|
||||
*/
|
||||
|
||||
int
|
||||
lws_plat_socket_offset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_plat_pipe_create(struct lws *wsi)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_plat_pipe_signal(struct lws *wsi)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
lws_plat_pipe_close(struct lws *wsi)
|
||||
{
|
||||
}
|
||||
|
||||
void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen);
|
||||
|
||||
unsigned long long time_in_microseconds(void)
|
||||
|
@ -22,6 +45,19 @@ lws_get_random(struct lws_context *context, void *buf, int len)
|
|||
LWS_VISIBLE int
|
||||
lws_send_pipe_choked(struct lws *wsi)
|
||||
{
|
||||
struct lws *wsi_eff = wsi;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
wsi_eff = lws_get_network_wsi(wsi);
|
||||
#endif
|
||||
|
||||
/* the fact we checked implies we avoided back-to-back writes */
|
||||
wsi_eff->could_have_pending = 0;
|
||||
|
||||
/* treat the fact we got a truncated send pending as if we're choked */
|
||||
if (wsi_eff->trunc_len)
|
||||
return 1;
|
||||
|
||||
#if 0
|
||||
struct lws_pollfd fds;
|
||||
|
||||
|
@ -52,32 +88,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service_pt(struct lws *wsi)
|
||||
{
|
||||
#if 0
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char buf = 0;
|
||||
|
||||
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
|
||||
lwsl_err("Cannot write to dummy pipe");
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service(struct lws_context *context)
|
||||
{
|
||||
#if 0
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
char buf = 0, m = context->count_threads;
|
||||
|
||||
while (m--) {
|
||||
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
|
||||
lwsl_err("Cannot write to dummy pipe");
|
||||
pt++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
||||
{
|
||||
|
@ -111,8 +121,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
context->service_tid_detected =
|
||||
context->vhost_list->protocols[0].callback(
|
||||
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
context->service_tid = context->service_tid_detected;
|
||||
context->service_tid_detected = 1;
|
||||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
|
@ -126,19 +137,20 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
/* yes... come back again quickly */
|
||||
timeout_ms = 0;
|
||||
}
|
||||
#if 1
|
||||
|
||||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
#endif
|
||||
m = 0;
|
||||
|
||||
if (pt->context->tls_ops &&
|
||||
pt->context->tls_ops->fake_POLLIN_for_buffered)
|
||||
m = pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
|
||||
|
||||
if (/*!pt->ws.rx_draining_ext_list && */!m && !n) { /* nothing to do */
|
||||
lws_service_fd_tsi(context, NULL, tsi);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
faked_service:
|
||||
m = lws_service_flag_pending(context, tsi);
|
||||
if (m)
|
||||
|
@ -152,7 +164,7 @@ faked_service:
|
|||
c = n;
|
||||
|
||||
/* any socket with events to service? */
|
||||
for (n = 0; n < pt->fds_count && c; n++) {
|
||||
for (n = 0; n < (int)pt->fds_count && c; n++) {
|
||||
if (!pt->fds[n].revents)
|
||||
continue;
|
||||
|
||||
|
@ -194,7 +206,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
||||
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -269,8 +281,8 @@ lws_plat_inet_pton(int af, const char *src, void *dst)
|
|||
}
|
||||
|
||||
LWS_VISIBLE lws_fop_fd_t
|
||||
_lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
|
||||
const char *filename, lws_fop_flags_t *flags)
|
||||
_lws_plat_file_open(const struct lws_plat_file_ops *fops,
|
||||
const char *filename, const char *vpath, lws_fop_flags_t *flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -306,11 +318,11 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_init(struct lws_context *context,
|
||||
struct lws_context_creation_info *info)
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
/* master context has the global fd lookup array */
|
||||
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
|
||||
context->max_fds);
|
||||
context->max_fds, "lws_lookup");
|
||||
if (context->lws_lookup == NULL) {
|
||||
lwsl_err("OOM on lws_lookup array for %d connections\n",
|
||||
context->max_fds);
|
||||
|
@ -327,3 +339,28 @@ lws_plat_init(struct lws_context *context,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
|
||||
int len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_read_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_recommended_rsa_bits(void)
|
||||
{
|
||||
return 4096;
|
||||
}
|
|
@ -1,4 +1,26 @@
|
|||
#include "private-libwebsockets.h"
|
||||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "core/private.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
@ -8,14 +30,60 @@
|
|||
#endif
|
||||
#include <dirent.h>
|
||||
|
||||
int
|
||||
lws_plat_socket_offset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* included from libwebsockets.c for unix builds
|
||||
*/
|
||||
int
|
||||
lws_plat_pipe_create(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
#if defined(LWS_HAVE_PIPE2)
|
||||
return pipe2(pt->dummy_pipe_fds, O_NONBLOCK);
|
||||
#else
|
||||
return pipe(pt->dummy_pipe_fds);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
lws_plat_pipe_signal(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char buf = 0;
|
||||
int n;
|
||||
|
||||
n = write(pt->dummy_pipe_fds[1], &buf, 1);
|
||||
|
||||
return n != 1;
|
||||
}
|
||||
|
||||
void
|
||||
lws_plat_pipe_close(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1)
|
||||
close(pt->dummy_pipe_fds[0]);
|
||||
if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1)
|
||||
close(pt->dummy_pipe_fds[1]);
|
||||
|
||||
pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1;
|
||||
}
|
||||
|
||||
#ifdef __QNX__
|
||||
# include "netinet/tcp_var.h"
|
||||
# define TCP_KEEPINTVL TCPCTL_KEEPINTVL
|
||||
# define TCP_KEEPIDLE TCPCTL_KEEPIDLE
|
||||
# define TCP_KEEPCNT TCPCTL_KEEPCNT
|
||||
#endif
|
||||
|
||||
unsigned long long time_in_microseconds(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
|
||||
}
|
||||
|
@ -30,12 +98,20 @@ LWS_VISIBLE int
|
|||
lws_send_pipe_choked(struct lws *wsi)
|
||||
{
|
||||
struct lws_pollfd fds;
|
||||
struct lws *wsi_eff = wsi;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
wsi_eff = lws_get_network_wsi(wsi);
|
||||
#endif
|
||||
|
||||
/* the fact we checked implies we avoided back-to-back writes */
|
||||
wsi_eff->could_have_pending = 0;
|
||||
|
||||
/* treat the fact we got a truncated send pending as if we're choked */
|
||||
if (wsi->trunc_len)
|
||||
if (wsi_eff->trunc_len)
|
||||
return 1;
|
||||
|
||||
fds.fd = wsi->desc.sockfd;
|
||||
fds.fd = wsi_eff->desc.sockfd;
|
||||
fds.events = POLLOUT;
|
||||
fds.revents = 0;
|
||||
|
||||
|
@ -56,29 +132,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
|
|||
return poll(fd, 1, 0);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service_pt(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
char buf = 0;
|
||||
|
||||
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
|
||||
lwsl_err("Cannot write to dummy pipe");
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
char buf = 0, m = context->count_threads;
|
||||
|
||||
while (m--) {
|
||||
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
|
||||
lwsl_err("Cannot write to dummy pipe");
|
||||
pt++;
|
||||
}
|
||||
}
|
||||
|
||||
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
||||
{
|
||||
int syslog_level = LOG_DEBUG;
|
||||
|
@ -103,9 +156,10 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
||||
{
|
||||
volatile struct lws_foreign_thread_pollfd *ftp, *next;
|
||||
volatile struct lws_context_per_thread *vpt;
|
||||
struct lws_context_per_thread *pt;
|
||||
int n = -1, m, c;
|
||||
char buf;
|
||||
|
||||
/* stay dead once we are dead */
|
||||
|
||||
|
@ -113,15 +167,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
return 1;
|
||||
|
||||
pt = &context->pt[tsi];
|
||||
vpt = (volatile struct lws_context_per_thread *)pt;
|
||||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
|
||||
|
||||
if (timeout_ms < 0)
|
||||
goto faked_service;
|
||||
|
||||
lws_libev_run(context, tsi);
|
||||
lws_libuv_run(context, tsi);
|
||||
lws_libevent_run(context, tsi);
|
||||
if (context->event_loop_ops->run_pt)
|
||||
context->event_loop_ops->run_pt(context, tsi);
|
||||
|
||||
if (!context->service_tid_detected) {
|
||||
struct lws _lws;
|
||||
|
@ -132,8 +186,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
context->service_tid_detected =
|
||||
context->vhost_list->protocols[0].callback(
|
||||
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
|
||||
context->service_tid = context->service_tid_detected;
|
||||
context->service_tid_detected = 1;
|
||||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
|
@ -147,15 +202,73 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
timeout_ms = 0;
|
||||
}
|
||||
|
||||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
if (timeout_ms) {
|
||||
lws_pt_lock(pt, __func__);
|
||||
/* don't stay in poll wait longer than next hr timeout */
|
||||
lws_usec_t t = __lws_hrtimer_service(pt);
|
||||
if ((lws_usec_t)timeout_ms * 1000 > t)
|
||||
timeout_ms = t / 1000;
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (!pt->rx_draining_ext_list &&
|
||||
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
|
||||
#else
|
||||
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
|
||||
vpt->inside_poll = 1;
|
||||
lws_memory_barrier();
|
||||
n = poll(pt->fds, pt->fds_count, timeout_ms);
|
||||
vpt->inside_poll = 0;
|
||||
lws_memory_barrier();
|
||||
|
||||
/* Collision will be rare and brief. Just spin until it completes */
|
||||
while (vpt->foreign_spinlock)
|
||||
;
|
||||
|
||||
/*
|
||||
* At this point we are not inside a foreign thread pollfd change,
|
||||
* and we have marked ourselves as outside the poll() wait. So we
|
||||
* are the only guys that can modify the lws_foreign_thread_pollfd
|
||||
* list on the pt. Drain the list and apply the changes to the
|
||||
* affected pollfds in the correct order.
|
||||
*/
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
|
||||
ftp = vpt->foreign_pfd_list;
|
||||
//lwsl_notice("cleared list %p\n", ftp);
|
||||
while (ftp) {
|
||||
struct lws *wsi;
|
||||
struct lws_pollfd *pfd;
|
||||
|
||||
next = ftp->next;
|
||||
pfd = &vpt->fds[ftp->fd_index];
|
||||
if (lws_socket_is_valid(pfd->fd)) {
|
||||
wsi = wsi_from_fd(context, pfd->fd);
|
||||
if (wsi)
|
||||
__lws_change_pollfd(wsi, ftp->_and, ftp->_or);
|
||||
}
|
||||
lws_free((void *)ftp);
|
||||
ftp = next;
|
||||
}
|
||||
vpt->foreign_pfd_list = NULL;
|
||||
lws_memory_barrier();
|
||||
|
||||
/* we have come out of a poll wait... check the hrtimer list */
|
||||
|
||||
__lws_hrtimer_service(pt);
|
||||
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
m = 0;
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
m |= !!pt->ws.rx_draining_ext_list;
|
||||
#endif
|
||||
|
||||
if (pt->context->tls_ops &&
|
||||
pt->context->tls_ops->fake_POLLIN_for_buffered)
|
||||
m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
|
||||
|
||||
if (!m && !n) { /* nothing to do */
|
||||
lws_service_fd_tsi(context, NULL, tsi);
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -172,18 +285,12 @@ faked_service:
|
|||
c = n;
|
||||
|
||||
/* any socket with events to service? */
|
||||
for (n = 0; n < pt->fds_count && c; n++) {
|
||||
for (n = 0; n < (int)pt->fds_count && c; n++) {
|
||||
if (!pt->fds[n].revents)
|
||||
continue;
|
||||
|
||||
c--;
|
||||
|
||||
if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
|
||||
if (read(pt->fds[n].fd, &buf, 1) != 1)
|
||||
lwsl_err("Cannot read from dummy pipe.");
|
||||
continue;
|
||||
}
|
||||
|
||||
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
|
@ -192,6 +299,8 @@ faked_service:
|
|||
n--;
|
||||
}
|
||||
|
||||
lws_service_do_ripe_rxflow(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -216,7 +325,8 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
#if defined(__APPLE__) || \
|
||||
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__HAIKU__)
|
||||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
|
@ -230,7 +340,8 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
#if defined(__APPLE__) || \
|
||||
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun)
|
||||
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \
|
||||
defined(__HAIKU__)
|
||||
|
||||
/*
|
||||
* didn't find a way to set these per-socket, need to
|
||||
|
@ -238,6 +349,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
*/
|
||||
#else
|
||||
/* set the keepalive conditions we want on it too */
|
||||
|
||||
#if defined(LWS_HAVE_TCP_USER_TIMEOUT)
|
||||
optval = 1000 * (vhost->ka_time +
|
||||
(vhost->ka_interval * vhost->ka_probes));
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
#endif
|
||||
optval = vhost->ka_time;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
(const void *)&optval, optlen) < 0)
|
||||
|
@ -268,13 +387,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
|
||||
/* Disable Nagle */
|
||||
optval = 1;
|
||||
#if defined (__sun)
|
||||
#if defined (__sun) || defined(__QNX__)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
#elif !defined(__APPLE__) && \
|
||||
!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \
|
||||
!defined(__NetBSD__) && \
|
||||
!defined(__OpenBSD__)
|
||||
!defined(__OpenBSD__) && \
|
||||
!defined(__HAIKU__)
|
||||
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
#else
|
||||
|
@ -292,7 +412,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
|
|||
|
||||
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
|
||||
static void
|
||||
_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
|
||||
_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
|
||||
{
|
||||
cap_t caps;
|
||||
|
||||
|
@ -309,17 +429,17 @@ _lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
|
|||
#endif
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
||||
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
|
||||
{
|
||||
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
|
||||
int n;
|
||||
#endif
|
||||
|
||||
if (info->gid != -1)
|
||||
if (info->gid && info->gid != -1)
|
||||
if (setgid(info->gid))
|
||||
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
|
||||
|
||||
if (info->uid != -1) {
|
||||
if (info->uid && info->uid != -1) {
|
||||
struct passwd *p = getpwuid(info->uid);
|
||||
|
||||
if (p) {
|
||||
|
@ -349,7 +469,7 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
|||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
|
||||
#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
|
||||
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
|
||||
|
||||
/* libuv.c implements these in a cross-platform way */
|
||||
|
||||
|
@ -417,15 +537,15 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
|
|||
goto skip;
|
||||
}
|
||||
|
||||
plugin = lws_malloc(sizeof(*plugin));
|
||||
plugin = lws_malloc(sizeof(*plugin), "plugin");
|
||||
if (!plugin) {
|
||||
lwsl_err("OOM\n");
|
||||
goto bail;
|
||||
}
|
||||
plugin->list = context->plugin_list;
|
||||
context->plugin_list = plugin;
|
||||
strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
|
||||
plugin->name[sizeof(plugin->name) - 1] = '\0';
|
||||
lws_strncpy(plugin->name, namelist[i]->d_name,
|
||||
sizeof(plugin->name));
|
||||
plugin->l = l;
|
||||
plugin->caps = lcaps;
|
||||
context->plugin_protocol_count += lcaps.count_protocols;
|
||||
|
@ -497,7 +617,6 @@ static void
|
|||
sigabrt_handler(int x)
|
||||
{
|
||||
printf("%s\n", __func__);
|
||||
//*(char *)0 = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -508,8 +627,6 @@ lws_plat_context_early_init(void)
|
|||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
// signal(SIGABRT, sigabrt_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -521,9 +638,6 @@ lws_plat_context_early_destroy(struct lws_context *context)
|
|||
LWS_VISIBLE void
|
||||
lws_plat_context_late_destroy(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int m = context->count_threads;
|
||||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
if (context->plugin_list)
|
||||
lws_plat_plugins_destroy(context);
|
||||
|
@ -532,13 +646,6 @@ lws_plat_context_late_destroy(struct lws_context *context)
|
|||
if (context->lws_lookup)
|
||||
lws_free(context->lws_lookup);
|
||||
|
||||
while (m--) {
|
||||
if (pt->dummy_pipe_fds[0])
|
||||
close(pt->dummy_pipe_fds[0]);
|
||||
if (pt->dummy_pipe_fds[1])
|
||||
close(pt->dummy_pipe_fds[1]);
|
||||
pt++;
|
||||
}
|
||||
if (!context->fd_random)
|
||||
lwsl_err("ZERO RANDOM FD\n");
|
||||
if (context->fd_random != LWS_INVALID_FILE)
|
||||
|
@ -551,11 +658,11 @@ LWS_VISIBLE int
|
|||
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
||||
size_t addrlen)
|
||||
{
|
||||
int rc = -1;
|
||||
int rc = LWS_ITOSA_NOT_EXIST;
|
||||
|
||||
struct ifaddrs *ifr;
|
||||
struct ifaddrs *ifc;
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
|
||||
#endif
|
||||
|
||||
|
@ -564,14 +671,21 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
|||
if (!ifc->ifa_addr)
|
||||
continue;
|
||||
|
||||
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
|
||||
lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
|
||||
|
||||
if (strcmp(ifc->ifa_name, ifname))
|
||||
continue;
|
||||
|
||||
switch (ifc->ifa_addr->sa_family) {
|
||||
#if defined(AF_PACKET)
|
||||
case AF_PACKET:
|
||||
/* interface exists but is not usable */
|
||||
rc = LWS_ITOSA_NOT_USABLE;
|
||||
continue;
|
||||
#endif
|
||||
|
||||
case AF_INET:
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (ipv6) {
|
||||
/* map IPv4 to IPv6 */
|
||||
bzero((char *)&addr6->sin6_addr,
|
||||
|
@ -587,7 +701,7 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
|||
(struct sockaddr_in *)ifc->ifa_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
break;
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
case AF_INET6:
|
||||
memcpy(&addr6->sin6_addr,
|
||||
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
|
||||
|
@ -597,20 +711,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
|
|||
default:
|
||||
continue;
|
||||
}
|
||||
rc = 0;
|
||||
rc = LWS_ITOSA_USABLE;
|
||||
}
|
||||
|
||||
freeifaddrs(ifr);
|
||||
|
||||
if (rc == -1) {
|
||||
if (rc) {
|
||||
/* check if bind to IP address */
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
|
||||
rc = 0;
|
||||
rc = LWS_ITOSA_USABLE;
|
||||
else
|
||||
#endif
|
||||
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
|
||||
rc = 0;
|
||||
rc = LWS_ITOSA_USABLE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -621,9 +735,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
if (context->event_loop_ops->io)
|
||||
context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
|
||||
|
||||
pt->fds[pt->fds_count++].revents = 0;
|
||||
}
|
||||
|
@ -634,9 +747,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
|
|||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
|
||||
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
|
||||
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
|
||||
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
|
||||
if (context->event_loop_ops->io)
|
||||
context->event_loop_ops->io(wsi,
|
||||
LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
|
||||
|
||||
pt->fds_count--;
|
||||
}
|
||||
|
@ -717,7 +830,8 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
|
|||
{
|
||||
lws_fileofs_t r;
|
||||
|
||||
if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
|
||||
if (offset > 0 &&
|
||||
offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
|
||||
offset = fop_fd->len - fop_fd->pos;
|
||||
|
||||
if ((lws_fileofs_t)fop_fd->pos + offset < 0)
|
||||
|
@ -771,24 +885,22 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_init(struct lws_context *context,
|
||||
struct lws_context_creation_info *info)
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int n = context->count_threads, fd;
|
||||
int fd;
|
||||
|
||||
/* master context has the global fd lookup array */
|
||||
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
|
||||
context->max_fds);
|
||||
context->max_fds, "lws_lookup");
|
||||
if (context->lws_lookup == NULL) {
|
||||
lwsl_err("OOM on lws_lookup array for %d connections\n",
|
||||
context->max_fds);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lwsl_notice(" mem: platform fd map: %5lu bytes\n",
|
||||
lwsl_info(" mem: platform fd map: %5lu bytes\n",
|
||||
(unsigned long)(sizeof(struct lws *) * context->max_fds));
|
||||
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
|
||||
|
||||
|
@ -799,26 +911,6 @@ lws_plat_init(struct lws_context *context,
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!lws_libev_init_fd_table(context) &&
|
||||
!lws_libuv_init_fd_table(context) &&
|
||||
!lws_libevent_init_fd_table(context)) {
|
||||
/* otherwise libev handled it instead */
|
||||
|
||||
while (n--) {
|
||||
if (pipe(pt->dummy_pipe_fds)) {
|
||||
lwsl_err("Unable to create pipe\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* use the read end of pipe as first item */
|
||||
pt->fds[0].fd = pt->dummy_pipe_fds[0];
|
||||
pt->fds[0].events = LWS_POLLIN;
|
||||
pt->fds[0].revents = 0;
|
||||
pt->fds_count = 1;
|
||||
pt++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
if (info->plugin_dirs)
|
||||
lws_plat_plugins_init(context, info->plugin_dirs);
|
||||
|
@ -826,3 +918,52 @@ lws_plat_init(struct lws_context *context,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
|
||||
int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(fd, buf, len);
|
||||
|
||||
fsync(fd);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
return n != len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
int m, fd;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd == -1)
|
||||
return 1;
|
||||
|
||||
m = write(fd, buf, len);
|
||||
close(fd);
|
||||
|
||||
return m != len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_read_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
int n, fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
n = read(fd, buf, len);
|
||||
close(fd);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_recommended_rsa_bits(void)
|
||||
{
|
||||
return 4096;
|
||||
}
|
|
@ -1,7 +1,34 @@
|
|||
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#endif
|
||||
#include "private-libwebsockets.h"
|
||||
#include "core/private.h"
|
||||
|
||||
int
|
||||
lws_plat_socket_offset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_plat_pipe_create(struct lws *wsi)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_plat_pipe_signal(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
WSASetEvent(pt->events[0]); /* trigger the cancel event */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
lws_plat_pipe_close(struct lws *wsi)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
time_in_microseconds()
|
||||
|
@ -19,9 +46,10 @@ time_in_microseconds()
|
|||
#endif
|
||||
|
||||
/*
|
||||
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
|
||||
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
|
||||
* prevent alignment faults on 64-bit Windows).
|
||||
* As per Windows documentation for FILETIME, copy the resulting
|
||||
* FILETIME structure to a ULARGE_INTEGER structure using memcpy
|
||||
* (using memcpy instead of direct assignment can prevent alignment
|
||||
* faults on 64-bit Windows).
|
||||
*/
|
||||
memcpy(&datetime, &filetime, sizeof(datetime));
|
||||
|
||||
|
@ -81,7 +109,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
|
|||
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
|
||||
while (n < context->fd_hashtable[h].length) {
|
||||
context->fd_hashtable[h].wsi[n] =
|
||||
context->fd_hashtable[h].wsi[n + 1];
|
||||
context->fd_hashtable[h].wsi[n + 1];
|
||||
n++;
|
||||
}
|
||||
context->fd_hashtable[h].length--;
|
||||
|
@ -94,8 +122,8 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
|
|||
return 1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_get_random(struct lws_context *context,
|
||||
void *buf, int len)
|
||||
LWS_VISIBLE int
|
||||
lws_get_random(struct lws_context *context, void *buf, int len)
|
||||
{
|
||||
int n;
|
||||
char *p = (char *)buf;
|
||||
|
@ -106,16 +134,25 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
|
|||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
|
||||
{
|
||||
LWS_VISIBLE int
|
||||
lws_send_pipe_choked(struct lws *wsi)
|
||||
{ struct lws *wsi_eff = wsi;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
wsi_eff = lws_get_network_wsi(wsi);
|
||||
#endif
|
||||
/* the fact we checked implies we avoided back-to-back writes */
|
||||
wsi_eff->could_have_pending = 0;
|
||||
|
||||
/* treat the fact we got a truncated send pending as if we're choked */
|
||||
if (wsi->trunc_len)
|
||||
if (wsi_eff->trunc_len)
|
||||
return 1;
|
||||
|
||||
return (int)wsi->sock_send_blocking;
|
||||
return (int)wsi_eff->sock_send_blocking;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
|
||||
LWS_VISIBLE int
|
||||
lws_poll_listen_fd(struct lws_pollfd *fd)
|
||||
{
|
||||
fd_set readfds;
|
||||
struct timeval tv = { 0, 0 };
|
||||
|
@ -125,29 +162,11 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
|
|||
FD_ZERO(&readfds);
|
||||
FD_SET(fd->fd, &readfds);
|
||||
|
||||
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
|
||||
return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service(struct lws_context *context)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int n = context->count_threads;
|
||||
|
||||
while (n--) {
|
||||
WSASetEvent(pt->events[0]);
|
||||
pt++;
|
||||
}
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_cancel_service_pt(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
WSASetEvent(pt->events[0]);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
|
||||
lwsl_emit_syslog(int level, const char *line)
|
||||
{
|
||||
lwsl_emit_stderr(level, line);
|
||||
}
|
||||
|
@ -164,7 +183,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
int n, m;
|
||||
|
||||
/* stay dead once we are dead */
|
||||
if (context == NULL)
|
||||
if (context == NULL || !context->vhost_list)
|
||||
return 1;
|
||||
|
||||
pt = &context->pt[tsi];
|
||||
|
@ -178,12 +197,12 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
context->service_tid_detected = context->vhost_list->
|
||||
protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID,
|
||||
NULL, NULL, 0);
|
||||
context->service_tid = context->service_tid_detected;
|
||||
context->service_tid_detected = 1;
|
||||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
if (timeout_ms < 0)
|
||||
{
|
||||
if (lws_service_flag_pending(context, tsi)) {
|
||||
if (timeout_ms < 0) {
|
||||
if (lws_service_flag_pending(context, tsi)) {
|
||||
/* any socket with events to service? */
|
||||
for (n = 0; n < (int)pt->fds_count; n++) {
|
||||
if (!pt->fds[n].revents)
|
||||
|
@ -200,6 +219,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (context->event_loop_ops->run_pt)
|
||||
context->event_loop_ops->run_pt(context, tsi);
|
||||
|
||||
for (i = 0; i < pt->fds_count; ++i) {
|
||||
pfd = &pt->fds[i];
|
||||
|
||||
|
@ -219,6 +241,9 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
if (n)
|
||||
i--;
|
||||
|
||||
/*
|
||||
* any wsi has truncated, force him signalled
|
||||
*/
|
||||
if (wsi->trunc_len)
|
||||
WSASetEvent(pt->events[0]);
|
||||
}
|
||||
|
@ -235,32 +260,48 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
timeout_ms = 0;
|
||||
}
|
||||
|
||||
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
|
||||
if (timeout_ms) {
|
||||
lws_usec_t t;
|
||||
|
||||
lws_pt_lock(pt, __func__);
|
||||
/* don't stay in poll wait longer than next hr timeout */
|
||||
t = __lws_hrtimer_service(pt);
|
||||
|
||||
if ((lws_usec_t)timeout_ms * 1000 > t)
|
||||
timeout_ms = (int)(t / 1000);
|
||||
lws_pt_unlock(pt);
|
||||
}
|
||||
|
||||
ev = WSAWaitForMultipleEvents(1, pt->events, FALSE, timeout_ms, FALSE);
|
||||
if (ev == WSA_WAIT_EVENT_0) {
|
||||
unsigned int eIdx;
|
||||
unsigned int eIdx, err;
|
||||
|
||||
WSAResetEvent(pt->events[0]);
|
||||
|
||||
if (pt->context->tls_ops &&
|
||||
pt->context->tls_ops->fake_POLLIN_for_buffered)
|
||||
pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
|
||||
|
||||
for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
|
||||
if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) {
|
||||
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO);
|
||||
if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0,
|
||||
&networkevents) == SOCKET_ERROR) {
|
||||
lwsl_err("WSAEnumNetworkEvents() failed "
|
||||
"with error %d\n", LWS_ERRNO);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pfd = &pt->fds[eIdx];
|
||||
pfd->revents = (short)networkevents.lNetworkEvents;
|
||||
|
||||
err = networkevents.iErrorCode[FD_CONNECT_BIT];
|
||||
|
||||
if ((networkevents.lNetworkEvents & FD_CONNECT) &&
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT] &&
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY &&
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS &&
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK &&
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) {
|
||||
lwsl_debug("Unable to connect errno=%d\n",
|
||||
networkevents.iErrorCode[FD_CONNECT_BIT]);
|
||||
pfd->revents = LWS_POLLHUP;
|
||||
} else
|
||||
pfd->revents = (short)networkevents.lNetworkEvents;
|
||||
err && err != LWS_EALREADY &&
|
||||
err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK &&
|
||||
err != WSAEINVAL) {
|
||||
lwsl_debug("Unable to connect errno=%d\n", err);
|
||||
pfd->revents |= LWS_POLLHUP;
|
||||
}
|
||||
|
||||
if (pfd->revents & LWS_POLLOUT) {
|
||||
wsi = wsi_from_fd(context, pfd->fd);
|
||||
|
@ -269,21 +310,19 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
}
|
||||
/* if something closed, retry this slot */
|
||||
if (pfd->revents & LWS_POLLHUP)
|
||||
--eIdx;
|
||||
--eIdx;
|
||||
|
||||
if( pfd->revents != 0 ) {
|
||||
if (pfd->revents)
|
||||
lws_service_fd_tsi(context, pfd, tsi);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context->service_tid = 0;
|
||||
|
||||
if (ev == WSA_WAIT_TIMEOUT) {
|
||||
if (ev == WSA_WAIT_TIMEOUT)
|
||||
lws_service_fd(context, NULL);
|
||||
}
|
||||
return 0;;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
|
@ -309,7 +348,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
|
|||
/* enable keepalive on this socket */
|
||||
optval = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
|
||||
(const char *)&optval, optlen) < 0)
|
||||
(const char *)&optval, optlen) < 0)
|
||||
return 1;
|
||||
|
||||
alive.onoff = TRUE;
|
||||
|
@ -317,7 +356,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
|
|||
alive.keepaliveinterval = vhost->ka_interval;
|
||||
|
||||
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
|
||||
NULL, 0, &dwBytesRet, NULL, NULL))
|
||||
NULL, 0, &dwBytesRet, NULL, NULL))
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -343,7 +382,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
|
|||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
||||
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -401,12 +440,12 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_interface_to_sa(int ipv6,
|
||||
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
|
||||
{
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
|
||||
|
||||
if (ipv6) {
|
||||
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
|
||||
return 0;
|
||||
return LWS_ITOSA_USABLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -420,11 +459,11 @@ lws_interface_to_sa(int ipv6,
|
|||
}
|
||||
|
||||
if (address == INADDR_NONE)
|
||||
return -1;
|
||||
return LWS_ITOSA_NOT_EXIST;
|
||||
|
||||
addr->sin_addr.s_addr = (lws_intptr_t)address;
|
||||
addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
|
||||
|
||||
return 0;
|
||||
return LWS_ITOSA_USABLE;
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
|
@ -499,7 +538,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
|
|||
DWORD bufferlen = cnt;
|
||||
BOOL ok = FALSE;
|
||||
|
||||
buffer = lws_malloc(bufferlen * 2);
|
||||
buffer = lws_malloc(bufferlen * 2, "inet_ntop");
|
||||
if (!buffer) {
|
||||
lwsl_err("Out of memory\n");
|
||||
return NULL;
|
||||
|
@ -513,7 +552,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
|
|||
|
||||
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
|
||||
ok = TRUE;
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
} else if (af == AF_INET6) {
|
||||
struct sockaddr_in6 srcaddr;
|
||||
bzero(&srcaddr, sizeof(srcaddr));
|
||||
|
@ -542,10 +581,10 @@ LWS_VISIBLE int
|
|||
lws_plat_inet_pton(int af, const char *src, void *dst)
|
||||
{
|
||||
WCHAR *buffer;
|
||||
DWORD bufferlen = strlen(src) + 1;
|
||||
DWORD bufferlen = (int)strlen(src) + 1;
|
||||
BOOL ok = FALSE;
|
||||
|
||||
buffer = lws_malloc(bufferlen * 2);
|
||||
buffer = lws_malloc(bufferlen * 2, "inet_pton");
|
||||
if (!buffer) {
|
||||
lwsl_err("Out of memory\n");
|
||||
return -1;
|
||||
|
@ -567,7 +606,7 @@ lws_plat_inet_pton(int af, const char *src, void *dst)
|
|||
ok = TRUE;
|
||||
memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
|
||||
}
|
||||
#ifdef LWS_USE_IPV6
|
||||
#ifdef LWS_WITH_IPV6
|
||||
} else if (af == AF_INET6) {
|
||||
struct sockaddr_in6 dstaddr;
|
||||
int dstaddrlen = sizeof(dstaddr);
|
||||
|
@ -692,14 +731,14 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
|
|||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_init(struct lws_context *context,
|
||||
struct lws_context_creation_info *info)
|
||||
const struct lws_context_creation_info *info)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[0];
|
||||
int i, n = context->count_threads;
|
||||
|
||||
for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
|
||||
context->fd_hashtable[i].wsi =
|
||||
lws_zalloc(sizeof(struct lws*) * context->max_fds);
|
||||
lws_zalloc(sizeof(struct lws*) * context->max_fds, "win hashtable");
|
||||
|
||||
if (!context->fd_hashtable[i].wsi)
|
||||
return -1;
|
||||
|
@ -707,7 +746,7 @@ lws_plat_init(struct lws_context *context,
|
|||
|
||||
while (n--) {
|
||||
pt->events = lws_malloc(sizeof(WSAEVENT) *
|
||||
(context->fd_limit_per_thread + 1));
|
||||
(context->fd_limit_per_thread + 1), "event table");
|
||||
if (pt->events == NULL) {
|
||||
lwsl_err("Unable to allocate events array for %d connections\n",
|
||||
context->fd_limit_per_thread + 1);
|
||||
|
@ -715,7 +754,7 @@ lws_plat_init(struct lws_context *context,
|
|||
}
|
||||
|
||||
pt->fds_count = 0;
|
||||
pt->events[0] = WSACreateEvent();
|
||||
pt->events[0] = WSACreateEvent(); /* the cancel event */
|
||||
|
||||
pt++;
|
||||
}
|
||||
|
@ -743,3 +782,50 @@ int fork(void)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
|
||||
int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(fd, buf, len);
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
return n != len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_write_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
int m, fd;
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
m = write(fd, buf, len);
|
||||
close(fd);
|
||||
|
||||
return m != len;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_read_file(const char *filename, void *buf, int len)
|
||||
{
|
||||
int n, fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
n = read(fd, buf, len);
|
||||
close(fd);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_plat_recommended_rsa_bits(void)
|
||||
{
|
||||
return 4096;
|
||||
}
|
File diff suppressed because it is too large
Load diff
161
lib/roles/README.md
Normal file
161
lib/roles/README.md
Normal file
|
@ -0,0 +1,161 @@
|
|||
## Information for new role implementers
|
||||
|
||||
### Introduction
|
||||
|
||||
In lws the "role" is the job the wsi is doing in the system, eg,
|
||||
being an http1 or h2, or ws connection, or being a listen socket, etc.
|
||||
|
||||
This is different than, eg, a new ws protocol or a different callback
|
||||
for an existing role. A new role is needed when you want to add support for
|
||||
something completely new, like a completely new wire protocol that
|
||||
doesn't use http or ws.
|
||||
|
||||
So... what's the point of implementing the protocol inside the lws role framework?
|
||||
|
||||
You inherit all the well-maintained lws core functionality around:
|
||||
|
||||
- connection lifecycle sequencing in a valgrind-clean way
|
||||
|
||||
- proxy support, HTTP and Socks5
|
||||
|
||||
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
|
||||
|
||||
- apis for cert lifecycle management and parsing
|
||||
|
||||
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
|
||||
|
||||
- clean connection tracking and closing even on advanced event loops
|
||||
|
||||
- user code follows the same simple callbacks on wsi
|
||||
|
||||
- multi-vhost support
|
||||
|
||||
- core multithreaded service support with usually no locking requirement on the role code
|
||||
|
||||
- direct compatibility with all other lws roles + protocols in the same event loop
|
||||
|
||||
- compatibility with higher-level stuff like lwsws as the server application
|
||||
|
||||
### Code placement
|
||||
|
||||
The code specific to that role should live in `./lib/roles/**role name**`
|
||||
|
||||
If a role is asymmetic between a client and server side, like http is, it
|
||||
should generally be implemented as a single role.
|
||||
|
||||
### Allowing control over enabling roles
|
||||
|
||||
All roles should add a cmake define `LWS_ROLE_**role name**` and make its build
|
||||
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
|
||||
as well so user builds can understand if the role is available in the lws build it is
|
||||
trying to bind to.
|
||||
|
||||
If the role is disabled in cmake, nothing in its directory is built.
|
||||
|
||||
### Role ops struct
|
||||
|
||||
The role is defined by `struct lws_role_ops` in `lib/private/libwebsockets.h`,
|
||||
each role instantiates one of these and fills in the appropriate ops
|
||||
callbacks to perform its job. By convention that lives in
|
||||
`./lib/roles/**role name**/ops-**role_name**.c`.
|
||||
|
||||
### Private role declarations
|
||||
|
||||
Truly private declarations for the role can go in the role directory as you like.
|
||||
However when the declarations must be accessible to other things in lws build, eg,
|
||||
the role adds members to `struct lws` when enabled, they should be in the role
|
||||
directory in a file `private.h`.
|
||||
|
||||
Search for "bring in role private declarations" in `./lib/roles/private.h
|
||||
and add your private role file there following the style used for the other roles,
|
||||
eg,
|
||||
|
||||
```
|
||||
#if defined(LWS_ROLE_WS)
|
||||
#include "roles/ws/private.h"
|
||||
#else
|
||||
#define lwsi_role_ws(wsi) (0)
|
||||
#endif
|
||||
```
|
||||
|
||||
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
|
||||
|
||||
### Integrating role assets to lws
|
||||
|
||||
If your role needs special storage in lws objects, that's no problem. But to keep
|
||||
things sane, there are some rules.
|
||||
|
||||
- declare a "container struct" in your private.h for everything, eg, the ws role wants
|
||||
to add storage in lws_vhost for enabled extensions, it declares in its private.h
|
||||
|
||||
```
|
||||
struct lws_vhost_role_ws {
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
const struct lws_extension *extensions;
|
||||
#endif
|
||||
};
|
||||
```
|
||||
|
||||
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
|
||||
eg, again for LWS_ROLE_WS
|
||||
|
||||
```
|
||||
struct lws_vhost {
|
||||
|
||||
...
|
||||
|
||||
#if defined(LWS_ROLE_WS)
|
||||
struct lws_vhost_role_ws ws;
|
||||
#endif
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### Adding to lws available roles list
|
||||
|
||||
Edit the NULL-terminated array `available_roles` at the top of `./lib/context.c` to include
|
||||
a pointer to your new role's ops struct, following the style already there.
|
||||
|
||||
```
|
||||
const struct lws_role_ops * available_roles[] = {
|
||||
#if defined(LWS_ROLE_H2)
|
||||
&role_ops_h2,
|
||||
#endif
|
||||
...
|
||||
```
|
||||
|
||||
This makes lws aware that your role exists, and it can auto-generate some things like
|
||||
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
|
||||
|
||||
### Enabling role adoption
|
||||
|
||||
The primary way wsi get bound to a specific role is via the lws adoption api
|
||||
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./lib/libwebsockets.h`
|
||||
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
|
||||
to bind a wsi with suitable flags to your role ops.
|
||||
|
||||
### Implementation of the role
|
||||
|
||||
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
|
||||
bound to them via the adoption api.
|
||||
|
||||
The core support for wsis in lws has some generic concepts
|
||||
|
||||
- the wsi holds a pointer member `role_ops` that indicates which role ops the
|
||||
wsi is bound to
|
||||
|
||||
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
|
||||
|
||||
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
|
||||
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
|
||||
and `lwsi_role_server(wsi)`.
|
||||
|
||||
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
|
||||
various proxy or tunnel states, to 'established', and then various states shutting
|
||||
down until 'dead socket'. The states have testable flags and helpers to discover if
|
||||
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
|
||||
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
|
||||
|
||||
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
|
||||
you can adjust the state using `lwsi_set_state()`.
|
||||
|
1123
lib/roles/cgi/cgi-server.c
Normal file
1123
lib/roles/cgi/cgi-server.c
Normal file
File diff suppressed because it is too large
Load diff
102
lib/roles/cgi/ops-cgi.c
Normal file
102
lib/roles/cgi/ops-cgi.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <core/private.h>
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_cgi_args args;
|
||||
|
||||
assert(wsi->role_ops == &role_ops_cgi);
|
||||
|
||||
if (wsi->cgi_channel >= LWS_STDOUT &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (wsi->cgi_channel == LWS_STDIN &&
|
||||
!(pollfd->revents & pollfd->events & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (wsi->cgi_channel == LWS_STDIN &&
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
|
||||
args.ch = wsi->cgi_channel;
|
||||
args.stdwsi = &wsi->parent->http.cgi->stdwsi[0];
|
||||
args.hdr_state = wsi->hdr_state;
|
||||
|
||||
lwsl_debug("CGI LWS_STDOUT %p wsistate 0x%x\n",
|
||||
wsi->parent, wsi->wsistate);
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->parent->protocol->callback,
|
||||
wsi->parent, LWS_CALLBACK_CGI,
|
||||
wsi->parent->user_space,
|
||||
(void *)&args, 0))
|
||||
return 1;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_handle_POLLOUT_cgi(struct lws *wsi)
|
||||
{
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_periodic_checks_cgi(struct lws_context *context, int tsi, time_t now)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
|
||||
lws_cgi_kill_terminated(pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_cgi = {
|
||||
/* role name */ "cgi",
|
||||
/* alpn id */ NULL,
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ rops_periodic_checks_cgi,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_cgi,
|
||||
/* handle_POLLOUT */ rops_handle_POLLOUT_cgi,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ NULL,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* alpn_negotiated */ NULL,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ NULL,
|
||||
/* writeable cb clnt, srv */ { 0, 0 },
|
||||
/* close cb clnt, srv */ { 0, 0 },
|
||||
/* file_handle */ 0,
|
||||
};
|
69
lib/roles/cgi/private.h
Normal file
69
lib/roles/cgi/private.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_ROLE_WS
|
||||
*/
|
||||
|
||||
extern struct lws_role_ops role_ops_cgi;
|
||||
|
||||
#define lwsi_role_cgi(wsi) (wsi->role_ops == &role_ops_cgi)
|
||||
|
||||
#define LWS_HTTP_CHUNK_HDR_SIZE 16
|
||||
|
||||
enum {
|
||||
SIGNIFICANT_HDR_CONTENT_LENGTH,
|
||||
SIGNIFICANT_HDR_LOCATION,
|
||||
SIGNIFICANT_HDR_STATUS,
|
||||
SIGNIFICANT_HDR_TRANSFER_ENCODING,
|
||||
|
||||
SIGNIFICANT_HDR_COUNT
|
||||
};
|
||||
|
||||
struct lws;
|
||||
|
||||
/* wsi who is master of the cgi points to an lws_cgi */
|
||||
|
||||
struct lws_cgi {
|
||||
struct lws_cgi *cgi_list;
|
||||
struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
|
||||
struct lws *wsi; /* owner */
|
||||
unsigned char *headers_buf;
|
||||
unsigned char *headers_start;
|
||||
unsigned char *headers_pos;
|
||||
unsigned char *headers_dumped;
|
||||
unsigned char *headers_end;
|
||||
|
||||
char summary[128];
|
||||
|
||||
lws_filepos_t content_length;
|
||||
lws_filepos_t content_length_seen;
|
||||
|
||||
int pipe_fds[3][2];
|
||||
int match[SIGNIFICANT_HDR_COUNT];
|
||||
char l[12];
|
||||
int pid;
|
||||
int response_code;
|
||||
int lp;
|
||||
|
||||
unsigned char being_closed:1;
|
||||
unsigned char explicitly_chunked:1;
|
||||
|
||||
unsigned char chunked_grace;
|
||||
};
|
688
lib/roles/h1/ops-h1.c
Normal file
688
lib/roles/h1/ops-h1.c
Normal file
|
@ -0,0 +1,688 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <core/private.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
* into multiple fragments. They may contain unknown headers with arbitrary
|
||||
* argument lengths. So, we parse using a single-character at a time state
|
||||
* machine that is completely independent of packet size.
|
||||
*
|
||||
* Returns <0 for error or length of chars consumed from buf (up to len)
|
||||
*/
|
||||
|
||||
int
|
||||
lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
||||
{
|
||||
unsigned char *last_char, *oldbuf = buf;
|
||||
lws_filepos_t body_chunk_len;
|
||||
size_t n;
|
||||
|
||||
// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
|
||||
case LRS_ISSUING_FILE:
|
||||
return 0;
|
||||
|
||||
case LRS_ESTABLISHED:
|
||||
|
||||
if (lwsi_role_ws(wsi))
|
||||
goto ws_mode;
|
||||
|
||||
if (lwsi_role_client(wsi))
|
||||
break;
|
||||
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case LRS_HEADERS:
|
||||
if (!wsi->http.ah) {
|
||||
lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
|
||||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
|
||||
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
#endif
|
||||
last_char = buf;
|
||||
if (lws_handshake_server(wsi, &buf, (size_t)len))
|
||||
/* Handshake indicates this session is done. */
|
||||
goto bail;
|
||||
|
||||
/* we might have transitioned to RAW */
|
||||
if (wsi->role_ops == &role_ops_raw_skt ||
|
||||
wsi->role_ops == &role_ops_raw_file)
|
||||
/* we gave the read buffer to RAW handler already */
|
||||
goto read_ok;
|
||||
|
||||
/*
|
||||
* It's possible that we've exhausted our data already, or
|
||||
* rx flow control has stopped us dealing with this early,
|
||||
* but lws_handshake_server doesn't update len for us.
|
||||
* Figure out how much was read, so that we can proceed
|
||||
* appropriately:
|
||||
*/
|
||||
len -= (buf - last_char);
|
||||
// lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
/* More header content on the way */
|
||||
goto read_ok;
|
||||
|
||||
switch (lwsi_state(wsi)) {
|
||||
case LRS_ESTABLISHED:
|
||||
case LRS_HEADERS:
|
||||
goto read_ok;
|
||||
case LRS_ISSUING_FILE:
|
||||
goto read_ok;
|
||||
case LRS_BODY:
|
||||
wsi->http.rx_content_remain =
|
||||
wsi->http.rx_content_length;
|
||||
if (wsi->http.rx_content_remain)
|
||||
goto http_postbody;
|
||||
|
||||
/* there is no POST content */
|
||||
goto postbody_completion;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_BODY:
|
||||
http_postbody:
|
||||
lwsl_debug("%s: http post body: remain %d\n", __func__,
|
||||
(int)wsi->http.rx_content_remain);
|
||||
while (len && wsi->http.rx_content_remain) {
|
||||
/* Copy as much as possible, up to the limit of:
|
||||
* what we have in the read buffer (len)
|
||||
* remaining portion of the POST body (content_remain)
|
||||
*/
|
||||
body_chunk_len = min(wsi->http.rx_content_remain, len);
|
||||
wsi->http.rx_content_remain -= body_chunk_len;
|
||||
len -= body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->http.cgi) {
|
||||
struct lws_cgi_args args;
|
||||
|
||||
args.ch = LWS_STDIN;
|
||||
args.stdwsi = &wsi->http.cgi->stdwsi[0];
|
||||
args.data = buf;
|
||||
args.len = body_chunk_len;
|
||||
|
||||
/* returns how much used */
|
||||
n = user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
|
||||
wsi->user_space,
|
||||
(void *)&args, 0);
|
||||
if ((int)n < 0)
|
||||
goto bail;
|
||||
} else {
|
||||
#endif
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
|
||||
buf, (size_t)body_chunk_len);
|
||||
if (n)
|
||||
goto bail;
|
||||
n = (size_t)body_chunk_len;
|
||||
#ifdef LWS_WITH_CGI
|
||||
}
|
||||
#endif
|
||||
buf += n;
|
||||
|
||||
if (wsi->http.rx_content_remain) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
wsi->context->timeout_secs);
|
||||
break;
|
||||
}
|
||||
/* he sent all the content in time */
|
||||
postbody_completion:
|
||||
#ifdef LWS_WITH_CGI
|
||||
/*
|
||||
* If we're running a cgi, we can't let him off the
|
||||
* hook just because he sent his POST data
|
||||
*/
|
||||
if (wsi->http.cgi)
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI,
|
||||
wsi->context->timeout_secs);
|
||||
else
|
||||
#endif
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (!wsi->http.cgi)
|
||||
#endif
|
||||
{
|
||||
lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n",
|
||||
wsi, wsi->protocol->name);
|
||||
n = wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_HTTP_BODY_COMPLETION,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n)
|
||||
goto bail;
|
||||
|
||||
if (wsi->http2_substream)
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LRS_RETURNED_CLOSE:
|
||||
case LRS_AWAITING_CLOSE_ACK:
|
||||
case LRS_WAITING_TO_SEND_CLOSE:
|
||||
case LRS_SHUTDOWN:
|
||||
|
||||
ws_mode:
|
||||
#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
|
||||
// lwsl_notice("%s: ws_mode\n", __func__);
|
||||
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
|
||||
goto bail;
|
||||
#endif
|
||||
#if defined(LWS_ROLE_WS)
|
||||
if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
|
||||
/*
|
||||
* for h2 we are on the swsi
|
||||
*/
|
||||
lws_parse_ws(wsi, &buf, (size_t)len) < 0) {
|
||||
lwsl_info("%s: lws_parse_ws bailed\n", __func__);
|
||||
goto bail;
|
||||
}
|
||||
#endif
|
||||
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
|
||||
// lws_ptr_diff(buf, oldbuf));
|
||||
break;
|
||||
|
||||
case LRS_DEFERRING_ACTION:
|
||||
lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
|
||||
break;
|
||||
|
||||
case LRS_SSL_ACK_PENDING:
|
||||
break;
|
||||
|
||||
case LRS_DEAD_SOCKET:
|
||||
lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
|
||||
goto bail;
|
||||
// assert(0);
|
||||
/* fallthru */
|
||||
|
||||
default:
|
||||
lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
|
||||
assert(0);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
read_ok:
|
||||
/* Nothing more to do for now */
|
||||
// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
|
||||
// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
|
||||
|
||||
return lws_ptr_diff(buf, oldbuf);
|
||||
|
||||
bail:
|
||||
/*
|
||||
* h2 / h2-ws calls us recursively in
|
||||
*
|
||||
* lws_read_h1()->
|
||||
* lws_h2_parser()->
|
||||
* lws_read_h1()
|
||||
*
|
||||
* pattern, having stripped the h2 framing in the middle.
|
||||
*
|
||||
* When taking down the whole connection, make sure that only the
|
||||
* outer lws_read() does the wsi close.
|
||||
*/
|
||||
if (!wsi->outer_will_close)
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"lws_read_h1 bail");
|
||||
|
||||
return -1;
|
||||
}
|
||||
#if !defined(LWS_NO_SERVER)
|
||||
static int
|
||||
lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws_tokens ebuf;
|
||||
int n, buffered;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
|
||||
goto try_pollout;
|
||||
|
||||
/* any incoming data ready? */
|
||||
|
||||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
|
||||
goto try_pollout;
|
||||
|
||||
/*
|
||||
* If we previously just did POLLIN when IN and OUT were signaled
|
||||
* (because POLLIN processing may have used up the POLLOUT), don't let
|
||||
* that happen twice in a row... next time we see the situation favour
|
||||
* POLLOUT
|
||||
*/
|
||||
|
||||
if (wsi->favoured_pollin &&
|
||||
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
|
||||
// lwsl_notice("favouring pollout\n");
|
||||
wsi->favoured_pollin = 0;
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
/*
|
||||
* We haven't processed that the tunnel is set up yet, so
|
||||
* defer reading
|
||||
*/
|
||||
|
||||
if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* these states imply we MUST have an ah attached */
|
||||
|
||||
if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
|
||||
lwsi_state(wsi) == LRS_ISSUING_FILE ||
|
||||
lwsi_state(wsi) == LRS_HEADERS ||
|
||||
lwsi_state(wsi) == LRS_BODY)) {
|
||||
|
||||
if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
|
||||
lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
/*
|
||||
* We got here because there was specifically POLLIN...
|
||||
* regardless of our buflist state, we need to get it,
|
||||
* and either use it, or append to the buflist and use
|
||||
* buflist head material.
|
||||
*
|
||||
* We will not notice a connection close until the buflist is
|
||||
* exhausted and we tried to do a read of some kind.
|
||||
*/
|
||||
|
||||
buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
|
||||
switch (ebuf.len) {
|
||||
case 0:
|
||||
lwsl_info("%s: read 0 len a\n", __func__);
|
||||
wsi->seen_zero_length_recv = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* autobahn requires us to win the race between close
|
||||
* and draining the extensions
|
||||
*/
|
||||
if (wsi->ws &&
|
||||
(wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
|
||||
goto try_pollout;
|
||||
#endif
|
||||
/*
|
||||
* normally, we respond to close with logically closing
|
||||
* our side immediately
|
||||
*/
|
||||
goto fail;
|
||||
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
goto fail;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
/* just ignore incoming if waiting for close */
|
||||
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
|
||||
lwsl_notice("%s: just ignoring\n", __func__);
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
if (lwsi_state(wsi) == LRS_ISSUING_FILE) {
|
||||
// lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered);
|
||||
if (lws_buflist_aware_consume(wsi, &ebuf, 0, buffered))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
goto try_pollout;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise give it to whoever wants it according to the
|
||||
* connection state
|
||||
*/
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
|
||||
n = lws_read_h2(wsi, (uint8_t *)ebuf.token, ebuf.len);
|
||||
else
|
||||
#endif
|
||||
n = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
|
||||
if (n < 0) /* we closed wsi */
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
|
||||
lwsl_debug("%s: consumed %d\n", __func__, n);
|
||||
|
||||
if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
/*
|
||||
* during the parsing our role changed to something non-http,
|
||||
* so the ah has no further meaning
|
||||
*/
|
||||
|
||||
if (wsi->http.ah &&
|
||||
!lwsi_role_h1(wsi) &&
|
||||
!lwsi_role_h2(wsi) &&
|
||||
!lwsi_role_cgi(wsi))
|
||||
lws_header_table_detach(wsi, 0);
|
||||
|
||||
/*
|
||||
* He may have used up the writability above, if we will defer
|
||||
* POLLOUT processing in favour of POLLIN, note it
|
||||
*/
|
||||
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->favoured_pollin = 1;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* He may have used up the writability above, if we will defer POLLOUT
|
||||
* processing in favour of POLLIN, note it
|
||||
*/
|
||||
|
||||
if (pollfd->revents & LWS_POLLOUT)
|
||||
wsi->favoured_pollin = 1;
|
||||
|
||||
try_pollout:
|
||||
|
||||
/* this handles POLLOUT for http serving fragments */
|
||||
|
||||
if (!(pollfd->revents & LWS_POLLOUT))
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
/* one shot */
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_notice("%s a\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* clear back-to-back write detection */
|
||||
wsi->could_have_pending = 0;
|
||||
|
||||
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
|
||||
lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__);
|
||||
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
|
||||
lwsl_info("failed at set pollfd\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wsi->hdr_parsing_completed)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_C_WRITEABLE_CB, 1);
|
||||
#if defined(LWS_WITH_STATS)
|
||||
if (wsi->active_writable_req_us) {
|
||||
uint64_t ul = time_in_microseconds() -
|
||||
wsi->active_writable_req_us;
|
||||
|
||||
lws_stats_atomic_bump(wsi->context, pt,
|
||||
LWSSTATS_MS_WRITABLE_DELAY, ul);
|
||||
lws_stats_atomic_max(wsi->context, pt,
|
||||
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
|
||||
wsi->active_writable_req_us = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
n = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_HTTP_WRITEABLE,
|
||||
wsi->user_space, NULL, 0);
|
||||
if (n < 0) {
|
||||
lwsl_info("writeable_fail\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
/* >0 == completion, <0 == error
|
||||
*
|
||||
* We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
|
||||
* it's done. That's the case even if we just completed the
|
||||
* send, so wait for that.
|
||||
*/
|
||||
n = lws_serve_http_file_fragment(wsi);
|
||||
if (n < 0)
|
||||
goto fail;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
|
||||
fail:
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
|
||||
"server socket svc fail");
|
||||
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_pollfd *pollfd)
|
||||
{
|
||||
|
||||
// lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
|
||||
// wsi->wsistate, wsi->role_ops->name, pollfd->revents);
|
||||
|
||||
#ifdef LWS_WITH_CGI
|
||||
if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) {
|
||||
if (lws_handle_POLLOUT_event(wsi, pollfd))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lws_is_flowcontrolled(wsi))
|
||||
/* We cannot deal with any kind of new RX because we are
|
||||
* RX-flowcontrolled.
|
||||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#if !defined(LWS_NO_SERVER)
|
||||
if (!lwsi_role_client(wsi)) {
|
||||
int n;
|
||||
|
||||
lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
|
||||
n = lws_h1_server_socket_service(wsi, pollfd);
|
||||
if (n != LWS_HPI_RET_HANDLED)
|
||||
return n;
|
||||
if (lwsi_state(wsi) != LRS_SSL_INIT)
|
||||
if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if ((pollfd->revents & LWS_POLLIN) &&
|
||||
wsi->hdr_parsing_completed && !wsi->told_user_closed) {
|
||||
|
||||
/*
|
||||
* In SSL mode we get POLLIN notification about
|
||||
* encrypted data in.
|
||||
*
|
||||
* But that is not necessarily related to decrypted
|
||||
* data out becoming available; in may need to perform
|
||||
* other in or out before that happens.
|
||||
*
|
||||
* simply mark ourselves as having readable data
|
||||
* and turn off our POLLIN
|
||||
*/
|
||||
wsi->client_rx_avail = 1;
|
||||
lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
|
||||
//lwsl_notice("calling back %s\n", wsi->protocol->name);
|
||||
|
||||
/* let user code know, he'll usually ask for writeable
|
||||
* callback and drain / re-enable it there
|
||||
*/
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
|
||||
wsi->user_space, NULL, 0)) {
|
||||
lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if (lwsi_state(wsi) == LRS_ESTABLISHED)
|
||||
// return LWS_HPI_RET_HANDLED;
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if ((pollfd->revents & LWS_POLLOUT) &&
|
||||
lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
lwsl_debug("POLLOUT event closed it\n");
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
if (lws_client_socket_service(wsi, pollfd, NULL))
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
#endif
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
int rops_handle_POLLOUT_h1(struct lws *wsi)
|
||||
{
|
||||
if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
if (lwsi_role_client(wsi))
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
|
||||
enum lws_write_protocol *wp)
|
||||
{
|
||||
#if 0
|
||||
/* if not in a state to send stuff, then just send nothing */
|
||||
|
||||
if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
|
||||
//assert(0);
|
||||
lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return lws_issue_raw(wsi, (unsigned char *)buf, len);
|
||||
}
|
||||
|
||||
static int
|
||||
rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
|
||||
{
|
||||
lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lwsi_role_client(wsi)) {
|
||||
/*
|
||||
* If alpn asserts it is http/1.1, server support for KA is
|
||||
* mandatory.
|
||||
*
|
||||
* Knowing this lets us proceed with sending pipelined headers
|
||||
* before we received the first response headers.
|
||||
*/
|
||||
wsi->keepalive_active = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rops_destroy_role_h1(struct lws *wsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct allocated_headers *ah;
|
||||
|
||||
/* we may not have an ah, but may be on the waiting list... */
|
||||
lwsl_info("%s: ah det due to close\n", __func__);
|
||||
__lws_header_table_detach(wsi, 0);
|
||||
|
||||
ah = pt->http.ah_list;
|
||||
|
||||
while (ah) {
|
||||
if (ah->in_use && ah->wsi == wsi) {
|
||||
lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
|
||||
ah->in_use = 0;
|
||||
ah->wsi = NULL;
|
||||
pt->http.ah_count_in_use--;
|
||||
break;
|
||||
}
|
||||
ah = ah->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws_role_ops role_ops_h1 = {
|
||||
/* role name */ "h1",
|
||||
/* alpn id */ "http/1.1",
|
||||
/* check_upgrades */ NULL,
|
||||
/* init_context */ NULL,
|
||||
/* init_vhost */ NULL,
|
||||
/* destroy_vhost */ NULL,
|
||||
/* periodic_checks */ NULL,
|
||||
/* service_flag_pending */ NULL,
|
||||
/* handle_POLLIN */ rops_handle_POLLIN_h1,
|
||||
/* handle_POLLOUT */ rops_handle_POLLOUT_h1,
|
||||
/* perform_user_POLLOUT */ NULL,
|
||||
/* callback_on_writable */ NULL,
|
||||
/* tx_credit */ NULL,
|
||||
/* write_role_protocol */ rops_write_role_protocol_h1,
|
||||
/* encapsulation_parent */ NULL,
|
||||
/* alpn_negotiated */ rops_alpn_negotiated_h1,
|
||||
/* close_via_role_protocol */ NULL,
|
||||
/* close_role */ NULL,
|
||||
/* close_kill_connection */ NULL,
|
||||
/* destroy_role */ rops_destroy_role_h1,
|
||||
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
|
||||
LWS_CALLBACK_HTTP_WRITEABLE },
|
||||
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
LWS_CALLBACK_CLOSED_HTTP },
|
||||
/* file_handle */ 0,
|
||||
};
|
27
lib/roles/h1/private.h
Normal file
27
lib/roles/h1/private.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*
|
||||
* This is included from core/private.h if LWS_ROLE_H1
|
||||
*
|
||||
* Most of the h1 business is defined in the h1 / h2 common roles/http dir
|
||||
*/
|
||||
|
||||
extern struct lws_role_ops role_ops_h1;
|
||||
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue