mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
autobahn fixes
This replaces the old test-app for echo with separate client and server minimal versions. The autobahn test script is made more autonomous and tests both client and server.
This commit is contained in:
parent
3459d4fe43
commit
7b227eb333
36 changed files with 2199 additions and 1451 deletions
|
@ -4,7 +4,7 @@ env:
|
|||
global:
|
||||
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
|
||||
matrix:
|
||||
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- 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=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
|
||||
- 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"
|
||||
|
@ -29,7 +29,7 @@ install:
|
|||
# - 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 . && if [ "$LWS_METHOD" = "lwsws" ] ; then sudo make install && ../minimal-examples/selftests.sh ; fi ; fi ; fi
|
||||
- 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 . && if [ "$LWS_METHOD" = "lwsws" ] ; then sudo make install && ../minimal-examples/selftests.sh && ../test-apps/attack.sh && ../scripts/autobahn-test.sh ; fi ; fi ; fi
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
|
|
|
@ -69,7 +69,6 @@ option(LWS_WITHOUT_TESTAPPS "Don't build the libwebsocket-test-apps" OFF)
|
|||
option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF)
|
||||
option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF)
|
||||
option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF)
|
||||
option(LWS_WITHOUT_TEST_ECHO "Don't build the echo test application" OFF)
|
||||
option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF)
|
||||
option(LWS_WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF)
|
||||
#
|
||||
|
@ -702,10 +701,6 @@ set(SOURCES
|
|||
if (LWS_ROLE_H1)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/h1/ops-h1.c)
|
||||
if (NOT LWS_WITHOUT_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
lib/roles/h1/client-h1.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LWS_ROLE_WS)
|
||||
|
@ -997,8 +992,15 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID
|
|||
if (LWS_WITH_GCOV)
|
||||
set (GCOV_FLAGS "-fprofile-arcs -ftest-coverage -O0")
|
||||
endif()
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "DEBUG")
|
||||
set(CMAKE_C_FLAGS "-O0" ${CMAKE_C_FLAGS})
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "-O3" ${CMAKE_C_FLAGS})
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT LWS_WITH_ESP32)
|
||||
set(CMAKE_C_FLAGS "-O3 -Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
|
||||
set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
|
||||
endif()
|
||||
|
@ -1731,12 +1733,6 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
if (NOT LWS_WITHOUT_TEST_PING AND NOT LWS_WITHOUT_SERVER AND NOT LWS_WITHOUT_CLIENT)
|
||||
create_test_app(test-ping "test-apps/test-ping.c" "" "" "" "" "")
|
||||
endif()
|
||||
#
|
||||
# test-echo
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_ECHO AND NOT LWS_WITHOUT_SERVER AND NOT LWS_WITHOUT_CLIENT)
|
||||
create_test_app(test-echo "test-apps/test-echo.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
endif(NOT LWS_WITHOUT_CLIENT)
|
||||
|
||||
|
@ -2160,7 +2156,6 @@ message(" LWS_WITHOUT_TESTAPPS = ${LWS_WITHOUT_TESTAPPS}")
|
|||
message(" LWS_WITHOUT_TEST_SERVER = ${LWS_WITHOUT_TEST_SERVER}")
|
||||
message(" LWS_WITHOUT_TEST_SERVER_EXTPOLL = ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}")
|
||||
message(" LWS_WITHOUT_TEST_PING = ${LWS_WITHOUT_TEST_PING}")
|
||||
message(" LWS_WITHOUT_TEST_ECHO = ${LWS_WITHOUT_TEST_ECHO}")
|
||||
message(" LWS_WITHOUT_TEST_CLIENT = ${LWS_WITHOUT_TEST_CLIENT}")
|
||||
message(" LWS_WITHOUT_TEST_FRAGGLE = ${LWS_WITHOUT_TEST_FRAGGLE}")
|
||||
message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}")
|
||||
|
|
77
README.md
77
README.md
|
@ -7,6 +7,26 @@
|
|||
News
|
||||
----
|
||||
|
||||
## Major CI improvements for QA
|
||||
|
||||
The Travis build of lws done on every commit now runs
|
||||
|
||||
Tests|Count|Explanation
|
||||
---|---|---
|
||||
Build / Linux / gcc|12|-Wall -Werror
|
||||
Build / Mac / Clang|12|-Wall -Werror
|
||||
Build / Windows / MSVC|7|default
|
||||
Selftests|29|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
|
||||
|
||||
The over 1,200 tests run on every commit take 40 minutes 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
|
||||
|
||||

|
||||
|
@ -78,60 +98,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-----
|
||||
```
|
||||
|
|
|
@ -238,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
|
||||
|
@ -436,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.
|
||||
|
@ -474,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
|
||||
|
||||
|
|
|
@ -1346,21 +1346,11 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
|
||||
lws_plat_drop_app_privileges(info);
|
||||
|
||||
/*
|
||||
* give all extensions a chance to create any per-context
|
||||
* allocations they need
|
||||
*/
|
||||
if (info->port != CONTEXT_PORT_NO_LISTEN) {
|
||||
if (lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
goto bail;
|
||||
} else
|
||||
if (lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
|
||||
goto bail;
|
||||
|
||||
time(&context->last_cert_check_s);
|
||||
|
||||
/* expedite post-context init (eg, protocols) */
|
||||
lws_cancel_service(context);
|
||||
|
||||
#if defined(LWS_WITH_SELFTESTS)
|
||||
lws_jws_selftest();
|
||||
#endif
|
||||
|
@ -1747,17 +1737,6 @@ lws_context_destroy(struct lws_context *context)
|
|||
lws_pt_mutex_destroy(pt);
|
||||
}
|
||||
|
||||
/*
|
||||
* give all extensions a chance to clean up any per-context
|
||||
* allocations they might have made
|
||||
*/
|
||||
|
||||
n = lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT, NULL, 0);
|
||||
|
||||
n = lws_ext_cb_all_exts(context, NULL,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT, NULL, 0);
|
||||
|
||||
/*
|
||||
* inform all the protocols that they are done and will have no more
|
||||
* callbacks.
|
||||
|
|
|
@ -534,8 +534,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
struct lws_context_per_thread *pt;
|
||||
struct lws *wsi1, *wsi2;
|
||||
struct lws_context *context;
|
||||
struct lws_tokens ebuf;
|
||||
int n, m;
|
||||
int n;
|
||||
|
||||
lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
|
||||
|
||||
|
@ -651,6 +650,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
/* we tried the polite way... */
|
||||
case LRS_WAITING_TO_SEND_CLOSE:
|
||||
case LRS_AWAITING_CLOSE_ACK:
|
||||
case LRS_RETURNED_CLOSE:
|
||||
goto just_kill_connection;
|
||||
|
||||
case LRS_FLUSHING_BEFORE_CLOSE:
|
||||
|
@ -685,45 +685,6 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* are his extensions okay with him closing? Eg he might be a mux
|
||||
* parent and just his ch1 aspect is closing?
|
||||
*/
|
||||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE,
|
||||
NULL, 0) > 0) {
|
||||
lwsl_ext("extension vetoed close\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* flush any tx pending from extensions, since we may send close packet
|
||||
* if there are problems with send, just nuke the connection
|
||||
*/
|
||||
do {
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
/* show every extension the new incoming data */
|
||||
|
||||
m = lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_FLUSH_PENDING_TX, &ebuf, 0);
|
||||
if (m < 0) {
|
||||
lwsl_ext("Extension reports fatal error\n");
|
||||
goto just_kill_connection;
|
||||
}
|
||||
|
||||
/* assuming they left us something to send, send it */
|
||||
|
||||
if (ebuf.len)
|
||||
if (lws_issue_raw(wsi, (unsigned char *)ebuf.token,
|
||||
ebuf.len) !=
|
||||
ebuf.len) {
|
||||
lwsl_debug("close: ext spill failed\n");
|
||||
goto just_kill_connection;
|
||||
}
|
||||
} while (m);
|
||||
|
||||
/*
|
||||
* signal we are closing, lws_write will
|
||||
* add any necessary version-specific stuff. If the write fails,
|
||||
|
@ -865,6 +826,8 @@ just_kill_connection:
|
|||
|
||||
if (!wsi->protocol)
|
||||
pro = &wsi->vhost->protocols[0];
|
||||
//lwsl_notice("%s: est %d told %d cbin %d %s\n", __func__, lwsi_state_est_PRE_CLOSE(wsi), !wsi->told_user_closed,
|
||||
// wsi->role_ops->close_cb[lwsi_role_server(wsi)], pro->name);
|
||||
pro->callback(wsi,
|
||||
wsi->role_ops->close_cb[lwsi_role_server(wsi)],
|
||||
wsi->user_space, NULL, 0);
|
||||
|
@ -875,13 +838,6 @@ just_kill_connection:
|
|||
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
|
||||
lwsl_warn("extension destruction failed\n");
|
||||
/*
|
||||
* inform all extensions in case they tracked this guy out of band
|
||||
* even though not active on him specifically
|
||||
*/
|
||||
if (lws_ext_cb_all_exts(context, wsi,
|
||||
LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
|
||||
lwsl_warn("ext destroy wsi failed\n");
|
||||
|
||||
async_close:
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
@ -1049,7 +1005,6 @@ lws_buflist_use_segment(struct lws_buflist **head, size_t len)
|
|||
{
|
||||
assert(*head);
|
||||
assert(len);
|
||||
|
||||
assert((*head)->pos + len <= (*head)->len);
|
||||
|
||||
(*head)->pos += len;
|
||||
|
@ -2065,6 +2020,12 @@ lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
|
|||
if (!lwsl_visible(hexdump_level))
|
||||
return;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if (!vbuf)
|
||||
return;
|
||||
|
||||
_lws_log(hexdump_level, "\n");
|
||||
|
||||
for (n = 0; n < len;) {
|
||||
|
|
|
@ -345,6 +345,7 @@ lwsl_timestamp(int level, char *p, int len);
|
|||
#define lwsl_hexdump_warn(...) lwsl_hexdump_level(LLL_WARN, __VA_ARGS__)
|
||||
#define lwsl_hexdump_notice(...) lwsl_hexdump_level(LLL_NOTICE, __VA_ARGS__)
|
||||
#define lwsl_hexdump_info(...) lwsl_hexdump_level(LLL_INFO, __VA_ARGS__)
|
||||
#define lwsl_hexdump_debug(...) lwsl_hexdump_level(LLL_DEBUG, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* lwsl_hexdump_level() - helper to hexdump a buffer at a selected debug level
|
||||
|
@ -2143,27 +2144,10 @@ lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
|
|||
* add it at where specified so existing users are unaffected.
|
||||
*/
|
||||
enum lws_extension_callback_reasons {
|
||||
LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1,
|
||||
LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2,
|
||||
LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3,
|
||||
LWS_EXT_CB_CONSTRUCT = 4,
|
||||
LWS_EXT_CB_CLIENT_CONSTRUCT = 5,
|
||||
LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7,
|
||||
LWS_EXT_CB_DESTROY = 8,
|
||||
LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9,
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10,
|
||||
LWS_EXT_CB_PACKET_RX_PREPARSE = 11,
|
||||
LWS_EXT_CB_PACKET_TX_PRESEND = 12,
|
||||
LWS_EXT_CB_PACKET_TX_DO_SEND = 13,
|
||||
LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14,
|
||||
LWS_EXT_CB_FLUSH_PENDING_TX = 15,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16,
|
||||
LWS_EXT_CB_UNUSED1 = 17,
|
||||
LWS_EXT_CB_1HZ = 18,
|
||||
LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19,
|
||||
LWS_EXT_CB_IS_WRITEABLE = 20,
|
||||
LWS_EXT_CB_PAYLOAD_TX = 21,
|
||||
LWS_EXT_CB_PAYLOAD_RX = 22,
|
||||
LWS_EXT_CB_OPTION_DEFAULT = 23,
|
||||
|
@ -2241,18 +2225,6 @@ struct lws_ext_option_arg {
|
|||
* user data is deleted. This same callback is used whether you
|
||||
* are in client or server instantiation context.
|
||||
*
|
||||
* LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on
|
||||
* a connection, and a packet of data arrived at the connection,
|
||||
* it is passed to this callback to give the extension a chance to
|
||||
* change the data, eg, decompress it. user is pointing to the
|
||||
* extension's private connection context data, in is pointing
|
||||
* to an lws_tokens struct, it consists of a char * pointer called
|
||||
* token, and an int called len. At entry, these are
|
||||
* set to point to the received buffer and set to the content
|
||||
* length. If the extension will grow the content, it should use
|
||||
* a new buffer allocated in its private user context data and
|
||||
* set the pointed-to lws_tokens members to point to its buffer.
|
||||
*
|
||||
* LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as
|
||||
* LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the
|
||||
* extension a chance to change websocket data just before it will
|
||||
|
@ -2822,8 +2794,8 @@ struct lws_context_creation_info {
|
|||
short max_http_header_pool;
|
||||
/**< CONTEXT: The max number of connections with http headers that
|
||||
* can be processed simultaneously (the corresponding memory is
|
||||
* allocated for the lifetime of the context). If the pool is
|
||||
* busy new incoming connections must wait for accept until one
|
||||
* allocated and deallocated dynamically as needed). If the pool is
|
||||
* fully busy new incoming connections must wait for accept until one
|
||||
* becomes free. */
|
||||
|
||||
unsigned int count_threads;
|
||||
|
|
262
lib/output.c
262
lib/output.c
|
@ -30,9 +30,6 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
size_t real_len = len;
|
||||
unsigned int n;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
int m;
|
||||
#endif
|
||||
|
||||
// lwsl_hexdump_err(buf, len);
|
||||
|
||||
|
@ -74,15 +71,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
|
||||
return -1;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, (int)len);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
if (m) /* handled */ {
|
||||
n = m;
|
||||
goto handle_truncated_send;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
|
||||
lwsl_warn("** error invalid sock but expected to send\n");
|
||||
|
||||
|
@ -120,9 +109,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
n = 0;
|
||||
break;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
handle_truncated_send:
|
||||
#endif
|
||||
|
||||
/*
|
||||
* we were already handling a truncated send?
|
||||
*/
|
||||
|
@ -240,251 +227,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
|
||||
}
|
||||
|
||||
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, *pstart;
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
unsigned char finished = 0;
|
||||
#endif
|
||||
int n, m;
|
||||
|
||||
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
|
||||
|
||||
do {
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wsi->http.filepos == wsi->http.filelen)
|
||||
goto all_sent;
|
||||
|
||||
n = 0;
|
||||
|
||||
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
p = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
|
||||
|
||||
lwsl_notice("%s: doing range start %llu\n", __func__,
|
||||
wsi->http.range.start);
|
||||
|
||||
if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
|
||||
wsi->http.range.start -
|
||||
wsi->http.filepos) < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->http.filepos = wsi->http.range.start;
|
||||
|
||||
if (wsi->http.range.count_ranges > 1) {
|
||||
n = lws_snprintf((char *)p,
|
||||
context->pt_serv_buf_size -
|
||||
LWS_H2_FRAME_HEADER_LENGTH,
|
||||
"_lws\x0d\x0a"
|
||||
"Content-Type: %s\x0d\x0a"
|
||||
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
|
||||
"\x0d\x0a",
|
||||
wsi->http.multipart_content_type,
|
||||
wsi->http.range.start,
|
||||
wsi->http.range.end,
|
||||
wsi->http.range.extent);
|
||||
p += n;
|
||||
}
|
||||
|
||||
wsi->http.range.budget = wsi->http.range.end -
|
||||
wsi->http.range.start + 1;
|
||||
wsi->http.range.inside = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
if (wsi->http.tx_content_length)
|
||||
if (poss > wsi->http.tx_content_remain)
|
||||
poss = wsi->http.tx_content_remain;
|
||||
|
||||
/*
|
||||
* 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 (wsi->role_ops->tx_credit) {
|
||||
lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
|
||||
|
||||
if (!txc) {
|
||||
lwsl_info("%s: came here with no tx credit\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
if (txc < poss)
|
||||
poss = txc;
|
||||
|
||||
/*
|
||||
* consumption of the actual payload amount sent will be
|
||||
* handled when the role data frame is sent
|
||||
*/
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges) {
|
||||
if (wsi->http.range.count_ranges > 1)
|
||||
poss -= 7; /* allow for final boundary */
|
||||
if (poss > wsi->http.range.budget)
|
||||
poss = wsi->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->http.fop_fd, &amount, p, poss) < 0)
|
||||
goto file_had_it; /* caller will close */
|
||||
|
||||
if (wsi->sending_chunked)
|
||||
n = (int)amount;
|
||||
else
|
||||
n = lws_ptr_diff(p, pstart) + (int)amount;
|
||||
|
||||
lwsl_debug("%s: sending %d\n", __func__, n);
|
||||
|
||||
if (n) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
context->timeout_secs);
|
||||
|
||||
if (wsi->interpreting) {
|
||||
args.p = (char *)p;
|
||||
args.len = n;
|
||||
args.max_len = (unsigned int)poss + 128;
|
||||
args.final = wsi->http.filepos + n ==
|
||||
wsi->http.filelen;
|
||||
args.chunked = wsi->sending_chunked;
|
||||
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 = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.send_ctr + 1 ==
|
||||
wsi->http.range.count_ranges && // last range
|
||||
wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
|
||||
wsi->http.range.budget - amount == 0) {// final part
|
||||
n += lws_snprintf((char *)pstart + n, 6,
|
||||
"_lws\x0d\x0a"); // append trailing boundary
|
||||
lwsl_debug("added trailing boundary\n");
|
||||
}
|
||||
#endif
|
||||
m = lws_write(wsi, p, n,
|
||||
wsi->http.filepos + amount == wsi->http.filelen ?
|
||||
LWS_WRITE_HTTP_FINAL :
|
||||
LWS_WRITE_HTTP
|
||||
);
|
||||
if (m < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->http.filepos += amount;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges >= 1) {
|
||||
wsi->http.range.budget -= amount;
|
||||
if (wsi->http.range.budget == 0) {
|
||||
lwsl_notice("range budget exhausted\n");
|
||||
wsi->http.range.inside = 0;
|
||||
wsi->http.range.send_ctr++;
|
||||
|
||||
if (lws_ranges_next(&wsi->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->http.fop_fd,
|
||||
m - n) ==
|
||||
(lws_fileofs_t)-1)
|
||||
goto file_had_it;
|
||||
}
|
||||
}
|
||||
|
||||
all_sent:
|
||||
if ((!wsi->trunc_len && wsi->http.filepos >= wsi->http.filelen)
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
|| finished)
|
||||
#else
|
||||
)
|
||||
#endif
|
||||
{
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
/* we might be in keepalive, so close it off here */
|
||||
lws_vfs_file_close(&wsi->http.fop_fd);
|
||||
|
||||
lwsl_debug("file completed\n");
|
||||
|
||||
if (wsi->protocol->callback &&
|
||||
user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL,
|
||||
0) < 0) {
|
||||
/*
|
||||
* For http/1.x, the choices from
|
||||
* transaction_completed are either
|
||||
* 0 to use the connection for pipelined
|
||||
* or nonzero to hang it up.
|
||||
*
|
||||
* However for http/2. while we are
|
||||
* still interested in hanging up the
|
||||
* nwsi if there was a network-level
|
||||
* fatal error, simply completing the
|
||||
* transaction is a matter of the stream
|
||||
* state, not the root connection at the
|
||||
* network level
|
||||
*/
|
||||
if (wsi->http2_substream)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
} while (0); // while (!lws_send_pipe_choked(wsi))
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0; /* indicates further processing must be done */
|
||||
|
||||
file_had_it:
|
||||
lws_vfs_file_close(&wsi->http.fop_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
||||
{
|
||||
|
|
|
@ -332,7 +332,7 @@ extern "C" {
|
|||
#define LWS_MAX_PROTOCOLS 5
|
||||
#endif
|
||||
#ifndef LWS_MAX_EXTENSIONS_ACTIVE
|
||||
#define LWS_MAX_EXTENSIONS_ACTIVE 2
|
||||
#define LWS_MAX_EXTENSIONS_ACTIVE 1
|
||||
#endif
|
||||
#ifndef LWS_MAX_EXT_OFFERS
|
||||
#define LWS_MAX_EXT_OFFERS 8
|
||||
|
@ -491,7 +491,7 @@ enum lwsi_state {
|
|||
|
||||
LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 24,
|
||||
LRS_RETURNED_CLOSE = LWSIFS_POCB | 25,
|
||||
LRS_AWAITING_CLOSE_ACK = 26,
|
||||
LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 26,
|
||||
LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 27,
|
||||
LRS_SHUTDOWN = 28,
|
||||
|
||||
|
@ -1480,6 +1480,7 @@ struct lws {
|
|||
unsigned int told_user_closed:1;
|
||||
unsigned int told_event_loop_closed:1;
|
||||
unsigned int waiting_to_send_close_frame:1;
|
||||
unsigned int close_needs_ack:1;
|
||||
unsigned int ipv6:1;
|
||||
unsigned int parent_carries_io:1;
|
||||
unsigned int parent_pending_cb_on_writable:1;
|
||||
|
@ -1611,7 +1612,7 @@ lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
|
|||
#endif
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_client_rx_sm(struct lws *wsi, unsigned char c);
|
||||
lws_ws_client_rx_sm(struct lws *wsi, unsigned char c);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_parse(struct lws *wsi, unsigned char *buf, int *len);
|
||||
|
@ -1696,10 +1697,7 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
|||
lws_client_interpret_server_handshake(struct lws *wsi);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_ws_rx_sm(struct lws *wsi, unsigned char c);
|
||||
|
||||
LWS_EXTERN int
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len);
|
||||
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
@ -1755,13 +1753,13 @@ lws_change_pollfd(struct lws *wsi, int _and, int _or);
|
|||
LWS_EXTERN struct lws_vhost *
|
||||
lws_select_vhost(struct lws_context *context, int port, const char *servername);
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
LWS_EXTERN void
|
||||
lws_server_get_canonical_hostname(struct lws_context *context,
|
||||
struct lws_context_creation_info *info);
|
||||
#else
|
||||
#define lws_context_init_server(_a, _b) (0)
|
||||
#define lws_interpret_incoming_packet(_a, _b, _c) (0)
|
||||
#define lws_parse_ws(_a, _b, _c) (0)
|
||||
#define lws_server_get_canonical_hostname(_a, _b)
|
||||
#endif
|
||||
|
||||
|
@ -1939,13 +1937,10 @@ lws_http_transaction_completed_client(struct lws *wsi);
|
|||
#if !defined(LWS_WITH_TLS)
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
#endif
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
LWS_EXTERN void
|
||||
lws_decode_ssl_error(void);
|
||||
#else
|
||||
#define lws_context_init_client_ssl(_a, _b) (0)
|
||||
#define lws_handshake_client(_a, _b, _c) (0)
|
||||
#endif
|
||||
|
||||
LWS_EXTERN int
|
||||
|
@ -2150,6 +2145,9 @@ lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn);
|
|||
int
|
||||
lws_tls_server_conn_alpn(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* 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 <private-libwebsockets.h>
|
||||
|
||||
int
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
|
||||
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
|
||||
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
|
||||
!lwsi_role_client(wsi))
|
||||
return 0;
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_debug("%s: caching %ld\n", __func__, (long)len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
return 0;
|
||||
}
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
#endif
|
||||
m = lws_ws_rx_sm(wsi, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (lws_buflist_next_segment_len(&wsi->buflist, NULL) &&
|
||||
!lws_buflist_use_segment(&wsi->buflist, 1)) {
|
||||
lwsl_debug("%s: removed wsi %p from rxflow list\n", __func__, wsi);
|
||||
lws_dll_lws_remove(&wsi->dll_buflist);
|
||||
}
|
||||
|
||||
if (lws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_debug("client_rx_sm exited\n");
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -25,6 +25,54 @@
|
|||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
static int
|
||||
lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m;
|
||||
|
||||
if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
|
||||
(lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
|
||||
(lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
|
||||
!lwsi_role_client(wsi))
|
||||
return 0;
|
||||
|
||||
// lwsl_notice("%s: hs client gets %d in\n", __func__, (int)len);
|
||||
|
||||
while (len) {
|
||||
/*
|
||||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
//lwsl_notice("%s: caching %ld\n", __func__, (long)len);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
//lwsl_notice("%s: draining ext\n", __func__);
|
||||
if (lwsi_role_client(wsi))
|
||||
m = lws_ws_client_rx_sm(wsi, 0);
|
||||
else
|
||||
m = lws_ws_rx_sm(wsi, 0, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
/* caller will account for buflist usage */
|
||||
|
||||
if (lws_ws_client_rx_sm(wsi, *(*buf)++)) {
|
||||
lwsl_notice("%s: client_rx_sm exited, DROPPING %d\n",
|
||||
__func__, (int)len);
|
||||
return -1;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
// lwsl_notice("%s: finished with %ld\n", __func__, (long)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We have to take care about parsing because the headers may be split
|
||||
|
@ -67,10 +115,10 @@ lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
|||
assert(0);
|
||||
}
|
||||
lwsl_parser("issuing %d bytes to parser\n", (int)len);
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (lws_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. */
|
||||
|
@ -200,19 +248,22 @@ postbody_completion:
|
|||
case LRS_SHUTDOWN:
|
||||
|
||||
ws_mode:
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
// lwsl_notice("%s: ws_mode\n", __func__);
|
||||
if (lws_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_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
|
||||
lwsl_info("interpret_incoming_packet bailed\n");
|
||||
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:
|
||||
|
@ -253,8 +304,8 @@ bail:
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
#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];
|
||||
|
@ -456,6 +507,7 @@ fail:
|
|||
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
|
|
|
@ -2185,6 +2185,7 @@ lws_read_h2(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
|||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lws_rxflow_cache(wsi, buf, 0, (int)len);
|
||||
buf += len;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,22 +198,24 @@ read:
|
|||
wsi->context->pt_serv_buf_size);
|
||||
switch (ebuf.len) {
|
||||
case 0:
|
||||
lwsl_info("%s: zero length read\n",
|
||||
__func__);
|
||||
lwsl_info("%s: zero length read\n", __func__);
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
case LWS_SSL_CAPABLE_MORE_SERVICE:
|
||||
lwsl_info("SSL Capable more service\n");
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
|
||||
__func__);
|
||||
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n", __func__);
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
// lwsl_notice("Actual RX %d\n", ebuf.len);
|
||||
// lwsl_hexdump_notice(ebuf.token, 64);
|
||||
// lwsl_notice("%s: Actual RX %d\n", __func__, ebuf.len);
|
||||
// if (ebuf.len > 0)
|
||||
// lwsl_hexdump_notice(ebuf.token, ebuf.len);
|
||||
}
|
||||
|
||||
if (ebuf.len < 0)
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
|
||||
drain:
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (lwsi_role_http(wsi) && lwsi_role_client(wsi) &&
|
||||
|
@ -254,11 +256,11 @@ drain:
|
|||
n = 0;
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY) {
|
||||
n = lws_read_h2(wsi, (unsigned char *)ebuf.token,
|
||||
ebuf.len);
|
||||
ebuf.len);
|
||||
// lwsl_notice("h2 n = %d\n", n);
|
||||
} else {
|
||||
n = lws_read_h1(wsi, (unsigned char *)ebuf.token,
|
||||
ebuf.len);
|
||||
ebuf.len);
|
||||
// lwsl_notice("h1 n = %d\n", n);
|
||||
}
|
||||
|
||||
|
@ -296,8 +298,6 @@ drain:
|
|||
|
||||
pending = lws_ssl_pending(wsi);
|
||||
if (pending) {
|
||||
pending = pending > wsi->context->pt_serv_buf_size ?
|
||||
wsi->context->pt_serv_buf_size : pending;
|
||||
lwsl_err("going around\n");
|
||||
goto read;
|
||||
}
|
||||
|
|
|
@ -593,7 +593,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
|
|||
const char *path, const char *host)
|
||||
{
|
||||
char origin[300] = "", protocol[300] = "", method[32] = "",
|
||||
iface[16] = "", alpn[32], *p;
|
||||
iface[16] = "", alpn[32] = "", *p;
|
||||
struct lws *wsi = *pwsi;
|
||||
|
||||
if (wsi->redirects == 3) {
|
||||
|
|
|
@ -2335,6 +2335,252 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|||
return 0;
|
||||
}
|
||||
|
||||
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, *pstart;
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
unsigned char finished = 0;
|
||||
#endif
|
||||
int n, m;
|
||||
|
||||
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
|
||||
|
||||
do {
|
||||
|
||||
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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wsi->http.filepos == wsi->http.filelen)
|
||||
goto all_sent;
|
||||
|
||||
n = 0;
|
||||
|
||||
pstart = pt->serv_buf + LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
p = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges && !wsi->http.range.inside) {
|
||||
|
||||
lwsl_notice("%s: doing range start %llu\n", __func__,
|
||||
wsi->http.range.start);
|
||||
|
||||
if ((long long)lws_vfs_file_seek_cur(wsi->http.fop_fd,
|
||||
wsi->http.range.start -
|
||||
wsi->http.filepos) < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->http.filepos = wsi->http.range.start;
|
||||
|
||||
if (wsi->http.range.count_ranges > 1) {
|
||||
n = lws_snprintf((char *)p,
|
||||
context->pt_serv_buf_size -
|
||||
LWS_H2_FRAME_HEADER_LENGTH,
|
||||
"_lws\x0d\x0a"
|
||||
"Content-Type: %s\x0d\x0a"
|
||||
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
|
||||
"\x0d\x0a",
|
||||
wsi->http.multipart_content_type,
|
||||
wsi->http.range.start,
|
||||
wsi->http.range.end,
|
||||
wsi->http.range.extent);
|
||||
p += n;
|
||||
}
|
||||
|
||||
wsi->http.range.budget = wsi->http.range.end -
|
||||
wsi->http.range.start + 1;
|
||||
wsi->http.range.inside = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
|
||||
|
||||
if (wsi->http.tx_content_length)
|
||||
if (poss > wsi->http.tx_content_remain)
|
||||
poss = wsi->http.tx_content_remain;
|
||||
|
||||
/*
|
||||
* 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 (wsi->role_ops->tx_credit) {
|
||||
lws_filepos_t txc = wsi->role_ops->tx_credit(wsi);
|
||||
|
||||
if (!txc) {
|
||||
lwsl_info("%s: came here with no tx credit\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
if (txc < poss)
|
||||
poss = txc;
|
||||
|
||||
/*
|
||||
* consumption of the actual payload amount sent will be
|
||||
* handled when the role data frame is sent
|
||||
*/
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges) {
|
||||
if (wsi->http.range.count_ranges > 1)
|
||||
poss -= 7; /* allow for final boundary */
|
||||
if (poss > wsi->http.range.budget)
|
||||
poss = wsi->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->http.fop_fd, &amount, p, poss) < 0)
|
||||
goto file_had_it; /* caller will close */
|
||||
|
||||
if (wsi->sending_chunked)
|
||||
n = (int)amount;
|
||||
else
|
||||
n = lws_ptr_diff(p, pstart) + (int)amount;
|
||||
|
||||
lwsl_debug("%s: sending %d\n", __func__, n);
|
||||
|
||||
if (n) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
context->timeout_secs);
|
||||
|
||||
if (wsi->interpreting) {
|
||||
args.p = (char *)p;
|
||||
args.len = n;
|
||||
args.max_len = (unsigned int)poss + 128;
|
||||
args.final = wsi->http.filepos + n ==
|
||||
wsi->http.filelen;
|
||||
args.chunked = wsi->sending_chunked;
|
||||
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 = pstart;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.send_ctr + 1 ==
|
||||
wsi->http.range.count_ranges && // last range
|
||||
wsi->http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
|
||||
wsi->http.range.budget - amount == 0) {// final part
|
||||
n += lws_snprintf((char *)pstart + n, 6,
|
||||
"_lws\x0d\x0a"); // append trailing boundary
|
||||
lwsl_debug("added trailing boundary\n");
|
||||
}
|
||||
#endif
|
||||
m = lws_write(wsi, p, n,
|
||||
wsi->http.filepos + amount == wsi->http.filelen ?
|
||||
LWS_WRITE_HTTP_FINAL :
|
||||
LWS_WRITE_HTTP
|
||||
);
|
||||
if (m < 0)
|
||||
goto file_had_it;
|
||||
|
||||
wsi->http.filepos += amount;
|
||||
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
if (wsi->http.range.count_ranges >= 1) {
|
||||
wsi->http.range.budget -= amount;
|
||||
if (wsi->http.range.budget == 0) {
|
||||
lwsl_notice("range budget exhausted\n");
|
||||
wsi->http.range.inside = 0;
|
||||
wsi->http.range.send_ctr++;
|
||||
|
||||
if (lws_ranges_next(&wsi->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->http.fop_fd,
|
||||
m - n) ==
|
||||
(lws_fileofs_t)-1)
|
||||
goto file_had_it;
|
||||
}
|
||||
}
|
||||
|
||||
all_sent:
|
||||
if ((!wsi->trunc_len && wsi->http.filepos >= wsi->http.filelen)
|
||||
#if defined(LWS_WITH_RANGES)
|
||||
|| finished)
|
||||
#else
|
||||
)
|
||||
#endif
|
||||
{
|
||||
lwsi_set_state(wsi, LRS_ESTABLISHED);
|
||||
/* we might be in keepalive, so close it off here */
|
||||
lws_vfs_file_close(&wsi->http.fop_fd);
|
||||
|
||||
lwsl_debug("file completed\n");
|
||||
|
||||
if (wsi->protocol->callback &&
|
||||
user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL,
|
||||
0) < 0) {
|
||||
/*
|
||||
* For http/1.x, the choices from
|
||||
* transaction_completed are either
|
||||
* 0 to use the connection for pipelined
|
||||
* or nonzero to hang it up.
|
||||
*
|
||||
* However for http/2. while we are
|
||||
* still interested in hanging up the
|
||||
* nwsi if there was a network-level
|
||||
* fatal error, simply completing the
|
||||
* transaction is a matter of the stream
|
||||
* state, not the root connection at the
|
||||
* network level
|
||||
*/
|
||||
if (wsi->http2_substream)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
} while (0); // while (!lws_send_pipe_choked(wsi))
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0; /* indicates further processing must be done */
|
||||
|
||||
file_had_it:
|
||||
lws_vfs_file_close(&wsi->http.fop_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_server_get_canonical_hostname(struct lws_context *context,
|
||||
struct lws_context_creation_info *info)
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* sync with changes here, esp related to ext draining
|
||||
*/
|
||||
|
||||
int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
||||
int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
|
||||
{
|
||||
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
|
||||
int handled, n, m, rx_draining_ext = 0;
|
||||
|
@ -34,10 +34,12 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
struct lws_tokens ebuf;
|
||||
unsigned char *pp;
|
||||
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
assert(!c);
|
||||
ebuf.token = NULL;
|
||||
ebuf.len = 0;
|
||||
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
rx_draining_ext = 1;
|
||||
lwsl_debug("%s: doing draining flow\n", __func__);
|
||||
|
@ -65,17 +67,20 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
wsi->context->options,
|
||||
LWS_SERVER_OPTION_VALIDATE_UTF8);
|
||||
wsi->ws->utf8 = 0;
|
||||
wsi->ws->first_fragment = 1;
|
||||
break;
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
wsi->ws->rsv_first_msg = (c & 0x70);
|
||||
wsi->ws->check_utf8 = 0;
|
||||
wsi->ws->continuation_possible = 1;
|
||||
wsi->ws->first_fragment = 1;
|
||||
break;
|
||||
case LWSWSOPC_CONTINUATION:
|
||||
if (!wsi->ws->continuation_possible) {
|
||||
lwsl_info("disordered continuation\n");
|
||||
return -1;
|
||||
}
|
||||
wsi->ws->first_fragment = 0;
|
||||
break;
|
||||
case LWSWSOPC_CLOSE:
|
||||
wsi->ws->check_utf8 = 0;
|
||||
|
@ -164,15 +169,15 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
|
||||
break;
|
||||
default:
|
||||
wsi->ws->rx_packet_length = c;
|
||||
wsi->ws->rx_packet_length = c & 0x7f;
|
||||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else {
|
||||
if (c)
|
||||
if (wsi->ws->rx_packet_length) {
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
else {
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
} else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
|
@ -193,7 +198,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
else {
|
||||
if (wsi->ws->rx_packet_length)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
|
@ -259,7 +264,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
else {
|
||||
if (wsi->ws->rx_packet_length)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
|
@ -295,14 +300,14 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
|
|||
|
||||
if (wsi->ws->rx_packet_length)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
|
||||
case LWS_RXPS_WS_FRAME_PAYLOAD:
|
||||
|
||||
assert(wsi->ws->rx_ubuf);
|
||||
|
||||
|
@ -387,26 +392,19 @@ spill:
|
|||
wsi->ws->rx_ubuf_head))
|
||||
return -1;
|
||||
|
||||
if (lws_partial_buffered(wsi))
|
||||
/*
|
||||
* if we're in the middle of something,
|
||||
* we can't do a normal close response and
|
||||
* have to just close our end.
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
else
|
||||
/*
|
||||
* parrot the close packet payload back
|
||||
* we do not care about how it went, we are closing
|
||||
* immediately afterwards
|
||||
*/
|
||||
lws_write(wsi, (unsigned char *)
|
||||
&wsi->ws->rx_ubuf[LWS_PRE],
|
||||
wsi->ws->rx_ubuf_head,
|
||||
LWS_WRITE_CLOSE);
|
||||
lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
|
||||
/* close the connection */
|
||||
return -1;
|
||||
memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
|
||||
wsi->ws->rx_ubuf_head);
|
||||
wsi->ws->close_in_ping_buffer_len = wsi->ws->rx_ubuf_head;
|
||||
|
||||
lwsl_notice("%s: scheduling return close as ack\n", __func__);
|
||||
__lws_change_pollfd(wsi, LWS_POLLIN, 0);
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
|
||||
wsi->waiting_to_send_close_frame = 1;
|
||||
wsi->close_needs_ack = 0;
|
||||
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
|
||||
lws_callback_on_writable(wsi);
|
||||
handled = 1;
|
||||
break;
|
||||
|
||||
case LWSWSOPC_PING:
|
||||
lwsl_info("received %d byte ping, sending pong\n",
|
||||
|
@ -467,30 +465,11 @@ ping_drop:
|
|||
break;
|
||||
|
||||
default:
|
||||
/* not handled or failed */
|
||||
lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
|
||||
lwsl_parser("Reserved opc 0x%2X\n", wsi->ws->opcode);
|
||||
|
||||
/*
|
||||
* It's something special we can't understand here.
|
||||
* Pass the payload up to the extension's parsing
|
||||
* state machine.
|
||||
*/
|
||||
|
||||
ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
ebuf.len = wsi->ws->rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
|
||||
&ebuf, 0) <= 0) {
|
||||
/* not handled or failed */
|
||||
lwsl_ext("Unhandled ext opc 0x%x\n",
|
||||
wsi->ws->opcode);
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
handled = 1;
|
||||
break;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -520,7 +499,7 @@ drain_extension:
|
|||
#else
|
||||
n = 0;
|
||||
#endif
|
||||
lwsl_ext("post inflate ebuf len %d\n", ebuf.len);
|
||||
lwsl_notice("post inflate ebuf len %d\n", ebuf.len);
|
||||
|
||||
if (rx_draining_ext && !ebuf.len) {
|
||||
lwsl_debug(" --- ending drain on 0 read result\n");
|
||||
|
@ -530,15 +509,24 @@ drain_extension:
|
|||
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
|
||||
if (lws_check_utf8(&wsi->ws->utf8,
|
||||
(unsigned char *)ebuf.token,
|
||||
ebuf.len))
|
||||
ebuf.len)) {
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"bad utf8", 8);
|
||||
goto utf8_fail;
|
||||
}
|
||||
|
||||
/* we are ending partway through utf-8 character? */
|
||||
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
|
||||
wsi->ws->utf8 && !n) {
|
||||
lwsl_info("FINAL utf8 error\n");
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"partial utf8", 12);
|
||||
utf8_fail:
|
||||
lwsl_info("utf8 error\n");
|
||||
lwsl_hexdump_info(ebuf.token, ebuf.len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -582,6 +570,11 @@ utf8_fail:
|
|||
(enum lws_callback_reasons)callback_action,
|
||||
wsi->user_space, ebuf.token, ebuf.len);
|
||||
|
||||
wsi->ws->first_fragment = 0;
|
||||
|
||||
// lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
|
||||
// __func__, wsi->ws->rx_ubuf_head, ebuf.len);
|
||||
|
||||
/* if user code wants to close, let caller know */
|
||||
if (m)
|
||||
return 1;
|
||||
|
|
|
@ -99,14 +99,7 @@ lws_generate_client_ws_handshake(struct lws *wsi, char *p)
|
|||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
ext = wsi->vhost->extensions;
|
||||
while (ext && ext->callback) {
|
||||
n = lws_ext_cb_all_exts(wsi->context, wsi,
|
||||
LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION,
|
||||
(char *)ext->name, 0);
|
||||
if (n) { /* an extension vetos us */
|
||||
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
n = wsi->vhost->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
|
||||
wsi->user_space, (char *)ext->name, 0);
|
||||
|
@ -172,7 +165,6 @@ lws_client_ws_upgrade(struct lws *wsi, const char **cce)
|
|||
const char *c, *a;
|
||||
char ignore;
|
||||
int more = 1;
|
||||
void *v;
|
||||
#endif
|
||||
|
||||
if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
|
||||
|
@ -575,24 +567,6 @@ check_accept:
|
|||
*cce = "HS: Rejected at CLIENT_ESTABLISHED";
|
||||
goto bail3;
|
||||
}
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* inform all extensions, not just active ones since they
|
||||
* already know
|
||||
*/
|
||||
ext = wsi->vhost->extensions;
|
||||
|
||||
while (ext && ext->callback) {
|
||||
v = NULL;
|
||||
for (n = 0; n < wsi->count_act_ext; n++)
|
||||
if (wsi->active_extensions[n] == ext)
|
||||
v = wsi->act_ext_user[n];
|
||||
|
||||
ext->callback(context, ext, wsi,
|
||||
LWS_EXT_CB_ANY_WSI_ESTABLISHED, v, NULL, 0);
|
||||
ext++;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
oa = in;
|
||||
if (!oa->option_name)
|
||||
break;
|
||||
lwsl_ext("%s: named option set: %s\n", __func__, oa->option_name);
|
||||
for (n = 0; n < (int)ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
|
||||
if (!strcmp(lws_ext_pm_deflate_options[n].name,
|
||||
oa->option_name))
|
||||
|
@ -95,8 +96,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
|
||||
case LWS_EXT_CB_OPTION_SET:
|
||||
oa = in;
|
||||
lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
|
||||
oa->option_index, oa->start, oa->len);
|
||||
lwsl_ext("%s: option set: idx %d, %s, len %d\n", __func__,
|
||||
oa->option_index, oa->start, oa->len);
|
||||
if (oa->start)
|
||||
priv->args[oa->option_index] = atoi(oa->start);
|
||||
else
|
||||
|
@ -179,14 +180,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
if (!(wsi->ws->rsv_first_msg & 0x40))
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
for (n = 0; n < ebuf->len; n++) {
|
||||
printf("%02X ", (unsigned char)ebuf->token[n]);
|
||||
if ((n & 15) == 15)
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
// lwsl_hexdump_debug(ebuf->token, ebuf->len);
|
||||
|
||||
if (!priv->rx_init)
|
||||
if (inflateInit2(&priv->rx,
|
||||
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
|
||||
|
@ -250,7 +245,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
case Z_STREAM_ERROR:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
lwsl_info("zlib error inflate %d: %s\n",
|
||||
lwsl_notice("zlib error inflate %d: %s\n",
|
||||
n, priv->rx.msg);
|
||||
return -1;
|
||||
}
|
||||
|
@ -320,15 +315,13 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
if (was_fin) {
|
||||
priv->count_rx_between_fin = 0;
|
||||
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
|
||||
lwsl_ext("PMD_SERVER_NO_CONTEXT_TAKEOVER\n");
|
||||
(void)inflateEnd(&priv->rx);
|
||||
priv->rx_init = 0;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
for (n = 0; n < ebuf->len; n++)
|
||||
putchar(ebuf->token[n]);
|
||||
puts("\n");
|
||||
#endif
|
||||
|
||||
// lwsl_hexdump_debug(ebuf->token, ebuf->len);
|
||||
|
||||
return priv->rx_held_valid;
|
||||
|
||||
|
|
|
@ -24,18 +24,21 @@
|
|||
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
|
||||
|
||||
/*
|
||||
* client-parser.c: lws_client_rx_sm() needs to be roughly kept in
|
||||
* client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in
|
||||
* sync with changes here, esp related to ext draining
|
||||
*/
|
||||
|
||||
int
|
||||
lws_ws_rx_sm(struct lws *wsi, unsigned char c)
|
||||
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
|
||||
{
|
||||
int callback_action = LWS_CALLBACK_RECEIVE;
|
||||
int ret = 0, rx_draining_ext = 0;
|
||||
unsigned short close_code;
|
||||
struct lws_tokens ebuf;
|
||||
unsigned char *pp;
|
||||
int n = 0;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
int n;
|
||||
int lin;
|
||||
#endif
|
||||
|
||||
ebuf.token = NULL;
|
||||
|
@ -131,14 +134,42 @@ handle_first:
|
|||
wsi->ws->opcode = c & 0xf;
|
||||
wsi->ws->rsv = c & 0x70;
|
||||
wsi->ws->final = !!((c >> 7) & 1);
|
||||
wsi->ws->defeat_check_utf8 = 0;
|
||||
|
||||
if (((wsi->ws->opcode) & 8) && !wsi->ws->final) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
|
||||
(uint8_t *)"frag ctl", 8);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_TEXT_FRAME:
|
||||
wsi->ws->check_utf8 = lws_check_opt(
|
||||
wsi->context->options,
|
||||
LWS_SERVER_OPTION_VALIDATE_UTF8);
|
||||
/* fallthru */
|
||||
case LWSWSOPC_BINARY_FRAME:
|
||||
if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)
|
||||
wsi->ws->check_utf8 = 0;
|
||||
if (wsi->ws->continuation_possible) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
|
||||
return -1;
|
||||
}
|
||||
wsi->ws->rsv_first_msg = (c & 0x70);
|
||||
wsi->ws->frame_is_binary =
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
|
||||
wsi->ws->first_fragment = 1;
|
||||
wsi->ws->continuation_possible = !wsi->ws->final;
|
||||
break;
|
||||
case LWSWSOPC_CONTINUATION:
|
||||
if (!wsi->ws->continuation_possible) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case LWSWSOPC_CLOSE:
|
||||
wsi->ws->check_utf8 = 0;
|
||||
wsi->ws->utf8 = 0;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
|
@ -150,10 +181,37 @@ handle_first:
|
|||
case 0xd:
|
||||
case 0xe:
|
||||
case 0xf:
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad opc", 7);
|
||||
lwsl_info("illegal opcode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wsi->ws->owed_a_fin &&
|
||||
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
|
||||
lwsl_info("hey you owed us a FIN\n");
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad fin", 7);
|
||||
return -1;
|
||||
}
|
||||
if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
|
||||
wsi->ws->continuation_possible = 0;
|
||||
wsi->ws->owed_a_fin = 0;
|
||||
}
|
||||
|
||||
if (!wsi->ws->final)
|
||||
wsi->ws->owed_a_fin = 1;
|
||||
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
|
||||
if (wsi->ws->rsv &&
|
||||
(
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
!wsi->count_act_ext ||
|
||||
#endif
|
||||
(wsi->ws->rsv & ~0x40))) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
|
||||
(uint8_t *)"rsv bits", 8);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN:
|
||||
|
@ -177,14 +235,16 @@ handle_first:
|
|||
break;
|
||||
default:
|
||||
wsi->ws->rx_packet_length = c & 0x7f;
|
||||
|
||||
|
||||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
if (wsi->ws->rx_packet_length)
|
||||
if (wsi->ws->rx_packet_length) {
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
else {
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
} else {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
|
@ -202,9 +262,10 @@ handle_first:
|
|||
if (wsi->ws->this_frame_masked)
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
else {
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
|
||||
|
@ -263,8 +324,7 @@ handle_first:
|
|||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
|
||||
else
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
break;
|
||||
|
||||
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
|
||||
|
@ -292,8 +352,7 @@ handle_first:
|
|||
wsi->ws->mask[3] = c;
|
||||
if (c)
|
||||
wsi->ws->all_zero_nonce = 0;
|
||||
wsi->lws_rx_parse_state =
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
|
||||
wsi->ws->mask_idx = 0;
|
||||
if (wsi->ws->rx_packet_length == 0) {
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
|
@ -302,32 +361,36 @@ handle_first:
|
|||
break;
|
||||
|
||||
|
||||
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
|
||||
case LWS_RXPS_WS_FRAME_PAYLOAD:
|
||||
assert(wsi->ws->rx_ubuf);
|
||||
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
goto drain_extension;
|
||||
|
||||
if (wsi->ws->rx_ubuf_head + LWS_PRE >=
|
||||
wsi->ws->rx_ubuf_alloc) {
|
||||
if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) {
|
||||
lwsl_err("Attempted overflow \n");
|
||||
return -1;
|
||||
}
|
||||
if (wsi->ws->all_zero_nonce)
|
||||
wsi->ws->rx_ubuf[LWS_PRE +
|
||||
(wsi->ws->rx_ubuf_head++)] = c;
|
||||
else
|
||||
wsi->ws->rx_ubuf[LWS_PRE +
|
||||
(wsi->ws->rx_ubuf_head++)] =
|
||||
c ^ wsi->ws->mask[
|
||||
(wsi->ws->mask_idx++) & 3];
|
||||
if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) {
|
||||
if (wsi->ws->all_zero_nonce)
|
||||
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
|
||||
c;
|
||||
else
|
||||
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
|
||||
c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
|
||||
|
||||
if (--wsi->ws->rx_packet_length == 0) {
|
||||
--wsi->ws->rx_packet_length;
|
||||
}
|
||||
|
||||
if (!wsi->ws->rx_packet_length) {
|
||||
lwsl_debug("%s: ws fragment length exhausted\n", __func__);
|
||||
/* spill because we have the whole frame */
|
||||
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
|
||||
goto spill;
|
||||
}
|
||||
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
|
||||
goto drain_extension;
|
||||
}
|
||||
|
||||
/*
|
||||
* if there's no protocol max frame size given, we are
|
||||
* supposed to default to context->pt_serv_buf_size
|
||||
|
@ -352,6 +415,19 @@ spill:
|
|||
switch (wsi->ws->opcode) {
|
||||
case LWSWSOPC_CLOSE:
|
||||
|
||||
if (wsi->ws->peer_has_sent_close)
|
||||
break;
|
||||
|
||||
wsi->ws->peer_has_sent_close = 1;
|
||||
|
||||
pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
|
||||
if (lws_check_opt(wsi->context->options,
|
||||
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
|
||||
wsi->ws->rx_ubuf_head > 2 &&
|
||||
lws_check_utf8(&wsi->ws->utf8, pp + 2,
|
||||
wsi->ws->rx_ubuf_head - 2))
|
||||
goto utf8_fail;
|
||||
|
||||
/* is this an acknowledgment of our close? */
|
||||
if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
|
||||
/*
|
||||
|
@ -376,6 +452,23 @@ spill:
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (wsi->ws->rx_ubuf_head >= 2) {
|
||||
close_code = (pp[0] << 8) | pp[1];
|
||||
if (close_code < 1000 ||
|
||||
close_code == 1004 ||
|
||||
close_code == 1005 ||
|
||||
close_code == 1006 ||
|
||||
close_code == 1012 ||
|
||||
close_code == 1013 ||
|
||||
close_code == 1014 ||
|
||||
close_code == 1015 ||
|
||||
(close_code >= 1016 && close_code < 3000)
|
||||
) {
|
||||
pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
|
||||
pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
|
||||
|
@ -445,26 +538,9 @@ ping_drop:
|
|||
break;
|
||||
|
||||
default:
|
||||
lwsl_parser("passing opc %x up to exts\n",
|
||||
wsi->ws->opcode);
|
||||
/*
|
||||
* It's something special we can't understand here.
|
||||
* Pass the payload up to the extension's parsing
|
||||
* state machine.
|
||||
*/
|
||||
lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
|
||||
|
||||
ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
|
||||
ebuf.len = wsi->ws->rx_ubuf_head;
|
||||
|
||||
if (lws_ext_cb_active(wsi,
|
||||
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
|
||||
&ebuf, 0) <= 0)
|
||||
/* not handle or fail */
|
||||
lwsl_ext("ext opc opcode 0x%x unknown\n",
|
||||
wsi->ws->opcode);
|
||||
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -480,19 +556,24 @@ ping_drop:
|
|||
goto already_done;
|
||||
|
||||
drain_extension:
|
||||
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
|
||||
// lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
|
||||
goto already_done;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lin = ebuf.len;
|
||||
//if (lin)
|
||||
// lwsl_hexdump_notice(ebuf.token, ebuf.len);
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
|
||||
lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
already_processed &= ~ALREADY_PROCESSED_NO_CB;
|
||||
#endif
|
||||
/*
|
||||
* ebuf may be pointing somewhere completely different now,
|
||||
* it's the output
|
||||
*/
|
||||
wsi->ws->first_fragment = 0;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (n < 0) {
|
||||
/*
|
||||
|
@ -515,11 +596,38 @@ drain_extension:
|
|||
else
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
|
||||
if (ebuf.len > 0 ||
|
||||
callback_action == LWS_CALLBACK_RECEIVE_PONG) {
|
||||
ebuf.token[ebuf.len] = '\0';
|
||||
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
|
||||
if (lws_check_utf8(&wsi->ws->utf8,
|
||||
(unsigned char *)ebuf.token,
|
||||
ebuf.len)) {
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"bad utf8", 8);
|
||||
goto utf8_fail;
|
||||
}
|
||||
|
||||
if (wsi->protocol->callback) {
|
||||
/* we are ending partway through utf-8 character? */
|
||||
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
|
||||
wsi->ws->utf8 && !n) {
|
||||
lwsl_info("FINAL utf8 error\n");
|
||||
lws_close_reason(wsi,
|
||||
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"partial utf8", 12);
|
||||
utf8_fail:
|
||||
lwsl_notice("utf8 error\n");
|
||||
lwsl_hexdump_notice(ebuf.token, ebuf.len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wsi->wsistate_pre_close && (ebuf.len >= 0 ||
|
||||
callback_action == LWS_CALLBACK_RECEIVE_PONG)) {
|
||||
if (ebuf.len)
|
||||
ebuf.token[ebuf.len] = '\0';
|
||||
|
||||
if (wsi->protocol->callback &&
|
||||
!(already_processed & ALREADY_PROCESSED_NO_CB)) {
|
||||
if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
|
||||
lwsl_info("Doing pong callback\n");
|
||||
|
||||
|
@ -531,10 +639,14 @@ drain_extension:
|
|||
ebuf.token,
|
||||
ebuf.len);
|
||||
}
|
||||
else
|
||||
lwsl_err("No callback on payload spill!\n");
|
||||
wsi->ws->first_fragment = 0;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (!lin)
|
||||
break;
|
||||
#endif
|
||||
|
||||
already_done:
|
||||
wsi->ws->rx_ubuf_head = 0;
|
||||
break;
|
||||
|
@ -569,7 +681,7 @@ lws_add_wsi_to_draining_ext_list(struct lws *wsi)
|
|||
if (wsi->ws->rx_draining_ext)
|
||||
return;
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
|
||||
lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
|
||||
|
||||
wsi->ws->rx_draining_ext = 1;
|
||||
wsi->ws->rx_draining_ext_list = pt->rx_draining_ext_list;
|
||||
|
@ -585,7 +697,7 @@ lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
|
|||
if (!wsi->ws->rx_draining_ext)
|
||||
return;
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
|
||||
lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__);
|
||||
|
||||
wsi->ws->rx_draining_ext = 0;
|
||||
|
||||
|
@ -630,70 +742,6 @@ lws_0405_frame_mask_generate(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
|
||||
* to expect in that state and can deal with it in bulk more efficiently.
|
||||
*/
|
||||
|
||||
int
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
|
||||
size_t *len)
|
||||
{
|
||||
unsigned char *buffer = *buf, mask[4];
|
||||
int buffer_size, n;
|
||||
unsigned int avail;
|
||||
char *rx_ubuf;
|
||||
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
buffer_size = (int)wsi->protocol->rx_buffer_size;
|
||||
else
|
||||
buffer_size = wsi->context->pt_serv_buf_size;
|
||||
avail = buffer_size - wsi->ws->rx_ubuf_head;
|
||||
|
||||
/* do not consume more than we should */
|
||||
if (avail > wsi->ws->rx_packet_length)
|
||||
avail = (unsigned int)wsi->ws->rx_packet_length;
|
||||
|
||||
/* do not consume more than what is in the buffer */
|
||||
if (avail > *len)
|
||||
avail = (unsigned int)(*len);
|
||||
|
||||
/* we want to leave 1 byte for the parser to handle properly */
|
||||
if (avail <= 1)
|
||||
return 0;
|
||||
|
||||
avail--;
|
||||
rx_ubuf = wsi->ws->rx_ubuf + LWS_PRE + wsi->ws->rx_ubuf_head;
|
||||
if (wsi->ws->all_zero_nonce)
|
||||
memcpy(rx_ubuf, buffer, avail);
|
||||
else {
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
|
||||
|
||||
/* deal with 4-byte chunks using unwrapped loop */
|
||||
n = avail >> 2;
|
||||
while (n--) {
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[0];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[1];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[2];
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[3];
|
||||
}
|
||||
/* and the remaining bytes bytewise */
|
||||
for (n = 0; n < (int)(avail & 3); n++)
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[n];
|
||||
|
||||
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
|
||||
}
|
||||
|
||||
(*buf) += avail;
|
||||
wsi->ws->rx_ubuf_head += avail;
|
||||
wsi->ws->rx_packet_length -= avail;
|
||||
*len -= avail;
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_server_init_wsi_for_ws(struct lws *wsi)
|
||||
{
|
||||
|
@ -753,7 +801,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
|
|||
LWS_VISIBLE int
|
||||
lws_is_final_fragment(struct lws *wsi)
|
||||
{
|
||||
lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
|
||||
lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
|
||||
wsi->ws->final, (long)wsi->ws->rx_packet_length,
|
||||
(long)wsi->ws->rx_draining_ext);
|
||||
return wsi->ws->final && !wsi->ws->rx_packet_length &&
|
||||
|
@ -821,16 +869,21 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
{
|
||||
struct lws_tokens ebuf;
|
||||
unsigned int pending = 0;
|
||||
char draining_flow = 0, buffered = 0;
|
||||
char buffered = 0;
|
||||
int n = 0, m;
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
struct lws *wsi1;
|
||||
#endif
|
||||
|
||||
if (!wsi->ws) {
|
||||
lwsl_err("ws role wsi with no ws\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
|
||||
|
||||
lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
|
||||
wsi->wsistate, pollfd->revents & LWS_POLLOUT);
|
||||
//lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
|
||||
// wsi->wsistate, pollfd->revents & LWS_POLLOUT);
|
||||
|
||||
/*
|
||||
* something went wrong with parsing the handshake, and
|
||||
|
@ -859,6 +912,8 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
//lwsl_notice("%s: wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n", __func__, wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate, lwsi_state_can_handle_POLLOUT(wsi));
|
||||
|
||||
/* 1: something requested a callback when it was OK to write */
|
||||
|
||||
if ((pollfd->revents & LWS_POLLOUT) &&
|
||||
|
@ -866,14 +921,12 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
lws_handle_POLLOUT_event(wsi, pollfd)) {
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
|
||||
lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
|
||||
/* the write failed... it's had it */
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
||||
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
|
||||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
|
||||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
|
||||
/*
|
||||
* we stopped caring about anything except control
|
||||
* packets. Force flow control off, defeat tx
|
||||
|
@ -884,7 +937,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
wsi->ws->tx_draining_ext = 0;
|
||||
}
|
||||
|
||||
if (wsi->ws && wsi->ws->tx_draining_ext)
|
||||
if (wsi->ws->tx_draining_ext)
|
||||
/*
|
||||
* We cannot deal with new RX until the TX ext path has
|
||||
* been drained. It's because new rx will, eg, crap on
|
||||
|
@ -895,11 +948,13 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
*/
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
if (lws_is_flowcontrolled(wsi))
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
/* We cannot deal with any kind of new RX because we are
|
||||
* RX-flowcontrolled.
|
||||
*/
|
||||
lwsl_info("flowcontrolled\n");
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (wsi->http2_substream || wsi->upgraded_to_http2) {
|
||||
|
@ -917,23 +972,23 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
/* 2: RX Extension needs to be drained
|
||||
*/
|
||||
|
||||
if (lwsi_role_ws(wsi) && wsi->ws && wsi->ws->rx_draining_ext) {
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
|
||||
lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
|
||||
lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__);
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (lwsi_role_client(wsi)) {
|
||||
n = lws_client_rx_sm(wsi, 0);
|
||||
n = lws_ws_client_rx_sm(wsi, 0);
|
||||
if (n < 0)
|
||||
/* we closed wsi */
|
||||
n = 0;
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
} else
|
||||
#endif
|
||||
n = lws_ws_rx_sm(wsi, 0);
|
||||
n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
|
||||
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
}
|
||||
|
||||
if (wsi->ws && wsi->ws->rx_draining_ext)
|
||||
if (wsi->ws->rx_draining_ext)
|
||||
/*
|
||||
* We have RX EXT content to drain, but can't do it
|
||||
* right now. That means we cannot do anything lower
|
||||
|
@ -943,7 +998,8 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
|
||||
/* 3: buflist needs to be drained
|
||||
*/
|
||||
|
||||
read:
|
||||
//lws_buflist_describe(&wsi->buflist, wsi);
|
||||
ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
|
||||
(uint8_t **)&ebuf.token);
|
||||
if (ebuf.len) {
|
||||
|
@ -955,7 +1011,6 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
|
|||
if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->ah)
|
||||
return LWS_HPI_RET_HANDLED;
|
||||
|
||||
read:
|
||||
if (lws_is_flowcontrolled(wsi)) {
|
||||
lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
|
||||
__func__, wsi, wsi->rxflow_bitmap);
|
||||
|
@ -964,18 +1019,21 @@ read:
|
|||
|
||||
if (!(lwsi_role_client(wsi) &&
|
||||
(lwsi_state(wsi) != LRS_ESTABLISHED &&
|
||||
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK &&
|
||||
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
|
||||
/*
|
||||
* extension may not consume everything
|
||||
* (eg, pmd may be constrained
|
||||
* as to what it can output...) has to go in
|
||||
* per-wsi rx buf area.
|
||||
* Otherwise in large temp serv_buf area.
|
||||
* In case we are going to react to this rx by scheduling
|
||||
* writes, we need to restrict the amount of rx to the size
|
||||
* the protocol reported for rx buffer.
|
||||
*
|
||||
* Otherwise we get a situation we have to absorb possibly a
|
||||
* lot of reads before we get a chance to drain them by writing
|
||||
* them, eg, with echo type tests in autobahn.
|
||||
*/
|
||||
|
||||
buffered = 0;
|
||||
ebuf.token = (char *)pt->serv_buf;
|
||||
if (lws_is_ws_with_ext(wsi))
|
||||
if (lwsi_role_ws(wsi))
|
||||
ebuf.len = wsi->ws->rx_ubuf_alloc;
|
||||
else
|
||||
ebuf.len = wsi->context->pt_serv_buf_size;
|
||||
|
@ -1026,15 +1084,9 @@ drain:
|
|||
*/
|
||||
m = 0;
|
||||
do {
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE,
|
||||
&ebuf, 0);
|
||||
if (m < 0)
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
#endif
|
||||
|
||||
/* service incoming data */
|
||||
|
||||
//lws_buflist_describe(&wsi->buflist, wsi);
|
||||
if (ebuf.len) {
|
||||
#if defined(LWS_ROLE_H2)
|
||||
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
|
||||
|
@ -1050,6 +1102,8 @@ drain:
|
|||
n = 0;
|
||||
return LWS_HPI_RET_WSI_ALREADY_DIED;
|
||||
}
|
||||
//lws_buflist_describe(&wsi->buflist, wsi);
|
||||
//lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len);
|
||||
if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
|
||||
return LWS_HPI_RET_PLEASE_CLOSE_ME;
|
||||
}
|
||||
|
@ -1078,7 +1132,7 @@ drain:
|
|||
goto read;
|
||||
}
|
||||
|
||||
if (draining_flow && /* were draining, now nothing left */
|
||||
if (buffered && /* were draining, now nothing left */
|
||||
!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
|
||||
lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
|
||||
/* having drained the rxflow buffer, can rearm POLLIN */
|
||||
|
@ -1103,7 +1157,8 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
#endif
|
||||
int n;
|
||||
|
||||
// lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
|
||||
lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
|
||||
wsi->protocol->name, wsi->ws->tx_draining_ext);
|
||||
|
||||
/* Priority 3: pending control packets (pong or close)
|
||||
*
|
||||
|
@ -1112,16 +1167,22 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
|
||||
if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
|
||||
lwsl_debug("sending close packet\n");
|
||||
lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE],
|
||||
wsi->ws->close_in_ping_buffer_len);
|
||||
wsi->waiting_to_send_close_frame = 0;
|
||||
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
|
||||
wsi->ws->close_in_ping_buffer_len,
|
||||
LWS_WRITE_CLOSE);
|
||||
if (n >= 0) {
|
||||
lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
|
||||
lwsl_debug("sent close indication, awaiting ack\n");
|
||||
if (wsi->close_needs_ack) {
|
||||
lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
|
||||
lwsl_debug("sent close indication, awaiting ack\n");
|
||||
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
wsi->close_needs_ack = 0;
|
||||
lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
|
||||
}
|
||||
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
@ -1135,6 +1196,14 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
|
||||
if (wsi->ws->payload_is_close)
|
||||
write_type = LWS_WRITE_CLOSE;
|
||||
else {
|
||||
if (wsi->wsistate_pre_close) {
|
||||
/* we started close flow, forget pong */
|
||||
wsi->ws->ping_pending_flag = 0;
|
||||
return LWS_HP_RET_BAIL_OK;
|
||||
}
|
||||
lwsl_info("issuing pong %d on wsi %p\n", wsi->ws->ping_payload_len, wsi);
|
||||
}
|
||||
|
||||
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
|
||||
wsi->ws->ping_payload_len, write_type);
|
||||
|
@ -1199,10 +1268,6 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
|
|||
/* Priority 6: extensions
|
||||
*/
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
|
||||
if (m)
|
||||
return LWS_HP_RET_BAIL_DIE;
|
||||
|
||||
if (!wsi->extension_data_pending)
|
||||
return LWS_HP_RET_USER_SERVICE;
|
||||
|
||||
|
@ -1389,6 +1454,7 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
|
|||
}
|
||||
|
||||
wsi->waiting_to_send_close_frame = 1;
|
||||
wsi->close_needs_ack = 1;
|
||||
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
|
||||
__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
|
||||
|
||||
|
@ -1417,7 +1483,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
|
|||
|
||||
if (wsi->ws->tx_draining_ext) {
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
|
||||
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
|
@ -1449,14 +1515,18 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
int masked7 = lwsi_role_client(wsi);
|
||||
unsigned char is_masked_bit = 0;
|
||||
unsigned char *dropmask = NULL;
|
||||
enum lws_write_protocol wpt;
|
||||
struct lws_tokens ebuf;
|
||||
size_t orig_len = len;
|
||||
int pre = 0, n;
|
||||
int pre = 0, n = 0;
|
||||
|
||||
// lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
|
||||
|
||||
if (wsi->ws->tx_draining_ext) {
|
||||
/* remove us from the list */
|
||||
struct lws **w = &pt->tx_draining_ext_list;
|
||||
|
||||
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
|
||||
wsi->ws->tx_draining_ext = 0;
|
||||
/* remove us from context draining ext list */
|
||||
while (*w) {
|
||||
|
@ -1467,10 +1537,23 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
w = &((*w)->ws->tx_draining_ext_list);
|
||||
}
|
||||
wsi->ws->tx_draining_ext_list = NULL;
|
||||
*wp = (wsi->ws->tx_draining_stashed_wp & 0xc0) |
|
||||
|
||||
wpt = *wp;
|
||||
*wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)|
|
||||
LWS_WRITE_CONTINUATION;
|
||||
|
||||
lwsl_ext("FORCED draining wp to 0x%02X\n", *wp);
|
||||
/*
|
||||
* When we are just flushing (len == 0), we can trust the
|
||||
* stashed wp info completely. Otherwise adjust it to the
|
||||
* FIN status of the incoming packet.
|
||||
*/
|
||||
|
||||
if (!(wpt & LWS_WRITE_NO_FIN) && len)
|
||||
*wp &= ~LWS_WRITE_NO_FIN;
|
||||
|
||||
lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
|
||||
wsi->ws->tx_draining_stashed_wp, wpt);
|
||||
// assert(0);
|
||||
}
|
||||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
@ -1515,17 +1598,19 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
|
|||
break;
|
||||
default:
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
|
||||
// lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n");
|
||||
// m = (int)ebuf.len;
|
||||
/* returns 0 if no more tx pending, 1 if more pending */
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
// lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp);
|
||||
|
||||
if (n && ebuf.len) {
|
||||
lwsl_debug("drain len %d\n", (int)ebuf.len);
|
||||
lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
|
||||
/* extension requires further draining */
|
||||
wsi->ws->tx_draining_ext = 1;
|
||||
wsi->ws->tx_draining_ext_list =
|
||||
pt->tx_draining_ext_list;
|
||||
wsi->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);
|
||||
|
@ -1763,8 +1848,6 @@ rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
|
|||
static int
|
||||
rops_callback_on_writable_ws(struct lws *wsi)
|
||||
{
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
|
||||
return 1;
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (lwsi_role_h2_ENCAPSULATION(wsi)) {
|
||||
/* we know then that it has an h2 parent */
|
||||
|
|
|
@ -50,7 +50,7 @@ enum lws_rx_parse_state {
|
|||
LWS_RXPS_07_COLLECT_FRAME_KEY_3,
|
||||
LWS_RXPS_07_COLLECT_FRAME_KEY_4,
|
||||
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
|
||||
LWS_RXPS_WS_FRAME_PAYLOAD
|
||||
};
|
||||
|
||||
enum lws_websocket_opcodes_07 {
|
||||
|
@ -70,6 +70,9 @@ enum lws_websocket_opcodes_07 {
|
|||
/* this is not usable directly by user code any more, lws_close_reason() */
|
||||
#define LWS_WRITE_CLOSE 4
|
||||
|
||||
#define ALREADY_PROCESSED_IGNORE_CHAR 1
|
||||
#define ALREADY_PROCESSED_NO_CB 2
|
||||
|
||||
struct _lws_websocket_related {
|
||||
char *rx_ubuf;
|
||||
struct lws *rx_draining_ext_list;
|
||||
|
@ -113,6 +116,7 @@ struct _lws_websocket_related {
|
|||
unsigned int tx_draining_ext:1;
|
||||
unsigned int send_check_ping:1;
|
||||
unsigned int first_fragment:1;
|
||||
unsigned int peer_has_sent_close:1;
|
||||
};
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
|
|
|
@ -75,10 +75,14 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
|
|||
|
||||
while (more) {
|
||||
|
||||
if (c >= (char *)pt->serv_buf + 255)
|
||||
return -1;
|
||||
|
||||
if (*c && (*c != ',' && *c != '\t')) {
|
||||
if (*c == ';') {
|
||||
ignore = 1;
|
||||
args = c + 1;
|
||||
if (!args)
|
||||
args = c + 1;
|
||||
}
|
||||
if (ignore || *c == ' ') {
|
||||
c++;
|
||||
|
@ -173,11 +177,19 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
|
|||
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
|
||||
|
||||
/*
|
||||
* go through the options trying to apply the
|
||||
* The client may send a bunch of different option
|
||||
* sets for the same extension, we are supposed to
|
||||
* pick one we like the look of. The option sets are
|
||||
* separated by comma.
|
||||
*
|
||||
* Actually we just either accept the first one or
|
||||
* nothing.
|
||||
*
|
||||
* Go through the options trying to apply the
|
||||
* recognized ones
|
||||
*/
|
||||
|
||||
lwsl_debug("ext args %s", args);
|
||||
lwsl_info("ext args %s\n", args);
|
||||
|
||||
while (args && *args && *args != ',') {
|
||||
while (*args == ' ')
|
||||
|
@ -194,9 +206,10 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
|
|||
oa.option_name = NULL;
|
||||
oa.option_index = (int)(po - opts);
|
||||
oa.start = NULL;
|
||||
lwsl_debug("setting %s\n", po->name);
|
||||
if (!ext->callback(
|
||||
lws_get_context(wsi), ext, wsi,
|
||||
oa.len = 0;
|
||||
lwsl_info("setting '%s'\n", po->name);
|
||||
if (!ext->callback(lws_get_context(wsi),
|
||||
ext, wsi,
|
||||
LWS_EXT_CB_OPTION_SET,
|
||||
wsi->act_ext_user[
|
||||
wsi->count_act_ext],
|
||||
|
@ -211,11 +224,17 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
|
|||
}
|
||||
while (*args && *args != ',' && *args != ';')
|
||||
args++;
|
||||
|
||||
if (*args == ';')
|
||||
args++;
|
||||
}
|
||||
|
||||
wsi->count_act_ext++;
|
||||
lwsl_parser("cnt_act_ext <- %d\n", wsi->count_act_ext);
|
||||
|
||||
if (args && *args == ',')
|
||||
more = 0;
|
||||
|
||||
ext++;
|
||||
}
|
||||
|
||||
|
@ -454,7 +473,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
/* make a buffer big enough for everything */
|
||||
|
||||
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
|
||||
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + 256 + LWS_PRE;
|
||||
p = response;
|
||||
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
|
||||
"Upgrade: WebSocket\x0d\x0a"
|
||||
|
@ -499,23 +518,18 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
LWS_CPYAPP(p, "\x0d\x0a");
|
||||
|
||||
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
|
||||
response, p - response)) {
|
||||
/* okay send the handshake response accepting the connection */
|
||||
|
||||
/* okay send the handshake response accepting the connection */
|
||||
|
||||
lwsl_parser("issuing resp pkt %d len\n",
|
||||
lws_ptr_diff(p, response));
|
||||
lwsl_parser("issuing resp pkt %d len\n",
|
||||
lws_ptr_diff(p, response));
|
||||
#if defined(DEBUG)
|
||||
fwrite(response, 1, p - response, stderr);
|
||||
fwrite(response, 1, p - response, stderr);
|
||||
#endif
|
||||
n = lws_write(wsi, (unsigned char *)response, p - response,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (n != (p - response)) {
|
||||
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
n = lws_write(wsi, (unsigned char *)response, p - response,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (n != (p - response)) {
|
||||
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* alright clean up and set ourselves into established state */
|
||||
|
@ -543,15 +557,200 @@ bail:
|
|||
}
|
||||
|
||||
|
||||
int
|
||||
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
|
||||
/*
|
||||
* Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
|
||||
* to expect in that state and can deal with it in bulk more efficiently.
|
||||
*/
|
||||
|
||||
static int
|
||||
lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
|
||||
{
|
||||
int m, draining_flow = 0;
|
||||
uint8_t *buffer = *buf, mask[4];
|
||||
struct lws_tokens ebuf;
|
||||
unsigned int avail = len;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
|
||||
#endif
|
||||
int n = 0;
|
||||
|
||||
if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
|
||||
draining_flow = 1;
|
||||
/*
|
||||
* With zlib, we can give it as much input as we like. The pmd
|
||||
* extension will draw it down in chunks (default 1024).
|
||||
*
|
||||
* If we try to restrict how much we give it, because we must go
|
||||
* back to the event loop each time, we will drop the remainder...
|
||||
*/
|
||||
|
||||
lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (!wsi->count_act_ext)
|
||||
#endif
|
||||
{
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
avail = (int)wsi->protocol->rx_buffer_size;
|
||||
else
|
||||
avail = wsi->context->pt_serv_buf_size;
|
||||
}
|
||||
|
||||
/* do not consume more than we should */
|
||||
if (avail > wsi->ws->rx_packet_length)
|
||||
avail = (unsigned int)wsi->ws->rx_packet_length;
|
||||
|
||||
/* do not consume more than what is in the buffer */
|
||||
if (avail > len)
|
||||
avail = (unsigned int)len;
|
||||
|
||||
if (avail <= 0)
|
||||
return 0;
|
||||
|
||||
ebuf.token = (char *)buffer;
|
||||
ebuf.len = avail;
|
||||
|
||||
//lwsl_hexdump_notice(ebuf.token, ebuf.len);
|
||||
|
||||
if (!wsi->ws->all_zero_nonce) {
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
|
||||
|
||||
/* deal with 4-byte chunks using unwrapped loop */
|
||||
n = avail >> 2;
|
||||
while (n--) {
|
||||
*(buffer) = *(buffer) ^ mask[0];
|
||||
buffer++;
|
||||
*(buffer) = *(buffer) ^ mask[1];
|
||||
buffer++;
|
||||
*(buffer) = *(buffer) ^ mask[2];
|
||||
buffer++;
|
||||
*(buffer) = *(buffer) ^ mask[3];
|
||||
buffer++;
|
||||
}
|
||||
/* and the remaining bytes bytewise */
|
||||
for (n = 0; n < (int)(avail & 3); n++) {
|
||||
*(buffer) = *(buffer) ^ mask[n];
|
||||
buffer++;
|
||||
}
|
||||
|
||||
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
|
||||
}
|
||||
|
||||
lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
|
||||
avail, (int)len);
|
||||
|
||||
(*buf) += avail;
|
||||
len -= avail;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
|
||||
lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
|
||||
#endif
|
||||
/*
|
||||
* ebuf may be pointing somewhere completely different now,
|
||||
* it's the output
|
||||
*/
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
if (n < 0) {
|
||||
/*
|
||||
* we may rely on this to get RX, just drop connection
|
||||
*/
|
||||
lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
wsi->ws->rx_packet_length -= avail;
|
||||
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
/*
|
||||
* if we had an rx fragment right at the last compressed byte of the
|
||||
* message, we can get a zero length inflated output, where no prior
|
||||
* rx inflated output marked themselves with FIN, since there was
|
||||
* raw ws payload still to drain at that time.
|
||||
*
|
||||
* Then we need to generate a zero length ws rx that can be understood
|
||||
* as the message completion.
|
||||
*/
|
||||
|
||||
if (!ebuf.len && /* zero-length inflation output */
|
||||
!n && /* nothing left to drain from the inflator */
|
||||
wsi->count_act_ext && /* we are using pmd */
|
||||
old_packet_length && /* we gave the inflator new input */
|
||||
!wsi->ws->rx_packet_length && /* raw ws packet payload all gone */
|
||||
wsi->ws->final && /* the raw ws packet is a FIN guy */
|
||||
wsi->protocol->callback &&
|
||||
!wsi->wsistate_pre_close) {
|
||||
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_RECEIVE,
|
||||
wsi->user_space, NULL, 0))
|
||||
return -1;
|
||||
|
||||
return avail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ebuf.len)
|
||||
return avail;
|
||||
|
||||
if (
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
n &&
|
||||
#endif
|
||||
ebuf.len)
|
||||
/* extension had more... main loop will come back */
|
||||
lws_add_wsi_to_draining_ext_list(wsi);
|
||||
else
|
||||
lws_remove_wsi_from_draining_ext_list(wsi);
|
||||
|
||||
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
|
||||
if (lws_check_utf8(&wsi->ws->utf8,
|
||||
(unsigned char *)ebuf.token, ebuf.len)) {
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"bad utf8", 8);
|
||||
goto utf8_fail;
|
||||
}
|
||||
|
||||
/* we are ending partway through utf-8 character? */
|
||||
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
|
||||
wsi->ws->utf8 && !n) {
|
||||
lwsl_info("FINAL utf8 error\n");
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
|
||||
(uint8_t *)"partial utf8", 12);
|
||||
|
||||
utf8_fail:
|
||||
lwsl_info("utf8 error\n");
|
||||
lwsl_hexdump_info(ebuf.token, ebuf.len);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (wsi->protocol->callback && !wsi->wsistate_pre_close)
|
||||
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
|
||||
LWS_CALLBACK_RECEIVE,
|
||||
wsi->user_space,
|
||||
ebuf.token, ebuf.len))
|
||||
return -1;
|
||||
|
||||
wsi->ws->first_fragment = 0;
|
||||
|
||||
lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
|
||||
__func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
|
||||
|
||||
return avail; /* how much we used from the input */
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
|
||||
{
|
||||
int m, bulk = 0;
|
||||
|
||||
lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
|
||||
|
||||
//lwsl_hexdump_notice(*buf, len);
|
||||
|
||||
/* let the rx protocol state machine have as much as it needs */
|
||||
|
||||
|
@ -560,51 +759,73 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
* we were accepting input but now we stopped doing so
|
||||
*/
|
||||
if (wsi->rxflow_bitmap) {
|
||||
lwsl_info("%s: doing rxflow\n", __func__);
|
||||
lws_rxflow_cache(wsi, *buf, 0, (int)len);
|
||||
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
|
||||
buf += len; /* stashing it is taking care of it */
|
||||
*buf += len; /* stashing it is taking care of it */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wsi->ws->rx_draining_ext) {
|
||||
m = lws_ws_rx_sm(wsi, 0);
|
||||
lwsl_debug("%s: draining rx ext\n", __func__);
|
||||
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* consume payload bytes efficiently */
|
||||
if (wsi->lws_rx_parse_state ==
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
|
||||
m = lws_payload_until_length_exhausted(wsi, buf, &len);
|
||||
if (draining_flow &&
|
||||
!lws_buflist_use_segment(&wsi->buflist, m))
|
||||
lws_dll_lws_remove(&wsi->dll_buflist);
|
||||
while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
|
||||
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
|
||||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
|
||||
wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
|
||||
len) {
|
||||
uint8_t *bin = *buf;
|
||||
|
||||
bulk = 1;
|
||||
m = lws_ws_frame_rest_is_payload(wsi, buf, len);
|
||||
assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
|
||||
len -= lws_ptr_diff(*buf, bin);
|
||||
|
||||
if (!m) {
|
||||
|
||||
break;
|
||||
}
|
||||
if (m < 0) {
|
||||
lwsl_info("%s: rest_is_payload bailed\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* process the byte */
|
||||
m = lws_ws_rx_sm(wsi, *(*buf)++);
|
||||
if (m < 0)
|
||||
if (!bulk) {
|
||||
/* process the byte */
|
||||
m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
|
||||
len--;
|
||||
} else {
|
||||
/*
|
||||
* We already handled this byte in bulk, just deal
|
||||
* with the ramifications
|
||||
*/
|
||||
lwsl_debug("%s: coming out of bulk with len %d, "
|
||||
"wsi->ws->rx_draining_ext %d\n",
|
||||
__func__, (int)len,
|
||||
wsi->ws->rx_draining_ext);
|
||||
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
|
||||
ALREADY_PROCESSED_NO_CB, 0);
|
||||
}
|
||||
|
||||
if (m < 0) {
|
||||
lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
|
||||
bulk);
|
||||
|
||||
return -1;
|
||||
len--;
|
||||
|
||||
/* account for what we're using in rxflow buffer */
|
||||
if (draining_flow &&
|
||||
!lws_buflist_use_segment(&wsi->buflist, 1)) {
|
||||
lws_dll_lws_remove(&wsi->dll_buflist);
|
||||
|
||||
lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
|
||||
|
||||
/* having drained the rxflow buffer, can rearm POLLIN */
|
||||
#ifdef LWS_NO_SERVER
|
||||
m =
|
||||
#endif
|
||||
__lws_rx_flow_control(wsi);
|
||||
/* m ignored, needed for NO_SERVER case */
|
||||
}
|
||||
|
||||
bulk = 0;
|
||||
}
|
||||
|
||||
lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
|
||||
lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ lws_callback_as_writeable(struct lws *wsi)
|
|||
}
|
||||
#endif
|
||||
|
||||
assert(!(lwsi_role_ws(wsi) && wsi->ws->tx_draining_ext));
|
||||
|
||||
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
|
||||
|
||||
m = user_callback_handle_rxflow(wsi->protocol->callback,
|
||||
|
@ -56,7 +58,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
volatile struct lws *vwsi = (volatile struct lws *)wsi;
|
||||
int n;
|
||||
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
//lwsl_notice("%s: %p\n", __func__, wsi);
|
||||
|
||||
vwsi->leave_pollout_active = 0;
|
||||
vwsi->handling_pollout = 1;
|
||||
|
@ -220,13 +222,6 @@ __lws_service_timeout_check(struct lws *wsi, time_t sec)
|
|||
|
||||
(void)n;
|
||||
|
||||
/*
|
||||
* if extensions want in on it (eg, we are a mux parent)
|
||||
* give them a chance to service child timeouts
|
||||
*/
|
||||
if (lws_ext_cb_active(wsi, LWS_EXT_CB_1HZ, NULL, sec) < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* if we went beyond the allowed time, kill the
|
||||
* connection
|
||||
|
@ -358,7 +353,7 @@ int
|
|||
lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
|
||||
struct lws_tokens *ebuf)
|
||||
{
|
||||
ebuf->len = lws_buflist_next_segment_len(&wsi->buflist,
|
||||
ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
|
||||
(uint8_t **)&ebuf->token);
|
||||
if (!ebuf->len) {
|
||||
ebuf->token = (char *)pt->serv_buf;
|
||||
|
@ -388,7 +383,7 @@ lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
|
|||
if (m)
|
||||
return 0;
|
||||
|
||||
lwsl_notice("%s: removed %p from dll_buflist\n", __func__, wsi);
|
||||
lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
|
||||
lws_dll_lws_remove(&wsi->dll_buflist);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|name|demonstrates|
|
||||
---|---
|
||||
minimal-ws-client-echo|Simple client that connects to a ws server and echos anything the server sends
|
||||
minimal-ws-client-pmd-bulk|Client that sends bulk multifragment data to the minimal-ws-server-pmd-bulk example
|
||||
minimal-ws-client-rx|Connects to the dumb-increment-protocol wss server at https://libwebsockets.org and demonstrates receiving ws data
|
||||
minimal-ws-client-tx|Connects to the minimal-ws-broker example as a publisher, demonstrating sending ws data
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
cmake_minimum_required(VERSION 2.8.9)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-ws-client-echo)
|
||||
set(SRCS minimal-ws-client-echo.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
|
||||
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
35
minimal-examples/ws-client/minimal-ws-client-echo/README.md
Normal file
35
minimal-examples/ws-client/minimal-ws-client-echo/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# lws minimal ws client + permessage-deflate echo
|
||||
|
||||
This example opens a ws client connection to localhost:7681 and
|
||||
echoes back anything that comes from the server.
|
||||
|
||||
You can use it for testing lws against Autobahn.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-p port|Port to connect to
|
||||
-u url|URL path part to connect to
|
||||
-o|Finish after one connection
|
||||
|
||||
```
|
||||
$ ./lws-minimal-ws-client-echo
|
||||
[2018/04/22 20:03:50:2343] USER: LWS minimal ws client echo + permessage-deflate + multifragment bulk message
|
||||
[2018/04/22 20:03:50:2344] USER: lws-minimal-ws-client-echo [-n (no exts)] [-u url] [-o (once)]
|
||||
[2018/04/22 20:03:50:2344] USER: options 0
|
||||
[2018/04/22 20:03:50:2345] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
|
||||
[2018/04/22 20:03:51:2356] USER: connecting to localhost:9001//runCase?case=362&agent=libwebsockets
|
||||
[2018/04/22 20:03:51:2385] NOTICE: checking client ext permessage-deflate
|
||||
[2018/04/22 20:03:51:2386] NOTICE: instantiating client ext permessage-deflate
|
||||
[2018/04/22 20:03:51:2386] USER: LWS_CALLBACK_CLIENT_ESTABLISHED
|
||||
...
|
||||
```
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* lws-minimal-ws-client-echo
|
||||
*
|
||||
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates a ws client that echoes back what it was sent, in a
|
||||
* way compatible with autobahn -m fuzzingserver
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define LWS_PLUGIN_STATIC
|
||||
#include "protocol_lws_minimal_client_echo.c"
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO,
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
static int interrupted, port = 7681, options = 0;
|
||||
static const char *url = "/", *ads = "localhost";
|
||||
|
||||
/* pass pointers to shared vars to the protocol */
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_ads = {
|
||||
NULL,
|
||||
NULL,
|
||||
"ads", /* pvo name */
|
||||
(void *)&ads /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_url = {
|
||||
&pvo_ads,
|
||||
NULL,
|
||||
"url", /* pvo name */
|
||||
(void *)&url /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_options = {
|
||||
&pvo_url,
|
||||
NULL,
|
||||
"options", /* pvo name */
|
||||
(void *)&options /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_port = {
|
||||
&pvo_options,
|
||||
NULL,
|
||||
"port", /* pvo name */
|
||||
(void *)&port /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_interrupted = {
|
||||
&pvo_port,
|
||||
NULL,
|
||||
"interrupted", /* pvo name */
|
||||
(void *)&interrupted /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo = {
|
||||
NULL, /* "next" pvo linked-list */
|
||||
&pvo_interrupted, /* "child" pvo linked-list */
|
||||
"lws-minimal-client-echo", /* protocol name we belong to on this vhost */
|
||||
"" /* ignored */
|
||||
};
|
||||
static const struct lws_extension extensions[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate"
|
||||
"; client_no_context_takeover"
|
||||
"; client_max_window_bits"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
void sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
const char *p;
|
||||
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
|
||||
/* for LLL_ verbosity above NOTICE to be built into lws,
|
||||
* lws must have been configured and built with
|
||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
|
||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
|
||||
/* | LLL_DEBUG */;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal ws client echo + permessage-deflate + multifragment bulk message\n");
|
||||
lwsl_user(" lws-minimal-ws-client-echo [-n (no exts)] [-u url] [-p port] [-o (once)]\n");
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-u")))
|
||||
url = p;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
port = atoi(p);
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-o"))
|
||||
options |= 1;
|
||||
|
||||
lwsl_user("options %d\n", options);
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = CONTEXT_PORT_NO_LISTEN;
|
||||
info.protocols = protocols;
|
||||
info.pvo = &pvo;
|
||||
if (!lws_cmdline_option(argc, argv, "-n"))
|
||||
info.extensions = extensions;
|
||||
info.pt_serv_buf_size = 32 * 1024;
|
||||
info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 1000);
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed");
|
||||
|
||||
return interrupted != 2;
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* ws protocol handler plugin for "lws-minimal-client-echo"
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* The protocol shows how to send and receive bulk messages over a ws connection
|
||||
* that optionally may have the permessage-deflate extension negotiated on it.
|
||||
*/
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
#define LWS_DLL
|
||||
#define LWS_INTERNAL
|
||||
#include <libwebsockets.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define RING_DEPTH 1024
|
||||
|
||||
/* one of these created for each message */
|
||||
|
||||
struct msg {
|
||||
void *payload; /* is malloc'd */
|
||||
size_t len;
|
||||
char binary;
|
||||
char first;
|
||||
char final;
|
||||
};
|
||||
|
||||
struct per_session_data__minimal_client_echo {
|
||||
struct lws_ring *ring;
|
||||
uint32_t tail;
|
||||
uint8_t completed:1;
|
||||
};
|
||||
|
||||
struct vhd_minimal_client_echo {
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost;
|
||||
struct lws *client_wsi;
|
||||
|
||||
int *interrupted;
|
||||
int *options;
|
||||
const char **url;
|
||||
const char **ads;
|
||||
int *port;
|
||||
};
|
||||
|
||||
static int
|
||||
connect_client(struct vhd_minimal_client_echo *vhd)
|
||||
{
|
||||
struct lws_client_connect_info i;
|
||||
char host[128];
|
||||
|
||||
lws_snprintf(host, sizeof(host), "%s:%u", *vhd->ads, *vhd->port);
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
i.context = vhd->context;
|
||||
i.port = *vhd->port;
|
||||
i.address = *vhd->ads;
|
||||
i.path = *vhd->url;
|
||||
i.host = host;
|
||||
i.origin = host;
|
||||
i.ssl_connection = 0;
|
||||
i.vhost = vhd->vhost;
|
||||
//i.protocol = ;
|
||||
i.pwsi = &vhd->client_wsi;
|
||||
|
||||
lwsl_user("connecting to %s:%d/%s\n", i.address, i.port, i.path);
|
||||
|
||||
return !lws_client_connect_via_info(&i);
|
||||
}
|
||||
|
||||
static void
|
||||
__minimal_destroy_message(void *_msg)
|
||||
{
|
||||
struct msg *msg = _msg;
|
||||
|
||||
free(msg->payload);
|
||||
msg->payload = NULL;
|
||||
msg->len = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_callback(struct lws *wsi, int reason, int secs)
|
||||
{
|
||||
lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi), reason, secs);
|
||||
}
|
||||
|
||||
static int
|
||||
callback_minimal_client_echo(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__minimal_client_echo *pss =
|
||||
(struct per_session_data__minimal_client_echo *)user;
|
||||
struct vhd_minimal_client_echo *vhd = (struct vhd_minimal_client_echo *)
|
||||
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi));
|
||||
const struct msg *pmsg;
|
||||
struct msg amsg;
|
||||
int n, m, flags;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi),
|
||||
sizeof(struct vhd_minimal_client_echo));
|
||||
if (!vhd)
|
||||
return -1;
|
||||
|
||||
vhd->context = lws_get_context(wsi);
|
||||
vhd->vhost = lws_get_vhost(wsi);
|
||||
|
||||
/* get the pointer to "interrupted" we were passed in pvo */
|
||||
vhd->interrupted = (int *)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"interrupted")->value;
|
||||
vhd->port = (int *)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"port")->value;
|
||||
vhd->options = (int *)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"options")->value;
|
||||
vhd->ads = (const char **)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"ads")->value;
|
||||
vhd->url = (const char **)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"url")->value;
|
||||
|
||||
if (connect_client(vhd))
|
||||
schedule_callback(wsi, LWS_CALLBACK_USER, 1);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
lwsl_user("LWS_CALLBACK_CLIENT_ESTABLISHED\n");
|
||||
pss->ring = lws_ring_create(sizeof(struct msg), RING_DEPTH,
|
||||
__minimal_destroy_message);
|
||||
if (!pss->ring)
|
||||
return 1;
|
||||
pss->tail = 0;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
|
||||
lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
|
||||
do {
|
||||
pmsg = lws_ring_get_element(pss->ring, &pss->tail);
|
||||
if (!pmsg) {
|
||||
lwsl_user(" (nothing in ring)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
flags = lws_write_ws_flags(
|
||||
pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
|
||||
pmsg->first, pmsg->final);
|
||||
|
||||
/* notice we allowed for LWS_PRE in the payload already */
|
||||
m = lws_write(wsi, pmsg->payload + LWS_PRE, pmsg->len, flags);
|
||||
if (m < (int)pmsg->len) {
|
||||
lwsl_err("ERROR %d writing to ws socket\n", m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n",
|
||||
m, flags, pmsg->first, pmsg->final);
|
||||
|
||||
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1);
|
||||
|
||||
} while (lws_ring_get_element(pss->ring, &pss->tail) &&
|
||||
!lws_send_pipe_choked(wsi));
|
||||
|
||||
/* more to do for us? */
|
||||
if (lws_ring_get_element(pss->ring, &pss->tail))
|
||||
/* come back as soon as we can write more */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
if ((int)lws_ring_get_count_free_elements(pss->ring) > RING_DEPTH - 5)
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
|
||||
if ((*vhd->options & 1) && pmsg && pmsg->final)
|
||||
pss->completed = 1;
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
|
||||
lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, first %d, last %d, bin %d)\n",
|
||||
(int)len, (int)lws_remaining_packet_payload(wsi),
|
||||
lws_is_first_fragment(wsi),
|
||||
lws_is_final_fragment(wsi),
|
||||
lws_frame_is_binary(wsi));
|
||||
|
||||
// lwsl_hexdump_notice(in, len);
|
||||
|
||||
amsg.first = lws_is_first_fragment(wsi);
|
||||
amsg.final = lws_is_final_fragment(wsi);
|
||||
amsg.binary = lws_frame_is_binary(wsi);
|
||||
n = (int)lws_ring_get_count_free_elements(pss->ring);
|
||||
if (!n) {
|
||||
lwsl_user("dropping!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
amsg.len = len;
|
||||
/* notice we over-allocate by LWS_PRE */
|
||||
amsg.payload = malloc(LWS_PRE + len);
|
||||
if (!amsg.payload) {
|
||||
lwsl_user("OOM: dropping\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy((char *)amsg.payload + LWS_PRE, in, len);
|
||||
if (!lws_ring_insert(pss->ring, &amsg, 1)) {
|
||||
__minimal_destroy_message(&amsg);
|
||||
lwsl_user("dropping!\n");
|
||||
break;
|
||||
}
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
if (n < 3)
|
||||
lws_rx_flow_control(wsi, 0);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
|
||||
in ? (char *)in : "(null)");
|
||||
vhd->client_wsi = NULL;
|
||||
schedule_callback(wsi, LWS_CALLBACK_USER, 1);
|
||||
if (*vhd->options & 1) {
|
||||
if (!*vhd->interrupted)
|
||||
*vhd->interrupted = 1;
|
||||
lws_cancel_service(lws_get_context(wsi));
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||
lwsl_user("LWS_CALLBACK_CLIENT_CLOSED\n");
|
||||
lws_ring_destroy(pss->ring);
|
||||
vhd->client_wsi = NULL;
|
||||
// schedule_callback(wsi, LWS_CALLBACK_USER, 1);
|
||||
//if (*vhd->options & 1) {
|
||||
if (!*vhd->interrupted)
|
||||
*vhd->interrupted = 1 + pss->completed;
|
||||
lws_cancel_service(lws_get_context(wsi));
|
||||
// }
|
||||
break;
|
||||
|
||||
/* rate-limited client connect retries */
|
||||
|
||||
case LWS_CALLBACK_USER:
|
||||
lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__);
|
||||
if (connect_client(vhd))
|
||||
schedule_callback(wsi, LWS_CALLBACK_USER, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO \
|
||||
{ \
|
||||
"lws-minimal-client-echo", \
|
||||
callback_minimal_client_echo, \
|
||||
sizeof(struct per_session_data__minimal_client_echo), \
|
||||
1024, \
|
||||
0, NULL, 0 \
|
||||
}
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
|
||||
/* boilerplate needed if we are built as a dynamic plugin */
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
LWS_PLUGIN_PROTOCOL_MINIMAL_client_echo
|
||||
};
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
init_protocol_minimal_client_echo(struct lws_context *context,
|
||||
struct lws_plugin_capability *c)
|
||||
{
|
||||
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
|
||||
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
|
||||
c->api_magic);
|
||||
return 1;
|
||||
}
|
||||
|
||||
c->protocols = protocols;
|
||||
c->count_protocols = ARRAY_SIZE(protocols);
|
||||
c->extensions = NULL;
|
||||
c->count_extensions = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
destroy_protocol_minimal_client_echo(struct lws_context *context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
|Example|Demonstrates|
|
||||
---|---
|
||||
minimal-ws-broker|Simple ws server with a publish / broker / subscribe architecture
|
||||
minimal-ws-server-echo|Simple ws server that listens and echos back anything clients send
|
||||
minimal-ws-server-pmd-bulk|Simple ws server showing how to pass bulk data with permessage-deflate
|
||||
minimal-ws-server-pmd|Simple ws server with permessage-deflate support
|
||||
minimal-ws-server-ring|Like minimal-ws-server but holds the chat in a multi-tail ringbuffer
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
cmake_minimum_required(VERSION 2.8.9)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-ws-server-echo)
|
||||
set(SRCS minimal-ws-server-echo.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
|
||||
require_lws_config(LWS_WITHOUT_EXTENSIONS 0 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
30
minimal-examples/ws-server/minimal-ws-server-echo/README.md
Normal file
30
minimal-examples/ws-server/minimal-ws-server-echo/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# lws minimal ws server + permessage-deflate echo
|
||||
|
||||
This example serves no-protocl-name ws on localhost:7681
|
||||
and echoes back anything that comes from the client.
|
||||
|
||||
You can use it for testing lws against Autobahn (use the
|
||||
-p option to tell it to listen on 9001 for that)
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
Commandline option|Meaning
|
||||
---|---
|
||||
-d <loglevel>|Debug verbosity in decimal, eg, -d15
|
||||
-p port|Port to connect to
|
||||
-u url|URL path part to connect to
|
||||
-o|Finish after one connection
|
||||
|
||||
```
|
||||
$ ./lws-minimal-ws-server-echo
|
||||
[2018/04/24 10:29:34:6212] USER: LWS minimal ws server echo + permessage-deflate + multifragment bulk message
|
||||
[2018/04/24 10:29:34:6213] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
|
||||
...
|
||||
```
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* lws-minimal-ws-server-echo
|
||||
*
|
||||
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates a ws server that echoes back what it was sent, in a way
|
||||
* compatible with autobahn -m fuzzingclient
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define LWS_PLUGIN_STATIC
|
||||
#include "protocol_lws_minimal_server_echo.c"
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO,
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
static int interrupted, port = 7681, options;
|
||||
|
||||
/* pass pointers to shared vars to the protocol */
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_options = {
|
||||
NULL,
|
||||
NULL,
|
||||
"options", /* pvo name */
|
||||
(void *)&options /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo_interrupted = {
|
||||
&pvo_options,
|
||||
NULL,
|
||||
"interrupted", /* pvo name */
|
||||
(void *)&interrupted /* pvo value */
|
||||
};
|
||||
|
||||
static const struct lws_protocol_vhost_options pvo = {
|
||||
NULL, /* "next" pvo linked-list */
|
||||
&pvo_interrupted, /* "child" pvo linked-list */
|
||||
"lws-minimal-server-echo", /* protocol name we belong to on this vhost */
|
||||
"" /* ignored */
|
||||
};
|
||||
static const struct lws_extension extensions[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate"
|
||||
"; client_no_context_takeover"
|
||||
"; client_max_window_bits"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
void sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *context;
|
||||
const char *p;
|
||||
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
|
||||
/* for LLL_ verbosity above NOTICE to be built into lws,
|
||||
* lws must have been configured and built with
|
||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
|
||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
|
||||
/* | LLL_DEBUG */;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-d")))
|
||||
logs = atoi(p);
|
||||
|
||||
lws_set_log_level(logs, NULL);
|
||||
lwsl_user("LWS minimal ws client echo + permessage-deflate + multifragment bulk message\n");
|
||||
lwsl_user(" lws-minimal-ws-client-echo [-n (no exts)] [-p port] [-o (once)]\n");
|
||||
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
port = atoi(p);
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-o"))
|
||||
options |= 1;
|
||||
|
||||
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
|
||||
info.port = port;
|
||||
info.protocols = protocols;
|
||||
info.pvo = &pvo;
|
||||
if (!lws_cmdline_option(argc, argv, "-n"))
|
||||
info.extensions = extensions;
|
||||
info.pt_serv_buf_size = 32 * 1024;
|
||||
info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 1000);
|
||||
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_user("Completed %s\n", interrupted == 2 ? "OK" : "failed");
|
||||
|
||||
return interrupted != 2;
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* ws protocol handler plugin for "lws-minimal-server-echo"
|
||||
*
|
||||
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* The protocol shows how to send and receive bulk messages over a ws connection
|
||||
* that optionally may have the permessage-deflate extension negotiated on it.
|
||||
*/
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
#define LWS_DLL
|
||||
#define LWS_INTERNAL
|
||||
#include <libwebsockets.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define RING_DEPTH 4096
|
||||
|
||||
/* one of these created for each message */
|
||||
|
||||
struct msg {
|
||||
void *payload; /* is malloc'd */
|
||||
size_t len;
|
||||
char binary;
|
||||
char first;
|
||||
char final;
|
||||
};
|
||||
|
||||
struct per_session_data__minimal_server_echo {
|
||||
struct lws_ring *ring;
|
||||
uint32_t msglen;
|
||||
uint32_t tail;
|
||||
uint8_t completed:1;
|
||||
uint8_t flow_controlled:1;
|
||||
};
|
||||
|
||||
struct vhd_minimal_server_echo {
|
||||
struct lws_context *context;
|
||||
struct lws_vhost *vhost;
|
||||
|
||||
int *interrupted;
|
||||
int *options;
|
||||
};
|
||||
|
||||
static void
|
||||
__minimal_destroy_message(void *_msg)
|
||||
{
|
||||
struct msg *msg = _msg;
|
||||
|
||||
free(msg->payload);
|
||||
msg->payload = NULL;
|
||||
msg->len = 0;
|
||||
}
|
||||
#include <assert.h>
|
||||
static int
|
||||
callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__minimal_server_echo *pss =
|
||||
(struct per_session_data__minimal_server_echo *)user;
|
||||
struct vhd_minimal_server_echo *vhd = (struct vhd_minimal_server_echo *)
|
||||
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi));
|
||||
const struct msg *pmsg;
|
||||
struct msg amsg;
|
||||
int n, m, flags;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
||||
lws_get_protocol(wsi),
|
||||
sizeof(struct vhd_minimal_server_echo));
|
||||
if (!vhd)
|
||||
return -1;
|
||||
|
||||
vhd->context = lws_get_context(wsi);
|
||||
vhd->vhost = lws_get_vhost(wsi);
|
||||
|
||||
/* get the pointers we were passed in pvo */
|
||||
|
||||
vhd->interrupted = (int *)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"interrupted")->value;
|
||||
vhd->options = (int *)lws_pvo_search(
|
||||
(const struct lws_protocol_vhost_options *)in,
|
||||
"options")->value;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
lwsl_user("LWS_CALLBACK_ESTABLISHED\n");
|
||||
pss->ring = lws_ring_create(sizeof(struct msg), RING_DEPTH,
|
||||
__minimal_destroy_message);
|
||||
if (!pss->ring)
|
||||
return 1;
|
||||
pss->tail = 0;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
|
||||
lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE\n");
|
||||
do {
|
||||
pmsg = lws_ring_get_element(pss->ring, &pss->tail);
|
||||
if (!pmsg) {
|
||||
lwsl_user(" (nothing in ring)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
flags = lws_write_ws_flags(
|
||||
pmsg->binary ? LWS_WRITE_BINARY : LWS_WRITE_TEXT,
|
||||
pmsg->first, pmsg->final);
|
||||
|
||||
/* notice we allowed for LWS_PRE in the payload already */
|
||||
m = lws_write(wsi, pmsg->payload + LWS_PRE, pmsg->len, flags);
|
||||
if (m < (int)pmsg->len) {
|
||||
lwsl_err("ERROR %d writing to ws socket\n", m);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwsl_user(" wrote %d: flags: 0x%x first: %d final %d\n",
|
||||
m, flags, pmsg->first, pmsg->final);
|
||||
|
||||
lws_ring_consume_single_tail(pss->ring, &pss->tail, 1);
|
||||
|
||||
} while (lws_ring_get_element(pss->ring, &pss->tail) &&
|
||||
!lws_send_pipe_choked(wsi));
|
||||
|
||||
/* more to do for us? */
|
||||
if (lws_ring_get_element(pss->ring, &pss->tail))
|
||||
/* come back as soon as we can write more */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
if (pss->flow_controlled &&
|
||||
(int)lws_ring_get_count_free_elements(pss->ring) > RING_DEPTH - 5) {
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
pss->flow_controlled = 0;
|
||||
}
|
||||
|
||||
if ((*vhd->options & 1) && pmsg && pmsg->final)
|
||||
pss->completed = 1;
|
||||
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
|
||||
lwsl_user("LWS_CALLBACK_RECEIVE: %4d (rpp %5d, first %d, "
|
||||
"last %d, bin %d, msglen %d (+ %d = %d))\n",
|
||||
(int)len, (int)lws_remaining_packet_payload(wsi),
|
||||
lws_is_first_fragment(wsi),
|
||||
lws_is_final_fragment(wsi),
|
||||
lws_frame_is_binary(wsi), pss->msglen, (int)len,
|
||||
(int)pss->msglen + (int)len);
|
||||
|
||||
if (len) {
|
||||
;
|
||||
//puts((const char *)in);
|
||||
//lwsl_hexdump_notice(in, len);
|
||||
}
|
||||
|
||||
amsg.first = lws_is_first_fragment(wsi);
|
||||
amsg.final = lws_is_final_fragment(wsi);
|
||||
amsg.binary = lws_frame_is_binary(wsi);
|
||||
n = (int)lws_ring_get_count_free_elements(pss->ring);
|
||||
if (!n) {
|
||||
lwsl_user("dropping!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (amsg.final)
|
||||
pss->msglen = 0;
|
||||
else
|
||||
pss->msglen += len;
|
||||
|
||||
amsg.len = len;
|
||||
/* notice we over-allocate by LWS_PRE */
|
||||
amsg.payload = malloc(LWS_PRE + len);
|
||||
if (!amsg.payload) {
|
||||
lwsl_user("OOM: dropping\n");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy((char *)amsg.payload + LWS_PRE, in, len);
|
||||
if (!lws_ring_insert(pss->ring, &amsg, 1)) {
|
||||
__minimal_destroy_message(&amsg);
|
||||
lwsl_user("dropping!\n");
|
||||
break;
|
||||
}
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
if (n < 3 && !pss->flow_controlled) {
|
||||
pss->flow_controlled = 1;
|
||||
lws_rx_flow_control(wsi, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
lwsl_user("LWS_CALLBACK_CLOSED\n");
|
||||
lws_ring_destroy(pss->ring);
|
||||
|
||||
if (*vhd->options & 1) {
|
||||
if (!*vhd->interrupted)
|
||||
*vhd->interrupted = 1 + pss->completed;
|
||||
lws_cancel_service(lws_get_context(wsi));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO \
|
||||
{ \
|
||||
"lws-minimal-server-echo", \
|
||||
callback_minimal_server_echo, \
|
||||
sizeof(struct per_session_data__minimal_server_echo), \
|
||||
1024, \
|
||||
0, NULL, 0 \
|
||||
}
|
||||
|
||||
#if !defined (LWS_PLUGIN_STATIC)
|
||||
|
||||
/* boilerplate needed if we are built as a dynamic plugin */
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
LWS_PLUGIN_PROTOCOL_MINIMAL_server_echo
|
||||
};
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
init_protocol_minimal_server_echo(struct lws_context *context,
|
||||
struct lws_plugin_capability *c)
|
||||
{
|
||||
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
|
||||
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
|
||||
c->api_magic);
|
||||
return 1;
|
||||
}
|
||||
|
||||
c->protocols = protocols;
|
||||
c->count_protocols = ARRAY_SIZE(protocols);
|
||||
c->extensions = NULL;
|
||||
c->count_extensions = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_EXTERN LWS_VISIBLE int
|
||||
destroy_protocol_minimal_server_echo(struct lws_context *context)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -1,25 +1,98 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
#
|
||||
# Requires pip install autobahntestsuite
|
||||
#
|
||||
# you should run this from ./build, after building with
|
||||
# cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1
|
||||
#
|
||||
# It will use the minimal echo client and server to run
|
||||
# autobahn ws tests as both client and server.
|
||||
|
||||
set -u
|
||||
|
||||
PARALLEL=8
|
||||
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 &
|
||||
CLIE=bin/lws-minimal-ws-client-echo
|
||||
SERV=bin/lws-minimal-ws-server-echo
|
||||
|
||||
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
|
||||
RESULT=0
|
||||
|
||||
which wstest 2>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "wstest is not installed"
|
||||
exit 8
|
||||
fi
|
||||
|
||||
killall wstest 2>/dev/null
|
||||
|
||||
#
|
||||
# 2.10 / 2.11: There is no requirement to handle multiple PING / PONG
|
||||
# in flight in RFC6455. lws doesn't waste memory on it
|
||||
# since it is useless.
|
||||
#
|
||||
# 12.4.* / 12.5.*: Autobahn has been broken for these tests since Aug 2017
|
||||
# https://github.com/crossbario/autobahn-testsuite/issues/71
|
||||
|
||||
|
||||
cat << EOF >fuzzingserver.json
|
||||
{
|
||||
"url": "ws://127.0.0.1:9001",
|
||||
"outdir": "./reports/clients",
|
||||
"cases": ["*"],
|
||||
"exclude-cases": [ "2.10", "2.11", "12.4.*", "12.5.*"],
|
||||
"exclude-agent-cases": {}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat << EOF >fuzzingclient.json
|
||||
{
|
||||
"outdir": "./reports/servers",
|
||||
"servers": [
|
||||
{
|
||||
"url": "ws://127.0.0.1:9001"
|
||||
}
|
||||
],
|
||||
"cases": ["*"],
|
||||
"exclude-cases": ["2.10", "2.11", "12.4.*", "12.5.*" ],
|
||||
"exclude-agent-cases": {}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
||||
PYTHONHASHSEED=0 wstest -m fuzzingserver &
|
||||
Q=$!
|
||||
sleep 2s
|
||||
ps -p $Q > /dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "Problem with autobahn wstest install"
|
||||
exit 9
|
||||
fi
|
||||
|
||||
# 1) lws-as-client tests first
|
||||
|
||||
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', '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.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', '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
|
||||
|
||||
# if [ $N -ge 360 -a $N -le 393 ] ; then
|
||||
# echo "skipping broken autobahn tests (broken in autobahn) $i https://github.com/crossbario/autobahn-testsuite/issues/71"
|
||||
# else
|
||||
|
||||
echo $N: $i
|
||||
$CLIE -a 127.0.0.1 -p 9001 -u "/runCase?case=$N&agent=libwebsockets" -d3 &
|
||||
|
||||
C=99
|
||||
while [ $C -gt $PARALLEL ] ; do
|
||||
if [ $OS=SunOS ] ; then
|
||||
C=`ps -ef | grep client-echo | wc -l`
|
||||
else
|
||||
C=`ps fax | grep client-echo | wc -l`
|
||||
fi
|
||||
if [ $C -gt $PARALLEL ] ; then
|
||||
sleep 0.1s
|
||||
fi
|
||||
done
|
||||
# fi
|
||||
N=$(( $N + 1 ))
|
||||
done
|
||||
|
||||
|
@ -27,15 +100,80 @@ echo "waiting for forks to complete..."
|
|||
|
||||
while [ 1 ] ; do
|
||||
if [ $OS=SunOS ] ; then
|
||||
n=`ps -ef | grep libwebsocket | grep -v grep | wc -l`
|
||||
n=`ps -ef | grep client-echo | grep -v grep | wc -l`
|
||||
else
|
||||
n=`ps fax | grep libwebsocket | grep -v grep | wc -l`
|
||||
n=`ps fax | grep client-echo | grep -v grep | wc -l`
|
||||
fi
|
||||
echo "$n forks running..."
|
||||
if [ $n -eq 0 ] ; then
|
||||
echo "Completed"
|
||||
exit 0
|
||||
break
|
||||
fi
|
||||
sleep 1s
|
||||
sleep 2s
|
||||
done
|
||||
|
||||
# generate the report in ./reports
|
||||
#
|
||||
$CLIE -a 127.0.0.1 -p 9001 -u "/updateReports?agent=libwebsockets" -o
|
||||
sleep 2s
|
||||
killall wstest
|
||||
sleep 1s
|
||||
|
||||
# this squashes the results into single lines like
|
||||
#
|
||||
# "9.8.4": { "behavior": "OK", "behaviorClose": "OK", "duration": 1312, "remoteCloseCode": 1000, "reportfile": "libwebsockets_case_9_8_4.json"
|
||||
|
||||
cat reports/clients/index.json | tr '\n' '!' | sed "s|\},\!|\n|g" | tr '!' ' ' | tr -s ' ' > /tmp/ji
|
||||
|
||||
echo -n "AUTOBAHN SERVER / LWS CLIENT: Total tests: " `cat /tmp/ji | wc -l` " : "
|
||||
R="`cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`"
|
||||
if [ "$R" == "0" ] ; then
|
||||
echo "All pass"
|
||||
else
|
||||
RESULT=1
|
||||
echo -n "$R FAIL : "
|
||||
cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
|
||||
echo
|
||||
fi
|
||||
|
||||
# 2) lws-as-server tests
|
||||
|
||||
$SERV -p 9001 -d7 &
|
||||
wstest -m fuzzingclient
|
||||
R=$?
|
||||
echo "Autobahn client exit $R"
|
||||
|
||||
killall lws-minimal-ws-server-echo
|
||||
sleep 1s
|
||||
|
||||
# repeat the client results
|
||||
|
||||
R=`cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`
|
||||
echo -n "AUTOBAHN SERVER / LWS CLIENT: Total tests: " `cat /tmp/ji | wc -l` " : "
|
||||
if [ "$R" == "0" ] ;then
|
||||
echo "All pass"
|
||||
else
|
||||
RESULT=1
|
||||
echo -n "$R FAIL : "
|
||||
cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
|
||||
echo
|
||||
fi
|
||||
|
||||
# and then the server results
|
||||
|
||||
cat reports/servers/index.json | tr '\n' '!' | sed "s|\},\!|\n|g" | tr '!' ' ' | tr -s ' ' > /tmp/jis
|
||||
R=`cat /tmp/jis | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`
|
||||
|
||||
echo -n "AUTOBAHN CLIENT / LWS SERVER: Total tests: " `cat /tmp/jis | wc -l` " : "
|
||||
if [ "$R" == "0" ] ;then
|
||||
echo "All pass"
|
||||
else
|
||||
RESULT=$(( $RESULT + 2 ))
|
||||
echo -n "$R FAIL : "
|
||||
cat /tmp/jis | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
|
||||
echo
|
||||
fi
|
||||
|
||||
echo $RESULT
|
||||
exit $RESULT
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ then
|
|||
if [ "$LWS_METHOD" == "lwsws" ];
|
||||
then
|
||||
sudo apt-get install -y -qq realpath;
|
||||
sudo apt-get remove python-six
|
||||
sudo pip install six>=1.9
|
||||
sudo pip install Twisted==16.0.0
|
||||
sudo pip install pyopenssl>=0.14
|
||||
sudo pip install autobahntestsuite
|
||||
fi
|
||||
|
||||
if [ "$LWS_METHOD" == "libev" ];
|
||||
|
|
|
@ -20,7 +20,7 @@ function check {
|
|||
echo "(killed it) *******"
|
||||
exit 1
|
||||
fi
|
||||
dd if=$LOG bs=1 skip=$LEN 2>/dev/null
|
||||
#dd if=$LOG bs=1 skip=$LEN 2>/dev/null
|
||||
|
||||
if [ "$1" = "default" ] ; then
|
||||
diff /tmp/lwscap $INSTALLED/../share/libwebsockets-test-server/test.html > /dev/null
|
||||
|
@ -107,7 +107,7 @@ function check {
|
|||
|
||||
rm -rf $LOG
|
||||
killall libwebsockets-test-server 2>/dev/null
|
||||
libwebsockets-test-server -d1023 2>> $LOG &
|
||||
libwebsockets-test-server -d15 2>> $LOG >/dev/null &
|
||||
CPID=$!
|
||||
|
||||
echo "Started server on PID $CPID"
|
||||
|
@ -546,13 +546,13 @@ for i in \
|
|||
|
||||
R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | nc localhost 7681 2>/dev/null >/tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2`
|
||||
|
||||
cat /tmp/lwscap | head -n1
|
||||
echo ==== $R
|
||||
#cat /tmp/lwscap | head -n1
|
||||
#echo ==== $R
|
||||
|
||||
|
||||
if [ "$R" != "403" ]; then
|
||||
U=`cat $LOG | grep lws_http_serve | tail -n 1 | cut -d':' -f6 | cut -d' ' -f2`
|
||||
echo $U
|
||||
# echo $U
|
||||
echo "- \"$i\" -> $R \"$U\"" >>/tmp/results
|
||||
else
|
||||
echo "- \"$i\" -> $R" >>/tmp/results
|
||||
|
|
|
@ -1,511 +0,0 @@
|
|||
/*
|
||||
* libwebsockets-test-echo
|
||||
*
|
||||
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* The person who associated a work with this deed has dedicated
|
||||
* the work to the public domain by waiving all of his or her rights
|
||||
* to the work worldwide under copyright law, including all related
|
||||
* and neighboring rights, to the extent allowed by law. You can copy,
|
||||
* modify, distribute and perform the work, even for commercial purposes,
|
||||
* all without asking permission.
|
||||
*
|
||||
* The test apps are intended to be adapted for use in your code, which
|
||||
* may be proprietary. So unlike the library itself, they are licensed
|
||||
* Public Domain.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "../lib/libwebsockets.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include "gettimeofday.h"
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
static volatile int force_exit = 0;
|
||||
static int versa, state;
|
||||
static int times = -1;
|
||||
|
||||
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
|
||||
|
||||
#define MAX_ECHO_PAYLOAD 1024
|
||||
|
||||
struct per_session_data__echo {
|
||||
size_t rx, tx;
|
||||
unsigned char buf[LWS_PRE + MAX_ECHO_PAYLOAD];
|
||||
unsigned int len;
|
||||
unsigned int index;
|
||||
int final;
|
||||
int continuation;
|
||||
int binary;
|
||||
};
|
||||
|
||||
static int
|
||||
callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__echo *pss =
|
||||
(struct per_session_data__echo *)user;
|
||||
int n, flags;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
#ifndef LWS_NO_SERVER
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->index = 0;
|
||||
pss->len = -1;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
do_tx:
|
||||
if ((int)pss->len == -1)
|
||||
break;
|
||||
|
||||
flags = lws_write_ws_flags(pss->binary ? LWS_WRITE_BINARY :
|
||||
LWS_WRITE_TEXT, pss->continuation, pss->final);
|
||||
|
||||
lwsl_info("+++ test-echo: writing %d, with final %d\n",
|
||||
pss->len, pss->final);
|
||||
|
||||
pss->tx += pss->len;
|
||||
n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, flags);
|
||||
if (n < 0) {
|
||||
lwsl_err("ERROR %d writing to socket, hanging up\n", n);
|
||||
return 1;
|
||||
}
|
||||
pss->len = -1;
|
||||
if (pss->final)
|
||||
pss->continuation = 0;
|
||||
lws_rx_flow_control(wsi, 1);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_RECEIVE:
|
||||
do_rx:
|
||||
pss->final = lws_is_final_fragment(wsi);
|
||||
pss->binary = lws_frame_is_binary(wsi);
|
||||
lwsl_info("+++ test-echo: RX len %ld final %ld, pss->len=%ld\n",
|
||||
(long)len, (long)pss->final, (long)pss->len);
|
||||
|
||||
memcpy(&pss->buf[LWS_PRE], in, len);
|
||||
assert((int)pss->len == -1);
|
||||
pss->len = (unsigned int)len;
|
||||
pss->rx += len;
|
||||
|
||||
lws_rx_flow_control(wsi, 0);
|
||||
lws_callback_on_writable(wsi);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
/* when the callback is used for client operations --> */
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_debug("closed\n");
|
||||
state = 0;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
lwsl_debug("Client has connected\n");
|
||||
pss->index = 0;
|
||||
pss->len = -1;
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
#ifndef LWS_NO_SERVER
|
||||
if (versa)
|
||||
goto do_rx;
|
||||
#endif
|
||||
lwsl_notice("Client RX: %s", (char *)in);
|
||||
if (times == 0)
|
||||
force_exit = 1;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||
#ifndef LWS_NO_SERVER
|
||||
if (versa) {
|
||||
if (pss->len != (unsigned int)-1)
|
||||
goto do_tx;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* we will send our packet... */
|
||||
pss->len = sprintf((char *)&pss->buf[LWS_PRE],
|
||||
"hello from libwebsockets-test-echo client pid %d index %d\n",
|
||||
getpid(), pss->index++);
|
||||
lwsl_notice("Client TX: %s", &pss->buf[LWS_PRE]);
|
||||
n = lws_write(wsi, &pss->buf[LWS_PRE], pss->len, LWS_WRITE_TEXT);
|
||||
if (n < 0) {
|
||||
lwsl_err("ERROR %d writing to socket, hanging up\n", n);
|
||||
return -1;
|
||||
}
|
||||
if (n < (int)pss->len) {
|
||||
lwsl_err("Partial write\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct lws_protocols protocols[] = {
|
||||
/* first protocol must always be HTTP handler */
|
||||
|
||||
{
|
||||
"", /* name - can be overridden with -e */
|
||||
callback_echo,
|
||||
sizeof(struct per_session_data__echo), /* per_session_data_size */
|
||||
MAX_ECHO_PAYLOAD,
|
||||
},
|
||||
{
|
||||
NULL, NULL, 0 /* End of list */
|
||||
}
|
||||
};
|
||||
|
||||
static const struct lws_extension exts[] = {
|
||||
{
|
||||
"permessage-deflate",
|
||||
lws_extension_callback_pm_deflate,
|
||||
"permessage-deflate; client_no_context_takeover; client_max_window_bits"
|
||||
},
|
||||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
|
||||
void sighandler(int sig)
|
||||
{
|
||||
force_exit = 1;
|
||||
}
|
||||
|
||||
static struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "debug", required_argument, NULL, 'd' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "ssl-cert", required_argument, NULL, 'C' },
|
||||
{ "ssl-key", required_argument, NULL, 'k' },
|
||||
#ifndef LWS_NO_CLIENT
|
||||
{ "client", required_argument, NULL, 'c' },
|
||||
{ "ratems", required_argument, NULL, 'r' },
|
||||
#endif
|
||||
{ "ssl", no_argument, NULL, 's' },
|
||||
{ "versa", no_argument, NULL, 'v' },
|
||||
{ "uri", required_argument, NULL, 'u' },
|
||||
{ "passphrase", required_argument, NULL, 'P' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "times", required_argument, NULL, 'n' },
|
||||
{ "echogen", no_argument, NULL, 'e' },
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
{ "daemonize", no_argument, NULL, 'D' },
|
||||
#endif
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n = 0;
|
||||
int port = 7681;
|
||||
int use_ssl = 0;
|
||||
struct lws_context *context;
|
||||
int opts = 0;
|
||||
char interface_name[128] = "";
|
||||
const char *_interface = NULL;
|
||||
char ssl_cert[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
|
||||
char ssl_key[256] = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
|
||||
#ifndef _WIN32
|
||||
/* LOG_PERROR is not POSIX standard, and may not be portable */
|
||||
#ifdef __sun
|
||||
int syslog_options = LOG_PID;
|
||||
#else
|
||||
int syslog_options = LOG_PID | LOG_PERROR;
|
||||
#endif
|
||||
#endif
|
||||
int client = 0;
|
||||
int listen_port = 80;
|
||||
struct lws_context_creation_info info;
|
||||
char passphrase[256];
|
||||
char uri[256] = "/";
|
||||
#ifndef LWS_NO_CLIENT
|
||||
char address[256], ads_port[256 + 30];
|
||||
int rate_us = 250000;
|
||||
unsigned long long oldus;
|
||||
struct lws *wsi;
|
||||
int disallow_selfsigned = 0;
|
||||
struct timeval tv;
|
||||
const char *connect_protocol = NULL;
|
||||
struct lws_client_connect_info i;
|
||||
#endif
|
||||
|
||||
int debug_level = 7;
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
int daemonize = 0;
|
||||
#endif
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
lwsl_notice("Built to support client operations\n");
|
||||
#endif
|
||||
#ifndef LWS_NO_SERVER
|
||||
lwsl_notice("Built to support server operations\n");
|
||||
#endif
|
||||
|
||||
while (n >= 0) {
|
||||
n = getopt_long(argc, argv, "i:hsp:d:DC:k:P:vu:n:e"
|
||||
#ifndef LWS_NO_CLIENT
|
||||
"c:r:"
|
||||
#endif
|
||||
, options, NULL);
|
||||
if (n < 0)
|
||||
continue;
|
||||
switch (n) {
|
||||
case 'P':
|
||||
lws_strncpy(passphrase, optarg, sizeof(passphrase));
|
||||
info.ssl_private_key_password = passphrase;
|
||||
break;
|
||||
case 'C':
|
||||
lws_strncpy(ssl_cert, optarg, sizeof(ssl_cert));
|
||||
disallow_selfsigned = 1;
|
||||
break;
|
||||
case 'k':
|
||||
lws_strncpy(ssl_key, optarg, sizeof(ssl_key));
|
||||
break;
|
||||
case 'u':
|
||||
lws_strncpy(uri, optarg, sizeof(uri));
|
||||
break;
|
||||
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
#if !defined(_WIN32) && !defined(__sun)
|
||||
syslog_options &= ~LOG_PERROR;
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#ifndef LWS_NO_CLIENT
|
||||
case 'c':
|
||||
client = 1;
|
||||
lws_strncpy(address, optarg, sizeof(address));
|
||||
port = 80;
|
||||
break;
|
||||
case 'r':
|
||||
rate_us = atoi(optarg) * 1000;
|
||||
break;
|
||||
#endif
|
||||
case 'd':
|
||||
debug_level = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
use_ssl = 1; /* 1 = take care about cert verification, 2 = allow anything */
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
versa = 1;
|
||||
break;
|
||||
case 'e':
|
||||
protocols[0].name = "lws-echogen";
|
||||
connect_protocol = protocols[0].name;
|
||||
lwsl_err("using lws-echogen\n");
|
||||
break;
|
||||
case 'i':
|
||||
lws_strncpy(interface_name, optarg, sizeof interface_name);
|
||||
_interface = interface_name;
|
||||
break;
|
||||
case 'n':
|
||||
times = atoi(optarg) + 1;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
fprintf(stderr, "Usage: libwebsockets-test-echo\n"
|
||||
" --debug / -d <debug bitfield>\n"
|
||||
" --port / -p <port>\n"
|
||||
" --ssl-cert / -C <cert path>\n"
|
||||
" --ssl-key / -k <key path>\n"
|
||||
#ifndef LWS_NO_CLIENT
|
||||
" --client / -c <server IP>\n"
|
||||
" --ratems / -r <rate in ms>\n"
|
||||
#endif
|
||||
" --ssl / -s\n"
|
||||
" --passphrase / -P <passphrase>\n"
|
||||
" --interface / -i <interface>\n"
|
||||
" --uri / -u <uri path>\n"
|
||||
" --times / -n <-1 unlimited or times to echo>\n"
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
" --daemonize / -D\n"
|
||||
#endif
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
/*
|
||||
* normally lock path would be /var/lock/lwsts or similar, to
|
||||
* simplify getting started without having to take care about
|
||||
* permissions or running as root, set to /tmp/.lwsts-lock
|
||||
*/
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#else
|
||||
if (!client && daemonize && lws_daemonize("/tmp/.lwstecho-lock")) {
|
||||
fprintf(stderr, "Failed to daemonize\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* we will only try to log things according to our debug_level */
|
||||
setlogmask(LOG_UPTO (LOG_DEBUG));
|
||||
openlog("lwsts", syslog_options, LOG_DAEMON);
|
||||
#endif
|
||||
|
||||
/* tell the library what debug level to emit and to send it to syslog */
|
||||
lws_set_log_level(debug_level, lwsl_emit_syslog);
|
||||
|
||||
lwsl_notice("libwebsockets test server echo - license LGPL2.1+SLE\n");
|
||||
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (client) {
|
||||
lwsl_notice("Running in client mode\n");
|
||||
listen_port = CONTEXT_PORT_NO_LISTEN;
|
||||
if (use_ssl && !disallow_selfsigned) {
|
||||
lwsl_info("allowing selfsigned\n");
|
||||
use_ssl = 2;
|
||||
} else {
|
||||
lwsl_info("requiring server cert validation against %s\n",
|
||||
ssl_cert);
|
||||
info.ssl_ca_filepath = ssl_cert;
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
#ifndef LWS_NO_SERVER
|
||||
lwsl_notice("Running in server mode\n");
|
||||
listen_port = port;
|
||||
#endif
|
||||
#ifndef LWS_NO_CLIENT
|
||||
}
|
||||
#endif
|
||||
|
||||
info.port = listen_port;
|
||||
info.iface = _interface;
|
||||
info.protocols = protocols;
|
||||
if (use_ssl && !client) {
|
||||
info.ssl_cert_filepath = ssl_cert;
|
||||
info.ssl_private_key_filepath = ssl_key;
|
||||
} else
|
||||
if (use_ssl && client) {
|
||||
info.ssl_cert_filepath = NULL;
|
||||
info.ssl_private_key_filepath = NULL;
|
||||
}
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.extensions = exts;
|
||||
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
|
||||
if (use_ssl)
|
||||
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
#if !defined(LWS_WITHOUT_EXTENSIONS)
|
||||
info.extensions = exts;
|
||||
#endif
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
lwsl_err("libwebsocket init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
|
||||
#ifndef LWS_NO_CLIENT
|
||||
gettimeofday(&tv, NULL);
|
||||
oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
|
||||
#endif
|
||||
|
||||
n = 0;
|
||||
while (n >= 0 && !force_exit) {
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (client && !state && times) {
|
||||
state = 1;
|
||||
lwsl_notice("Client connecting to %s:%u....\n",
|
||||
address, port);
|
||||
/* we are in client mode */
|
||||
|
||||
address[sizeof(address) - 1] = '\0';
|
||||
sprintf(ads_port, "%s:%u", address, port & 65535);
|
||||
if (times > 0)
|
||||
times--;
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
i.context = context;
|
||||
i.address = address;
|
||||
i.port = port;
|
||||
i.ssl_connection = use_ssl;
|
||||
i.path = uri;
|
||||
i.host = ads_port;
|
||||
i.origin = ads_port;
|
||||
i.protocol = connect_protocol;
|
||||
|
||||
wsi = lws_client_connect_via_info(&i);
|
||||
if (!wsi) {
|
||||
lwsl_err("Client failed to connect to %s:%u\n",
|
||||
address, port);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
if (client && !versa && times) {
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
if ((int)((((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec) - oldus) > rate_us) {
|
||||
lws_callback_on_writable_all_protocol(context,
|
||||
&protocols[0]);
|
||||
oldus = ((unsigned long long)tv.tv_sec * 1000000) + tv.tv_usec;
|
||||
if (times > 0)
|
||||
times--;
|
||||
}
|
||||
}
|
||||
|
||||
if (client && !state && !times)
|
||||
break;
|
||||
#endif
|
||||
n = lws_service(context, 10);
|
||||
}
|
||||
#ifndef LWS_NO_CLIENT
|
||||
bail:
|
||||
#endif
|
||||
lws_context_destroy(context);
|
||||
|
||||
lwsl_notice("libwebsockets-test-echo exited cleanly\n");
|
||||
#ifndef _WIN32
|
||||
closelog();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue