From 7b227eb3335e782b6193f2402867dcafa6df1e17 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@warmcat.com>
Date: Fri, 20 Apr 2018 10:33:23 +0800
Subject: [PATCH] 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.
---
.travis.yml | 4 +-
CMakeLists.txt | 21 +-
README.md | 77 +--
READMEs/README.test-apps.md | 54 +-
lib/context.c | 27 +-
lib/libwebsockets.c | 59 +-
lib/libwebsockets.h | 34 +-
lib/output.c | 262 +--------
lib/private-libwebsockets.h | 22 +-
lib/roles/h1/client-h1.c | 72 ---
lib/roles/h1/ops-h1.c | 66 ++-
lib/roles/h2/http2.c | 1 +
lib/roles/h2/ops-h2.c | 20 +-
lib/roles/http/client/client-handshake.c | 2 +-
lib/roles/http/server/server.c | 246 +++++++++
lib/roles/ws/client-parser.c | 105 ++--
lib/roles/ws/client-ws.c | 28 +-
.../ws/ext/extension-permessage-deflate.c | 25 +-
lib/roles/ws/ops-ws.c | 425 +++++++++------
lib/roles/ws/private.h | 6 +-
lib/roles/ws/server-ws.c | 325 +++++++++--
lib/service.c | 15 +-
minimal-examples/ws-client/README.md | 1 +
.../minimal-ws-client-echo/CMakeLists.txt | 78 +++
.../minimal-ws-client-echo/README.md | 35 ++
.../minimal-ws-client-echo.c | 143 +++++
.../protocol_lws_minimal_client_echo.c | 309 +++++++++++
minimal-examples/ws-server/README.md | 1 +
.../minimal-ws-server-echo/CMakeLists.txt | 78 +++
.../minimal-ws-server-echo/README.md | 30 +
.../minimal-ws-server-echo.c | 117 ++++
.../protocol_lws_minimal_server_echo.c | 258 +++++++++
scripts/autobahn-test.sh | 176 +++++-
scripts/travis_install.sh | 5 +
test-apps/attack.sh | 10 +-
test-apps/test-echo.c | 511 ------------------
36 files changed, 2198 insertions(+), 1450 deletions(-)
delete mode 100644 lib/roles/h1/client-h1.c
create mode 100644 minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt
create mode 100644 minimal-examples/ws-client/minimal-ws-client-echo/README.md
create mode 100644 minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c
create mode 100644 minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c
create mode 100644 minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt
create mode 100644 minimal-examples/ws-server/minimal-ws-server-echo/README.md
create mode 100644 minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c
create mode 100644 minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c
delete mode 100644 test-apps/test-echo.c
diff --git a/.travis.yml b/.travis.yml
index 6960d353..099c6de8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e12e1889..70c079aa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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}")
diff --git a/README.md b/README.md
index 62ec302b..da1e2a42 100644
--- a/README.md
+++ b/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-----
-```
diff --git a/READMEs/README.test-apps.md b/READMEs/README.test-apps.md
index 9c251db1..4482ec8b 100644
--- a/READMEs/README.test-apps.md
+++ b/READMEs/README.test-apps.md
@@ -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
-
-3) Run tests like this
-
-libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=20&agent=libwebsockets" -v -d 65535 -n 1
-
-(this runs test 20)
+2) From your build dir: cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1 && make
-4) In a browser, go here
+3) ../scripts/autobahn-test.sh
-http://localhost:8080/test_browser.html
+4) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
-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
diff --git a/lib/context.c b/lib/context.c
index 7f3a456c..2a46ff9d 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -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.
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 1c609911..eabd4bc0 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -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;) {
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 6e31aec9..db6f1d3c 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -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;
diff --git a/lib/output.c b/lib/output.c
index c6d20f74..34530868 100644
--- a/lib/output.c
+++ b/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)
{
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 8ac106f1..ac976dbd 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -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
diff --git a/lib/roles/h1/client-h1.c b/lib/roles/h1/client-h1.c
deleted file mode 100644
index 41780394..00000000
--- a/lib/roles/h1/client-h1.c
+++ /dev/null
@@ -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;
-}
-
diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index c98fb950..976aa47b 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -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,
diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c
index 1f8dc2f2..a61dd218 100644
--- a/lib/roles/h2/http2.c
+++ b/lib/roles/h2/http2.c
@@ -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;
}
diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c
index ed4c992c..3ae0fa82 100644
--- a/lib/roles/h2/ops-h2.c
+++ b/lib/roles/h2/ops-h2.c
@@ -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;
}
diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c
index 1b24819d..197b28b5 100644
--- a/lib/roles/http/client/client-handshake.c
+++ b/lib/roles/http/client/client-handshake.c
@@ -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) {
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index d80dbf13..816e988a 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -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)
diff --git a/lib/roles/ws/client-parser.c b/lib/roles/ws/client-parser.c
index a1168883..111be039 100644
--- a/lib/roles/ws/client-parser.c
+++ b/lib/roles/ws/client-parser.c
@@ -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;
diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c
index 7c009125..e39abd65 100644
--- a/lib/roles/ws/client-ws.c
+++ b/lib/roles/ws/client-ws.c
@@ -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;
diff --git a/lib/roles/ws/ext/extension-permessage-deflate.c b/lib/roles/ws/ext/extension-permessage-deflate.c
index 556451b9..e23d80d2 100644
--- a/lib/roles/ws/ext/extension-permessage-deflate.c
+++ b/lib/roles/ws/ext/extension-permessage-deflate.c
@@ -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;
diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c
index 055fce5a..162acab4 100644
--- a/lib/roles/ws/ops-ws.c
+++ b/lib/roles/ws/ops-ws.c
@@ -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];
+
+ --wsi->ws->rx_packet_length;
+ }
- if (--wsi->ws->rx_packet_length == 0) {
+ 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.
- */
-
- ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
- ebuf.len = wsi->ws->rx_ubuf_head;
+ lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
- 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;
+ }
+
+ /* 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) {
+ 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 */
diff --git a/lib/roles/ws/private.h b/lib/roles/ws/private.h
index 21c6b656..c8cfd4d0 100644
--- a/lib/roles/ws/private.h
+++ b/lib/roles/ws/private.h
@@ -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)
diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c
index 69858316..9bcb3bcf 100644
--- a/lib/roles/ws/server-ws.c
+++ b/lib/roles/ws/server-ws.c
@@ -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:
}
+
+/*
+ * 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)
+{
+ 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;
+
+ /*
+ * 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...
+ */
+
+#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_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
+lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
{
- int m, draining_flow = 0;
+ int m, bulk = 0;
- if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
- draining_flow = 1;
+ lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
- lwsl_parser("%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;
- /* process the byte */
- m = lws_ws_rx_sm(wsi, *(*buf)++);
- if (m < 0)
- return -1;
- len--;
+ 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) {
- /* 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);
+ break;
+ }
+ if (m < 0) {
+ lwsl_info("%s: rest_is_payload bailed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ 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);
+ }
- lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
+ if (m < 0) {
+ lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
+ bulk);
- /* 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 */
+ return -1;
}
+
+ 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;
}
diff --git a/lib/service.c b/lib/service.c
index a2628c74..3c44ef29 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -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;
diff --git a/minimal-examples/ws-client/README.md b/minimal-examples/ws-client/README.md
index e769c01e..ede1b24b 100644
--- a/minimal-examples/ws-client/README.md
+++ b/minimal-examples/ws-client/README.md
@@ -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
diff --git a/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt b/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt
new file mode 100644
index 00000000..8a8cc747
--- /dev/null
+++ b/minimal-examples/ws-client/minimal-ws-client-echo/CMakeLists.txt
@@ -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()
diff --git a/minimal-examples/ws-client/minimal-ws-client-echo/README.md b/minimal-examples/ws-client/minimal-ws-client-echo/README.md
new file mode 100644
index 00000000..8417804b
--- /dev/null
+++ b/minimal-examples/ws-client/minimal-ws-client-echo/README.md
@@ -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
+...
+```
+
diff --git a/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c b/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c
new file mode 100644
index 00000000..cb8ce052
--- /dev/null
+++ b/minimal-examples/ws-client/minimal-ws-client-echo/minimal-ws-client-echo.c
@@ -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;
+}
diff --git a/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c b/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c
new file mode 100644
index 00000000..cdee75c4
--- /dev/null
+++ b/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c
@@ -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
diff --git a/minimal-examples/ws-server/README.md b/minimal-examples/ws-server/README.md
index 8781b9d9..eb33c7a3 100644
--- a/minimal-examples/ws-server/README.md
+++ b/minimal-examples/ws-server/README.md
@@ -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
diff --git a/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt b/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt
new file mode 100644
index 00000000..0ea8692c
--- /dev/null
+++ b/minimal-examples/ws-server/minimal-ws-server-echo/CMakeLists.txt
@@ -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()
diff --git a/minimal-examples/ws-server/minimal-ws-server-echo/README.md b/minimal-examples/ws-server/minimal-ws-server-echo/README.md
new file mode 100644
index 00000000..bf65c6ea
--- /dev/null
+++ b/minimal-examples/ws-server/minimal-ws-server-echo/README.md
@@ -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
+...
+```
+
diff --git a/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c b/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c
new file mode 100644
index 00000000..9e4ed455
--- /dev/null
+++ b/minimal-examples/ws-server/minimal-ws-server-echo/minimal-ws-server-echo.c
@@ -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;
+}
diff --git a/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c b/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c
new file mode 100644
index 00000000..7708dfb4
--- /dev/null
+++ b/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c
@@ -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
diff --git a/scripts/autobahn-test.sh b/scripts/autobahn-test.sh
index 91b91714..550c5e74 100755
--- a/scripts/autobahn-test.sh
+++ b/scripts/autobahn-test.sh
@@ -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 &
-
- 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
+CLIE=bin/lws-minimal-ws-client-echo
+SERV=bin/lws-minimal-ws-server-echo
+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
+
diff --git a/scripts/travis_install.sh b/scripts/travis_install.sh
index c74d3738..90f38802 100755
--- a/scripts/travis_install.sh
+++ b/scripts/travis_install.sh
@@ -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" ];
diff --git a/test-apps/attack.sh b/test-apps/attack.sh
index 672ff646..18e005ed 100755
--- a/test-apps/attack.sh
+++ b/test-apps/attack.sh
@@ -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
diff --git a/test-apps/test-echo.c b/test-apps/test-echo.c
deleted file mode 100644
index 5076b34c..00000000
--- a/test-apps/test-echo.c
+++ /dev/null
@@ -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;
-}
--
GitLab