1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

autobahn fixes

This replaces the old test-app for echo with separate client and server
minimal versions.

The autobahn test script is made more autonomous and tests both
client and server.
This commit is contained in:
Andy Green 2018-04-20 10:33:23 +08:00
parent 3459d4fe43
commit 7b227eb333
36 changed files with 2199 additions and 1451 deletions

View file

@ -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:

View file

@ -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}")

View file

@ -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
![wss-over-h2](https://libwebsockets.org/sc-wss-over-h2.png)
@ -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-----
```

View file

@ -238,30 +238,6 @@ For those two options libuv is needed to support the protocol plugins, if
that's not possible then the other variations with their own protocol code
should be considered.
@section echo Testing simple echo
You can test against `echo.websockets.org` as a sanity test like
this (the client connects to port `80` by default):
```
$ libwebsockets-test-echo --client echo.websocket.org
```
This echo test is of limited use though because it doesn't
negotiate any protocol. You can run the same test app as a
local server, by default on localhost:7681
```
$ libwebsockets-test-echo
```
and do the echo test against the local echo server
```
$ libwebsockets-test-echo --client localhost --port 7681
```
If you add the `--ssl` switch to both the client and server, you can also test
with an encrypted link.
@section tassl Testing SSL on the client side
To test SSL/WSS client action, just run the client test with
@ -436,37 +412,25 @@ treatment to the other app during that call.
@section autobahn Autobahn Test Suite
Lws can be tested against the autobahn websocket fuzzer.
Lws can be tested against the autobahn websocket fuzzer in both client and
server modes
1) pip install autobahntestsuite
2) wstest -m fuzzingserver
2) From your build dir: cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1 && make
3) Run tests like this
3) ../scripts/autobahn-test.sh
libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=20&agent=libwebsockets" -v -d 65535 -n 1
4) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
(this runs test 20)
4) In a browser, go here
http://localhost:8080/test_browser.html
fill in "libwebsockets" in "User Agent Identifier" and press "Update Reports (Manual)"
5) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
file:///projects/libwebsockets/reports/clients/index.html
file:///projects/libwebsockets/build/reports/clients/index.html
to see the results
@section autobahnnotes Autobahn Test Notes
1) Autobahn tests the user code + lws implementation. So to get the same
results, you need to follow test-echo.c in terms of user implementation.
2) Two of the tests make no sense for Libwebsockets to support and we fail them.
1) Two of the tests make no sense for Libwebsockets to support and we fail them.
- Tests 2.10 + 2.11: sends multiple pings on one connection. Lws policy is to
only allow one active ping in flight on each connection, the rest are dropped.
@ -474,5 +438,7 @@ The autobahn test itself admits this is not part of the standard, just someone's
random opinion about how they think a ws server should act. So we will fail
this by design and it is no problem about RFC6455 compliance.
2) Currently two parts of autobahn are broken and we skip them
https://github.com/crossbario/autobahn-testsuite/issues/71

View file

@ -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.

View file

@ -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;) {

View file

@ -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;

View file

@ -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)
{

View file

@ -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

View file

@ -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;
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -24,18 +24,21 @@
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
/*
* client-parser.c: lws_client_rx_sm() needs to be roughly kept in
* client-parser.c: lws_ws_client_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
int
lws_ws_rx_sm(struct lws *wsi, unsigned char c)
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
{
int callback_action = LWS_CALLBACK_RECEIVE;
int ret = 0, rx_draining_ext = 0;
unsigned short close_code;
struct lws_tokens ebuf;
unsigned char *pp;
int n = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
int n;
int lin;
#endif
ebuf.token = NULL;
@ -131,14 +134,42 @@ handle_first:
wsi->ws->opcode = c & 0xf;
wsi->ws->rsv = c & 0x70;
wsi->ws->final = !!((c >> 7) & 1);
wsi->ws->defeat_check_utf8 = 0;
if (((wsi->ws->opcode) & 8) && !wsi->ws->final) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
(uint8_t *)"frag ctl", 8);
return -1;
}
switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
wsi->ws->check_utf8 = lws_check_opt(
wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8);
/* fallthru */
case LWSWSOPC_BINARY_FRAME:
if (wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)
wsi->ws->check_utf8 = 0;
if (wsi->ws->continuation_possible) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
return -1;
}
wsi->ws->rsv_first_msg = (c & 0x70);
wsi->ws->frame_is_binary =
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME;
wsi->ws->first_fragment = 1;
wsi->ws->continuation_possible = !wsi->ws->final;
break;
case LWSWSOPC_CONTINUATION:
if (!wsi->ws->continuation_possible) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad cont", 8);
return -1;
}
break;
case LWSWSOPC_CLOSE:
wsi->ws->check_utf8 = 0;
wsi->ws->utf8 = 0;
break;
case 3:
case 4:
@ -150,10 +181,37 @@ handle_first:
case 0xd:
case 0xe:
case 0xf:
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad opc", 7);
lwsl_info("illegal opcode\n");
return -1;
}
if (wsi->ws->owed_a_fin &&
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_info("hey you owed us a FIN\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR, (uint8_t *)"bad fin", 7);
return -1;
}
if ((!(wsi->ws->opcode & 8)) && wsi->ws->final) {
wsi->ws->continuation_possible = 0;
wsi->ws->owed_a_fin = 0;
}
if (!wsi->ws->final)
wsi->ws->owed_a_fin = 1;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
if (wsi->ws->rsv &&
(
#if !defined(LWS_WITHOUT_EXTENSIONS)
!wsi->count_act_ext ||
#endif
(wsi->ws->rsv & ~0x40))) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_PROTOCOL_ERR,
(uint8_t *)"rsv bits", 8);
return -1;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
@ -177,14 +235,16 @@ handle_first:
break;
default:
wsi->ws->rx_packet_length = c & 0x7f;
if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
if (wsi->ws->rx_packet_length)
if (wsi->ws->rx_packet_length) {
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
LWS_RXPS_WS_FRAME_PAYLOAD;
} else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
@ -202,9 +262,10 @@ handle_first:
if (wsi->ws->this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
else {
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
LWS_RXPS_WS_FRAME_PAYLOAD;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
@ -263,8 +324,7 @@ handle_first:
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
@ -292,8 +352,7 @@ handle_first:
wsi->ws->mask[3] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
wsi->lws_rx_parse_state = LWS_RXPS_WS_FRAME_PAYLOAD;
wsi->ws->mask_idx = 0;
if (wsi->ws->rx_packet_length == 0) {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
@ -302,32 +361,36 @@ handle_first:
break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
case LWS_RXPS_WS_FRAME_PAYLOAD:
assert(wsi->ws->rx_ubuf);
if (wsi->ws->rx_draining_ext)
goto drain_extension;
if (wsi->ws->rx_ubuf_head + LWS_PRE >=
wsi->ws->rx_ubuf_alloc) {
if (wsi->ws->rx_ubuf_head + LWS_PRE >= wsi->ws->rx_ubuf_alloc) {
lwsl_err("Attempted overflow \n");
return -1;
}
if (wsi->ws->all_zero_nonce)
wsi->ws->rx_ubuf[LWS_PRE +
(wsi->ws->rx_ubuf_head++)] = c;
else
wsi->ws->rx_ubuf[LWS_PRE +
(wsi->ws->rx_ubuf_head++)] =
c ^ wsi->ws->mask[
(wsi->ws->mask_idx++) & 3];
if (!(already_processed & ALREADY_PROCESSED_IGNORE_CHAR)) {
if (wsi->ws->all_zero_nonce)
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
c;
else
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] =
c ^ wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
if (--wsi->ws->rx_packet_length == 0) {
--wsi->ws->rx_packet_length;
}
if (!wsi->ws->rx_packet_length) {
lwsl_debug("%s: ws fragment length exhausted\n", __func__);
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
if (wsi->ws->rx_draining_ext) {
lwsl_debug("%s: UNTIL_EXHAUSTED draining\n", __func__);
goto drain_extension;
}
/*
* if there's no protocol max frame size given, we are
* supposed to default to context->pt_serv_buf_size
@ -352,6 +415,19 @@ spill:
switch (wsi->ws->opcode) {
case LWSWSOPC_CLOSE:
if (wsi->ws->peer_has_sent_close)
break;
wsi->ws->peer_has_sent_close = 1;
pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->ws->rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->ws->utf8, pp + 2,
wsi->ws->rx_ubuf_head - 2))
goto utf8_fail;
/* is this an acknowledgment of our close? */
if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/*
@ -376,6 +452,23 @@ spill:
return -1;
}
if (wsi->ws->rx_ubuf_head >= 2) {
close_code = (pp[0] << 8) | pp[1];
if (close_code < 1000 ||
close_code == 1004 ||
close_code == 1005 ||
close_code == 1006 ||
close_code == 1012 ||
close_code == 1013 ||
close_code == 1014 ||
close_code == 1015 ||
(close_code >= 1016 && close_code < 3000)
) {
pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
}
}
if (user_callback_handle_rxflow(
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
@ -445,26 +538,9 @@ ping_drop:
break;
default:
lwsl_parser("passing opc %x up to exts\n",
wsi->ws->opcode);
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* state machine.
*/
lwsl_parser("unknown opc %x\n", wsi->ws->opcode);
ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
ebuf.len = wsi->ws->rx_ubuf_head;
if (lws_ext_cb_active(wsi,
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
&ebuf, 0) <= 0)
/* not handle or fail */
lwsl_ext("ext opc opcode 0x%x unknown\n",
wsi->ws->opcode);
wsi->ws->rx_ubuf_head = 0;
return 0;
return -1;
}
/*
@ -480,19 +556,24 @@ ping_drop:
goto already_done;
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
// lwsl_notice("%s: passing %d to ext\n", __func__, ebuf.len);
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
goto already_done;
#if !defined(LWS_WITHOUT_EXTENSIONS)
lin = ebuf.len;
//if (lin)
// lwsl_hexdump_notice(ebuf.token, ebuf.len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
lwsl_debug("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
if (wsi->ws->rx_draining_ext)
already_processed &= ~ALREADY_PROCESSED_NO_CB;
#endif
/*
* ebuf may be pointing somewhere completely different now,
* it's the output
*/
wsi->ws->first_fragment = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (n < 0) {
/*
@ -515,11 +596,38 @@ drain_extension:
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (ebuf.len > 0 ||
callback_action == LWS_CALLBACK_RECEIVE_PONG) {
ebuf.token[ebuf.len] = '\0';
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
(unsigned char *)ebuf.token,
ebuf.len)) {
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"bad utf8", 8);
goto utf8_fail;
}
if (wsi->protocol->callback) {
/* we are ending partway through utf-8 character? */
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
wsi->ws->utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_notice("utf8 error\n");
lwsl_hexdump_notice(ebuf.token, ebuf.len);
return -1;
}
}
if (!wsi->wsistate_pre_close && (ebuf.len >= 0 ||
callback_action == LWS_CALLBACK_RECEIVE_PONG)) {
if (ebuf.len)
ebuf.token[ebuf.len] = '\0';
if (wsi->protocol->callback &&
!(already_processed & ALREADY_PROCESSED_NO_CB)) {
if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
lwsl_info("Doing pong callback\n");
@ -531,10 +639,14 @@ drain_extension:
ebuf.token,
ebuf.len);
}
else
lwsl_err("No callback on payload spill!\n");
wsi->ws->first_fragment = 0;
}
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (!lin)
break;
#endif
already_done:
wsi->ws->rx_ubuf_head = 0;
break;
@ -569,7 +681,7 @@ lws_add_wsi_to_draining_ext_list(struct lws *wsi)
if (wsi->ws->rx_draining_ext)
return;
lwsl_ext("%s: RX EXT DRAINING: Adding to list\n", __func__);
lwsl_debug("%s: RX EXT DRAINING: Adding to list\n", __func__);
wsi->ws->rx_draining_ext = 1;
wsi->ws->rx_draining_ext_list = pt->rx_draining_ext_list;
@ -585,7 +697,7 @@ lws_remove_wsi_from_draining_ext_list(struct lws *wsi)
if (!wsi->ws->rx_draining_ext)
return;
lwsl_ext("%s: RX EXT DRAINING: Removing from list\n", __func__);
lwsl_debug("%s: RX EXT DRAINING: Removing from list\n", __func__);
wsi->ws->rx_draining_ext = 0;
@ -630,70 +742,6 @@ lws_0405_frame_mask_generate(struct lws *wsi)
return 0;
}
/* Once we reach LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED, we know how much
* to expect in that state and can deal with it in bulk more efficiently.
*/
int
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
size_t *len)
{
unsigned char *buffer = *buf, mask[4];
int buffer_size, n;
unsigned int avail;
char *rx_ubuf;
if (wsi->protocol->rx_buffer_size)
buffer_size = (int)wsi->protocol->rx_buffer_size;
else
buffer_size = wsi->context->pt_serv_buf_size;
avail = buffer_size - wsi->ws->rx_ubuf_head;
/* do not consume more than we should */
if (avail > wsi->ws->rx_packet_length)
avail = (unsigned int)wsi->ws->rx_packet_length;
/* do not consume more than what is in the buffer */
if (avail > *len)
avail = (unsigned int)(*len);
/* we want to leave 1 byte for the parser to handle properly */
if (avail <= 1)
return 0;
avail--;
rx_ubuf = wsi->ws->rx_ubuf + LWS_PRE + wsi->ws->rx_ubuf_head;
if (wsi->ws->all_zero_nonce)
memcpy(rx_ubuf, buffer, avail);
else {
for (n = 0; n < 4; n++)
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
/* deal with 4-byte chunks using unwrapped loop */
n = avail >> 2;
while (n--) {
*(rx_ubuf++) = *(buffer++) ^ mask[0];
*(rx_ubuf++) = *(buffer++) ^ mask[1];
*(rx_ubuf++) = *(buffer++) ^ mask[2];
*(rx_ubuf++) = *(buffer++) ^ mask[3];
}
/* and the remaining bytes bytewise */
for (n = 0; n < (int)(avail & 3); n++)
*(rx_ubuf++) = *(buffer++) ^ mask[n];
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
}
(*buf) += avail;
wsi->ws->rx_ubuf_head += avail;
wsi->ws->rx_packet_length -= avail;
*len -= avail;
return avail;
}
int
lws_server_init_wsi_for_ws(struct lws *wsi)
{
@ -753,7 +801,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
LWS_VISIBLE int
lws_is_final_fragment(struct lws *wsi)
{
lwsl_info("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
lwsl_debug("%s: final %d, rx pk length %ld, draining %ld\n", __func__,
wsi->ws->final, (long)wsi->ws->rx_packet_length,
(long)wsi->ws->rx_draining_ext);
return wsi->ws->final && !wsi->ws->rx_packet_length &&
@ -821,16 +869,21 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
{
struct lws_tokens ebuf;
unsigned int pending = 0;
char draining_flow = 0, buffered = 0;
char buffered = 0;
int n = 0, m;
#if defined(LWS_WITH_HTTP2)
struct lws *wsi1;
#endif
if (!wsi->ws) {
lwsl_err("ws role wsi with no ws\n");
return 1;
}
// lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
wsi->wsistate, pollfd->revents & LWS_POLLOUT);
//lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
// wsi->wsistate, pollfd->revents & LWS_POLLOUT);
/*
* something went wrong with parsing the handshake, and
@ -859,6 +912,8 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
return LWS_HPI_RET_HANDLED;
}
//lwsl_notice("%s: wsi->ws->tx_draining_ext %d revents 0x%x 0x%x %d\n", __func__, wsi->ws->tx_draining_ext, pollfd->revents, wsi->wsistate, lwsi_state_can_handle_POLLOUT(wsi));
/* 1: something requested a callback when it was OK to write */
if ((pollfd->revents & LWS_POLLOUT) &&
@ -866,14 +921,12 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
lws_handle_POLLOUT_event(wsi, pollfd)) {
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
/* the write failed... it's had it */
wsi->socket_is_permanently_unusable = 1;
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
/*
* we stopped caring about anything except control
* packets. Force flow control off, defeat tx
@ -884,7 +937,7 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
wsi->ws->tx_draining_ext = 0;
}
if (wsi->ws && wsi->ws->tx_draining_ext)
if (wsi->ws->tx_draining_ext)
/*
* We cannot deal with new RX until the TX ext path has
* been drained. It's because new rx will, eg, crap on
@ -895,11 +948,13 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
*/
return LWS_HPI_RET_HANDLED;
if (lws_is_flowcontrolled(wsi))
if (lws_is_flowcontrolled(wsi)) {
/* We cannot deal with any kind of new RX because we are
* RX-flowcontrolled.
*/
lwsl_info("flowcontrolled\n");
return LWS_HPI_RET_HANDLED;
}
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream || wsi->upgraded_to_http2) {
@ -917,23 +972,23 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
/* 2: RX Extension needs to be drained
*/
if (lwsi_role_ws(wsi) && wsi->ws && wsi->ws->rx_draining_ext) {
if (wsi->ws->rx_draining_ext) {
lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
lwsl_debug("%s: RX EXT DRAINING: Service\n", __func__);
#ifndef LWS_NO_CLIENT
if (lwsi_role_client(wsi)) {
n = lws_client_rx_sm(wsi, 0);
n = lws_ws_client_rx_sm(wsi, 0);
if (n < 0)
/* we closed wsi */
n = 0;
return LWS_HPI_RET_PLEASE_CLOSE_ME;
} else
#endif
n = lws_ws_rx_sm(wsi, 0);
n = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
return LWS_HPI_RET_HANDLED;
}
if (wsi->ws && wsi->ws->rx_draining_ext)
if (wsi->ws->rx_draining_ext)
/*
* We have RX EXT content to drain, but can't do it
* right now. That means we cannot do anything lower
@ -943,7 +998,8 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
/* 3: buflist needs to be drained
*/
read:
//lws_buflist_describe(&wsi->buflist, wsi);
ebuf.len = (int)lws_buflist_next_segment_len(&wsi->buflist,
(uint8_t **)&ebuf.token);
if (ebuf.len) {
@ -955,7 +1011,6 @@ rops_handle_POLLIN_ws(struct lws_context_per_thread *pt, struct lws *wsi,
if (!(pollfd->revents & pollfd->events & LWS_POLLIN) && !wsi->ah)
return LWS_HPI_RET_HANDLED;
read:
if (lws_is_flowcontrolled(wsi)) {
lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
__func__, wsi, wsi->rxflow_bitmap);
@ -964,18 +1019,21 @@ read:
if (!(lwsi_role_client(wsi) &&
(lwsi_state(wsi) != LRS_ESTABLISHED &&
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK &&
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
/*
* extension may not consume everything
* (eg, pmd may be constrained
* as to what it can output...) has to go in
* per-wsi rx buf area.
* Otherwise in large temp serv_buf area.
* In case we are going to react to this rx by scheduling
* writes, we need to restrict the amount of rx to the size
* the protocol reported for rx buffer.
*
* Otherwise we get a situation we have to absorb possibly a
* lot of reads before we get a chance to drain them by writing
* them, eg, with echo type tests in autobahn.
*/
buffered = 0;
ebuf.token = (char *)pt->serv_buf;
if (lws_is_ws_with_ext(wsi))
if (lwsi_role_ws(wsi))
ebuf.len = wsi->ws->rx_ubuf_alloc;
else
ebuf.len = wsi->context->pt_serv_buf_size;
@ -1026,15 +1084,9 @@ drain:
*/
m = 0;
do {
#if !defined(LWS_WITHOUT_EXTENSIONS)
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_RX_PREPARSE,
&ebuf, 0);
if (m < 0)
return LWS_HPI_RET_PLEASE_CLOSE_ME;
#endif
/* service incoming data */
//lws_buflist_describe(&wsi->buflist, wsi);
if (ebuf.len) {
#if defined(LWS_ROLE_H2)
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
@ -1050,6 +1102,8 @@ drain:
n = 0;
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
//lws_buflist_describe(&wsi->buflist, wsi);
//lwsl_notice("%s: consuming %d / %d\n", __func__, n, ebuf.len);
if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
@ -1078,7 +1132,7 @@ drain:
goto read;
}
if (draining_flow && /* were draining, now nothing left */
if (buffered && /* were draining, now nothing left */
!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
lwsl_info("%s: %p flow buf: drained\n", __func__, wsi);
/* having drained the rxflow buffer, can rearm POLLIN */
@ -1103,7 +1157,8 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
#endif
int n;
// lwsl_notice("%s: %s\n", __func__, wsi->protocol->name);
lwsl_debug("%s: %s: wsi->ws->tx_draining_ext %d\n", __func__,
wsi->protocol->name, wsi->ws->tx_draining_ext);
/* Priority 3: pending control packets (pong or close)
*
@ -1112,16 +1167,22 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
lwsl_debug("sending close packet\n");
lwsl_hexdump_debug(&wsi->ws->ping_payload_buf[LWS_PRE],
wsi->ws->close_in_ping_buffer_len);
wsi->waiting_to_send_close_frame = 0;
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
wsi->ws->close_in_ping_buffer_len,
LWS_WRITE_CLOSE);
if (n >= 0) {
lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
lwsl_debug("sent close indication, awaiting ack\n");
if (wsi->close_needs_ack) {
lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
lwsl_debug("sent close indication, awaiting ack\n");
return LWS_HP_RET_BAIL_OK;
return LWS_HP_RET_BAIL_OK;
}
wsi->close_needs_ack = 0;
lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
}
return LWS_HP_RET_BAIL_DIE;
@ -1135,6 +1196,14 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
if (wsi->ws->payload_is_close)
write_type = LWS_WRITE_CLOSE;
else {
if (wsi->wsistate_pre_close) {
/* we started close flow, forget pong */
wsi->ws->ping_pending_flag = 0;
return LWS_HP_RET_BAIL_OK;
}
lwsl_info("issuing pong %d on wsi %p\n", wsi->ws->ping_payload_len, wsi);
}
n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
wsi->ws->ping_payload_len, write_type);
@ -1199,10 +1268,6 @@ int rops_handle_POLLOUT_ws(struct lws *wsi)
/* Priority 6: extensions
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
if (m)
return LWS_HP_RET_BAIL_DIE;
if (!wsi->extension_data_pending)
return LWS_HP_RET_USER_SERVICE;
@ -1389,6 +1454,7 @@ rops_close_via_role_protocol_ws(struct lws *wsi, enum lws_close_status reason)
}
wsi->waiting_to_send_close_frame = 1;
wsi->close_needs_ack = 1;
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
@ -1417,7 +1483,7 @@ rops_close_role_ws(struct lws_context_per_thread *pt, struct lws *wsi)
if (wsi->ws->tx_draining_ext) {
struct lws **w = &pt->tx_draining_ext_list;
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
@ -1449,14 +1515,18 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
int masked7 = lwsi_role_client(wsi);
unsigned char is_masked_bit = 0;
unsigned char *dropmask = NULL;
enum lws_write_protocol wpt;
struct lws_tokens ebuf;
size_t orig_len = len;
int pre = 0, n;
int pre = 0, n = 0;
// lwsl_err("%s: wp 0x%x len %d\n", __func__, *wp, (int)len);
if (wsi->ws->tx_draining_ext) {
/* remove us from the list */
struct lws **w = &pt->tx_draining_ext_list;
lwsl_notice("%s: CLEARING tx_draining_ext\n", __func__);
wsi->ws->tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
@ -1467,10 +1537,23 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
w = &((*w)->ws->tx_draining_ext_list);
}
wsi->ws->tx_draining_ext_list = NULL;
*wp = (wsi->ws->tx_draining_stashed_wp & 0xc0) |
wpt = *wp;
*wp = (wsi->ws->tx_draining_stashed_wp & 0xc0)|
LWS_WRITE_CONTINUATION;
lwsl_ext("FORCED draining wp to 0x%02X\n", *wp);
/*
* When we are just flushing (len == 0), we can trust the
* stashed wp info completely. Otherwise adjust it to the
* FIN status of the incoming packet.
*/
if (!(wpt & LWS_WRITE_NO_FIN) && len)
*wp &= ~LWS_WRITE_NO_FIN;
lwsl_notice("FORCED draining wp to 0x%02X (stashed 0x%02X, incoming 0x%02X)\n", *wp,
wsi->ws->tx_draining_stashed_wp, wpt);
// assert(0);
}
lws_restart_ws_ping_pong_timer(wsi);
@ -1515,17 +1598,19 @@ rops_write_role_protocol_ws(struct lws *wsi, unsigned char *buf, size_t len,
break;
default:
#if !defined(LWS_WITHOUT_EXTENSIONS)
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
// lwsl_notice("LWS_EXT_CB_PAYLOAD_TX\n");
// m = (int)ebuf.len;
/* returns 0 if no more tx pending, 1 if more pending */
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &ebuf, *wp);
if (n < 0)
return -1;
// lwsl_notice("ext processed %d plaintext into %d compressed (wp 0x%x)\n", m, (int)ebuf.len, *wp);
if (n && ebuf.len) {
lwsl_debug("drain len %d\n", (int)ebuf.len);
lwsl_notice("write drain len %d (wp 0x%x) SETTING tx_draining_ext\n", (int)ebuf.len, *wp);
/* extension requires further draining */
wsi->ws->tx_draining_ext = 1;
wsi->ws->tx_draining_ext_list =
pt->tx_draining_ext_list;
wsi->ws->tx_draining_ext_list = pt->tx_draining_ext_list;
pt->tx_draining_ext_list = wsi;
/* we must come back to do more */
lws_callback_on_writable(wsi);
@ -1763,8 +1848,6 @@ rops_close_kill_connection_ws(struct lws *wsi, enum lws_close_status reason)
static int
rops_callback_on_writable_ws(struct lws *wsi)
{
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
return 1;
#if defined(LWS_WITH_HTTP2)
if (lwsi_role_h2_ENCAPSULATION(wsi)) {
/* we know then that it has an h2 parent */

View file

@ -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)

View file

@ -75,10 +75,14 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
while (more) {
if (c >= (char *)pt->serv_buf + 255)
return -1;
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
args = c + 1;
if (!args)
args = c + 1;
}
if (ignore || *c == ' ') {
c++;
@ -173,11 +177,19 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
/*
* go through the options trying to apply the
* The client may send a bunch of different option
* sets for the same extension, we are supposed to
* pick one we like the look of. The option sets are
* separated by comma.
*
* Actually we just either accept the first one or
* nothing.
*
* Go through the options trying to apply the
* recognized ones
*/
lwsl_debug("ext args %s", args);
lwsl_info("ext args %s\n", args);
while (args && *args && *args != ',') {
while (*args == ' ')
@ -194,9 +206,10 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
oa.option_name = NULL;
oa.option_index = (int)(po - opts);
oa.start = NULL;
lwsl_debug("setting %s\n", po->name);
if (!ext->callback(
lws_get_context(wsi), ext, wsi,
oa.len = 0;
lwsl_info("setting '%s'\n", po->name);
if (!ext->callback(lws_get_context(wsi),
ext, wsi,
LWS_EXT_CB_OPTION_SET,
wsi->act_ext_user[
wsi->count_act_ext],
@ -211,11 +224,17 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
}
while (*args && *args != ',' && *args != ';')
args++;
if (*args == ';')
args++;
}
wsi->count_act_ext++;
lwsl_parser("cnt_act_ext <- %d\n", wsi->count_act_ext);
if (args && *args == ',')
more = 0;
ext++;
}
@ -454,7 +473,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
/* make a buffer big enough for everything */
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + 256 + LWS_PRE;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
@ -499,23 +518,18 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
LWS_CPYAPP(p, "\x0d\x0a");
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
response, p - response)) {
/* okay send the handshake response accepting the connection */
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n",
lws_ptr_diff(p, response));
lwsl_parser("issuing resp pkt %d len\n",
lws_ptr_diff(p, response));
#if defined(DEBUG)
fwrite(response, 1, p - response, stderr);
fwrite(response, 1, p - response, stderr);
#endif
n = lws_write(wsi, (unsigned char *)response, p - response,
LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
goto bail;
}
n = lws_write(wsi, (unsigned char *)response, p - response,
LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_info("%s: ERROR writing to socket %d\n", __func__, n);
goto bail;
}
/* alright clean up and set ourselves into established state */
@ -543,15 +557,200 @@ bail:
}
int
lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
/*
* Once we reach LWS_RXPS_WS_FRAME_PAYLOAD, we know how much
* to expect in that state and can deal with it in bulk more efficiently.
*/
static int
lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len)
{
int m, draining_flow = 0;
uint8_t *buffer = *buf, mask[4];
struct lws_tokens ebuf;
unsigned int avail = len;
#if !defined(LWS_WITHOUT_EXTENSIONS)
unsigned int old_packet_length = (int)wsi->ws->rx_packet_length;
#endif
int n = 0;
if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
draining_flow = 1;
/*
* With zlib, we can give it as much input as we like. The pmd
* extension will draw it down in chunks (default 1024).
*
* If we try to restrict how much we give it, because we must go
* back to the event loop each time, we will drop the remainder...
*/
lwsl_parser("%s: received %d byte packet\n", __func__, (int)len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (!wsi->count_act_ext)
#endif
{
if (wsi->protocol->rx_buffer_size)
avail = (int)wsi->protocol->rx_buffer_size;
else
avail = wsi->context->pt_serv_buf_size;
}
/* do not consume more than we should */
if (avail > wsi->ws->rx_packet_length)
avail = (unsigned int)wsi->ws->rx_packet_length;
/* do not consume more than what is in the buffer */
if (avail > len)
avail = (unsigned int)len;
if (avail <= 0)
return 0;
ebuf.token = (char *)buffer;
ebuf.len = avail;
//lwsl_hexdump_notice(ebuf.token, ebuf.len);
if (!wsi->ws->all_zero_nonce) {
for (n = 0; n < 4; n++)
mask[n] = wsi->ws->mask[(wsi->ws->mask_idx + n) & 3];
/* deal with 4-byte chunks using unwrapped loop */
n = avail >> 2;
while (n--) {
*(buffer) = *(buffer) ^ mask[0];
buffer++;
*(buffer) = *(buffer) ^ mask[1];
buffer++;
*(buffer) = *(buffer) ^ mask[2];
buffer++;
*(buffer) = *(buffer) ^ mask[3];
buffer++;
}
/* and the remaining bytes bytewise */
for (n = 0; n < (int)(avail & 3); n++) {
*(buffer) = *(buffer) ^ mask[n];
buffer++;
}
wsi->ws->mask_idx = (wsi->ws->mask_idx + avail) & 3;
}
lwsl_info("%s: using %d of raw input (total %d on offer)\n", __func__,
avail, (int)len);
(*buf) += avail;
len -= avail;
#if !defined(LWS_WITHOUT_EXTENSIONS)
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
lwsl_info("%s: ext says %d / ebuf.len %d\n", __func__, n, ebuf.len);
#endif
/*
* ebuf may be pointing somewhere completely different now,
* it's the output
*/
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (n < 0) {
/*
* we may rely on this to get RX, just drop connection
*/
lwsl_notice("%s: LWS_EXT_CB_PAYLOAD_RX blew out\n", __func__);
wsi->socket_is_permanently_unusable = 1;
return -1;
}
#endif
wsi->ws->rx_packet_length -= avail;
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* if we had an rx fragment right at the last compressed byte of the
* message, we can get a zero length inflated output, where no prior
* rx inflated output marked themselves with FIN, since there was
* raw ws payload still to drain at that time.
*
* Then we need to generate a zero length ws rx that can be understood
* as the message completion.
*/
if (!ebuf.len && /* zero-length inflation output */
!n && /* nothing left to drain from the inflator */
wsi->count_act_ext && /* we are using pmd */
old_packet_length && /* we gave the inflator new input */
!wsi->ws->rx_packet_length && /* raw ws packet payload all gone */
wsi->ws->final && /* the raw ws packet is a FIN guy */
wsi->protocol->callback &&
!wsi->wsistate_pre_close) {
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_RECEIVE,
wsi->user_space, NULL, 0))
return -1;
return avail;
}
#endif
if (!ebuf.len)
return avail;
if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
n &&
#endif
ebuf.len)
/* extension had more... main loop will come back */
lws_add_wsi_to_draining_ext_list(wsi);
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
(unsigned char *)ebuf.token, ebuf.len)) {
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"bad utf8", 8);
goto utf8_fail;
}
/* we are ending partway through utf-8 character? */
if (!wsi->ws->rx_packet_length && wsi->ws->final &&
wsi->ws->utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_info("utf8 error\n");
lwsl_hexdump_info(ebuf.token, ebuf.len);
return -1;
}
}
if (wsi->protocol->callback && !wsi->wsistate_pre_close)
if (user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_RECEIVE,
wsi->user_space,
ebuf.token, ebuf.len))
return -1;
wsi->ws->first_fragment = 0;
lwsl_info("%s: input used %d, output %d, rem len %d, rx_draining_ext %d\n",
__func__, avail, ebuf.len, (int)len, wsi->ws->rx_draining_ext);
return avail; /* how much we used from the input */
}
int
lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len)
{
int m, bulk = 0;
lwsl_debug("%s: received %d byte packet\n", __func__, (int)len);
//lwsl_hexdump_notice(*buf, len);
/* let the rx protocol state machine have as much as it needs */
@ -560,51 +759,73 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
* we were accepting input but now we stopped doing so
*/
if (wsi->rxflow_bitmap) {
lwsl_info("%s: doing rxflow\n", __func__);
lws_rxflow_cache(wsi, *buf, 0, (int)len);
lwsl_parser("%s: cached %ld\n", __func__, (long)len);
buf += len; /* stashing it is taking care of it */
*buf += len; /* stashing it is taking care of it */
return 1;
}
if (wsi->ws->rx_draining_ext) {
m = lws_ws_rx_sm(wsi, 0);
lwsl_debug("%s: draining rx ext\n", __func__);
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR, 0);
if (m < 0)
return -1;
continue;
}
/* consume payload bytes efficiently */
if (wsi->lws_rx_parse_state ==
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED) {
m = lws_payload_until_length_exhausted(wsi, buf, &len);
if (draining_flow &&
!lws_buflist_use_segment(&wsi->buflist, m))
lws_dll_lws_remove(&wsi->dll_buflist);
while (wsi->lws_rx_parse_state == LWS_RXPS_WS_FRAME_PAYLOAD &&
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME ||
wsi->ws->opcode == LWSWSOPC_CONTINUATION) &&
len) {
uint8_t *bin = *buf;
bulk = 1;
m = lws_ws_frame_rest_is_payload(wsi, buf, len);
assert((int)lws_ptr_diff(*buf, bin) <= (int)len);
len -= lws_ptr_diff(*buf, bin);
if (!m) {
break;
}
if (m < 0) {
lwsl_info("%s: rest_is_payload bailed\n",
__func__);
return -1;
}
}
/* process the byte */
m = lws_ws_rx_sm(wsi, *(*buf)++);
if (m < 0)
if (!bulk) {
/* process the byte */
m = lws_ws_rx_sm(wsi, 0, *(*buf)++);
len--;
} else {
/*
* We already handled this byte in bulk, just deal
* with the ramifications
*/
lwsl_debug("%s: coming out of bulk with len %d, "
"wsi->ws->rx_draining_ext %d\n",
__func__, (int)len,
wsi->ws->rx_draining_ext);
m = lws_ws_rx_sm(wsi, ALREADY_PROCESSED_IGNORE_CHAR |
ALREADY_PROCESSED_NO_CB, 0);
}
if (m < 0) {
lwsl_info("%s: lws_ws_rx_sm bailed %d\n", __func__,
bulk);
return -1;
len--;
/* account for what we're using in rxflow buffer */
if (draining_flow &&
!lws_buflist_use_segment(&wsi->buflist, 1)) {
lws_dll_lws_remove(&wsi->dll_buflist);
lwsl_debug("%s: %p flow buf: drained\n", __func__, wsi);
/* having drained the rxflow buffer, can rearm POLLIN */
#ifdef LWS_NO_SERVER
m =
#endif
__lws_rx_flow_control(wsi);
/* m ignored, needed for NO_SERVER case */
}
bulk = 0;
}
lwsl_parser("%s: exit with %d unused\n", __func__, (int)len);
lwsl_debug("%s: exit with %d unused\n", __func__, (int)len);
return 0;
}

View file

@ -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;

View file

@ -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

View file

@ -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()

View file

@ -0,0 +1,35 @@
# lws minimal ws client + permessage-deflate echo
This example opens a ws client connection to localhost:7681 and
echoes back anything that comes from the server.
You can use it for testing lws against Autobahn.
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-p port|Port to connect to
-u url|URL path part to connect to
-o|Finish after one connection
```
$ ./lws-minimal-ws-client-echo
[2018/04/22 20:03:50:2343] USER: LWS minimal ws client echo + permessage-deflate + multifragment bulk message
[2018/04/22 20:03:50:2344] USER: lws-minimal-ws-client-echo [-n (no exts)] [-u url] [-o (once)]
[2018/04/22 20:03:50:2344] USER: options 0
[2018/04/22 20:03:50:2345] NOTICE: Creating Vhost 'default' (serving disabled), 1 protocols, IPv6 off
[2018/04/22 20:03:51:2356] USER: connecting to localhost:9001//runCase?case=362&agent=libwebsockets
[2018/04/22 20:03:51:2385] NOTICE: checking client ext permessage-deflate
[2018/04/22 20:03:51:2386] NOTICE: instantiating client ext permessage-deflate
[2018/04/22 20:03:51:2386] USER: LWS_CALLBACK_CLIENT_ESTABLISHED
...
```

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -0,0 +1,30 @@
# lws minimal ws server + permessage-deflate echo
This example serves no-protocl-name ws on localhost:7681
and echoes back anything that comes from the client.
You can use it for testing lws against Autobahn (use the
-p option to tell it to listen on 9001 for that)
## build
```
$ cmake . && make
```
## usage
Commandline option|Meaning
---|---
-d <loglevel>|Debug verbosity in decimal, eg, -d15
-p port|Port to connect to
-u url|URL path part to connect to
-o|Finish after one connection
```
$ ./lws-minimal-ws-server-echo
[2018/04/24 10:29:34:6212] USER: LWS minimal ws server echo + permessage-deflate + multifragment bulk message
[2018/04/24 10:29:34:6213] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off
...
```

View file

@ -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;
}

View file

@ -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

View file

@ -1,25 +1,98 @@
#!/bin/sh
#!/bin/bash
#
# Requires pip install autobahntestsuite
#
# you should run this from ./build, after building with
# cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1
#
# It will use the minimal echo client and server to run
# autobahn ws tests as both client and server.
set -u
PARALLEL=8
N=1
OS=`uname`
for i in '1.1.1' '1.1.2' '1.1.3' '1.1.4' '1.1.5' '1.1.6' '1.1.7' '1.1.8' '1.2.1' '1.2.2' '1.2.3' '1.2.4' '1.2.5' '1.2.6' '1.2.7' '1.2.8' '2.1' '2.2' '2.3' '2.4' '2.5' '2.6' '2.7' '2.8' '2.9' '2.10' '2.11' '3.1' '3.2' '3.3' '3.4' '3.5' '3.6' '3.7' '4.1.1' '4.1.2' '4.1.3' '4.1.4' '4.1.5' '4.2.1' '4.2.2' '4.2.3' '4.2.4' '4.2.5' '5.1' '5.2' '5.3' '5.4' '5.5' '5.6' '5.7' '5.8' '5.9' '5.10' '5.11' '5.12' '5.13' '5.14' '5.15' '5.16' '5.17' '5.18' '5.19' '5.20' '6.1.1' '6.1.2' '6.1.3' '6.2.1' '6.2.2' '6.2.3' '6.2.4' '6.3.1' '6.3.2' '6.4.1' '6.4.2' '6.4.3' '6.4.4' '6.5.1' '6.5.2' '6.5.3' '6.5.4' '6.5.5' '6.6.1' '6.6.2' '6.6.3' '6.6.4' '6.6.5' '6.6.6' '6.6.7' '6.6.8' '6.6.9' '6.6.10' '6.6.11' '6.7.1' '6.7.2' '6.7.3' '6.7.4' '6.8.1' '6.8.2' '6.9.1' '6.9.2' '6.9.3' '6.9.4' '6.10.1' '6.10.2' '6.10.3' '6.11.1' '6.11.2' '6.11.3' '6.11.4' '6.11.5' '6.12.1' '6.12.2' '6.12.3' '6.12.4' '6.12.5' '6.12.6' '6.12.7' '6.12.8' '6.13.1' '6.13.2' '6.13.3' '6.13.4' '6.13.5' '6.14.1' '6.14.2' '6.14.3' '6.14.4' '6.14.5' '6.14.6' '6.14.7' '6.14.8' '6.14.9' '6.14.10' '6.15.1' '6.16.1' '6.16.2' '6.16.3' '6.17.1' '6.17.2' '6.17.3' '6.17.4' '6.17.5' '6.18.1' '6.18.2' '6.18.3' '6.18.4' '6.18.5' '6.19.1' '6.19.2' '6.19.3' '6.19.4' '6.19.5' '6.20.1' '6.20.2' '6.20.3' '6.20.4' '6.20.5' '6.20.6' '6.20.7' '6.21.1' '6.21.2' '6.21.3' '6.21.4' '6.21.5' '6.21.6' '6.21.7' '6.21.8' '6.22.1' '6.22.2' '6.22.3' '6.22.4' '6.22.5' '6.22.6' '6.22.7' '6.22.8' '6.22.9' '6.22.10' '6.22.11' '6.22.12' '6.22.13' '6.22.14' '6.22.15' '6.22.16' '6.22.17' '6.22.18' '6.22.19' '6.22.20' '6.22.21' '6.22.22' '6.22.23' '6.22.24' '6.22.25' '6.22.26' '6.22.27' '6.22.28' '6.22.29' '6.22.30' '6.22.31' '6.22.32' '6.22.33' '6.22.34' '6.23.1' '6.23.2' '6.23.3' '6.23.4' '6.23.5' '6.23.6' '6.23.7' '7.1.1' '7.1.2' '7.1.3' '7.1.4' '7.1.5' '7.1.6' '7.3.1' '7.3.2' '7.3.3' '7.3.4' '7.3.5' '7.3.6' '7.5.1' '7.7.1' '7.7.2' '7.7.3' '7.7.4' '7.7.5' '7.7.6' '7.7.7' '7.7.8' '7.7.9' '7.7.10' '7.7.11' '7.7.12' '7.7.13' '7.9.1' '7.9.2' '7.9.3' '7.9.4' '7.9.5' '7.9.6' '7.9.7' '7.9.8' '7.9.9' '7.9.10' '7.9.11' '7.9.12' '7.9.13' '7.13.1' '7.13.2' '9.1.1' '9.1.2' '9.1.3' '9.1.4' '9.1.5' '9.1.6' '9.2.1' '9.2.2' '9.2.3' '9.2.4' '9.2.5' '9.2.6' '9.3.1' '9.3.2' '9.3.3' '9.3.4' '9.3.5' '9.3.6' '9.3.7' '9.3.8' '9.3.9' '9.4.1' '9.4.2' '9.4.3' '9.4.4' '9.4.5' '9.4.6' '9.4.7' '9.4.8' '9.4.9' '9.5.1' '9.5.2' '9.5.3' '9.5.4' '9.5.5' '9.5.6' '9.6.1' '9.6.2' '9.6.3' '9.6.4' '9.6.5' '9.6.6' '9.7.1' '9.7.2' '9.7.3' '9.7.4' '9.7.5' '9.7.6' '9.8.1' '9.8.2' '9.8.3' '9.8.4' '9.8.5' '9.8.6' '10.1.1' '12.1.1' '12.1.2' '12.1.3' '12.1.4' '12.1.5' '12.1.6' '12.1.7' '12.1.8' '12.1.9' '12.1.10' '12.1.11' '12.1.12' '12.1.13' '12.1.14' '12.1.15' '12.1.16' '12.1.17' '12.1.18' '12.2.1' '12.2.2' '12.2.3' '12.2.4' '12.2.5' '12.2.6' '12.2.7' '12.2.8' '12.2.9' '12.2.10' '12.2.11' '12.2.12' '12.2.13' '12.2.14' '12.2.15' '12.2.16' '12.2.17' '12.2.18' '12.3.1' '12.3.2' '12.3.3' '12.3.4' '12.3.5' '12.3.6' '12.3.7' '12.3.8' '12.3.9' '12.3.10' '12.3.11' '12.3.12' '12.3.13' '12.3.14' '12.3.15' '12.3.16' '12.3.17' '12.3.18' '12.4.1' '12.4.2' '12.4.3' '12.4.4' '12.4.5' '12.4.6' '12.4.7' '12.4.8' '12.4.9' '12.4.10' '12.4.11' '12.4.12' '12.4.13' '12.4.14' '12.4.15' '12.4.16' '12.4.17' '12.4.18' '12.5.1' '12.5.2' '12.5.3' '12.5.4' '12.5.5' '12.5.6' '12.5.7' '12.5.8' '12.5.9' '12.5.10' '12.5.11' '12.5.12' '12.5.13' '12.5.14' '12.5.15' '12.5.16' '12.5.17' '12.5.18' '13.1.1' '13.1.2' '13.1.3' '13.1.4' '13.1.5' '13.1.6' '13.1.7' '13.1.8' '13.1.9' '13.1.10' '13.1.11' '13.1.12' '13.1.13' '13.1.14' '13.1.15' '13.1.16' '13.1.17' '13.1.18' '13.2.1' '13.2.2' '13.2.3' '13.2.4' '13.2.5' '13.2.6' '13.2.7' '13.2.8' '13.2.9' '13.2.10' '13.2.11' '13.2.12' '13.2.13' '13.2.14' '13.2.15' '13.2.16' '13.2.17' '13.2.18' '13.3.1' '13.3.2' '13.3.3' '13.3.4' '13.3.5' '13.3.6' '13.3.7' '13.3.8' '13.3.9' '13.3.10' '13.3.11' '13.3.12' '13.3.13' '13.3.14' '13.3.15' '13.3.16' '13.3.17' '13.3.18' '13.4.1' '13.4.2' '13.4.3' '13.4.4' '13.4.5' '13.4.6' '13.4.7' '13.4.8' '13.4.9' '13.4.10' '13.4.11' '13.4.12' '13.4.13' '13.4.14' '13.4.15' '13.4.16' '13.4.17' '13.4.18' '13.5.1' '13.5.2' '13.5.3' '13.5.4' '13.5.5' '13.5.6' '13.5.7' '13.5.8' '13.5.9' '13.5.10' '13.5.11' '13.5.12' '13.5.13' '13.5.14' '13.5.15' '13.5.16' '13.5.17' '13.5.18' '13.6.1' '13.6.2' '13.6.3' '13.6.4' '13.6.5' '13.6.6' '13.6.7' '13.6.8' '13.6.9' '13.6.10' '13.6.11' '13.6.12' '13.6.13' '13.6.14' '13.6.15' '13.6.16' '13.6.17' '13.6.18' '13.7.1' '13.7.2' '13.7.3' '13.7.4' '13.7.5' '13.7.6' '13.7.7' '13.7.8' '13.7.9' '13.7.10' '13.7.11' '13.7.12' '13.7.13' '13.7.14' '13.7.15' '13.7.16' '13.7.17' '13.7.18' ; do
libwebsockets-test-echo --client 127.0.0.1 --port 9001 -u "/runCase?case=$N&agent=libwebsockets" -v -n 1 &
CLIE=bin/lws-minimal-ws-client-echo
SERV=bin/lws-minimal-ws-server-echo
C=99
while [ $C -gt 8 ] ; do
if [ $OS=SunOS ] ; then
C=`ps -ef | grep libwebsockets-test-echo | wc -l`
else
C=`ps fax | grep libwebsockets-test-echo | wc -l`
fi
if [ $C -gt 8 ] ; then
sleep 1s
fi
done
RESULT=0
which wstest 2>/dev/null
if [ $? -ne 0 ]; then
echo "wstest is not installed"
exit 8
fi
killall wstest 2>/dev/null
#
# 2.10 / 2.11: There is no requirement to handle multiple PING / PONG
# in flight in RFC6455. lws doesn't waste memory on it
# since it is useless.
#
# 12.4.* / 12.5.*: Autobahn has been broken for these tests since Aug 2017
# https://github.com/crossbario/autobahn-testsuite/issues/71
cat << EOF >fuzzingserver.json
{
"url": "ws://127.0.0.1:9001",
"outdir": "./reports/clients",
"cases": ["*"],
"exclude-cases": [ "2.10", "2.11", "12.4.*", "12.5.*"],
"exclude-agent-cases": {}
}
EOF
cat << EOF >fuzzingclient.json
{
"outdir": "./reports/servers",
"servers": [
{
"url": "ws://127.0.0.1:9001"
}
],
"cases": ["*"],
"exclude-cases": ["2.10", "2.11", "12.4.*", "12.5.*" ],
"exclude-agent-cases": {}
}
EOF
PYTHONHASHSEED=0 wstest -m fuzzingserver &
Q=$!
sleep 2s
ps -p $Q > /dev/null
if [ $? -ne 0 ] ; then
echo "Problem with autobahn wstest install"
exit 9
fi
# 1) lws-as-client tests first
for i in '1.1.1', '1.1.2', '1.1.3', '1.1.4', '1.1.5', '1.1.6', '1.1.7', '1.1.8', '1.2.1', '1.2.2', '1.2.3', '1.2.4', '1.2.5', '1.2.6', '1.2.7', '1.2.8', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '2.8', '2.9', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7', '4.1.1', '4.1.2', '4.1.3', '4.1.4', '4.1.5', '4.2.1', '4.2.2', '4.2.3', '4.2.4', '4.2.5', '5.1', '5.2', '5.3', '5.4', '5.5', '5.6', '5.7', '5.8', '5.9', '5.10', '5.11', '5.12', '5.13', '5.14', '5.15', '5.16', '5.17', '5.18', '5.19', '5.20', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.2.4', '6.3.1', '6.3.2', '6.4.1', '6.4.2', '6.4.3', '6.4.4', '6.5.1', '6.5.2', '6.5.3', '6.5.4', '6.5.5', '6.6.1', '6.6.2', '6.6.3', '6.6.4', '6.6.5', '6.6.6', '6.6.7', '6.6.8', '6.6.9', '6.6.10', '6.6.11', '6.7.1', '6.7.2', '6.7.3', '6.7.4', '6.8.1', '6.8.2', '6.9.1', '6.9.2', '6.9.3', '6.9.4', '6.10.1', '6.10.2', '6.10.3', '6.11.1', '6.11.2', '6.11.3', '6.11.4', '6.11.5', '6.12.1', '6.12.2', '6.12.3', '6.12.4', '6.12.5', '6.12.6', '6.12.7', '6.12.8', '6.13.1', '6.13.2', '6.13.3', '6.13.4', '6.13.5', '6.14.1', '6.14.2', '6.14.3', '6.14.4', '6.14.5', '6.14.6', '6.14.7', '6.14.8', '6.14.9', '6.14.10', '6.15.1', '6.16.1', '6.16.2', '6.16.3', '6.17.1', '6.17.2', '6.17.3', '6.17.4', '6.17.5', '6.18.1', '6.18.2', '6.18.3', '6.18.4', '6.18.5', '6.19.1', '6.19.2', '6.19.3', '6.19.4', '6.19.5', '6.20.1', '6.20.2', '6.20.3', '6.20.4', '6.20.5', '6.20.6', '6.20.7', '6.21.1', '6.21.2', '6.21.3', '6.21.4', '6.21.5', '6.21.6', '6.21.7', '6.21.8', '6.22.1', '6.22.2', '6.22.3', '6.22.4', '6.22.5', '6.22.6', '6.22.7', '6.22.8', '6.22.9', '6.22.10', '6.22.11', '6.22.12', '6.22.13', '6.22.14', '6.22.15', '6.22.16', '6.22.17', '6.22.18', '6.22.19', '6.22.20', '6.22.21', '6.22.22', '6.22.23', '6.22.24', '6.22.25', '6.22.26', '6.22.27', '6.22.28', '6.22.29', '6.22.30', '6.22.31', '6.22.32', '6.22.33', '6.22.34', '6.23.1', '6.23.2', '6.23.3', '6.23.4', '6.23.5', '6.23.6', '6.23.7', '7.1.1', '7.1.2', '7.1.3', '7.1.4', '7.1.5', '7.1.6', '7.3.1', '7.3.2', '7.3.3', '7.3.4', '7.3.5', '7.3.6', '7.5.1', '7.7.1', '7.7.2', '7.7.3', '7.7.4', '7.7.5', '7.7.6', '7.7.7', '7.7.8', '7.7.9', '7.7.10', '7.7.11', '7.7.12', '7.7.13', '7.9.1', '7.9.2', '7.9.3', '7.9.4', '7.9.5', '7.9.6', '7.9.7', '7.9.8', '7.9.9', '7.9.10', '7.9.11', '7.13.1', '7.13.2', '9.1.1', '9.1.2', '9.1.3', '9.1.4', '9.1.5', '9.1.6', '9.2.1', '9.2.2', '9.2.3', '9.2.4', '9.2.5', '9.2.6', '9.3.1', '9.3.2', '9.3.3', '9.3.4', '9.3.5', '9.3.6', '9.3.7', '9.3.8', '9.3.9', '9.4.1', '9.4.2', '9.4.3', '9.4.4', '9.4.5', '9.4.6', '9.4.7', '9.4.8', '9.4.9', '9.5.1', '9.5.2', '9.5.3', '9.5.4', '9.5.5', '9.5.6', '9.6.1', '9.6.2', '9.6.3', '9.6.4', '9.6.5', '9.6.6', '9.7.1', '9.7.2', '9.7.3', '9.7.4', '9.7.5', '9.7.6', '9.8.1', '9.8.2', '9.8.3', '9.8.4', '9.8.5', '9.8.6', '10.1.1', '12.1.1', '12.1.2', '12.1.3', '12.1.4', '12.1.5', '12.1.6', '12.1.7', '12.1.8', '12.1.9', '12.1.10', '12.1.11', '12.1.12', '12.1.13', '12.1.14', '12.1.15', '12.1.16', '12.1.17', '12.1.18', '12.2.1', '12.2.2', '12.2.3', '12.2.4', '12.2.5', '12.2.6', '12.2.7', '12.2.8', '12.2.9', '12.2.10', '12.2.11', '12.2.12', '12.2.13', '12.2.14', '12.2.15', '12.2.16', '12.2.17', '12.2.18', '12.3.1', '12.3.2', '12.3.3', '12.3.4', '12.3.5', '12.3.6', '12.3.7', '12.3.8', '12.3.9', '12.3.10', '12.3.11', '12.3.12', '12.3.13', '12.3.14', '12.3.15', '12.3.16', '12.3.17', '12.3.18', '13.1.1', '13.1.2', '13.1.3', '13.1.4', '13.1.5', '13.1.6', '13.1.7', '13.1.8', '13.1.9', '13.1.10', '13.1.11', '13.1.12', '13.1.13', '13.1.14', '13.1.15', '13.1.16', '13.1.17', '13.1.18', '13.2.1', '13.2.2', '13.2.3', '13.2.4', '13.2.5', '13.2.6', '13.2.7', '13.2.8', '13.2.9', '13.2.10', '13.2.11', '13.2.12', '13.2.13', '13.2.14', '13.2.15', '13.2.16', '13.2.17', '13.2.18', '13.3.1', '13.3.2', '13.3.3', '13.3.4', '13.3.5', '13.3.6', '13.3.7', '13.3.8', '13.3.9', '13.3.10', '13.3.11', '13.3.12', '13.3.13', '13.3.14', '13.3.15', '13.3.16', '13.3.17', '13.3.18', '13.4.1', '13.4.2', '13.4.3', '13.4.4', '13.4.5', '13.4.6', '13.4.7', '13.4.8', '13.4.9', '13.4.10', '13.4.11', '13.4.12', '13.4.13', '13.4.14', '13.4.15', '13.4.16', '13.4.17', '13.4.18', '13.5.1', '13.5.2', '13.5.3', '13.5.4', '13.5.5', '13.5.6', '13.5.7', '13.5.8', '13.5.9', '13.5.10', '13.5.11', '13.5.12', '13.5.13', '13.5.14', '13.5.15', '13.5.16', '13.5.17', '13.5.18', '13.6.1', '13.6.2', '13.6.3', '13.6.4', '13.6.5', '13.6.6', '13.6.7', '13.6.8', '13.6.9', '13.6.10', '13.6.11', '13.6.12', '13.6.13', '13.6.14', '13.6.15', '13.6.16', '13.6.17', '13.6.18', '13.7.1', '13.7.2', '13.7.3', '13.7.4', '13.7.5', '13.7.6', '13.7.7', '13.7.8', '13.7.9', '13.7.10', '13.7.11', '13.7.12', '13.7.13', '13.7.14', '13.7.15', '13.7.16', '13.7.17', '13.7.18'; do
# if [ $N -ge 360 -a $N -le 393 ] ; then
# echo "skipping broken autobahn tests (broken in autobahn) $i https://github.com/crossbario/autobahn-testsuite/issues/71"
# else
echo $N: $i
$CLIE -a 127.0.0.1 -p 9001 -u "/runCase?case=$N&agent=libwebsockets" -d3 &
C=99
while [ $C -gt $PARALLEL ] ; do
if [ $OS=SunOS ] ; then
C=`ps -ef | grep client-echo | wc -l`
else
C=`ps fax | grep client-echo | wc -l`
fi
if [ $C -gt $PARALLEL ] ; then
sleep 0.1s
fi
done
# fi
N=$(( $N + 1 ))
done
@ -27,15 +100,80 @@ echo "waiting for forks to complete..."
while [ 1 ] ; do
if [ $OS=SunOS ] ; then
n=`ps -ef | grep libwebsocket | grep -v grep | wc -l`
n=`ps -ef | grep client-echo | grep -v grep | wc -l`
else
n=`ps fax | grep libwebsocket | grep -v grep | wc -l`
n=`ps fax | grep client-echo | grep -v grep | wc -l`
fi
echo "$n forks running..."
if [ $n -eq 0 ] ; then
echo "Completed"
exit 0
break
fi
sleep 1s
sleep 2s
done
# generate the report in ./reports
#
$CLIE -a 127.0.0.1 -p 9001 -u "/updateReports?agent=libwebsockets" -o
sleep 2s
killall wstest
sleep 1s
# this squashes the results into single lines like
#
# "9.8.4": { "behavior": "OK", "behaviorClose": "OK", "duration": 1312, "remoteCloseCode": 1000, "reportfile": "libwebsockets_case_9_8_4.json"
cat reports/clients/index.json | tr '\n' '!' | sed "s|\},\!|\n|g" | tr '!' ' ' | tr -s ' ' > /tmp/ji
echo -n "AUTOBAHN SERVER / LWS CLIENT: Total tests: " `cat /tmp/ji | wc -l` " : "
R="`cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`"
if [ "$R" == "0" ] ; then
echo "All pass"
else
RESULT=1
echo -n "$R FAIL : "
cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
echo
fi
# 2) lws-as-server tests
$SERV -p 9001 -d7 &
wstest -m fuzzingclient
R=$?
echo "Autobahn client exit $R"
killall lws-minimal-ws-server-echo
sleep 1s
# repeat the client results
R=`cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`
echo -n "AUTOBAHN SERVER / LWS CLIENT: Total tests: " `cat /tmp/ji | wc -l` " : "
if [ "$R" == "0" ] ;then
echo "All pass"
else
RESULT=1
echo -n "$R FAIL : "
cat /tmp/ji | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
echo
fi
# and then the server results
cat reports/servers/index.json | tr '\n' '!' | sed "s|\},\!|\n|g" | tr '!' ' ' | tr -s ' ' > /tmp/jis
R=`cat /tmp/jis | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | wc -l`
echo -n "AUTOBAHN CLIENT / LWS SERVER: Total tests: " `cat /tmp/jis | wc -l` " : "
if [ "$R" == "0" ] ;then
echo "All pass"
else
RESULT=$(( $RESULT + 2 ))
echo -n "$R FAIL : "
cat /tmp/jis | grep -v '"behavior": "OK"' | grep -v '"behavior": "NON-STRICT"' | grep -v '"behavior": "INFORMATIONAL"' | cut -d\" -f2 | tr '\n' ','
echo
fi
echo $RESULT
exit $RESULT

View file

@ -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" ];

View file

@ -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

View file

@ -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;
}