Compare commits
117 commits
master
...
v1.7-stabl
Author | SHA1 | Date | |
---|---|---|---|
![]() |
da49e47daa | ||
![]() |
3b58cc4617 | ||
![]() |
249b2d049b | ||
![]() |
09d8720034 | ||
![]() |
72d78ed4f8 | ||
![]() |
31d2a2c6dd | ||
![]() |
386365d4bb | ||
![]() |
bfffba9e09 | ||
![]() |
fa7acb3b88 | ||
![]() |
821a97ac66 | ||
![]() |
98fd8e626f | ||
![]() |
0650cd0fe7 | ||
![]() |
8d8e9bec9b | ||
![]() |
6ed189965f | ||
![]() |
50b9259cb2 | ||
![]() |
1fc082eb7f | ||
![]() |
47671fe504 | ||
![]() |
a085a0ab38 | ||
![]() |
151aa809a6 | ||
![]() |
8d4c673d02 | ||
![]() |
499954a096 | ||
![]() |
e4ea29bd51 | ||
![]() |
6d19521b90 | ||
![]() |
60d656fb0f | ||
![]() |
5cd457626a | ||
![]() |
9a90ed20ac | ||
![]() |
d2feeee1d6 | ||
![]() |
f89e90f6a2 | ||
![]() |
29458b105d | ||
![]() |
381cbf2dc1 | ||
![]() |
c6e497ea48 | ||
![]() |
96487167dd | ||
![]() |
449e73433c | ||
![]() |
659fefe330 | ||
![]() |
12df0f0acd | ||
![]() |
461a9068f5 | ||
![]() |
edb92ee3c7 | ||
![]() |
c06ea5c7d7 | ||
![]() |
1458e7079c | ||
![]() |
82753d8db6 | ||
![]() |
3564e3d5e9 | ||
![]() |
101bd4272a | ||
![]() |
9ec343d795 | ||
![]() |
900204e3af | ||
![]() |
ec81fd460b | ||
![]() |
b1f80eeeb7 | ||
![]() |
934cc80d95 | ||
![]() |
8b83266301 | ||
![]() |
e3e89a7241 | ||
![]() |
8cd06ca315 | ||
![]() |
1ba878cdd5 | ||
![]() |
674609e69a | ||
![]() |
b45054ed39 | ||
![]() |
3d033984f5 | ||
![]() |
18c328a631 | ||
![]() |
7dbdb776d9 | ||
![]() |
5594735115 | ||
![]() |
a09b3cf569 | ||
![]() |
20f18e0b1e | ||
![]() |
934cc2a8ce | ||
![]() |
d36fa29ac4 | ||
![]() |
f192c01a13 | ||
![]() |
a4330f313a | ||
![]() |
ef1f035681 | ||
![]() |
ec11114182 | ||
![]() |
dc410bfb78 | ||
![]() |
3eb59c90bf | ||
![]() |
3816a05c4f | ||
![]() |
7c8ef84848 | ||
![]() |
980ca50c39 | ||
![]() |
ea3cf82769 | ||
![]() |
814a7d28c1 | ||
![]() |
6671327c8d | ||
![]() |
360c0a8ee4 | ||
![]() |
e841206653 | ||
![]() |
8df3ef15ab | ||
![]() |
5e091c62a8 | ||
![]() |
00aaa3aa12 | ||
![]() |
12369b0c91 | ||
![]() |
d19dec7922 | ||
![]() |
b55ea5eaed | ||
![]() |
d2a95e0c82 | ||
![]() |
314ca966a0 | ||
![]() |
34266b7861 | ||
![]() |
7bb600f0c7 | ||
![]() |
ee2f1ea667 | ||
![]() |
3daefb876a | ||
![]() |
b14f842891 | ||
![]() |
171d29695f | ||
![]() |
969212e1dd | ||
![]() |
b33c72c770 | ||
![]() |
4038721baf | ||
![]() |
9c6bd66859 | ||
![]() |
466defa72d | ||
![]() |
228ecc6e56 | ||
![]() |
00919de84e | ||
![]() |
a3f688c189 | ||
![]() |
1499ab6b33 | ||
![]() |
a3a0792012 | ||
![]() |
f2ee57e734 | ||
![]() |
2209ceb3f5 | ||
![]() |
daf7ca4fa9 | ||
![]() |
90a73cc45f | ||
![]() |
81c461f5de | ||
![]() |
78468e08b3 | ||
![]() |
df9be06522 | ||
![]() |
3310dd19b5 | ||
![]() |
1610223737 | ||
![]() |
4cdf00e04c | ||
![]() |
737049b065 | ||
![]() |
922f68c85e | ||
![]() |
48a967d4e7 | ||
![]() |
835ad120aa | ||
![]() |
59cb88f1fe | ||
![]() |
1b78f7946f | ||
![]() |
1c9c278f49 | ||
![]() |
7714f71e4e |
51 changed files with 2304 additions and 423 deletions
|
@ -20,7 +20,9 @@ language: c
|
|||
install:
|
||||
- ./travis_install.sh
|
||||
script:
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; 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 .; fi ; fi
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 2.8.9)
|
||||
|
||||
if(NOT DEFINED CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type")
|
||||
|
@ -10,11 +10,11 @@ set(PACKAGE "libwebsockets")
|
|||
set(CPACK_PACKAGE_NAME "${PACKAGE}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "7")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "9")
|
||||
set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
|
||||
set(CPACK_PACKAGE_VENDOR "andy@warmcat.com")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE} ${PACKAGE_VERSION}")
|
||||
set(SOVERSION "7")
|
||||
set(SOVERSION "7.1")
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
set(VERSION "${CPACK_PACKAGE_VERSION}")
|
||||
|
@ -33,12 +33,23 @@ find_package(Git)
|
|||
if(GIT_EXECUTABLE)
|
||||
execute_process(
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND "${GIT_EXECUTABLE}" log -n 1 --pretty=%h
|
||||
COMMAND "${GIT_EXECUTABLE}" describe
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(LWS_BUILD_HASH ${GIT_HASH})
|
||||
execute_process(
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND "whoami"
|
||||
OUTPUT_VARIABLE GIT_USER
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND "hostname"
|
||||
OUTPUT_VARIABLE GIT_HOST
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set(LWS_BUILD_HASH ${GIT_USER}@${GIT_HOST}-${GIT_HASH})
|
||||
message("Git commit hash: ${LWS_BUILD_HASH}")
|
||||
endif()
|
||||
|
||||
|
@ -76,6 +87,7 @@ option(LWS_IPV6 "Compile with support for ipv6" OFF)
|
|||
option(LWS_WITH_HTTP2 "Compile with support for http2" OFF)
|
||||
option(LWS_MBED3 "Platform is MBED3" OFF)
|
||||
option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
|
||||
option(LWS_STATIC_PIC "Build the static version of the library with position-independent code" OFF)
|
||||
|
||||
if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
|
||||
|
||||
|
@ -327,6 +339,9 @@ CHECK_FUNCTION_EXISTS(socket LWS_HAVE_SOCKET)
|
|||
CHECK_FUNCTION_EXISTS(strerror LWS_HAVE_STRERROR)
|
||||
CHECK_FUNCTION_EXISTS(vfork LWS_HAVE_VFORK)
|
||||
CHECK_FUNCTION_EXISTS(getifaddrs LWS_HAVE_GETIFADDRS)
|
||||
CHECK_FUNCTION_EXISTS(snprintf LWS_HAVE_SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(_snprintf LWS_HAVE__SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(_vsnprintf LWS_HAVE__VSNPRINTF)
|
||||
|
||||
if (NOT LWS_HAVE_GETIFADDRS)
|
||||
if (LWS_WITHOUT_BUILTIN_GETIFADDRS)
|
||||
|
@ -383,16 +398,6 @@ if (NOT LWS_HAVE_REALLOC)
|
|||
set(realloc rpl_realloc)
|
||||
endif()
|
||||
|
||||
# Generate the lws_config.h that includes all the public compilation settings.
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/lws_config.h.in"
|
||||
"${PROJECT_BINARY_DIR}/lws_config.h")
|
||||
|
||||
# Generate the lws_config.h that includes all the private compilation settings.
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/lws_config_private.h.in"
|
||||
"${PROJECT_BINARY_DIR}/lws_config_private.h")
|
||||
|
||||
if (MSVC)
|
||||
# Turn off stupid microsoft security warnings.
|
||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
|
||||
|
@ -539,6 +544,9 @@ source_group("Sources" FILES ${SOURCES})
|
|||
set(LWS_LIBRARIES)
|
||||
|
||||
if (LWS_WITH_STATIC)
|
||||
if (LWS_STATIC_PIC)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
add_library(websockets STATIC
|
||||
${HDR_PRIVATE}
|
||||
${HDR_PUBLIC}
|
||||
|
@ -681,6 +689,15 @@ if (LWS_WITH_SSL)
|
|||
include_directories("${OPENSSL_INCLUDE_DIRS}")
|
||||
list(APPEND LIB_LIST ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# older (0.98) Openssl lacks this
|
||||
set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS})
|
||||
check_include_file(openssl/ecdh.h LWS_HAVE_OPENSSL_ECDH_H)
|
||||
|
||||
if (LWS_SSL_SERVER_WITH_ECDH_CERT AND NOT LWS_HAVE_OPENSSL_ECDH_H)
|
||||
message(FATAL_ERROR "Missing openssl/ecdh.h, so cannot use LWS_SSL_SERVER_WITH_ECDH_CERT")
|
||||
endif()
|
||||
|
||||
endif(LWS_WITH_SSL)
|
||||
|
||||
if (LWS_WITH_LIBEV)
|
||||
|
@ -711,6 +728,18 @@ if (LWS_WITH_LIBUV)
|
|||
list(APPEND LIB_LIST ${LIBUV_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
# Generate the lws_config.h that includes all the public compilation settings.
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/lws_config.h.in"
|
||||
"${PROJECT_BINARY_DIR}/lws_config.h")
|
||||
|
||||
# Generate the lws_config.h that includes all the private compilation settings.
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/lws_config_private.h.in"
|
||||
"${PROJECT_BINARY_DIR}/lws_config_private.h")
|
||||
|
||||
|
||||
#
|
||||
# Platform specific libs.
|
||||
#
|
||||
|
@ -737,7 +766,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
#
|
||||
# Helper function for adding a test app.
|
||||
#
|
||||
macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4 S5)
|
||||
macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4 S5 S6)
|
||||
|
||||
set(TEST_SRCS ${MAIN_SRC})
|
||||
set(TEST_HDR)
|
||||
|
@ -757,6 +786,10 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
else()
|
||||
list(APPEND TEST_SRCS ${S5})
|
||||
endif()
|
||||
if ("${S6}" STREQUAL "")
|
||||
else()
|
||||
list(APPEND TEST_SRCS ${S6})
|
||||
endif()
|
||||
if (WIN32)
|
||||
list(APPEND TEST_SRCS
|
||||
${WIN32_HELPERS_PATH}/getopt.c
|
||||
|
@ -818,12 +851,14 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-status.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
if (UNIX)
|
||||
create_test_app(test-fuzxy "test-server/fuzxy.c"
|
||||
""
|
||||
""
|
||||
""
|
||||
""
|
||||
"")
|
||||
endif()
|
||||
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")))
|
||||
|
@ -832,24 +867,27 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-status.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
endif()
|
||||
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
AND LWS_WITH_LIBEV)
|
||||
create_test_app(test-server-libev
|
||||
"test-server/test-server-libev.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-status.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
endif()
|
||||
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||
AND LWS_WITH_LIBUV)
|
||||
create_test_app(test-server-libuv
|
||||
"test-server/test-server-libuv.c"
|
||||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-status.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -862,6 +900,7 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
"test-server/test-server-http.c"
|
||||
"test-server/test-server-dumb-increment.c"
|
||||
"test-server/test-server-mirror.c"
|
||||
"test-server/test-server-status.c"
|
||||
"test-server/test-server-echogen.c")
|
||||
# Set defines for this executable only.
|
||||
set_property(
|
||||
|
@ -967,27 +1006,27 @@ if (NOT LWS_WITHOUT_TESTAPPS)
|
|||
# test-client
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_CLIENT)
|
||||
create_test_app(test-client "test-server/test-client.c" "" "" "" "")
|
||||
create_test_app(test-client "test-server/test-client.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-fraggle
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_FRAGGLE)
|
||||
create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "" "")
|
||||
create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# test-ping
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_PING)
|
||||
create_test_app(test-ping "test-server/test-ping.c" "" "" "" "")
|
||||
create_test_app(test-ping "test-server/test-ping.c" "" "" "" "" "")
|
||||
endif()
|
||||
#
|
||||
# test-echo
|
||||
#
|
||||
if (NOT LWS_WITHOUT_TEST_ECHO)
|
||||
create_test_app(test-echo "test-server/test-echo.c" "" "" "" "")
|
||||
create_test_app(test-echo "test-server/test-echo.c" "" "" "" "" "")
|
||||
endif()
|
||||
|
||||
endif(NOT LWS_WITHOUT_CLIENT)
|
||||
|
@ -1139,6 +1178,10 @@ install(FILES
|
|||
"${PROJECT_BINARY_DIR}/LibwebsocketsConfigVersion.cmake"
|
||||
DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev)
|
||||
|
||||
# Install exports for the install-tree.
|
||||
install(EXPORT LibwebsocketsTargets
|
||||
DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev)
|
||||
|
||||
# build subdir is not part of sources
|
||||
set(CPACK_SOURCE_IGNORE_FILES $(CPACK_SOURCE_IGNORE_FILES) ".git" "build" "tgz" "tar.gz")
|
||||
|
||||
|
@ -1184,6 +1227,8 @@ message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}")
|
|||
message(" LWS_MBED3 = ${LWS_MBED3}")
|
||||
message(" LWS_SSL_SERVER_WITH_ECDH_CERT = ${LWS_SSL_SERVER_WITH_ECDH_CERT}")
|
||||
message(" LWS_MAX_SMP = ${LWS_MAX_SMP}")
|
||||
message(" LWS_HAVE_OPENSSL_ECDH_H = ${LWS_HAVE_OPENSSL_ECDH_H}")
|
||||
message(" LWS_STATIC_PIC = ${LWS_STATIC_PIC}")
|
||||
message("---------------------------------------------------------------------")
|
||||
|
||||
# These will be available to parent projects including libwebsockets using add_subdirectory()
|
||||
|
|
17
LICENSE
17
LICENSE
|
@ -1,7 +1,13 @@
|
|||
Libwebsockets and included programs are provided under the terms of the GNU
|
||||
Library General Public License (LGPL) 2.1, with the following exceptions:
|
||||
|
||||
1) Static linking of programs with the libwebsockets library does not
|
||||
1) Any reference, whether in these modifications or in the GNU
|
||||
Library General Public License 2.1, to this License, these terms, the
|
||||
GNU Lesser Public License, GNU Library General Public License, LGPL, or
|
||||
any similar reference shall refer to the GNU Library General Public
|
||||
License 2.1 as modified by these paragraphs 1) through 4).
|
||||
|
||||
2) Static linking of programs with the libwebsockets library does not
|
||||
constitute a derivative work and does not require the author to provide
|
||||
source code for the program, use the shared libwebsockets libraries, or
|
||||
link their program against a user-supplied version of libwebsockets.
|
||||
|
@ -10,7 +16,7 @@ If you link the program to a modified version of libwebsockets, then the
|
|||
changes to libwebsockets must be provided under the terms of the LGPL in
|
||||
sections 1, 2, and 4.
|
||||
|
||||
2) You do not have to provide a copy of the libwebsockets license with
|
||||
3) You do not have to provide a copy of the libwebsockets license with
|
||||
programs that are linked to the libwebsockets library, nor do you have to
|
||||
identify the libwebsockets license in your program or documentation as
|
||||
required by section 6 of the LGPL.
|
||||
|
@ -20,9 +26,9 @@ following example statement can be included in user documentation to
|
|||
satisfy this requirement:
|
||||
|
||||
"[program] is based in part on the work of the libwebsockets project
|
||||
(http://libwebsockets.org)"
|
||||
(https://libwebsockets.org)"
|
||||
|
||||
3) Some sources included have their own, more liberal licenses, or options
|
||||
4) Some sources included have their own, more liberal licenses, or options
|
||||
to get original sources with the liberal terms.
|
||||
|
||||
Original liberal license retained
|
||||
|
@ -41,7 +47,8 @@ Public Domain (CC-zero) to simplify reuse
|
|||
- test-server/*.c
|
||||
- test-server/*.h
|
||||
|
||||
|
||||
------ end of exceptions
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
|
|
|
@ -137,6 +137,15 @@ Building on Windows (Visual Studio)
|
|||
4. Now you should have a generated Visual Studio Solution in your
|
||||
`<path to src>/build` directory, which can be used to build.
|
||||
|
||||
5. Some additional deps may be needed
|
||||
|
||||
- iphlpapi.lib
|
||||
- psapi.lib
|
||||
- userenv.lib
|
||||
|
||||
6. If you're using libuv, you must make sure to compile libuv with the same multithread-dll / Mtd attributes as libwebsockets itself
|
||||
|
||||
|
||||
Building on Windows (MinGW)
|
||||
---------------------------
|
||||
1. Install MinGW: http://sourceforge.net/projects/mingw/files
|
||||
|
|
|
@ -326,7 +326,7 @@ cmake .. -DLWS_SSL_SERVER_WITH_ECDH_CERT=1
|
|||
|
||||
**and** the info->options flag
|
||||
|
||||
LWS_SERVER_OPTION_SSL_ECD
|
||||
LWS_SERVER_OPTION_SSL_ECDH
|
||||
|
||||
to build in support and select it at runtime.
|
||||
|
||||
|
|
13
README.md
13
README.md
|
@ -8,16 +8,21 @@ libwebsockets
|
|||
This is the libwebsockets C library for lightweight websocket clients and
|
||||
servers. For support, visit
|
||||
|
||||
http://libwebsockets.org
|
||||
https://libwebsockets.org
|
||||
https://github.com/warmcat/libwebsockets
|
||||
|
||||
and consider joining the project mailing list at
|
||||
|
||||
http://ml.libwebsockets.org/mailman/listinfo/libwebsockets
|
||||
https://libwebsockets.org/mailman/listinfo/libwebsockets
|
||||
|
||||
You can get the latest version of the library from git
|
||||
| News |
|
||||
------
|
||||
| We have updated https://libwebsockets.org, if you would like your project using lws featured in the image carousel, send an image, project URL and brief summary to andy@warmcat.com |
|
||||
|
||||
You can get the latest version of the library from git:
|
||||
|
||||
- http://git.libwebsockets.org
|
||||
- https://github.com/warmcat/libwebsockets
|
||||
- https://libwebsockets.org/git
|
||||
|
||||
for more information:
|
||||
|
||||
|
|
|
@ -78,6 +78,16 @@ same time as drawing random circles in the mirror protocol;
|
|||
if you connect to the test server using a browser at the
|
||||
same time you will be able to see the circles being drawn.
|
||||
|
||||
The test client supports SSL too, use
|
||||
|
||||
```bash
|
||||
$ libwebsockets-test-client localhost --ssl -s
|
||||
```
|
||||
|
||||
the -s tells it to accept the default selfsigned cert from the server,
|
||||
otherwise it will strictly fail the connection if there is no CA cert to
|
||||
validate the server's certificate.
|
||||
|
||||
|
||||
Testing simple echo
|
||||
-------------------
|
||||
|
|
|
@ -14,9 +14,10 @@ environment:
|
|||
- LWS_METHOD: nossl
|
||||
CMAKE_ARGS: -DLWS_WITH_SSL=OFF
|
||||
install:
|
||||
- appveyor DownloadFile https://slproweb.com/download/Win32OpenSSL-1_0_2f.exe
|
||||
- Win32OpenSSL-1_0_2f.exe /silent /verysilent /sp- /suppressmsgboxes
|
||||
- cinst -y nsis
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/Win32OpenSSL-1_0_2h.exe
|
||||
- Win32OpenSSL-1_0_2h.exe /silent /verysilent /sp- /suppressmsgboxes
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/nsis-3.0rc1-setup.exe
|
||||
- cmd /c start /wait nsis-3.0rc1-setup.exe /S /D=C:\nsis
|
||||
- SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;%PATH%
|
||||
build:
|
||||
|
||||
|
|
211
changelog
211
changelog
|
@ -1,6 +1,211 @@
|
|||
Changelog
|
||||
---------
|
||||
|
||||
v1.7.9
|
||||
======
|
||||
|
||||
NB: lws_snprintf() added, but only used in library and test apps
|
||||
SONAME bump to 7.1
|
||||
|
||||
Fixes:
|
||||
|
||||
1) Fix leak caused by undestroyed pthread mutex
|
||||
|
||||
2) Add CLIENT_CONNECTION_ERROR callbacks for more failure paths
|
||||
|
||||
3) Fix for client close to callback on one error path
|
||||
|
||||
4) lws_snprintf() needed in a couple of test apps (1.7.x lib doesn't use it
|
||||
for aggregated buffer truncation)
|
||||
|
||||
|
||||
v1.7.8
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes:
|
||||
|
||||
1) MINOR: user_space arg was mistakenly NULL on LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER
|
||||
|
||||
2) MINOR: treating recv() returning 0 as peer close destroyed throughput on ab
|
||||
tests, reverted
|
||||
|
||||
3) MINOR: %3d on URL part was always turned to _... this should only happen in
|
||||
?na%3dme=x part of the URL
|
||||
|
||||
4) MINOR: some malloc escaped check for NULL / OOM
|
||||
|
||||
|
||||
v1.7.7
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes:
|
||||
|
||||
1) MINOR: Android add needed include file
|
||||
|
||||
2) MINOR: Allow build on GCC < 3.4
|
||||
|
||||
3) MINOR: Fix client problems recovering cleanly from SSL negotiation failure
|
||||
|
||||
|
||||
v1.7.6
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) MINOR: libuv-specific fixes
|
||||
|
||||
2) MINOR: urldecode forbids malformed %xx in the library
|
||||
|
||||
3) MINOR: small fixes for Android and Windows
|
||||
|
||||
4) MINOR: handle 0 read() as closed connection
|
||||
|
||||
|
||||
v1.7.5
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) MINOR: fix build with musl C library
|
||||
|
||||
2) MINOR: libuv handle signals only if requested
|
||||
|
||||
3) MINOR: Fix compile warning if HTTP2 + RELEASE + ALPN-capable SSL
|
||||
|
||||
4) MINOR: produce and package
|
||||
%{_libdir}/cmake/libwebsockets/LibwebsocketsTargets.cmake
|
||||
|
||||
5) MAJOR: make permessage-deflate enforce protocol rx buffer size requirement
|
||||
|
||||
|
||||
v1.7.4
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) MINOR: don't send ext hdr if no exts to discuss
|
||||
|
||||
2) MINOR: libuv + libev small fixes
|
||||
|
||||
3) MINOR: some windows build environments have no snprintf
|
||||
|
||||
4) MINOR: cmake adapts better to ecdh.h cmake situation
|
||||
|
||||
5) MINOR: client missed WSI_CREATE callback
|
||||
|
||||
6) MINOR: base64 decode api worked fine for all ws key handling, however
|
||||
it was broken for some general decode if user code wanted to use it.
|
||||
|
||||
7) MINOR: add optimized parsing path for bulk incoming ws data
|
||||
|
||||
|
||||
v1.7.3
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) MAJOR connections on ah waiting list that closed did not get removed from
|
||||
the waiting list...
|
||||
|
||||
2) MAJOR since we added the ability to hold an ah across http keepalive
|
||||
transactions where more headers had already arrived, we broke the ability
|
||||
to tell if more headers had arrived. Result was if the browser didn't
|
||||
close the keepalive, we retained ah for the lifetime of the keepalive,
|
||||
using up the pool.
|
||||
|
||||
3) MAJOR windows-only-POLLHUP was not coming
|
||||
|
||||
4) Fix build on NetBSD
|
||||
|
||||
|
||||
v1.7.2
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) libuv one-per-session valgrind leak fixed
|
||||
|
||||
2) MINOR An error about hdr struct in _lws_ws_related is corrected, it's not
|
||||
known to affect anything added until after it was fixed
|
||||
|
||||
3) MINOR During the close shutdown wait state introduced at v1.7, if something
|
||||
requests callback on writeable for the socket it will busywait until the
|
||||
socket closes
|
||||
|
||||
4) MINOR update URLs in test html for libwebsockets.org https STS changes
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
1) test server html is updated with tabs and a new live server monitoring
|
||||
feature. Input sanitization added to the js.
|
||||
|
||||
|
||||
v1.7.1
|
||||
======
|
||||
|
||||
NB: No API change since v1.7.0
|
||||
|
||||
Fixes
|
||||
-----
|
||||
|
||||
1) MAJOR (Windows-only) fix assert firing
|
||||
|
||||
2) MAJOR http:/1.1 connections handled by lws_return_http_status() did not
|
||||
get sent a content-length resulting in the link hanging until the peer closed
|
||||
it. attack.sh updated to add a test for this.
|
||||
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
1) MINOR test-server gained some new switches
|
||||
|
||||
-C <file> use external SSL cert file
|
||||
-K <file> use external SSL key file
|
||||
-A <file> use external SSL CA cert file
|
||||
|
||||
-u <uid> set effective uid
|
||||
-g <gid> set effective gid
|
||||
|
||||
together you can use them like this to have the test-server work with the
|
||||
usual purchased SSL certs from an official CA.
|
||||
|
||||
--ssl -C your.crt -K your.key -A your.cer -u 99 -g 99
|
||||
|
||||
2) MINOR the OpenSSL magic to setup ECDH cipher usage is implemented in the
|
||||
library, and the ciphers restricted to use ECDH only.
|
||||
Using this, the lws test server can score an A at SSLLABS test
|
||||
|
||||
3) MINOR STS (SSL always) header is added to the test server if you use --ssl. With
|
||||
that, we score A+ at SSLLABS test
|
||||
|
||||
4) MINOR daemonize function (disabled at cmake by default) is updated to work
|
||||
with systemd
|
||||
|
||||
5) MINOR example systemd .service file now provided for test server
|
||||
(not installed by default)
|
||||
|
||||
|
||||
v1.7.0
|
||||
======
|
||||
|
||||
|
@ -164,11 +369,11 @@ cmake .. -DLWS_SSL_SERVER_WITH_ECDH_CERT=1
|
|||
|
||||
**and** the info->options flag
|
||||
|
||||
LWS_SERVER_OPTION_SSL_ECD
|
||||
LWS_SERVER_OPTION_SSL_ECDH
|
||||
|
||||
to build in support and select it at runtime.
|
||||
|
||||
6) There's a new api lws_parse_uri() that simplies chopping up
|
||||
6) There's a new api lws_parse_uri() that simplifies chopping up
|
||||
https://xxx:yyy/zzz uris into parts nicely. The test client now uses this
|
||||
to allow proper uris as well as the old address style.
|
||||
|
||||
|
@ -299,7 +504,7 @@ lws_ev_initloop (was lws_initloop) gets an extra argument for the
|
|||
thread service index (use 0 if you will just have 1 service thread).
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi);
|
||||
lws_ev_initloop(struct lws_context *context, ev_loop_t *loop, int tsi);
|
||||
|
||||
|
||||
v1.6.0-chrome48-firefox42
|
||||
|
|
|
@ -99,7 +99,7 @@ LWS_VISIBLE int
|
|||
lws_b64_decode_string(const char *in, char *out, int out_size)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
int i, c;
|
||||
int done = 0;
|
||||
unsigned char v;
|
||||
unsigned char quad[4];
|
||||
|
@ -110,19 +110,19 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
|
|||
for (i = 0; i < 4 && *in; i++) {
|
||||
|
||||
v = 0;
|
||||
c = 0;
|
||||
while (*in && !v) {
|
||||
|
||||
v = *in++;
|
||||
c = v = *in++;
|
||||
v = (v < 43 || v > 122) ? 0 : decode[v - 43];
|
||||
if (v)
|
||||
v = (v == '$') ? 0 : v - 61;
|
||||
if (*in) {
|
||||
len++;
|
||||
if (v)
|
||||
quad[i] = v - 1;
|
||||
} else
|
||||
quad[i] = 0;
|
||||
}
|
||||
if (c) {
|
||||
len++;
|
||||
if (v)
|
||||
quad[i] = v - 1;
|
||||
} else
|
||||
quad[i] = 0;
|
||||
}
|
||||
|
||||
if (out_size < (done + len - 1))
|
||||
|
@ -147,6 +147,7 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
|
|||
return done;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int
|
||||
lws_b64_selftest(void)
|
||||
{
|
||||
|
@ -183,3 +184,4 @@ lws_b64_selftest(void)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,11 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
lwsl_client("%s\n", __func__);
|
||||
|
||||
if (!wsi->u.hdr.ah) {
|
||||
lwsl_err("ah was NULL at cc2\n");
|
||||
goto oom4;
|
||||
}
|
||||
|
||||
/* proxy? */
|
||||
|
||||
if (context->http_proxy_port) {
|
||||
|
@ -171,7 +176,11 @@ lws_client_connect_2(struct lws *wsi)
|
|||
* past here, we can't simply free the structs as error
|
||||
* handling as oom4 does. We have to run the whole close flow.
|
||||
*/
|
||||
if (!wsi->protocol)
|
||||
wsi->protocol = &wsi->context->protocols[0];
|
||||
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE,
|
||||
wsi->user_space, NULL, 0);
|
||||
lws_set_timeout(wsi,
|
||||
PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
|
||||
AWAITING_TIMEOUT);
|
||||
|
@ -302,8 +311,14 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
oom4:
|
||||
/* we're closing, losing some rx is OK */
|
||||
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
||||
if (wsi->u.hdr.ah)
|
||||
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
||||
|
||||
lws_header_table_detach(wsi);
|
||||
|
||||
/* take care that we might be inserted in fds already */
|
||||
if (wsi->position_in_fds_table != -1)
|
||||
goto failed;
|
||||
lws_free(wsi);
|
||||
|
||||
return NULL;
|
||||
|
@ -394,6 +409,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
wsi->state = LWSS_CLIENT_UNCONNECTED;
|
||||
wsi->protocol = NULL;
|
||||
wsi->pending_timeout = NO_PENDING_TIMEOUT;
|
||||
wsi->position_in_fds_table = -1;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
wsi->use_ssl = i->ssl_connection;
|
||||
|
|
29
lib/client.c
Normal file → Executable file
29
lib/client.c
Normal file → Executable file
|
@ -264,7 +264,7 @@ some_wait:
|
|||
if (n != SSL_ERROR_NONE) {
|
||||
lwsl_err("SSL connect error %lu: %s\n",
|
||||
n, ERR_error_string(n, sb));
|
||||
return 0;
|
||||
goto bail3;
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
@ -318,7 +318,7 @@ some_wait:
|
|||
if (n != SSL_ERROR_NONE) {
|
||||
lwsl_err("SSL connect error %lu: %s\n",
|
||||
n, ERR_error_string(n, sb));
|
||||
return 0;
|
||||
goto bail3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ some_wait:
|
|||
n, ERR_error_string(n, sb));
|
||||
lws_close_free_wsi(wsi,
|
||||
LWS_CLOSE_STATUS_NOSTATUS);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* USE_WOLFSSL */
|
||||
|
@ -462,6 +462,10 @@ some_wait:
|
|||
|
||||
bail3:
|
||||
lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n");
|
||||
wsi->context->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
return -1;
|
||||
|
||||
|
@ -640,7 +644,7 @@ check_extensions:
|
|||
/* instantiate the accepted extensions */
|
||||
|
||||
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
|
||||
lwsl_ext("no client extenstions allowed by server\n");
|
||||
lwsl_ext("no client extensions allowed by server\n");
|
||||
goto check_accept;
|
||||
}
|
||||
|
||||
|
@ -707,10 +711,14 @@ check_extensions:
|
|||
|
||||
/* allow him to construct his ext instance */
|
||||
|
||||
ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
if (ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CB_CLIENT_CONSTRUCT,
|
||||
(void *)&wsi->act_ext_user[wsi->count_act_ext],
|
||||
(void *)&opts, 0);
|
||||
(void *)&opts, 0)) {
|
||||
lwsl_notice(" ext %s failed construction\n", ext_name);
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* allow the user code to override ext defaults if it
|
||||
|
@ -956,7 +964,6 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
|
||||
/* tell the server what extensions we could support */
|
||||
|
||||
p += sprintf(p, "Sec-WebSocket-Extensions: ");
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
ext = context->extensions;
|
||||
while (ext && ext->callback) {
|
||||
|
@ -988,13 +995,17 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
|
||||
if (ext_count)
|
||||
*p++ = ',';
|
||||
else
|
||||
p += sprintf(p, "Sec-WebSocket-Extensions: ");
|
||||
|
||||
p += sprintf(p, "%s", ext->client_offer);
|
||||
ext_count++;
|
||||
|
||||
ext++;
|
||||
}
|
||||
if (ext_count)
|
||||
p += sprintf(p, "\x0d\x0a");
|
||||
#endif
|
||||
p += sprintf(p, "\x0d\x0a");
|
||||
|
||||
if (wsi->ietf_spec_revision)
|
||||
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
|
||||
|
@ -1003,7 +1014,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
|
|||
/* give userland a chance to append, eg, cookies */
|
||||
|
||||
context->protocols[0].callback(wsi, LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
|
||||
NULL, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12);
|
||||
wsi->user_space, &p, (pkt + LWS_MAX_SOCKET_IO_BUF) - p - 12);
|
||||
|
||||
p += sprintf(p, "\x0d\x0a");
|
||||
|
||||
|
|
|
@ -79,6 +79,10 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
#endif
|
||||
char *p;
|
||||
int n, m;
|
||||
#if defined(__ANDROID__)
|
||||
struct rlimit rt;
|
||||
#endif
|
||||
|
||||
|
||||
lwsl_notice("Initial logging level %d\n", log_level);
|
||||
lwsl_notice("Libwebsockets version: %s\n", library_version);
|
||||
|
@ -115,7 +119,16 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
|
||||
}
|
||||
#endif
|
||||
context->max_fds = getdtablesize();
|
||||
#if defined(__ANDROID__)
|
||||
n = getrlimit ( RLIMIT_NOFILE,&rt);
|
||||
if (-1 == n) {
|
||||
lwsl_err("Get RLIMIT_NOFILE failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
context->max_fds = rt.rlim_cur;
|
||||
#else
|
||||
context->max_fds = getdtablesize();
|
||||
#endif
|
||||
|
||||
if (info->count_threads)
|
||||
context->count_threads = info->count_threads;
|
||||
|
@ -162,6 +175,8 @@ lws_create_context(struct lws_context_creation_info *info)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
context->pt[n].context = context;
|
||||
context->pt[n].tid = n;
|
||||
context->pt[n].http_header_data = lws_malloc(context->max_http_header_data *
|
||||
context->max_http_header_pool);
|
||||
if (!context->pt[n].http_header_data)
|
||||
|
@ -372,6 +387,7 @@ lws_context_destroy(struct lws_context *context)
|
|||
/* no protocol close */);
|
||||
n--;
|
||||
}
|
||||
lws_pt_mutex_destroy(pt);
|
||||
}
|
||||
/*
|
||||
* give all extensions a chance to clean up any per-context
|
||||
|
|
|
@ -41,7 +41,7 @@ child_handler(int signum)
|
|||
switch (signum) {
|
||||
|
||||
case SIGALRM: /* timed out daemonizing */
|
||||
exit(1);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case SIGUSR1: /* positive confirmation we daemonized well */
|
||||
|
@ -52,7 +52,7 @@ child_handler(int signum)
|
|||
fprintf(stderr,
|
||||
"unable to create lock file %s, code=%d (%s)\n",
|
||||
lock_path, errno, strerror(errno));
|
||||
exit(1);
|
||||
exit(5);
|
||||
}
|
||||
len = sprintf(sz, "%u", pid_daemon);
|
||||
sent = write(fd, sz, len);
|
||||
|
@ -62,10 +62,11 @@ child_handler(int signum)
|
|||
lock_path, errno, strerror(errno));
|
||||
|
||||
close(fd);
|
||||
exit(!!(sent == len));
|
||||
exit(0);
|
||||
//!!(sent == len));
|
||||
|
||||
case SIGCHLD: /* daemonization failed */
|
||||
exit(1);
|
||||
exit(6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -98,8 +99,8 @@ lws_daemonize(const char *_lock_path)
|
|||
char buf[10];
|
||||
|
||||
/* already a daemon */
|
||||
if (getppid() == 1)
|
||||
return 1;
|
||||
// if (getppid() == 1)
|
||||
// return 1;
|
||||
|
||||
fd = open(_lock_path, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
|
@ -138,7 +139,7 @@ lws_daemonize(const char *_lock_path)
|
|||
if (pid_daemon < 0) {
|
||||
fprintf(stderr, "unable to fork daemon, code=%d (%s)",
|
||||
errno, strerror(errno));
|
||||
exit(1);
|
||||
exit(9);
|
||||
}
|
||||
|
||||
/* If we got a good PID, then we can exit the parent process. */
|
||||
|
@ -153,7 +154,7 @@ lws_daemonize(const char *_lock_path)
|
|||
|
||||
pause();
|
||||
/* should not be reachable */
|
||||
exit(1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* At this point we are executing as the child process */
|
||||
|
@ -176,7 +177,7 @@ lws_daemonize(const char *_lock_path)
|
|||
fprintf(stderr,
|
||||
"unable to create a new session, code %d (%s)",
|
||||
errno, strerror(errno));
|
||||
exit(1);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -187,7 +188,7 @@ lws_daemonize(const char *_lock_path)
|
|||
fprintf(stderr,
|
||||
"unable to change directory to %s, code %d (%s)",
|
||||
"/", errno, strerror(errno));
|
||||
exit(1);
|
||||
exit(3);
|
||||
}
|
||||
|
||||
/* Redirect standard files to /dev/null */
|
||||
|
|
|
@ -75,6 +75,17 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
|
||||
case LWS_EXT_CB_CLIENT_CONSTRUCT:
|
||||
case LWS_EXT_CB_CONSTRUCT:
|
||||
|
||||
n = LWS_MAX_SOCKET_IO_BUF;
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
n = wsi->protocol->rx_buffer_size;
|
||||
|
||||
if (n < 128) {
|
||||
lwsl_err(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n",
|
||||
wsi->protocol->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* fill in **user */
|
||||
priv = lws_zalloc(sizeof(*priv));
|
||||
*((void **)user) = priv;
|
||||
|
@ -102,6 +113,23 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
|
||||
priv->args[PMD_COMP_LEVEL] = 1;
|
||||
priv->args[PMD_MEM_LEVEL] = 8;
|
||||
|
||||
/* cap the RX buf at the nearest power of 2 to protocol rx buf */
|
||||
|
||||
n = LWS_MAX_SOCKET_IO_BUF;
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
n = wsi->protocol->rx_buffer_size;
|
||||
|
||||
extra = 7;
|
||||
while (n >= 1 << (extra + 1))
|
||||
extra++;
|
||||
|
||||
if (extra < priv->args[PMD_RX_BUF_PWR2]) {
|
||||
priv->args[PMD_RX_BUF_PWR2] = extra;
|
||||
lwsl_err(" Capping pmd rx to %d\n", 1 << extra);
|
||||
}
|
||||
lwsl_err(" ok\n");
|
||||
|
||||
break;
|
||||
|
||||
case LWS_EXT_CB_DESTROY:
|
||||
|
@ -197,7 +225,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
* If we did not already send in the 00 00 FF FF, and he's
|
||||
* out of input, he did not EXACTLY fill the output buffer
|
||||
* (which is ambiguous and we will force it to go around
|
||||
* again by witholding a byte), and he's otherwise working on
|
||||
* again by withholding a byte), and he's otherwise working on
|
||||
* being a FIN fragment, then do the FIN message processing
|
||||
* of faking up the 00 00 FF FF that the sender stripped.
|
||||
*/
|
||||
|
@ -232,7 +260,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
|
|||
* more in the pipeline.
|
||||
*
|
||||
* So to work around that safely, if it used all output space
|
||||
* exactly, we ALWAYS say there is more coming and we withold
|
||||
* exactly, we ALWAYS say there is more coming and we withhold
|
||||
* the last byte of the buffer to guarantee that is true.
|
||||
*
|
||||
* That still leaves us at least one byte to finish with a FIN
|
||||
|
|
21
lib/header.c
21
lib/header.c
|
@ -173,18 +173,23 @@ lws_add_http_header_status(struct lws *wsi, unsigned int code,
|
|||
* consistently
|
||||
*/
|
||||
LWS_VISIBLE int
|
||||
lws_return_http_status(struct lws *wsi, unsigned int code, const char *html_body)
|
||||
lws_return_http_status(struct lws *wsi, unsigned int code,
|
||||
const char *html_body)
|
||||
{
|
||||
int n, m;
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
unsigned char *p = pt->serv_buf + LWS_PRE;
|
||||
unsigned char *start = p;
|
||||
unsigned char *start = p, *body = p + 512;
|
||||
unsigned char *end = p + LWS_MAX_SOCKET_IO_BUF - LWS_PRE;
|
||||
int n, m, len;
|
||||
char slen[20];
|
||||
|
||||
if (!html_body)
|
||||
html_body = "";
|
||||
|
||||
len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
|
||||
if (lws_add_http_header_status(wsi, code, &p, end))
|
||||
return 1;
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
|
||||
|
@ -195,6 +200,12 @@ lws_return_http_status(struct lws *wsi, unsigned int code, const char *html_body
|
|||
(unsigned char *)"text/html", 9,
|
||||
&p, end))
|
||||
return 1;
|
||||
n = sprintf(slen, "%d", len);
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
|
||||
(unsigned char *)slen, n,
|
||||
&p, end))
|
||||
return 1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return 1;
|
||||
|
||||
|
@ -202,9 +213,7 @@ lws_return_http_status(struct lws *wsi, unsigned int code, const char *html_body
|
|||
if (m != (int)(p - start))
|
||||
return 1;
|
||||
|
||||
n = sprintf((char *)start, "<html><body><h1>%u</h1>%s</body></html>",
|
||||
code, html_body);
|
||||
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
|
||||
m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
|
||||
|
||||
return m != n;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,8 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
|
|||
|
||||
if (!loop)
|
||||
loop = ev_loop_new(0);
|
||||
else
|
||||
context->pt[tsi].ev_loop_foreign = 1;
|
||||
|
||||
context->pt[tsi].io_loop_ev = loop;
|
||||
|
||||
|
@ -138,6 +140,9 @@ lws_libev_destroyloop(struct lws_context *context, int tsi)
|
|||
if (!(context->options & LWS_SERVER_OPTION_LIBEV))
|
||||
return;
|
||||
|
||||
if (!pt->io_loop_ev)
|
||||
return;
|
||||
|
||||
ev_io_stop(pt->io_loop_ev, &pt->w_accept.ev_watcher);
|
||||
if (context->use_ev_sigint)
|
||||
ev_signal_stop(pt->io_loop_ev,
|
||||
|
|
159
lib/libuv.c
159
lib/libuv.c
|
@ -31,34 +31,76 @@ lws_feature_status_libuv(struct lws_context_creation_info *info)
|
|||
}
|
||||
|
||||
static void
|
||||
lws_accept_cb(uv_poll_t *watcher, int status, int revents)
|
||||
lws_uv_idle(uv_idle_t *handle)
|
||||
{
|
||||
struct lws_context_per_thread *pt = container_of(handle,
|
||||
struct lws_context_per_thread, uv_idle);
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
|
||||
/*
|
||||
* is there anybody with pending stuff that needs service forcing?
|
||||
*/
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
|
||||
/* -1 timeout means just do forced service */
|
||||
lws_plat_service_tsi(pt->context, -1, pt->tid);
|
||||
/* still somebody left who wants forced service? */
|
||||
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
|
||||
/* yes... come back again later */
|
||||
return;
|
||||
}
|
||||
|
||||
/* there is nobody who needs service forcing, shut down idle */
|
||||
uv_idle_stop(handle);
|
||||
}
|
||||
|
||||
static void
|
||||
lws_io_cb(uv_poll_t *watcher, int status, int revents)
|
||||
{
|
||||
struct lws_io_watcher *lws_io = container_of(watcher,
|
||||
struct lws_io_watcher, uv_watcher);
|
||||
struct lws *wsi = container_of(lws_io, struct lws, w_read);
|
||||
struct lws_context *context = lws_io->context;
|
||||
struct lws_pollfd eventfd;
|
||||
|
||||
if (status < 0)
|
||||
return;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
eventfd.fd = watcher->socket;
|
||||
#else
|
||||
eventfd.fd = watcher->io_watcher.fd;
|
||||
#endif
|
||||
eventfd.events = 0;
|
||||
eventfd.revents = 0;//EV_NONE;
|
||||
if (revents & UV_READABLE) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & UV_WRITABLE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
eventfd.revents = 0;
|
||||
|
||||
if (status < 0) {
|
||||
/* at this point status will be an UV error, like UV_EBADF,
|
||||
we treat all errors as LWS_POLLHUP */
|
||||
|
||||
/* you might want to return; instead of servicing the fd in some cases */
|
||||
if (status == UV_EAGAIN)
|
||||
return;
|
||||
|
||||
eventfd.events |= LWS_POLLHUP;
|
||||
eventfd.revents |= LWS_POLLHUP;
|
||||
} else {
|
||||
if (revents & UV_READABLE) {
|
||||
eventfd.events |= LWS_POLLIN;
|
||||
eventfd.revents |= LWS_POLLIN;
|
||||
}
|
||||
if (revents & UV_WRITABLE) {
|
||||
eventfd.events |= LWS_POLLOUT;
|
||||
eventfd.revents |= LWS_POLLOUT;
|
||||
}
|
||||
}
|
||||
lws_service_fd(context, &eventfd);
|
||||
|
||||
uv_idle_start(&context->pt[(int)wsi->tsi].uv_idle, lws_uv_idle);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents)
|
||||
lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int signum)
|
||||
{
|
||||
//ev_break(loop, EVBREAK_ALL);
|
||||
lwsl_info("internal signal handler caught signal %d\n", signum);
|
||||
lws_libuv_stop(watcher->data);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
|
@ -74,8 +116,31 @@ lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_uv_timeout_cb(uv_timer_t *timer)
|
||||
{
|
||||
struct lws_context_per_thread *pt = container_of(timer,
|
||||
struct lws_context_per_thread, uv_timeout_watcher);
|
||||
|
||||
lwsl_debug("%s\n", __func__);
|
||||
lws_service_fd_tsi(pt->context, NULL, pt->tid);
|
||||
}
|
||||
|
||||
static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE };
|
||||
|
||||
struct lws_uv_sigint_ctx {
|
||||
struct lws_context *context;
|
||||
lws_uv_signal_cb_t *cb;
|
||||
};
|
||||
|
||||
static void lws_uv_sigint_cb_wrapper(uv_signal_t *signal, int signum)
|
||||
{
|
||||
struct lws_uv_sigint_ctx *p = signal->data;
|
||||
uv_signal_t signal_fwd = *signal;
|
||||
signal_fwd.data = p->context;
|
||||
p->cb(signal->loop, &signal_fwd, signum);
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, uv_signal_cb cb,
|
||||
int tsi)
|
||||
|
@ -86,17 +151,28 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, uv_signal_cb cb,
|
|||
|
||||
if (!loop) {
|
||||
loop = lws_malloc(sizeof(*loop));
|
||||
if (!loop) {
|
||||
lwsl_err("OOM\n");
|
||||
return -1;
|
||||
}
|
||||
uv_loop_init(loop);
|
||||
pt->ev_loop_foreign = 0;
|
||||
} else
|
||||
pt->ev_loop_foreign = 1;
|
||||
|
||||
pt->io_loop_uv = loop;
|
||||
uv_idle_init(loop, &pt->uv_idle);
|
||||
|
||||
assert(ARRAY_SIZE(sigs) <= ARRAY_SIZE(pt->signals));
|
||||
for (n = 0; n < ARRAY_SIZE(sigs); n++) {
|
||||
uv_signal_init(loop, &pt->signals[n]);
|
||||
uv_signal_start(&pt->signals[n], cb, sigs[n]);
|
||||
if (pt->context->use_ev_sigint) {
|
||||
assert(ARRAY_SIZE(sigs) <= ARRAY_SIZE(pt->signals));
|
||||
for (n = 0; n < ARRAY_SIZE(sigs); n++) {
|
||||
struct lws_uv_sigint_ctx *wrap_ctx = lws_malloc(sizeof(*wrap_ctx));
|
||||
wrap_ctx->context = pt->context;
|
||||
wrap_ctx->cb = context->lws_uv_sigint_cb;
|
||||
uv_signal_init(loop, &pt->signals[n]);
|
||||
pt->signals[n].data = wrap_ctx;
|
||||
uv_signal_start(&pt->signals[n], lws_uv_sigint_cb_wrapper, sigs[n]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -106,15 +182,31 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, uv_signal_cb cb,
|
|||
* We have to do it here because the uv loop(s) are not
|
||||
* initialized until after context creation.
|
||||
*/
|
||||
|
||||
if (wsi) {
|
||||
wsi->w_read.context = context;
|
||||
uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher, pt->lserv_fd);
|
||||
uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE, lws_accept_cb);
|
||||
uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher,
|
||||
pt->lserv_fd);
|
||||
uv_poll_start(&wsi->w_read.uv_watcher, UV_READABLE,
|
||||
lws_io_cb);
|
||||
}
|
||||
|
||||
uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
|
||||
uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb, 1000, 1000);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void lws_uv_close_cb(uv_handle_t *handle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void lws_uv_walk_cb(uv_handle_t *handle, void *arg)
|
||||
{
|
||||
uv_close(handle, lws_uv_close_cb);
|
||||
}
|
||||
|
||||
void
|
||||
lws_libuv_destroyloop(struct lws_context *context, int tsi)
|
||||
{
|
||||
|
@ -124,13 +216,22 @@ lws_libuv_destroyloop(struct lws_context *context, int tsi)
|
|||
if (!(context->options & LWS_SERVER_OPTION_LIBUV))
|
||||
return;
|
||||
|
||||
if (!pt->io_loop_uv)
|
||||
return;
|
||||
|
||||
if (context->use_ev_sigint)
|
||||
uv_signal_stop(&pt->w_sigint.uv_watcher);
|
||||
for (m = 0; m < ARRAY_SIZE(sigs); m++)
|
||||
for (m = 0; m < ARRAY_SIZE(sigs); m++) {
|
||||
uv_signal_stop(&pt->signals[m]);
|
||||
lws_free(pt->signals[m].data);
|
||||
}
|
||||
if (!pt->ev_loop_foreign) {
|
||||
uv_stop(pt->io_loop_uv);
|
||||
uv_walk(pt->io_loop_uv, lws_uv_walk_cb, NULL);
|
||||
while (uv_run(pt->io_loop_uv, UV_RUN_NOWAIT));
|
||||
m = uv_loop_close(pt->io_loop_uv);
|
||||
lwsl_debug("%s: uv_loop_close: %d\n", __func__, m);
|
||||
if (m == UV_EBUSY)
|
||||
lwsl_debug("%s: uv_loop_close: UV_EBUSY\n", __func__);
|
||||
lws_free(pt->io_loop_uv);
|
||||
}
|
||||
}
|
||||
|
@ -156,8 +257,13 @@ lws_libuv_io(struct lws *wsi, int flags)
|
|||
{
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
int current_events = wsi->w_read.uv_watcher.events &
|
||||
(UV_READABLE | UV_WRITABLE);
|
||||
#else
|
||||
int current_events = wsi->w_read.uv_watcher.io_watcher.pevents &
|
||||
(UV_READABLE | UV_WRITABLE);
|
||||
#endif
|
||||
struct lws_io_watcher *w = &wsi->w_read;
|
||||
|
||||
if (!LWS_LIBUV_ENABLED(context))
|
||||
|
@ -180,7 +286,7 @@ lws_libuv_io(struct lws *wsi, int flags)
|
|||
if (flags & LWS_EV_READ)
|
||||
current_events |= UV_READABLE;
|
||||
|
||||
uv_poll_start(&w->uv_watcher, current_events, lws_accept_cb);
|
||||
uv_poll_start(&w->uv_watcher, current_events, lws_io_cb);
|
||||
} else {
|
||||
if (flags & LWS_EV_WRITE)
|
||||
current_events &= ~UV_WRITABLE;
|
||||
|
@ -192,7 +298,7 @@ lws_libuv_io(struct lws *wsi, int flags)
|
|||
uv_poll_stop(&w->uv_watcher);
|
||||
else
|
||||
uv_poll_start(&w->uv_watcher, current_events,
|
||||
lws_accept_cb);
|
||||
lws_io_cb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,6 +332,7 @@ lws_libuv_kill(const struct lws_context *context)
|
|||
for (n = 0; n < context->count_threads; n++)
|
||||
if (context->pt[n].io_loop_uv && LWS_LIBUV_ENABLED(context))
|
||||
uv_stop(context->pt[n].io_loop_uv);
|
||||
// TODO uv_stop check foreign loop? or not?
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -277,8 +384,8 @@ lws_uv_getloop(struct lws_context *context, int tsi)
|
|||
static void
|
||||
lws_libuv_closewsi(uv_handle_t* handle)
|
||||
{
|
||||
struct lws *n = NULL, *wsi = (struct lws *)(((void *)handle) -
|
||||
(void *)(&n->w_read.uv_watcher));
|
||||
struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) -
|
||||
(char *)(&n->w_read.uv_watcher));
|
||||
struct lws_context *context = lws_get_context(wsi);
|
||||
|
||||
lws_close_free_wsi_final(wsi);
|
||||
|
|
109
lib/libwebsockets.c
Normal file → Executable file
109
lib/libwebsockets.c
Normal file → Executable file
|
@ -21,6 +21,10 @@
|
|||
|
||||
#include "private-libwebsockets.h"
|
||||
|
||||
#ifdef LWS_HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr;
|
||||
|
||||
|
@ -53,11 +57,12 @@ lws_free_wsi(struct lws *wsi)
|
|||
lws_free_set_NULL(wsi->rxflow_buffer);
|
||||
lws_free_set_NULL(wsi->trunc_alloc);
|
||||
|
||||
if (wsi->u.hdr.ah) {
|
||||
if (wsi->u.hdr.ah)
|
||||
/* we're closing, losing some rx is OK */
|
||||
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
||||
lws_header_table_detach(wsi);
|
||||
}
|
||||
|
||||
/* we may not have an ah, but may be on the waiting list... */
|
||||
lws_header_table_detach(wsi);
|
||||
|
||||
wsi->context->count_wsi_allocated--;
|
||||
lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
|
||||
|
@ -71,14 +76,17 @@ lws_remove_from_timeout_list(struct lws *wsi)
|
|||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
|
||||
if (!wsi->timeout_list_prev)
|
||||
if (!wsi->timeout_list_prev) /* ie, not part of the list */
|
||||
return;
|
||||
|
||||
lws_pt_lock(pt);
|
||||
/* if we have a next guy, set his prev to our prev */
|
||||
if (wsi->timeout_list)
|
||||
wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
|
||||
/* set our prev guy to our next guy instead of us */
|
||||
*wsi->timeout_list_prev = wsi->timeout_list;
|
||||
|
||||
/* we're out of the list, we should not point anywhere any more */
|
||||
wsi->timeout_list_prev = NULL;
|
||||
wsi->timeout_list = NULL;
|
||||
lws_pt_unlock(pt);
|
||||
|
@ -104,11 +112,15 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
|
|||
|
||||
time(&now);
|
||||
|
||||
if (!wsi->pending_timeout && reason) {
|
||||
if (reason && !wsi->timeout_list_prev) {
|
||||
/* our next guy is current first guy */
|
||||
wsi->timeout_list = pt->timeout_list;
|
||||
/* if there is a next guy, set his prev ptr to our next ptr */
|
||||
if (wsi->timeout_list)
|
||||
wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
|
||||
/* our prev ptr is first ptr */
|
||||
wsi->timeout_list_prev = &pt->timeout_list;
|
||||
/* set the first guy to be us */
|
||||
*wsi->timeout_list_prev = wsi;
|
||||
}
|
||||
|
||||
|
@ -252,7 +264,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
|
|||
if (!wsi->u.ws.close_in_ping_buffer_len) {
|
||||
wsi->u.ws.close_in_ping_buffer_len = 2;
|
||||
wsi->u.ws.ping_payload_buf[LWS_PRE] =
|
||||
(reason >> 16) & 0xff;
|
||||
(reason >> 8) & 0xff;
|
||||
wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
|
||||
reason & 0xff;
|
||||
}
|
||||
|
@ -299,11 +311,20 @@ just_kill_connection:
|
|||
n = shutdown(wsi->sock, SHUT_WR);
|
||||
if (n)
|
||||
lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
|
||||
wsi->state = LWSS_SHUTDOWN;
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
|
||||
context->timeout_secs);
|
||||
return;
|
||||
|
||||
// This causes problems with disconnection when the events are half closing connection
|
||||
// FD_READ | FD_CLOSE (33)
|
||||
#ifndef _WIN32_WCE
|
||||
/* libuv: no event available to guarantee completion */
|
||||
if (!LWS_LIBUV_ENABLED(context)) {
|
||||
|
||||
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
|
||||
wsi->state = LWSS_SHUTDOWN;
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
|
||||
context->timeout_secs);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -372,7 +393,9 @@ just_kill_connection:
|
|||
((wsi->state_pre_close == LWSS_ESTABLISHED) ||
|
||||
(wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
|
||||
(wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
|
||||
(wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE))) {
|
||||
(wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
|
||||
(wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
|
||||
(wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
|
||||
lwsl_debug("calling back CLOSED\n");
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
|
||||
wsi->user_space, NULL, 0);
|
||||
|
@ -382,10 +405,22 @@ just_kill_connection:
|
|||
wsi->user_space, NULL, 0 );
|
||||
} else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
|
||||
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
|
||||
char* errorString;
|
||||
|
||||
lwsl_debug("Connection closed before server reply\n");
|
||||
context->protocols[0].callback(wsi,
|
||||
errorString = NULL;
|
||||
if (wsi->u.hdr.ah)
|
||||
errorString = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
|
||||
if (errorString) {
|
||||
context->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, errorString,
|
||||
(unsigned int)strlen(errorString));
|
||||
} else {
|
||||
context->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, NULL, 0);
|
||||
}
|
||||
} else
|
||||
lwsl_debug("not calling back closed mode=%d state=%d\n",
|
||||
wsi->mode, wsi->state_pre_close);
|
||||
|
@ -922,18 +957,41 @@ lws_ensure_user_space(struct lws *wsi)
|
|||
|
||||
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
|
||||
{
|
||||
time_t o_now = time(NULL);
|
||||
unsigned long long now;
|
||||
struct tm *ptm = NULL;
|
||||
char buf[300];
|
||||
#ifndef WIN32
|
||||
struct tm tm;
|
||||
#endif
|
||||
int n;
|
||||
|
||||
#ifndef _WIN32_WCE
|
||||
#ifdef WIN32
|
||||
ptm = localtime(&o_now);
|
||||
#else
|
||||
if (localtime_r(&o_now, &tm))
|
||||
ptm = &tm;
|
||||
#endif
|
||||
#endif
|
||||
buf[0] = '\0';
|
||||
for (n = 0; n < LLL_COUNT; n++) {
|
||||
if (level != (1 << n))
|
||||
continue;
|
||||
now = time_in_microseconds() / 100;
|
||||
sprintf(buf, "[%llu:%04d] %s: ",
|
||||
(unsigned long long) now / 10000,
|
||||
(int)(now % 10000), log_level_names[n]);
|
||||
if (ptm)
|
||||
sprintf(buf, "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ",
|
||||
ptm->tm_year + 1900,
|
||||
ptm->tm_mon,
|
||||
ptm->tm_mday,
|
||||
ptm->tm_hour,
|
||||
ptm->tm_min,
|
||||
ptm->tm_sec,
|
||||
(int)(now % 10000), log_level_names[n]);
|
||||
else
|
||||
sprintf(buf, "[%llu:%04d] %s: ",
|
||||
(unsigned long long) now / 10000,
|
||||
(int)(now % 10000), log_level_names[n]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1255,6 +1313,25 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_snprintf(char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n;
|
||||
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(str, size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (n >= size)
|
||||
return size;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifdef LWS_NO_EXTENSIONS
|
||||
|
||||
/* we need to provide dummy callbacks for internal exts
|
||||
|
|
|
@ -114,9 +114,18 @@ struct sockaddr_in;
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <basetsd.h>
|
||||
#ifndef _WIN32_WCE
|
||||
#include <fcntl.h>
|
||||
#else
|
||||
#define _O_RDONLY 0x0000
|
||||
#define O_RDONLY _O_RDONLY
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
#define strcasecmp _stricmp
|
||||
#else
|
||||
#define strcasecmp stricmp
|
||||
#endif
|
||||
#define getdtablesize() 30000
|
||||
|
||||
#define LWS_INLINE __inline
|
||||
|
@ -137,9 +146,15 @@ struct sockaddr_in;
|
|||
#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
|
||||
#define LWS_O_RDONLY _O_RDONLY
|
||||
|
||||
#define snprintf _snprintf
|
||||
|
||||
#else /* NOT WIN32 */
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#define LWS_INLINE inline
|
||||
#define LWS_O_RDONLY O_RDONLY
|
||||
|
||||
|
@ -153,8 +168,15 @@ struct sockaddr_in;
|
|||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
/* warn_unused_result attribute only supported by GCC 3.4 or later */
|
||||
#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
|
||||
#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define LWS_WARN_UNUSED_RESULT
|
||||
#endif
|
||||
|
||||
#define LWS_VISIBLE __attribute__((visibility("default")))
|
||||
#define LWS_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
|
||||
#define LWS_WARN_DEPRECATED __attribute__ ((deprecated))
|
||||
#else
|
||||
#define LWS_VISIBLE
|
||||
|
@ -423,7 +445,7 @@ struct lws_plat_file_ops {
|
|||
unsigned char *buf, unsigned long len);
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1190,7 +1212,7 @@ struct lws_protocols {
|
|||
void *user;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
enum lws_ext_options_types {
|
||||
|
@ -1199,7 +1221,7 @@ enum lws_ext_options_types {
|
|||
EXTARG_OPT_DEC
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1216,7 +1238,7 @@ struct lws_ext_options {
|
|||
enum lws_ext_options_types type;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
struct lws_ext_option_arg {
|
||||
|
@ -1239,7 +1261,7 @@ struct lws_extension {
|
|||
const char *client_offer;
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibilty */
|
||||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1479,7 +1501,7 @@ lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents);
|
|||
#endif /* LWS_USE_LIBEV */
|
||||
|
||||
#ifdef LWS_USE_LIBUV
|
||||
typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents);
|
||||
typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int signum);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
|
||||
|
@ -1498,7 +1520,7 @@ LWS_VISIBLE LWS_EXTERN uv_loop_t *
|
|||
lws_uv_getloop(struct lws_context *context, int tsi);
|
||||
|
||||
LWS_VISIBLE void
|
||||
lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents);
|
||||
lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int signum);
|
||||
#endif /* LWS_USE_LIBUV */
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
|
@ -1584,6 +1606,23 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
|
|||
* to the address immediately after the padding won't cause an unaligned access
|
||||
* error. Sometimes for performance reasons the recommended padding is even
|
||||
* larger than sizeof(void *).
|
||||
*
|
||||
* Truncated Writes
|
||||
* ================
|
||||
*
|
||||
* The OS may not accept everything you asked to write on the connection.
|
||||
*
|
||||
* Posix defines POLLOUT indication from poll() to show that the connection
|
||||
* will accept more write data, but it doesn't specifiy how much. It may just
|
||||
* accept one byte of whatever you wanted to send.
|
||||
*
|
||||
* LWS will buffer the remainder automatically, and send it out autonomously.
|
||||
*
|
||||
* During that time, WRITABLE callbacks will be suppressed.
|
||||
*
|
||||
* This is to handle corner cases where unexpectedly the OS refuses what we
|
||||
* usually expect it to accept. You should try to send in chunks that are
|
||||
* almost always accepted in order to avoid the inefficiency of the buffering.
|
||||
*/
|
||||
|
||||
#if !defined(LWS_SIZEOFPTR)
|
||||
|
@ -1887,6 +1926,20 @@ lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
|
|||
LWS_VISIBLE LWS_EXTERN void
|
||||
lws_set_allocator(void *(*realloc)(void *ptr, size_t size));
|
||||
|
||||
/**
|
||||
* lws_snprintf(): lws_snprintf that truncates the returned length too
|
||||
*
|
||||
* \param str: destination buffer
|
||||
* \param size: bytes left in destination buffer
|
||||
* \param format: format string
|
||||
* \param ...: args for format
|
||||
*
|
||||
* This lets you correctly truncate buffers by concatenating lengths, if you
|
||||
* reach the limit the reported length doesn't exceed the limit.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_snprintf(char *str, size_t size, const char *format, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -50,15 +50,6 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
|
|||
return poll(fd, 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is just used to interrupt poll waiting
|
||||
* we don't have to do anything with it.
|
||||
*/
|
||||
static void
|
||||
lws_sigusr2(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_cancel_service_pt() - Cancel servicing of pending socket activity
|
||||
* on one thread
|
||||
|
@ -122,7 +113,7 @@ LWS_VISIBLE int
|
|||
lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &context->pt[tsi];
|
||||
int n, m, c;
|
||||
int n = -1, m, c;
|
||||
char buf;
|
||||
|
||||
/* stay dead once we are dead */
|
||||
|
@ -130,6 +121,9 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
if (!context)
|
||||
return 1;
|
||||
|
||||
if (timeout_ms < 0)
|
||||
goto faked_service;
|
||||
|
||||
lws_libev_run(context, tsi);
|
||||
lws_libuv_run(context, tsi);
|
||||
|
||||
|
@ -158,15 +152,17 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
if (LWS_ERRNO != LWS_EINTR)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c = n;
|
||||
|
||||
lws_service_flag_pending(context, tsi);
|
||||
faked_service:
|
||||
m = lws_service_flag_pending(context, tsi);
|
||||
if (m)
|
||||
c = -1; /* unknown limit */
|
||||
else
|
||||
if (n < 0) {
|
||||
if (LWS_ERRNO != LWS_EINTR)
|
||||
return -1;
|
||||
return 0;
|
||||
} else
|
||||
c = n;
|
||||
|
||||
/* any socket with events to service? */
|
||||
for (n = 0; n < pt->fds_count && c; n++) {
|
||||
|
@ -270,6 +266,10 @@ lws_plat_set_socket_options(struct lws_context *context, int fd)
|
|||
LWS_VISIBLE void
|
||||
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
||||
{
|
||||
if (info->gid != -1)
|
||||
if (setgid(info->gid))
|
||||
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
|
||||
|
||||
if (info->uid != -1) {
|
||||
struct passwd *p = getpwuid(info->uid);
|
||||
|
||||
|
@ -282,10 +282,6 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
|
|||
} else
|
||||
lwsl_warn("getpwuid: unable to find uid %d", info->uid);
|
||||
}
|
||||
if (info->gid != -1)
|
||||
if (setgid(info->gid))
|
||||
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -296,14 +292,6 @@ sigpipe_handler(int x)
|
|||
LWS_VISIBLE int
|
||||
lws_plat_context_early_init(void)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
signal(SIGUSR2, lws_sigusr2);
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR2);
|
||||
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
|
||||
signal(SIGPIPE, sigpipe_handler);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -111,7 +111,7 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
|
|||
fd_set readfds;
|
||||
struct timeval tv = { 0, 0 };
|
||||
|
||||
assert(fd->events == LWS_POLLIN);
|
||||
assert((fd->events & LWS_POLLIN) == LWS_POLLIN);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd->fd, &readfds);
|
||||
|
@ -176,6 +176,9 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
}
|
||||
context->service_tid = context->service_tid_detected;
|
||||
|
||||
if (timeout_ms < 0)
|
||||
goto faked_service;
|
||||
|
||||
for (i = 0; i < pt->fds_count; ++i) {
|
||||
pfd = &pt->fds[i];
|
||||
if (pfd->fd == pt->lserv_fd)
|
||||
|
@ -234,6 +237,8 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
wsi->sock_send_blocking = 0;
|
||||
}
|
||||
|
||||
faked_service:
|
||||
|
||||
/* if someone faked their LWS_POLLIN, then go through all active fds */
|
||||
|
||||
if (lws_service_flag_pending(context, tsi)) {
|
||||
|
@ -252,6 +257,9 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (timeout_ms < 0)
|
||||
return 0;
|
||||
|
||||
/* otherwise just do the one... must be a way to improve that... */
|
||||
|
||||
return lws_service_fd_tsi(context, pfd, tsi);
|
||||
|
@ -271,7 +279,10 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
|
|||
u_long optl = 1;
|
||||
DWORD dwBytesRet;
|
||||
struct tcp_keepalive alive;
|
||||
int protonbr;
|
||||
#ifndef _WIN32_WCE
|
||||
struct protoent *tcp_proto;
|
||||
#endif
|
||||
|
||||
if (context->ka_time) {
|
||||
/* enable keepalive on this socket */
|
||||
|
@ -291,13 +302,18 @@ lws_plat_set_socket_options(struct lws_context *context, lws_sockfd_type fd)
|
|||
|
||||
/* Disable Nagle */
|
||||
optval = 1;
|
||||
#ifndef _WIN32_WCE
|
||||
tcp_proto = getprotobyname("TCP");
|
||||
if (!tcp_proto) {
|
||||
lwsl_err("getprotobyname() failed with error %d\n", LWS_ERRNO);
|
||||
return 1;
|
||||
}
|
||||
protonbr = tcp_proto->p_proto;
|
||||
#else
|
||||
protonbr = 6;
|
||||
#endif
|
||||
|
||||
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen);
|
||||
setsockopt(fd, protonbr, TCP_NODELAY, (const char *)&optval, optlen);
|
||||
|
||||
/* We are nonblocking... */
|
||||
ioctlsocket(fd, FIONBIO, &optl);
|
||||
|
@ -387,7 +403,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
|
|||
|
||||
pt->fds[pt->fds_count++].revents = 0;
|
||||
pt->events[pt->fds_count] = WSACreateEvent();
|
||||
WSAEventSelect(wsi->sock, pt->events[pt->fds_count], LWS_POLLIN);
|
||||
WSAEventSelect(wsi->sock, pt->events[pt->fds_count],
|
||||
LWS_POLLIN | LWS_POLLHUP);
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
|
|
234
lib/parsers.c
234
lib/parsers.c
|
@ -97,7 +97,7 @@ lws_header_table_attach(struct lws *wsi)
|
|||
|
||||
/* if we are already bound to one, just clear it down */
|
||||
if (wsi->u.hdr.ah) {
|
||||
lwsl_err("cleardown\n");
|
||||
lwsl_info("cleardown\n");
|
||||
goto reset;
|
||||
}
|
||||
|
||||
|
@ -112,8 +112,8 @@ lws_header_table_attach(struct lws *wsi)
|
|||
goto bail;
|
||||
}
|
||||
/* new ah.... remove ourselves from waiting list */
|
||||
*pwsi = wsi->u.hdr.ah_wait_list;
|
||||
wsi->u.hdr.ah_wait_list = NULL;
|
||||
*pwsi = wsi->u.hdr.ah_wait_list; /* set our prev to our next */
|
||||
wsi->u.hdr.ah_wait_list = NULL; /* no next any more */
|
||||
pt->ah_wait_list_length--;
|
||||
break;
|
||||
}
|
||||
|
@ -189,26 +189,35 @@ int lws_header_table_detach(struct lws *wsi)
|
|||
lws_pt_lock(pt);
|
||||
|
||||
pwsi = &pt->ah_wait_list;
|
||||
if (!ah) { /* remove from wait list if that's all */
|
||||
if (wsi->socket_is_permanently_unusable)
|
||||
while (*pwsi) {
|
||||
if (*pwsi == wsi) {
|
||||
lwsl_info("%s: wsi %p, remv wait\n",
|
||||
__func__, wsi);
|
||||
*pwsi = wsi->u.hdr.ah_wait_list;
|
||||
wsi->u.hdr.ah_wait_list = NULL;
|
||||
pt->ah_wait_list_length--;
|
||||
goto bail;
|
||||
}
|
||||
pwsi = &(*pwsi)->u.hdr.ah_wait_list;
|
||||
if (!ah) { /* remove from wait list if none attached */
|
||||
while (*pwsi) {
|
||||
if (*pwsi == wsi) {
|
||||
lwsl_info("%s: wsi %p, remv wait\n",
|
||||
__func__, wsi);
|
||||
*pwsi = wsi->u.hdr.ah_wait_list;
|
||||
wsi->u.hdr.ah_wait_list = NULL;
|
||||
pt->ah_wait_list_length--;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
pwsi = &(*pwsi)->u.hdr.ah_wait_list;
|
||||
}
|
||||
/* no ah, not on list... no more business here */
|
||||
goto bail;
|
||||
}
|
||||
/* we did have an ah attached */
|
||||
time(&now);
|
||||
if (now - wsi->u.hdr.ah->assigned > 3)
|
||||
lwsl_err("header assign - free time %d\n",
|
||||
(int)(now - wsi->u.hdr.ah->assigned));
|
||||
if (now - wsi->u.hdr.ah->assigned > 3) {
|
||||
/*
|
||||
* we're detaching the ah, but it was held an
|
||||
* unreasonably long time
|
||||
*/
|
||||
lwsl_notice("%s: wsi %p: ah held %ds, "
|
||||
"ah.rxpos %d, ah.rxlen %d, mode/state %d %d,"
|
||||
"wsi->more_rx_waiting %d\n", __func__, wsi,
|
||||
(int)(now - wsi->u.hdr.ah->assigned),
|
||||
ah->rxpos, ah->rxlen, wsi->mode, wsi->state,
|
||||
wsi->more_rx_waiting);
|
||||
}
|
||||
|
||||
/* if we think we're detaching one, there should be one in use */
|
||||
assert(pt->ah_count_in_use > 0);
|
||||
|
@ -217,6 +226,7 @@ int lws_header_table_detach(struct lws *wsi)
|
|||
wsi->u.hdr.ah = NULL;
|
||||
ah->wsi = NULL; /* no owner */
|
||||
|
||||
/* oh there is nobody on the waiting list... leave it at that then */
|
||||
if (!*pwsi) {
|
||||
ah->in_use = 0;
|
||||
pt->ah_count_in_use--;
|
||||
|
@ -224,7 +234,7 @@ int lws_header_table_detach(struct lws *wsi)
|
|||
goto bail;
|
||||
}
|
||||
|
||||
/* somebody else on same tsi is waiting, give it to him */
|
||||
/* somebody else on same tsi is waiting, give it to oldest guy */
|
||||
|
||||
lwsl_info("pt wait list %p\n", *pwsi);
|
||||
while ((*pwsi)->u.hdr.ah_wait_list)
|
||||
|
@ -249,6 +259,7 @@ int lws_header_table_detach(struct lws *wsi)
|
|||
|
||||
/* point prev guy to next guy in list instead */
|
||||
*pwsi = wsi->u.hdr.ah_wait_list;
|
||||
/* the guy who got one is out of the list */
|
||||
wsi->u.hdr.ah_wait_list = NULL;
|
||||
pt->ah_wait_list_length--;
|
||||
|
||||
|
@ -535,6 +546,23 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
if (issue_char(wsi, '/') < 0)
|
||||
return -1;
|
||||
|
||||
if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) {
|
||||
/*
|
||||
* back up one dir level if possible
|
||||
* safe against header fragmentation because
|
||||
* the method URI can only be in 1 fragment
|
||||
*/
|
||||
if (ah->frags[ah->nfrag].len > 2) {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
do {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
} while (ah->frags[ah->nfrag].len > 1 &&
|
||||
ah->data[ah->pos] != '/');
|
||||
}
|
||||
}
|
||||
|
||||
/* begin parsing HTTP version: */
|
||||
if (issue_char(wsi, '\0') < 0)
|
||||
return -1;
|
||||
|
@ -542,7 +570,10 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
goto start_fragment;
|
||||
}
|
||||
|
||||
/* special URI processing... convert %xx */
|
||||
/*
|
||||
* PRIORITY 1
|
||||
* special URI processing... convert %xx
|
||||
*/
|
||||
|
||||
switch (wsi->u.hdr.ues) {
|
||||
case URIES_IDLE:
|
||||
|
@ -552,30 +583,19 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
}
|
||||
break;
|
||||
case URIES_SEEN_PERCENT:
|
||||
if (char_to_hex(c) < 0) {
|
||||
/* regurgitate */
|
||||
if (issue_char(wsi, '%') < 0)
|
||||
return -1;
|
||||
wsi->u.hdr.ues = URIES_IDLE;
|
||||
/* continue on to assess c */
|
||||
break;
|
||||
}
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
wsi->u.hdr.esc_stash = c;
|
||||
wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
|
||||
goto swallow;
|
||||
|
||||
case URIES_SEEN_PERCENT_H1:
|
||||
if (char_to_hex(c) < 0) {
|
||||
/* regurgitate */
|
||||
if (issue_char(wsi, '%') < 0)
|
||||
return -1;
|
||||
wsi->u.hdr.ues = URIES_IDLE;
|
||||
/* regurgitate + assess */
|
||||
if (lws_parse(wsi, wsi->u.hdr.esc_stash) < 0)
|
||||
return -1;
|
||||
/* continue on to assess c */
|
||||
break;
|
||||
}
|
||||
if (char_to_hex(c) < 0)
|
||||
/* illegal post-% char */
|
||||
goto forbid;
|
||||
|
||||
c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
|
||||
char_to_hex(c);
|
||||
enc = 1;
|
||||
|
@ -584,6 +604,7 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
}
|
||||
|
||||
/*
|
||||
* PRIORITY 2
|
||||
* special URI processing...
|
||||
* convert /.. or /... or /../ etc to /
|
||||
* convert /./ to /
|
||||
|
@ -614,7 +635,9 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
goto swallow;
|
||||
}
|
||||
/* uriencoded = in the name part, disallow */
|
||||
if (c == '=' && enc && !wsi->u.hdr.post_literal_equal)
|
||||
if (c == '=' && enc &&
|
||||
ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] &&
|
||||
!wsi->u.hdr.post_literal_equal)
|
||||
c = '_';
|
||||
|
||||
/* after the real =, we don't care how many = */
|
||||
|
@ -642,20 +665,6 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
case URIPS_SEEN_SLASH_DOT:
|
||||
/* swallow second . */
|
||||
if (c == '.') {
|
||||
/*
|
||||
* back up one dir level if possible
|
||||
* safe against header fragmentation because
|
||||
* the method URI can only be in 1 fragment
|
||||
*/
|
||||
if (ah->frags[ah->nfrag].len > 2) {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
do {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
} while (ah->frags[ah->nfrag].len > 1 &&
|
||||
ah->data[ah->pos] != '/');
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
|
||||
goto swallow;
|
||||
}
|
||||
|
@ -671,19 +680,44 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
break;
|
||||
|
||||
case URIPS_SEEN_SLASH_DOT_DOT:
|
||||
/* swallow prior .. chars and any subsequent . */
|
||||
if (c == '.')
|
||||
|
||||
/* /../ or /..[End of URI] --> backup to last / */
|
||||
if (c == '/' || c == '?') {
|
||||
/*
|
||||
* back up one dir level if possible
|
||||
* safe against header fragmentation because
|
||||
* the method URI can only be in 1 fragment
|
||||
*/
|
||||
if (ah->frags[ah->nfrag].len > 2) {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
do {
|
||||
ah->pos--;
|
||||
ah->frags[ah->nfrag].len--;
|
||||
} while (ah->frags[ah->nfrag].len > 1 &&
|
||||
ah->data[ah->pos] != '/');
|
||||
}
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
if (ah->frags[ah->nfrag].len > 1)
|
||||
break;
|
||||
goto swallow;
|
||||
/* last issued was /, so another / == // */
|
||||
if (c == '/')
|
||||
goto swallow;
|
||||
/* last we issued was / so SEEN_SLASH */
|
||||
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
|
||||
}
|
||||
|
||||
/* /..[^/] ... regurgitate and allow */
|
||||
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
if (issue_char(wsi, '.') < 0)
|
||||
return -1;
|
||||
wsi->u.hdr.ups = URIPS_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '?' && !enc &&
|
||||
!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
|
||||
/* seal off uri header */
|
||||
if (issue_char(wsi, '\0') < 0)
|
||||
return -1;
|
||||
|
@ -703,10 +737,12 @@ lws_parse(struct lws *wsi, unsigned char c)
|
|||
}
|
||||
|
||||
check_eol:
|
||||
|
||||
/* bail at EOL */
|
||||
if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
|
||||
c == '\x0d') {
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
|
||||
c = '\0';
|
||||
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
|
||||
lwsl_parser("*\n");
|
||||
|
@ -752,7 +788,7 @@ swallow:
|
|||
*/
|
||||
if (m == ARRAY_SIZE(methods)) {
|
||||
lwsl_info("Unknown method - dropping\n");
|
||||
return -1;
|
||||
goto forbid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -840,6 +876,8 @@ excessive:
|
|||
|
||||
case WSI_TOKEN_SKIPPING_SAW_CR:
|
||||
lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
if (c == '\x0a') {
|
||||
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
|
||||
wsi->u.hdr.lextable_pos = 0;
|
||||
|
@ -856,7 +894,8 @@ excessive:
|
|||
return 0;
|
||||
|
||||
set_parsing_complete:
|
||||
|
||||
if (wsi->u.hdr.ues != URIES_IDLE)
|
||||
goto forbid;
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
|
||||
wsi->ietf_spec_revision =
|
||||
|
@ -868,6 +907,11 @@ set_parsing_complete:
|
|||
wsi->hdr_parsing_completed = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
forbid:
|
||||
lwsl_notice(" forbidding on uri sanitation\n");
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1404,3 +1448,63 @@ lws_remaining_packet_payload(struct lws *wsi)
|
|||
{
|
||||
return wsi->u.ws.rx_packet_length;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
void
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf,
|
||||
size_t *len)
|
||||
{
|
||||
unsigned char *buffer = *buf, mask[4];
|
||||
int buffer_size, avail, n;
|
||||
char *rx_ubuf;
|
||||
|
||||
if (wsi->protocol->rx_buffer_size)
|
||||
buffer_size = wsi->protocol->rx_buffer_size;
|
||||
else
|
||||
buffer_size = LWS_MAX_SOCKET_IO_BUF;
|
||||
avail = buffer_size - wsi->u.ws.rx_ubuf_head;
|
||||
|
||||
/* do not consume more than we should */
|
||||
if (avail > wsi->u.ws.rx_packet_length)
|
||||
avail = wsi->u.ws.rx_packet_length;
|
||||
|
||||
/* do not consume more than what is in the buffer */
|
||||
if (avail > *len)
|
||||
avail = *len;
|
||||
|
||||
/* we want to leave 1 byte for the parser to handle properly */
|
||||
if (avail <= 1)
|
||||
return;
|
||||
|
||||
avail--;
|
||||
rx_ubuf = wsi->u.ws.rx_ubuf + LWS_PRE + wsi->u.ws.rx_ubuf_head;
|
||||
if (wsi->u.ws.all_zero_nonce)
|
||||
memcpy(rx_ubuf, buffer, avail);
|
||||
else {
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
mask[n] = wsi->u.ws.mask[(wsi->u.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 < (avail & 3); n++)
|
||||
*(rx_ubuf++) = *(buffer++) ^ mask[n];
|
||||
|
||||
wsi->u.ws.mask_idx = (wsi->u.ws.mask_idx + avail) & 3;
|
||||
}
|
||||
|
||||
(*buf) += avail;
|
||||
wsi->u.ws.rx_ubuf_head += avail;
|
||||
wsi->u.ws.rx_packet_length -= avail;
|
||||
*len -= avail;
|
||||
}
|
||||
|
|
|
@ -277,7 +277,12 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
#ifdef LWS_USE_HTTP2
|
||||
struct lws *network_wsi, *wsi2;
|
||||
int already;
|
||||
#endif
|
||||
|
||||
if (wsi->state == LWSS_SHUTDOWN)
|
||||
return 0;
|
||||
|
||||
#ifdef LWS_USE_HTTP2
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
|
||||
if (wsi->mode != LWSCM_HTTP2_SERVING)
|
||||
|
|
|
@ -84,6 +84,14 @@
|
|||
|
||||
#ifdef _WIN32_WCE
|
||||
#define vsnprintf _vsnprintf
|
||||
#else
|
||||
#ifdef LWS_HAVE__VSNPRINTF
|
||||
#define vsnprintf _vsnprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef LWS_HAVE__SNPRINTF
|
||||
#define lws_snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#else /* not windows --> */
|
||||
|
@ -106,6 +114,7 @@
|
|||
#endif
|
||||
#if defined (__ANDROID__)
|
||||
#include <syslog.h>
|
||||
#include <sys/resource.h>
|
||||
#else
|
||||
#include <sys/syslog.h>
|
||||
#endif
|
||||
|
@ -224,10 +233,15 @@ typedef unsigned __int64 u_int64_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M)))
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
@ -530,6 +544,7 @@ struct lws_context_per_thread {
|
|||
struct lws *rx_draining_ext_list;
|
||||
struct lws *tx_draining_ext_list;
|
||||
struct lws *timeout_list;
|
||||
struct lws_context *context;
|
||||
void *http_header_data;
|
||||
struct allocated_headers *ah_pool;
|
||||
struct lws *ah_wait_list;
|
||||
|
@ -546,6 +561,8 @@ struct lws_context_per_thread {
|
|||
#if defined(LWS_USE_LIBUV)
|
||||
uv_loop_t *io_loop_uv;
|
||||
uv_signal_t signals[8];
|
||||
uv_timer_t uv_timeout_watcher;
|
||||
uv_idle_t uv_idle;
|
||||
#endif
|
||||
#if defined(LWS_USE_LIBEV)
|
||||
struct lws_io_watcher w_accept;
|
||||
|
@ -571,6 +588,7 @@ struct lws_context_per_thread {
|
|||
unsigned int fds_count;
|
||||
|
||||
short ah_count_in_use;
|
||||
unsigned char tid;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -948,12 +966,11 @@ struct _lws_header_related {
|
|||
char post_literal_equal;
|
||||
unsigned char parser_state; /* enum lws_token_indexes */
|
||||
char redirects;
|
||||
char more_rx_waiting;
|
||||
};
|
||||
|
||||
struct _lws_websocket_related {
|
||||
/* cheapest way to deal with ah overlap with ws union transition */
|
||||
struct _lws_header_related *hdr;
|
||||
struct _lws_header_related hdr;
|
||||
char *rx_ubuf;
|
||||
unsigned int rx_ubuf_alloc;
|
||||
struct lws *rx_draining_ext_list;
|
||||
|
@ -1056,6 +1073,7 @@ struct lws {
|
|||
unsigned int user_space_externally_allocated:1;
|
||||
unsigned int socket_is_permanently_unusable:1;
|
||||
unsigned int rxflow_change_to:2;
|
||||
unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
unsigned int extension_data_pending:1;
|
||||
#endif
|
||||
|
@ -1206,6 +1224,9 @@ lws_client_interpret_server_handshake(struct lws *wsi);
|
|||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_rx_sm(struct lws *wsi, unsigned char c);
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_payload_until_length_exhausted(struct lws *wsi, unsigned char **buf, size_t *len);
|
||||
|
||||
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
|
||||
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
|
||||
|
||||
|
@ -1367,6 +1388,13 @@ lws_pt_mutex_init(struct lws_context_per_thread *pt)
|
|||
{
|
||||
pthread_mutex_init(&pt->lock, NULL);
|
||||
}
|
||||
|
||||
static LWS_INLINE void
|
||||
lws_pt_mutex_destroy(struct lws_context_per_thread *pt)
|
||||
{
|
||||
pthread_mutex_destroy(&pt->lock);
|
||||
}
|
||||
|
||||
static LWS_INLINE void
|
||||
lws_pt_lock(struct lws_context_per_thread *pt)
|
||||
{
|
||||
|
@ -1380,6 +1408,7 @@ lws_pt_unlock(struct lws_context_per_thread *pt)
|
|||
}
|
||||
#else
|
||||
#define lws_pt_mutex_init(_a) (void)(_a)
|
||||
#define lws_pt_mutex_destroy(_a) (void)(_a)
|
||||
#define lws_pt_lock(_a) (void)(_a)
|
||||
#define lws_pt_unlock(_a) (void)(_a)
|
||||
#endif
|
||||
|
|
|
@ -123,11 +123,6 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
|
||||
/* apply it */
|
||||
|
||||
if (ext_count)
|
||||
*(*p)++ = ',';
|
||||
else
|
||||
LWS_CPYAPP(*p, "\x0d\x0aSec-WebSocket-Extensions: ");
|
||||
*p += sprintf(*p, "%s", ext_name);
|
||||
ext_count++;
|
||||
|
||||
/* instantiate the extension on this conn */
|
||||
|
@ -136,10 +131,21 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
|
|||
|
||||
/* allow him to construct his context */
|
||||
|
||||
ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
if (ext->callback(lws_get_context(wsi), ext, wsi,
|
||||
LWS_EXT_CB_CONSTRUCT,
|
||||
(void *)&wsi->act_ext_user[wsi->count_act_ext],
|
||||
NULL, 0);
|
||||
NULL, 0)) {
|
||||
lwsl_notice("ext %s failed construction\n", ext_name);
|
||||
ext_count--;
|
||||
ext++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext_count > 1)
|
||||
*(*p)++ = ',';
|
||||
else
|
||||
LWS_CPYAPP(*p, "\x0d\x0aSec-WebSocket-Extensions: ");
|
||||
*p += sprintf(*p, "%s", ext_name);
|
||||
|
||||
wsi->count_act_ext++;
|
||||
lwsl_parser("count_act_ext <- %d\n", wsi->count_act_ext);
|
||||
|
@ -208,12 +214,9 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
|
|||
strcpy(p, (char *)pt->serv_buf);
|
||||
p += accept_len;
|
||||
|
||||
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
|
||||
if (wsi->protocol->name && wsi->protocol->name[0]) {
|
||||
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
|
||||
n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
|
||||
if (n < 0)
|
||||
goto bail;
|
||||
p += n;
|
||||
p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
|
||||
}
|
||||
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
|
|
41
lib/server.c
41
lib/server.c
|
@ -243,6 +243,14 @@ lws_http_action(struct lws *wsi)
|
|||
break;
|
||||
}
|
||||
|
||||
/* we insist on absolute paths */
|
||||
|
||||
if (uri_ptr[0] != '/') {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
|
||||
goto bail_nuke_ah;
|
||||
}
|
||||
|
||||
/* HTTP header had a content length? */
|
||||
|
||||
wsi->u.http.content_length = 0;
|
||||
|
@ -306,6 +314,11 @@ lws_http_action(struct lws *wsi)
|
|||
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
wsi->user_space, uri_ptr, uri_len);
|
||||
if (n) {
|
||||
lwsl_info("LWS_CALLBACK_HTTP closing\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're not issuing a file, check for content_length or
|
||||
|
@ -347,7 +360,7 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
assert(wsi->u.hdr.ah);
|
||||
|
||||
while (len--) {
|
||||
wsi->u.hdr.more_rx_waiting = !!len;
|
||||
wsi->more_rx_waiting = !!len;
|
||||
|
||||
assert(wsi->mode == LWSCM_HTTP_SERVING);
|
||||
|
||||
|
@ -359,7 +372,9 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
|
||||
continue;
|
||||
|
||||
lwsl_parser("lws_parse sees parsing complete\n");
|
||||
lwsl_parser("%s: lws_parse sees parsing complete\n", __func__);
|
||||
lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
|
||||
wsi->more_rx_waiting);
|
||||
|
||||
wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
|
||||
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
|
||||
|
@ -551,7 +566,7 @@ upgrade_ws:
|
|||
|
||||
default:
|
||||
lwsl_warn("Unknown client spec version %d\n",
|
||||
wsi->ietf_spec_revision);
|
||||
wsi->ietf_spec_revision);
|
||||
goto bail_nuke_ah;
|
||||
}
|
||||
|
||||
|
@ -562,8 +577,9 @@ upgrade_ws:
|
|||
* upgrade request and to already be in the ah rx buffer.
|
||||
*/
|
||||
|
||||
lwsl_err("%s: %p: inheriting ah in ws mode (rxpos: %d, rxlen: %d)\n",
|
||||
__func__, wsi, wsi->u.hdr.ah->rxpos, wsi->u.hdr.ah->rxlen);
|
||||
lwsl_info("%s: %p: inheriting ah in ws mode (rxpos:%d, rxlen:%d)\n",
|
||||
__func__, wsi, wsi->u.hdr.ah->rxpos,
|
||||
wsi->u.hdr.ah->rxlen);
|
||||
lws_pt_lock(pt);
|
||||
hdr = wsi->u.hdr;
|
||||
|
||||
|
@ -574,7 +590,7 @@ upgrade_ws:
|
|||
* mode any more then... ah_temp member is at start the same
|
||||
* though)
|
||||
*
|
||||
* Beacuse rxpos/rxlen shows something in the ah, we will get
|
||||
* Because rxpos/rxlen shows something in the ah, we will get
|
||||
* service guaranteed next time around the event loop
|
||||
*
|
||||
* All union members begin with hdr, so we can use it even
|
||||
|
@ -738,9 +754,13 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
* reset the existing header table and keep it.
|
||||
*/
|
||||
if (wsi->u.hdr.ah) {
|
||||
if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen)
|
||||
lwsl_info("%s: wsi->more_rx_waiting=%d\n", __func__,
|
||||
wsi->more_rx_waiting);
|
||||
|
||||
if (!wsi->more_rx_waiting) {
|
||||
wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
|
||||
lws_header_table_detach(wsi);
|
||||
else
|
||||
} else
|
||||
lws_header_table_reset(wsi);
|
||||
}
|
||||
|
||||
|
@ -1162,6 +1182,11 @@ lws_interpret_incoming_packet(struct lws *wsi, unsigned char **buf, size_t len)
|
|||
if (wsi->rxflow_buffer)
|
||||
wsi->rxflow_pos++;
|
||||
|
||||
/* consume payload bytes efficiently */
|
||||
if (wsi->lws_rx_parse_state ==
|
||||
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED)
|
||||
lws_payload_until_length_exhausted(wsi, buf, &len);
|
||||
|
||||
/* process the byte */
|
||||
m = lws_rx_sm(wsi, *(*buf)++);
|
||||
if (m < 0)
|
||||
|
|
|
@ -143,6 +143,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
/* Priority 6: user can get the callback
|
||||
*/
|
||||
m = lws_ext_cb_active(wsi, LWS_EXT_CB_IS_WRITEABLE, NULL, 0);
|
||||
if (m)
|
||||
return -1;
|
||||
#ifndef LWS_NO_EXTENSIONS
|
||||
if (!wsi->extension_data_pending)
|
||||
goto user_service;
|
||||
|
@ -292,7 +294,6 @@ int
|
|||
lws_service_timeout_check(struct lws *wsi, unsigned int sec)
|
||||
{
|
||||
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
|
||||
struct lws **pwsi;
|
||||
|
||||
/*
|
||||
* if extensions want in on it (eg, we are a mux parent)
|
||||
|
@ -316,21 +317,6 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
|
|||
pt->ah_wait_list_length,
|
||||
pt->fds[wsi->sock].events);
|
||||
#endif
|
||||
lws_pt_lock(pt);
|
||||
|
||||
pwsi = &pt->ah_wait_list;
|
||||
if (!pwsi)
|
||||
return 0;
|
||||
while (*pwsi) {
|
||||
if (*pwsi == wsi)
|
||||
break;
|
||||
pwsi = &(*pwsi)->u.hdr.ah_wait_list;
|
||||
lwsl_err("%s: pwsi=%p\n", __func__, pwsi);
|
||||
}
|
||||
lws_pt_unlock(pt);
|
||||
|
||||
if (!*pwsi)
|
||||
lwsl_err("*** not on ah wait list ***\n");
|
||||
/*
|
||||
* Since he failed a timeout, he already had a chance to do
|
||||
* something and was unable to... that includes situations like
|
||||
|
@ -339,6 +325,10 @@ lws_service_timeout_check(struct lws *wsi, unsigned int sec)
|
|||
* cleanup like flush partials.
|
||||
*/
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
if (wsi->mode == LWSCM_WSCL_WAITING_SSL)
|
||||
wsi->context->protocols[0].callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
wsi->user_space, NULL, 0);
|
||||
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
|
||||
|
||||
return 1;
|
||||
|
@ -359,6 +349,8 @@ int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
|
|||
/* a new rxflow, buffer it and warn caller */
|
||||
lwsl_info("new rxflow input buffer len %d\n", len - n);
|
||||
wsi->rxflow_buffer = lws_malloc(len - n);
|
||||
if (!wsi->rxflow_buffer)
|
||||
return -1;
|
||||
wsi->rxflow_len = len - n;
|
||||
wsi->rxflow_pos = 0;
|
||||
memcpy(wsi->rxflow_buffer, buf + n, len - n);
|
||||
|
@ -383,13 +375,13 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
|
|||
|
||||
/* 1) if we know we are draining rx ext, do not wait in poll */
|
||||
if (pt->rx_draining_ext_list)
|
||||
timeout_ms = 0;
|
||||
return 0;
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
/* 2) if we know we have non-network pending data, do not wait in poll */
|
||||
if (lws_ssl_anybody_has_buffered_read_tsi(context, tsi)) {
|
||||
timeout_ms = 0;
|
||||
lwsl_err("ssl buffered read\n");
|
||||
lwsl_info("ssl buffered read\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -398,8 +390,7 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
|
|||
if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) {
|
||||
/* any ah with pending rx must be attached to someone */
|
||||
assert(pt->ah_pool[n].wsi);
|
||||
timeout_ms = 0;
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return timeout_ms;
|
||||
|
@ -617,9 +608,6 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
|
|||
n = lws_server_socket_service(context, wsi, pollfd);
|
||||
if (n) /* closed by above */
|
||||
return 1;
|
||||
pending = lws_ssl_pending(wsi);
|
||||
if (pending)
|
||||
goto handle_pending;
|
||||
goto handled;
|
||||
|
||||
case LWSCM_WS_SERVING:
|
||||
|
@ -806,7 +794,6 @@ drain:
|
|||
|
||||
pending = lws_ssl_pending(wsi);
|
||||
if (pending) {
|
||||
handle_pending:
|
||||
pending = pending > LWS_MAX_SOCKET_IO_BUF ?
|
||||
LWS_MAX_SOCKET_IO_BUF : pending;
|
||||
goto read;
|
||||
|
|
|
@ -99,7 +99,6 @@ static const unsigned int _K[] =
|
|||
sha1_step(ctxt); \
|
||||
}
|
||||
|
||||
static void sha1_step __P((struct sha1_ctxt *));
|
||||
|
||||
static void
|
||||
sha1_step(struct sha1_ctxt *ctxt)
|
||||
|
|
|
@ -125,6 +125,7 @@ void lws_http2_configure_if_upgraded(struct lws *wsi)
|
|||
return;
|
||||
}
|
||||
|
||||
(void)method;
|
||||
lwsl_info("negotiated %s using %s\n", name, method);
|
||||
wsi->use_ssl = 1;
|
||||
if (strncmp((char *)name, "http/1.1", 8) == 0)
|
||||
|
|
144
lib/ssl.c
144
lib/ssl.c
|
@ -24,6 +24,10 @@
|
|||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#ifdef LWS_HAVE_OPENSSL_ECDH_H
|
||||
#include <openssl/ecdh.h>
|
||||
#endif
|
||||
|
||||
int openssl_websocket_private_data_index;
|
||||
|
||||
static int
|
||||
|
@ -84,6 +88,85 @@ OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
return !n;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh(struct lws_context *context)
|
||||
{
|
||||
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
int KeyType;
|
||||
EC_KEY *EC_key = NULL;
|
||||
X509 *x;
|
||||
EVP_PKEY *pkey;
|
||||
|
||||
if (!(context->options & LWS_SERVER_OPTION_SSL_ECDH))
|
||||
return 0;
|
||||
|
||||
lwsl_notice(" Using ECDH certificate support\n");
|
||||
|
||||
/* Get X509 certificate from ssl context */
|
||||
x = sk_X509_value(context->ssl_ctx->extra_certs, 0);
|
||||
if (!x) {
|
||||
lwsl_err("%s: x is NULL\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
/* Get the public key from certificate */
|
||||
pkey = X509_get_pubkey(x);
|
||||
if (!pkey) {
|
||||
lwsl_err("%s: pkey is NULL\n", __func__);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Get the key type */
|
||||
KeyType = EVP_PKEY_type(pkey->type);
|
||||
|
||||
if (EVP_PKEY_EC != KeyType) {
|
||||
lwsl_notice("Key type is not EC\n");
|
||||
return 0;
|
||||
}
|
||||
/* Get the key */
|
||||
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
/* Set ECDH parameter */
|
||||
if (!EC_key) {
|
||||
lwsl_err("%s: ECDH key is NULL \n", __func__);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(context->ssl_ctx, EC_key);
|
||||
EC_KEY_free(EC_key);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lws_context_ssl_init_ecdh_curve(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
{
|
||||
#ifdef LWS_HAVE_OPENSSL_ECDH_H
|
||||
EC_KEY *ecdh;
|
||||
int ecdh_nid;
|
||||
const char *ecdh_curve = "prime256v1";
|
||||
|
||||
ecdh_nid = OBJ_sn2nid(ecdh_curve);
|
||||
if (NID_undef == ecdh_nid) {
|
||||
lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
|
||||
if (NULL == ecdh) {
|
||||
lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve);
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(context->ssl_ctx, ecdh);
|
||||
EC_KEY_free(ecdh);
|
||||
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
|
||||
|
||||
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
|
||||
#else
|
||||
lwsl_notice(" OpenSSL doesn't support ECDH\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
LWS_VISIBLE int
|
||||
lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
||||
struct lws_context *context)
|
||||
|
@ -92,12 +175,6 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
struct lws wsi;
|
||||
int error;
|
||||
int n;
|
||||
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
int KeyType;
|
||||
EC_KEY *EC_key = NULL;
|
||||
X509 *x;
|
||||
EVP_PKEY *pkey;
|
||||
#endif
|
||||
|
||||
if (info->port != CONTEXT_PORT_NO_LISTEN) {
|
||||
|
||||
|
@ -170,6 +247,7 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
#ifdef SSL_OP_NO_COMPRESSION
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_SINGLE_DH_USE);
|
||||
SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
if (info->ssl_cipher_list)
|
||||
SSL_CTX_set_cipher_list(context->ssl_ctx,
|
||||
|
@ -190,16 +268,25 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
|
||||
SSL_CTX_set_verify(context->ssl_ctx,
|
||||
verify_options, OpenSSL_verify_callback);
|
||||
}
|
||||
|
||||
/*
|
||||
* give user code a chance to load certs into the server
|
||||
* allowing it to verify incoming client certs
|
||||
*/
|
||||
/*
|
||||
* give user code a chance to load certs into the server
|
||||
* allowing it to verify incoming client certs
|
||||
*/
|
||||
|
||||
context->protocols[0].callback(&wsi,
|
||||
if (info->ssl_ca_filepath &&
|
||||
!SSL_CTX_load_verify_locations(context->ssl_ctx,
|
||||
info->ssl_ca_filepath, NULL)) {
|
||||
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n");
|
||||
}
|
||||
|
||||
if (lws_context_ssl_init_ecdh_curve(info, context))
|
||||
return -1;
|
||||
|
||||
context->protocols[0].callback(&wsi,
|
||||
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
|
||||
context->ssl_ctx, NULL, 0);
|
||||
}
|
||||
|
||||
if (info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT)
|
||||
/* Normally SSL listener rejects non-ssl, optionally allow */
|
||||
|
@ -249,33 +336,8 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
if (context->options & LWS_SERVER_OPTION_SSL_ECDH) {
|
||||
lwsl_notice(" Using ECDH certificate support\n");
|
||||
|
||||
/* Get X509 certificate from ssl context */
|
||||
x = sk_X509_value(context->ssl_ctx->extra_certs, 0);
|
||||
/* Get the public key from certificate */
|
||||
pkey = X509_get_pubkey(x);
|
||||
/* Get the key type */
|
||||
KeyType = EVP_PKEY_type(pkey->type);
|
||||
|
||||
if (EVP_PKEY_EC != KeyType) {
|
||||
lwsl_err("Key type is not EC\n");
|
||||
return 1;
|
||||
}
|
||||
/* Get the key */
|
||||
EC_key = EVP_PKEY_get1_EC_KEY(pkey);
|
||||
/* Set ECDH parameter */
|
||||
if (!EC_key) {
|
||||
error = ERR_get_error();
|
||||
lwsl_err("ECDH key is NULL \n");
|
||||
return 1;
|
||||
}
|
||||
SSL_CTX_set_tmp_ecdh(context->ssl_ctx, EC_key);
|
||||
EC_KEY_free(EC_key);
|
||||
}
|
||||
#endif
|
||||
if (lws_context_ssl_init_ecdh(context))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* SSL is happy and has a cert it's content with
|
||||
|
@ -297,6 +359,7 @@ lws_ssl_destroy(struct lws_context *context)
|
|||
if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
|
||||
SSL_CTX_free(context->ssl_client_ctx);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100006L)
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
|
@ -305,6 +368,7 @@ lws_ssl_destroy(struct lws_context *context)
|
|||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
}
|
||||
|
||||
LWS_VISIBLE void
|
||||
|
@ -770,6 +834,7 @@ lws_ssl_context_destroy(struct lws_context *context)
|
|||
if (!context->user_supplied_ssl_ctx && context->ssl_client_ctx)
|
||||
SSL_CTX_free(context->ssl_client_ctx);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10100006L)
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x01000000) || defined(USE_WOLFSSL)
|
||||
ERR_remove_state(0);
|
||||
#else
|
||||
|
@ -778,4 +843,5 @@ lws_ssl_context_destroy(struct lws_context *context)
|
|||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1674,3 +1674,27 @@ Otherwise a default timeout is used.
|
|||
<dd>Length of data in <tt><b>buf</b></tt> to send
|
||||
</dl>
|
||||
<hr>
|
||||
<h2>lws_snprintf - </h2>
|
||||
<i>LWS_EXTERN int</i>
|
||||
<b>lws_snprintf</b>
|
||||
(<i>char *</i> <b>str</b>,
|
||||
<i>size_t</i> <b>size</b>,
|
||||
<i>const char *</i> <b>format</b>,
|
||||
<i></i> <b>...</b>)
|
||||
<h3>Arguments</h3>
|
||||
<dl>
|
||||
<dt><b>...</b>
|
||||
<dd>variable arguments
|
||||
</dl>
|
||||
<h3>Description</h3>
|
||||
<blockquote>
|
||||
<p>
|
||||
\param str: destination buffer
|
||||
\param size: bytes left in destination buffer
|
||||
\param format: format string
|
||||
\param ...: args for format
|
||||
<p>
|
||||
This lets you correctly truncate buffers by concatenating lengths, if you
|
||||
reach the limit the reported length doesn't exceed the limit.
|
||||
</blockquote>
|
||||
<hr>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Name: libwebsockets
|
||||
Version: 1.7.0
|
||||
Version: 1.7.9
|
||||
Release: 1%{?dist}
|
||||
Summary: Websocket Server and Client Library
|
||||
|
||||
Group: System Environment/Libraries
|
||||
License: LGPLv2 with exceptions
|
||||
URL: http://warmcat.com
|
||||
URL: https://libwebsockets.org
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
|
||||
|
||||
|
@ -55,10 +55,11 @@ rm -rf $RPM_BUILD_ROOT
|
|||
/usr/bin/libwebsockets-test-echo
|
||||
/usr/bin/libwebsockets-test-fraggle
|
||||
/usr/bin/libwebsockets-test-fuzxy
|
||||
/%{_libdir}/libwebsockets.so.6
|
||||
/%{_libdir}/libwebsockets.so.7.1
|
||||
/%{_libdir}/libwebsockets.so
|
||||
/%{_libdir}/cmake/libwebsockets/LibwebsocketsConfig.cmake
|
||||
/%{_libdir}/cmake/libwebsockets/LibwebsocketsConfigVersion.cmake
|
||||
/%{_libdir}/cmake/libwebsockets/LibwebsocketsTargets.cmake
|
||||
/usr/share/libwebsockets-test-server
|
||||
%doc
|
||||
%files devel
|
||||
|
@ -69,6 +70,37 @@ rm -rf $RPM_BUILD_ROOT
|
|||
/%{_libdir}/pkgconfig/libwebsockets.pc
|
||||
|
||||
%changelog
|
||||
|
||||
* Thu Sep 15 2016 Andy Green <andy@warmcat.com> 1.7.9-1
|
||||
- MINOR fixes Upstream 1.7.9 release (see changelog)
|
||||
|
||||
* Thu May 12 2016 Andy Green <andy@warmcat.com> 1.7.8-1
|
||||
- MINOR fixes Upstream 1.7.8 release (see changelog)
|
||||
|
||||
* Sat May 03 2016 Andy Green <andy@warmcat.com> 1.7.7-1
|
||||
- MINOR fixes Upstream 1.7.7 release (see changelog)
|
||||
|
||||
* Sat Apr 22 2016 Andy Green <andy@warmcat.com> 1.7.6-1
|
||||
- MINOR fixes Upstream 1.7.6 release (see changelog)
|
||||
|
||||
* Fri Apr 1 2016 Andy Green <andy@warmcat.com> 1.7.5-1
|
||||
- MAJOR fixes Upstream 1.7.5 release (see changelog)
|
||||
|
||||
* Tue Mar 29 2016 Andy Green <andy@warmcat.com> 1.7.4-2
|
||||
- MINOR added LibwebsocketsTargets.cmake
|
||||
|
||||
* Mon Mar 22 2016 Andy Green <andy@warmcat.com> 1.7.4-1
|
||||
- MINOR fixes Upstream 1.7.4 release (see changelog)
|
||||
|
||||
* Mon Feb 29 2016 Andy Green <andy@warmcat.com> 1.7.3-1
|
||||
- MAJOR fixes Upstream 1.7.3 release (see changelog)
|
||||
|
||||
* Thu Feb 25 2016 Andy Green <andy@warmcat.com> 1.7.2-1
|
||||
- MINOR Upstream 1.7.2 release (see changelog)
|
||||
|
||||
* Sat Feb 20 2016 Andy Green <andy@warmcat.com> 1.7.1-1
|
||||
- MINOR Upstream 1.7.1 release (see changelog)
|
||||
|
||||
* Tue Feb 16 2016 Andy Green <andy@warmcat.com> 1.7.0-1
|
||||
- MAJOR SONAMEBUMP APICHANGES Upstream 1.7.0 release
|
||||
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
/* SSL server using ECDH certificate */
|
||||
#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
|
||||
|
||||
/* whether the Openssl is recent enough, and / or built with, ecdh */
|
||||
#cmakedefine LWS_HAVE_OPENSSL_ECDH_H
|
||||
|
||||
/* Maximum supported service threads */
|
||||
#define LWS_MAX_SMP ${LWS_MAX_SMP}
|
||||
|
||||
|
|
|
@ -38,6 +38,11 @@ Release Checklist
|
|||
|
||||
set(SOVERSION "6")
|
||||
|
||||
libwebsockets.spec
|
||||
|
||||
-/%{_libdir}/libwebsockets.so.6
|
||||
+/%{_libdir}/libwebsockets.so.7
|
||||
|
||||
3) changelog
|
||||
|
||||
a) Add next version tag header.
|
||||
|
|
|
@ -35,6 +35,29 @@ function check {
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" = "rejected" ] ; then
|
||||
if [ -z "`grep '<h1>406</h1>' /tmp/lwscap`" ] ; then
|
||||
echo "FAIL: should have told forbidden (test server has no dirs)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "$1" = "media" ] ; then
|
||||
if [ -z "`grep '<h1>415</h1>' /tmp/lwscap`" ] ; then
|
||||
echo "FAIL: should have told unknown media type"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" == "0" ] ; then
|
||||
a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep "get\ \ =" | tr -s ' ' | cut -d' ' -f4-`"
|
||||
if [ "$a" != "$2" ] ; then
|
||||
echo "URL path '$a' not $2"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$1" == "1" ] ; then
|
||||
a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep URI\ Arg\ 1\: | tr -s ' ' | cut -d' ' -f5-`"
|
||||
if [ "$a" != "$2" ] ; then
|
||||
|
@ -66,7 +89,7 @@ function check {
|
|||
|
||||
rm -rf $LOG
|
||||
killall libwebsockets-test-server 2>/dev/null
|
||||
libwebsockets-test-server -d31 2>> $LOG &
|
||||
libwebsockets-test-server -d15 2>> $LOG &
|
||||
CPID=$!
|
||||
|
||||
while [ -z "`grep Listening $LOG`" ] ; do
|
||||
|
@ -91,9 +114,10 @@ check 1 "key1=value1"
|
|||
check
|
||||
|
||||
echo
|
||||
echo "---- ? processing (/test?key1%3d2=value1)"
|
||||
echo "---- ? processing (/t%3dest?key1%3d2=value1)"
|
||||
rm -f /tmp/lwscap
|
||||
echo -e "GET /test?key1%3d2=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
echo -e "GET /t%3dest?key1%3d2=value1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
check 0 "/t=est"
|
||||
check 1 "key1_2=value1"
|
||||
check
|
||||
|
||||
|
@ -208,11 +232,18 @@ echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD.................
|
|||
check default
|
||||
check
|
||||
|
||||
echo
|
||||
echo "---- nonexistant file"
|
||||
rm -f /tmp/lwscap
|
||||
echo -e "GET /nope HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
check media
|
||||
check
|
||||
|
||||
echo
|
||||
echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
|
||||
rm -f /tmp/lwscap
|
||||
echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
check forbidden
|
||||
check rejected
|
||||
check
|
||||
|
||||
echo
|
||||
|
@ -254,14 +285,14 @@ echo
|
|||
echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
|
||||
rm -f /tmp/lwscap
|
||||
echo -e "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
check forbidden
|
||||
check rejected
|
||||
check
|
||||
|
||||
echo
|
||||
echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
|
||||
rm -f /tmp/lwscap
|
||||
echo -e "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
|
||||
check forbidden
|
||||
check rejected
|
||||
check
|
||||
|
||||
echo
|
||||
|
@ -274,6 +305,449 @@ if [ "$good" != "`md5sum /tmp/lwsdump | cut -d' ' -f 1`" ] ; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "---- mass testing uri variations"
|
||||
|
||||
rm -f /tmp/results
|
||||
|
||||
for i in \
|
||||
/..../ \
|
||||
/.../. \
|
||||
/...// \
|
||||
/.../a \
|
||||
/.../w \
|
||||
/.../? \
|
||||
/.../% \
|
||||
/../.. \
|
||||
/.././ \
|
||||
/../.a \
|
||||
/../.w \
|
||||
/../.. \
|
||||
/../.% \
|
||||
/..//. \
|
||||
/../// \
|
||||
/..//a \
|
||||
/..//w \
|
||||
/..//? \
|
||||
/..//% \
|
||||
/../a. \
|
||||
/../a/ \
|
||||
/../aa \
|
||||
/../aw \
|
||||
/../a? \
|
||||
/../a% \
|
||||
/../w. \
|
||||
/../w/ \
|
||||
/../wa \
|
||||
/../ww \
|
||||
/../w? \
|
||||
/../w% \
|
||||
/../?. \
|
||||
/../?/ \
|
||||
/../?a \
|
||||
/../?w \
|
||||
/../?? \
|
||||
/../?% \
|
||||
/../%. \
|
||||
/../%/ \
|
||||
/../%a \
|
||||
/../%w \
|
||||
/../%? \
|
||||
/../%% \
|
||||
/./... \
|
||||
/./../ \
|
||||
/./..a \
|
||||
/./..w \
|
||||
/./..? \
|
||||
/./..% \
|
||||
/.//.. \
|
||||
/.a../ \
|
||||
/.a/.. \
|
||||
/.w../ \
|
||||
/.w/.. \
|
||||
/.?../ \
|
||||
/../.. \
|
||||
/.%../ \
|
||||
/.%/.. \
|
||||
//.... \
|
||||
//.../ \
|
||||
//...a \
|
||||
//...w \
|
||||
//...? \
|
||||
//...% \
|
||||
//../. \
|
||||
//..// \
|
||||
//../a \
|
||||
//../w \
|
||||
//../? \
|
||||
//../% \
|
||||
//..a. \
|
||||
//..a/ \
|
||||
//..aa \
|
||||
//..aw \
|
||||
//..a? \
|
||||
//..a% \
|
||||
//..w. \
|
||||
//..w/ \
|
||||
//..wa \
|
||||
//..ww \
|
||||
//..w? \
|
||||
//..w% \
|
||||
//..?. \
|
||||
//..?/ \
|
||||
//..?a \
|
||||
//..?w \
|
||||
//..?? \
|
||||
//..?% \
|
||||
//..%. \
|
||||
//..%/ \
|
||||
//..%a \
|
||||
//..%w \
|
||||
//..%? \
|
||||
//..%% \
|
||||
//./.. \
|
||||
///... \
|
||||
///../ \
|
||||
///..a \
|
||||
///..w \
|
||||
///..? \
|
||||
///..% \
|
||||
////.. \
|
||||
//a../ \
|
||||
//a/.. \
|
||||
//w../ \
|
||||
//w/.. \
|
||||
//?../ \
|
||||
//?/.. \
|
||||
//%../ \
|
||||
//%/.. \
|
||||
/a.../ \
|
||||
/a../. \
|
||||
/a..// \
|
||||
/a../a \
|
||||
/a../w \
|
||||
/a../? \
|
||||
/a../% \
|
||||
/a./.. \
|
||||
/a/... \
|
||||
/a/../ \
|
||||
/a/..a \
|
||||
/a/..w \
|
||||
/a/..? \
|
||||
/a/..% \
|
||||
/a//.. \
|
||||
/aa../ \
|
||||
/aa/.. \
|
||||
/aw../ \
|
||||
/aw/.. \
|
||||
/a?../ \
|
||||
/a?/.. \
|
||||
/a%../ \
|
||||
/a%/.. \
|
||||
/w.../ \
|
||||
/w../. \
|
||||
/w..// \
|
||||
/w../a \
|
||||
/w../w \
|
||||
/w../? \
|
||||
/w../% \
|
||||
/w./.. \
|
||||
/w/... \
|
||||
/w/../ \
|
||||
/w/..a \
|
||||
/w/..w \
|
||||
/w/..? \
|
||||
/w/..% \
|
||||
/w//.. \
|
||||
/wa../ \
|
||||
/wa/.. \
|
||||
/ww../ \
|
||||
/ww/.. \
|
||||
/w?../ \
|
||||
/w?/.. \
|
||||
/w%../ \
|
||||
/w%/.. \
|
||||
/?.../ \
|
||||
/?../. \
|
||||
/?..// \
|
||||
/?../a \
|
||||
/?../w \
|
||||
/?../? \
|
||||
/?../% \
|
||||
/?./.. \
|
||||
/?/... \
|
||||
/?/../ \
|
||||
/?/..a \
|
||||
/?/..w \
|
||||
/?/..? \
|
||||
/?/..% \
|
||||
/?//.. \
|
||||
/?a../ \
|
||||
/?a/.. \
|
||||
/?w../ \
|
||||
/?w/.. \
|
||||
/??../ \
|
||||
/??/.. \
|
||||
/?%../ \
|
||||
/?%/.. \
|
||||
/%.../ \
|
||||
/%../. \
|
||||
/%..// \
|
||||
/%../a \
|
||||
/%../w \
|
||||
/%../? \
|
||||
/%../% \
|
||||
/%./.. \
|
||||
/%/... \
|
||||
/%/../ \
|
||||
/%/..a \
|
||||
/%/..w \
|
||||
/%/..? \
|
||||
/%/..% \
|
||||
/%//.. \
|
||||
/%a../ \
|
||||
/%a/.. \
|
||||
/%w../ \
|
||||
/%w/.. \
|
||||
/%?../ \
|
||||
/%?/.. \
|
||||
/%%../ \
|
||||
/%%/.. \
|
||||
/a/w/../a \
|
||||
/path/to/dir/../other/dir \
|
||||
; do
|
||||
|
||||
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
|
||||
|
||||
|
||||
if [ "$R" != "403" ]; then
|
||||
U=`cat $LOG | grep lws_http_serve | tail -n 1 | cut -d':' -f3 | cut -d' ' -f2`
|
||||
echo $U
|
||||
echo "- \"$i\" -> $R \"$U\"" >>/tmp/results
|
||||
else
|
||||
echo "- \"$i\" -> $R" >>/tmp/results
|
||||
fi
|
||||
done
|
||||
|
||||
cat <<EOF >/tmp/lwsresult1
|
||||
- "/..../" -> 406 "/..../"
|
||||
- "/.../." -> 406 "/.../"
|
||||
- "/...//" -> 406 "/.../"
|
||||
- "/.../a" -> 406 "/.../a"
|
||||
- "/.../w" -> 406 "/.../w"
|
||||
- "/.../?" -> 406 "/.../"
|
||||
- "/.../%" -> 403
|
||||
- "/../.." -> 200 "/"
|
||||
- "/.././" -> 200 "/"
|
||||
- "/../.a" -> 415 "/.a"
|
||||
- "/../.w" -> 415 "/.w"
|
||||
- "/../.." -> 200 "/"
|
||||
- "/../.%" -> 403
|
||||
- "/..//." -> 200 "/"
|
||||
- "/..///" -> 200 "/"
|
||||
- "/..//a" -> 415 "/a"
|
||||
- "/..//w" -> 415 "/w"
|
||||
- "/..//?" -> 200 "/"
|
||||
- "/..//%" -> 403
|
||||
- "/../a." -> 415 "/a."
|
||||
- "/../a/" -> 406 "/a/"
|
||||
- "/../aa" -> 415 "/aa"
|
||||
- "/../aw" -> 415 "/aw"
|
||||
- "/../a?" -> 415 "/a"
|
||||
- "/../a%" -> 403
|
||||
- "/../w." -> 415 "/w."
|
||||
- "/../w/" -> 406 "/w/"
|
||||
- "/../wa" -> 415 "/wa"
|
||||
- "/../ww" -> 415 "/ww"
|
||||
- "/../w?" -> 415 "/w"
|
||||
- "/../w%" -> 403
|
||||
- "/../?." -> 200 "/"
|
||||
- "/../?/" -> 200 "/"
|
||||
- "/../?a" -> 200 "/"
|
||||
- "/../?w" -> 200 "/"
|
||||
- "/../??" -> 200 "/"
|
||||
- "/../?%" -> 403
|
||||
- "/../%." -> 403
|
||||
- "/../%/" -> 403
|
||||
- "/../%a" -> 403
|
||||
- "/../%w" -> 403
|
||||
- "/../%?" -> 403
|
||||
- "/../%%" -> 403
|
||||
- "/./..." -> 415 "/..."
|
||||
- "/./../" -> 200 "/"
|
||||
- "/./..a" -> 415 "/..a"
|
||||
- "/./..w" -> 415 "/..w"
|
||||
- "/./..?" -> 200 "/"
|
||||
- "/./..%" -> 403
|
||||
- "/.//.." -> 200 "/"
|
||||
- "/.a../" -> 406 "/.a../"
|
||||
- "/.a/.." -> 200 "/"
|
||||
- "/.w../" -> 406 "/.w../"
|
||||
- "/.w/.." -> 200 "/"
|
||||
- "/.?../" -> 415 "/."
|
||||
- "/../.." -> 200 "/"
|
||||
- "/.%../" -> 403
|
||||
- "/.%/.." -> 403
|
||||
- "//...." -> 415 "/...."
|
||||
- "//.../" -> 406 "/.../"
|
||||
- "//...a" -> 415 "/...a"
|
||||
- "//...w" -> 415 "/...w"
|
||||
- "//...?" -> 415 "/..."
|
||||
- "//...%" -> 403
|
||||
- "//../." -> 200 "/"
|
||||
- "//..//" -> 200 "/"
|
||||
- "//../a" -> 415 "/a"
|
||||
- "//../w" -> 415 "/w"
|
||||
- "//../?" -> 200 "/"
|
||||
- "//../%" -> 403
|
||||
- "//..a." -> 415 "/..a."
|
||||
- "//..a/" -> 406 "/..a/"
|
||||
- "//..aa" -> 415 "/..aa"
|
||||
- "//..aw" -> 415 "/..aw"
|
||||
- "//..a?" -> 415 "/..a"
|
||||
- "//..a%" -> 403
|
||||
- "//..w." -> 415 "/..w."
|
||||
- "//..w/" -> 406 "/..w/"
|
||||
- "//..wa" -> 415 "/..wa"
|
||||
- "//..ww" -> 415 "/..ww"
|
||||
- "//..w?" -> 415 "/..w"
|
||||
- "//..w%" -> 403
|
||||
- "//..?." -> 200 "/"
|
||||
- "//..?/" -> 200 "/"
|
||||
- "//..?a" -> 415 "/a"
|
||||
- "//..?w" -> 415 "/w"
|
||||
- "//..??" -> 200 "/"
|
||||
- "//..?%" -> 403
|
||||
- "//..%." -> 403
|
||||
- "//..%/" -> 403
|
||||
- "//..%a" -> 403
|
||||
- "//..%w" -> 403
|
||||
- "//..%?" -> 403
|
||||
- "//..%%" -> 403
|
||||
- "//./.." -> 200 "/"
|
||||
- "///..." -> 415 "/..."
|
||||
- "///../" -> 200 "/"
|
||||
- "///..a" -> 415 "/..a"
|
||||
- "///..w" -> 415 "/..w"
|
||||
- "///..?" -> 200 "/"
|
||||
- "///..%" -> 403
|
||||
- "////.." -> 200 "/"
|
||||
- "//a../" -> 406 "/a../"
|
||||
- "//a/.." -> 200 "/"
|
||||
- "//w../" -> 406 "/w../"
|
||||
- "//w/.." -> 200 "/"
|
||||
- "//?../" -> 200 "/"
|
||||
- "//?/.." -> 200 "/"
|
||||
- "//%../" -> 403
|
||||
- "//%/.." -> 403
|
||||
- "/a.../" -> 406 "/a.../"
|
||||
- "/a../." -> 406 "/a../"
|
||||
- "/a..//" -> 406 "/a../"
|
||||
- "/a../a" -> 406 "/a../a"
|
||||
- "/a../w" -> 406 "/a../w"
|
||||
- "/a../?" -> 406 "/a../"
|
||||
- "/a../%" -> 403
|
||||
- "/a./.." -> 200 "/"
|
||||
- "/a/..." -> 406 "/a/..."
|
||||
- "/a/../" -> 200 "/"
|
||||
- "/a/..a" -> 406 "/a/..a"
|
||||
- "/a/..w" -> 406 "/a/..w"
|
||||
- "/a/..?" -> 200 "/"
|
||||
- "/a/..%" -> 403
|
||||
- "/a//.." -> 200 "/"
|
||||
- "/aa../" -> 406 "/aa../"
|
||||
- "/aa/.." -> 200 "/"
|
||||
- "/aw../" -> 406 "/aw../"
|
||||
- "/aw/.." -> 200 "/"
|
||||
- "/a?../" -> 415 "/a"
|
||||
- "/a?/.." -> 415 "/a"
|
||||
- "/a%../" -> 403
|
||||
- "/a%/.." -> 403
|
||||
- "/w.../" -> 406 "/w.../"
|
||||
- "/w../." -> 406 "/w../"
|
||||
- "/w..//" -> 406 "/w../"
|
||||
- "/w../a" -> 406 "/w../a"
|
||||
- "/w../w" -> 406 "/w../w"
|
||||
- "/w../?" -> 406 "/w../"
|
||||
- "/w../%" -> 403
|
||||
- "/w./.." -> 200 "/"
|
||||
- "/w/..." -> 406 "/w/..."
|
||||
- "/w/../" -> 200 "/"
|
||||
- "/w/..a" -> 406 "/w/..a"
|
||||
- "/w/..w" -> 406 "/w/..w"
|
||||
- "/w/..?" -> 200 "/"
|
||||
- "/w/..%" -> 403
|
||||
- "/w//.." -> 200 "/"
|
||||
- "/wa../" -> 406 "/wa../"
|
||||
- "/wa/.." -> 200 "/"
|
||||
- "/ww../" -> 406 "/ww../"
|
||||
- "/ww/.." -> 200 "/"
|
||||
- "/w?../" -> 415 "/w"
|
||||
- "/w?/.." -> 415 "/w"
|
||||
- "/w%../" -> 403
|
||||
- "/w%/.." -> 403
|
||||
- "/?.../" -> 200 "/"
|
||||
- "/?../." -> 200 "/"
|
||||
- "/?..//" -> 200 "/"
|
||||
- "/?../a" -> 200 "/"
|
||||
- "/?../w" -> 200 "/"
|
||||
- "/?../?" -> 200 "/"
|
||||
- "/?../%" -> 403
|
||||
- "/?./.." -> 200 "/"
|
||||
- "/?/..." -> 200 "/"
|
||||
- "/?/../" -> 200 "/"
|
||||
- "/?/..a" -> 200 "/"
|
||||
- "/?/..w" -> 200 "/"
|
||||
- "/?/..?" -> 200 "/"
|
||||
- "/?/..%" -> 403
|
||||
- "/?//.." -> 200 "/"
|
||||
- "/?a../" -> 200 "/"
|
||||
- "/?a/.." -> 200 "/"
|
||||
- "/?w../" -> 200 "/"
|
||||
- "/?w/.." -> 200 "/"
|
||||
- "/??../" -> 200 "/"
|
||||
- "/??/.." -> 200 "/"
|
||||
- "/?%../" -> 403
|
||||
- "/?%/.." -> 403
|
||||
- "/%.../" -> 403
|
||||
- "/%../." -> 403
|
||||
- "/%..//" -> 403
|
||||
- "/%../a" -> 403
|
||||
- "/%../w" -> 403
|
||||
- "/%../?" -> 403
|
||||
- "/%../%" -> 403
|
||||
- "/%./.." -> 403
|
||||
- "/%/..." -> 403
|
||||
- "/%/../" -> 403
|
||||
- "/%/..a" -> 403
|
||||
- "/%/..w" -> 403
|
||||
- "/%/..?" -> 403
|
||||
- "/%/..%" -> 403
|
||||
- "/%//.." -> 403
|
||||
- "/%a../" -> 403
|
||||
- "/%a/.." -> 403
|
||||
- "/%w../" -> 403
|
||||
- "/%w/.." -> 403
|
||||
- "/%?../" -> 403
|
||||
- "/%?/.." -> 403
|
||||
- "/%%../" -> 403
|
||||
- "/%%/.." -> 403
|
||||
- "/a/w/../a" -> 406 "/a/a"
|
||||
- "/path/to/dir/../other/dir" -> 406 "/path/to/other/dir"
|
||||
EOF
|
||||
|
||||
if [ "`md5sum /tmp/results | cut -d' ' -f 1`" != "`md5sum /tmp/lwsresult1 | cut -d' ' -f1`" ] ; then
|
||||
echo "Differences..."
|
||||
diff -urN /tmp/results /tmp/lwsresult1
|
||||
exit 1
|
||||
else
|
||||
echo "OK"
|
||||
fi
|
||||
|
||||
|
||||
echo
|
||||
echo "--- survived OK ---"
|
||||
kill -2 $CPID
|
||||
|
|
|
@ -62,6 +62,10 @@
|
|||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#define MAX_FUZZ_BUF (1024 * 1024)
|
||||
|
||||
enum types {
|
||||
|
@ -953,4 +957,3 @@ bail1:
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
12
test-server/libwebsockets-test-server.service
Normal file
12
test-server/libwebsockets-test-server.service
Normal file
|
@ -0,0 +1,12 @@
|
|||
[Unit]
|
||||
Description=Libwebsockets test server
|
||||
After=syslog.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/libwebsockets-test-server --ssl -C /etc/pki/tls/certs/libwebsockets.org.crt -K /etc/pki/tls/private/libwebsockets.org.key -A /etc/pki/tls/certs/libwebsockets.org.cer --port 7681 -u 99 -g 99 --daemonize
|
||||
Type=forking
|
||||
PIDFile=/tmp/.lwsts-lock
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -206,13 +206,13 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
|
||||
static struct lws_protocols protocols[] = {
|
||||
{
|
||||
"dumb-increment-protocol,fake-nonexistant-protocol",
|
||||
"dumb-increment-protocol",
|
||||
callback_dumb_increment,
|
||||
0,
|
||||
20,
|
||||
},
|
||||
{
|
||||
"fake-nonexistant-protocol,lws-mirror-protocol",
|
||||
"lws-mirror-protocol",
|
||||
callback_lws_mirror,
|
||||
0,
|
||||
128,
|
||||
|
@ -335,7 +335,15 @@ int main(int argc, char **argv)
|
|||
if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
|
||||
use_ssl = 0;
|
||||
if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
|
||||
use_ssl = 1;
|
||||
if (!use_ssl)
|
||||
use_ssl = 1;
|
||||
|
||||
if (use_ssl) {
|
||||
if (use_ssl == 1)
|
||||
lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
|
||||
else
|
||||
lwsl_notice(" Selfsigned certs allowed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* create the websockets context. This tracks open connections and
|
||||
|
|
|
@ -66,6 +66,11 @@ callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
#ifndef LWS_NO_SERVER
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
pss->index = 0;
|
||||
pss->len = -1;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
do_tx:
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ int main(int argc, char **argv)
|
|||
struct lws_client_connect_info i;
|
||||
|
||||
address = argv[optind];
|
||||
snprintf(ads_port, sizeof(ads_port), "%s:%u",
|
||||
lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
|
||||
address, port & 65535);
|
||||
memset(&i, 0, sizeof(i));
|
||||
i.context = context;
|
||||
|
|
|
@ -450,7 +450,7 @@ int main(int argc, char **argv)
|
|||
/* create client websockets using dumb increment protocol */
|
||||
|
||||
address = argv[optind];
|
||||
snprintf(ads_port, sizeof(ads_port), "%s:%u",
|
||||
lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
|
||||
address, port & 65535);
|
||||
lwsl_notice("Connecting to %s...\n", ads_port);
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
|
|
@ -134,9 +134,12 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
struct lws_pollargs *pa = (struct lws_pollargs *)in;
|
||||
#endif
|
||||
|
||||
|
||||
switch (reason) {
|
||||
case LWS_CALLBACK_HTTP:
|
||||
|
||||
lwsl_notice("lws_http_serve: %s\n",in);
|
||||
|
||||
if (debug_level & LLL_INFO) {
|
||||
dump_handshake_info(wsi);
|
||||
|
||||
|
@ -147,6 +150,15 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
lwsl_notice("URI Arg %d: %s\n", ++n, buf);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char name[100], rip[50];
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name,
|
||||
sizeof(name), rip, sizeof(rip));
|
||||
sprintf(buf, "%s (%s)", name, rip);
|
||||
lwsl_notice("HTTP connect from %s\n", buf);
|
||||
}
|
||||
|
||||
if (len < 1) {
|
||||
lws_return_http_status(wsi,
|
||||
HTTP_STATUS_BAD_REQUEST, NULL);
|
||||
|
@ -155,7 +167,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
/* this example server has no concept of directories */
|
||||
if (strchr((const char *)in + 1, '/')) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
|
||||
lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL);
|
||||
goto try_to_reuse;
|
||||
}
|
||||
|
||||
|
@ -259,8 +271,8 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
|
||||
/* demonstrates how to set a cookie on / */
|
||||
|
||||
other_headers = NULL;
|
||||
n = 0;
|
||||
other_headers = leaf_path;
|
||||
p = (unsigned char *)leaf_path;
|
||||
if (!strcmp((const char *)in, "/") &&
|
||||
!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
|
||||
/* this isn't very unguessable but it'll do for us */
|
||||
|
@ -269,16 +281,22 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
|||
(unsigned int)tv.tv_sec,
|
||||
(unsigned int)tv.tv_usec);
|
||||
|
||||
p = (unsigned char *)leaf_path;
|
||||
|
||||
if (lws_add_http_header_by_name(wsi,
|
||||
(unsigned char *)"set-cookie:",
|
||||
(unsigned char *)b64, n, &p,
|
||||
(unsigned char *)leaf_path + sizeof(leaf_path)))
|
||||
return 1;
|
||||
n = (char *)p - leaf_path;
|
||||
other_headers = leaf_path;
|
||||
}
|
||||
if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi,
|
||||
(unsigned char *)
|
||||
"Strict-Transport-Security:",
|
||||
(unsigned char *)
|
||||
"max-age=15768000 ; "
|
||||
"includeSubDomains", 36, &p,
|
||||
(unsigned char *)leaf_path +
|
||||
sizeof(leaf_path)))
|
||||
return 1;
|
||||
n = (char *)p - leaf_path;
|
||||
|
||||
n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n);
|
||||
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
||||
|
|
|
@ -31,7 +31,7 @@ struct lws_plat_file_ops fops_plat;
|
|||
char *resource_path = LOCAL_RESOURCE_PATH;
|
||||
|
||||
/*
|
||||
* libev dumps their hygeine problems on their users blaming compiler
|
||||
* libev dumps their hygiene problems on their users blaming compiler
|
||||
* http://lists.schmorp.de/pipermail/libev/2008q4/000442.html
|
||||
*/
|
||||
|
||||
|
|
|
@ -89,6 +89,12 @@ static struct lws_protocols protocols[] = {
|
|||
sizeof(struct per_session_data__lws_mirror),
|
||||
128,
|
||||
},
|
||||
{
|
||||
"lws-status",
|
||||
callback_lws_status,
|
||||
sizeof(struct per_session_data__lws_status),
|
||||
128,
|
||||
},
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
||||
|
@ -106,7 +112,7 @@ static const struct lws_extension exts[] = {
|
|||
{ NULL, NULL, NULL /* terminator */ }
|
||||
};
|
||||
|
||||
void signal_cb(uv_signal_t *watcher, int revents)
|
||||
void signal_cb(uv_loop_t *loop, uv_signal_t *watcher, int signum)
|
||||
{
|
||||
lwsl_err("Signal %d caught, exiting...\n", watcher->signum);
|
||||
switch (watcher->signum) {
|
||||
|
@ -122,9 +128,8 @@ void signal_cb(uv_signal_t *watcher, int revents)
|
|||
}
|
||||
|
||||
static void
|
||||
uv_timeout_cb(uv_timer_t *w)
|
||||
uv_timeout_cb_dumb_increment(uv_timer_t *w)
|
||||
{
|
||||
lwsl_info("%s\n", __func__);
|
||||
lws_callback_on_writable_all_protocol(context,
|
||||
&protocols[PROTOCOL_DUMB_INCREMENT]);
|
||||
}
|
||||
|
@ -222,7 +227,8 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
|
||||
#if !defined(WIN32)
|
||||
#if !defined(LWS_NO_DAEMONIZE)
|
||||
/*
|
||||
* normally lock path would be /var/lock/lwsts or similar, to
|
||||
* simplify getting started without having to take care about
|
||||
|
@ -237,6 +243,7 @@ int main(int argc, char **argv)
|
|||
/* 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);
|
||||
|
@ -273,6 +280,7 @@ int main(int argc, char **argv)
|
|||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.max_http_header_pool = 1;
|
||||
info.timeout_secs = 5;
|
||||
info.options = opts | LWS_SERVER_OPTION_LIBUV;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
|
@ -281,10 +289,12 @@ int main(int argc, char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
lws_uv_initloop(context, NULL, signal_cb, 0);
|
||||
lws_uv_sigint_cfg(context, 1, signal_cb);
|
||||
|
||||
lws_uv_initloop(context, NULL, NULL, 0);
|
||||
|
||||
uv_timer_init(lws_uv_getloop(context, 0), &timeout_watcher);
|
||||
uv_timer_start(&timeout_watcher, uv_timeout_cb, 50, 50);
|
||||
uv_timer_start(&timeout_watcher, uv_timeout_cb_dumb_increment, 50, 50);
|
||||
|
||||
lws_libuv_run(context, 0);
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ int main(int argc, char **argv)
|
|||
|
||||
/* wait for all the service threads to exit */
|
||||
|
||||
for (n = 0; n < lws_get_count_threads(context); n++)
|
||||
while ((--n) >= 0)
|
||||
pthread_join(pthread_service[n], &retval);
|
||||
|
||||
/* wait for pthread_dumb to exit */
|
||||
|
|
168
test-server/test-server-status.c
Normal file
168
test-server/test-server-status.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* libwebsockets-test-server - libwebsockets test implementation
|
||||
*
|
||||
* 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 "test-server.h"
|
||||
#include <time.h>
|
||||
|
||||
static unsigned char server_info[1024];
|
||||
static int server_info_len;
|
||||
static int current;
|
||||
static char cache[16384];
|
||||
static int cache_len;
|
||||
static struct per_session_data__lws_status *list;
|
||||
static int live_wsi;
|
||||
|
||||
|
||||
static void
|
||||
update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
|
||||
{
|
||||
struct per_session_data__lws_status **pp = &list;
|
||||
int subsequent = 0;
|
||||
char *p = cache + LWS_PRE, *start = p;
|
||||
char date[128];
|
||||
time_t t;
|
||||
struct tm *ptm;
|
||||
#ifndef WIN32
|
||||
struct tm tm;
|
||||
#endif
|
||||
|
||||
p += lws_snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
|
||||
server_info, live_wsi);
|
||||
|
||||
/* render the list */
|
||||
while (*pp) {
|
||||
t = (*pp)->tv_established.tv_sec;
|
||||
#ifdef WIN32
|
||||
ptm = localtime(&t);
|
||||
if (!ptm)
|
||||
#else
|
||||
ptm = &tm;
|
||||
if (!localtime_r(&t, &tm))
|
||||
#endif
|
||||
strcpy(date, "unknown");
|
||||
else
|
||||
#ifdef WIN32
|
||||
strftime(date, sizeof(date), "%Y %H:%M %Z", ptm);
|
||||
#else
|
||||
strftime(date, sizeof(date), "%F %H:%M %Z", ptm);
|
||||
#endif
|
||||
if ((p - start) > (sizeof(cache) - 512))
|
||||
break;
|
||||
if (subsequent)
|
||||
*p++ = ',';
|
||||
subsequent = 1;
|
||||
p += lws_snprintf(p, sizeof(cache) - (p - start) - 1,
|
||||
"{\"peer\":\"%s\",\"time\":\"%s\","
|
||||
"\"ua\":\"%s\"}",
|
||||
(*pp)->ip, date, (*pp)->user_agent);
|
||||
pp = &((*pp)->list);
|
||||
}
|
||||
|
||||
p += sprintf(p, "]}");
|
||||
cache_len = p - start;
|
||||
lwsl_err("cache_len %d\n", cache_len);
|
||||
*p = '\0';
|
||||
|
||||
/* since we changed the list, increment the 'version' */
|
||||
current++;
|
||||
/* update everyone */
|
||||
lws_callback_on_writable_all_protocol(lws_get_context(wsi),
|
||||
lws_get_protocol(wsi));
|
||||
}
|
||||
|
||||
|
||||
/* lws-status protocol */
|
||||
|
||||
int
|
||||
callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct per_session_data__lws_status *pss =
|
||||
(struct per_session_data__lws_status *)user,
|
||||
**pp;
|
||||
char name[128], rip[128];
|
||||
int m;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
||||
/*
|
||||
* Prepare the static server info
|
||||
*/
|
||||
server_info_len = sprintf((char *)server_info,
|
||||
"\"version\":\"%s\","
|
||||
" \"hostname\":\"%s\"",
|
||||
lws_get_library_version(),
|
||||
lws_canonical_hostname(
|
||||
lws_get_context(wsi)));
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_ESTABLISHED:
|
||||
/*
|
||||
* we keep a linked list of live pss, so we can walk it
|
||||
*/
|
||||
pss->last = 0;
|
||||
pss->list = list;
|
||||
list = pss;
|
||||
live_wsi++;
|
||||
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name,
|
||||
sizeof(name), rip, sizeof(rip));
|
||||
sprintf(pss->ip, "%s (%s)", name, rip);
|
||||
gettimeofday(&pss->tv_established, NULL);
|
||||
strcpy(pss->user_agent, "unknown");
|
||||
lws_hdr_copy(wsi, pss->user_agent, sizeof(pss->user_agent),
|
||||
WSI_TOKEN_HTTP_USER_AGENT);
|
||||
update_status(wsi, pss);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||
m = lws_write(wsi, (unsigned char *)cache + LWS_PRE, cache_len,
|
||||
LWS_WRITE_TEXT);
|
||||
if (m < server_info_len) {
|
||||
lwsl_err("ERROR %d writing to di socket\n", m);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLOSED:
|
||||
/*
|
||||
* remove ourselves from live pss list
|
||||
*/
|
||||
lwsl_err("CLOSING pss %p ********\n", pss);
|
||||
|
||||
pp = &list;
|
||||
while (*pp) {
|
||||
if (*pp == pss) {
|
||||
*pp = pss->list;
|
||||
pss->list = NULL;
|
||||
live_wsi--;
|
||||
break;
|
||||
}
|
||||
pp = &((*pp)->list);
|
||||
}
|
||||
|
||||
update_status(wsi, pss);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -68,6 +68,7 @@ enum demo_protocols {
|
|||
PROTOCOL_DUMB_INCREMENT,
|
||||
PROTOCOL_LWS_MIRROR,
|
||||
PROTOCOL_LWS_ECHOGEN,
|
||||
PROTOCOL_LWS_STATUS,
|
||||
|
||||
/* always last */
|
||||
DEMO_PROTOCOL_COUNT
|
||||
|
@ -88,19 +89,25 @@ static struct lws_protocols protocols[] = {
|
|||
"dumb-increment-protocol",
|
||||
callback_dumb_increment,
|
||||
sizeof(struct per_session_data__dumb_increment),
|
||||
10,
|
||||
10, /* rx buf size must be >= permessage-deflate rx size */
|
||||
},
|
||||
{
|
||||
"lws-mirror-protocol",
|
||||
callback_lws_mirror,
|
||||
sizeof(struct per_session_data__lws_mirror),
|
||||
128,
|
||||
128, /* rx buf size must be >= permessage-deflate rx size */
|
||||
},
|
||||
{
|
||||
"lws-echogen",
|
||||
callback_lws_echogen,
|
||||
sizeof(struct per_session_data__echogen),
|
||||
128,
|
||||
128, /* rx buf size must be >= permessage-deflate rx size */
|
||||
},
|
||||
{
|
||||
"lws-status",
|
||||
callback_lws_status,
|
||||
sizeof(struct per_session_data__lws_status),
|
||||
128, /* rx buf size must be >= permessage-deflate rx size */
|
||||
},
|
||||
{ NULL, NULL, 0, 0 } /* terminator */
|
||||
};
|
||||
|
@ -155,6 +162,9 @@ static struct option options[] = {
|
|||
{ "allow-non-ssl", no_argument, NULL, 'a' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "closetest", no_argument, NULL, 'c' },
|
||||
{ "ssl-cert", required_argument, NULL, 'C' },
|
||||
{ "ssl-key", required_argument, NULL, 'K' },
|
||||
{ "ssl-ca", required_argument, NULL, 'A' },
|
||||
{ "libev", no_argument, NULL, 'e' },
|
||||
#ifndef LWS_NO_DAEMONIZE
|
||||
{ "daemonize", no_argument, NULL, 'D' },
|
||||
|
@ -169,8 +179,10 @@ int main(int argc, char **argv)
|
|||
char interface_name[128] = "";
|
||||
unsigned int ms, oldms = 0;
|
||||
const char *iface = NULL;
|
||||
char cert_path[1024];
|
||||
char key_path[1024];
|
||||
char cert_path[1024] = "";
|
||||
char key_path[1024] = "";
|
||||
char ca_path[1024] = "";
|
||||
int uid = -1, gid = -1;
|
||||
int use_ssl = 0;
|
||||
int opts = 0;
|
||||
int n = 0;
|
||||
|
@ -189,7 +201,7 @@ int main(int argc, char **argv)
|
|||
info.port = 7681;
|
||||
|
||||
while (n >= 0) {
|
||||
n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
|
||||
n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:u:g:", options, NULL);
|
||||
if (n < 0)
|
||||
continue;
|
||||
switch (n) {
|
||||
|
@ -204,6 +216,12 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
break;
|
||||
#endif
|
||||
case 'u':
|
||||
uid = atoi(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
gid = atoi(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
debug_level = atoi(optarg);
|
||||
break;
|
||||
|
@ -231,6 +249,15 @@ int main(int argc, char **argv)
|
|||
resource_path = optarg;
|
||||
printf("Setting resource path to \"%s\"\n", resource_path);
|
||||
break;
|
||||
case 'C':
|
||||
strncpy(cert_path, optarg, sizeof cert_path);
|
||||
break;
|
||||
case 'K':
|
||||
strncpy(key_path, optarg, sizeof key_path);
|
||||
break;
|
||||
case 'A':
|
||||
strncpy(ca_path, optarg, sizeof ca_path);
|
||||
break;
|
||||
case 'h':
|
||||
fprintf(stderr, "Usage: test-server "
|
||||
"[--port=<p>] [--ssl] "
|
||||
|
@ -248,7 +275,7 @@ int main(int argc, char **argv)
|
|||
*/
|
||||
if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
|
||||
fprintf(stderr, "Failed to daemonize\n");
|
||||
return 1;
|
||||
return 10;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -287,23 +314,41 @@ int main(int argc, char **argv)
|
|||
lwsl_err("resource path too long\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(cert_path, "%s/libwebsockets-test-server.pem",
|
||||
if (!cert_path[0])
|
||||
sprintf(cert_path, "%s/libwebsockets-test-server.pem",
|
||||
resource_path);
|
||||
if (strlen(resource_path) > sizeof(key_path) - 32) {
|
||||
lwsl_err("resource path too long\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
|
||||
if (!key_path[0])
|
||||
sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
|
||||
resource_path);
|
||||
|
||||
info.ssl_cert_filepath = cert_path;
|
||||
info.ssl_private_key_filepath = key_path;
|
||||
if (ca_path[0])
|
||||
info.ssl_ca_filepath = ca_path;
|
||||
}
|
||||
info.gid = -1;
|
||||
info.uid = -1;
|
||||
info.max_http_header_pool = 1;
|
||||
info.gid = gid;
|
||||
info.uid = uid;
|
||||
info.max_http_header_pool = 16;
|
||||
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8;
|
||||
info.extensions = exts;
|
||||
info.timeout_secs = 5;
|
||||
info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:"
|
||||
"DHE-RSA-AES256-GCM-SHA384:"
|
||||
"ECDHE-RSA-AES256-SHA384:"
|
||||
"HIGH:!aNULL:!eNULL:!EXPORT:"
|
||||
"!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
|
||||
"!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
|
||||
"!DHE-RSA-AES128-SHA256:"
|
||||
"!AES128-GCM-SHA256:"
|
||||
"!AES128-SHA256:"
|
||||
"!DHE-RSA-AES256-SHA256:"
|
||||
"!AES256-GCM-SHA384:"
|
||||
"!AES256-SHA256";
|
||||
context = lws_create_context(&info);
|
||||
if (context == NULL) {
|
||||
lwsl_err("libwebsocket init failed\n");
|
||||
|
|
|
@ -94,6 +94,16 @@ struct per_session_data__echogen {
|
|||
int wr;
|
||||
};
|
||||
|
||||
struct per_session_data__lws_status {
|
||||
struct per_session_data__lws_status *list;
|
||||
struct timeval tv_established;
|
||||
int last;
|
||||
char ip[270];
|
||||
char user_agent[512];
|
||||
const char *pos;
|
||||
int len;
|
||||
};
|
||||
|
||||
extern int
|
||||
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
|
||||
void *in, size_t len);
|
||||
|
@ -106,6 +116,10 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
extern int
|
||||
callback_lws_echogen(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
extern int
|
||||
callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len);
|
||||
|
||||
|
||||
extern void
|
||||
dump_handshake_info(struct lws *wsi);
|
||||
|
|
|
@ -4,12 +4,76 @@
|
|||
<meta charset=utf-8 http-equiv="Content-Language" content="en"/>
|
||||
<title>Minimal Websocket test app</title>
|
||||
<style type="text/css">
|
||||
div.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
|
||||
span.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
|
||||
.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
|
||||
.group2 { width:600px; vertical-align:middle; text-align:center; background:#f0f0e0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
|
||||
.explain { vertical-align:middle; text-align:center; background:#f0f0c0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; color:#404000; }
|
||||
.group2 { vertical-align:middle;
|
||||
text-align:center;
|
||||
background:#f0f0e0;
|
||||
padding:12px;
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px; }
|
||||
.explain { vertical-align:middle;
|
||||
text-align:center;
|
||||
background:#f0f0c0; padding:12px;
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
color:#404000; }
|
||||
td.wsstatus { vertical-align:middle; width:200px; height:50px;
|
||||
text-align:center;
|
||||
background:#f0f0c0; padding:6px;
|
||||
-webkit-border-radius:8px;
|
||||
-moz-border-radius:8px;
|
||||
border-radius:8px;
|
||||
color:#404000; }
|
||||
td.l { vertical-align:middle;
|
||||
text-align:center;
|
||||
background:#d0d0b0;
|
||||
padding:3px;
|
||||
-webkit-border-radius:3px;
|
||||
-moz-border-radius:3px;
|
||||
border-radius:3px; }
|
||||
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
|
||||
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
|
||||
.tabs {
|
||||
position: relative;
|
||||
min-height: 750px; /* This part sucks */
|
||||
clear: both;
|
||||
margin: 25px 0;
|
||||
}
|
||||
.tab {
|
||||
float: left;
|
||||
}
|
||||
.tab label {
|
||||
background: #eee;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -1px;
|
||||
position: relative;
|
||||
left: 1px;
|
||||
}
|
||||
.tab [type=radio] {
|
||||
display: none;
|
||||
}
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
left: 0;
|
||||
background: white;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
[type=radio]:checked ~ label {
|
||||
background: white;
|
||||
border-bottom: 1px solid white;
|
||||
z-index: 2;
|
||||
}
|
||||
[type=radio]:checked ~ label ~ .content {
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
@ -19,33 +83,76 @@
|
|||
|
||||
<table><tr><td>
|
||||
|
||||
<table width="100%"><tr><td valign=middle align=center><a href="http://libwebsockets.org"><img src="/libwebsockets.org-logo.png"></a></td><td>
|
||||
<section class="browser">Detected Browser: <div id=brow>...</div></section></td></tr></table>
|
||||
<table width=600px>
|
||||
<tr>
|
||||
<td valign=middle align=center>
|
||||
<a href="https://libwebsockets.org">
|
||||
<img src="/libwebsockets.org-logo.png"></a></td><td>
|
||||
<section class="browser">Detected Browser:
|
||||
<div id=brow>...</div></section>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</td></tr><tr><td>
|
||||
|
||||
<section id="increment" class="group2">
|
||||
<div class="title">libwebsockets "dumb-increment-protocol"</div>
|
||||
<table><tr><td>
|
||||
<table class="content" width="200px">
|
||||
<tr><td align=center><input type=button id=offset value="Reset counter" onclick="reset();" ></td></tr>
|
||||
<tr><td width=200px align=center><div id=number> </div></td></tr>
|
||||
<tr><td id=wsdi_statustd align=center class="explain"><div id=wsdi_status>Not initialized</div></td></tr>
|
||||
</tr>
|
||||
</table>
|
||||
</td><td class="explain">
|
||||
The incrementing number is coming from the server and is individual for
|
||||
</td></tr>
|
||||
<tr><td colspan=2 align=center>
|
||||
Click <a href="/leaf.jpg" target="_blank">Here</a> to
|
||||
have the test server send a big picture by http.
|
||||
</td></tr>
|
||||
<tr><td colspan=2>
|
||||
<div class="tabs">
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-1" name="tab-group-1" checked>
|
||||
<label for="tab-1">Dumb Increment Demo</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="dumb" class="group2">
|
||||
<table>
|
||||
<tr>
|
||||
<td id=wsdi_statustd align=center class="wsstatus">
|
||||
<span id=wsdi_status>Websocket connection not initialized</span></td>
|
||||
<td><span class="title">dumb increment-protocol</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="explain" colspan=2>
|
||||
The incrementing number is coming from the server at 20Hz and is individual for
|
||||
each connection to the server... try opening a second browser window.
|
||||
<br/><br/>
|
||||
The button zeros just this connection's number.
|
||||
<br/><br/>
|
||||
Click <a href="/leaf.jpg" target="_blank">Here</a> to have the test server send a big picture by http.
|
||||
</td></tr></table>
|
||||
</section>
|
||||
<br>
|
||||
<section id="mirror" class="group2">
|
||||
<div class="title">libwebsockets "lws-mirror-protocol"</div>
|
||||
<div class="explain">
|
||||
The button sends a message over the websocket link to ask the server
|
||||
to zero just this connection's number.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align=center><div id=number style="font-size:120%;"> </div></td>
|
||||
<td align=center>
|
||||
<input type=button id=offset value="Reset counter" onclick="reset();" >
|
||||
<input type=button id=junk value="Send junk" onclick="junk();" >
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-2" name="tab-group-1">
|
||||
<label for="tab-2">Mirror Demo</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="mirror" class="group2">
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan=1 id=wslm_statustd align=center class="wsstatus">
|
||||
<span id=wslm_status>Websocket connection not initialized</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="title">lws-mirror-protocol</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>
|
||||
<div class="explain">
|
||||
Use the mouse to draw on the canvas below -- all other browser windows open
|
||||
on this page see your drawing in realtime and you can see any of theirs as
|
||||
well.
|
||||
|
@ -56,57 +163,118 @@ protocol, including the guy who sent the packet.
|
|||
<br/><br/>
|
||||
<b>libwebsockets-test-client</b> joins in by spamming circles on to this shared canvas when
|
||||
run.
|
||||
</div>
|
||||
<table class="content">
|
||||
<tr>
|
||||
<td>Drawing color:
|
||||
<select id="color" onchange="update_color();">
|
||||
<option value=#000000>Black</option>
|
||||
<option value=#0000ff>Blue</option>
|
||||
<option value=#20ff20>Green</option>
|
||||
<option value=#802020>Dark Red</option>
|
||||
</select>
|
||||
</td>
|
||||
<td id=wslm_statustd align=center class="explain"><div id=wslm_status>Not initialized</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2 width=500 class="content">
|
||||
<div id="wslm_drawing">
|
||||
</div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2>Drawing color:
|
||||
<select id="color" onchange="update_color();">
|
||||
<option value=#000000>Black</option>
|
||||
<option value=#0000ff>Blue</option>
|
||||
<option value=#20ff20>Green</option>
|
||||
<option value=#802020>Dark Red</option>
|
||||
</select>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan=2 width=500 height=320>
|
||||
<div id="wslm_drawing" style="background:white"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-3" name="tab-group-1">
|
||||
<label for="tab-3">Close Testing</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="ot" class="group2">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
<section id="ot" class="group2">
|
||||
<div class="title">libwebsockets "open and close testing"</div>
|
||||
<table><tr><td>
|
||||
<table class="content" width="200px">
|
||||
</td></tr>
|
||||
<tr><td id=ot_statustd align=center class="wsstatus">
|
||||
<span id=ot_status>Websocket connection not initialized</span>
|
||||
</td>
|
||||
<td colspan=2><span class="title">Open and close testing</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="explain" colspan=3 style="padding:3">
|
||||
To help with open and close testing, you can open and close a connection by hand using
|
||||
the buttons.<br>
|
||||
"<b>Close</b>" closes the connection from the browser with code 3000
|
||||
and reason 'Bye!".<br>
|
||||
"<b>Request Server Close</b>" sends a message asking the server to
|
||||
initiate the close, which it does with code 1001 and reason "Seeya".
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align=center><input type=button id=ot_open_btn value="Open" onclick="ot_open();" ></td>
|
||||
<td align=center><input type=button id=ot_close_btn disabled value="Close" onclick="ot_close();" ></td>
|
||||
<td align=center><input type=button id=ot_req_close_btn disabled value="Request Server Close" onclick="ot_req_close();" ></td>
|
||||
</tr>
|
||||
<tr><td colspan="3" id=ot_statustd align=center class="explain"><div id=ot_status>Not initialized</div></td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input type="radio" id="tab-4" name="tab-group-1">
|
||||
<label for="tab-4">Server info</label>
|
||||
|
||||
<div class="content">
|
||||
<div id="ot" class="group2">
|
||||
<table>
|
||||
<tr>
|
||||
<td id=s_statustd align=center class="wsstatus">
|
||||
<div id=s_status>Websocket connection not initialized</div>
|
||||
</td>
|
||||
<td colspan=1>
|
||||
<span class="title">Server Info</span>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td class="explain" colspan=2>
|
||||
This information is sent by the server over a ws[s] link and updated live
|
||||
whenever the information changes server-side.
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align=center colspan=2><div id=servinfo></</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align=center colspan=2><div id=conninfo style="border : solid 2px #e0d040; padding : 4px; width : 500px; height : 350px; overflow : auto; "></</div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td><td class="explain">
|
||||
To help with open and close testing, you can open and close a connection by hand using
|
||||
the buttons. "Request Server Close" sends a message asking the server to
|
||||
initiate the close.
|
||||
</td></tr></table>
|
||||
</section>
|
||||
<br>
|
||||
|
||||
</td></tr><tr><td>
|
||||
Looking for support? <a href="http://libwebsockets.org">http://libwebsockets.org</a><br/>
|
||||
Join the mailing list: <a href="http://ml.libwebsockets.org/mailman/listinfo/libwebsockets">http://ml.libwebsockets.org/mailman/listinfo/libwebsockets</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</td></tr></table>
|
||||
|
||||
Looking for support? <a href="https://libwebsockets.org">https://libwebsockets.org</a>, <a href="https://github.com/warmcat/libwebsockets">https://github.com/warmcat/libwebsockets</a></a><br/>
|
||||
Join the mailing list: <a href="https://libwebsockets.org/mailman/listinfo/libwebsockets">https://libwebsockets.org/mailman/listinfo/libwebsockets</a>
|
||||
|
||||
</article>
|
||||
|
||||
<script>
|
||||
|
||||
/*
|
||||
* We display untrusted stuff in html context... reject anything
|
||||
* that has HTML stuff in it
|
||||
*/
|
||||
|
||||
function san(s)
|
||||
{
|
||||
if (s.search("<") != -1)
|
||||
return "invalid string";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/* BrowserDetect came from http://www.quirksmode.org/js/detect.html */
|
||||
|
||||
var BrowserDetect = {
|
||||
|
@ -277,7 +445,9 @@ document.getElementById("number").textContent = get_appropriate_ws_url();
|
|||
try {
|
||||
socket_di.onopen = function() {
|
||||
document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("wsdi_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
document.getElementById("wsdi_status").innerHTML =
|
||||
" <b>websocket connection opened</b><br>" +
|
||||
san(socket_di.extensions);
|
||||
}
|
||||
|
||||
socket_di.onmessage =function got_packet(msg) {
|
||||
|
@ -291,11 +461,66 @@ document.getElementById("number").textContent = get_appropriate_ws_url();
|
|||
} catch(exception) {
|
||||
alert('<p>Error' + exception);
|
||||
}
|
||||
|
||||
var socket_status, jso, s;
|
||||
|
||||
if (typeof MozWebSocket != "undefined") {
|
||||
socket_status = new MozWebSocket(get_appropriate_ws_url(),
|
||||
"lws-status");
|
||||
} else {
|
||||
socket_status = new WebSocket(get_appropriate_ws_url(),
|
||||
"lws-status");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
socket_status.onopen = function() {
|
||||
document.getElementById("s_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("s_status").innerHTML =
|
||||
" <b>websocket connection opened</b><br>" +
|
||||
san(socket_status.extensions);
|
||||
}
|
||||
|
||||
socket_status.onmessage =function got_packet(msg) {
|
||||
jso = JSON.parse(msg.data);
|
||||
|
||||
document.getElementById("servinfo").innerHTML =
|
||||
"<table><tr><td class=l>Build info</td><td>"+
|
||||
san(jso.version) + "</td></tr>" +
|
||||
"<tr><td class=l>Server info</td><td>" +
|
||||
san(jso.hostname) + "</td></tr>" +
|
||||
"</table>";
|
||||
s="<table>";
|
||||
var n;
|
||||
for (n = 0; n < jso.conns.length; n++)
|
||||
s = s + "<tr><td class=l>client " + (n + 1) +
|
||||
"</td><td><b>" + san(jso.conns[n].peer) +
|
||||
"</b><br>" + san(jso.conns[n].time) +
|
||||
"<br>" + san(jso.conns[n].ua) +
|
||||
"</td></tr>";
|
||||
s = s + "</table>";
|
||||
|
||||
document.getElementById("conninfo").innerHTML = s;
|
||||
}
|
||||
|
||||
socket_status.onclose = function(){
|
||||
document.getElementById("s_statustd").style.backgroundColor = "#ff4040";
|
||||
document.getElementById("s_status").textContent = " websocket connection CLOSED ";
|
||||
}
|
||||
} catch(exception) {
|
||||
alert('<p>Error' + exception);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
socket_di.send("reset\n");
|
||||
}
|
||||
|
||||
|
||||
function junk() {
|
||||
for(var word = ''; word.length < 9000; word += 'a'){}
|
||||
socket_di.send(word);
|
||||
}
|
||||
|
||||
var socket_ot;
|
||||
|
||||
function ot_open() {
|
||||
|
@ -309,7 +534,7 @@ function ot_open() {
|
|||
try {
|
||||
socket_ot.onopen = function() {
|
||||
document.getElementById("ot_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("ot_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
document.getElementById("ot_status").innerHTML = " <b>websocket connection opened</b><br>" + san(socket_di.extensions);
|
||||
document.getElementById("ot_open_btn").disabled = true;
|
||||
document.getElementById("ot_close_btn").disabled = false;
|
||||
document.getElementById("ot_req_close_btn").disabled = false;
|
||||
|
@ -359,7 +584,9 @@ function ot_req_close() {
|
|||
try {
|
||||
socket_lm.onopen = function() {
|
||||
document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40";
|
||||
document.getElementById("wslm_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
|
||||
document.getElementById("wslm_status").innerHTML =
|
||||
" <b>websocket connection opened</b><br>" +
|
||||
san(socket_lm.extensions);
|
||||
}
|
||||
|
||||
socket_lm.onmessage =function got_packet(msg) {
|
||||
|
|
|
@ -69,7 +69,7 @@ Section "Files" SecInstall
|
|||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "DisplayName" "libwebsockets library and clients"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\""
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "QuietUninstallString" "$\"$INSTDIR\Uninstall.exe$\" /S"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "HelpLink" "http://libwebsockets.org/"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "HelpLink" "https://libwebsockets.org/"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "URLInfoAbout" "http://libwebsockets.org/"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "DisplayVersion" "${VERSION}"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\libwebsockets" "NoModify" "1"
|
||||
|
|
Loading…
Add table
Reference in a new issue