Compare commits

..

No commits in common. "master" and "fix_readme_format" have entirely different histories.

198 changed files with 13023 additions and 55159 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
#Ignore build files
Makefile
config.h
config.log
config.status
@ -22,7 +23,6 @@ win32port/zlib/Release*/
*.suo
*.su
*.m4
*.a
missing
depcomp
install-sh
@ -37,4 +37,3 @@ config.sub
ar-lib
libwebsockets.pc
build/
*.swp

View file

@ -2,36 +2,30 @@ env:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
global:
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
- secure: "amfzN1OzBBZYPJVx8TCYsV1nQ5SPm7QswgGpuHcNKaMAixn1s4tKliR0wyVs1aiMqKco1zrJ3vXll+D5gknKr5obWOeZ61T3PYyZmhjB0n/D+7Y41EikNa1Hn1pP6etcHh3ciJ0qe8FC+9YB5yEII3G/jHsltge8Nu+5o2YCSCw="
matrix:
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON"
- LWS_METHOD=default
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON"
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON"
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON"
- LWS_METHOD=libev CMAKE_ARGS="-DLWS_WITH_LIBEV=ON"
- LWS_METHOD=noipv6 CMAKE_ARGS="-DLWS_IPV6=OFF"
- LWS_METHOD=http2 CMAKE_ARGS="-DLWS_WITH_HTTP2=ON"
- LWS_METHOD=nossl CMAKE_ARGS="-DLWS_WITH_SSL=OFF"
- LWS_METHOD=nodaemon CMAKE_ARGS="-DLWS_WITHOUT_DAEMONIZE=ON"
- LWS_METHOD=cgi CMAKE_ARGS="-DLWS_WITH_CGI=ON"
- LWS_METHOD=nologs CMAKE_ARGS="-DLWS_WITH_NO_LOGS=ON"
os:
- linux
- osx
language: generic
language: c
install:
- ./travis_install.sh
script:
- if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "osx" ]; then mkdir build && cd build && cmake -DOPENSSL_ROOT_DIR="/usr/local/opt/openssl" $CMAKE_ARGS .. && cmake --build .; else if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "linux" ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi ; fi
sudo: required
dist: trusty
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi
addons:
coverity_scan:
project:
name: "warmcat/libwebsockets"
notification_email: andy@warmcat.com
notification_email: andy.green@linaro.org
build_command_prepend: "mkdir build && cd build && cmake .."
build_command: "cmake --build ."
branch_pattern: coverity_scan

File diff suppressed because it is too large Load diff

32
Kconfig
View file

@ -1,32 +0,0 @@
menu "Libwebsockets"
config LWS_MODEL_NAME
string "Model name of device firmware is for"
default "lws"
config LWS_IS_FACTORY_APPLICATION
bool "Is this application is designed for the FACTORY flash slot"
default "n"
config LWS_OTA_SERVER_FQDN
depends on LWS_IS_FACTORY_APPLICATION
string "Domain name of OTA update server, eg, warmcat.com"
default ""
config LWS_OTA_SERVER_BASE_URL
depends on LWS_IS_FACTORY_APPLICATION
string "Base URL on OTA update server, eg, /esp32-ota (model is added)"
default "/esp32-ota"
config LWS_OTA_SERVER_UPLOAD_USER
depends on LWS_IS_FACTORY_APPLICATION
string "User to scp to upload server with"
default "root"
config LWS_OTA_SERVER_UPLOAD_PATH
depends on LWS_IS_FACTORY_APPLICATION
string "Path served in upload server (eg, \"/var/www/libwebsockets.org\""
default "/var/www/libwebsockets.org"
endmenu

34
LICENSE
View file

@ -1,13 +1,7 @@
Libwebsockets and included programs are provided under the terms of the GNU
Library General Public License (LGPL) 2.1, with the following exceptions:
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
1) 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.
@ -16,7 +10,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.
3) You do not have to provide a copy of the libwebsockets license with
2) 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.
@ -26,30 +20,8 @@ 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
(https://libwebsockets.org)"
(http://libwebsockets.org)"
4) Some sources included have their own, more liberal licenses, or options
to get original sources with the liberal terms.
Original liberal license retained
- lib/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h)
Relicensed to libwebsocket license
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version
Public Domain (CC-zero) to simplify reuse
- test-server/*.c
- test-server/*.h
- lwsws/*
------ end of exceptions
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

View file

@ -1,8 +1,5 @@
Notes about building lws
========================
@section cm Introduction to CMake
Introduction to CMake
---------------------
CMake is a multi-platform build tool that can generate build files for many
different target platforms. See more info at http://www.cmake.org
@ -13,7 +10,7 @@ create elaborate clean scripts to get a clean source tree, instead you
simply remove your build directory.
Libwebsockets has been tested to build successfully on the following platforms
with SSL support (for OpenSSL/wolfSSL/BoringSSL):
with SSL support (both OpenSSL/wolfSSL):
- Windows (Visual Studio)
- Windows (MinGW)
@ -21,8 +18,8 @@ with SSL support (for OpenSSL/wolfSSL/BoringSSL):
- OSX
- NetBSD
@section build1 Building the library and test apps
Building the library and test apps
----------------------------------
The project settings used by CMake to generate the platform specific build
files is called [CMakeLists.txt](CMakeLists.txt). CMake then uses one of its "Generators" to
@ -32,8 +29,8 @@ the available generators for your platform, simply run the "cmake" command.
Note that by default OpenSSL will be linked, if you don't want SSL support
see below on how to toggle compile options.
@section bu Building on Unix:
Building on Unix:
-----------------
1. Install CMake 2.8 or greater: http://cmake.org/cmake/resources/software.html
(Most Unix distributions comes with a packaged version also)
@ -41,109 +38,69 @@ see below on how to toggle compile options.
2. Install OpenSSL.
3. Generate the build files (default is Make files):
```
$ cd /path/to/src
$ mkdir build
$ cd build
$ cmake ..
```
4. Finally you can build using the generated Makefile:
```
$ make && sudo make install
```
**NOTE**: The `build/`` directory can have any name and be located anywhere
on your filesystem, and that the argument `..` given to cmake is simply
the source directory of **libwebsockets** containing the [CMakeLists.txt](CMakeLists.txt)
project file. All examples in this file assumes you use ".."
```bash
$ cd /path/to/src
$ mkdir build
$ cd build
$ cmake ..
```
**NOTE2**:
A common option you may want to give is to set the install path, same
as --prefix= with autotools. It defaults to /usr/local.
You can do this by, eg
```
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .
```
(**NOTE**: The `build/`` directory can have any name and be located anywhere
on your filesystem, and that the argument `..` given to cmake is simply
the source directory of **libwebsockets** containing the [CMakeLists.txt](CMakeLists.txt)
project file. All examples in this file assumes you use "..")
**NOTE3**:
On machines that want libraries in lib64, you can also add the
following to the cmake line
```
**NOTE2**:
A common option you may want to give is to set the install path, same
as --prefix= with autotools. It defaults to /usr/local.
You can do this by, eg
```bash
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ..
```
**NOTE3**:
On machines that want libraries in lib64, you can also add the
following to the cmake line
```bash
-DLIB_SUFFIX=64
```
```
**NOTE4**:
If you are building against a non-distro OpenSSL (eg, in order to get
access to ALPN support only in newer OpenSSL versions) the nice way to
express that in one cmake command is eg,
```
**NOTE4**:
If you are building against a non-distro OpenSSL (eg, in order to get
access to ALPN support only in newer OpenSSL versions) the nice way to
express that in one cmake command is eg,
```bash
$ cmake .. -DOPENSSL_ROOT_DIR=/usr/local/ssl \
-DCMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE=/usr/local/ssl \
-DLWS_WITH_HTTP2=1
```
```
When you run the test apps using non-distro SSL, you have to force them
to use your libs, not the distro ones
```
When you run the test apps using non-distro SSL, you have to force them
to use your libs, not the distro ones
```bash
$ LD_LIBRARY_PATH=/usr/local/ssl/lib libwebsockets-test-server --ssl
```
To get it to build on latest openssl (2016-04-10) it needed this approach
```
cmake .. -DLWS_WITH_HTTP2=1 -DLWS_OPENSSL_INCLUDE_DIRS=/usr/local/include/openssl -DLWS_OPENSSL_LIBRARIES="/usr/local/lib64/libssl.so;/usr/local/lib64/libcrypto.so"
```
Mac users have reported
```
$ export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2k; cmake ..; make -j4
```
worked for them when using "homebrew" OpenSSL
**NOTE5**:
To build with debug info and _DEBUG for lower priority debug messages
compiled in, use
```
$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
```
**NOTE6**
To build on Solaris the linker needs to be informed to use lib socket
and libnsl, and only builds in 64bit mode.
```bash
$ cmake .. -DCMAKE_C_FLAGS=-m64 -DCMAKE_EXE_LINKER_FLAGS="-lsocket -lnsl"
```
```
4. Finally you can build using the generated Makefile:
```bash
```bash
$ make
```
```
@section lcap Linux Capabilities
On Linux, lws now lets you retain selected root capabilities when dropping
privileges. If libcap-dev or similar package is installed providing
sys/capabilities.h, and libcap or similar package is installed providing
libcap.so, CMake will enable the capability features.
The context creation info struct .caps[] and .count_caps members can then
be set by user code to enable selected root capabilities to survive the
transition to running under an unprivileged user.
@section cmq Quirk of cmake
Quirk of cmake
--------------
When changing cmake options, for some reason the only way to get it to see the
changes sometimes is delete the contents of your build directory and do the
cmake from scratch.
deleting build/CMakeCache.txt may be enough.
@section cmw Building on Windows (Visual Studio)
Building on Windows (Visual Studio)
-----------------------------------
1. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
2. Install OpenSSL binaries. http://www.openssl.org/related/binaries.html
@ -156,12 +113,12 @@ deleting build/CMakeCache.txt may be enough.
3. Generate the Visual studio project by opening the Visual Studio cmd prompt:
```
cd <path to src>
md build
cd build
cmake -G "Visual Studio 10" ..
```
```bash
cd <path to src>
md build
cd build
cmake -G "Visual Studio 10" ..
```
(**NOTE**: There is also a cmake-gui available on Windows if you prefer that)
@ -172,49 +129,35 @@ deleting build/CMakeCache.txt may be enough.
4. Now you should have a generated Visual Studio Solution in your
`<path to src>/build` directory, which can be used to build.
5. Some additional deps may be needed
- iphlpapi.lib
- psapi.lib
- userenv.lib
6. If you're using libuv, you must make sure to compile libuv with the same multithread-dll / Mtd attributes as libwebsockets itself
@section cmwmgw Building on Windows (MinGW)
Building on Windows (MinGW)
---------------------------
1. Install MinGW: http://sourceforge.net/projects/mingw/files
(**NOTE**: Preferably in the default location C:\MinGW)
2. Fix up MinGW headers
a) If still necessary, sdd the following lines to C:\MinGW\include\winsock2.h:
```
#if(_WIN32_WINNT >= 0x0600)
a) Add the following lines to C:\MinGW\include\winsock2.h:
```c
#if(_WIN32_WINNT >= 0x0600)
typedef struct pollfd {
typedef struct pollfd {
SOCKET fd;
SHORT events;
SHORT revents;
SOCKET fd;
SHORT events;
SHORT revents;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
#endif // (_WIN32_WINNT >= 0x0600)
```
Update crtdefs.h line 47 to say:
```
typedef __int64 ssize_t;
```
#endif // (_WIN32_WINNT >= 0x0600)
```
b) Create C:\MinGW\include\mstcpip.h and copy and paste the content from following link into it:
https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/include/mstcpip.h
http://wine-unstable.sourcearchive.com/documentation/1.1.32/mstcpip_8h-source.html
3. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
@ -227,12 +170,14 @@ deleting build/CMakeCache.txt may be enough.
<OpenSSL install location>\bin\openssl.cfg
5. Generate the build files (default is Make files) using MSYS shell:
```
$ cd /drive/path/to/src
$ mkdir build
$ cd build
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW ..
```
```bash
$ cd /drive/path/to/src
$ mkdir build
$ cd build
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW ..
```
(**NOTE**: The `build/`` directory can have any name and be located anywhere
on your filesystem, and that the argument `..` given to cmake is simply
the source directory of **libwebsockets** containing the [CMakeLists.txt](CMakeLists.txt)
@ -241,35 +186,27 @@ deleting build/CMakeCache.txt may be enough.
**NOTE2**:
To generate build files allowing to create libwebsockets binaries with debug information
set the CMAKE_BUILD_TYPE flag to DEBUG:
```
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW -DCMAKE_BUILD_TYPE=DEBUG ..
```
```bash
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW -DCMAKE_BUILD_TYPE=DEBUG ..
```
6. Finally you can build using the generated Makefile and get the results deployed into your MinGW installation:
```
$ make
$ make install
```
```bash
$ make
$ make install
```
@section optee Building for OP-TEE
OP-TEE is a "Secure World" Trusted Execution Environment.
Although lws is only part of the necessary picture to have an https-enabled
TA, it does support OP-TEE as a platform and if you provide the other
pieces, does work very well.
Select it in cmake with `-DLWS_PLAT_OPTEE=1`
@section cmco Setting compile options
Setting compile options
-----------------------
To set compile time flags you can either use one of the CMake gui applications
or do it via the command line.
or do it via command line.
@subsection cmcocl Command line
To list available options (omit the H if you don't want the help text):
Command line
------------
To list avaialable options (ommit the H if you don't want the help text):
cmake -LH ..
@ -279,21 +216,20 @@ Then to set an option and build (for example turn off SSL support):
or
cmake -DLWS_WITH_SSL:BOOL=OFF ..
@subsection cmcoug Unix GUI
Unix GUI
--------
If you have a curses-enabled build you simply type:
(not all packages include this, my debian install does not for example).
ccmake
@subsection cmcowg Windows GUI
Windows GUI
-----------
On windows CMake comes with a gui application:
Start -> Programs -> CMake -> CMake (cmake-gui)
@section wolf wolfSSL/CyaSSL replacement for OpenSSL
wolfSSL/CyaSSL replacement for OpenSSL
--------------------------------------
wolfSSL/CyaSSL is a lightweight SSL library targeted at embedded systems:
https://www.wolfssl.com/wolfSSL/Products-wolfssl.html
@ -303,102 +239,57 @@ much link to it instead of OpenSSL, giving a much smaller footprint.
**NOTE**: wolfssl needs to be compiled using the `--enable-opensslextra` flag for
this to work.
@section wolf1 Compiling libwebsockets with wolfSSL
Compiling libwebsockets with wolfSSL
------------------------------------
```
cmake .. -DLWS_USE_WOLFSSL=1 \
-DLWS_WOLFSSL_INCLUDE_DIRS=/path/to/wolfssl \
-DLWS_WOLFSSL_LIBRARIES=/path/to/wolfssl/wolfssl.a ..
```bash
cmake .. -DLWS_USE_WOLFSSL=1 \
-DLWS_WOLFSSL_INCLUDE_DIRS=/path/to/wolfssl \
-DLWS_WOLFSSL_LIBRARIES=/path/to/wolfssl/wolfssl.a ..
```
**NOTE**: On windows use the .lib file extension for `LWS_WOLFSSL_LIBRARIES` instead.
@section cya Compiling libwebsockets with CyaSSL
Compiling libwebsockets with CyaSSL
-----------------------------------
```
cmake .. -DLWS_USE_CYASSL=1 \
-DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \
-DLWS_CYASSL_LIBRARIES=/path/to/wolfssl/cyassl.a ..
```bash
cmake .. -DLWS_USE_CYASSL=1 \
-DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \
-DLWS_CYASSL_LIBRARIES=/path/to/wolfssl/cyassl.a ..
```
**NOTE**: On windows use the .lib file extension for `LWS_CYASSL_LIBRARIES` instead.
@section esp32 Building for ESP32
Step 1, get ESP-IDF with lws integrated as a component
```
$ git clone --int --recursive https://github.com/lws-team/lws-esp-idf
```
Step 2: Get Application including the test plugins
```
$ git clone https://github.com/lws-team/lws-esp32
```
Set your IDF_PATH to point to the esp-idf you downloaded in 1)
There's docs for how to build the lws-esp32 test app and reproduce it in the README.md here
https://github.com/lws-team/lws-esp32/blob/master/README.md
@section extplugins Building plugins outside of lws itself
The directory ./plugin-standalone/ shows how easy it is to create plugins
outside of lws itself. First build lws itself with -DLWS_WITH_PLUGINS,
then use the same flow to build the standalone plugin
```
cd ./plugin-standalone
mkdir build
cd build
cmake ..
make && sudo make install
```
if you changed the default plugin directory when you built lws, you must
also give the same arguments to cmake here (eg,
` -DCMAKE_INSTALL_PREFIX:PATH=/usr/something/else...` )
Otherwise if you run lwsws or libwebsockets-test-server-v2.0, it will now
find the additional plugin "libprotocol_example_standalone.so"
```
lwsts[21257]: Plugins:
lwsts[21257]: libprotocol_dumb_increment.so
lwsts[21257]: libprotocol_example_standalone.so
lwsts[21257]: libprotocol_lws_mirror.so
lwsts[21257]: libprotocol_lws_server_status.so
lwsts[21257]: libprotocol_lws_status.so
```
If you have multiple vhosts, you must enable plugins at the vhost
additionally, discovered plugins are not enabled automatically for security
reasons. You do this using info->pvo or for lwsws, in the JSON config.
@section http2rp Reproducing HTTP2.0 tests
Reproducing HTTP2.0 tests
-------------------------
You must have built and be running lws against a version of openssl that has
ALPN / NPN. Most distros still have older versions. You'll know it's right by
seeing
```bash
lwsts[4752]: Compiled with OpenSSL support
lwsts[4752]: Using SSL mode
lwsts[4752]: HTTP2 / ALPN enabled
```
lwsts[4752]: Compiled with OpenSSL support
lwsts[4752]: Using SSL mode
lwsts[4752]: HTTP2 / ALPN enabled
```
at lws startup.
For non-SSL HTTP2.0 upgrade
```bash
$ nghttp -nvasu http://localhost:7681/test.htm
```
$ nghttp -nvasu http://localhost:7681/test.htm
```
For SSL / ALPN HTTP2.0 upgrade
```
$ nghttp -nvas https://localhost:7681/test.html
$ nghttp -nvas https://localhost:7681/test.html
```
@section cross Cross compiling
Cross compiling
---------------
To enable cross-compiling **libwebsockets** using CMake you need to create
a "Toolchain file" that you supply to CMake when generating your build files.
CMake will then use the cross compilers and build paths specified in this file
@ -408,11 +299,13 @@ to look for dependencies and such.
you can use as a starting point.
The commandline to configure for cross with this would look like
```bash
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
-DWITHOUT_EXTENSIONS=1 -DWITH_SSL=0
```
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
-DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_SSL=0
```
The example shows how to build with no external cross lib dependencies, you
need to provide the cross libraries otherwise.
@ -422,22 +315,25 @@ need to provide the cross libraries otherwise.
Additional information on cross compilation with CMake:
http://www.vtk.org/Wiki/CMake_Cross_Compiling
@section mem Memory efficiency
Memory efficiency
-----------------
Embedded server-only configuration without extensions (ie, no compression
on websocket connections), but with full v13 websocket features and http
server, built on ARM Cortex-A9:
Update at 8dac94d (2013-02-18)
```
$ ./configure --without-client --without-extensions --disable-debug --without-daemonize
Context Creation, 1024 fd limit[2]: 16720 (includes 12 bytes per fd)
Per-connection [3]: 72 bytes, +1328 during headers
```bash
$ ./configure --without-client --without-extensions --disable-debug --without-daemonize
.text .rodata .data .bss
11512 2784 288 4
Context Creation, 1024 fd limit[2]: 16720 (includes 12 bytes per fd)
Per-connection [3]: 72 bytes, +1328 during headers
.text .rodata .data .bss
11512 2784 288 4
```
This shows the impact of the major configuration with/without options at
13ba5bbc633ea962d46d using Ubuntu ARM on a PandaBoard ES.

View file

@ -1,7 +1,5 @@
Notes about coding with lws
===========================
@section dae Daemonization
Daemonization
-------------
There's a helper api `lws_daemonize` built by default that does everything you
need to daemonize well, including creating a lock file. If you're making
@ -13,7 +11,8 @@ daemon is headless, so you'll need to sort out alternative logging, by, eg,
syslog.
@section conns Maximum number of connections
Maximum number of connections
-----------------------------
The maximum number of connections the library can deal with is decided when
it starts by querying the OS to find out how many file descriptors it is
@ -22,13 +21,12 @@ allow up to that many connections, minus whatever other file descriptors are
in use by the user code.
If you want to restrict that allocation, or increase it, you can use ulimit or
similar to change the available number of file descriptors, and when restarted
similar to change the avaiable number of file descriptors, and when restarted
**libwebsockets** will adapt accordingly.
@section evtloop Libwebsockets is singlethreaded
Libwebsockets works in a serialized event loop, in a single thread.
Libwebsockets is singlethreaded
-------------------------------
Directly performing websocket actions from other threads is not allowed.
Aside from the internal data being inconsistent in `forked()` processes,
@ -38,19 +36,6 @@ with the socket closing and the `wsi` freed.
Websocket write activities should only take place in the
`LWS_CALLBACK_SERVER_WRITEABLE` callback as described below.
[This network-programming necessity to link the issue of new data to
the peer taking the previous data is not obvious to all users so let's
repeat that in other words:
***ONLY DO LWS_WRITE FROM THE WRITEABLE CALLBACK***
There is another network-programming truism that surprises some people which
is if the sink for the data cannot accept more:
***YOU MUST PERFORM RX FLOW CONTROL***
See the mirror protocol implementations for example code.
Only live connections appear in the user callbacks, so this removes any
possibility of trying to used closed and freed wsis.
@ -66,7 +51,8 @@ SSL_library_init() is called from the context create api and it also is not
reentrant. So at least create the contexts sequentially.
@section writeable Only send data when socket writeable
Only send data when socket writeable
------------------------------------
You should only send data on a websocket connection from the user callback
`LWS_CALLBACK_SERVER_WRITEABLE` (or `LWS_CALLBACK_CLIENT_WRITEABLE` for
@ -75,9 +61,8 @@ clients).
If you want to send something, do not just send it but request a callback
when the socket is writeable using
- `lws_callback_on_writable(context, wsi)` for a specific `wsi`, or
- `lws_callback_on_writable_all_protocol(protocol)` for all connections
- `libwebsocket_callback_on_writable(context, wsi)`` for a specific `wsi`, or
- `libwebsocket_callback_on_writable_all_protocol(protocol)` for all connections
using that protocol to get a callback when next writeable.
Usually you will get called back immediately next time around the service
@ -88,7 +73,8 @@ in the ...WRITEABLE callback.
See the test server code for an example of how to do this.
@section otherwr Do not rely on only your own WRITEABLE requests appearing
Do not rely on only your own WRITEABLE requests appearing
---------------------------------------------------------
Libwebsockets may generate additional `LWS_CALLBACK_CLIENT_WRITEABLE` events
if it met network conditions where it had to buffer your send data internally.
@ -101,12 +87,13 @@ It's quite possible you get an 'extra' writeable callback at any time and
just need to `return 0` and wait for the expected callback later.
@section closing Closing connections from the user side
Closing connections from the user side
--------------------------------------
When you want to close a connection, you do it by returning `-1` from a
callback for that connection.
You can provoke a callback by calling `lws_callback_on_writable` on
You can provoke a callback by calling `libwebsocket_callback_on_writable` on
the wsi, then notice in the callback you want to close it and just return -1.
But usually, the decision to close is made in a callback already and returning
-1 is simple.
@ -118,70 +105,44 @@ take care of closing the connection automatically.
If you have a silently dead connection, it's possible to enter a state where
the send pipe on the connection is choked but no ack will ever come, so the
dead connection will never become writeable. To cover that, you can use TCP
keepalives (see later in this document) or pings.
keepalives (see later in this document)
@section gzip Serving from inside a zip file
Lws now supports serving gzipped files from inside a zip container. Thanks to
Per Bothner for contributing the code.
This has the advtantage that if the client can accept GZIP encoding, lws can
simply send the gzip-compressed file from inside the zip file with no further
processing, saving time and bandwidth.
In the case the client can't understand gzip compression, lws automatically
decompressed the file and sends it normally.
Clients with limited storage and RAM will find this useful; the memory needed
for the inflate case is constrained so that only one input buffer at a time
is ever in memory.
To use this feature, ensure LWS_WITH_ZIP_FOPS is enabled at CMake (it is by
default).
`libwebsockets-test-server-v2.0` includes a mount using this technology
already, run that test server and navigate to http://localhost:7681/ziptest/candide.html
This will serve the book Candide in html, together with two jpgs, all from
inside a .zip file in /usr/[local/]share-libwebsockets-test-server/candide.zip
Usage is otherwise automatic, if you arrange a mount that points to the zipfile,
eg, "/ziptest" -> "mypath/test.zip", then URLs like `/ziptest/index.html` will be
servied from `index.html` inside `mypath/test.zip`
@section frags Fragmented messages
Fragmented messages
-------------------
To support fragmented messages you need to check for the final
frame of a message with `lws_is_final_fragment`. This
frame of a message with `libwebsocket_is_final_fragment`. This
check can be combined with `libwebsockets_remaining_packet_payload`
to gather the whole contents of a message, eg:
```
case LWS_CALLBACK_RECEIVE:
{
Client * const client = (Client *)user;
const size_t remaining = lws_remaining_packet_payload(wsi);
if (!remaining && lws_is_final_fragment(wsi)) {
if (client->HasFragments()) {
client->AppendMessageFragment(in, len, 0);
in = (void *)client->GetMessage();
len = client->GetMessageLength();
}
client->ProcessMessage((char *)in, len, wsi);
client->ResetMessage();
} else
client->AppendMessageFragment(in, len, remaining);
}
break;
case LWS_CALLBACK_RECEIVE:
{
Client * const client = (Client *)user;
const size_t remaining = libwebsockets_remaining_packet_payload(wsi);
if (!remaining && libwebsocket_is_final_fragment(wsi)) {
if (client->HasFragments()) {
client->AppendMessageFragment(in, len, 0);
in = (void *)client->GetMessage();
len = client->GetMessageLength();
}
client->ProcessMessage((char *)in, len, wsi);
client->ResetMessage();
} else
client->AppendMessageFragment(in, len, remaining);
}
break;
```
The test app libwebsockets-test-fraggle sources also show how to
deal with fragmented messages.
@section debuglog Debug Logging
Debug Logging
-------------
Also using `lws_set_log_level` api you may provide a custom callback to actually
emit the log string. By default, this points to an internal emit function
@ -202,17 +163,9 @@ The logging apis are made available for user code.
The difference between notice and info is that notice will be logged by default
whereas info is ignored by default.
If you are not building with _DEBUG defined, ie, without this
```
$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
```
then log levels below notice do not actually get compiled in.
@section extpoll External Polling Loop support
External Polling Loop support
-----------------------------
**libwebsockets** maintains an internal `poll()` array for all of its
sockets, but you can instead integrate the sockets into an
@ -225,39 +178,28 @@ Four callbacks `LWS_CALLBACK_ADD_POLL_FD`, `LWS_CALLBACK_DEL_POLL_FD`,
appear in the callback for protocol 0 and allow interface code to
manage socket descriptors in other poll loops.
You can pass all pollfds that need service to `lws_service_fd()`, even
You can pass all pollfds that need service to `libwebsocket_service_fd()`, even
if the socket or file does not belong to **libwebsockets** it is safe.
If **libwebsocket** handled it, it zeros the pollfd `revents` field before returning.
So you can let **libwebsockets** try and if `pollfd->revents` is nonzero on return,
you know it needs handling by your code.
Also note that when integrating a foreign event loop like libev or libuv where
it doesn't natively use poll() semantics, and you must return a fake pollfd
reflecting the real event:
- be sure you set .events to .revents value as well in the synthesized pollfd
- check the built-in support for the event loop if possible (eg, ./lib/libuv.c)
to see how it interfaces to lws
- use LWS_POLLHUP / LWS_POLLIN / LWS_POLLOUT from libwebsockets.h to avoid
losing windows compatibility
@section cpp Using with in c++ apps
Using with in c++ apps
----------------------
The library is ready for use by C++ apps. You can get started quickly by
copying the test server
```
$ cp test-server/test-server.c test.cpp
```bash
$ cp test-server/test-server.c test.cpp
```
and building it in C++ like this
```
$ g++ -DINSTALL_DATADIR=\"/usr/share\" -ocpptest test.cpp -lwebsockets
```bash
$ g++ -DINSTALL_DATADIR=\"/usr/share\" -ocpptest test.cpp -lwebsockets
```
`INSTALL_DATADIR` is only needed because the test server uses it as shipped, if
@ -265,21 +207,18 @@ you remove the references to it in your app you don't need to define it on
the g++ line either.
@section headerinfo Availability of header information
Availability of header information
----------------------------------
HTTP Header information is managed by a pool of "ah" structs. These are a
limited resource so there is pressure to free the headers and return the ah to
the pool for reuse.
For that reason header information on HTTP connections that get upgraded to
websockets is lost after the ESTABLISHED callback. Anything important that
isn't processed by user code before then should be copied out for later.
For HTTP connections that don't upgrade, header info remains available the
whole time.
From v1.2 of the library onwards, the HTTP header content is `free()`d as soon
as the websocket connection is established. For websocket servers, you can
copy interesting headers by handling `LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION`
callback, for clients there's a new callback just for this purpose
`LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH`.
@section ka TCP Keepalive
TCP Keepalive
-------------
It is possible for a connection which is not being used to send to die
silently somewhere between the peer and the side not sending. In this case
@ -304,8 +243,8 @@ like Linux does. On those systems you can enable keepalive by a nonzero
value in `ka_time`, but the systemwide kernel settings for the time / probes/
interval are used, regardless of what nonzero value is in `ka_time`.
@section sslopt Optimizing SSL connections
Optimizing SSL connections
--------------------------
There's a member `ssl_cipher_list` in the `lws_context_creation_info` struct
which allows the user code to restrict the possible cipher selection at
@ -314,17 +253,16 @@ context-creation time.
You might want to look into that to stop the ssl peers selecting a cipher which
is too computationally expensive. To use it, point it to a string like
`"RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"`
`"RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"`
if left `NULL`, then the "DEFAULT" set of ciphers are all possible to select.
You can also set it to `"ALL"` to allow everything (including insecure ciphers).
Async nature of client connections
----------------------------------
@section clientasync Async nature of client connections
When you call `lws_client_connect_info(..)` and get a `wsi` back, it does not
mean your connection is active. It just means it started trying to connect.
When you call `libwebsocket_client_connect(..)` and get a `wsi` back, it does not
mean your connection is active. It just mean it started trying to connect.
Your client connection is actually active only when you receive
`LWS_CALLBACK_CLIENT_ESTABLISHED` for it.
@ -335,611 +273,6 @@ other reasons, if any of that happens you'll get a
`wsi`.
After attempting the connection and getting back a non-`NULL` `wsi` you should
loop calling `lws_service()` until one of the above callbacks occurs.
loop calling `libwebsocket_service()` until one of the above callbacks occurs.
As usual, see [test-client.c](test-server/test-client.c) for example code.
Notice that the client connection api tries to progress the connection
somewhat before returning. That means it's possible to get callbacks like
CONNECTION_ERROR on the new connection before your user code had a chance to
get the wsi returned to identify it (in fact if the connection did fail early,
NULL will be returned instead of the wsi anyway).
To avoid that problem, you can fill in `pwsi` in the client connection info
struct to point to a struct lws that get filled in early by the client
connection api with the related wsi. You can then check for that in the
callback to confirm the identity of the failing client connection.
@section fileapi Lws platform-independent file access apis
lws now exposes his internal platform file abstraction in a way that can be
both used by user code to make it platform-agnostic, and be overridden or
subclassed by user code. This allows things like handling the URI "directory
space" as a virtual filesystem that may or may not be backed by a regular
filesystem. One example use is serving files from inside large compressed
archive storage without having to unpack anything except the file being
requested.
The test server shows how to use it, basically the platform-specific part of
lws prepares a file operations structure that lives in the lws context.
The user code can get a pointer to the file operations struct
```
LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops *
`lws_get_fops`(struct lws_context *context);
```
and then can use helpers to also leverage these platform-independent
file handling apis
```
lws_fop_fd_t
`lws_plat_file_open`(struct lws_plat_file_ops *fops, const char *filename,
lws_fop_flags_t *flags)
int
`lws_plat_file_close`(lws_fop_fd_t fop_fd)
unsigned long
`lws_plat_file_seek_cur`(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
int
`lws_plat_file_read`(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
int
`lws_plat_file_write`(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len )
```
Generic helpers are provided which provide access to generic fops information or
call through to the above fops
```
lws_filepos_t
lws_vfs_tell(lws_fop_fd_t fop_fd);
lws_filepos_t
lws_vfs_get_length(lws_fop_fd_t fop_fd);
uint32_t
lws_vfs_get_mod_time(lws_fop_fd_t fop_fd);
lws_fileofs_t
lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
lws_fileofs_t
lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset);
```
The user code can also override or subclass the file operations, to either
wrap or replace them. An example is shown in test server.
### Changes from v2.1 and before fops
There are several changes:
1) Pre-2.2 fops directly used platform file descriptors. Current fops returns and accepts a wrapper type lws_fop_fd_t which is a pointer to a malloc'd struct containing information specific to the filesystem implementation.
2) Pre-2.2 fops bound the fops to a wsi. This is completely removed, you just give a pointer to the fops struct that applies to this file when you open it. Afterwards, the operations in the fops just need the lws_fop_fd_t returned from the open.
3) Everything is wrapped in typedefs. See lws-plat-unix.c for examples of how to implement.
4) Position in the file, File Length, and a copy of Flags left after open are now generically held in the fop_fd.
VFS implementation must set and manage this generic information now. See the implementations in lws-plat-unix.c for
examples.
5) The file length is no longer set at a pointer provided by the open() fop. The api `lws_vfs_get_length()` is provided to
get the file length after open.
6) If your file namespace is virtual, ie, is not reachable by platform fops directly, you must set LWS_FOP_FLAG_VIRTUAL
on the flags during open.
7) There is an optional `mod_time` uint32_t member in the generic fop_fd. If you are able to set it during open, you
should indicate it by setting `LWS_FOP_FLAG_MOD_TIME_VALID` on the flags.
@section rawfd RAW file descriptor polling
LWS allows you to include generic platform file descriptors in the lws service / poll / event loop.
Open your fd normally and then
```
lws_sock_file_fd_type u;
u.filefd = your_open_file_fd;
if (!lws_adopt_descriptor_vhost(vhost, 0, u,
"protocol-name-to-bind-to",
optional_wsi_parent_or_NULL)) {
// failed
}
// OK
```
A wsi is created for the file fd that acts like other wsi, you will get these
callbacks on the named protocol
```
LWS_CALLBACK_RAW_ADOPT_FILE
LWS_CALLBACK_RAW_RX_FILE
LWS_CALLBACK_RAW_WRITEABLE_FILE
LWS_CALLBACK_RAW_CLOSE_FILE
```
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
`protocol-lws-raw-test` plugin provides a method for testing this with
`libwebsockets-test-server-v2.0`:
The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
You can feed it data through the FIFO like this
```
$ sudo sh -c "echo hello > /tmp/lws-test-raw"
```
This plugin simply prints the data. But it does it through the lws event
loop / service poll.
@section rawsrvsocket RAW server socket descriptor polling
You can also enable your vhost to accept RAW socket connections, in addition to
HTTP[s] and WS[s]. If the first bytes written on the connection are not a
valid HTTP method, then the connection switches to RAW mode.
This is disabled by default, you enable it by setting the `.options` flag
LWS_SERVER_OPTION_FALLBACK_TO_RAW when creating the vhost.
RAW mode socket connections receive the following callbacks
```
LWS_CALLBACK_RAW_ADOPT
LWS_CALLBACK_RAW_RX
LWS_CALLBACK_RAW_WRITEABLE
LWS_CALLBACK_RAW_CLOSE
```
You can control which protocol on your vhost handles these RAW mode
incoming connections by marking the selected protocol with a pvo `raw`, eg
```
"protocol-lws-raw-test": {
"status": "ok",
"raw": "1"
},
```
The "raw" pvo marks this protocol as being used for RAW connections.
`protocol-lws-raw-test` plugin provides a method for testing this with
`libwebsockets-test-server-v2.0`:
Run libwebsockets-test-server-v2.0 and connect to it by telnet, eg
```
$ telnet 127.0.0.1 7681
```
type something that isn't a valid HTTP method and enter, before the
connection times out. The connection will switch to RAW mode using this
protocol, and pass the unused rx as a raw RX callback.
The test protocol echos back what was typed on telnet to telnet.
@section rawclientsocket RAW client socket descriptor polling
You can now also open RAW socket connections in client mode.
Follow the usual method for creating a client connection, but set the
`info.method` to "RAW". When the connection is made, the wsi will be
converted to RAW mode and operate using the same callbacks as the
server RAW sockets described above.
The libwebsockets-test-client supports this using raw:// URLS. To
test, open a netcat listener in one window
```
$ nc -l 9999
```
and in another window, connect to it using the test client
```
$ libwebsockets-test-client raw://127.0.0.1:9999
```
The connection should succeed, and text typed in the netcat window (including a CRLF)
will be received in the client.
@section ecdh ECDH Support
ECDH Certs are now supported. Enable the CMake option
cmake .. -DLWS_SSL_SERVER_WITH_ECDH_CERT=1
**and** the info->options flag
LWS_SERVER_OPTION_SSL_ECDH
to build in support and select it at runtime.
@section sslinfo SSL info callbacks
OpenSSL allows you to receive callbacks for various events defined in a
bitmask in openssl/ssl.h. The events include stuff like TLS Alerts.
By default, lws doesn't register for these callbacks.
However if you set the info.ssl_info_event_mask to nonzero (ie, set some
of the bits in it like `SSL_CB_ALERT` at vhost creation time, then
connections to that vhost will call back using LWS_CALLBACK_SSL_INFO
for the wsi, and the `in` parameter will be pointing to a struct of
related args:
```
struct lws_ssl_info {
int where;
int ret;
};
```
The default callback handler in lws has a handler for LWS_CALLBACK_SSL_INFO
which prints the related information, You can test it using the switch
-S -s on `libwebsockets-test-server-v2.0`.
Returning nonzero from the callback will close the wsi.
@section smp SMP / Multithreaded service
SMP support is integrated into LWS without any internal threading. It's
very simple to use, libwebsockets-test-server-pthread shows how to do it,
use -j <n> argument there to control the number of service threads up to 32.
Two new members are added to the info struct
unsigned int count_threads;
unsigned int fd_limit_per_thread;
leave them at the default 0 to get the normal singlethreaded service loop.
Set count_threads to n to tell lws you will have n simultaneous service threads
operating on the context.
There is still a single listen socket on one port, no matter how many
service threads.
When a connection is made, it is accepted by the service thread with the least
connections active to perform load balancing.
The user code is responsible for spawning n threads running the service loop
associated to a specific tsi (Thread Service Index, 0 .. n - 1). See
the libwebsockets-test-server-pthread for how to do.
If you leave fd_limit_per_thread at 0, then the process limit of fds is shared
between the service threads; if you process was allowed 1024 fds overall then
each thread is limited to 1024 / n.
You can set fd_limit_per_thread to a nonzero number to control this manually, eg
the overall supported fd limit is less than the process allowance.
You can control the context basic data allocation for multithreading from Cmake
using -DLWS_MAX_SMP=, if not given it's set to 32. The serv_buf allocation
for the threads (currently 4096) is made at runtime only for active threads.
Because lws will limit the requested number of actual threads supported
according to LWS_MAX_SMP, there is an api lws_get_count_threads(context) to
discover how many threads were actually allowed when the context was created.
It's required to implement locking in the user code in the same way that
libwebsockets-test-server-pthread does it, for the FD locking callbacks.
There is no knowledge or dependency in lws itself about pthreads. How the
locking is implemented is entirely up to the user code.
@section libevuv Libev / Libuv support
You can select either or both
-DLWS_WITH_LIBEV=1
-DLWS_WITH_LIBUV=1
at cmake configure-time. The user application may use one of the
context init options flags
LWS_SERVER_OPTION_LIBEV
LWS_SERVER_OPTION_LIBUV
to indicate it will use either of the event libraries.
@section extopts Extension option control from user code
User code may set per-connection extension options now, using a new api
`lws_set_extension_option()`.
This should be called from the ESTABLISHED callback like this
```
lws_set_extension_option(wsi, "permessage-deflate",
"rx_buf_size", "12"); /* 1 << 12 */
```
If the extension is not active (missing or not negotiated for the
connection, or extensions are disabled on the library) the call is
just returns -1. Otherwise the connection's extension has its
named option changed.
The extension may decide to alter or disallow the change, in the
example above permessage-deflate restricts the size of his rx
output buffer also considering the protocol's rx_buf_size member.
@section httpsclient Client connections as HTTP[S] rather than WS[S]
You may open a generic http client connection using the same
struct lws_client_connect_info used to create client ws[s]
connections.
To stay in http[s], set the optional info member "method" to
point to the string "GET" instead of the default NULL.
After the server headers are processed, when payload from the
server is available the callback LWS_CALLBACK_RECEIVE_CLIENT_HTTP
will be made.
You can choose whether to process the data immediately, or
queue a callback when an outgoing socket is writeable to provide
flow control, and process the data in the writable callback.
Either way you use the api `lws_http_client_read()` to access the
data, eg
```
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
{
char buffer[1024 + LWS_PRE];
char *px = buffer + LWS_PRE;
int lenx = sizeof(buffer) - LWS_PRE;
lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP\n");
/*
* Often you need to flow control this by something
* else being writable. In that case call the api
* to get a callback when writable here, and do the
* pending client read in the writeable callback of
* the output.
*/
if (lws_http_client_read(wsi, &px, &lenx) < 0)
return -1;
while (lenx--)
putchar(*px++);
}
break;
```
Notice that if you will use SSL client connections on a vhost, you must
prepare the client SSL context for the vhost after creating the vhost, since
this is not normally done if the vhost was set up to listen / serve. Call
the api lws_init_vhost_client_ssl() to also allow client SSL on the vhost.
@section vhosts Using lws vhosts
If you set LWS_SERVER_OPTION_EXPLICIT_VHOSTS options flag when you create
your context, it won't create a default vhost using the info struct
members for compatibility. Instead you can call lws_create_vhost()
afterwards to attach one or more vhosts manually.
```
LWS_VISIBLE struct lws_vhost *
lws_create_vhost(struct lws_context *context,
struct lws_context_creation_info *info);
```
lws_create_vhost() uses the same info struct as lws_create_context(),
it ignores members related to context and uses the ones meaningful
for vhost (marked with VH in libwebsockets.h).
```
struct lws_context_creation_info {
int port; /* VH */
const char *iface; /* VH */
const struct lws_protocols *protocols; /* VH */
const struct lws_extension *extensions; /* VH */
...
```
When you attach the vhost, if the vhost's port already has a listen socket
then both vhosts share it and use SNI (is SSL in use) or the Host: header
from the client to select the right one. Or if no other vhost already
listening the a new listen socket is created.
There are some new members but mainly it's stuff you used to set at
context creation time.
@section sni How lws matches hostname or SNI to a vhost
LWS first strips any trailing :port number.
Then it tries to find an exact name match for a vhost listening on the correct
port, ie, if SNI or the Host: header provided abc.com:1234, it will match on a
vhost named abc.com that is listening on port 1234.
If there is no exact match, lws will consider wildcard matches, for example
if cats.abc.com:1234 is provided by the client by SNI or Host: header, it will
accept a vhost "abc.com" listening on port 1234. If there was a better, exact,
match, it will have been chosen in preference to this.
Connections with SSL will still have the client go on to check the
certificate allows wildcards and error out if not.
@section mounts Using lws mounts on a vhost
The last argument to lws_create_vhost() lets you associate a linked
list of lws_http_mount structures with that vhost's URL 'namespace', in
a similar way that unix lets you mount filesystems into areas of your /
filesystem how you like and deal with the contents transparently.
```
struct lws_http_mount {
struct lws_http_mount *mount_next;
const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
const char *def; /* default target, eg, "index.html" */
struct lws_protocol_vhost_options *cgienv;
int cgi_timeout;
int cache_max_age;
unsigned int cache_reusable:1;
unsigned int cache_revalidate:1;
unsigned int cache_intermediaries:1;
unsigned char origin_protocol;
unsigned char mountpoint_len;
};
```
The last mount structure should have a NULL mount_next, otherwise it should
point to the 'next' mount structure in your list.
Both the mount structures and the strings must persist until the context is
destroyed, since they are not copied but used in place.
`.origin_protocol` should be one of
```
enum {
LWSMPRO_HTTP,
LWSMPRO_HTTPS,
LWSMPRO_FILE,
LWSMPRO_CGI,
LWSMPRO_REDIR_HTTP,
LWSMPRO_REDIR_HTTPS,
LWSMPRO_CALLBACK,
};
```
- LWSMPRO_FILE is used for mapping url namespace to a filesystem directory and
serve it automatically.
- LWSMPRO_CGI associates the url namespace with the given CGI executable, which
runs when the URL is accessed and the output provided to the client.
- LWSMPRO_REDIR_HTTP and LWSMPRO_REDIR_HTTPS auto-redirect clients to the given
origin URL.
- LWSMPRO_CALLBACK causes the http connection to attach to the callback
associated with the named protocol (which may be a plugin).
@section mountcallback Operation of LWSMPRO_CALLBACK mounts
The feature provided by CALLBACK type mounts is binding a part of the URL
namespace to a named protocol callback handler.
This allows protocol plugins to handle areas of the URL namespace. For example
in test-server-v2.0.c, the URL area "/formtest" is associated with the plugin
providing "protocol-post-demo" like this
```
static const struct lws_http_mount mount_post = {
NULL, /* linked-list pointer to next*/
"/formtest", /* mountpoint in URL namespace on this vhost */
"protocol-post-demo", /* handler */
NULL, /* default filename if none given */
NULL,
0,
0,
0,
0,
0,
LWSMPRO_CALLBACK, /* origin points to a callback */
9, /* strlen("/formtest"), ie length of the mountpoint */
};
```
Client access to /formtest[anything] will be passed to the callback registered
with the named protocol, which in this case is provided by a protocol plugin.
Access by all methods, eg, GET and POST are handled by the callback.
protocol-post-demo deals with accepting and responding to the html form that
is in the test server HTML.
When a connection accesses a URL related to a CALLBACK type mount, the
connection protocol is changed until the next access on the connection to a
URL outside the same CALLBACK mount area. User space on the connection is
arranged to be the size of the new protocol user space allocation as given in
the protocol struct.
This allocation is only deleted / replaced when the connection accesses a
URL region with a different protocol (or the default protocols[0] if no
CALLBACK area matches it).
@section BINDTODEV SO_BIND_TO_DEVICE
The .bind_iface flag in the context / vhost creation struct lets you
declare that you want all traffic for listen and transport on that
vhost to be strictly bound to the network interface named in .iface.
This Linux-only feature requires SO_BIND_TO_DEVICE, which in turn
requires CAP_NET_RAW capability... root has this capability.
However this feature needs to apply the binding also to accepted
sockets during normal operation, which implies the server must run
the whole time as root.
You can avoid this by using the Linux capabilities feature to have
the unprivileged user inherit just the CAP_NET_RAW capability.
You can confirm this with the test server
```
$ sudo /usr/local/bin/libwebsockets-test-server -u agreen -i eno1 -k
```
The part that ensures the capability is inherited by the unprivileged
user is
```
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
info.caps[0] = CAP_NET_RAW;
info.count_caps = 1;
#endif
```
@section dim Dimming webpage when connection lost
The lws test plugins' html provides useful feedback on the webpage about if it
is still connected to the server, by greying out the page if not. You can
also add this to your own html easily
- include lws-common.js from your HEAD section
<script src="/lws-common.js"></script>
- dim the page during initialization, in a script section on your page
lws_gray_out(true,{'zindex':'499'});
- in your ws onOpen(), remove the dimming
lws_gray_out(false);
- in your ws onClose(), reapply the dimming
lws_gray_out(true,{'zindex':'499'});

View file

@ -1,23 +0,0 @@
ESP32 Support
=============
Lws provides a "factory" application
https://github.com/warmcat/lws-esp32-factory
and a test application which implements the generic lws server test apps
https://github.com/warmcat/lws-esp32-test-server-demos
The behaviours of the generic factory are are quite rich, and cover uploading SSL certs through factory and user configuration, AP selection and passphrase entry, and managing a switch to allow the user to force entry to user setup mode at boot subsequently.
The factory app comes with partitioning for a 1MB factory partition containing that app and data, and a single 2.9MB OTA partition containing the main app.
The factory app is able to do OTA updates for both the factory and OTA partition slots; updating the factory slot first writes the new image to the OTA slot and copies it into place at the next boot, after which the user can reload the OTA slot.
State|Image|AP SSID|Port|URL|Mode
---|---|---|---|---|---
Factory Reset or Uninitialized|Factory|AP: ESP_012345|80|http://192.168.4.1|factory.html - to set certificates and serial
User configuration|Factory|AP: config-model-serial|443|https://192.168.4.1|index.html - user set up his AP information
Operation|OTA|Station only|443|https://model-serial.local|OTA application

View file

@ -1,34 +0,0 @@
ESP8266 lws port
----------------
lws can now work well on the ESP8266.
You should get the ESP8266 Espressif SDK-based project here
https://github.com/lws-team/esplws
which includes lws as an "app" in the build. The project provides full AP-based setup over the web, and once the device has been configured to associate to a local AP, a separate station vhost with the lws test protocols.
Instructions for building that are here
https://github.com/lws-team/esplws/blob/master/README.md
There are also instructions there for how to remove the test apps from the build and customize your own station content.
Information about lws integration on ESP8266
--------------------------------------------
The following existing lws features are used to make a nice integration:
- vhosts: there are separate vhosts for the configuration AP mode and the normal station mode.
- file_ops: the lws file operations are overridden and handled by a ROMFS parser
- mounts: mounts are used to serve files automatically from the ROMFS
- plugins: standalone protocol plugins are included into the build, so there are clean individual implementations for each protocol, while everything is statically linked
- lws stability and security features like bytewise parsers, sophisticated timeouts, http/1.1 keepalive support

View file

@ -1,373 +0,0 @@
Notes about generic-sessions Plugin
===================================
@section gseb Enabling lwsgs for build
Enable at CMake with -DLWS_WITH_GENERIC_SESSIONS=1
This also needs sqlite3 (libsqlite3-dev or similar package)
@section gsi lwsgs Introduction
The generic-sessions protocol plugin provides cookie-based login
authentication for lws web and ws connections.
The plugin handles everything about generic account registration,
email verification, lost password, account deletion, and other generic account
management.
Other code, in another eg, ws protocol handler, only needs very high-level
state information from generic-sessions, ie, which user the client is
authenticated as. Everything underneath is managed in generic-sessions.
- random 20-byte session id managed in a cookie
- all information related to the session held at the server, nothing managed clientside
- sqlite3 used at the server to manage active sessions and users
- defaults to creating anonymous sessions with no user associated
- admin account (with user-selectable username) is defined in config with a SHA-1 of the password; rest of the accounts are in sqlite3
- user account passwords stored as salted SHA-1 with additional confounder
only stored in the JSON config, not the database
- login, logout, register account + email verification built-in with examples
- in a mount, some file suffixes (ie, .js) can be associated with a protocol for the purposes of rewriting symbolnames. These are read-only copies of logged-in server state.
- When your page fetches .js or other rewritten files from that mount, "$lwsgs_user" and so on are rewritten on the fly using chunked transfer encoding
- Eliminates server-side scripting with a few rewritten symbols and
javascript on client side
- 32-bit bitfield for authentication sectoring, mounts can provide a mask on the loggin-in session's associated server-side bitfield that must be set for access.
- No code (just config) required for, eg, private URL namespace that requires login to access.
@section gsin Lwsgs Integration to HTML
Only three steps are needed to integrate lwsgs in your HTML.
1) lwsgs HTML UI is bundled with the javascript it uses in `lwsgs.js`, so
import that script file in your head section
2) define an empty div of id "lwsgs" somewhere
3) Call lwsgs_initial() in your page
That's it. An example is below
```
<html>
<head>
<script src="lwsgs.js"></script>
<style>
.body { font-size: 12 }
.gstitle { font-size: 18 }
</style>
</head>
<body style="background-image:url(seats.jpg)">
<table style="width:100%;transition: max-height 2s;">
<tr>
<td style="vertical-align:top;text-align:left;width=200px">
<img src="lwsgs-logo.png">
</td>
<td style="vertical-align:top;float:right">
<div id=lwsgs style="text-align:right;background-color: rgba(255, 255, 255, 0.8);"></div>
</td>
</tr>
</table>
</form>
<script>lwsgs_initial();</script>
</body>
</html>
```
@section gsof Lwsgs Overall Flow@
When the protocol is initialized, it gets per-vhost information from the config, such
as where the sqlite3 databases are to be stored. The admin username and sha-1 of the
admin password are also taken from here.
In the mounts using protocol-generic-sessions, a cookie is maintained against any requests; if no cookie was active on the initial request a new session is
created with no attached user.
So there should always be an active session after any transactions with the server.
In the example html going to the mount /lwsgs loads a login / register page as the default.
The <form> in the login page contains 'next url' hidden inputs that let the html 'program' where the form handler will go after a successful admin login, a successful user login and a failed login.
After a successful login, the sqlite record at the server for the current session is updated to have the logged-in username associated with it.
@section gsconf Lwsgs Configuration
"auth-mask" defines the authorization sector bits that must be enabled on the session to gain access.
"auth-mask" 0 is the default.
- b0 is set if you are logged in as a user at all.
- b1 is set if you are logged in with the user configured to be admin
- b2 is set if the account has been verified (the account configured for admin is always verified)
- b3 is set if your session just did the forgot password flow successfully
```
{
# things in here can always be served
"mountpoint": "/lwsgs",
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions",
"origin": "callback://protocol-lws-messageboard",
"default": "generic-sessions-login-example.html",
"auth-mask": "0",
"interpret": {
".js": "protocol-lws-messageboard"
}
}, {
# things in here can only be served if logged in as a user
"mountpoint": "/lwsgs/needauth",
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needauth",
"origin": "callback://protocol-lws-messageboard",
"default": "generic-sessions-login-example.html",
"auth-mask": "5", # logged in as a verified user
"interpret": {
".js": "protocol-lws-messageboard"
}
}, {
# things in here can only be served if logged in as admin
"mountpoint": "/lwsgs/needadmin",
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needadmin",
"origin": "callback://protocol-lws-messageboard",
"default": "generic-sessions-login-example.html",
"auth-mask": "7", # b2 = verified (by email / or admin), b1 = admin, b0 = logged in with any user name
"interpret": {
".js": "protocol-lws-messageboard"
}
}
```
Note that the name of the real application protocol that uses generic-sessions
is used, not generic-sessions itself.
The vhost configures the storage dir, admin credentials and session cookie lifetimes:
```
"ws-protocols": [{
"protocol-generic-sessions": {
"status": "ok",
"admin-user": "admin",
# create the pw hash like this (for the example pw, "jipdocesExunt" )
# $ echo -n "jipdocesExunt" | sha1sum
# 046ce9a9cca769e85798133be06ef30c9c0122c9 -
#
# Obviously ** change this password hash to a secret one before deploying **
#
"admin-password-sha1": "046ce9a9cca769e85798133be06ef30c9c0122c9",
"session-db": "/var/www/sessions/lws.sqlite3",
"timeout-idle-secs": "600",
"timeout-anon-idle-secs": "1200",
"timeout-absolute-secs": "6000",
# the confounder is part of the salted password hashes. If this config
# file is in a 0700 root:root dir, an attacker with apache credentials
# will have to get the confounder out of the process image to even try
# to guess the password hashes.
"confounder": "Change to <=31 chars of junk",
"email-from": "noreply@example.com",
"email-smtp-ip": "127.0.0.1",
"email-expire": "3600",
"email-helo": "myhost.com",
"email-contact-person": "Set Me <real-person@email.com>",
"email-confirm-url-base": "http://localhost:7681/lwsgs"
}
```
The email- related settings control generation of automatic emails for
registration and forgotten password.
- `email-from`: The email address automatic emails are sent from
- `email-smtp-ip`: Normally 127.0.0.1, if you have a suitable server on port
25 on your lan you can use this instead here.
- `email-expire`: Seconds that links sent in email will work before being
deleted
- `email-helo`: HELO to use when communicating with your SMTP server
- `email-contact-person`: mentioned in the automatic emails as a human who can
answer questions
- `email-confirm-url-base`: the URL to start links with in the emails, so the
recipient can get back to the web server
The real protocol that makes use of generic-sessions must also be listed and
any configuration it needs given
```
"protocol-lws-messageboard": {
"status": "ok",
"message-db": "/var/www/sessions/messageboard.sqlite3"
},
```
Notice the real application uses his own sqlite db, no details about how
generic-sessions works or how it stores data are available to it.
@section gspwc Lwsgs Password Confounder
You can also define a per-vhost confounder shown in the example above, used
when aggregating the password with the salt when it is hashed. Any attacker
will also need to get the confounder along with the database, which you can
make harder by making the config dir only eneterable / readable by root.
@section gsprep Lwsgs Preparing the db directory
You will have to prepare the db directory so it's suitable for the lwsws user to use,
that usually means apache, eg
```
# mkdir -p /var/www/sessions
# chown root:apache /var/www/sessions
# chmod 770 /var/www/sessions
```
@section gsrmail Lwsgs Email configuration
lwsgs will can send emails by talking to an SMTP server on localhost:25. That
will usually be sendmail or postfix, you should confirm that works first by
itself using the `mail` application to send on it.
lwsgs has been tested on stock Fedora sendmail and postfix.
@section gsap Lwsgs Integration with another protocol
lwsgs is designed to provide sessions and accounts in a standalone and generic way.
But it's not useful by itself, there will always be the actual application who wants
to make use of generic-sessions features.
We provide the "messageboard" plugin as an example of how to integrate with
your actual application protocol.
The basic approach is the 'real' protocol handler (usually a plugin itself)
subclasses the generic-sessions plugin and calls through to it by default.
The "real" protocol handler entirely deals with ws-related stuff itself, since
generic-sessions does not use ws. But for
- LWS_CALLBACK_HTTP
- LWS_CALLBACK_HTTP_BODY
- LWS_CALLBACK_HTTP_BODY_COMPLETION
- LWS_CALLBACK_HTTP_DROP_PROTOCOL
the "real" protocol handler checks if it recognizes the activity (eg, his own
POST form URL) and if not, passes stuff through to the generic-sessions protocol callback to handle it. To simplify matters the real protocol can just pass
through any unhandled messages to generic-sessions.
The "real" protocol can get a pointer to generic-sessions protocol on the
same vhost using
```
vhd->gsp = lws_vhost_name_to_protocol(vhd->vh, "protocol-generic-sessions");
```
The "real" protocol must also arrange generic-sessions per_session_data in his
own per-session allocation. To allow keeping generic-sessions opaque, the
real protocol must allocate that space at runtime, using the pss size
the generic-sessions protocol struct exposes
```
struct per_session_data__myapp {
void *pss_gs;
...
pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
```
The allocation reserved for generic-sessions is then used as user_space when
the real protocol calls through to the generic-sessions callback
```
vhd->gsp->callback(wsi, reason, &pss->pss_gs, in, len);
```
In that way the "real" protocol can subclass generic-sessions functionality.
To ease management of these secondary allocations, there are callbacks that
occur when a wsi binds to a protocol and when the binding is dropped. These
should be used to malloc and free and kind of per-connection
secondary allocations.
```
case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
if (!pss || pss->pss_gs)
break;
pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
if (!pss->pss_gs)
return -1;
memset(pss->pss_gs, 0, vhd->gsp->per_session_data_size);
break;
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
if (vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len))
return -1;
if (pss->pss_gs) {
free(pss->pss_gs);
pss->pss_gs = NULL;
}
break;
```
#section gsapsib Getting session-specific information from another protocol
At least at the time when someone tries to upgrade an http(s) connection to
ws(s) with your real protocol, it is necessary to confirm the cookie the http(s)
connection has with generic-sessions and find out his username and other info.
Generic sessions lets another protocol check it again by calling his callback,
and lws itself provides a generic session info struct to pass the related data
```
struct lws_session_info {
char username[32];
char email[100];
char ip[72];
unsigned int mask;
char session[42];
};
struct lws_session_info sinfo;
...
vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO,
&pss->pss_gs, &sinfo, 0);
```
After the call to generic-sessions, the results can be
- all the strings will be zero-length and .mask zero, there is no usable cookie
- only .ip and .session are set: the cookie is OK but no user logged in
- all the strings contain information about the logged-in user
the real protocol can use this to reject attempts to open ws connections from
http connections that are not authenticated; afterwards there's no need to
check the ws connection auth status again.

View file

@ -1,219 +0,0 @@
Notes about generic-table
=========================
@section gtint What is generic-table?
Generic-table is a JSON schema and client-side JS file that makes it easy to
display live, table structured HTML over a ws link.
An example plugin and index.html using it are provided, but lwsgt itself doesn't
have its own plugin, it's just a JSON schema and client-side JS that other
plugins can use to simplify displaying live, table-based data without having
to reinvent the wheel each time.
The ws protocol sends JSON describing the table, and then JSON updating the table
contents when it chooses, the brower table is updated automatically, live.
\image html lwsgt-overview.png
- Example protocol plugin (displays directory contents): https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/protocol_table_dirlisting.c
- Example HTML: https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/assets/index.html
- lwsgt.js (client-side table rendering / ws link management): https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/assets/lwsgt.js
@section gteb Enabling for build
Enable the demo plugin at CMake with -DLWS_WITH_PLUGINS=1
@section gtinth Integrating with your html
- In your HEAD section, include lwsgt.js
```
<script src="lwsgt.js"></script>
```
- Also in your HEAD section, style the lwsgt CSS, eg
```
<style>
.lwsgt_title { font-size: 24; text-align:center }
.lwsgt_breadcrumbs { font-size: 18; text-align:left }
.lwsgt_table { font-size: 14; padding:12px; margin: 12px; align:center }
.lwsgt_hdr { font-size: 18; text-align:center;
background-color: rgba(40, 40, 40, 0.8); color: white }
.lwsgt_tr { padding: 10px }
.lwsgt_td { padding: 3px }
</style>
```
You can skip this but the result will be less beautiful until some CSS is
provided.
- In your body section, declare a div with an id (can be whatever you want)
```
<tr><td><div id="lwsgt1" class="group1"></div></td></tr>
```
lwsgt JS will put its content there.
- Finally in a <script> at the end of your page, instantiate lwsgt and
provide a custom callback for clickable links
```
<script>
var v1 = new lwsgt_initial("Dir listing demo",
"protocol-lws-table-dirlisting",
"lwsgt1", "lwsgt_dir_click", "v1");
function lwsgt_dir_click(gt, u, col, row)
{
if (u[0] == '=') { /* change directory */
window[gt].lwsgt_ws.send(u.substring(1, u.length));
return;
}
var win = window.open(u, '_blank');
win.focus();
}
</script>
```
In the callback, you can recover the ws object by `window[gt].lwsgt_ws`.
@section gtc Lwsgt constructor
To instantiate the ws link and lwsgt instance, your HTML must call a lwsgt
constructor for each region on the page managed by lwsgt.
`var myvar = new lwsgt_initial(title, ws_protocol, div_id, click_cb, myvar);`
All of the arguments are strings.
| Parameter | Description |
|-----------------|---------------------------------------------------------|
| title | Title string to go above the table |
| ws_protocol | Protocol name string to use when making ws connection |
| div_id | HTML id of div to fill with content |
| click_cb | Callback function name string to handle clickable links |
| myvar | Name of var used to hold this instantiation globally |
Note "myvar" is needed so it can be passed to the click handling callback.
@section gtclick Lwsgt click handling function
When a clickable link produced by lwsgt is clicked, the function named in the
click_cb parameter to lwsgt_initial is called.
That function is expected to take four parameters, eg
`function lwsgt_dir_click(gt, u, col, row)`
| Parameter | Description |
|------- ---|-----------------------------------------------------------|
| gt | Name of global var holding this lwsgt context (ie, myvar) |
| u | Link "url" string |
| col | Table column number link is from |
| row | Table row number link is from |
@section gtgj Generic-table JSON
### Column layout
When the ws connection is established, the protocol should send a JSON message
describing the table columns. For example
```
"cols": [
{ "name": "Date" },
{ "name": "Size", "align": "right" },
{ "name": "Icon" },
{ "name": "Name", "href": "uri"},
{ "name": "uri", "hide": "1" }
]
}
```
- This describes 5 columns
- Only four columns (not "uri") should be visible
- "Name" should be presented as a clickable link using "uri" as the
destination, when a "uri" field is presented.
- "Size" field should be presented aligned to the right
### Breadcrumbs
When a view is hierarchical, it's useful to provide a "path" with links back
in the "path", known as "breadcrumbs".
Elements before the last one should provide a "url" member as well as the
displayable name, which is used to create the link destination.
The last element, being the current displayed page should not have a url
member and be displayed without link style.
```
"breadcrumbs":[{"name":"top", "url": "/" }, {"name":"mydir"}]
```
### Table data
The actual file data consists of an array of rows, containing the columns
mentioned in the original "cols" section.
```
"data":[
{
"Icon":" ",
"Date":"2015-Feb-06 03:08:35 +0000",
"Size":"1406",
"uri":"./serve//favicon.ico",
"Name":"favicon.ico"
}
]
```
@section gtdirl Setting up protocol-lws-table-dirlisting
The example protocol needs two mounts, one to provide the index.html, js and
the protocol itself
```
{
"mountpoint": "/dirtest",
"origin": "file:///usr/share/libwebsockets-test-server/generic-table",
"origin": "callback://protocol-lws-table-dirlisting",
"default": "index.html",
"pmo": [{
"dir": "/usr/share/libwebsockets-test-server"
}]
},
```
The protocol wants a per-mount option (PMO) to tell it the base directory it
is serving from, named "dir".
The other mount is there to simply serve items that get clicked on from the
table in a secure way
```
{
"mountpoint": "/dirtest/serve",
"origin": "file:///usr/share/libwebsockets-test-server",
"default": "index.html"
},
```
This last bit is not related to using lwsgt itself.

View file

@ -1,192 +0,0 @@
# lws-meta protocol
lws-meta is a lightweight ws subprotocol that accepts other ws connections
to the same server inside it and multiplexes their access to the connection.
```
Client Server
conn1: \ / :conn1
conn2: = mux ------ lws-meta ws protocol ----- mux = :conn2
conn3: / \ :conn3
```
You may have n client ws connections back to the server, but you now
only have one tcp connection (and one SSL wrapper if using SSL) instead
of n of those.
If you currently make multiple ws connections back to the server, so you
can have different protocols active in one webpage, this if for you.
- The subprotocol code for the connections inside a lws-meta connection
need zero changes from being a normal ws connection. It is unaware
it is inside an lws-meta parent connection.
- The traffic on the lws-meta connection is indistinguishable from
standard ws traffic, so intermediaries won't object to it
- The multiplexing is done in the protocol, **not by an extension**. So
it's compatible with all browsers.
- Javascript helper code is provided to very simply use lws-meta
protocol instead of direct connections. The lws test server has
been converted to use this by default.
# Converting your server
1) include the provided lws-meta plugin (plugins/protocl_lws_meta.c) as an
active protocol for your server. You can do that using runtime plugins, or
include the plugin sources into your server at build-time. The lws test
server uses the latter approach.
That's all you need to do on the server side.
# Converting your browser JS
1) import lws-common.js
2) Instantiate a parent lws-meta connection object
```
var lws_meta = new lws_meta_ws();
```
3) Connect the lws-meta object to your server
```
lws_meta.new_parent(get_appropriate_ws_url("?mirror=" + mirror_name));
```
4) Convert your actual ws connections to go via the lws_meta object
```
var my_ws = lws_meta.new_ws("", "dumb-increment-protocol");
```
The first arg is the URL path, the second arg is the ws protocol you want.
That's it. my_ws will get `onopen()`, `onmessage()` etc calls as before.
# lws-meta wire protocol
lws-meta works by adding some bytes at the start of a message indicating
which channel the message applies to.
Channel messages are atomic on the wire. The reason is if we tried to
intersperse other channel fragments between one channels message fragments,
an intermediary would observe violations of the ws framing rule about
having to start a message with TEXT or BINARY, and use only CONTINUATION
for the subsequent fragments. Eg
```
[ ch1 TEXT NOFIN ] [ ch2 BINARY FIN ] [ ch1 CONTINUATION FIN ]
```
is illegal to an observer that doesn't understand lws-meta headers in the
packet payloads. So to avoid this situation, only complete messages may
be sent from one subchannel in each direction at a time.
Consequently, only the first fragment of each message is modified to
have the extra two bytes identifying the subchannel it is aimed at, since
the rest of the message from the same subchannel is defined to follow.
If it makes latencies, modify the protocol sending large messages to
send smaller messages, so the transmission of messages from other channels
can be sent inbetween the smaller messages.
## lws-meta commands
1) CSTRING indicates a string terminated by 0x00 byte
2) Channel IDs are sent with 0x20 added to them, to guarantee valid UTF-8
### 0x41: RX: LWS_META_CMD_OPEN_SUBCHANNEL
- CSTRING: protocol name
- CSTRING: url
- CSTRING: cookie (7 bytes max)
Client is requesting to open a new channel with the given protocol name,
at the given url. The cookie (eg, channel name) is only used in
LWS_META_CMD_OPEN_RESULT, when the channel id is assigned, so it is
applied to the right channel.
### 0x42: TX: LWS_META_CMD_OPEN_RESULT
- CSTRING cookie
- BYTE channel id (0 indicates failed)
- CSTRING: selected protocol name
The server is informing the client of the results of a previous
open request. The cookie the client sent to identify the request
is returned along with a channel id to be used subsequently. If
the channel ID is 0 (after subtracting the transport offset of
0x20) then the open request has failed.
### 0x43: TX: LWS_META_CMD_CLOSE_NOTIFY
- BYTE channel id
- BYTE: payload length + 0x20
- BYTE: close code MSB
- BYTE: close code LSB
- PAYLOAD: payload (< 123 bytes)
Server notifies the client that a child has closed, for whatever reason.
### 0x44: RX: LWS_META_CMD_CLOSE_RQ
- BYTE: channel id
- BYTE: payload length + 0x20
- BYTE: close code MSB
- BYTE: close code LSB
- PAYLOAD: payload (< 123 bytes)
The client requests to close a child connection
### 0x45: TX: LWS_META_CMD_WRITE
- BYTE: channel id
Normal write of payload n from lws-meta perspective is actually
LWS_META_CMD_WRITE, channel id, then (n - 2) bytes of payload
The command only appears at the start of a message, continuations do
not have the command.
## Protocol Notes
- Once the subchannel is up, overhead is only +2 bytes per message
- Close reasons are supported in both directions
- Ping and Pong are only supported at the lws-meta level, using normal ws ping and pong packets.
- Only the final close of the tcp lws-meta connection itself goes out as
a normal ws close frame. Subchannels close is done in a normal TEXT
message using LWS_META_CMD_CLOSE_RQ and then the close packet payload.
This is so intermediaries do not mistake subchannel closures for the
tcp / ws link going down.
Messages that start with LWS_META_CMD_OPEN_SUBCHANNEL only contain those
commands but may contain any number of them for the whole duration of the
message. The lws-meta js support collects child open requests made before
the parent lws-meta connection is open, and dumps them all in a single
message when it does open.
Messages that start with LWS_META_CMD_OPEN_RESULT or LWS_META_CMD_CLOSE_NOTIFY
only contain those two commands, but they may contain any number of them
for the whole duration of the message.
# Current Implemention Limitations
- only server side is supported in lws. The client side JS for
a browser is supported.
- max number of child connections per parent at the moment is 8
- child connection URL paramter when opening the connection is
ignored
- there is no ah attached when the child connections are
established inside the lws-meta parent. So header access
functions will fail.

View file

@ -1,601 +0,0 @@
Notes about lwsws
=================
@section lwsws Libwebsockets Web Server
lwsws is an implementation of a very lightweight, ws-capable generic web
server, which uses libwebsockets to implement everything underneath.
If you are basically implementing a standalone server with lws, you can avoid
reinventing the wheel and use a debugged server including lws.
@section lwswsb Build
Just enable -DLWS_WITH_LWSWS=1 at cmake-time.
It enables libuv and plugin support automatically.
NOTICE on Ubuntu, the default libuv package is called "libuv-0.10". This is ancient.
You should replace this with libuv1 and libuv1-dev before proceeding.
@section lwswsc Lwsws Configuration
lwsws uses JSON config files, they're pure JSON except:
- '#' may be used to turn the rest of the line into a comment.
- There's also a single substitution, if a string contains "_lws_ddir_", then that is
replaced with the LWS install data directory path, eg, "/usr/share" or whatever was
set when LWS was built + installed. That lets you refer to installed paths without
having to change the config if your install path was different.
There is a single file intended for global settings
/etc/lwsws/conf
```
# these are the server global settings
# stuff related to vhosts should go in one
# file per vhost in ../conf.d/
{
"global": {
"uid": "48", # apache user
"gid": "48", # apache user
"count-threads": "1",
"server-string": "myserver v1", # returned in http headers
"ws-pingpong-secs": "200", # confirm idle established ws connections this often
"init-ssl": "yes"
}
}
```
and a config directory intended to take one file per vhost
/etc/lwsws/conf.d/warmcat.com
```
{
"vhosts": [{
"name": "warmcat.com",
"port": "443",
"interface": "eth0", # optional
"host-ssl-key": "/etc/pki/tls/private/warmcat.com.key", # if given enable ssl
"host-ssl-cert": "/etc/pki/tls/certs/warmcat.com.crt",
"host-ssl-ca": "/etc/pki/tls/certs/warmcat.com.cer",
"mounts": [{ # autoserve
"mountpoint": "/",
"origin": "file:///var/www/warmcat.com",
"default": "index.html"
}]
}]
}
```
To get started quickly, an example config reproducing the old test server
on port 7681, non-SSL is provided. To set it up
```
# mkdir -p /etc/lwsws/conf.d /var/log/lwsws
# cp ./lwsws/etc-lwsws-conf-EXAMPLE /etc/lwsws/conf
# cp ./lwsws/etc-lwsws-conf.d-localhost-EXAMPLE /etc/lwsws/conf.d/test-server
# sudo lwsws
```
@section lwsogo Other Global Options
- `reject-service-keywords` allows you to return an HTTP error code and message of your choice
if a keyword is found in the user agent
```
"reject-service-keywords": [{
"scumbot": "404 Not Found"
}]
```
- `timeout-secs` lets you set the global timeout for various network-related
operations in lws, in seconds. It defaults to 5.
@section lwswsv Lwsws Vhosts
One server can run many vhosts, where SSL is in use SNI is used to match
the connection to a vhost and its vhost-specific SSL keys during SSL
negotiation.
Listing multiple vhosts looks something like this
```
{
"vhosts": [ {
"name": "localhost",
"port": "443",
"host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key",
"host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
"host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer",
"mounts": [{
"mountpoint": "/",
"origin": "file:///var/www/libwebsockets.org",
"default": "index.html"
}, {
"mountpoint": "/testserver",
"origin": "file:///usr/local/share/libwebsockets-test-server",
"default": "test.html"
}],
# which protocols are enabled for this vhost, and optional
# vhost-specific config options for the protocol
#
"ws-protocols": [{
"warmcat,timezoom": {
"status": "ok"
}
}]
},
{
"name": "localhost",
"port": "7681",
"host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key",
"host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
"host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer",
"mounts": [{
"mountpoint": "/",
"origin": ">https://localhost"
}]
},
{
"name": "localhost",
"port": "80",
"mounts": [{
"mountpoint": "/",
"origin": ">https://localhost"
}]
}
]
}
```
That sets up three vhosts all called "localhost" on ports 443 and 7681 with SSL, and port 80 without SSL but with a forced redirect to https://localhost
@section lwswsvn Lwsws Vhost name and port sharing
The vhost name field is used to match on incoming SNI or Host: header, so it
must always be the host name used to reach the vhost externally.
- Vhosts may have the same name and different ports, these will each create a
listening socket on the appropriate port.
- Vhosts may also have the same port and different name: these will be treated as
true vhosts on one listening socket and the active vhost decided at SSL
negotiation time (via SNI) or if no SSL, then after the Host: header from
the client has been parsed.
@section lwswspr Lwsws Protocols
Vhosts by default have available the union of any initial protocols from context creation time, and
any protocols exposed by plugins.
Vhosts can select which plugins they want to offer and give them per-vhost settings using this syntax
```
"ws-protocols": [{
"warmcat-timezoom": {
"status": "ok"
}
}]
```
The "x":"y" parameters like "status":"ok" are made available to the protocol during its per-vhost
LWS_CALLBACK_PROTOCOL_INIT (@in is a pointer to a linked list of struct lws_protocol_vhost_options
containing the name and value pointers).
To indicate that a protocol should be used when no Protocol: header is sent
by the client, you can use "default": "1"
```
"ws-protocols": [{
"warmcat-timezoom": {
"status": "ok",
"default": "1"
}
}]
```
@section lwswsovo Lwsws Other vhost options
- If the three options `host-ssl-cert`, `host-ssl-ca` and `host-ssl-key` are given, then the vhost supports SSL.
Each vhost may have its own certs, SNI is used during the initial connection negotiation to figure out which certs to use by the server name it's asking for from the request DNS name.
- `keeplive-timeout` (in secs) defaults to 60 for lwsws, it may be set as a vhost option
- `interface` lets you specify which network interface to listen on, if not given listens on all
- "`unix-socket`": "1" causes the unix socket specified in the interface option to be used instead of an INET socket
- "`sts`": "1" causes lwsws to send a Strict Transport Security header with responses that informs the client he should never accept to connect to this address using http. This is needed to get the A+ security rating from SSL Labs for your server.
- "`access-log`": "filepath" sets where apache-compatible access logs will be written
- `"enable-client-ssl"`: `"1"` enables the vhost's client SSL context, you will need this if you plan to create client conections on the vhost that will use SSL. You don't need it if you only want http / ws client connections.
- "`ciphers`": "<cipher list>" sets the allowed list of ciphers and key exchange protocols for the vhost. The default list is restricted to only those providing PFS (Perfect Forward Secrecy) on the author's Fedora system.
If you need to allow weaker ciphers,you can provide an alternative list here per-vhost.
- "`ecdh-curve`": "<curve name>" The default ecdh curve is "prime256v1", but you can override it here, per-vhost
- "`noipv6`": "on" Disable ipv6 completely for this vhost
- "`ipv6only`": "on" Only allow ipv6 on this vhost / "off" only allow ipv4 on this vhost
- "`ssl-option-set`": "<decimal>" Sets the SSL option flag value for the vhost.
It may be used multiple times and OR's the flags together.
The values are derived from /usr/include/openssl/ssl.h
```
# define SSL_OP_NO_TLSv1_1 0x10000000L
```
would equate to
```
"`ssl-option-set`": "268435456"
```
- "`ssl-option-clear'": "<decimal>" Clears the SSL option flag value for the vhost.
It may be used multiple times and OR's the flags together.
- "`headers':: [{ "header1": "h1value", "header2": "h2value" }]
allows you to set arbitrary headers on every file served by the vhost
recommended vhost headers for good client security are
```
"headers": [{
"Content-Security-Policy": "script-src 'self'",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"X-Frame-Options": "SAMEORIGIN"
}]
```
@section lwswsm Lwsws Mounts
Where mounts are given in the vhost definition, then directory contents may
be auto-served if it matches the mountpoint.
Mount protocols are used to control what kind of translation happens
- file:// serve the uri using the remainder of the url past the mountpoint based on the origin directory.
Eg, with this mountpoint
```
{
"mountpoint": "/",
"origin": "file:///var/www/mysite.com",
"default": "/"
}
```
The uri /file.jpg would serve /var/www/mysite.com/file.jpg, since / matched.
- ^http:// or ^https:// these cause any url matching the mountpoint to issue a redirect to the origin url
- cgi:// this causes any matching url to be given to the named cgi, eg
```
{
"mountpoint": "/git",
"origin": "cgi:///var/www/cgi-bin/cgit",
"default": "/"
}, {
"mountpoint": "/cgit-data",
"origin": "file:///usr/share/cgit",
"default": "/"
},
```
would cause the url /git/myrepo to pass "myrepo" to the cgi /var/www/cgi-bin/cgit and send the results to the client.
- http:// or https:// these perform reverse proxying, serving the remote origin content from the mountpoint. Eg
```
{
"mountpoint": "/proxytest",
"origin": "https://libwebsockets.org"
}
```
This will cause your local url `/proxytest` to serve content fetched from libwebsockets.org over ssl; whether it's served from your server using ssl is unrelated and depends how you configured your local server. Notice if you will use the proxying feature, `LWS_WITH_HTTP_PROXY` is required to be enabled at cmake, and for `https` proxy origins, your lwsws configuration must include `"init-ssl": "1"` and the vhost with the proxy mount must have `"enable-client-ssl": "1"`, even if you are not using ssl to serve.
`/proxytest/abc`, or `/proxytest/abc?def=ghi` etc map to the origin + the part past `/proxytest`, so links and img src urls etc work as do all urls under the origin path.
In addition link and src urls in the document are rewritten so / or the origin url part are rewritten to the mountpoint part.
@section lwswsomo Lwsws Other mount options
1) Some protocols may want "per-mount options" in name:value format. You can
provide them using "pmo"
{
"mountpoint": "/stuff",
"origin": "callback://myprotocol",
"pmo": [{
"myname": "myvalue"
}]
}
2) When using a cgi:// protcol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
```
{
"mountpoint": "/git",
"origin": "cgi:///var/www/cgi-bin/cgit",
"default": "/",
"cgi-env": [{
"CGIT_CONFIG": "/etc/cgitrc/libwebsockets.org"
}]
}
```
This allows you to customize one cgi depending on the mountpoint (and / or vhost).
3) It's also possible to set the cgi timeout (in secs) per cgi:// mount, like this
```
"cgi-timeout": "30"
```
4) `callback://` protocol may be used when defining a mount to associate a
named protocol callback with the URL namespace area. For example
```
{
"mountpoint": "/formtest",
"origin": "callback://protocol-post-demo"
}
```
All handling of client access to /formtest[anything] will be passed to the
callback registered to the protocol "protocol-post-demo".
This is useful for handling POST http body content or general non-cgi http
payload generation inside a plugin.
See the related notes in README.coding.md
5) Cache policy of the files in the mount can also be set. If no
options are given, the content is marked uncacheable.
```
{
"mountpoint": "/",
"origin": "file:///var/www/mysite.com",
"cache-max-age": "60", # seconds
"cache-reuse": "1", # allow reuse at client at all
"cache-revalidate": "1", # check it with server each time
"cache-intermediaries": "1" # allow intermediary caches to hold
}
```
6) You can also define a list of additional mimetypes per-mount
```
"extra-mimetypes": {
".zip": "application/zip",
".doc": "text/evil"
}
```
Normally a file suffix MUST match one of the canned mimetypes or one of the extra
mimetypes, or the file is not served. This adds a little bit of security because
even if there is a bug somewhere and the mount dirs are circumvented, lws will not
serve, eg, /etc/passwd.
If you provide an extra mimetype entry
"*": ""
Then any file is served, if the mimetype was not known then it is served without a
Content-Type: header.
7) A mount can be protected by HTTP Basic Auth. This only makes sense when using
https, since otherwise the password can be sniffed.
You can add a `basic-auth` entry on a mount like this
```
{
"mountpoint": "/basic-auth",
"origin": "file://_lws_ddir_/libwebsockets-test-server/private",
"basic-auth": "/var/www/balogins-private"
}
```
Before serving anything, lws will signal to the browser that a username / password
combination is required, and it will pop up a dialog. When the user has filled it
in, lwsws checks the user:password string against the text file named in the `basic-auth`
entry.
The file should contain user:pass one per line
```
testuser:testpass
myuser:hispass
```
The file should be readable by lwsws, and for a little bit of extra security not
have a file suffix, so lws would reject to serve it even if it could find it on
a mount.
@section lwswspl Lwsws Plugins
Protcols and extensions may also be provided from "plugins", these are
lightweight dynamic libraries. They are scanned for at init time, and
any protocols and extensions found are added to the list given at context
creation time.
Protocols receive init (LWS_CALLBACK_PROTOCOL_INIT) and destruction
(LWS_CALLBACK_PROTOCOL_DESTROY) callbacks per-vhost, and there are arrangements
they can make per-vhost allocations and get hold of the correct pointer from
the wsi at the callback.
This allows a protocol to choose to strictly segregate data on a per-vhost
basis, and also allows the plugin to handle its own initialization and
context storage.
To help that happen conveniently, there are some new apis
- lws_vhost_get(wsi)
- lws_protocol_get(wsi)
- lws_callback_on_writable_all_protocol_vhost(vhost, protocol)
- lws_protocol_vh_priv_zalloc(vhost, protocol, size)
- lws_protocol_vh_priv_get(vhost, protocol)
dumb increment, mirror and status protocol plugins are provided as examples.
@section lwswsplaplp Additional plugin search paths
Packages that have their own lws plugins can install them in their own
preferred dir and ask lwsws to scan there by using a config fragment
like this, in its own conf.d/ file managed by the other package
```
{
"global": {
"plugin-dir": "/usr/local/share/coherent-timeline/plugins"
}
}
```
@section lwswsssp lws-server-status plugin
One provided protocol can be used to monitor the server status.
Enable the protocol like this on a vhost's ws-protocols section
```
"lws-server-status": {
"status": "ok",
"update-ms": "5000"
}
```
`"update-ms"` is used to control how often updated JSON is sent on a ws link.
And map the provided HTML into the vhost in the mounts section
```
{
"mountpoint": "/server-status",
"origin": "file:///usr/local/share/libwebsockets-test-server/server-status",
"default": "server-status.html"
}
```
You might choose to put it on its own vhost which has "interface": "lo", so it's not
externally visible, or use the Basic Auth support to require authentication to
access it.
`"hide-vhosts": "{0 | 1}"` lets you control if information about your vhosts is included.
Since this includes mounts, you might not want to leak that information, mount names,
etc.
`"filespath":"{path}"` lets you give a server filepath which is read and sent to the browser
on each refresh. For example, you can provide server temperature information on most
Linux systems by giving an appropriate path down /sys.
This may be given multiple times.
@section lwswsreload Lwsws Configuration Reload
You may send lwsws a `HUP` signal, by, eg
```
$ sudo killall -HUP lwsws
```
This causes lwsws to "deprecate" the existing lwsws process, and remove and close all of
its listen sockets, but otherwise allowing it to continue to run, until all
of its open connections close.
When a deprecated lwsws process has no open connections left, it is destroyed
automatically.
After sending the SIGHUP to the main lwsws process, a new lwsws process, which can
pick up the newly-available listen sockets, and use the current configuration
files, is automatically started.
The new configuration may differ from the original one in arbitrary ways, the new
context is created from scratch each time without reference to the original one.
Notes
1) Protocols that provide a "shared world" like mirror will have as many "worlds"
as there are lwsws processes still active. People connected to a deprecated lwsws
process remain connected to the existing peers.
But any new connections will apply to the new lwsws process, which does not share
per-vhost "shared world" data with the deprecated process. That means no new
connections on the deprecated context, ie a "shrinking world" for those guys, and a
"growing world" for people who connect after the SIGHUP.
2) The new lwsws process owes nothing to the previous one. It starts with fresh
plugins, fresh configuration, fresh root privileges if that how you start it.
The plugins may have been updated in arbitrary ways including struct size changes
etc, and lwsws or lws may also have been updated arbitrarily.
3) A root parent process is left up that is not able to do anything except
respond to SIGHUP or SIGTERM. Actual serving and network listening etc happens
in child processes which use the privileges set in the lwsws config files.
@section lwswssysd Lwsws Integration with Systemd
lwsws needs a service file like this as `/usr/lib/systemd/system/lwsws.service`
```
[Unit]
Description=Libwebsockets Web Server
After=syslog.target
[Service]
ExecStart=/usr/local/bin/lwsws
ExecReload=/usr/bin/killall -s SIGHUP lwsws ; sleep 1 ; /usr/local/bin/lwsws
StandardError=null
[Install]
WantedBy=multi-user.target
```
You can find this prepared in `./lwsws/usr-lib-systemd-system-lwsws.service`
@section lwswslr Lwsws Integration with logrotate
For correct operation with logrotate, `/etc/logrotate.d/lwsws` (if that's
where we're putting the logs) should contain
```
/var/log/lwsws/*log {
copytruncate
missingok
notifempty
delaycompress
}
```
You can find this prepared in `/lwsws/etc-logrotate.d-lwsws`
Prepare the log directory like this
```
sudo mkdir /var/log/lwsws
sudo chmod 700 /var/log/lwsws
```
@section lwswsgdb Debugging lwsws with gdb
Hopefully you won't need to debug lwsws itself, but you may want to debug your plugins. start lwsws like this to have everything running under gdb
```
sudo gdb -ex "set follow-fork-mode child" -ex "run" --args /usr/local/bin/lwsws
```
this will give nice backtraces in lwsws itself and in plugins, if they were built with symbols.
@section lwswsvgd Running lwsws under valgrind
You can just run lwsws under galgrind as usual and get valid results. However the results / analysis part of valgrind runs
after the plugins have removed themselves, this means valgrind backtraces into plugin code is opaque, without
source-level info because the dynamic library is gone.
There's a simple workaround, use LD_PRELOAD=<plugin.so> before running lwsws, this has the loader bring the plugin
in before executing lwsws as if it was a direct dependency. That means it's still mapped until the whole process
exits after valgtind has done its thing.

View file

@ -1,38 +1,29 @@
[![Travis Build Status](https://travis-ci.org/warmcat/libwebsockets.svg)](https://travis-ci.org/warmcat/libwebsockets)
[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/qfasji8mnfnd2r8t?svg=true)](https://ci.appveyor.com/project/lws-team/libwebsockets)
[![Travis Build Status](https://travis-ci.org/warmcat/libwebsockets.png)](https://travis-ci.org/warmcat/libwebsockets)
[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/qfasji8mnfnd2r8t)](https://ci.appveyor.com/project/warmcat/libwebsockets)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3576/badge.svg)](https://scan.coverity.com/projects/3576)
libwebsockets
-------------
News
----
v2.3 is out... see the changelog https://github.com/warmcat/libwebsockets/blob/v2.3-stable/changelog
ESP32 is now supported in lws! Download the
- factory https://github.com/warmcat/lws-esp32-factory and
- test server app https://github.com/warmcat/lws-esp32-test-server-demos
This is the libwebsockets C library for lightweight websocket clients and
servers. For support, visit
https://libwebsockets.org
https://github.com/warmcat/libwebsockets
http://libwebsockets.org
and consider joining the project mailing list at
https://libwebsockets.org/mailman/listinfo/libwebsockets
http://ml.libwebsockets.org/mailman/listinfo/libwebsockets
You can get the latest version of the library from git:
You can get the latest version of the library from git
- http://git.libwebsockets.org
- https://github.com/warmcat/libwebsockets
- https://libwebsockets.org/git
Doxygen API docs for master: https://libwebsockets.org/lws-api-doc-master/html/index.html
for more information:
- [README.build.md](README.build.md) - information on building the library
- [README.coding.md](README.coding.md) - information for writing code using the library
- [README.test-apps.md](README.test-apps.md) - information about the test apps built with the library
After libwebsockets 1.3, tags will be signed using a key corresponding to this public key

View file

@ -1,43 +0,0 @@
Debugging problems
==================
Library is a component
----------------------
As a library, lws is always just a component in a bigger application.
When users have a problem involving lws, what is happening in the bigger
application is usually critical to understand what is going on (and where the
solution lies).
Many users are able to share their sources, but others decide not to, for
presumed "commercial advantage" or whatever. (In any event, it can be painful
looking through large chunks of someone else's sources for problems when that
is not the library author's responsibility.)
This makes answering questions like "what is wrong with my code I am not
going to show you?" or even "what is wrong with my code?" very difficult.
Even if it's clear there is a problem somewhere, it cannot be understood or
reproduced by anyone else if it needs user code that isn't provided.
The biggest question is, "is this an lws problem actually"?
Use the test apps as sanity checks
----------------------------------
The test server and client are extremely useful for sanity checks and debugging
guidance.
- test apps work on your platform, then either
- your user code is broken, align it to how the test apps work, or,
- something from your code is required to show an lws problem, provide a
minimal patch on a test app so it can be reproduced
- test apps break on your platform, but work on, eg, x86_64, either
- toolchain or platform-specific (eg, OS) issue, or
- lws platform support issue
- test apps break everywhere
- sounds like lws problem, info to reproduce and / or a patch is appreciated

View file

@ -1,58 +1,10 @@
Overview of lws test apps
=========================
Are you building a client? You just need to look at the test client
[libwebsockets-test-client](test-server/test-client.c).
If you are building a standalone server, there are three choices, in order of
preferability.
1) lwsws + protocol plugins
Lws provides a generic web server app that can be configured with JSON
config files. https://libwebsockets.org itself uses this method.
With lwsws handling the serving part, you only need to write an lws protocol
plugin. See [plugin-standalone](plugin-standalone) for an example of how
to do that outside lws itself, using lws public apis.
$ cmake .. -DLWS_WITH_LWSWS=1
See [README.lwsws.md](README.lwsws.md) for information on how to configure
lwsws.
NOTE this method implies libuv is used by lws, to provide crossplatform
implementations of timers, dynamic lib loading etc for plugins and lwsws.
2) test-server-v2.0.c
This method lets you configure web serving in code, instead of using lwsws.
Plugins are still used, which implies libuv needed.
$ cmake .. -DLWS_WITH_PLUGINS=1
See [test-server-v2.0.c](test-server/test-server-v2.0.c)
3) protocols in the server app
This is the original way lws implemented servers, plugins and libuv are not
required, but without plugins separating the protocol code directly, the
combined code is all squidged together and is much less maintainable.
This method is still supported in lws but all ongoing and future work is
being done in protocol plugins only.
Notes about lws test apps
=========================
@section tsb Testing server with a browser
Testing server with a browser
-----------------------------
If you run [libwebsockets-test-server](test-server/test-server.c) and point your browser
(eg, Chrome) to
http://127.0.0.1:7681
http://127.0.0.1:7681
It will fetch a script in the form of `test.html`, and then run the
script in there on the browser to open a websocket connection.
@ -62,7 +14,8 @@ By default the test server logs to both stderr and syslog, you can control
what is logged using `-d <log level>`, see later.
@section tsd Running test server as a Daemon
Running test server as a Daemon
-------------------------------
You can use the -D option on the test server to have it fork into the
background and return immediately. In this daemonized mode all stderr is
@ -73,9 +26,11 @@ of the master process, and deletes this file when the master process
terminates.
To stop the daemon, do
```bash
$ kill `cat /tmp/.lwsts-lock`
```
$ kill cat /tmp/.lwsts-lock
```
If it finds a stale lock (the pid mentioned in the file does not exist
any more) it will delete the lock and create a new one during startup.
@ -83,16 +38,19 @@ If the lock is valid, the daemon will exit with a note on stderr that
it was already running.
@section sssl Using SSL on the server side
Using SSL on the server side
----------------------------
To test it using SSL/WSS, just run the test server with
```bash
$ libwebsockets-test-server --ssl
```
$ libwebsockets-test-server --ssl
```
and use the URL
```
https://127.0.0.1:7681
```
https://127.0.0.1:7681
The connection will be entirely encrypted using some generated
certificates that your browser will not accept, since they are
not signed by any real Certificate Authority. Just accept the
@ -103,21 +61,15 @@ same.
[test-server.c](test-server/test-server.c) is all that is needed to use libwebsockets for
serving both the script html over http and websockets.
@section lwstsdynvhost Dynamic Vhosts
You can send libwebsockets-test-server or libwebsockets-test-server-v2.0 a SIGUSR1
to toggle the creation and destruction of an identical second vhost on port + 1.
This is intended as a test and demonstration for how to bring up and remove
vhosts dynamically.
@section wscl Testing websocket client support
Testing websocket client support
--------------------------------
If you run the test server as described above, you can also
connect to it using the test client as well as a browser.
```
$ libwebsockets-test-client localhost
```bash
$ libwebsockets-test-client localhost
```
will by default connect to the test server on localhost:7681
@ -126,89 +78,76 @@ 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
```
$ libwebsockets-test-client localhost --ssl -s
```
the -s tells it to accept the default self-signed cert from the server,
otherwise it will strictly fail the connection if there is no CA cert to
validate the server's certificate.
@section choosingts Choosing between test server variations
If you will be doing standalone serving with lws, ideally you should avoid
making your own server at all, and use lwsws with your own protocol plugins.
The second best option is follow test-server-v2.0.c, which uses a mount to
autoserve a directory, and lws protocol plugins for ws, without needing any
user callback code (other than what's needed in the protocol plugin).
For those two options libuv is needed to support the protocol plugins, if
that's not possible then the other variations with their own protocol code
should be considered.
@section echo Testing simple echo
Testing simple echo
-------------------
You can test against `echo.websockets.org` as a sanity test like
this (the client connects to port `80` by default):
```
$ libwebsockets-test-echo --client echo.websocket.org
```bash
$ libwebsockets-test-echo --client echo.websocket.org
```
This echo test is of limited use though because it doesn't
negotiate any protocol. You can run the same test app as a
local server, by default on localhost:7681
```bash
$ libwebsockets-test-echo
```
$ libwebsockets-test-echo
```
and do the echo test against the local echo server
```bash
$ libwebsockets-test-echo --client localhost --port 7681
```
$ libwebsockets-test-echo --client localhost --port 7681
```
If you add the `--ssl` switch to both the client and server, you can also test
with an encrypted link.
@section tassl Testing SSL on the client side
Testing SSL on the client side
------------------------------
To test SSL/WSS client action, just run the client test with
```bash
$ libwebsockets-test-client localhost --ssl
```
$ libwebsockets-test-client localhost --ssl
```
By default the client test applet is set to accept self-signed
By default the client test applet is set to accept selfsigned
certificates used by the test server, this is indicated by the
`use_ssl` var being set to `2`. Set it to `1` to reject any server
certificate that it doesn't have a trusted CA cert for.
@section taping Using the websocket ping utility
Using the websocket ping utility
--------------------------------
libwebsockets-test-ping connects as a client to a remote
websocket server and pings it like the
websocket server using 04 protocol and pings it like the
normal unix ping utility.
```bash
$ libwebsockets-test-ping localhost
handshake OK for protocol lws-mirror-protocol
Websocket PING localhost.localdomain (127.0.0.1) 64 bytes of data.
64 bytes from localhost: req=1 time=0.1ms
64 bytes from localhost: req=2 time=0.1ms
64 bytes from localhost: req=3 time=0.1ms
64 bytes from localhost: req=4 time=0.2ms
64 bytes from localhost: req=5 time=0.1ms
64 bytes from localhost: req=6 time=0.2ms
64 bytes from localhost: req=7 time=0.2ms
64 bytes from localhost: req=8 time=0.1ms
^C
--- localhost.localdomain websocket ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7458ms
rtt min/avg/max = 0.110/0.185/0.218 ms
$
```
$ libwebsockets-test-ping localhost
handshake OK for protocol lws-mirror-protocol
Websocket PING localhost.localdomain (127.0.0.1) 64 bytes of data.
64 bytes from localhost: req=1 time=0.1ms
64 bytes from localhost: req=2 time=0.1ms
64 bytes from localhost: req=3 time=0.1ms
64 bytes from localhost: req=4 time=0.2ms
64 bytes from localhost: req=5 time=0.1ms
64 bytes from localhost: req=6 time=0.2ms
64 bytes from localhost: req=7 time=0.2ms
64 bytes from localhost: req=8 time=0.1ms
^C
--- localhost.localdomain websocket ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7458ms
rtt min/avg/max = 0.110/0.185/0.218 ms
$
```
By default it sends 64 byte payload packets using the 04
PING packet opcode type. You can change the payload size
using the `-s=` flag, up to a maximum of 125 mandated by the
@ -228,44 +167,49 @@ Before you can even use the PING opcode that is part of the
standard, you must complete a handshake with a specified
protocol. By default lws-mirror-protocol is used which is
supported by the test server. But if you are using it on
another server, you can specify the protocol to handshake with
another server, you can specify the protcol to handshake with
by `--protocol=protocolname`
@section ta fraggle Fraggle test app
Fraggle test app
----------------
By default it runs in server mode
```bash
$ libwebsockets-test-fraggle
libwebsockets test fraggle
(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> licensed under LGPL2.1
Compiled with SSL support, not using it
Listening on port 7681
server sees client connect
accepted v06 connection
Spamming 360 random fragments
Spamming session over, len = 371913. sum = 0x2D3C0AE
Spamming 895 random fragments
Spamming session over, len = 875970. sum = 0x6A74DA1
...
```
$ libwebsockets-test-fraggle
libwebsockets test fraggle
(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> licensed under LGPL2.1
Compiled with SSL support, not using it
Listening on port 7681
server sees client connect
accepted v06 connection
Spamming 360 random fragments
Spamming session over, len = 371913. sum = 0x2D3C0AE
Spamming 895 random fragments
Spamming session over, len = 875970. sum = 0x6A74DA1
...
```
You need to run a second session in client mode, you have to
give the `-c` switch and the server address at least:
```bash
$ libwebsockets-test-fraggle -c localhost
libwebsockets test fraggle
(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> licensed under LGPL2.1
Client mode
Connecting to localhost:7681
denied deflate-stream extension
handshake OK for protocol fraggle-protocol
client connects to server
EOM received 371913 correctly from 360 fragments
EOM received 875970 correctly from 895 fragments
EOM received 247140 correctly from 258 fragments
EOM received 695451 correctly from 692 fragments
...
```
$ libwebsockets-test-fraggle -c localhost
libwebsockets test fraggle
(C) Copyright 2010-2011 Andy Green <andy@warmcat.com> licensed under LGPL2.1
Client mode
Connecting to localhost:7681
denied deflate-stream extension
handshake OK for protocol fraggle-protocol
client connects to server
EOM received 371913 correctly from 360 fragments
EOM received 875970 correctly from 895 fragments
EOM received 247140 correctly from 258 fragments
EOM received 695451 correctly from 692 fragments
...
```
The fraggle test sends a random number up to 1024 fragmented websocket frames
each of a random size between 1 and 2001 bytes in a single message, then sends
a checksum and starts sending a new randomly sized and fragmented message.
@ -275,30 +219,31 @@ same checksum using websocket framing to see when the message has ended. It
then accepts the server checksum message and compares that to its checksum.
@section taproxy proxy support
proxy support
-------------
The http_proxy environment variable is respected by the client
connection code for both `ws://` and `wss://`. It doesn't support
authentication.
You use it like this
```
$ export http_proxy=myproxy.com:3128
$ libwebsockets-test-client someserver.com
```bash
$ export http_proxy=myproxy.com:3128
$ libwebsockets-test-client someserver.com
```
@section talog debug logging
debug logging
-------------
By default logging of severity "notice", "warn" or "err" is enabled to stderr.
Again by default other logging is compiled in but disabled from printing.
By default debug logs below "notice" in severity are not compiled in. To get
them included, add this option in CMAKE
```
$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
```
If you want to eliminate the debug logging below notice in severity, use the
`--disable-debug` configure option to have it removed from the code by the
preprocesser.
If you want to see more detailed debug logs, you can control a bitfield to
select which logs types may print using the `lws_set_log_level()` api, in the
@ -317,13 +262,15 @@ available are (OR together the numbers to select multiple)
- 512 LATENCY
@section ws13 Websocket version supported
Websocket version supported
---------------------------
The final IETF standard is supported for both client and server, protocol
version 13.
@section latency Latency Tracking
Latency Tracking
----------------
Since libwebsockets runs using `poll()` and a single threaded approach, any
unexpected latency coming from system calls would be bad news. There's now
@ -344,47 +291,3 @@ in the logging is the time taken by this particular attempt. High figures
here may indicate a problem, or if you system is loaded with another app at
that time, such as the browser, it may simply indicate the OS gave preferential
treatment to the other app during that call.
@section autobahn Autobahn Test Suite
Lws can be tested against the autobahn websocket fuzzer.
1) pip install autobahntestsuite
2) wstest -m fuzzingserver
3) Run tests like this
libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=20&agent=libwebsockets" -v -d 65535 -n 1
(this runs test 20)
4) In a browser, go here
http://localhost:8080/test_browser.html
fill in "libwebsockets" in "User Agent Identifier" and press "Update Reports (Manual)"
5) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
file:///projects/libwebsockets/reports/clients/index.html
to see the results
@section autobahnnotes Autobahn Test Notes
1) Autobahn tests the user code + lws implementation. So to get the same
results, you need to follow test-echo.c in terms of user implementation.
2) Two of the tests make no sense for Libwebsockets to support and we fail them.
- Tests 2.10 + 2.11: sends multiple pings on one connection. Lws policy is to
only allow one active ping in flight on each connection, the rest are dropped.
The autobahn test itself admits this is not part of the standard, just someone's
random opinion about how they think a ws server should act. So we will fail
this by design and it is no problem about RFC6455 compliance.

View file

@ -1,8 +1,5 @@
environment:
matrix:
- LWS_METHOD: lwsws
CMAKE_ARGS: -DLWS_WITH_LWSWS=1 -DSQLITE3_INCLUDE_DIRS=C:\assets\sqlite3 -DSQLITE3_LIBRARIES=C:\assets\sqlite3\sqlite3.lib -DLIBUV_INCLUDE_DIRS=C:\assets\libuv\include -DLIBUV_LIBRARIES=C:\assets\libuv\libuv.lib
- LWS_METHOD: default
- LWS_METHOD: noserver
@ -17,19 +14,10 @@ environment:
- LWS_METHOD: nossl
CMAKE_ARGS: -DLWS_WITH_SSL=OFF
install:
- appveyor DownloadFile https://libwebsockets.org:444/win-libuv.zip
- mkdir c:\assets
- mkdir c:\assets\libuv
- 7z x -oc:\assets\libuv win-libuv.zip
# - appveyor DownloadFile https://slproweb.com/download/Win32OpenSSL-1_0_2h.exe
# - appveyor DownloadFile https://libwebsockets.org:444/Win32OpenSSL-1_0_2L.exe
# - Win32OpenSSL-1_0_2L.exe /silent /verysilent /sp- /suppressmsgboxes
- appveyor DownloadFile https://libwebsockets.org:444/nsis-3.0rc1-setup.exe
- cmd /c start /wait nsis-3.0rc1-setup.exe /S /D=C:\nsis
- appveyor DownloadFile https://libwebsockets.org:444/sqlite-dll-win32-x86-3130000.zip
- mkdir c:\assets\sqlite3
- 7z x -oc:\assets\sqlite3 sqlite-dll-win32-x86-3130000.zip
- SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;c:\nsis;%PATH%
- appveyor DownloadFile https://slproweb.com/download/Win32OpenSSL-1_0_2d.exe
- Win32OpenSSL-1_0_2d.exe /silent /verysilent /sp- /suppressmsgboxes
- cinst -y nsis
- SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;%PATH%
build:
build_script:
@ -38,21 +26,19 @@ build_script:
- cmake -DCMAKE_BUILD_TYPE=Release %CMAKE_ARGS% ..
- cmake --build . --config Release
# TODO: Keeps breaking Windows build, should be rewritten using CPack properly instead...
after_build:
- 7z a lws.zip %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.lib %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.exp %APPVEYOR_BUILD_FOLDER%\build\bin\Release\websockets.dll %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h %APPVEYOR_BUILD_FOLDER%\build\lws_config.h %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe
# TODO: Keeps braking Windows build, should be rewritten using CPack properly instead...
#after_build:
# - cd ..
# - cd win32port
# - makensis -DVERSION=%APPVEYOR_BUILD_VERSION% libwebsockets.nsi
artifacts:
- path: lws.zip
name: lws.zip
type: Zip
- name: Installer
path: 'win32port/libwebsockets-*-install.exe'
#cache:
# - C:\OpenSSL-Win32
cache:
- C:\OpenSSL-Win32
matrix:
fast_finish: true

View file

@ -1,41 +0,0 @@
#!/bin/sh
set -u
N=1
OS=`uname`
for i in '1.1.1' '1.1.2' '1.1.3' '1.1.4' '1.1.5' '1.1.6' '1.1.7' '1.1.8' '1.2.1' '1.2.2' '1.2.3' '1.2.4' '1.2.5' '1.2.6' '1.2.7' '1.2.8' '2.1' '2.2' '2.3' '2.4' '2.5' '2.6' '2.7' '2.8' '2.9' '2.10' '2.11' '3.1' '3.2' '3.3' '3.4' '3.5' '3.6' '3.7' '4.1.1' '4.1.2' '4.1.3' '4.1.4' '4.1.5' '4.2.1' '4.2.2' '4.2.3' '4.2.4' '4.2.5' '5.1' '5.2' '5.3' '5.4' '5.5' '5.6' '5.7' '5.8' '5.9' '5.10' '5.11' '5.12' '5.13' '5.14' '5.15' '5.16' '5.17' '5.18' '5.19' '5.20' '6.1.1' '6.1.2' '6.1.3' '6.2.1' '6.2.2' '6.2.3' '6.2.4' '6.3.1' '6.3.2' '6.4.1' '6.4.2' '6.4.3' '6.4.4' '6.5.1' '6.5.2' '6.5.3' '6.5.4' '6.5.5' '6.6.1' '6.6.2' '6.6.3' '6.6.4' '6.6.5' '6.6.6' '6.6.7' '6.6.8' '6.6.9' '6.6.10' '6.6.11' '6.7.1' '6.7.2' '6.7.3' '6.7.4' '6.8.1' '6.8.2' '6.9.1' '6.9.2' '6.9.3' '6.9.4' '6.10.1' '6.10.2' '6.10.3' '6.11.1' '6.11.2' '6.11.3' '6.11.4' '6.11.5' '6.12.1' '6.12.2' '6.12.3' '6.12.4' '6.12.5' '6.12.6' '6.12.7' '6.12.8' '6.13.1' '6.13.2' '6.13.3' '6.13.4' '6.13.5' '6.14.1' '6.14.2' '6.14.3' '6.14.4' '6.14.5' '6.14.6' '6.14.7' '6.14.8' '6.14.9' '6.14.10' '6.15.1' '6.16.1' '6.16.2' '6.16.3' '6.17.1' '6.17.2' '6.17.3' '6.17.4' '6.17.5' '6.18.1' '6.18.2' '6.18.3' '6.18.4' '6.18.5' '6.19.1' '6.19.2' '6.19.3' '6.19.4' '6.19.5' '6.20.1' '6.20.2' '6.20.3' '6.20.4' '6.20.5' '6.20.6' '6.20.7' '6.21.1' '6.21.2' '6.21.3' '6.21.4' '6.21.5' '6.21.6' '6.21.7' '6.21.8' '6.22.1' '6.22.2' '6.22.3' '6.22.4' '6.22.5' '6.22.6' '6.22.7' '6.22.8' '6.22.9' '6.22.10' '6.22.11' '6.22.12' '6.22.13' '6.22.14' '6.22.15' '6.22.16' '6.22.17' '6.22.18' '6.22.19' '6.22.20' '6.22.21' '6.22.22' '6.22.23' '6.22.24' '6.22.25' '6.22.26' '6.22.27' '6.22.28' '6.22.29' '6.22.30' '6.22.31' '6.22.32' '6.22.33' '6.22.34' '6.23.1' '6.23.2' '6.23.3' '6.23.4' '6.23.5' '6.23.6' '6.23.7' '7.1.1' '7.1.2' '7.1.3' '7.1.4' '7.1.5' '7.1.6' '7.3.1' '7.3.2' '7.3.3' '7.3.4' '7.3.5' '7.3.6' '7.5.1' '7.7.1' '7.7.2' '7.7.3' '7.7.4' '7.7.5' '7.7.6' '7.7.7' '7.7.8' '7.7.9' '7.7.10' '7.7.11' '7.7.12' '7.7.13' '7.9.1' '7.9.2' '7.9.3' '7.9.4' '7.9.5' '7.9.6' '7.9.7' '7.9.8' '7.9.9' '7.9.10' '7.9.11' '7.9.12' '7.9.13' '7.13.1' '7.13.2' '9.1.1' '9.1.2' '9.1.3' '9.1.4' '9.1.5' '9.1.6' '9.2.1' '9.2.2' '9.2.3' '9.2.4' '9.2.5' '9.2.6' '9.3.1' '9.3.2' '9.3.3' '9.3.4' '9.3.5' '9.3.6' '9.3.7' '9.3.8' '9.3.9' '9.4.1' '9.4.2' '9.4.3' '9.4.4' '9.4.5' '9.4.6' '9.4.7' '9.4.8' '9.4.9' '9.5.1' '9.5.2' '9.5.3' '9.5.4' '9.5.5' '9.5.6' '9.6.1' '9.6.2' '9.6.3' '9.6.4' '9.6.5' '9.6.6' '9.7.1' '9.7.2' '9.7.3' '9.7.4' '9.7.5' '9.7.6' '9.8.1' '9.8.2' '9.8.3' '9.8.4' '9.8.5' '9.8.6' '10.1.1' '12.1.1' '12.1.2' '12.1.3' '12.1.4' '12.1.5' '12.1.6' '12.1.7' '12.1.8' '12.1.9' '12.1.10' '12.1.11' '12.1.12' '12.1.13' '12.1.14' '12.1.15' '12.1.16' '12.1.17' '12.1.18' '12.2.1' '12.2.2' '12.2.3' '12.2.4' '12.2.5' '12.2.6' '12.2.7' '12.2.8' '12.2.9' '12.2.10' '12.2.11' '12.2.12' '12.2.13' '12.2.14' '12.2.15' '12.2.16' '12.2.17' '12.2.18' '12.3.1' '12.3.2' '12.3.3' '12.3.4' '12.3.5' '12.3.6' '12.3.7' '12.3.8' '12.3.9' '12.3.10' '12.3.11' '12.3.12' '12.3.13' '12.3.14' '12.3.15' '12.3.16' '12.3.17' '12.3.18' '12.4.1' '12.4.2' '12.4.3' '12.4.4' '12.4.5' '12.4.6' '12.4.7' '12.4.8' '12.4.9' '12.4.10' '12.4.11' '12.4.12' '12.4.13' '12.4.14' '12.4.15' '12.4.16' '12.4.17' '12.4.18' '12.5.1' '12.5.2' '12.5.3' '12.5.4' '12.5.5' '12.5.6' '12.5.7' '12.5.8' '12.5.9' '12.5.10' '12.5.11' '12.5.12' '12.5.13' '12.5.14' '12.5.15' '12.5.16' '12.5.17' '12.5.18' '13.1.1' '13.1.2' '13.1.3' '13.1.4' '13.1.5' '13.1.6' '13.1.7' '13.1.8' '13.1.9' '13.1.10' '13.1.11' '13.1.12' '13.1.13' '13.1.14' '13.1.15' '13.1.16' '13.1.17' '13.1.18' '13.2.1' '13.2.2' '13.2.3' '13.2.4' '13.2.5' '13.2.6' '13.2.7' '13.2.8' '13.2.9' '13.2.10' '13.2.11' '13.2.12' '13.2.13' '13.2.14' '13.2.15' '13.2.16' '13.2.17' '13.2.18' '13.3.1' '13.3.2' '13.3.3' '13.3.4' '13.3.5' '13.3.6' '13.3.7' '13.3.8' '13.3.9' '13.3.10' '13.3.11' '13.3.12' '13.3.13' '13.3.14' '13.3.15' '13.3.16' '13.3.17' '13.3.18' '13.4.1' '13.4.2' '13.4.3' '13.4.4' '13.4.5' '13.4.6' '13.4.7' '13.4.8' '13.4.9' '13.4.10' '13.4.11' '13.4.12' '13.4.13' '13.4.14' '13.4.15' '13.4.16' '13.4.17' '13.4.18' '13.5.1' '13.5.2' '13.5.3' '13.5.4' '13.5.5' '13.5.6' '13.5.7' '13.5.8' '13.5.9' '13.5.10' '13.5.11' '13.5.12' '13.5.13' '13.5.14' '13.5.15' '13.5.16' '13.5.17' '13.5.18' '13.6.1' '13.6.2' '13.6.3' '13.6.4' '13.6.5' '13.6.6' '13.6.7' '13.6.8' '13.6.9' '13.6.10' '13.6.11' '13.6.12' '13.6.13' '13.6.14' '13.6.15' '13.6.16' '13.6.17' '13.6.18' '13.7.1' '13.7.2' '13.7.3' '13.7.4' '13.7.5' '13.7.6' '13.7.7' '13.7.8' '13.7.9' '13.7.10' '13.7.11' '13.7.12' '13.7.13' '13.7.14' '13.7.15' '13.7.16' '13.7.17' '13.7.18' ; do
libwebsockets-test-echo --client 127.0.0.1 --port 9001 -u "/runCase?case=$N&agent=libwebsockets" -v -n 1 &
C=99
while [ $C -gt 8 ] ; do
if [ $OS=SunOS ] ; then
C=`ps -ef | grep libwebsockets-test-echo | wc -l`
else
C=`ps fax | grep libwebsockets-test-echo | wc -l`
fi
if [ $C -gt 8 ] ; then
sleep 1s
fi
done
N=$(( $N + 1 ))
done
echo "waiting for forks to complete..."
while [ 1 ] ; do
if [ $OS=SunOS ] ; then
n=`ps -ef | grep libwebsocket | grep -v grep | wc -l`
else
n=`ps fax | grep libwebsocket | grep -v grep | wc -l`
fi
echo "$n forks running..."
if [ $n -eq 0 ] ; then
echo "Completed"
exit 0
fi
sleep 1s
done

1404
changelog

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
COMPONENT_OWNBUILDTARGET:= 1
CROSS_PATH1:=$(shell which xtensa-esp32-elf-gcc )
CROSS_PATH:= $(shell dirname $(CROSS_PATH1) )/..
#-DLWS_USE_BORINGSSL=1 \
# -DOPENSSL_ROOT_DIR="${PWD}/../../boringssl" \
# -DOPENSSL_LIBRARIES="${PWD}/../../boringssl/build/ssl/libssl.a;${PWD}/../../boringssl/build/crypto/libcrypto.a" \
# -DOPENSSL_INCLUDE_DIRS="${PWD}/../../boringssl/include" \
# -DNDEBUG=1 after cflags
# -DOPENSSL_LIBRARIES=x \
# -DCOMPONENT_PATH=$(COMPONENT_PATH) \
.PHONY: build
build:
cd $(COMPONENT_BUILD_DIR) ; \
echo "doing lws cmake" ; \
cmake $(COMPONENT_PATH) -DLWS_C_FLAGS="$(CFLAGS) -DNDEBUG=1" \
-DIDF_PATH=$(IDF_PATH) \
-DCROSS_PATH=$(CROSS_PATH) \
-DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/cross-esp32.cmake \
-DCMAKE_BUILD_TYPE=RELEASE \
-DOPENSSL_INCLUDE_DIR=${IDF_PATH}/components/openssl/include \
-DLWS_WITH_STATS=0 \
-DZLIB_LIBRARY=$(BUILD_DIR_BASE)/zlib/libzlib.a \
-DZLIB_INCLUDE_DIR=$(COMPONENT_PATH)/../zlib \
-DLWS_WITH_ESP32=1 ;\
make && \
cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a
clean: myclean
myclean:
rm -rf ./build
INCLUDES := $(INCLUDES) -I build/

View file

@ -1,52 +0,0 @@
ABI Compatility Tracking
========================
This directory contains files that can be used to generate an ABI compatibility
timeline for libwebsockets. This gives users an idea of where the library has
changed and can be used by the developers to see when incompatible changes have
been introduced and either increase the library SO version or fix the changes.
The tools used are the abi-\* family available at https://github.com/lvc/ and
some example output is here: http://abi-laboratory.pro/tracker/timeline/libuv/
The tools download existing source tarballs and git repository to generate this
data, so past versions are compared and in-development code can be compared as
well.
Although the application is not being included here, FYI the license is dual
LGPL2 / GPL2 at your choice.
Installation
------------
The author provides an easy way to install the various tools he provides:
git clone https://github.com/lvc/installer
cd installer
make prefix=/usr/local target=abi-tracker
It will also list any dependencies that you need to install through normal
means. (Although in the case of needing "elfutils-libelf-devel", it may
crash during install of vtable-dumper without giving a nice list)
Generating the output
---------------------
Use the `lws-abi-update.sh` script to download the source files, build them and
generate the output html. The output can be deployed to a directory on a web
server for example. Modify the commented line in lws-abi-update.sh to do this.
As it is configured, lws-abi-update.sh will only download new source - ones
that it hasn't built before - so is suitable for use with a cron job.
Viewing the output
------------------
The best place to start looking at the data is the `timeline/libwebsockets`
directory. If your path is on a web server, navigate there, otherwise you could
try:
lynx timeline/libwebsockets/

View file

@ -1,107 +0,0 @@
{
"Name": "libwebsockets",
"SourceUrl": "https://github.com/warmcat/libwebsockets/releases",
"Git": "https://github.com/warmcat/libwebsockets",
"Versions": [
{
"Number": "current",
"Installed": "installed/libwebsockets/current",
"Source": "src/libwebsockets/current",
"Changelog": "On",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.7.4",
"Installed": "installed/libwebsockets/1.7.4",
"Source": "src/libwebsockets/1.7.4/libwebsockets-1.7.4.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.7.3",
"Installed": "installed/libwebsockets/1.7.3",
"Source": "src/libwebsockets/1.7.3/libwebsockets-1.7.3.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.7.2",
"Installed": "installed/libwebsockets/1.7.2",
"Source": "src/libwebsockets/1.7.2/libwebsockets-1.7.2.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.7.1",
"Installed": "installed/libwebsockets/1.7.1",
"Source": "src/libwebsockets/1.7.1/libwebsockets-1.7.1.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.7.0",
"Installed": "installed/libwebsockets/1.7.0",
"Source": "src/libwebsockets/1.7.0/libwebsockets-1.7.0.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.6.3",
"Installed": "installed/libwebsockets/1.6.3",
"Source": "src/libwebsockets/1.6.3/libwebsockets-1.6.3.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.6.2",
"Installed": "installed/libwebsockets/1.6.2",
"Source": "src/libwebsockets/1.6.2/libwebsockets-1.6.2.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.6.1",
"Installed": "installed/libwebsockets/1.6.1",
"Source": "src/libwebsockets/1.6.1/libwebsockets-1.6.1.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
},
{
"Number": "1.5.1",
"Installed": "installed/libwebsockets/1.5.1",
"Source": "src/libwebsockets/1.5.1/libwebsockets-1.5.1.tar.gz",
"Changelog": "Off",
"HeadersDiff": "On",
"PkgDiff": "Off",
"ABIView": "Off",
"ABIDiff": "Off"
}]
}

View file

@ -1,17 +0,0 @@
#!/bin/sh
if [ ! -z "$1" ] ; then
OUT=$1
else
OUT="/tmp/lws-abi-track-htdocs"
fi
D=`dirname $0`
if [ ! -z "$D" ] ; then
D=$D/
fi
J=$D"libwebsockets.json"
abi-monitor -get -build-new $J
abi-tracker -build $J
abi-tracker -deploy $OUT $J

View file

@ -1,114 +0,0 @@
#!/bin/bash
#
# Build libwebsockets static library for Android
#
# requires debian package xutils-dev for makedepend (openssl make depend)
#
# This is based on http://stackoverflow.com/questions/11929773/compiling-the-latest-openssl-for-android/
# via https://github.com/warmcat/libwebsockets/pull/502
# path to NDK
export NDK=/opt/Android/SDK/ndk-bundle
set -e
# Download packages libz, openssl and libwebsockets
[ ! -f zlib-1.2.8.tar.gz ] && {
wget http://prdownloads.sourceforge.net/libpng/zlib-1.2.8.tar.gz
}
[ ! -f openssl-1.0.2g.tar.gz ] && {
wget https://openssl.org/source/openssl-1.0.2g.tar.gz
}
[ ! -f libwebsockets.tar.gz ] && {
git clone https://github.com/warmcat/libwebsockets.git
tar caf libwebsockets.tar.gz libwebsockets
}
# Clean then Unzip
[ -d zlib-1.2.8 ] && rm -fr zlib-1.2.8
[ -d openssl-1.0.2g ] && rm -fr openssl-1.0.2g
[ -d libwebsockets ] && rm -fr libwebsockets
[ -d android-toolchain-arm ] && rm -fr android-toolchain-arm
tar xf zlib-1.2.8.tar.gz
tar xf openssl-1.0.2g.tar.gz
tar xf libwebsockets.tar.gz
# create a local android toolchain
$NDK/build/tools/make-standalone-toolchain.sh \
--platform=android-9 \
--toolchain=arm-linux-androideabi-4.9 \
--install-dir=`pwd`/android-toolchain-arm
# setup environment to use the gcc/ld from the android toolchain
export TOOLCHAIN_PATH=`pwd`/android-toolchain-arm/bin
export TOOL=arm-linux-androideabi
export NDK_TOOLCHAIN_BASENAME=${TOOLCHAIN_PATH}/${TOOL}
export CC=$NDK_TOOLCHAIN_BASENAME-gcc
export CXX=$NDK_TOOLCHAIN_BASENAME-g++
export LINK=${CXX}
export LD=$NDK_TOOLCHAIN_BASENAME-ld
export AR=$NDK_TOOLCHAIN_BASENAME-ar
export RANLIB=$NDK_TOOLCHAIN_BASENAME-ranlib
export STRIP=$NDK_TOOLCHAIN_BASENAME-strip
# setup buildflags
export ARCH_FLAGS="-mthumb"
export ARCH_LINK=
export CPPFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 "
export CXXFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 -frtti -fexceptions "
export CFLAGS=" ${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 "
export LDFLAGS=" ${ARCH_LINK} "
# configure and build zlib
[ ! -f ./android-toolchain-arm/lib/libz.a ] && {
cd zlib-1.2.8
PATH=$TOOLCHAIN_PATH:$PATH ./configure --static --prefix=$TOOLCHAIN_PATH/..
PATH=$TOOLCHAIN_PATH:$PATH make
PATH=$TOOLCHAIN_PATH:$PATH make install
cd ..
}
# configure and build openssl
[ ! -f ./android-toolchain-arm/lib/libssl.a ] && {
PREFIX=$TOOLCHAIN_PATH/..
cd openssl-1.0.2g
./Configure android --prefix=${PREFIX} no-shared no-idea no-mdc2 no-rc5 no-zlib no-zlib-dynamic enable-tlsext no-ssl2 no-ssl3 enable-ec enable-ecdh enable-ecp
PATH=$TOOLCHAIN_PATH:$PATH make depend
PATH=$TOOLCHAIN_PATH:$PATH make
PATH=$TOOLCHAIN_PATH:$PATH make install_sw
cd ..
}
# configure and build libwebsockets
[ ! -f ./android-toolchain-arm/lib/libwebsockets.a ] && {
cd libwebsockets
[ ! -d build ] && mkdir build
cd build
PATH=$TOOLCHAIN_PATH:$PATH cmake \
-DCMAKE_C_COMPILER=$CC \
-DCMAKE_AR=$AR \
-DCMAKE_RANLIB=$RANLIB \
-DCMAKE_C_FLAGS="$CFLAGS" \
-DCMAKE_INSTALL_PREFIX=$TOOLCHAIN_PATH/.. \
-DLWS_WITH_SHARED=OFF \
-DLWS_WITH_STATIC=ON \
-DLWS_WITHOUT_DAEMONIZE=ON \
-DLWS_WITHOUT_TESTAPPS=ON \
-DLWS_IPV6=OFF \
-DLWS_USE_BUNDLED_ZLIB=OFF \
-DLWS_WITH_SSL=ON \
-DLWS_WITH_HTTP2=ON \
-DLWS_OPENSSL_LIBRARIES="$TOOLCHAIN_PATH/../lib/libssl.a;$TOOLCHAIN_PATH/../lib/libcrypto.a" \
-DLWS_OPENSSL_INCLUDE_DIRS=$TOOLCHAIN_PATH/../include \
-DCMAKE_BUILD_TYPE=Debug \
..
PATH=$TOOLCHAIN_PATH:$PATH make
PATH=$TOOLCHAIN_PATH:$PATH make install
cd ../..
}

View file

@ -1,32 +0,0 @@
#
# CMake Toolchain file for crosscompiling on ARM.
#
# This can be used when running cmake in the following way:
# cd build/
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake
#
# Target operating system name.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
# Name of C compiler.
set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "aarch64-linux-gnu-g++")
#-nostdlib
SET(CMAKE_C_FLAGS "-DGCC_VER=\"\\\"$(GCC_VER)\\\"\" -DARM64=1 -D__LP64__=1 -Os -g3 -fpie -mstrict-align -DOPTEE_DEV_KIT=../../../optee_os/out/arm-plat-hikey/export-ta_arm64/include -fPIC -ffunction-sections -fdata-sections" CACHE STRING "" FORCE)
# Where to look for the target environment. (More paths can be added here)
set(CMAKE_FIND_ROOT_PATH "/projects/aist-tb/arm64-tc/")
# Adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search headers and libraries in the target environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View file

@ -1,46 +0,0 @@
#
# CMake Toolchain file for crosscompiling on ARM.
#
# This can be used when running cmake in the following way:
# cd build/
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake
#
# Target operating system name.
set(CMAKE_SYSTEM_NAME Linux)
# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc")
set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar")
set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib")
set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld")
SET(CMAKE_C_FLAGS "-nostdlib -Wall -Werror \
-I${BUILD_DIR_BASE}/include \
-I${IDF_PATH}/components/mdns/include \
-I${IDF_PATH}/components/driver/include \
-I${IDF_PATH}/components/spi_flash/include \
-I${IDF_PATH}/components/nvs_flash/include \
-I${IDF_PATH}/components/tcpip_adapter/include \
-I${IDF_PATH}/components/lwip/include/lwip/posix \
-I${IDF_PATH}/components/lwip/include/lwip \
-I${IDF_PATH}/components/lwip/include/lwip/port \
-I${IDF_PATH}/components/esp32/include/ \
-I${IDF_PATH}/components/bootloader_support/include/ \
-I${IDF_PATH}/components/app_update/include/ \
-I$(IDF_PATH)/components/soc/esp32/include/ \
${LWS_C_FLAGS} -Os \
-I${IDF_PATH}/components/nvs_flash/test_nvs_host \
-I${IDF_PATH}/components/freertos/include" CACHE STRING "" FORCE)
# Where to look for the target environment. (More paths can be added here)
set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}")
# Adjust the default behavior of the FIND_XXX() commands:
# search programs in the host environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search headers and libraries in the target environment only.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

View file

@ -1,61 +1,9 @@
#include "private-libwebsockets.h"
#if defined(LWS_PLAT_OPTEE)
#define TEE_USER_MEM_HINT_NO_FILL_ZERO 0x80000000
void *__attribute__((weak))
TEE_Malloc(uint32_t size, uint32_t hint)
{
return NULL;
}
void *__attribute__((weak))
TEE_Realloc(void *buffer, uint32_t newSize)
{
return NULL;
}
void __attribute__((weak))
TEE_Free(void *buffer)
{
}
void *lws_realloc(void *ptr, size_t size)
{
return TEE_Realloc(ptr, size);
}
void *lws_malloc(size_t size)
{
return TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
}
void lws_free(void *p)
{
TEE_Free(p);
}
void *lws_zalloc(size_t size)
{
void *ptr = TEE_Malloc(size, TEE_USER_MEM_HINT_NO_FILL_ZERO);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
{
(void)cb;
}
#else
static void *_realloc(void *ptr, size_t size)
{
if (size)
#if defined(LWS_PLAT_OPTEE)
return (void *)TEE_Realloc(ptr, size);
#else
return (void *)realloc(ptr, size);
#endif
return realloc(ptr, size);
else if (ptr)
free(ptr);
return NULL;
@ -80,4 +28,3 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
{
_lws_realloc = cb;
}
#endif

View file

@ -33,11 +33,12 @@
* Bob Trower 08/04/01 -- Create Version 0.00.00B
*
* I cleaned it up quite a bit to match the (linux kernel) style of the rest
* of libwebsockets; this version is under LGPL2.1 + SLE like the rest of lws
* of libwebsockets; this version is under LGPL2 like the rest of libwebsockets
* since he explicitly allows sublicensing, but I give the URL above so you can
* get the original with Bob's super-liberal terms directly if you prefer.
*/
#include <stdio.h>
#include <string.h>
#include "private-libwebsockets.h"
@ -97,8 +98,11 @@ lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
LWS_VISIBLE int
lws_b64_decode_string(const char *in, char *out, int out_size)
{
int len, i, c = 0, done = 0;
unsigned char v, quad[4];
int len;
int i;
int done = 0;
unsigned char v;
unsigned char quad[4];
while (*in) {
@ -106,34 +110,25 @@ 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) {
c = v = *in++;
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))
/* out buffer is too small */
return -1;
/*
* "The '==' sequence indicates that the last group contained
* only one byte, and '=' indicates that it contained two
* bytes." (wikipedia)
*/
if (!*in && c == '=')
len--;
if (len >= 2)
*out++ = quad[0] << 2 | quad[1] >> 4;
if (len >= 3)
@ -147,34 +142,22 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
if (done + 1 >= out_size)
return -1;
*out = '\0';
*out++ = '\0';
return done;
}
#if 0
int
lws_b64_selftest(void)
{
char buf[64];
unsigned int n, r = 0;
unsigned int test;
/* examples from https://en.wikipedia.org/wiki/Base64 */
int n;
int test;
static const char * const plaintext[] = {
"any carnal pleasure.",
"any carnal pleasure",
"any carnal pleasur",
"any carnal pleasu",
"any carnal pleas",
"Admin:kloikloi"
"sanity check base 64"
};
static const char * const coded[] = {
"YW55IGNhcm5hbCBwbGVhc3VyZS4=",
"YW55IGNhcm5hbCBwbGVhc3VyZQ==",
"YW55IGNhcm5hbCBwbGVhc3Vy",
"YW55IGNhcm5hbCBwbGVhc3U=",
"YW55IGNhcm5hbCBwbGVhcw==",
"QWRtaW46a2xvaWtsb2k="
"c2FuaXR5IGNoZWNrIGJhc2UgNjQ="
};
for (test = 0; test < sizeof plaintext / sizeof(plaintext[0]); test++) {
@ -185,7 +168,7 @@ lws_b64_selftest(void)
if (n != strlen(coded[test]) || strcmp(buf, coded[test])) {
lwsl_err("Failed lws_b64 encode selftest "
"%d result '%s' %d\n", test, buf, n);
r = -1;
return -1;
}
buf[sizeof(buf) - 1] = '\0';
@ -193,14 +176,10 @@ lws_b64_selftest(void)
if (n != strlen(plaintext[test]) ||
strcmp(buf, plaintext[test])) {
lwsl_err("Failed lws_b64 decode selftest "
"%d result '%s' / '%s', %d / %d\n",
test, buf, plaintext[test], n, strlen(plaintext[test]));
r = -1;
"%d result '%s' %d\n", test, buf, n);
return -1;
}
}
lwsl_notice("Base 64 selftests passed\n");
return r;
return 0;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -21,118 +21,27 @@
#include "private-libwebsockets.h"
/*
* parsers.c: lws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
int lws_client_rx_sm(struct lws *wsi, unsigned char c)
int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
int handled, n, m, rx_draining_ext = 0;
unsigned short close_code;
int handled;
struct lws_tokens eff_buf;
unsigned char *pp;
if (wsi->u.ws.rx_draining_ext) {
assert(!c);
eff_buf.token = NULL;
eff_buf.token_len = 0;
lws_remove_wsi_from_draining_ext_list(wsi);
rx_draining_ext = 1;
lwsl_debug("%s: doing draining flow\n", __func__);
goto drain_extension;
}
if (wsi->socket_is_permanently_unusable)
return -1;
int m;
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */
wsi->u.ws.defeat_check_utf8 = 0;
switch (wsi->ietf_spec_revision) {
case 13:
wsi->u.ws.opcode = c & 0xf;
/* revisit if an extension wants them... */
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70);
wsi->u.ws.continuation_possible = 1;
wsi->u.ws.check_utf8 = lws_check_opt(
wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8);
wsi->u.ws.utf8 = 0;
break;
case LWSWSOPC_BINARY_FRAME:
wsi->u.ws.rsv_first_msg = (c & 0x70);
wsi->u.ws.check_utf8 = 0;
wsi->u.ws.continuation_possible = 1;
break;
case LWSWSOPC_CONTINUATION:
if (!wsi->u.ws.continuation_possible) {
lwsl_info("disordered continuation\n");
return -1;
}
break;
case LWSWSOPC_CLOSE:
wsi->u.ws.check_utf8 = 0;
wsi->u.ws.utf8 = 0;
break;
case 3:
case 4:
case 5:
case 6:
case 7:
case 0xb:
case 0xc:
case 0xd:
case 0xe:
case 0xf:
lwsl_info("illegal opcode\n");
return -1;
default:
wsi->u.ws.defeat_check_utf8 = 1;
break;
}
wsi->u.ws.rsv = (c & 0x70);
/* revisit if an extension wants them... */
if (
#ifndef LWS_NO_EXTENSIONS
!wsi->count_act_ext &&
#endif
wsi->u.ws.rsv) {
lwsl_info("illegal rsv bits set\n");
return -1;
}
wsi->u.ws.final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__, wsi->u.ws.final);
if (wsi->u.ws.owed_a_fin &&
(wsi->u.ws.opcode == LWSWSOPC_TEXT_FRAME ||
wsi->u.ws.opcode == LWSWSOPC_BINARY_FRAME)) {
lwsl_info("hey you owed us a FIN\n");
return -1;
}
if ((!(wsi->u.ws.opcode & 8)) && wsi->u.ws.final) {
wsi->u.ws.continuation_possible = 0;
wsi->u.ws.owed_a_fin = 0;
}
if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) {
lwsl_info("control message cannot be fragmented\n");
return -1;
}
if (!wsi->u.ws.final)
wsi->u.ws.owed_a_fin = 1;
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
LWSWSOPC_BINARY_FRAME;
LWS_WS_OPCODE_07__BINARY_FRAME;
break;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
@ -145,6 +54,7 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->u.ws.this_frame_masked = !!(c & 0x80);
@ -188,7 +98,8 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->u.ws.rx_packet_length |= c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1;
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
@ -267,28 +178,28 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->u.ws.mask[0] = c;
wsi->u.ws.frame_masking_nonce_04[0] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->u.ws.mask[1] = c;
wsi->u.ws.frame_masking_nonce_04[1] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->u.ws.mask[2] = c;
wsi->u.ws.frame_masking_nonce_04[2] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->u.ws.mask[3] = c;
wsi->u.ws.frame_masking_nonce_04[3] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
@ -303,15 +214,19 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
assert(wsi->u.ws.rx_ubuf);
if (!wsi->u.ws.rx_user_buffer) {
lwsl_err("NULL client rx_user_buffer\n");
return 1;
}
if (wsi->u.ws.rx_draining_ext)
goto drain_extension;
if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce)
c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c;
if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] = c;
else
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] =
c ^ wsi->u.ws.frame_masking_nonce_04[
(wsi->u.ws.frame_mask_index++) & 3];
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */
@ -321,14 +236,17 @@ int lws_client_rx_sm(struct lws *wsi, unsigned char c)
/*
* if there's no protocol max frame size given, we are
* supposed to default to context->pt_serv_buf_size
* supposed to default to LWS_MAX_SOCKET_IO_BUF
*/
if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
break;
if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
LWS_MAX_SOCKET_IO_BUF)
break;
else
if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
@ -342,17 +260,9 @@ spill:
*/
switch (wsi->u.ws.opcode) {
case LWSWSOPC_CLOSE:
pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->u.ws.rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->u.ws.utf8, pp + 2,
wsi->u.ws.rx_ubuf_head - 2))
goto utf8_fail;
case LWS_WS_OPCODE_07__CLOSE:
/* is this an acknowledgement of our close? */
if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
@ -360,59 +270,24 @@ spill:
lwsl_parser("seen server's close ack\n");
return -1;
}
lwsl_parser("client sees server close len = %d\n",
wsi->u.ws.rx_ubuf_head);
if (wsi->u.ws.rx_ubuf_head >= 2) {
close_code = (pp[0] << 8) | pp[1];
if (close_code < 1000 ||
close_code == 1004 ||
close_code == 1005 ||
close_code == 1006 ||
close_code == 1012 ||
close_code == 1013 ||
close_code == 1014 ||
close_code == 1015 ||
(close_code >= 1016 && close_code < 3000)
) {
pp[0] = (LWS_CLOSE_STATUS_PROTOCOL_ERR >> 8) & 0xff;
pp[1] = LWS_CLOSE_STATUS_PROTOCOL_ERR & 0xff;
}
}
if (user_callback_handle_rxflow(
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp,
wsi->u.ws.rx_ubuf_head))
return -1;
if (lws_partial_buffered(wsi))
/*
* if we're in the middle of something,
* we can't do a normal close response and
* have to just close our end.
*/
wsi->socket_is_permanently_unusable = 1;
else
/*
* parrot the close packet payload back
* we do not care about how it went, we are closing
* immediately afterwards
*/
lws_write(wsi, (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head,
LWS_WRITE_CLOSE);
wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
wsi->u.ws.rx_user_buffer_head);
/*
* parrot the close packet payload back
* we do not care about how it went, we are closing
* immediately afterwards
*/
libwebsocket_write(wsi, (unsigned char *)
&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
/* close the connection */
return -1;
case LWSWSOPC_PING:
case LWS_WS_OPCODE_07__PING:
lwsl_info("received %d byte ping, sending pong\n",
wsi->u.ws.rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */
if (wsi->u.ws.close_in_ping_buffer_len)
goto ping_drop;
wsi->u.ws.rx_user_buffer_head);
if (wsi->u.ws.ping_pending_flag) {
/*
@ -424,43 +299,52 @@ spill:
}
/* control packets can only be < 128 bytes long */
if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
if (wsi->u.ws.rx_user_buffer_head > 128 - 4) {
lwsl_parser("DROP PING payload too large\n");
goto ping_drop;
}
/* stash the pong payload */
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head);
/* if existing buffer is too small, drop it */
if (wsi->u.ws.ping_payload_buf &&
wsi->u.ws.ping_payload_alloc < wsi->u.ws.rx_user_buffer_head)
lws_free2(wsi->u.ws.ping_payload_buf);
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
/* if no buffer, allocate it */
if (!wsi->u.ws.ping_payload_buf) {
wsi->u.ws.ping_payload_buf = lws_malloc(wsi->u.ws.rx_user_buffer_head
+ LWS_SEND_BUFFER_PRE_PADDING);
wsi->u.ws.ping_payload_alloc =
wsi->u.ws.rx_user_buffer_head;
}
/* stash the pong payload */
memcpy(wsi->u.ws.ping_payload_buf + LWS_SEND_BUFFER_PRE_PADDING,
&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head);
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_user_buffer_head;
wsi->u.ws.ping_pending_flag = 1;
/* get it sent as soon as possible */
lws_callback_on_writable(wsi);
libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi);
ping_drop:
wsi->u.ws.rx_ubuf_head = 0;
wsi->u.ws.rx_user_buffer_head = 0;
handled = 1;
break;
case LWSWSOPC_PONG:
case LWS_WS_OPCODE_07__PONG:
lwsl_info("client receied pong\n");
lwsl_hexdump(&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head);
if (wsi->pending_timeout == PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG) {
lwsl_info("received expected PONG on wsi %p\n", wsi);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
}
lwsl_hexdump(&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head);
/* issue it */
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;
case LWSWSOPC_CONTINUATION:
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
case LWS_WS_OPCODE_07__CONTINUATION:
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
break;
default:
@ -473,15 +357,17 @@ ping_drop:
* state machine.
*/
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
if (lws_ext_cb_active(wsi,
LWS_EXT_CB_EXTENDED_PAYLOAD_RX,
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
&eff_buf, 0) <= 0) { /* not handle or fail */
lwsl_ext("Unhandled ext opc 0x%x\n", wsi->u.ws.opcode);
wsi->u.ws.rx_ubuf_head = 0;
lwsl_ext("Unhandled ext opc 0x%x\n",
wsi->u.ws.opcode);
wsi->u.ws.rx_user_buffer_head = 0;
return 0;
}
@ -492,51 +378,22 @@ ping_drop:
/*
* No it's real payload, pass it up to the user callback.
* It's nicely buffered with the pre-padding taken care of
* so it can be sent straight out again using lws_write
* so it can be sent straight out again using libwebsocket_write
*/
if (handled)
goto already_done;
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
lwsl_ext("Ext RX returned %d\n", n);
if (n < 0) {
wsi->socket_is_permanently_unusable = 1;
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PAYLOAD_RX,
&eff_buf, 0) < 0) /* fail */
return -1;
}
lwsl_ext("post inflate eff_buf len %d\n", eff_buf.token_len);
if (rx_draining_ext && !eff_buf.token_len) {
lwsl_err(" --- ignoring zero drain result, ending drain\n");
goto already_done;
}
if (wsi->u.ws.check_utf8 && !wsi->u.ws.defeat_check_utf8) {
if (lws_check_utf8(&wsi->u.ws.utf8,
(unsigned char *)eff_buf.token,
eff_buf.token_len))
goto utf8_fail;
/* we are ending partway through utf-8 character? */
if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final &&
wsi->u.ws.utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
utf8_fail: lwsl_info("utf8 error\n");
return -1;
}
}
if (eff_buf.token_len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
if (!eff_buf.token)
if (eff_buf.token_len <= 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
eff_buf.token[eff_buf.token_len] = '\0';
@ -547,31 +404,20 @@ utf8_fail: lwsl_info("utf8 error\n");
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n");
if (n && eff_buf.token_len)
/* extension had more... main loop will come back
* we want callback to be done with this set, if so,
* because lws_is_final() hides it was final until the
* last chunk
*/
lws_add_wsi_to_draining_ext_list(wsi);
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
wsi->state == LWSS_AWAITING_CLOSE_ACK)
goto already_done;
m = wsi->protocol->callback(wsi,
(enum lws_callback_reasons)callback_action,
wsi->user_space, eff_buf.token, eff_buf.token_len);
m = wsi->protocol->callback(
wsi->protocol->owning_server,
wsi,
(enum libwebsocket_callback_reasons)callback_action,
wsi->user_space,
eff_buf.token,
eff_buf.token_len);
/* if user code wants to close, let caller know */
if (m)
return 1;
already_done:
wsi->u.ws.rx_ubuf_head = 0;
wsi->u.ws.rx_user_buffer_head = 0;
break;
default:
lwsl_err("client rx illegal state\n");
@ -581,6 +427,7 @@ already_done:
return 0;
illegal_ctl_length:
lwsl_warn("Control frame asking for extended length is illegal\n");
/* kill the connection */
return -1;

1364
lib/client.c Executable file → Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,7 @@
* he replied it is Public Domain. Use the URL above to get the original
* Public Domain version if you want it.
*
* This version is LGPL2.1+SLE like the rest of libwebsockets and is
* Copyright (c)2006 - 2013 Andy Green <andy@warmcat.com>
* This version is LGPL2 and is (c)2006 - 2013 Andy Green <andy@warmcat.com>
*/
#include <stdlib.h>
@ -35,41 +34,39 @@ int get_daemonize_pid()
static void
child_handler(int signum)
{
int fd, len, sent;
int fd;
int len;
int sent;
char sz[20];
switch (signum) {
case SIGALRM: /* timed out daemonizing */
exit(0);
exit(1);
break;
case SIGUSR1: /* positive confirmation we daemonized well */
/* Create the lock file as the current user */
if (lock_path) {
/* Create the lock file as the current user */
fd = open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
if (fd < 0) {
fprintf(stderr,
"unable to create lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
exit(0);
}
len = sprintf(sz, "%u", pid_daemon);
sent = write(fd, sz, len);
if (sent != len)
fprintf(stderr,
"unable to write pid to lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
close(fd);
fd = open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
if (fd < 0) {
fprintf(stderr,
"unable to create lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
exit(1);
}
exit(0);
//!!(sent == len));
len = sprintf(sz, "%u", pid_daemon);
sent = write(fd, sz, len);
if (sent != len)
fprintf(stderr,
"unable to write pid to lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
close(fd);
exit(!!(sent == len));
case SIGCHLD: /* daemonization failed */
exit(0);
exit(1);
break;
}
}
@ -79,7 +76,7 @@ static void lws_daemon_closing(int sigact)
if (getpid() == pid_daemon)
if (lock_path) {
unlink(lock_path);
lws_free_set_NULL(lock_path);
lws_free2(lock_path);
}
kill(getpid(), SIGKILL);
@ -96,44 +93,43 @@ static void lws_daemon_closing(int sigact)
LWS_VISIBLE int
lws_daemonize(const char *_lock_path)
{
struct sigaction act;
pid_t sid, parent;
int n, fd, ret;
int fd;
char buf[10];
int n, ret;
struct sigaction act;
/* already a daemon */
// if (getppid() == 1)
// return 1;
if (getppid() == 1)
return 1;
if (_lock_path) {
fd = open(_lock_path, O_RDONLY);
if (fd >= 0) {
n = read(fd, buf, sizeof(buf));
close(fd);
if (n) {
n = atoi(buf);
ret = kill(n, 0);
if (ret >= 0) {
fprintf(stderr,
"Daemon already running from pid %d\n", n);
exit(1);
}
fd = open(_lock_path, O_RDONLY);
if (fd >= 0) {
n = read(fd, buf, sizeof(buf));
close(fd);
if (n) {
n = atoi(buf);
ret = kill(n, 0);
if (ret >= 0) {
fprintf(stderr,
"Removing stale lock file %s from dead pid %d\n",
_lock_path, n);
unlink(lock_path);
"Daemon already running from pid %d\n", n);
exit(1);
}
fprintf(stderr,
"Removing stale lock file %s from dead pid %d\n",
_lock_path, n);
unlink(lock_path);
}
n = strlen(_lock_path) + 1;
lock_path = lws_malloc(n);
if (!lock_path) {
fprintf(stderr, "Out of mem in lws_daemonize\n");
return 1;
}
strcpy(lock_path, _lock_path);
}
n = strlen(_lock_path) + 1;
lock_path = lws_malloc(n);
if (!lock_path) {
fprintf(stderr, "Out of mem in lws_daemonize\n");
return 1;
}
strcpy(lock_path, _lock_path);
/* Trap signals that we expect to receive */
signal(SIGCHLD, child_handler); /* died */
signal(SIGUSR1, child_handler); /* was happy */
@ -144,23 +140,23 @@ lws_daemonize(const char *_lock_path)
if (pid_daemon < 0) {
fprintf(stderr, "unable to fork daemon, code=%d (%s)",
errno, strerror(errno));
exit(9);
exit(1);
}
/* If we got a good PID, then we can exit the parent process. */
/* If we got a good PID, then we can exit the parent process. */
if (pid_daemon > 0) {
/*
* Wait for confirmation signal from the child via
* SIGCHILD / USR1, or for two seconds to elapse
* (SIGALRM). pause() should not return.
*/
alarm(2);
/*
* Wait for confirmation signal from the child via
* SIGCHILD / USR1, or for two seconds to elapse
* (SIGALRM). pause() should not return.
*/
alarm(2);
pause();
/* should not be reachable */
exit(1);
}
pause();
/* should not be reachable */
exit(1);
}
/* At this point we are executing as the child process */
parent = getppid();
@ -182,18 +178,18 @@ lws_daemonize(const char *_lock_path)
fprintf(stderr,
"unable to create a new session, code %d (%s)",
errno, strerror(errno));
exit(2);
exit(1);
}
/*
* Change the current working directory. This prevents the current
* directory from being locked; hence not being able to remove it.
*/
if (chdir("/tmp") < 0) {
if (chdir("/") < 0) {
fprintf(stderr,
"unable to change directory to %s, code %d (%s)",
"/", errno, strerror(errno));
exit(3);
exit(1);
}
/* Redirect standard files to /dev/null */

View file

@ -0,0 +1,288 @@
#include "private-libwebsockets.h"
#include "extension-deflate-frame.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_WINDOW_BITS 15
#define LWS_ZLIB_MEMLEVEL 8
int lws_extension_callback_deflate_frame(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_deflate_frame_conn *conn =
(struct lws_ext_deflate_frame_conn *)user;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
size_t current_payload, remaining_payload, total_payload;
int n;
size_t len_so_far;
switch (reason) {
/*
* for deflate-frame, both client and server sides act the same
*/
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
case LWS_EXT_CALLBACK_CONSTRUCT:
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
if (n != Z_OK) {
lwsl_ext("deflateInit returned %d\n", n);
return 1;
}
n = deflateInit2(&conn->zs_out,
(context->listen_port ?
DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER :
DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT),
Z_DEFLATED,
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_ext("deflateInit2 returned %d\n", n);
return 1;
}
conn->buf_pre_used = 0;
conn->buf_pre_length = 0;
conn->buf_in_length = sizeof(conn->buf_in);
conn->buf_out_length = sizeof(conn->buf_out);
conn->compressed_out = 0;
conn->buf_pre = NULL;
conn->buf_in = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_in_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_in)
goto bail;
conn->buf_out = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_out_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_out)
goto bail;
lwsl_ext("zlibs constructed\n");
break;
bail:
lwsl_err("Out of mem\n");
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
return -1;
case LWS_EXT_CALLBACK_DESTROY:
lws_free(conn->buf_pre);
lws_free(conn->buf_in);
lws_free(conn->buf_out);
conn->buf_pre_used = 0;
conn->buf_pre_length = 0;
conn->buf_in_length = 0;
conn->buf_out_length = 0;
conn->compressed_out = 0;
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
lwsl_ext("zlibs destructed\n");
break;
case LWS_EXT_CALLBACK_PAYLOAD_RX:
if (!(wsi->u.ws.rsv & 0x40))
return 0;
/*
* inflate the incoming payload
*/
current_payload = eff_buf->token_len;
remaining_payload = wsi->u.ws.rx_packet_length;
if (remaining_payload) {
total_payload = conn->buf_pre_used +
current_payload +
remaining_payload;
if (conn->buf_pre_length < total_payload) {
conn->buf_pre_length = total_payload;
lws_free(conn->buf_pre);
conn->buf_pre = lws_malloc(total_payload + 4);
if (!conn->buf_pre) {
lwsl_err("Out of memory\n");
return -1;
}
}
memcpy(conn->buf_pre + conn->buf_pre_used,
eff_buf->token, current_payload);
conn->buf_pre_used += current_payload;
eff_buf->token = NULL;
eff_buf->token_len = 0;
return 0;
}
if (conn->buf_pre_used) {
total_payload = conn->buf_pre_used +
current_payload;
memcpy(conn->buf_pre + conn->buf_pre_used,
eff_buf->token, current_payload);
conn->buf_pre_used = 0;
conn->zs_in.next_in = conn->buf_pre;
} else {
total_payload = current_payload;
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
}
conn->zs_in.next_in[total_payload + 0] = 0;
conn->zs_in.next_in[total_payload + 1] = 0;
conn->zs_in.next_in[total_payload + 2] = 0xff;
conn->zs_in.next_in[total_payload + 3] = 0xff;
conn->zs_in.avail_in = total_payload + 4;
conn->zs_in.next_out =
conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
conn->zs_in.avail_out = conn->buf_in_length;
while (1) {
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
/*
* screwed.. close the connection...
* we will get a destroy callback to take care
* of closing nicely
*/
lwsl_info("zlib error inflate %d: %s\n",
n, conn->zs_in.msg);
return -1;
}
if (conn->zs_in.avail_out)
break;
len_so_far = conn->zs_in.next_out -
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
conn->buf_in_length *= 2;
if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) {
lwsl_ext("zlib in buffer hit limit %u\n",
LWS_MAX_ZLIB_CONN_BUFFER);
return -1;
}
conn->buf_in = lws_realloc(conn->buf_in,
LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_in_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_in) {
lwsl_err("Out of memory\n");
return -1;
}
lwsl_debug(
"deflate-frame ext RX did realloc to %ld\n",
conn->buf_in_length);
conn->zs_in.next_out = conn->buf_in +
LWS_SEND_BUFFER_PRE_PADDING + len_so_far;
conn->zs_in.avail_out =
conn->buf_in_length - len_so_far;
}
/* rewrite the buffer pointers and length */
eff_buf->token =
(char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
eff_buf->token_len = (int)(conn->zs_in.next_out -
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
return 0;
case LWS_EXT_CALLBACK_PAYLOAD_TX:
/*
* deflate the outgoing payload
*/
current_payload = eff_buf->token_len;
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
conn->zs_out.avail_in = current_payload;
conn->zs_out.next_out =
conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
conn->zs_out.avail_out = conn->buf_out_length;
while (1) {
n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
if (n == Z_STREAM_ERROR) {
/*
* screwed.. close the connection... we will
* get a destroy callback to take care of
* closing nicely
*/
lwsl_ext("zlib error deflate\n");
return -1;
}
if (conn->zs_out.avail_out)
break;
len_so_far = (conn->zs_out.next_out -
(conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING));
conn->buf_out_length *= 2;
if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) {
lwsl_ext("zlib out hit limit %u\n",
LWS_MAX_ZLIB_CONN_BUFFER);
return -1;
}
conn->buf_out = lws_realloc(conn->buf_out,
LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_out_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_out) {
lwsl_err("Out of memory\n");
return -1;
}
lwsl_debug(
"deflate-frame ext TX did realloc to %ld\n",
conn->buf_out_length);
conn->zs_out.next_out = (conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
conn->zs_out.avail_out =
(conn->buf_out_length - len_so_far);
}
conn->compressed_out = 1;
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)(conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING);
eff_buf->token_len = (int)(conn->zs_out.next_out -
(conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
return 0;
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
if (conn->compressed_out) {
conn->compressed_out = 0;
*((unsigned char *)eff_buf->token) |= 0x40;
}
break;
case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
/* Avoid x-webkit-deflate-frame extension on client */
if (!strcmp((char *)in, "x-webkit-deflate-frame"))
return 1;
break;
default:
break;
}
return 0;
}

View file

@ -0,0 +1,25 @@
#include <zlib.h>
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
struct lws_ext_deflate_frame_conn {
z_stream zs_in;
z_stream zs_out;
size_t buf_pre_used;
size_t buf_pre_length;
size_t buf_in_length;
size_t buf_out_length;
int compressed_out;
unsigned char *buf_pre;
unsigned char *buf_in;
unsigned char *buf_out;
};
extern int lws_extension_callback_deflate_frame(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len);

View file

@ -0,0 +1,166 @@
#include "private-libwebsockets.h"
#include "extension-deflate-stream.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_WINDOW_BITS 15
#define LWS_ZLIB_MEMLEVEL 8
int lws_extension_callback_deflate_stream(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_deflate_stream_conn *conn =
(struct lws_ext_deflate_stream_conn *)user;
int n;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
switch (reason) {
/*
* for deflate-stream, both client and server sides act the same
*/
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
case LWS_EXT_CALLBACK_CONSTRUCT:
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
if (n != Z_OK) {
lwsl_err("deflateInit returned %d\n", n);
return 1;
}
n = deflateInit2(&conn->zs_out,
DEFLATE_STREAM_COMPRESSION_LEVEL, Z_DEFLATED,
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_err("deflateInit returned %d\n", n);
return 1;
}
lwsl_ext("zlibs constructed\n");
conn->remaining_in = 0;
break;
case LWS_EXT_CALLBACK_DESTROY:
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
lwsl_ext("zlibs destructed\n");
break;
case LWS_EXT_CALLBACK_PACKET_RX_PREPARSE:
/*
* inflate the incoming compressed data
* Notice, length may be 0 and pointer NULL
* in the case we are flushing with nothing new coming in
*/
if (conn->remaining_in) {
conn->zs_in.next_in = conn->buf_in;
conn->zs_in.avail_in = conn->remaining_in;
conn->remaining_in = 0;
} else {
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
conn->zs_in.avail_in = eff_buf->token_len;
}
conn->zs_in.next_out = conn->buf_out;
conn->zs_in.avail_out = sizeof(conn->buf_out);
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
/*
* screwed.. close the connection... we will get a
* destroy callback to take care of closing nicely
*/
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)conn->buf_out;
eff_buf->token_len =
sizeof(conn->buf_out) - conn->zs_in.avail_out;
/* copy avail data if not consumed */
if (conn->zs_in.avail_in > 0) {
conn->remaining_in = conn->zs_in.avail_in;
memcpy(conn->buf_in, conn->zs_in.next_in,
conn->zs_in.avail_in);
return 1;
}
/*
* if we filled the output buffer, signal that we likely have
* more and need to be called again
*/
if (eff_buf->token_len == sizeof(conn->buf_out))
return 1;
/* we don't need calling again until new input data comes */
return 0;
case LWS_EXT_CALLBACK_FLUSH_PENDING_TX:
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
/*
* deflate the outgoing compressed data
*/
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
conn->zs_out.avail_in = eff_buf->token_len;
conn->zs_out.next_out = conn->buf_out;
conn->zs_out.avail_out = sizeof(conn->buf_out);
n = Z_PARTIAL_FLUSH;
if (reason == LWS_EXT_CALLBACK_FLUSH_PENDING_TX)
n = Z_FULL_FLUSH;
n = deflate(&conn->zs_out, n);
if (n == Z_STREAM_ERROR) {
/*
* screwed.. close the connection... we will get a
* destroy callback to take care of closing nicely
*/
lwsl_ext("zlib error deflate\n");
return -1;
}
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)conn->buf_out;
eff_buf->token_len =
sizeof(conn->buf_out) - conn->zs_out.avail_out;
/*
* if we filled the output buffer, signal that we likely have
* more and need to be called again... even in deflate case
* we might sometimes need to spill more than came in
*/
if (eff_buf->token_len == sizeof(conn->buf_out))
return 1;
/* we don't need calling again until new input data comes */
return 0;
default:
break;
}
return 0;
}

View file

@ -0,0 +1,20 @@
#include <zlib.h>
#define DEFLATE_STREAM_CHUNK 128
#define DEFLATE_STREAM_COMPRESSION_LEVEL 1
struct lws_ext_deflate_stream_conn {
z_stream zs_in;
z_stream zs_out;
int remaining_in;
unsigned char buf_in[LWS_MAX_SOCKET_IO_BUF];
unsigned char buf_out[LWS_MAX_SOCKET_IO_BUF];
};
extern int lws_extension_callback_deflate_stream(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len);

View file

@ -1,473 +0,0 @@
/*
* ./lib/extension-permessage-deflate.c
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_MEMLEVEL 8
const struct lws_ext_options lws_ext_pm_deflate_options[] = {
/* public RFC7692 settings */
{ "server_no_context_takeover", EXTARG_NONE },
{ "client_no_context_takeover", EXTARG_NONE },
{ "server_max_window_bits", EXTARG_OPT_DEC },
{ "client_max_window_bits", EXTARG_OPT_DEC },
/* ones only user code can set */
{ "rx_buf_size", EXTARG_DEC },
{ "tx_buf_size", EXTARG_DEC },
{ "compression_level", EXTARG_DEC },
{ "mem_level", EXTARG_DEC },
{ NULL, 0 }, /* sentinel */
};
static void
lws_extension_pmdeflate_restrict_args(struct lws *wsi,
struct lws_ext_pm_deflate_priv *priv)
{
int n, extra;
/* cap the RX buf at the nearest power of 2 to protocol rx buf */
n = wsi->context->pt_serv_buf_size;
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);
}
}
LWS_VISIBLE int
lws_extension_callback_pm_deflate(struct lws_context *context,
const struct lws_extension *ext,
struct lws *wsi,
enum lws_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_pm_deflate_priv *priv =
(struct lws_ext_pm_deflate_priv *)user;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
static unsigned char trail[] = { 0, 0, 0xff, 0xff };
int n, ret = 0, was_fin = 0, extra;
struct lws_ext_option_arg *oa;
switch (reason) {
case LWS_EXT_CB_NAMED_OPTION_SET:
oa = in;
if (!oa->option_name)
break;
for (n = 0; n < ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name))
break;
if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
break;
oa->option_index = n;
/* fallthru */
case LWS_EXT_CB_OPTION_SET:
oa = in;
lwsl_notice("%s: option set: idx %d, %s, len %d\n", __func__,
oa->option_index, oa->start, oa->len);
if (oa->start)
priv->args[oa->option_index] = atoi(oa->start);
else
priv->args[oa->option_index] = 1;
if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_OPTION_CONFIRM:
if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
return -1;
break;
case LWS_EXT_CB_CLIENT_CONSTRUCT:
case LWS_EXT_CB_CONSTRUCT:
n = context->pt_serv_buf_size;
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;
lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
memset(priv, 0, sizeof(*priv));
/* fill in pointer to options list */
if (in)
*((const struct lws_ext_options **)in) =
lws_ext_pm_deflate_options;
/* fallthru */
case LWS_EXT_CB_OPTION_DEFAULT:
/* set the public, RFC7692 defaults... */
priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
/* ...and the ones the user code can override */
priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
priv->args[PMD_COMP_LEVEL] = 1;
priv->args[PMD_MEM_LEVEL] = 8;
lws_extension_pmdeflate_restrict_args(wsi, priv);
break;
case LWS_EXT_CB_DESTROY:
lwsl_ext("%s: LWS_EXT_CB_DESTROY\n", __func__);
lws_free(priv->buf_rx_inflated);
lws_free(priv->buf_tx_deflated);
if (priv->rx_init)
(void)inflateEnd(&priv->rx);
if (priv->tx_init)
(void)deflateEnd(&priv->tx);
lws_free(priv);
return ret;
case LWS_EXT_CB_PAYLOAD_RX:
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
__func__, eff_buf->token_len, priv->rx.avail_in);
if (!(wsi->u.ws.rsv_first_msg & 0x40))
return 0;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
if (!priv->rx_init)
if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
lwsl_err("%s: iniflateInit failed\n", __func__);
return -1;
}
priv->rx_init = 1;
if (!priv->buf_rx_inflated)
priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_RX_BUF_PWR2]));
if (!priv->buf_rx_inflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
/*
* We have to leave the input stream alone if we didn't
* finish with it yet. The input stream is held in the wsi
* rx buffer by the caller, so this assumption is safe while
* we block new rx while draining the existing rx
*/
if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) {
priv->rx.next_in = (unsigned char *)eff_buf->token;
priv->rx.avail_in = eff_buf->token_len;
}
priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
eff_buf->token = (char *)priv->rx.next_out;
priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
if (priv->rx_held_valid) {
lwsl_ext("-- RX piling on held byte --\n");
*(priv->rx.next_out++) = priv->rx_held;
priv->rx.avail_out--;
priv->rx_held_valid = 0;
}
/* if...
*
* - he has no remaining input content for this message, and
* - and this is the final fragment, and
* - we used everything that could be drained on the input side
*
* ...then put back the 00 00 FF FF the sender stripped as our
* input to zlib
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
}
n = inflate(&priv->rx, Z_NO_FLUSH);
lwsl_ext("inflate ret %d, avi %d, avo %d, wsifinal %d\n", n,
priv->rx.avail_in, priv->rx.avail_out, wsi->u.ws.final);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
/*
* 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 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.
*/
if (!priv->rx.avail_in && wsi->u.ws.final &&
!wsi->u.ws.rx_packet_length && !was_fin &&
priv->rx.avail_out /* ambiguous as to if it is the end */
) {
lwsl_ext("RX APPEND_TRAILER-DO\n");
was_fin = 1;
priv->rx.next_in = trail;
priv->rx.avail_in = sizeof(trail);
n = inflate(&priv->rx, Z_SYNC_FLUSH);
lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n,
priv->rx.avail_in, priv->rx.avail_out);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
}
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* 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 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
* on, even if actually nothing more is coming from the next
* inflate action itself.
*/
if (!priv->rx.avail_out) { /* he used all available out buf */
lwsl_ext("-- rx grabbing held --\n");
/* snip the last byte and hold it for next time */
priv->rx_held = *(--priv->rx.next_out);
priv->rx_held_valid = 1;
}
eff_buf->token_len = (char *)priv->rx.next_out - eff_buf->token;
priv->count_rx_between_fin += eff_buf->token_len;
lwsl_ext(" %s: RX leaving with new effbuff len %d, "
"ret %d, rx.avail_in=%d, TOTAL RX since FIN %lu\n",
__func__, eff_buf->token_len, priv->rx_held_valid,
priv->rx.avail_in,
(unsigned long)priv->count_rx_between_fin);
if (was_fin) {
priv->count_rx_between_fin = 0;
if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
(void)inflateEnd(&priv->rx);
priv->rx_init = 0;
}
}
#if 0
for (n = 0; n < eff_buf->token_len; n++)
putchar(eff_buf->token[n]);
puts("\n");
#endif
return priv->rx_held_valid;
case LWS_EXT_CB_PAYLOAD_TX:
if (!priv->tx_init) {
n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
Z_DEFLATED,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS +
(wsi->vhost->listen_port <= 0)],
priv->args[PMD_MEM_LEVEL],
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_ext("inflateInit2 failed %d\n", n);
return 1;
}
}
priv->tx_init = 1;
if (!priv->buf_tx_deflated)
priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 +
(1 << priv->args[PMD_TX_BUF_PWR2]));
if (!priv->buf_tx_deflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
if (eff_buf->token) {
lwsl_ext("%s: TX: eff_buf length %d\n", __func__,
eff_buf->token_len);
priv->tx.next_in = (unsigned char *)eff_buf->token;
priv->tx.avail_in = eff_buf->token_len;
}
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
printf("\n");
#endif
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
eff_buf->token = (char *)priv->tx.next_out;
priv->tx.avail_out = 1 << priv->args[PMD_TX_BUF_PWR2];
n = deflate(&priv->tx, Z_SYNC_FLUSH);
if (n == Z_STREAM_ERROR) {
lwsl_ext("%s: Z_STREAM_ERROR\n", __func__);
return -1;
}
if (priv->tx_held_valid) {
priv->tx_held_valid = 0;
if (priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
/*
* we can get a situation he took something in
* but did not generate anything out, at the end
* of a message (eg, next thing he sends is 80
* 00, a zero length FIN, like Authobahn can
* send).
* If we have come back as a FIN, we must not
* place the pending trailer 00 00 FF FF, just
* the 1 byte of live data
*/
*(--eff_buf->token) = priv->tx_held[0];
else {
/* he generated data, prepend whole pending */
eff_buf->token -= 5;
for (n = 0; n < 5; n++)
eff_buf->token[n] = priv->tx_held[n];
}
}
priv->compressed_out = 1;
eff_buf->token_len = (int)(priv->tx.next_out -
(unsigned char *)eff_buf->token);
/*
* we must announce in our returncode now if there is more
* output to be expected from inflate, so we can decide to
* set the FIN bit on this bufferload or not. However zlib
* is ambiguous when we exactly filled the inflate buffer. It
* does not give us a clue as to whether we should understand
* that to mean he ended on a buffer boundary, or if there is
* more in the pipeline.
*
* Worse, the guy providing the stuff we are sending may not
* know until after that this was, actually, the last chunk,
* that can happen even if we did not fill the output buf, ie
* he may send after this a zero-length FIN fragment.
*
* This is super difficult because we must snip the last 4
* bytes in the case this is the last compressed output of the
* message. The only way to deal with it is defer sending the
* last 5 bytes of each frame until the next one, when we will
* be in a position to understand if that has a FIN or not.
*/
extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
if (eff_buf->token_len >= 4 + extra) {
lwsl_ext("tx held %d\n", 4 + extra);
priv->tx_held_valid = extra;
for (n = 3 + extra; n >= 0; n--)
priv->tx_held[n] = *(--priv->tx.next_out);
eff_buf->token_len -= 4 + extra;
}
lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
eff_buf->token_len, !priv->tx.avail_out);
return !priv->tx.avail_out; /* 1 == have more tx pending */
case LWS_EXT_CB_PACKET_TX_PRESEND:
if (!priv->compressed_out)
break;
priv->compressed_out = 0;
if ((*(eff_buf->token) & 0x80) &&
priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
lwsl_debug("PMD_CLIENT_NO_CONTEXT_TAKEOVER\n");
(void)deflateEnd(&priv->tx);
priv->tx_init = 0;
}
n = *(eff_buf->token) & 15;
/* set RSV1, but not on CONTINUATION */
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
*eff_buf->token |= 0x40;
#if 0
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
puts("\n");
}
puts("\n");
#endif
lwsl_ext("%s: tx opcode 0x%02X\n", __func__,
(unsigned char)*eff_buf->token);
break;
default:
break;
}
return 0;
}

View file

@ -1,41 +0,0 @@
#include <zlib.h>
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
enum arg_indexes {
PMD_SERVER_NO_CONTEXT_TAKEOVER,
PMD_CLIENT_NO_CONTEXT_TAKEOVER,
PMD_SERVER_MAX_WINDOW_BITS,
PMD_CLIENT_MAX_WINDOW_BITS,
PMD_RX_BUF_PWR2,
PMD_TX_BUF_PWR2,
PMD_COMP_LEVEL,
PMD_MEM_LEVEL,
PMD_ARG_COUNT
};
struct lws_ext_pm_deflate_priv {
z_stream rx;
z_stream tx;
unsigned char *buf_rx_inflated; /* RX inflated output buffer */
unsigned char *buf_tx_deflated; /* TX deflated output buffer */
size_t count_rx_between_fin;
unsigned char args[PMD_ARG_COUNT];
unsigned char tx_held[5];
unsigned char rx_held;
unsigned char tx_init:1;
unsigned char rx_init:1;
unsigned char compressed_out:1;
unsigned char rx_held_valid:1;
unsigned char tx_held_valid:1;
unsigned char rx_append_trailer:1;
unsigned char pending_tx_trailer:1;
};

View file

@ -1,204 +1,87 @@
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
#include "extension-deflate-frame.h"
#include "extension-deflate-stream.h"
struct libwebsocket_extension libwebsocket_internal_extensions[] = {
#ifdef LWS_EXT_DEFLATE_STREAM
{
"deflate-stream",
lws_extension_callback_deflate_stream,
sizeof(struct lws_ext_deflate_stream_conn)
},
#else
{
"x-webkit-deflate-frame",
lws_extension_callback_deflate_frame,
sizeof(struct lws_ext_deflate_frame_conn)
},
{
"deflate-frame",
lws_extension_callback_deflate_frame,
sizeof(struct lws_ext_deflate_frame_conn)
},
#endif
{ /* terminator */
NULL, NULL, 0
}
};
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,
struct lws_context *context)
struct libwebsocket_context *context)
{
context->extensions = info->extensions;
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
}
enum lws_ext_option_parser_states {
LEAPS_SEEK_NAME,
LEAPS_EAT_NAME,
LEAPS_SEEK_VAL,
LEAPS_EAT_DEC,
LEAPS_SEEK_ARG_TERM
};
LWS_VISIBLE int
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
void *ext_user, const struct lws_ext_options *opts,
const char *in, int len)
LWS_VISIBLE struct libwebsocket_extension *libwebsocket_get_internal_extensions()
{
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
pending_close_quote = 0;
struct lws_ext_option_arg oa;
oa.option_name = NULL;
while (opts[count_options].name)
count_options++;
while (len) {
lwsl_ext("'%c' %d", *in, leap);
switch (leap) {
case LEAPS_SEEK_NAME:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
match_map = (1 << count_options) - 1;
leap = LEAPS_EAT_NAME;
w = 0;
/* fallthru */
case LEAPS_EAT_NAME:
oa.start = NULL;
oa.len = 0;
m = match_map;
n = 0;
pending_close_quote = 0;
while (m) {
if (m & 1) {
lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
if (*in == opts[n].name[w]) {
if (!opts[n].name[w + 1]) {
oa.option_index = n;
lwsl_ext("hit %d\n", oa.option_index);
leap = LEAPS_SEEK_VAL;
if (len == 1)
goto set_arg;
break;
}
} else {
match_map &= ~(1 << n);
if (!match_map) {
lwsl_ext("empty match map\n");
return -1;
}
}
}
m >>= 1;
n++;
}
w++;
break;
case LEAPS_SEEK_VAL:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
if (*in == ';' || len == 1) { /* ie,nonoptional */
if (opts[oa.option_index].type == EXTARG_DEC)
return -1;
leap = LEAPS_SEEK_NAME;
goto set_arg;
}
if (*in == '=') {
w = 0;
pending_close_quote = 0;
if (opts[oa.option_index].type == EXTARG_NONE)
return -1;
leap = LEAPS_EAT_DEC;
break;
}
return -1;
case LEAPS_EAT_DEC:
if (*in >= '0' && *in <= '9') {
if (!w)
oa.start = in;
w++;
if (len != 1)
break;
}
if (!w && *in =='"') {
pending_close_quote = 1;
break;
}
if (!w)
return -1;
if (pending_close_quote && *in != '"' && len != 1)
return -1;
leap = LEAPS_SEEK_ARG_TERM;
if (oa.start)
oa.len = in - oa.start;
if (len == 1)
oa.len++;
set_arg:
ext->callback(lws_get_context(wsi),
ext, wsi, LWS_EXT_CB_OPTION_SET,
ext_user, (char *)&oa, 0);
if (len == 1)
break;
if (pending_close_quote && *in == '"')
break;
/* fallthru */
case LEAPS_SEEK_ARG_TERM:
if (*in == ' ')
break;
if (*in == ';') {
leap = LEAPS_SEEK_NAME;
break;
}
if (*in == ',') {
len = 1;
break;
}
return -1;
}
len--;
in++;
}
return 0;
return libwebsocket_internal_extensions;
}
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
int lws_ext_callback_for_each_active(struct libwebsocket *wsi, int reason,
void *arg, int len)
{
int n, m, handled = 0;
for (n = 0; n < wsi->count_act_ext; n++) {
m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
wsi->active_extensions[n], wsi, reason,
wsi->act_ext_user[n], arg, len);
for (n = 0; n < wsi->count_active_extensions; n++) {
m = wsi->active_extensions[n]->callback(
wsi->protocol->owning_server,
wsi->active_extensions[n], wsi,
reason,
wsi->active_extensions_user[n],
arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
lwsl_ext(
"Extension '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->act_ext_user[n] = NULL;
if (m > handled)
handled = m;
}
return handled;
}
int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int reason, void *arg, int len)
int lws_ext_callback_for_each_extension_type(
struct libwebsocket_context *context, struct libwebsocket *wsi,
int reason, void *arg, int len)
{
int n = 0, m, handled = 0;
const struct lws_extension *ext;
if (!wsi || !wsi->vhost)
return 0;
ext = wsi->vhost->extensions;
struct libwebsocket_extension *ext = context->extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(lws_intptr_t)n, arg, len);
(void *)(long)n, arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
lwsl_ext(
"Extension '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m)
@ -207,15 +90,18 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
ext++;
n++;
}
return 0;
}
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
lws_issue_raw_ext_access(struct libwebsocket *wsi,
unsigned char *buf, size_t len)
{
int ret;
struct lws_tokens eff_buf;
int ret, m, n = 0;
int m;
int n = 0;
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
@ -233,8 +119,8 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
ret = 0;
/* show every extension the new incoming data */
m = lws_ext_cb_active(wsi,
LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PACKET_TX_PRESEND, &eff_buf, 0);
if (m < 0)
return -1;
if (m) /* handled */
@ -279,7 +165,7 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
if (!lws_send_pipe_choked(wsi) && !wsi->truncated_send_len)
/* no we could add more, lets's do that */
continue;
@ -289,7 +175,8 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
* Yes, he's choked. Don't spill the rest now get a callback
* when he is ready to send and take care of it there
*/
lws_callback_on_writable(wsi);
libwebsocket_callback_on_writable(
wsi->protocol->owning_server, wsi);
wsi->extension_data_pending = 1;
ret = 0;
}
@ -298,47 +185,24 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
}
int
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
void *v, size_t len)
lws_any_extension_handled(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons r,
void *v, size_t len)
{
struct lws_context *context = wsi->context;
int n, handled = 0;
int n;
int handled = 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->count_act_ext && !handled; n++) {
for (n = 0; n < wsi->count_active_extensions && !handled; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
handled |= wsi->active_extensions[n]->callback(context,
wsi->active_extensions[n], wsi,
r, wsi->act_ext_user[n], v, len);
r, wsi->active_extensions_user[n], v, len);
}
return handled;
}
int
lws_set_extension_option(struct lws *wsi, const char *ext_name,
const char *opt_name, const char *opt_val)
{
struct lws_ext_option_arg oa;
int idx = 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->count_act_ext &&
strcmp(wsi->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->count_act_ext)
return -1; /* request ext not active on this wsi */
oa.option_name = opt_name;
oa.option_index = 0;
oa.start = opt_val;
oa.len = 0;
return wsi->active_extensions[idx]->callback(
wsi->context, wsi->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
}

View file

@ -1,669 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Original code used in this source file:
*
* https://github.com/PerBothner/DomTerm.git @912add15f3d0aec
*
* ./lws-term/io.c
* ./lws-term/junzip.c
*
* Copyright (C) 2017 Per Bothner <per@bothner.com>
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* ( copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
* lws rewrite:
*
* Copyright (C) 2017 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include <zlib.h>
/*
* This code works with zip format containers which may have files compressed
* with gzip deflate (type 8) or store uncompressed (type 0).
*
* Linux zip produces such zipfiles by default, eg
*
* $ zip ../myzip.zip file1 file2 file3
*/
#define ZIP_COMPRESSION_METHOD_STORE 0
#define ZIP_COMPRESSION_METHOD_DEFLATE 8
typedef struct {
lws_filepos_t filename_start;
uint32_t crc32;
uint32_t comp_size;
uint32_t uncomp_size;
uint32_t offset;
uint32_t mod_time;
uint16_t filename_len;
uint16_t extra;
uint16_t method;
uint16_t file_com_len;
} lws_fops_zip_hdr_t;
typedef struct {
struct lws_fop_fd fop_fd; /* MUST BE FIRST logical fop_fd into
* file inside zip: fops_zip fops */
lws_fop_fd_t zip_fop_fd; /* logical fop fd on to zip file
* itself: using platform fops */
lws_fops_zip_hdr_t hdr;
z_stream inflate;
lws_filepos_t content_start;
lws_filepos_t exp_uncomp_pos;
union {
uint8_t trailer8[8];
uint32_t trailer32[2];
} u;
uint8_t rbuf[128]; /* decompression chunk size */
int entry_count;
unsigned int decompress:1; /* 0 = direct from file */
unsigned int add_gzip_container:1;
} *lws_fops_zip_t;
struct lws_plat_file_ops fops_zip;
#define fop_fd_to_priv(FD) ((lws_fops_zip_t)(FD))
static const uint8_t hd[] = { 31, 139, 8, 0, 0, 0, 0, 0, 0, 3 };
enum {
ZC_SIGNATURE = 0,
ZC_VERSION_MADE_BY = 4,
ZC_VERSION_NEEDED_TO_EXTRACT = 6,
ZC_GENERAL_PURPOSE_BIT_FLAG = 8,
ZC_COMPRESSION_METHOD = 10,
ZC_LAST_MOD_FILE_TIME = 12,
ZC_LAST_MOD_FILE_DATE = 14,
ZC_CRC32 = 16,
ZC_COMPRESSED_SIZE = 20,
ZC_UNCOMPRESSED_SIZE = 24,
ZC_FILE_NAME_LENGTH = 28,
ZC_EXTRA_FIELD_LENGTH = 30,
ZC_FILE_COMMENT_LENGTH = 32,
ZC_DISK_NUMBER_START = 34,
ZC_INTERNAL_FILE_ATTRIBUTES = 36,
ZC_EXTERNAL_FILE_ATTRIBUTES = 38,
ZC_REL_OFFSET_LOCAL_HEADER = 42,
ZC_DIRECTORY_LENGTH = 46,
ZE_SIGNATURE_OFFSET = 0,
ZE_DESK_NUMBER = 4,
ZE_CENTRAL_DIRECTORY_DISK_NUMBER = 6,
ZE_NUM_ENTRIES_THIS_DISK = 8,
ZE_NUM_ENTRIES = 10,
ZE_CENTRAL_DIRECTORY_SIZE = 12,
ZE_CENTRAL_DIR_OFFSET = 16,
ZE_ZIP_COMMENT_LENGTH = 20,
ZE_DIRECTORY_LENGTH = 22,
ZL_REL_OFFSET_CONTENT = 28,
ZL_HEADER_LENGTH = 30,
LWS_FZ_ERR_SEEK_END_RECORD = 1,
LWS_FZ_ERR_READ_END_RECORD,
LWS_FZ_ERR_END_RECORD_MAGIC,
LWS_FZ_ERR_END_RECORD_SANITY,
LWS_FZ_ERR_CENTRAL_SEEK,
LWS_FZ_ERR_CENTRAL_READ,
LWS_FZ_ERR_CENTRAL_SANITY,
LWS_FZ_ERR_NAME_TOO_LONG,
LWS_FZ_ERR_NAME_SEEK,
LWS_FZ_ERR_NAME_READ,
LWS_FZ_ERR_CONTENT_SANITY,
LWS_FZ_ERR_CONTENT_SEEK,
LWS_FZ_ERR_SCAN_SEEK,
LWS_FZ_ERR_NOT_FOUND,
LWS_FZ_ERR_ZLIB_INIT,
LWS_FZ_ERR_READ_CONTENT,
LWS_FZ_ERR_SEEK_COMPRESSED,
};
static uint16_t
get_u16(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint16_t)((c[0] | (c[1] << 8)));
}
static uint32_t
get_u32(void *p)
{
const uint8_t *c = (const uint8_t *)p;
return (uint32_t)((c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)));
}
int
lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
{
lws_filepos_t amount;
uint8_t buf[96];
int i;
if (lws_vfs_file_seek_end(priv->zip_fop_fd, -ZE_DIRECTORY_LENGTH) < 0)
return LWS_FZ_ERR_SEEK_END_RECORD;
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZE_DIRECTORY_LENGTH))
return LWS_FZ_ERR_READ_END_RECORD;
if (amount != ZE_DIRECTORY_LENGTH)
return LWS_FZ_ERR_READ_END_RECORD;
/*
* We require the zip to have the last record right at the end
* Linux zip always does this if no zip comment.
*/
if (buf[0] != 'P' || buf[1] != 'K' || buf[2] != 5 || buf[3] != 6)
return LWS_FZ_ERR_END_RECORD_MAGIC;
i = get_u16(buf + ZE_NUM_ENTRIES);
if (get_u16(buf + ZE_DESK_NUMBER) ||
get_u16(buf + ZE_CENTRAL_DIRECTORY_DISK_NUMBER) ||
i != get_u16(buf + ZE_NUM_ENTRIES_THIS_DISK))
return LWS_FZ_ERR_END_RECORD_SANITY;
/* end record is OK... look for our file in the central dir */
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
get_u32(buf + ZE_CENTRAL_DIR_OFFSET)) < 0)
return LWS_FZ_ERR_CENTRAL_SEEK;
while (i--) {
priv->content_start = lws_vfs_tell(priv->zip_fop_fd);
if (lws_vfs_file_read(priv->zip_fop_fd, &amount, buf,
ZC_DIRECTORY_LENGTH))
return LWS_FZ_ERR_CENTRAL_READ;
if (amount != ZC_DIRECTORY_LENGTH)
return LWS_FZ_ERR_CENTRAL_READ;
if (get_u32(buf + ZC_SIGNATURE) != 0x02014B50)
return LWS_FZ_ERR_CENTRAL_SANITY;
lwsl_debug("cstart 0x%lx\n", (unsigned long)priv->content_start);
priv->hdr.filename_len = get_u16(buf + ZC_FILE_NAME_LENGTH);
priv->hdr.extra = get_u16(buf + ZC_EXTRA_FIELD_LENGTH);
priv->hdr.filename_start = lws_vfs_tell(priv->zip_fop_fd);
priv->hdr.method = get_u16(buf + ZC_COMPRESSION_METHOD);
priv->hdr.crc32 = get_u32(buf + ZC_CRC32);
priv->hdr.comp_size = get_u32(buf + ZC_COMPRESSED_SIZE);
priv->hdr.uncomp_size = get_u32(buf + ZC_UNCOMPRESSED_SIZE);
priv->hdr.offset = get_u32(buf + ZC_REL_OFFSET_LOCAL_HEADER);
priv->hdr.mod_time = get_u32(buf + ZC_LAST_MOD_FILE_TIME);
priv->hdr.file_com_len = get_u16(buf + ZC_FILE_COMMENT_LENGTH);
if (priv->hdr.filename_len != len)
goto next;
if (len >= sizeof(buf) - 1)
return LWS_FZ_ERR_NAME_TOO_LONG;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf, len))
return LWS_FZ_ERR_NAME_READ;
if (amount != len)
return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0';
lwsl_debug("check %s vs %s\n", buf, name);
if (strcmp((const char *)buf, name))
goto next;
/* we found a match */
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->hdr.offset) < 0)
return LWS_FZ_ERR_NAME_SEEK;
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
&amount, buf,
ZL_HEADER_LENGTH))
return LWS_FZ_ERR_NAME_READ;
if (amount != ZL_HEADER_LENGTH)
return LWS_FZ_ERR_NAME_READ;
priv->content_start = priv->hdr.offset +
ZL_HEADER_LENGTH +
priv->hdr.filename_len +
get_u16(buf + ZL_REL_OFFSET_CONTENT);
lwsl_debug("content supposed to start at 0x%lx\n",
(unsigned long)priv->content_start);
if (priv->content_start > priv->zip_fop_fd->len)
return LWS_FZ_ERR_CONTENT_SANITY;
if (lws_vfs_file_seek_set(priv->zip_fop_fd,
priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
/* we are aligned at the start of the content */
priv->exp_uncomp_pos = 0;
return 0;
next:
if (i && lws_vfs_file_seek_set(priv->zip_fop_fd,
priv->content_start +
ZC_DIRECTORY_LENGTH +
priv->hdr.filename_len +
priv->hdr.extra +
priv->hdr.file_com_len) < 0)
return LWS_FZ_ERR_SCAN_SEEK;
}
return LWS_FZ_ERR_NOT_FOUND;
}
static int
lws_fops_zip_reset_inflate(lws_fops_zip_t priv)
{
if (priv->decompress)
inflateEnd(&priv->inflate);
priv->inflate.zalloc = Z_NULL;
priv->inflate.zfree = Z_NULL;
priv->inflate.opaque = Z_NULL;
priv->inflate.avail_in = 0;
priv->inflate.next_in = Z_NULL;
if (inflateInit2(&priv->inflate, -MAX_WBITS) != Z_OK) {
lwsl_err("inflate init failed\n");
return LWS_FZ_ERR_ZLIB_INIT;
}
if (lws_vfs_file_seek_set(priv->zip_fop_fd, priv->content_start) < 0)
return LWS_FZ_ERR_CONTENT_SEEK;
priv->exp_uncomp_pos = 0;
return 0;
}
static lws_fop_fd_t
lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
const char *vpath, lws_fop_flags_t *flags)
{
lws_fop_flags_t local_flags = 0;
lws_fops_zip_t priv;
char rp[192];
int m;
/*
* vpath points at the / after the fops signature in vfs_path, eg
* with a vfs_path "/var/www/docs/manual.zip/index.html", vpath
* will come pointing at "/index.html"
*/
priv = lws_zalloc(sizeof(*priv));
if (!priv)
return NULL;
priv->fop_fd.fops = &fops_zip;
m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m)
m = vpath - vfs_path - 1;
strncpy(rp, vfs_path, m);
rp[m] = '\0';
/* open the zip file itself using the incoming fops, not fops_zip */
priv->zip_fop_fd = fops->LWS_FOP_OPEN(fops, rp, NULL, &local_flags);
if (!priv->zip_fop_fd) {
lwsl_err("unable to open zip %s\n", rp);
goto bail1;
}
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, strlen(vpath));
if (m) {
lwsl_err("unable to find record matching '%s' %d\n", vpath, m);
goto bail2;
}
/* the directory metadata tells us modification time, so pass it on */
priv->fop_fd.mod_time = priv->hdr.mod_time;
*flags |= LWS_FOP_FLAG_MOD_TIME_VALID | LWS_FOP_FLAG_VIRTUAL;
priv->fop_fd.flags = *flags;
/* The zip fop_fd is left pointing at the start of the content.
*
* 1) Content could be uncompressed (STORE), and we can always serve
* that directly
*
* 2) Content could be compressed (GZIP), and the client can handle
* receiving GZIP... we can wrap it in a GZIP header and trailer
* and serve the content part directly. The flag indicating we
* are providing GZIP directly is set so lws will send the right
* headers.
*
* 3) Content could be compressed (GZIP) but the client can't handle
* receiving GZIP... we can decompress it and serve as it is
* inflated piecemeal.
*
* 4) Content may be compressed some unknown way... fail
*
*/
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_STORE) {
/*
* it is stored uncompressed, leave it indicated as
* uncompressed, and just serve it from inside the
* zip with no gzip container;
*/
lwsl_info("direct zip serving (stored)\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
return &priv->fop_fd;
}
if ((*flags & LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP) &&
priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/*
* We can serve the gzipped file contents directly as gzip
* from inside the zip container; client says it is OK.
*
* To convert to standalone gzip, we have to add a 10-byte
* constant header and a variable 8-byte trailer around the
* content.
*
* The 8-byte trailer is prepared now and held in the priv.
*/
lwsl_info("direct zip serving (gzipped)\n");
priv->fop_fd.len = sizeof(hd) + priv->hdr.comp_size +
sizeof(priv->u);
if (lws_is_be()) {
uint8_t *p = priv->u.trailer8;
*p++ = (uint8_t)priv->hdr.crc32;
*p++ = (uint8_t)(priv->hdr.crc32 >> 8);
*p++ = (uint8_t)(priv->hdr.crc32 >> 16);
*p++ = (uint8_t)(priv->hdr.crc32 >> 24);
*p++ = (uint8_t)priv->hdr.uncomp_size;
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 8);
*p++ = (uint8_t)(priv->hdr.uncomp_size >> 16);
*p = (uint8_t)(priv->hdr.uncomp_size >> 24);
} else {
priv->u.trailer32[0] = priv->hdr.crc32;
priv->u.trailer32[1] = priv->hdr.uncomp_size;
}
*flags |= LWS_FOP_FLAG_COMPR_IS_GZIP;
priv->fop_fd.flags = *flags;
priv->add_gzip_container = 1;
return &priv->fop_fd;
}
if (priv->hdr.method == ZIP_COMPRESSION_METHOD_DEFLATE) {
/* we must decompress it to serve it */
lwsl_info("decompressed zip serving\n");
priv->fop_fd.len = priv->hdr.uncomp_size;
if (lws_fops_zip_reset_inflate(priv)) {
lwsl_err("inflate init failed\n");
goto bail2;
}
priv->decompress = 1;
return &priv->fop_fd;
}
/* we can't handle it ... */
lwsl_err("zipped file %s compressed in unknown way (%d)\n", vfs_path,
priv->hdr.method);
bail2:
lws_vfs_file_close(&priv->zip_fop_fd);
bail1:
free(priv);
return NULL;
}
/* ie, we are closing the fop_fd for the file inside the gzip */
static int
lws_fops_zip_close(lws_fop_fd_t *fd)
{
lws_fops_zip_t priv = fop_fd_to_priv(*fd);
if (priv->decompress)
inflateEnd(&priv->inflate);
lws_vfs_file_close(&priv->zip_fop_fd); /* close the gzip fop_fd */
free(priv);
*fd = NULL;
return 0;
}
static lws_fileofs_t
lws_fops_zip_seek_cur(lws_fop_fd_t fd, lws_fileofs_t offset_from_cur_pos)
{
fd->pos += offset_from_cur_pos;
return fd->pos;
}
static int
lws_fops_zip_read(lws_fop_fd_t fd, lws_filepos_t *amount, uint8_t *buf,
lws_filepos_t len)
{
lws_fops_zip_t priv = fop_fd_to_priv(fd);
lws_filepos_t ramount, rlen, cur = lws_vfs_tell(fd);
int ret;
if (priv->decompress) {
if (priv->exp_uncomp_pos != fd->pos) {
/*
* there has been a seek in the uncompressed fop_fd
* we have to restart the decompression and loop eating
* the decompressed data up to the seek point
*/
lwsl_info("seek in decompressed\n");
lws_fops_zip_reset_inflate(priv);
while (priv->exp_uncomp_pos != fd->pos) {
rlen = len;
if (rlen > fd->pos - priv->exp_uncomp_pos)
rlen = fd->pos - priv->exp_uncomp_pos;
if (lws_fops_zip_read(fd, amount, buf, rlen))
return LWS_FZ_ERR_SEEK_COMPRESSED;
}
*amount = 0;
}
priv->inflate.avail_out = (unsigned int)len;
priv->inflate.next_out = buf;
spin:
if (!priv->inflate.avail_in) {
rlen = sizeof(priv->rbuf);
if (rlen > priv->hdr.comp_size -
(cur - priv->content_start))
rlen = priv->hdr.comp_size -
(priv->hdr.comp_size -
priv->content_start);
if (priv->zip_fop_fd->fops->LWS_FOP_READ(
priv->zip_fop_fd, &ramount, priv->rbuf,
rlen))
return LWS_FZ_ERR_READ_CONTENT;
cur += ramount;
priv->inflate.avail_in = (unsigned int)ramount;
priv->inflate.next_in = priv->rbuf;
}
ret = inflate(&priv->inflate, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR)
return ret;
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
/* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
return ret;
}
if (!priv->inflate.avail_in && priv->inflate.avail_out &&
cur != priv->content_start + priv->hdr.comp_size)
goto spin;
*amount = len - priv->inflate.avail_out;
priv->exp_uncomp_pos += *amount;
fd->pos += *amount;
return 0;
}
if (priv->add_gzip_container) {
lwsl_info("%s: gzip + container\n", __func__);
*amount = 0;
/* place the canned header at the start */
if (len && fd->pos < sizeof(hd)) {
rlen = sizeof(hd) - fd->pos;
if (rlen > len)
rlen = len;
/* provide stuff from canned header */
memcpy(buf, hd + fd->pos, (size_t)rlen);
fd->pos += rlen;
buf += rlen;
len -= rlen;
*amount += rlen;
}
/* serve gzipped data direct from zipfile */
if (len && fd->pos >= sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd)) {
rlen = priv->hdr.comp_size - (priv->zip_fop_fd->pos -
priv->content_start);
if (rlen > len)
rlen = len;
if (rlen &&
priv->zip_fop_fd->pos < (priv->hdr.comp_size +
priv->content_start)) {
if (lws_vfs_file_read(priv->zip_fop_fd,
&ramount, buf, rlen))
return LWS_FZ_ERR_READ_CONTENT;
*amount += ramount;
fd->pos += ramount; // virtual pos
buf += ramount;
len -= ramount;
}
}
/* place the prepared trailer at the end */
if (len && fd->pos >= priv->hdr.comp_size + sizeof(hd) &&
fd->pos < priv->hdr.comp_size + sizeof(hd) +
sizeof(priv->u)) {
cur = fd->pos - priv->hdr.comp_size - sizeof(hd);
rlen = sizeof(priv->u) - cur;
if (rlen > len)
rlen = len;
memcpy(buf, priv->u.trailer8 + cur, (size_t)rlen);
*amount += rlen;
fd->pos += rlen;
}
return 0;
}
lwsl_info("%s: store\n", __func__);
if (len > priv->hdr.uncomp_size - (cur - priv->content_start))
len = priv->hdr.comp_size - (priv->hdr.comp_size -
priv->content_start);
if (priv->zip_fop_fd->fops->LWS_FOP_READ(priv->zip_fop_fd,
amount, buf, len))
return LWS_FZ_ERR_READ_CONTENT;
return 0;
}
struct lws_plat_file_ops fops_zip = {
lws_fops_zip_open,
lws_fops_zip_close,
lws_fops_zip_seek_cur,
lws_fops_zip_read,
NULL,
{ { ".zip/", 5 }, { ".jar/", 5 }, { ".war/", 5 } },
NULL,
};

View file

@ -61,7 +61,8 @@
#include "getifaddrs.h"
static int
getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
getifaddrs2(struct ifaddrs **ifap,
int af, int siocgifconf, int siocgifflags,
size_t ifreq_sz)
{
int ret;
@ -73,6 +74,7 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
size_t sz;
struct sockaddr sa_zero;
struct ifreq *ifr;
struct ifaddrs *start, **end = &start;
buf = NULL;
@ -229,7 +231,7 @@ print_addr(const char *s, struct sockaddr *sa)
printf(" %s=%d/", s, sa->sa_family);
#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN
for (i = 0;
i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++)
i < sa->sa_len - ((long)sa->sa_data - (long)&sa->sa_family); i++)
printf("%02x", ((unsigned char *)sa->sa_data)[i]);
#else
for (i = 0; i < sizeof(sa->sa_data); i++)
@ -260,8 +262,8 @@ int
main()
{
struct ifaddrs *a = NULL, *b;
getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
getifaddrs2(&a, AF_INET, SIOCGIFCONF,
SIOCGIFFLAGS, sizeof(struct ifreq));
print_ifaddrs(a);
printf("---\n");
getifaddrs(&b);

View file

@ -1,7 +1,3 @@
#ifndef LWS_HAVE_GETIFADDRS
#define LWS_HAVE_GETIFADDRS 0
#endif
#if LWS_HAVE_GETIFADDRS
#include <sys/types.h>
#include <ifaddrs.h>

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -49,30 +49,26 @@
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
*
* Returns <0 for error or length of chars consumed from buf (up to len)
*/
LWS_VISIBLE int
lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
libwebsocket_read(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
unsigned char *last_char, *oldbuf = buf;
lws_filepos_t body_chunk_len;
size_t n;
lwsl_debug("%s: incoming len %d state %d\n", __func__, (int)len, wsi->state);
int body_chunk_len;
unsigned char *last_char;
switch (wsi->state) {
#ifdef LWS_USE_HTTP2
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
case WSI_STATE_HTTP2_ESTABLISHED:
n = 0;
while (n < len) {
/*
@ -87,78 +83,55 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
/* account for what we're using in rxflow buffer */
if (wsi->rxflow_buffer)
wsi->rxflow_pos++;
if (lws_http2_parser(wsi, buf[n++])) {
lwsl_debug("%s: http2_parser bailed\n", __func__);
if (lws_http2_parser(context, wsi, buf[n++]))
goto bail;
}
}
break;
#endif
case LWSS_HTTP_ISSUING_FILE:
return 0;
case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
case LWSS_HTTP:
http_new:
case WSI_STATE_HTTP:
wsi->hdr_parsing_completed = 0;
/* fallthru */
case LWSS_HTTP_HEADERS:
if (!wsi->u.hdr.ah) {
lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
assert(0);
}
case WSI_STATE_HTTP_ISSUING_FILE:
wsi->state = WSI_STATE_HTTP_HEADERS;
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
/* fallthru */
case WSI_STATE_HTTP_HEADERS:
lwsl_parser("issuing %d bytes to parser\n", (int)len);
if (lws_handshake_client(wsi, &buf, (size_t)len))
if (lws_handshake_client(wsi, &buf, len))
goto bail;
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)len))
if (lws_handshake_server(context, wsi, &buf, len))
/* Handshake indicates this session is done. */
goto bail;
/* we might have transitioned to RAW */
if (wsi->mode == LWSCM_RAW)
/* we gave the read buffer to RAW handler already */
goto read_ok;
/*
* It's possible that we've exhausted our data already, or
* rx flow control has stopped us dealing with this early,
* but lws_handshake_server doesn't update len for us.
* Figure out how much was read, so that we can proceed
* appropriately:
*/
/* It's possible that we've exhausted our data already, but
* lws_handshake_server doesn't update len for us. Figure out how
* much was read, so that we can proceed appropriately: */
len -= (buf - last_char);
lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
if (!wsi->hdr_parsing_completed)
/* More header content on the way */
goto read_ok;
switch (wsi->state) {
case LWSS_HTTP:
case LWSS_HTTP_HEADERS:
case WSI_STATE_HTTP:
case WSI_STATE_HTTP_HEADERS:
goto http_complete;
case WSI_STATE_HTTP_ISSUING_FILE:
goto read_ok;
case LWSS_HTTP_ISSUING_FILE:
goto read_ok;
case LWSS_HTTP_BODY:
wsi->u.http.content_remain =
wsi->u.http.content_length;
if (wsi->u.http.content_remain)
goto http_postbody;
/* there is no POST content */
goto postbody_completion;
case WSI_STATE_HTTP_BODY:
wsi->u.http.content_remain = wsi->u.http.content_length;
goto http_postbody;
default:
break;
}
break;
case LWSS_HTTP_BODY:
case WSI_STATE_HTTP_BODY:
http_postbody:
while (len && wsi->u.http.content_remain) {
/* Copy as much as possible, up to the limit of:
@ -168,75 +141,44 @@ http_postbody:
body_chunk_len = min(wsi->u.http.content_remain,len);
wsi->u.http.content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->cgi) {
struct lws_cgi_args args;
args.ch = LWS_STDIN;
args.stdwsi = &wsi->cgi->stdwsi[0];
args.data = buf;
args.len = body_chunk_len;
/* returns how much used */
n = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_CGI_STDIN_DATA,
wsi->user_space,
(void *)&args, 0);
if ((int)n < 0)
goto bail;
} else {
#endif
n = wsi->protocol->callback(wsi,
if (wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY, wsi->user_space,
buf, (size_t)body_chunk_len);
if (n)
goto bail;
n = (size_t)body_chunk_len;
#ifdef LWS_WITH_CGI
}
#endif
buf += n;
if (wsi->u.http.content_remain) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
wsi->context->timeout_secs);
break;
}
/* he sent all the content in time */
postbody_completion:
#ifdef LWS_WITH_CGI
/* if we're running a cgi, we can't let him off the hook just because he sent his POST data */
if (wsi->cgi)
lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, wsi->context->timeout_secs);
else
#endif
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
#ifdef LWS_WITH_CGI
if (!wsi->cgi)
#endif
{
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
buf, body_chunk_len);
if (n)
goto bail;
}
buf += body_chunk_len;
break;
if (!wsi->u.http.content_remain) {
/* he sent the content in time */
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
if (wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
}
goto http_complete;
} else
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_HTTP_CONTENT,
AWAITING_TIMEOUT);
}
break;
case LWSS_ESTABLISHED:
case LWSS_AWAITING_CLOSE_ACK:
case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
case LWSS_SHUTDOWN:
if (lws_handshake_client(wsi, &buf, (size_t)len))
case WSI_STATE_ESTABLISHED:
case WSI_STATE_AWAITING_CLOSE_ACK:
if (lws_handshake_client(wsi, &buf, len))
goto bail;
switch (wsi->mode) {
case LWSCM_WS_SERVING:
case LWS_CONNMODE_WS_SERVING:
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
@ -244,19 +186,35 @@ postbody_completion:
}
break;
default:
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
lwsl_err("libwebsocket_read: Unhandled state\n");
break;
}
read_ok:
/* Nothing more to do for now */
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
/* Nothing more to do for now. */
lwsl_debug("libwebsocket_read: read_ok\n");
return buf - oldbuf;
return 0;
http_complete:
lwsl_debug("libwebsocket_read: http_complete\n");
#ifndef LWS_NO_SERVER
/* Did the client want to keep the HTTP connection going? */
if (lws_http_transaction_completed(wsi))
goto bail;
#endif
/* If we have more data, loop back around: */
if (len)
goto http_new;
return 0;
bail:
//lwsl_notice("closing connection at lws_read bail:\n");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
lwsl_debug("closing connection at libwebsocket_read bail:\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

View file

@ -20,29 +20,26 @@
*/
#include "private-libwebsockets.h"
#include "lextable-strings.h"
const unsigned char *lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= ARRAY_SIZE(set))
return NULL;
return (unsigned char *)set[token];
}
int
lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
int lws_add_http_header_by_name(struct libwebsocket_context *context,
struct libwebsocket *wsi,
const unsigned char *name,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
{
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
(void)wsi;
if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING)
return lws_add_http2_header_by_name(context, wsi, name, value, length, p, end);
#endif
if (name) {
while (*p < end && *name)
@ -58,53 +55,55 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
*p += length;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
int lws_finalize_http_header(struct libwebsocket_context *context,
struct libwebsocket *wsi,
unsigned char **p,
unsigned char *end)
{
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING)
return 0;
#else
(void)wsi;
#endif
if ((lws_intptr_t)(end - *p) < 3)
if ((long)(end - *p) < 3)
return 1;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';
return 0;
}
int
lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
int lws_add_http_header_by_token(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum lws_token_indexes token,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
{
const unsigned char *name;
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_token(wsi, token, value, length, p, end);
if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING)
return lws_add_http2_header_by_token(context, wsi, token, value, length, p, end);
#endif
name = lws_token_to_string(token);
if (!name)
return 1;
return lws_add_http_header_by_name(wsi, name, value, length, p, end);
return lws_add_http_header_by_name(context, wsi, name, value, length, p, end);
}
int lws_add_http_header_content_length(struct lws *wsi,
lws_filepos_t content_length,
unsigned char **p, unsigned char *end)
int lws_add_http_header_content_length(struct libwebsocket_context *context,
struct libwebsocket *wsi,
unsigned long content_length,
unsigned char **p,
unsigned char *end)
{
char b[24];
int n;
n = sprintf(b, "%llu", (unsigned long long)content_length);
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
n = sprintf(b, "%lu", content_length);
if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)b, n, p, end))
return 1;
wsi->u.http.content_length = content_length;
wsi->u.http.content_remain = content_length;
@ -112,7 +111,7 @@ int lws_add_http_header_content_length(struct lws *wsi,
return 0;
}
STORE_IN_ROM static const char * const err400[] = {
static const char *err400[] = {
"Bad Request",
"Unauthorized",
"Payment Required",
@ -133,7 +132,7 @@ STORE_IN_ROM static const char * const err400[] = {
"Expectation Failed"
};
STORE_IN_ROM static const char * const err500[] = {
static const char *err500[] = {
"Internal Server Error",
"Not Implemented",
"Bad Gateway",
@ -142,176 +141,69 @@ STORE_IN_ROM static const char * const err500[] = {
"HTTP Version Not Supported"
};
int
lws_add_http_header_status(struct lws *wsi, unsigned int _code,
unsigned char **p, unsigned char *end)
int lws_add_http_header_status(struct libwebsocket_context *context,
struct libwebsocket *wsi,
unsigned int code,
unsigned char **p,
unsigned char *end)
{
STORE_IN_ROM static const char * const hver[] = {
"HTTP/1.0", "HTTP/1.1", "HTTP/2"
};
const struct lws_protocol_vhost_options *headers;
unsigned int code = _code & LWSAHH_CODE_MASK;
const char *description = "", *p1;
unsigned char code_and_desc[60];
const char *description = "";
int n;
#ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.response = code;
#endif
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_status(wsi, code, p, end);
if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING)
return lws_add_http2_header_status(context, wsi, code, p, end);
#endif
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
description = err400[code - 400];
if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
description = err500[code - 500];
if (code == 200)
description = "OK";
n = sprintf((char *)code_and_desc, "HTTP/1.0 %u %s", code, description);
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
if (wsi->u.http.request_version < ARRAY_SIZE(hver))
p1 = hver[wsi->u.http.request_version];
else
p1 = hver[0];
n = sprintf((char *)code_and_desc, "%s %u %s", p1, code, description);
if (lws_add_http_header_by_name(wsi, NULL, code_and_desc, n, p, end))
return 1;
headers = wsi->vhost->headers;
while (headers) {
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
strlen(headers->value), p, end))
return 1;
headers = headers->next;
}
if (wsi->context->server_string &&
!(_code & LWSAHH_FLAG_NO_SERVER_NAME))
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER,
(unsigned char *)wsi->context->server_string,
wsi->context->server_string_len, p, end))
return 1;
if (wsi->vhost->options & LWS_SERVER_OPTION_STS)
if (lws_add_http_header_by_name(wsi, (unsigned char *)
"Strict-Transport-Security:",
(unsigned char *)"max-age=15768000 ; "
"includeSubDomains", 36, p, end))
return 1;
return 0;
return lws_add_http_header_by_name(context, wsi, NULL, code_and_desc, n, p, end);
}
LWS_VISIBLE int
lws_return_http_status(struct lws *wsi, unsigned int code,
const char *html_body)
/**
* libwebsockets_return_http_status() - Return simple http status
* @context: libwebsockets context
* @wsi: Websocket instance (available from user callback)
* @code: Status index, eg, 404
* @html_body: User-readable HTML description < 1KB, or NULL
*
* Helper to report HTTP errors back to the client cleanly and
* consistently
*/
LWS_VISIBLE int libwebsockets_return_http_status(
struct libwebsocket_context *context, struct libwebsocket *wsi,
unsigned int code, const char *html_body)
{
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;
int n, m;
unsigned char *p = context->service_buffer + LWS_SEND_BUFFER_PRE_PADDING;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
int n = 0, m, len;
char slen[20];
unsigned char *end = p + sizeof(context->service_buffer) -
LWS_SEND_BUFFER_PRE_PADDING;
if (!html_body)
html_body = "";
if (lws_add_http_header_status(wsi, code, &p, end))
if (lws_add_http_header_status(context, wsi, code, &p, end))
return 1;
if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end))
return 1;
if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/html", 9, &p, end))
return 1;
if (lws_finalize_http_header(context, wsi, &p, end))
return 1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9,
&p, end))
m = libwebsocket_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != (int)(p - start))
return 1;
len = 35 + strlen(html_body) + sprintf(slen, "%d", code);
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;
#if defined(LWS_USE_HTTP2)
{
unsigned char *body = p + 512;
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != (int)(p - start))
return 1;
len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
n = len;
m = lws_write(wsi, body, len, LWS_WRITE_HTTP);
}
#else
p += lws_snprintf((char *)p, end - p - 1,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
n = (int)(p - start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
#endif
n = sprintf((char *)start, "<html><body><h1>%u</h1>%s</body></html>", code, html_body);
m = libwebsocket_write(wsi, start, n, LWS_WRITE_HTTP);
return m != n;
}
LWS_VISIBLE int
lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len,
unsigned char **p, unsigned char *end)
{
unsigned char *start = *p;
int n;
if (lws_add_http_header_status(wsi, code, p, end))
return -1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_LOCATION,
loc, len, p, end))
return -1;
/*
* if we're going with http/1.1 and keepalive,
* we have to give fake content metadata so the
* client knows we completed the transaction and
* it can do the redirect...
*/
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/html", 9,
p, end))
return -1;
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)"0", 1, p, end))
return -1;
if (lws_finalize_http_header(wsi, p, end))
return -1;
n = lws_write(wsi, start, *p - start,
LWS_WRITE_HTTP_HEADERS);
return n;
}

View file

@ -189,55 +189,51 @@ static int huftable_decode(int pos, char c)
return pos + (lextable[q] << 1);
}
static int lws_hpack_update_table_size(struct lws *wsi, int idx)
static int lws_hpack_update_table_size(struct libwebsocket *wsi, int idx)
{
lwsl_info("hpack set table size %d\n", idx);
return 0;
}
static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
static int lws_frag_start(struct libwebsocket *wsi, int hdr_token_idx)
{
struct allocated_headers * ah = wsi->u.http2.http.ah;
if (!hdr_token_idx) {
lwsl_err("%s: zero hdr_token_idx\n", __func__);
if (!hdr_token_idx)
return 1;
}
if (ah->nfrag >= ARRAY_SIZE(ah->frag_index)) {
lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag);
if (ah->next_frag_index >= ARRAY_SIZE(ah->frag_index))
return 1;
}
ah->frags[ah->nfrag].offset = ah->pos;
ah->frags[ah->nfrag].len = 0;
ah->frags[ah->nfrag].nfrag = 0;
ah->frag_index[hdr_token_idx] = ah->nfrag;
ah->frags[ah->next_frag_index].offset = ah->pos;
ah->frags[ah->next_frag_index].len = 0;
ah->frags[ah->next_frag_index].next_frag_index = 0;
ah->frag_index[hdr_token_idx] = ah->next_frag_index;
return 0;
}
static int lws_frag_append(struct lws *wsi, unsigned char c)
static int lws_frag_append(struct libwebsocket *wsi, unsigned char c)
{
struct allocated_headers * ah = wsi->u.http2.http.ah;
ah->data[ah->pos++] = c;
ah->frags[ah->nfrag].len++;
return ah->pos >= wsi->context->max_http_header_data;
ah->frags[ah->next_frag_index].len++;
return ah->pos >= sizeof(ah->data);
}
static int lws_frag_end(struct lws *wsi)
static int lws_frag_end(struct libwebsocket *wsi)
{
if (lws_frag_append(wsi, 0))
return 1;
wsi->u.http2.http.ah->nfrag++;
wsi->u.http2.http.ah->next_frag_index++;
return 0;
}
static void lws_dump_header(struct lws *wsi, int hdr)
static void lws_dump_header(struct libwebsocket *wsi, int hdr)
{
char s[200];
int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
@ -245,15 +241,14 @@ static void lws_dump_header(struct lws *wsi, int hdr)
lwsl_info(" hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
}
static int
lws_token_from_index(struct lws *wsi, int index, char **arg, int *len)
static int lws_token_from_index(struct libwebsocket *wsi, int index, char **arg, int *len)
{
struct hpack_dynamic_table *dyn;
/* dynamic table only belongs to network wsi */
wsi = lws_http2_get_network_wsi(wsi);
dyn = wsi->u.http2.hpack_dyn_table;
if (index < ARRAY_SIZE(static_token))
@ -261,25 +256,24 @@ lws_token_from_index(struct lws *wsi, int index, char **arg, int *len)
if (!dyn)
return 0;
index -= ARRAY_SIZE(static_token);
if (index >= dyn->num_entries)
return 0;
if (arg && len) {
*arg = dyn->args + dyn->entries[index].arg_offset;
*len = dyn->entries[index].arg_len;
}
return dyn->entries[index].token;
}
static int
lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
static int lws_hpack_add_dynamic_header(struct libwebsocket *wsi, int token, char *arg, int len)
{
struct hpack_dynamic_table *dyn;
int ret = 1;
wsi = lws_http2_get_network_wsi(wsi);
dyn = wsi->u.http2.hpack_dyn_table;
@ -288,7 +282,7 @@ lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
if (!dyn)
return 1;
wsi->u.http2.hpack_dyn_table = dyn;
dyn->args = lws_malloc(1024);
if (!dyn->args)
goto bail1;
@ -298,25 +292,24 @@ lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
goto bail2;
dyn->num_entries = 20;
}
if (dyn->next == dyn->num_entries)
return 1;
if (dyn->args_length - dyn->pos < len)
return 1;
dyn->entries[dyn->next].token = token;
dyn->entries[dyn->next].arg_offset = dyn->pos;
if (len)
memcpy(dyn->args + dyn->pos, arg, len);
dyn->entries[dyn->next].arg_len = len;
lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
__func__, dyn->next, token, lws_token_to_string(token), len);
lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n", __func__, dyn->next, token, lws_token_to_string(token), len);
dyn->pos += len;
dyn->next++;
return 0;
bail2:
@ -328,13 +321,12 @@ bail1:
return ret;
}
static int lws_write_indexed_hdr(struct lws *wsi, int idx)
static int lws_write_indexed_hdr(struct libwebsocket *wsi, int idx)
{
const char *p;
int tok = lws_token_from_index(wsi, idx, NULL, 0);
lwsl_info("writing indexed hdr %d (tok %d '%s')\n", idx, tok,
lws_token_to_string(tok));
lwsl_info("writing indexed hdr %d (tok %d '%s')\n", idx, tok, lws_token_to_string(tok));
if (lws_frag_start(wsi, tok))
return 1;
@ -347,20 +339,19 @@ static int lws_write_indexed_hdr(struct lws *wsi, int idx)
}
if (lws_frag_end(wsi))
return 1;
lws_dump_header(wsi, tok);
return 0;
}
int lws_hpack_interpret(struct lws *wsi, unsigned char c)
int lws_hpack_interpret(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char c)
{
unsigned int prev;
unsigned char c1;
int n;
lwsl_debug(" state %d\n", wsi->u.http2.hpack);
switch (wsi->u.http2.hpack) {
case HPKS_OPT_PADDING:
wsi->u.http2.padding = c;
@ -383,18 +374,17 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
/* weight */
wsi->u.http2.hpack = HPKS_TYPE;
break;
case HPKS_TYPE:
if (wsi->u.http2.count > (wsi->u.http2.length - wsi->u.http2.padding)) {
lwsl_info("padding eat\n");
break;
}
if (c & 0x80) { /* indexed header field only */
/* just a possibly-extended integer */
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_7;
lwsl_debug("HKPS_TYPE setting header_index %d\n", c & 0x7f);
wsi->u.http2.header_index = c & 0x7f;
if ((c & 0x7f) == 0x7f) {
wsi->u.http2.hpack_len = c & 0x7f;
@ -402,7 +392,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
wsi->u.http2.hpack = HPKS_IDX_EXT;
break;
}
lwsl_debug("HKPS_TYPE: %d\n", c & 0x7f);
if (lws_write_indexed_hdr(wsi, c & 0x7f))
return 1;
/* stay at same state */
@ -414,7 +403,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
* H + possibly-extended value length
* literal value
*/
lwsl_debug("HKPS_TYPE 2 setting header_index %d\n", 0);
wsi->u.http2.header_index = 0;
if (c == 0x40) { /* literal name */
wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE_INCR;
@ -430,7 +418,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
wsi->u.http2.hpack = HPKS_IDX_EXT;
break;
}
lwsl_debug("HKPS_TYPE 3 setting header_index %d\n", c & 0x3f);
wsi->u.http2.header_index = c & 0x3f;
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
@ -439,7 +426,7 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
switch(c & 0xf0) {
case 0x10: /* literal header never index */
case 0: /* literal header without indexing */
/*
/*
* follows 0x40 except 4-bit hdr idx
* and don't add to index
*/
@ -449,7 +436,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
wsi->u.http2.value = 0;
break;
}
//lwsl_debug("indexed\n");
/* indexed name */
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_4_VALUE;
wsi->u.http2.header_index = 0;
@ -459,7 +445,6 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
wsi->u.http2.hpack = HPKS_IDX_EXT;
break;
}
//lwsl_err("HKPS_TYPE 5 setting header_index %d\n", c & 0xf);
wsi->u.http2.header_index = c & 0xf;
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
@ -480,21 +465,18 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
break;
}
break;
case HPKS_IDX_EXT:
wsi->u.http2.hpack_len += (c & 0x7f) << wsi->u.http2.hpack_m;
wsi->u.http2.hpack_m += 7;
if (!(c & 0x80)) {
switch (wsi->u.http2.hpack_type) {
case HPKT_INDEXED_HDR_7:
//lwsl_err("HKPS_IDX_EXT hdr idx %d\n", wsi->u.http2.hpack_len);
if (lws_write_indexed_hdr(wsi, wsi->u.http2.hpack_len))
return 1;
wsi->u.http2.hpack = HPKS_TYPE;
break;
default:
// lwsl_err("HKPS_IDX_EXT setting header_index %d\n",
// wsi->u.http2.hpack_len);
wsi->u.http2.header_index = wsi->u.http2.hpack_len;
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
@ -510,13 +492,10 @@ int lws_hpack_interpret(struct lws *wsi, unsigned char c)
if (wsi->u.http2.hpack_len < 0x7f) {
pre_data:
if (wsi->u.http2.value) {
if (wsi->u.http2.header_index)
if (lws_frag_start(wsi, lws_token_from_index(wsi,
wsi->u.http2.header_index,
NULL, NULL))) {
// lwsl_notice("%s: hlen failed\n", __func__);
if (lws_frag_start(wsi,
lws_token_from_index(wsi,
wsi->u.http2.header_index, NULL, NULL)))
return 1;
}
} else
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.http2.hpack = HPKS_DATA;
@ -525,7 +504,7 @@ pre_data:
wsi->u.http2.hpack_m = 0;
wsi->u.http2.hpack = HPKS_HLEN_EXT;
break;
case HPKS_HLEN_EXT:
wsi->u.http2.hpack_len += (c & 0x7f) <<
wsi->u.http2.hpack_m;
@ -539,9 +518,10 @@ pre_data:
for (n = 0; n < 8; n++) {
if (wsi->u.http2.huff) {
prev = wsi->u.http2.hpack_pos;
wsi->u.http2.hpack_pos = huftable_decode(
wsi->u.http2.hpack_pos =
huftable_decode(
wsi->u.http2.hpack_pos,
(c >> 7) & 1);
(c >> 7) & 1);
c <<= 1;
if (wsi->u.http2.hpack_pos == 0xffff)
return 1;
@ -549,7 +529,7 @@ pre_data:
continue;
c1 = wsi->u.http2.hpack_pos & 0x7fff;
wsi->u.http2.hpack_pos = 0;
if (!c1 && prev == HUFTABLE_0x100_PREV)
; /* EOT */
} else {
@ -557,46 +537,39 @@ pre_data:
c1 = c;
}
if (wsi->u.http2.value) { /* value */
if (wsi->u.http2.header_index)
if (lws_frag_append(wsi, c1))
return 1;
} else { /* name */
if (lws_parse(wsi, c1))
if (lws_frag_append(wsi, c1))
return 1;
} else { /* name */
if (libwebsocket_parse(context, wsi, c1))
return 1;
}
}
if (--wsi->u.http2.hpack_len == 0) {
switch (wsi->u.http2.hpack_type) {
case HPKT_LITERAL_HDR_VALUE_INCR:
case HPKT_INDEXED_HDR_6_VALUE_INCR: // !!!
if (lws_hpack_add_dynamic_header(wsi,
lws_token_from_index(wsi,
wsi->u.http2.header_index,
NULL, NULL), NULL, 0))
if (lws_hpack_add_dynamic_header(wsi, lws_token_from_index(wsi, wsi->u.http2.header_index, NULL, NULL), NULL, 0))
return 1;
break;
default:
break;
}
n = 8;
if (wsi->u.http2.value) {
if (lws_frag_end(wsi))
return 1;
// lwsl_err("data\n");
lws_dump_header(wsi, lws_token_from_index(
wsi, wsi->u.http2.header_index,
NULL, NULL));
if (wsi->u.http2.count + wsi->u.http2.padding ==
wsi->u.http2.length)
lws_dump_header(wsi, lws_token_from_index(wsi, wsi->u.http2.header_index, NULL, NULL));
if (wsi->u.http2.count + wsi->u.http2.padding == wsi->u.http2.length)
wsi->u.http2.hpack = HKPS_OPT_DISCARD_PADDING;
else
wsi->u.http2.hpack = HPKS_TYPE;
} else { /* name */
//if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
}
@ -608,12 +581,11 @@ pre_data:
wsi->u.http2.hpack = HPKS_TYPE;
break;
}
return 0;
}
static int lws_http2_num(int starting_bits, unsigned long num,
unsigned char **p, unsigned char *end)
static int lws_http2_num(int starting_bits, unsigned long num, unsigned char **p, unsigned char *end)
{
int mask = (1 << starting_bits) - 1;
@ -621,11 +593,11 @@ static int lws_http2_num(int starting_bits, unsigned long num,
*((*p)++) |= num;
return *p >= end;
}
*((*p)++) |= mask;
if (*p >= end)
return 1;
num -= mask;
while (num >= 128) {
*((*p)++) = 0x80 | (num & 0x7f);
@ -633,19 +605,22 @@ static int lws_http2_num(int starting_bits, unsigned long num,
return 1;
num >>= 7;
}
return 0;
}
int lws_add_http2_header_by_name(struct lws *wsi,
const unsigned char *name,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
int lws_add_http2_header_by_name(struct libwebsocket_context *context,
struct libwebsocket *wsi,
const unsigned char *name,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
{
int len;
lwsl_info("%s: %p %s:%s\n", __func__, *p, name, value);
len = strlen((char *)name);
if (len)
if (name[len - 1] == ':')
@ -665,39 +640,43 @@ int lws_add_http2_header_by_name(struct lws *wsi,
*(*p) = 0; /* non-HUF */
if (lws_http2_num(7, length, p, end))
return 1;
memcpy(*p, value, length);
*p += length;
return 0;
}
int lws_add_http2_header_by_token(struct lws *wsi, enum lws_token_indexes token,
const unsigned char *value, int length,
unsigned char **p, unsigned char *end)
int lws_add_http2_header_by_token(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum lws_token_indexes token,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
{
const unsigned char *name;
name = lws_token_to_string(token);
if (!name)
return 1;
return lws_add_http2_header_by_name(wsi, name, value, length, p, end);
return lws_add_http2_header_by_name(context, wsi, name, value, length, p, end);
}
int lws_add_http2_header_status(struct lws *wsi,
unsigned int code, unsigned char **p,
unsigned char *end)
int lws_add_http2_header_status(struct libwebsocket_context *context,
struct libwebsocket *wsi,
unsigned int code,
unsigned char **p,
unsigned char *end)
{
unsigned char status[10];
int n;
wsi->u.http2.send_END_STREAM = !!(code >= 400);
n = sprintf((char *)status, "%u", code);
if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS,
status, n, p, end))
if (lws_add_http2_header_by_token(context, wsi, WSI_TOKEN_HTTP_COLON_STATUS, status, n, p, end))
return 1;
return 0;

View file

@ -38,33 +38,31 @@ void lws_http2_init(struct http2_settings *settings)
memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings));
}
struct lws *
lws_http2_wsi_from_id(struct lws *wsi, unsigned int sid)
struct libwebsocket *
lws_http2_wsi_from_id(struct libwebsocket *wsi, unsigned int sid)
{
do {
if (wsi->u.http2.my_stream_id == sid)
return wsi;
wsi = wsi->u.http2.next_child_wsi;
} while (wsi);
return NULL;
}
struct lws *
lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
unsigned int sid)
struct libwebsocket *
lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *parent_wsi, unsigned int sid)
{
struct lws *wsi = lws_create_new_server_wsi(vhost);
struct libwebsocket *wsi = libwebsocket_create_new_server_wsi(context);
if (!wsi)
return NULL;
/* no more children allowed by parent */
if (parent_wsi->u.http2.child_count + 1 ==
parent_wsi->u.http2.peer_settings.setting[
LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS])
goto bail;
if (parent_wsi->u.http2.child_count + 1 == parent_wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS])
return NULL;
lws_http2_init(&wsi->u.http2.peer_settings);
lws_http2_init(&wsi->u.http2.my_settings);
wsi->u.http2.stream_id = sid;
@ -74,59 +72,49 @@ lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi;
parent_wsi->u.http2.next_child_wsi = wsi;
parent_wsi->u.http2.child_count++;
wsi->u.http2.my_priority = 16;
wsi->u.http2.tx_credit = 65535;
wsi->state = LWSS_HTTP2_ESTABLISHED;
wsi->state = WSI_STATE_HTTP2_ESTABLISHED;
wsi->mode = parent_wsi->mode;
wsi->protocol = &context->protocols[0];
libwebsocket_ensure_user_space(wsi);
wsi->protocol = &vhost->protocols[0];
if (lws_ensure_user_space(wsi))
goto bail;
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__,
parent_wsi, wsi, sid, wsi->user_space);
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space);
return wsi;
bail:
vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
NULL, NULL, 0);
lws_free(wsi);
return NULL;
}
int lws_remove_server_child_wsi(struct lws_context *context, struct lws *wsi)
int lws_remove_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
struct lws **w = &wsi->u.http2.parent_wsi;
struct libwebsocket **w = &wsi->u.http2.parent_wsi;
do {
if (*w == wsi) {
*w = wsi->u.http2.next_child_wsi;
(wsi->u.http2.parent_wsi)->u.http2.child_count--;
return 0;
}
w = &((*w)->u.http2.next_child_wsi);
} while (*w);
lwsl_err("%s: can't find %p\n", __func__, wsi);
return 1;
}
int
lws_http2_interpret_settings_payload(struct http2_settings *settings,
unsigned char *buf, int len)
lws_http2_interpret_settings_payload(struct http2_settings *settings, unsigned char *buf, int len)
{
unsigned int a, b;
if (!len)
return 0;
if (len < LWS_HTTP2_SETTINGS_LENGTH)
return 1;
while (len >= LWS_HTTP2_SETTINGS_LENGTH) {
a = (buf[0] << 8) | buf[1];
if (a < LWS_HTTP2_SETTINGS__COUNT) {
@ -137,25 +125,24 @@ lws_http2_interpret_settings_payload(struct http2_settings *settings,
len -= LWS_HTTP2_SETTINGS_LENGTH;
buf += LWS_HTTP2_SETTINGS_LENGTH;
}
if (len)
return 1;
return 0;
}
struct lws *lws_http2_get_network_wsi(struct lws *wsi)
struct libwebsocket *lws_http2_get_network_wsi(struct libwebsocket *wsi)
{
while (wsi->u.http2.parent_wsi)
wsi = wsi->u.http2.parent_wsi;
return wsi;
}
int lws_http2_frame_write(struct lws *wsi, int type, int flags,
unsigned int sid, unsigned int len, unsigned char *buf)
int lws_http2_frame_write(struct libwebsocket *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf)
{
struct lws *wsi_eff = lws_http2_get_network_wsi(wsi);
struct libwebsocket *wsi_eff = lws_http2_get_network_wsi(wsi);
unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
int n;
@ -168,28 +155,24 @@ int lws_http2_frame_write(struct lws *wsi, int type, int flags,
*p++ = sid >> 16;
*p++ = sid >> 8;
*p++ = sid;
lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d, tx_credit=%d\n",
__func__, wsi, wsi_eff, type, flags, sid, len,
wsi->u.http2.tx_credit);
lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d\n",
__func__, wsi, wsi_eff, type, flags, sid, len, wsi->u.http2.tx_credit);
if (type == LWS_HTTP2_FRAME_TYPE_DATA) {
if (wsi->u.http2.tx_credit < len)
lwsl_err("%s: %p: sending payload len %d"
" but tx_credit only %d!\n", __func__, wsi, len,
wsi->u.http2.tx_credit);
lwsl_err("%s: %p: sending payload len %d but tx_credit only %d!\n", len, wsi->u.http2.tx_credit);
wsi->u.http2.tx_credit -= len;
}
n = lws_issue_raw(wsi_eff, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH],
len + LWS_HTTP2_FRAME_HEADER_LENGTH);
n = lws_issue_raw(wsi_eff, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH], len + LWS_HTTP2_FRAME_HEADER_LENGTH);
if (n >= LWS_HTTP2_FRAME_HEADER_LENGTH)
return n - LWS_HTTP2_FRAME_HEADER_LENGTH;
return n;
}
static void lws_http2_settings_write(struct lws *wsi, int n, unsigned char *buf)
static void lws_http2_settings_write(struct libwebsocket *wsi, int n, unsigned char *buf)
{
*buf++ = n >> 8;
*buf++ = n;
@ -203,35 +186,36 @@ static const char * https_client_preface =
"PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a";
int
lws_http2_parser(struct lws *wsi, unsigned char c)
lws_http2_parser(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char c)
{
struct lws *swsi;
struct libwebsocket *swsi;
int n;
//dstruct libwebsocket *wsi_new;
switch (wsi->state) {
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
if (https_client_preface[wsi->u.http2.count++] != c)
return 1;
if (!https_client_preface[wsi->u.http2.count]) {
lwsl_info("http2: %p: established\n", wsi);
wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS;
wsi->state = WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS;
wsi->u.http2.count = 0;
wsi->u.http2.tx_credit = 65535;
/*
/*
* we must send a settings frame -- empty one is OK...
* that must be the first thing sent by server
* and the peer must send a SETTINGS with ACK flag...
*/
lws_set_protocol_write_pending(wsi,
LWS_PPS_HTTP2_MY_SETTINGS);
lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_MY_SETTINGS);
}
break;
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
case WSI_STATE_HTTP2_ESTABLISHED:
if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { // payload
wsi->u.http2.count++;
wsi->u.http2.stream_wsi->u.http2.count = wsi->u.http2.count;
@ -249,15 +233,8 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
case LWS_HTTP2_FRAME_TYPE_HEADERS:
lwsl_info(" %02X\n", c);
if (!wsi->u.http2.stream_wsi->u.hdr.ah)
if (lws_header_table_attach(wsi->u.http2.stream_wsi, 0)) {
lwsl_err("%s: Failed to get ah\n", __func__);
return 1;
}
if (lws_hpack_interpret(wsi->u.http2.stream_wsi, c)) {
lwsl_notice("%s: lws_hpack_interpret failed\n", __func__);
if (lws_hpack_interpret(context, wsi->u.http2.stream_wsi, c))
return 1;
}
break;
case LWS_HTTP2_FRAME_TYPE_GOAWAY:
if (wsi->u.http2.count >= 5 && wsi->u.http2.count <= 8) {
@ -292,7 +269,7 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
}
if (wsi->u.http2.count != wsi->u.http2.length)
break;
/* end of frame */
wsi->u.http2.frame_state = 0;
@ -308,26 +285,26 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
case LWS_HTTP2_FRAME_TYPE_HEADERS:
/* service the http request itself */
lwsl_info("servicing initial http request, wsi=%p, stream wsi=%p\n", wsi, wsi->u.http2.stream_wsi);
n = lws_http_action(swsi);
n = lws_http_action(context, swsi);
(void)n;
lwsl_info(" action result %d\n", n);
break;
case LWS_HTTP2_FRAME_TYPE_PING:
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
} else { /* they're sending us a ping request */
lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_PONG);
lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_PONG);
}
break;
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
wsi->u.http2.hpack_e_dep &= ~(1 << 31);
if ((lws_intptr_t)swsi->u.http2.tx_credit + (lws_intptr_t)wsi->u.http2.hpack_e_dep > (~(1 << 31)))
if ((long long)swsi->u.http2.tx_credit + (unsigned long long)wsi->u.http2.hpack_e_dep > (~(1 << 31)))
return 1; /* actually need to close swsi not the whole show */
swsi->u.http2.tx_credit += wsi->u.http2.hpack_e_dep;
if (swsi->u.http2.waiting_tx_credit && swsi->u.http2.tx_credit > 0) {
lwsl_info("%s: %p: waiting_tx_credit -> wait on writeable\n", __func__, wsi);
swsi->u.http2.waiting_tx_credit = 0;
lws_callback_on_writable(swsi);
}
libwebsocket_callback_on_writable(context, swsi);
}
break;
}
break;
@ -359,21 +336,21 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
lwsl_info("frame: type 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n",
wsi->u.http2.type, wsi->u.http2.flags, wsi->u.http2.stream_id, wsi->u.http2.length);
wsi->u.http2.count = 0;
wsi->u.http2.stream_wsi = wsi;
if (wsi->u.http2.stream_id)
wsi->u.http2.stream_wsi = lws_http2_wsi_from_id(wsi, wsi->u.http2.stream_id);
switch (wsi->u.http2.type) {
case LWS_HTTP2_FRAME_TYPE_SETTINGS:
/* nonzero sid on settings is illegal */
if (wsi->u.http2.stream_id)
return 1;
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_SETTINGS_ACK) { // ack
} else
/* non-ACK coming in means we must ACK it */
lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
break;
case LWS_HTTP2_FRAME_TYPE_PING:
if (wsi->u.http2.stream_id)
@ -390,26 +367,23 @@ lws_http2_parser(struct lws *wsi, unsigned char c)
lwsl_info("LWS_HTTP2_FRAME_TYPE_HEADERS: stream_id = %d\n", wsi->u.http2.stream_id);
if (!wsi->u.http2.stream_id)
return 1;
if (!wsi->u.http2.stream_wsi) {
wsi->u.http2.stream_wsi =
lws_create_server_child_wsi(wsi->vhost, wsi, wsi->u.http2.stream_id);
wsi->u.http2.stream_wsi->http2_substream = 1;
}
if (!wsi->u.http2.stream_wsi)
wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id);
/* END_STREAM means after servicing this, close the stream */
wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
lwsl_info("%s: headers END_STREAM = %d\n",__func__, wsi->u.http2.END_STREAM);
update_end_headers:
/* no END_HEADERS means CONTINUATION must come */
wsi->u.http2.END_HEADERS = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_HEADERS);
swsi = wsi->u.http2.stream_wsi;
if (!swsi)
return 1;
/* prepare the hpack parser at the right start */
swsi->u.http2.flags = wsi->u.http2.flags;
swsi->u.http2.length = wsi->u.http2.length;
swsi->u.http2.END_STREAM = wsi->u.http2.END_STREAM;
@ -435,29 +409,29 @@ update_end_headers:
}
break;
}
return 0;
}
int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
struct lws *swsi;
unsigned char settings[LWS_SEND_BUFFER_PRE_PADDING + 6 * LWS_HTTP2_SETTINGS__COUNT];
struct libwebsocket *swsi;
int n, m = 0;
lwsl_debug("%s: %p: %d\n", __func__, wsi, wsi->pps);
switch (wsi->pps) {
case LWS_PPS_HTTP2_MY_SETTINGS:
for (n = 1; n < LWS_HTTP2_SETTINGS__COUNT; n++)
if (wsi->u.http2.my_settings.setting[n] != lws_http2_default_settings.setting[n]) {
lws_http2_settings_write(wsi, n,
&settings[LWS_PRE + m]);
&settings[LWS_SEND_BUFFER_PRE_PADDING + m]);
m += sizeof(wsi->u.http2.one_setting);
}
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
0, LWS_HTTP2_STREAM_ID_MASTER, m,
&settings[LWS_PRE]);
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
if (n != m) {
lwsl_info("send %d %d\n", n, m);
return 1;
@ -467,35 +441,34 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
/* send ack ... always empty */
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
1, LWS_HTTP2_STREAM_ID_MASTER, 0,
&settings[LWS_PRE]);
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
if (n) {
lwsl_err("ack tells %d\n", n);
return 1;
}
/* this is the end of the preface dance then? */
if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) {
wsi->state = LWSS_HTTP2_ESTABLISHED;
wsi->u.http.fop_fd = NULL;
if (wsi->state == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS) {
wsi->state = WSI_STATE_HTTP2_ESTABLISHED;
wsi->u.http.fd = LWS_INVALID_FILE;
if (lws_is_ssl(lws_http2_get_network_wsi(wsi))) {
lwsl_info("skipping nonexistent ssl upgrade headers\n");
lwsl_info("skipping nonexistant ssl upgrade headers\n");
break;
}
/*
/*
* we need to treat the headers from this upgrade
* as the first job. These need to get
* shifted to stream ID 1
*/
lwsl_info("%s: setting up sid 1\n", __func__);
swsi = wsi->u.http2.stream_wsi =
lws_create_server_child_wsi(wsi->vhost, wsi, 1);
swsi = wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, 1);
/* pass on the initial headers to SID 1 */
swsi->u.http.ah = wsi->u.http.ah;
wsi->u.http.ah = NULL;
lwsl_info("%s: inherited headers %p\n", __func__, swsi->u.http.ah);
swsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
lwsl_info("initial tx credit on conn %p: %d\n", swsi, swsi->u.http2.tx_credit);
@ -503,15 +476,15 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
/* demanded by HTTP2 */
swsi->u.http2.END_STREAM = 1;
lwsl_info("servicing initial http request\n");
return lws_http_action(swsi);
return lws_http_action(context, swsi);
}
break;
case LWS_PPS_HTTP2_PONG:
memcpy(&settings[LWS_PRE], wsi->u.http2.ping_payload, 8);
memcpy(&settings[LWS_SEND_BUFFER_PRE_PADDING], wsi->u.http2.ping_payload, 8);
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_PING,
LWS_HTTP2_FLAG_SETTINGS_ACK,
LWS_HTTP2_STREAM_ID_MASTER, 8,
&settings[LWS_PRE]);
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
if (n != 8) {
lwsl_info("send %d %d\n", n, m);
return 1;
@ -520,11 +493,11 @@ int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
default:
break;
}
return 0;
}
struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
struct libwebsocket * lws_http2_get_nth_child(struct libwebsocket *wsi, int n)
{
do {
wsi = wsi->u.http2.next_child_wsi;

View file

@ -1,916 +0,0 @@
/*
* libwebsockets web server application
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "lejp.h"
#ifndef _WIN32
/* this is needed for Travis CI */
#include <dirent.h>
#endif
#define ESC_INSTALL_DATADIR "_lws_ddir_"
static const char * const paths_global[] = {
"global.uid",
"global.gid",
"global.count-threads",
"global.init-ssl",
"global.server-string",
"global.plugin-dir",
"global.ws-pingpong-secs",
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
};
enum lejp_global_paths {
LEJPGP_UID,
LEJPGP_GID,
LEJPGP_COUNT_THREADS,
LWJPGP_INIT_SSL,
LEJPGP_SERVER_STRING,
LEJPGP_PLUGIN_DIR,
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS
};
static const char * const paths_vhosts[] = {
"vhosts[]",
"vhosts[].mounts[]",
"vhosts[].name",
"vhosts[].port",
"vhosts[].interface",
"vhosts[].unix-socket",
"vhosts[].sts",
"vhosts[].host-ssl-key",
"vhosts[].host-ssl-cert",
"vhosts[].host-ssl-ca",
"vhosts[].access-log",
"vhosts[].mounts[].mountpoint",
"vhosts[].mounts[].origin",
"vhosts[].mounts[].protocol",
"vhosts[].mounts[].default",
"vhosts[].mounts[].auth-mask",
"vhosts[].mounts[].cgi-timeout",
"vhosts[].mounts[].cgi-env[].*",
"vhosts[].mounts[].cache-max-age",
"vhosts[].mounts[].cache-reuse",
"vhosts[].mounts[].cache-revalidate",
"vhosts[].mounts[].basic-auth",
"vhosts[].mounts[].cache-intermediaries",
"vhosts[].mounts[].extra-mimetypes.*",
"vhosts[].mounts[].interpret.*",
"vhosts[].ws-protocols[].*.*",
"vhosts[].ws-protocols[].*",
"vhosts[].ws-protocols[]",
"vhosts[].keepalive_timeout",
"vhosts[].enable-client-ssl",
"vhosts[].ciphers",
"vhosts[].ecdh-curve",
"vhosts[].noipv6",
"vhosts[].ipv6only",
"vhosts[].ssl-option-set",
"vhosts[].ssl-option-clear",
"vhosts[].mounts[].pmo[].*",
"vhosts[].headers[].*",
"vhosts[].headers[]",
"vhosts[].client-ssl-key",
"vhosts[].client-ssl-cert",
"vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers",
};
enum lejp_vhost_paths {
LEJPVP,
LEJPVP_MOUNTS,
LEJPVP_NAME,
LEJPVP_PORT,
LEJPVP_INTERFACE,
LEJPVP_UNIXSKT,
LEJPVP_STS,
LEJPVP_HOST_SSL_KEY,
LEJPVP_HOST_SSL_CERT,
LEJPVP_HOST_SSL_CA,
LEJPVP_ACCESS_LOG,
LEJPVP_MOUNTPOINT,
LEJPVP_ORIGIN,
LEJPVP_MOUNT_PROTOCOL,
LEJPVP_DEFAULT,
LEJPVP_DEFAULT_AUTH_MASK,
LEJPVP_CGI_TIMEOUT,
LEJPVP_CGI_ENV,
LEJPVP_MOUNT_CACHE_MAX_AGE,
LEJPVP_MOUNT_CACHE_REUSE,
LEJPVP_MOUNT_CACHE_REVALIDATE,
LEJPVP_MOUNT_BASIC_AUTH,
LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
LEJPVP_MOUNT_EXTRA_MIMETYPES,
LEJPVP_MOUNT_INTERPRET,
LEJPVP_PROTOCOL_NAME_OPT,
LEJPVP_PROTOCOL_NAME,
LEJPVP_PROTOCOL,
LEJPVP_KEEPALIVE_TIMEOUT,
LEJPVP_ENABLE_CLIENT_SSL,
LEJPVP_CIPHERS,
LEJPVP_ECDH_CURVE,
LEJPVP_NOIPV6,
LEJPVP_IPV6ONLY,
LEJPVP_SSL_OPTION_SET,
LEJPVP_SSL_OPTION_CLEAR,
LEJPVP_PMO,
LEJPVP_HEADERS_NAME,
LEJPVP_HEADERS,
LEJPVP_CLIENT_SSL_KEY,
LEJPVP_CLIENT_SSL_CERT,
LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS,
};
static const char * const parser_errs[] = {
"",
"",
"No opening '{'",
"Expected closing '}'",
"Expected '\"'",
"String underrun",
"Illegal unescaped control char",
"Illegal escape format",
"Illegal hex number",
"Expected ':'",
"Illegal value start",
"Digit required after decimal point",
"Bad number format",
"Bad exponent format",
"Unknown token",
"Too many ']'",
"Mismatched ']'",
"Expected ']'",
"JSON nesting limit exceeded",
"Nesting tracking used up",
"Number too long",
"Comma or block end expected",
"Unknown",
"Parser callback errored (see earlier error)",
};
#define MAX_PLUGIN_DIRS 10
struct jpargs {
struct lws_context_creation_info *info;
struct lws_context *context;
const struct lws_protocols *protocols;
const struct lws_extension *extensions;
char *p, *end, valid;
struct lws_http_mount *head, *last;
struct lws_protocol_vhost_options *pvo;
struct lws_protocol_vhost_options *pvo_em;
struct lws_protocol_vhost_options *pvo_int;
struct lws_http_mount m;
const char **plugin_dirs;
int count_plugin_dirs;
unsigned int enable_client_ssl:1;
unsigned int fresh_mount:1;
unsigned int any_vhosts:1;
};
static void *
lwsws_align(struct jpargs *a)
{
if ((lws_intptr_t)(a->p) & 15)
a->p += 16 - ((lws_intptr_t)(a->p) & 15);
return a->p;
}
static int
arg_to_bool(const char *s)
{
static const char * const on[] = { "on", "yes", "true" };
int n = atoi(s);
if (n)
return 1;
for (n = 0; n < ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
return 0;
}
static char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *rej;
int n;
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
/* this catches, eg, vhosts[].headers[].xxx */
if (reason == LEJPCB_VAL_STR_END &&
ctx->path_match == LWJPGP_REJECT_SERVICE_KEYWORDS_NAME + 1) {
rej = lwsws_align(a);
a->p += sizeof(*rej);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
rej->next = a->info->reject_service_keywords;
a->info->reject_service_keywords = rej;
rej->name = a->p;
lwsl_notice(" adding rej %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = '\0';
rej->value = a->p;
rej->options = NULL;
goto dostring;
}
switch (ctx->path_match - 1) {
case LEJPGP_UID:
a->info->uid = atoi(ctx->buf);
return 0;
case LEJPGP_GID:
a->info->gid = atoi(ctx->buf);
return 0;
case LEJPGP_COUNT_THREADS:
a->info->count_threads = atoi(ctx->buf);
return 0;
case LWJPGP_INIT_SSL:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
return 0;
case LEJPGP_SERVER_STRING:
a->info->server_string = a->p;
break;
case LEJPGP_PLUGIN_DIR:
if (a->count_plugin_dirs == MAX_PLUGIN_DIRS - 1) {
lwsl_err("Too many plugin dirs\n");
return -1;
}
a->plugin_dirs[a->count_plugin_dirs++] = a->p;
break;
case LWJPGP_PINGPONG_SECS:
a->info->ws_ping_pong_interval = atoi(ctx->buf);
return 0;
case LWJPGP_TIMEOUT_SECS:
a->info->timeout_secs = atoi(ctx->buf);
return 0;
default:
return 0;
}
dostring:
a->p += lws_snprintf(a->p, a->end - a->p, "%s", ctx->buf);
*(a->p)++ = '\0';
return 0;
}
static char
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
struct lws_protocol_vhost_options *pvo, *mp_cgienv, *headers;
struct lws_http_mount *m;
char *p, *p1;
int n;
#if 0
lwsl_notice(" %d: %s (%d)\n", reason, ctx->path, ctx->path_match);
for (n = 0; n < ctx->wildcount; n++)
lwsl_notice(" %d\n", ctx->wild[n]);
#endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
a->info->port = 0;
a->info->iface = NULL;
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
a->info->ssl_cert_filepath = NULL;
a->info->ssl_private_key_filepath = NULL;
a->info->ssl_ca_filepath = NULL;
a->info->client_ssl_cert_filepath = NULL;
a->info->client_ssl_private_key_filepath = NULL;
a->info->client_ssl_ca_filepath = NULL;
a->info->client_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";
a->info->timeout_secs = 5;
a->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";
a->info->pvo = NULL;
a->info->headers = NULL;
a->info->keepalive_timeout = 5;
a->info->log_filepath = NULL;
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK |
LWS_SERVER_OPTION_STS);
a->enable_client_ssl = 0;
}
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
a->fresh_mount = 1;
memset(&a->m, 0, sizeof(a->m));
}
/* this catches, eg, vhosts[].ws-protocols[].xxx-protocol */
if (reason == LEJPCB_OBJECT_START &&
ctx->path_match == LEJPVP_PROTOCOL_NAME + 1) {
a->pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo;
a->pvo->name = a->p;
lwsl_notice(" adding protocol %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
goto dostring;
}
/* this catches, eg, vhosts[].headers[].xxx */
if (reason == LEJPCB_VAL_STR_END &&
ctx->path_match == LEJPVP_HEADERS_NAME + 1) {
headers = lwsws_align(a);
a->p += sizeof(*headers);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
headers->next = a->info->headers;
a->info->headers = headers;
headers->name = a->p;
// lwsl_notice(" adding header %s=%s\n", a->p, ctx->buf);
a->p += n - 1;
*(a->p++) = ':';
if (a->p < a->end)
*(a->p++) = '\0';
else
*(a->p - 1) = '\0';
headers->value = a->p;
headers->options = NULL;
goto dostring;
}
if (reason == LEJPCB_OBJECT_END &&
(ctx->path_match == LEJPVP + 1 || !ctx->path[0]) &&
a->valid) {
struct lws_vhost *vhost;
//lwsl_notice("%s\n", ctx->path);
if (!a->info->port) {
lwsl_err("Port required (eg, 443)");
return 1;
}
a->valid = 0;
a->info->mounts = a->head;
vhost = lws_create_vhost(a->context, a->info);
if (!vhost) {
lwsl_err("Failed to create vhost %s\n",
a->info->vhost_name);
return 1;
}
a->any_vhosts = 1;
if (a->enable_client_ssl) {
const char *cert_filepath = a->info->client_ssl_cert_filepath;
const char *private_key_filepath = a->info->client_ssl_private_key_filepath;
const char *ca_filepath = a->info->client_ssl_ca_filepath;
const char *cipher_list = a->info->client_ssl_cipher_list;
memset(a->info, 0, sizeof(*a->info));
a->info->client_ssl_cert_filepath = cert_filepath;
a->info->client_ssl_private_key_filepath = private_key_filepath;
a->info->client_ssl_ca_filepath = ca_filepath;
a->info->client_ssl_cipher_list = cipher_list;
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost);
}
return 0;
}
if (reason == LEJPCB_OBJECT_END &&
ctx->path_match == LEJPVP_MOUNTS + 1) {
static const char * const mount_protocols[] = {
"http://",
"https://",
"file://",
"cgi://",
">http://",
">https://",
"callback://",
"gzip://",
};
if (!a->fresh_mount)
return 0;
if (!a->m.mountpoint || !a->m.origin) {
lwsl_err("mountpoint and origin required\n");
return 1;
}
lwsl_debug("adding mount %s\n", a->m.mountpoint);
m = lwsws_align(a);
memcpy(m, &a->m, sizeof(*m));
if (a->last)
a->last->mount_next = m;
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_err("----%s\n", a->m.origin);
m->origin_protocol = n;
m->origin = a->m.origin +
strlen(mount_protocols[n]);
break;
}
if (n == ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
a->p += sizeof(*m);
if (!a->head)
a->head = m;
a->last = m;
a->fresh_mount = 0;
}
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
case LEJPVP_NAME:
a->info->vhost_name = a->p;
break;
case LEJPVP_PORT:
a->info->port = atoi(ctx->buf);
return 0;
case LEJPVP_INTERFACE:
a->info->iface = a->p;
break;
case LEJPVP_UNIXSKT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_UNIX_SOCK;
else
a->info->options &= ~(LWS_SERVER_OPTION_UNIX_SOCK);
return 0;
case LEJPVP_STS:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_STS;
else
a->info->options &= ~(LWS_SERVER_OPTION_STS);
return 0;
case LEJPVP_HOST_SSL_KEY:
a->info->ssl_private_key_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CERT:
a->info->ssl_cert_filepath = a->p;
break;
case LEJPVP_HOST_SSL_CA:
a->info->ssl_ca_filepath = a->p;
break;
case LEJPVP_ACCESS_LOG:
a->info->log_filepath = a->p;
break;
case LEJPVP_MOUNTPOINT:
a->m.mountpoint = a->p;
a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
break;
case LEJPVP_ORIGIN:
if (!strncmp(ctx->buf, "callback://", 11))
a->m.protocol = a->p + 11;
if (!a->m.origin)
a->m.origin = a->p;
break;
case LEJPVP_DEFAULT:
a->m.def = a->p;
break;
case LEJPVP_DEFAULT_AUTH_MASK:
a->m.auth_mask = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_MAX_AGE:
a->m.cache_max_age = atoi(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REUSE:
a->m.cache_reusable = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_REVALIDATE:
a->m.cache_revalidate = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_MOUNT_CACHE_INTERMEDIARIES:
a->m.cache_intermediaries = arg_to_bool(ctx->buf);;
return 0;
case LEJPVP_MOUNT_BASIC_AUTH:
a->m.basic_auth_login_file = a->p;
break;
case LEJPVP_CGI_TIMEOUT:
a->m.cgi_timeout = atoi(ctx->buf);
return 0;
case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf);
return 0;
case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p;
break;
case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p;
break;
case LEJPVP_ECDH_CURVE:
a->info->ecdh_curve = a->p;
break;
case LEJPVP_PMO:
case LEJPVP_CGI_ENV:
mp_cgienv = lwsws_align(a);
a->p += sizeof(*a->m.cgienv);
mp_cgienv->next = a->m.cgienv;
a->m.cgienv = mp_cgienv;
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
mp_cgienv->name = a->p;
a->p += n;
mp_cgienv->value = a->p;
mp_cgienv->options = NULL;
//lwsl_notice(" adding pmo / cgi-env '%s' = '%s'\n", mp_cgienv->name,
// mp_cgienv->value);
goto dostring;
case LEJPVP_PROTOCOL_NAME_OPT:
/* this catches, eg,
* vhosts[].ws-protocols[].xxx-protocol.yyy-option
* ie, these are options attached to a protocol with { }
*/
pvo = lwsws_align(a);
a->p += sizeof(*a->pvo);
n = lejp_get_wildcard(ctx, 1, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
pvo->next = a->pvo->options;
a->pvo->options = pvo;
pvo->name = a->p;
a->p += n;
pvo->value = a->p;
pvo->options = NULL;
break;
case LEJPVP_MOUNT_EXTRA_MIMETYPES:
a->pvo_em = lwsws_align(a);
a->p += sizeof(*a->pvo_em);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo_em->next = a->m.extra_mimetypes;
a->m.extra_mimetypes = a->pvo_em;
a->pvo_em->name = a->p;
lwsl_notice(" adding extra-mimetypes %s -> %s\n", a->p, ctx->buf);
a->p += n;
a->pvo_em->value = a->p;
a->pvo_em->options = NULL;
break;
case LEJPVP_MOUNT_INTERPRET:
a->pvo_int = lwsws_align(a);
a->p += sizeof(*a->pvo_int);
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
/* ie, enable this protocol, no options yet */
a->pvo_int->next = a->m.interpret;
a->m.interpret = a->pvo_int;
a->pvo_int->name = a->p;
lwsl_notice(" adding interpret %s -> %s\n", a->p,
ctx->buf);
a->p += n;
a->pvo_int->value = a->p;
a->pvo_int->options = NULL;
break;
case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0;
case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CERT:
a->info->client_ssl_cert_filepath = a->p;
break;
case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p;
break;
case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6;
else
a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
return 0;
case LEJPVP_IPV6ONLY:
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE;
else
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0;
case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf);
return 0;
case LEJPVP_SSL_OPTION_CLEAR:
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
default:
return 0;
}
dostring:
p = ctx->buf;
p1 = strstr(p, ESC_INSTALL_DATADIR);
if (p1) {
n = p1 - p;
if (n > a->end - a->p)
n = a->end - a->p;
strncpy(a->p, p, n);
a->p += n;
a->p += lws_snprintf(a->p, a->end - a->p, "%s", LWS_INSTALL_DATADIR);
p += n + strlen(ESC_INSTALL_DATADIR);
}
a->p += lws_snprintf(a->p, a->end - a->p, "%s", p);
*(a->p)++ = '\0';
return 0;
}
/*
* returns 0 = OK, 1 = can't open, 2 = parsing error
*/
static int
lwsws_get_config(void *user, const char *f, const char * const *paths,
int count_paths, lejp_callback cb)
{
unsigned char buf[128];
struct lejp_ctx ctx;
int n, m, fd;
fd = open(f, O_RDONLY);
if (fd < 0) {
lwsl_err("Cannot open %s\n", f);
return 2;
}
lwsl_info("%s: %s\n", __func__, f);
lejp_construct(&ctx, cb, user, paths, count_paths);
do {
n = read(fd, buf, sizeof(buf));
if (!n)
break;
m = (int)(signed char)lejp_parse(&ctx, buf, n);
} while (m == LEJP_CONTINUE);
close(fd);
n = ctx.line;
lejp_destruct(&ctx);
if (m < 0) {
lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m,
parser_errs[-m]);
return 2;
}
return 0;
}
#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
uv_dirent_t dent;
uv_fs_t req;
char path[256];
int ret = 0, ir;
uv_loop_t loop;
ir = uv_loop_init(&loop);
if (ir) {
lwsl_err("%s: loop init failed %d\n", __func__, ir);
}
if (!uv_fs_scandir(&loop, &req, d, 0, NULL)) {
lwsl_err("Scandir on %s failed\n", d);
return 2;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d, dent.name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret)
goto bail;
}
bail:
uv_fs_req_cleanup(&req);
while (uv_loop_close(&loop))
;
return ret;
}
#else
#ifndef _WIN32
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
#endif
static int
lwsws_get_config_d(void *user, const char *d, const char * const *paths,
int count_paths, lejp_callback cb)
{
#ifndef _WIN32
struct dirent **namelist;
char path[256];
int n, i, ret = 0;
n = scandir(d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %s failed\n", d);
}
for (i = 0; i < n; i++) {
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
namelist[i]->d_name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
if (ret) {
while (i++ < n)
free(namelist[i]);
goto bail;
}
free(namelist[i]);
}
bail:
free(namelist);
return ret;
#else
return 0;
#endif
}
#endif
int
lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
const char * const *old = info->plugin_dirs;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = (a.p + *len) - 1;
a.valid = 0;
lwsws_align(&a);
info->plugin_dirs = (void *)a.p;
a.plugin_dirs = (void *)a.p; /* writeable version */
a.p += MAX_PLUGIN_DIRS * sizeof(void *);
/* copy any default paths */
while (old && *old) {
a.plugin_dirs[a.count_plugin_dirs++] = *old;
old++;
}
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_global,
ARRAY_SIZE(paths_global), lejp_globals_cb) > 1)
return 1;
a.plugin_dirs[a.count_plugin_dirs] = NULL;
*cs = a.p;
*len = a.end - a.p;
return 0;
}
int
lwsws_get_config_vhosts(struct lws_context *context,
struct lws_context_creation_info *info, const char *d,
char **cs, int *len)
{
struct jpargs a;
char dd[128];
memset(&a, 0, sizeof(a));
a.info = info;
a.p = *cs;
a.end = a.p + *len;
a.valid = 0;
a.context = context;
a.protocols = info->protocols;
a.extensions = info->extensions;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf", d);
if (lwsws_get_config(&a, dd, paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
lws_snprintf(dd, sizeof(dd) - 1, "%s/conf.d", d);
if (lwsws_get_config_d(&a, dd, paths_vhosts,
ARRAY_SIZE(paths_vhosts), lejp_vhosts_cb) > 1)
return 1;
*cs = a.p;
*len = a.end - a.p;
if (!a.any_vhosts) {
lwsl_err("Need at least one vhost\n");
return 1;
}
// lws_finalize_startup(context);
return 0;
}

View file

@ -1,709 +0,0 @@
/*
* Lightweight Embedded JSON Parser
*
* Copyright (C) 2013 Andy Green <andy@warmcat.com>
* This code is licensed under LGPL 2.1
* http://www.gnu.org/licenses/lgpl-2.1.html
*/
#include <string.h>
#include "lejp.h"
#include <stdio.h>
/**
* lejp_construct - prepare a struct lejp_ctx for use
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
* \param user: optional user data pointer untouched by lejp
* \param paths: your array of name elements you are interested in
* \param count_paths: ARRAY_SIZE() of @paths
*
* Prepares your context struct for use with lejp
*/
void
lejp_construct(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char count_paths)
{
ctx->st[0].s = 0;
ctx->st[0].p = 0;
ctx->st[0].i = 0;
ctx->st[0].b = 0;
ctx->sp = 0;
ctx->ipos = 0;
ctx->ppos = 0;
ctx->path_match = 0;
ctx->path[0] = '\0';
ctx->callback = callback;
ctx->user = user;
ctx->paths = paths;
ctx->count_paths = count_paths;
ctx->line = 1;
ctx->callback(ctx, LEJPCB_CONSTRUCTED);
}
/**
* lejp_destruct - retire a previously constructed struct lejp_ctx
*
* \param ctx: pointer to your struct lejp_ctx
*
* lejp does not perform any allocations, but since your user code might, this
* provides a one-time LEJPCB_DESTRUCTED callback at destruction time where
* you can clean up in your callback.
*/
void
lejp_destruct(struct lejp_ctx *ctx)
{
/* no allocations... just let callback know what it happening */
ctx->callback(ctx, LEJPCB_DESTRUCTED);
}
/**
* lejp_change_callback - switch to a different callback from now on
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
*
* This tells the old callback it was destroyed, in case you want to take any
* action because that callback "lost focus", then changes to the new
* callback and tells it first that it was constructed, and then started.
*
* Changing callback is a cheap and powerful trick to split out handlers
* according to information earlier in the parse. For example you may have
* a JSON pair "schema" whose value defines what can be expected for the rest
* of the JSON. Rather than having one huge callback for all cases, you can
* have an initial one looking for "schema" which then calls
* lejp_change_callback() to a handler specific for the schema.
*
* Notice that afterwards, you need to construct the context again anyway to
* parse another JSON object, and the callback is reset then to the main,
* schema-interpreting one. The construction action is very lightweight.
*/
void
lejp_change_callback(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason))
{
ctx->callback(ctx, LEJPCB_DESTRUCTED);
ctx->callback = callback;
ctx->callback(ctx, LEJPCB_CONSTRUCTED);
ctx->callback(ctx, LEJPCB_START);
}
static void
lejp_check_path_match(struct lejp_ctx *ctx)
{
const char *p, *q;
int n;
/* we only need to check if a match is not active */
for (n = 0; !ctx->path_match && n < ctx->count_paths; n++) {
ctx->wildcount = 0;
p = ctx->path;
q = ctx->paths[n];
while (*p && *q) {
if (*q != '*') {
if (*p != *q)
break;
p++;
q++;
continue;
}
ctx->wild[ctx->wildcount++] = p - ctx->path;
q++;
/*
* if * has something after it, match to .
* if ends with *, eat everything.
* This implies match sequences must be ordered like
* x.*.*
* x.*
* if both options are possible
*/
while (*p && (*p != '.' || !*q))
p++;
}
if (*p || *q)
continue;
ctx->path_match = n + 1;
ctx->path_match_len = ctx->ppos;
return;
}
if (!ctx->path_match)
ctx->wildcount = 0;
}
int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
{
int n;
if (wildcard >= ctx->wildcount || !len)
return 0;
n = ctx->wild[wildcard];
while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++];
*dest = '\0';
n++;
return n - ctx->wild[wildcard];
}
/**
* lejp_parse - interpret some more incoming data incrementally
*
* \param ctx: previously constructed parsing context
* \param json: char buffer with the new data to interpret
* \param len: amount of data in the buffer
*
* Because lejp is a stream parser, it incrementally parses as new data
* becomes available, maintaining all state in the context struct. So an
* incomplete JSON is a normal situation, getting you a LEJP_CONTINUE
* return, signalling there's no error but to call again with more data when
* it comes to complete the parsing. Successful parsing completes with a
* 0 or positive integer indicating how much of the last input buffer was
* unused.
*/
int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
{
unsigned char c, n, s, ret = LEJP_REJECT_UNKNOWN;
static const char esc_char[] = "\"\\/bfnrt";
static const char esc_tran[] = "\"\\/\b\f\n\r\t";
static const char tokens[] = "rue alse ull ";
if (!ctx->sp && !ctx->ppos)
ctx->callback(ctx, LEJPCB_START);
while (len--) {
c = *json++;
s = ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') {
if (c == '\n') {
ctx->line++;
ctx->st[ctx->sp].s &= ~LEJP_FLAG_WS_COMMENTLINE;
}
if (!(s & LEJP_FLAG_WS_KEEP)) {
if (c == '#')
ctx->st[ctx->sp].s |=
LEJP_FLAG_WS_COMMENTLINE;
continue;
}
}
if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE)
continue;
switch (s) {
case LEJP_IDLE:
if (c != '{') {
ret = LEJP_REJECT_IDLE_NO_BRACE;
goto reject;
}
if (ctx->callback(ctx, LEJPCB_OBJECT_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MEMBERS;
break;
case LEJP_MEMBERS:
if (c == '}') {
ctx->st[ctx->sp].s = LEJP_IDLE;
ret = LEJP_REJECT_MEMBERS_NO_CLOSE;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_M_P;
goto redo_character;
case LEJP_M_P:
if (c != '\"') {
ret = LEJP_REJECT_MP_NO_OPEN_QUOTE;
goto reject;
}
/* push */
ctx->st[ctx->sp].s = LEJP_MP_DELIM;
c = LEJP_MP_STRING;
goto add_stack_level;
case LEJP_MP_STRING:
if (c == '\"') {
if (!ctx->sp) {
ret = LEJP_REJECT_MP_STRING_UNDERRUN;
goto reject;
}
if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
ctx->buf[ctx->npos] = '\0';
if (ctx->callback(ctx,
LEJPCB_VAL_STR_END) < 0) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
}
/* pop */
ctx->sp--;
break;
}
if (c == '\\') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC;
break;
}
if (c < ' ') {/* "control characters" not allowed */
ret = LEJP_REJECT_MP_ILLEGAL_CTRL;
goto reject;
}
goto emit_string_char;
case LEJP_MP_STRING_ESC:
if (c == 'u') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1;
ctx->uni = 0;
break;
}
for (n = 0; n < sizeof(esc_char); n++) {
if (c != esc_char[n])
continue;
/* found it */
c = esc_tran[n];
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
}
ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC;
/* illegal escape char */
goto reject;
case LEJP_MP_STRING_ESC_U1:
case LEJP_MP_STRING_ESC_U2:
case LEJP_MP_STRING_ESC_U3:
case LEJP_MP_STRING_ESC_U4:
ctx->uni <<= 4;
if (c >= '0' && c <= '9')
ctx->uni |= c - '0';
else
if (c >= 'a' && c <= 'f')
ctx->uni = c - 'a' + 10;
else
if (c >= 'A' && c <= 'F')
ctx->uni = c - 'A' + 10;
else {
ret = LEJP_REJECT_ILLEGAL_HEX;
goto reject;
}
ctx->st[ctx->sp].s++;
switch (s) {
case LEJP_MP_STRING_ESC_U2:
if (ctx->uni < 0x08)
break;
/*
* 0x08-0xff (0x0800 - 0xffff)
* emit 3-byte UTF-8
*/
c = 0xe0 | ((ctx->uni >> 4) & 0xf);
goto emit_string_char;
case LEJP_MP_STRING_ESC_U3:
if (ctx->uni >= 0x080) {
/*
* 0x080 - 0xfff (0x0800 - 0xffff)
* middle 3-byte seq
* send ....XXXXXX..
*/
c = 0x80 | ((ctx->uni >> 2) & 0x3f);
goto emit_string_char;
}
if (ctx->uni < 0x008)
break;
/*
* 0x008 - 0x7f (0x0080 - 0x07ff)
* start 2-byte seq
*/
c = 0xc0 | (ctx->uni >> 2);
goto emit_string_char;
case LEJP_MP_STRING_ESC_U4:
if (ctx->uni >= 0x0080)
/* end of 2 or 3-byte seq */
c = 0x80 | (ctx->uni & 0x3f);
else
/* literal */
c = (unsigned char)ctx->uni;
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
default:
break;
}
break;
case LEJP_MP_DELIM:
if (c != ':') {
ret = LEJP_REJECT_MP_DELIM_MISSING_COLON;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
ctx->path[ctx->ppos] = '\0';
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case LEJP_MP_VALUE:
if (c >= '0' && c <= '9') {
ctx->npos = 0;
ctx->dcount = 0;
ctx->f = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
goto redo_character;
}
switch (c) {
case'\"':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MP_STRING;
ctx->npos = 0;
ctx->buf[0] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
goto add_stack_level;
case '{':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MEMBERS;
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->path_match = 0;
goto add_stack_level;
case '[':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END;
c = LEJP_MP_VALUE;
ctx->path[ctx->ppos++] = '[';
ctx->path[ctx->ppos++] = ']';
ctx->path[ctx->ppos] = '\0';
if (ctx->callback(ctx, LEJPCB_ARRAY_START)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->i[ctx->ipos++] = 0;
if (ctx->ipos > ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
goto add_stack_level;
case 't': /* true */
ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'f':
ctx->uni = 4;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'n':
ctx->uni = 4 + 5;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
default:
ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START;
goto reject;
}
break;
case LEJP_MP_VALUE_NUM_INT:
if (!ctx->npos && c == '-') {
ctx->f |= LEJP_SEEN_MINUS;
goto append_npos;
}
if (ctx->dcount < 10 && c >= '0' && c <= '9') {
if (ctx->f & LEJP_SEEN_POINT)
ctx->f |= LEJP_SEEN_POST_POINT;
ctx->dcount++;
goto append_npos;
}
if (c == '.') {
if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_POINT;
goto append_npos;
}
/*
* before exponent, if we had . we must have had at
* least one more digit
*/
if ((ctx->f &
(LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) ==
LEJP_SEEN_POINT) {
ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC;
goto reject;
}
if (c == 'e' || c == 'E') {
if (ctx->f & LEJP_SEEN_EXP) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_EXP;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP;
goto append_npos;
}
/* if none of the above, did we even have a number? */
if (!ctx->dcount) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->buf[ctx->npos] = '\0';
if (ctx->f & LEJP_SEEN_POINT) {
if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
} else {
if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
}
/* then this is the post-number character, loop */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
goto redo_character;
case LEJP_MP_VALUE_NUM_EXP:
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
if (c >= '0' && c <= '9')
goto redo_character;
if (c == '+' || c == '-')
goto append_npos;
ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP;
goto reject;
case LEJP_MP_VALUE_TOK: /* true, false, null */
if (c != tokens[ctx->uni]) {
ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN;
goto reject;
}
ctx->uni++;
if (tokens[ctx->uni] != ' ')
break;
switch (ctx->uni) {
case 3:
ctx->buf[0] = '1';
ctx->buf[1] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case 8:
ctx->buf[0] = '0';
ctx->buf[1] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
case 12:
ctx->buf[0] = '\0';
if (ctx->callback(ctx, LEJPCB_VAL_NULL)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
}
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
break;
case LEJP_MP_COMMA_OR_END:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
ctx->st[ctx->sp].s = LEJP_M_P;
if (!ctx->sp) {
ctx->ppos = 0;
/*
* since we came back to root level,
* no path can still match
*/
ctx->path_match = 0;
break;
}
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END)
break;
/* top level is definitely an array... */
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
break;
}
if (c == ']') {
if (!ctx->sp) {
ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
goto reject;
}
/* pop */
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
/* do LEJP_MP_ARRAY_END processing */
goto redo_character;
}
if (c == '}') {
if (ctx->sp == 0) {
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_END)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->callback(ctx, LEJPCB_COMPLETE);
/* done, return unused amount */
return len;
}
/* pop */
ctx->sp--;
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
lejp_check_path_match(ctx);
if (ctx->callback(ctx, LEJPCB_OBJECT_END)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
break;
}
ret = LEJP_REJECT_MP_C_OR_E_NEITHER;
goto reject;
case LEJP_MP_ARRAY_END:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
if (ctx->sp)
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->path[ctx->ppos] = '\0';
break;
}
if (c != ']') {
ret = LEJP_REJECT_MP_ARRAY_END_MISSING;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
ctx->callback(ctx, LEJPCB_ARRAY_END);
break;
}
continue;
emit_string_char:
if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
/* assemble the string value into chunks */
ctx->buf[ctx->npos++] = c;
if (ctx->npos == sizeof(ctx->buf) - 1) {
if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) {
ret = LEJP_REJECT_CALLBACK;
goto reject;
}
ctx->npos = 0;
}
continue;
}
/* name part of name:value pair */
ctx->path[ctx->ppos++] = c;
continue;
add_stack_level:
/* push on to the object stack */
if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END &&
ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END)
ctx->path[ctx->ppos++] = '.';
ctx->st[ctx->sp].p = ctx->ppos;
ctx->st[ctx->sp].i = ctx->ipos;
if (++ctx->sp == ARRAY_SIZE(ctx->st)) {
ret = LEJP_REJECT_STACK_OVERFLOW;
goto reject;
}
ctx->path[ctx->ppos] = '\0';
ctx->st[ctx->sp].s = c;
ctx->st[ctx->sp].b = 0;
continue;
append_npos:
if (ctx->npos >= sizeof(ctx->buf)) {
ret = LEJP_REJECT_NUM_TOO_LONG;
goto reject;
}
ctx->buf[ctx->npos++] = c;
continue;
redo_character:
json--;
len++;
}
return LEJP_CONTINUE;
reject:
ctx->callback(ctx, LEJPCB_FAILED);
return ret;
}

View file

@ -1,232 +0,0 @@
#include "libwebsockets.h"
struct lejp_ctx;
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof(_x[0]))
#endif
#define LEJP_FLAG_WS_KEEP 64
#define LEJP_FLAG_WS_COMMENTLINE 32
enum lejp_states {
LEJP_IDLE = 0,
LEJP_MEMBERS = 1,
LEJP_M_P = 2,
LEJP_MP_STRING = LEJP_FLAG_WS_KEEP | 3,
LEJP_MP_STRING_ESC = LEJP_FLAG_WS_KEEP | 4,
LEJP_MP_STRING_ESC_U1 = LEJP_FLAG_WS_KEEP | 5,
LEJP_MP_STRING_ESC_U2 = LEJP_FLAG_WS_KEEP | 6,
LEJP_MP_STRING_ESC_U3 = LEJP_FLAG_WS_KEEP | 7,
LEJP_MP_STRING_ESC_U4 = LEJP_FLAG_WS_KEEP | 8,
LEJP_MP_DELIM = 9,
LEJP_MP_VALUE = 10,
LEJP_MP_VALUE_NUM_INT = LEJP_FLAG_WS_KEEP | 11,
LEJP_MP_VALUE_NUM_EXP = LEJP_FLAG_WS_KEEP | 12,
LEJP_MP_VALUE_TOK = LEJP_FLAG_WS_KEEP | 13,
LEJP_MP_COMMA_OR_END = 14,
LEJP_MP_ARRAY_END = 15,
};
enum lejp_reasons {
LEJP_CONTINUE = -1,
LEJP_REJECT_IDLE_NO_BRACE = -2,
LEJP_REJECT_MEMBERS_NO_CLOSE = -3,
LEJP_REJECT_MP_NO_OPEN_QUOTE = -4,
LEJP_REJECT_MP_STRING_UNDERRUN = -5,
LEJP_REJECT_MP_ILLEGAL_CTRL = -6,
LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC = -7,
LEJP_REJECT_ILLEGAL_HEX = -8,
LEJP_REJECT_MP_DELIM_MISSING_COLON = -9,
LEJP_REJECT_MP_DELIM_BAD_VALUE_START = -10,
LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC = -11,
LEJP_REJECT_MP_VAL_NUM_FORMAT = -12,
LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP = -13,
LEJP_REJECT_MP_VAL_TOK_UNKNOWN = -14,
LEJP_REJECT_MP_C_OR_E_UNDERF = -15,
LEJP_REJECT_MP_C_OR_E_NOTARRAY = -16,
LEJP_REJECT_MP_ARRAY_END_MISSING = -17,
LEJP_REJECT_STACK_OVERFLOW = -18,
LEJP_REJECT_MP_DELIM_ISTACK = -19,
LEJP_REJECT_NUM_TOO_LONG = -20,
LEJP_REJECT_MP_C_OR_E_NEITHER = -21,
LEJP_REJECT_UNKNOWN = -22,
LEJP_REJECT_CALLBACK = -23
};
#define LEJP_FLAG_CB_IS_VALUE 64
enum lejp_callbacks {
LEJPCB_CONSTRUCTED = 0,
LEJPCB_DESTRUCTED = 1,
LEJPCB_START = 2,
LEJPCB_COMPLETE = 3,
LEJPCB_FAILED = 4,
LEJPCB_PAIR_NAME = 5,
LEJPCB_VAL_TRUE = LEJP_FLAG_CB_IS_VALUE | 6,
LEJPCB_VAL_FALSE = LEJP_FLAG_CB_IS_VALUE | 7,
LEJPCB_VAL_NULL = LEJP_FLAG_CB_IS_VALUE | 8,
LEJPCB_VAL_NUM_INT = LEJP_FLAG_CB_IS_VALUE | 9,
LEJPCB_VAL_NUM_FLOAT = LEJP_FLAG_CB_IS_VALUE | 10,
LEJPCB_VAL_STR_START = 11, /* notice handle separately */
LEJPCB_VAL_STR_CHUNK = LEJP_FLAG_CB_IS_VALUE | 12,
LEJPCB_VAL_STR_END = LEJP_FLAG_CB_IS_VALUE | 13,
LEJPCB_ARRAY_START = 14,
LEJPCB_ARRAY_END = 15,
LEJPCB_OBJECT_START = 16,
LEJPCB_OBJECT_END = 17
};
/**
* _lejp_callback() - User parser actions
* \param ctx: LEJP context
* \param reason: Callback reason
*
* Your user callback is associated with the context at construction time,
* and receives calls as the parsing progresses.
*
* All of the callbacks may be ignored and just return 0.
*
* The reasons it might get called, found in @reason, are:
*
* LEJPCB_CONSTRUCTED: The context was just constructed... you might want to
* perform one-time allocation for the life of the context.
*
* LEJPCB_DESTRUCTED: The context is being destructed... if you made any
* allocations at construction-time, you can free them now
*
* LEJPCB_START: Parsing is beginning at the first byte of input
*
* LEJPCB_COMPLETE: Parsing has completed successfully. You'll get a 0 or
* positive return code from lejp_parse indicating the
* amount of unused bytes left in the input buffer
*
* LEJPCB_FAILED: Parsing failed. You'll get a negative error code
* returned from lejp_parse
*
* LEJPCB_PAIR_NAME: When a "name":"value" pair has had the name parsed,
* this callback occurs. You can find the new name at
* the end of ctx->path[]
*
* LEJPCB_VAL_TRUE: The "true" value appeared
*
* LEJPCB_VAL_FALSE: The "false" value appeared
*
* LEJPCB_VAL_NULL: The "null" value appeared
*
* LEJPCB_VAL_NUM_INT: A string representing an integer is in ctx->buf
*
* LEJPCB_VAL_NUM_FLOAT: A string representing a float is in ctx->buf
*
* LEJPCB_VAL_STR_START: We are starting to parse a string, no data yet
*
* LEJPCB_VAL_STR_CHUNK: We parsed LEJP_STRING_CHUNK -1 bytes of string data in
* ctx->buf, which is as much as we can buffer, so we are
* spilling it. If all your strings are less than
* LEJP_STRING_CHUNK - 1 bytes, you will never see this
* callback.
*
* LEJPCB_VAL_STR_END: String parsing has completed, the last chunk of the
* string is in ctx->buf.
*
* LEJPCB_ARRAY_START: An array started
*
* LEJPCB_ARRAY_END: An array ended
*
* LEJPCB_OBJECT_START: An object started
*
* LEJPCB_OBJECT_END: An object ended
*/
LWS_EXTERN char _lejp_callback(struct lejp_ctx *ctx, char reason);
typedef char (*lejp_callback)(struct lejp_ctx *ctx, char reason);
#ifndef LEJP_MAX_DEPTH
#define LEJP_MAX_DEPTH 12
#endif
#ifndef LEJP_MAX_INDEX_DEPTH
#define LEJP_MAX_INDEX_DEPTH 5
#endif
#ifndef LEJP_MAX_PATH
#define LEJP_MAX_PATH 128
#endif
#ifndef LEJP_STRING_CHUNK
/* must be >= 30 to assemble floats */
#define LEJP_STRING_CHUNK 255
#endif
enum num_flags {
LEJP_SEEN_MINUS = (1 << 0),
LEJP_SEEN_POINT = (1 << 1),
LEJP_SEEN_POST_POINT = (1 << 2),
LEJP_SEEN_EXP = (1 << 3)
};
struct _lejp_stack {
char s; /* lejp_state stack*/
char p; /* path length */
char i; /* index array length */
char b; /* user bitfield */
};
struct lejp_ctx {
/* sorted by type for most compact alignment
*
* pointers
*/
char (*callback)(struct lejp_ctx *ctx, char reason);
void *user;
const char * const *paths;
/* arrays */
struct _lejp_stack st[LEJP_MAX_DEPTH];
unsigned short i[LEJP_MAX_INDEX_DEPTH]; /* index array */
unsigned short wild[LEJP_MAX_INDEX_DEPTH]; /* index array */
char path[LEJP_MAX_PATH];
char buf[LEJP_STRING_CHUNK];
/* int */
unsigned int line;
/* short */
unsigned short uni;
/* char */
unsigned char npos;
unsigned char dcount;
unsigned char f;
unsigned char sp; /* stack head */
unsigned char ipos; /* index stack depth */
unsigned char ppos;
unsigned char count_paths;
unsigned char path_match;
unsigned char path_match_len;
unsigned char wildcount;
};
LWS_VISIBLE LWS_EXTERN void
lejp_construct(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char paths_count);
LWS_VISIBLE LWS_EXTERN void
lejp_destruct(struct lejp_ctx *ctx);
LWS_VISIBLE LWS_EXTERN int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len);
LWS_VISIBLE LWS_EXTERN void
lejp_change_callback(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason));
LWS_VISIBLE LWS_EXTERN int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len);

View file

@ -1,10 +1,6 @@
/* set of parsable strings -- ALL LOWER CASE */
#if !defined(STORE_IN_ROM)
#define STORE_IN_ROM
#endif
STORE_IN_ROM static const char * const set[] = {
static const char *set[] = {
"get ",
"post ",
"options ",
@ -43,13 +39,13 @@ STORE_IN_ROM static const char * const set[] = {
"sec-websocket-key:",
"sec-websocket-version:",
"sec-websocket-origin:",
":authority",
":method",
":path",
":scheme",
":status",
":authority:",
":method:",
":path:",
":scheme:",
":status:",
"accept-charset:",
"accept-ranges:",
"access-control-allow-origin:",
@ -83,20 +79,12 @@ STORE_IN_ROM static const char * const set[] = {
"vary:",
"via:",
"www-authenticate:",
"proxy ",
"patch",
"put",
"delete",
"uri-args", /* fake header used for uri-only storage */
"proxy ",
"x-real-ip:",
"http/1.0 ",
"x-forwarded-for",
"connect ",
"", /* not matchable */
};

File diff suppressed because it is too large Load diff

View file

@ -23,92 +23,84 @@
void lws_feature_status_libev(struct lws_context_creation_info *info)
{
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEV))
if (info->options & LWS_SERVER_OPTION_LIBEV)
lwsl_notice("libev support compiled in and enabled\n");
else
lwsl_notice("libev support compiled in but disabled\n");
}
static void
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
static void
libwebsocket_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct lws_io_watcher *lws_io = lws_container_of(watcher,
struct lws_io_watcher, ev_watcher);
struct lws_context *context = lws_io->context;
struct lws_pollfd eventfd;
struct libwebsocket_pollfd eventfd;
struct lws_io_watcher *lws_io = container_of(watcher, struct lws_io_watcher, watcher);
struct libwebsocket_context *context = lws_io->context;
if (revents & EV_ERROR)
return;
eventfd.fd = watcher->fd;
eventfd.events = 0;
eventfd.revents = EV_NONE;
if (revents & EV_READ) {
eventfd.events |= LWS_POLLIN;
if (revents & EV_READ)
eventfd.revents |= LWS_POLLIN;
}
if (revents & EV_WRITE) {
eventfd.events |= LWS_POLLOUT;
if (revents & EV_WRITE)
eventfd.revents |= LWS_POLLOUT;
}
lws_service_fd(context, &eventfd);
libwebsocket_service_fd(context, &eventfd);
}
LWS_VISIBLE void
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
libwebsocket_sigint_cb(struct ev_loop *loop,
struct ev_signal *watcher, int revents)
{
ev_break(loop, EVBREAK_ALL);
}
LWS_VISIBLE int
lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint,
lws_ev_signal_cb_t *cb)
LWS_VISIBLE int libwebsocket_sigint_cfg(
struct libwebsocket_context *context,
int use_ev_sigint,
lws_ev_signal_cb* cb)
{
context->use_ev_sigint = use_ev_sigint;
if (cb)
if( cb ) {
context->lws_ev_sigint_cb = cb;
else
context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
}
else {
context->lws_ev_sigint_cb = &libwebsocket_sigint_cb;
};
return 0;
}
};
LWS_VISIBLE int
lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
libwebsocket_initloop(
struct libwebsocket_context *context,
struct ev_loop *loop)
{
struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher;
struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher;
struct lws_vhost *vh = context->vhost_list;
const char * backend_name;
int status = 0;
int backend;
const char * backend_name;
struct ev_io *w_accept = &context->w_accept.watcher;
struct ev_signal *w_sigint = &context->w_sigint.watcher;
if (!loop)
loop = ev_loop_new(0);
else
context->pt[tsi].ev_loop_foreign = 1;
context->pt[tsi].io_loop_ev = loop;
loop = ev_default_loop(0);
context->io_loop = loop;
/*
* Initialize the accept w_accept with all the listening sockets
* and register a callback for read operations
* Initialize the accept w_accept with the listening socket
* and register a callback for read operations:
*/
while (vh) {
if (vh->lserv_wsi) {
vh->lserv_wsi->w_read.context = context;
ev_io_init(w_accept, lws_accept_cb, vh->lserv_wsi->desc.sockfd,
EV_READ);
}
vh = vh->vhost_next;
}
ev_io_start(context->pt[tsi].io_loop_ev, w_accept);
ev_io_init(w_accept, libwebsocket_accept_cb,
context->listen_service_fd, EV_READ);
ev_io_start(context->io_loop,w_accept);
/* Register the signal watcher unless the user says not to */
if (context->use_ev_sigint) {
/* Register the signal watcher unless the user has indicated otherwise: */
if( context->use_ev_sigint ) {
ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT);
ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint);
}
ev_signal_start(context->io_loop,w_sigint);
};
backend = ev_backend(loop);
switch (backend) {
@ -133,101 +125,70 @@ lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
default:
backend_name = "Unknown libev backend";
break;
}
};
lwsl_notice(" libev backend: %s\n", backend_name);
return status;
}
void
lws_libev_destroyloop(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV))
return;
if (!pt->io_loop_ev)
return;
ev_io_stop(pt->io_loop_ev, &pt->w_accept.ev_watcher);
if (context->use_ev_sigint)
ev_signal_stop(pt->io_loop_ev,
&pt->w_sigint.ev_watcher);
if (!pt->ev_loop_foreign)
ev_loop_destroy(pt->io_loop_ev);
}
LWS_VISIBLE void
lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
lws_libev_accept(struct libwebsocket_context *context,
struct libwebsocket *new_wsi, int accept_fd)
{
struct lws_context *context = lws_get_context(new_wsi);
struct ev_io *r = &new_wsi->w_read.ev_watcher;
struct ev_io *w = &new_wsi->w_write.ev_watcher;
int fd;
struct ev_io *r = &new_wsi->w_read.watcher;
struct ev_io *w = &new_wsi->w_write.watcher;
if (!LWS_LIBEV_ENABLED(context))
return;
if (new_wsi->mode == LWSCM_RAW_FILEDESC)
fd = desc.filefd;
else
fd = desc.sockfd;
new_wsi->w_read.context = context;
new_wsi->w_write.context = context;
ev_io_init(r, lws_accept_cb, fd, EV_READ);
ev_io_init(w, lws_accept_cb, fd, EV_WRITE);
new_wsi->w_read.context = context;
new_wsi->w_write.context = context;
ev_io_init(r, libwebsocket_accept_cb, accept_fd, EV_READ);
ev_io_init(w, libwebsocket_accept_cb, accept_fd, EV_WRITE);
}
LWS_VISIBLE void
lws_libev_io(struct lws *wsi, int flags)
lws_libev_io(struct libwebsocket_context *context,
struct libwebsocket *wsi, int flags)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!LWS_LIBEV_ENABLED(context))
return;
if (!pt->io_loop_ev)
if (!context->io_loop)
return;
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
if (flags & LWS_EV_START) {
if (flags & LWS_EV_WRITE)
ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher);
ev_io_start(context->io_loop, &wsi->w_write.watcher);
if (flags & LWS_EV_READ)
ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher);
ev_io_start(context->io_loop, &wsi->w_read.watcher);
} else {
if (flags & LWS_EV_WRITE)
ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher);
ev_io_stop(context->io_loop, &wsi->w_write.watcher);
if (flags & LWS_EV_READ)
ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher);
ev_io_stop(context->io_loop, &wsi->w_read.watcher);
}
}
LWS_VISIBLE int
lws_libev_init_fd_table(struct lws_context *context)
lws_libev_init_fd_table(struct libwebsocket_context *context)
{
int n;
if (!LWS_LIBEV_ENABLED(context))
return 0;
for (n = 0; n < context->count_threads; n++) {
context->pt[n].w_accept.context = context;
context->pt[n].w_sigint.context = context;
}
context->w_accept.context = context;
context->w_sigint.context = context;
return 1;
}
LWS_VISIBLE void
lws_libev_run(const struct lws_context *context, int tsi)
lws_libev_run(struct libwebsocket_context *context)
{
if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context))
ev_run(context->pt[tsi].io_loop_ev, 0);
if (context->io_loop && LWS_LIBEV_ENABLED(context))
ev_run(context->io_loop, 0);
}

View file

@ -1,249 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
void lws_feature_status_libevent(struct lws_context_creation_info *info)
{
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEVENT))
lwsl_notice("libevent support compiled in and enabled\n");
else
lwsl_notice("libevent support compiled in but disabled\n");
}
static void
lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx)
{
struct lws_io_watcher *lws_io = (struct lws_io_watcher *)ctx;
struct lws_context *context = lws_io->context;
struct lws_pollfd eventfd;
if (revents & EV_TIMEOUT)
return;
/* !!! EV_CLOSED doesn't exist in libevent2 */
#if LIBEVENT_VERSION_NUMBER < 0x02000000
if (revents & EV_CLOSED)
{
event_del(lws_io->event_watcher);
event_free(lws_io->event_watcher);
return;
}
#endif
eventfd.fd = sock_fd;
eventfd.events = 0;
eventfd.revents = 0;
if (revents & EV_READ)
{
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & EV_WRITE)
{
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
lws_service_fd(context, &eventfd);
}
LWS_VISIBLE void
lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx)
{
struct lws_context_per_thread *pt = ctx;
if (!pt->ev_loop_foreign)
event_base_loopbreak(pt->io_loop_event_base);
}
LWS_VISIBLE int
lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint,
lws_event_signal_cb_t *cb)
{
context->use_ev_sigint = use_event_sigint;
if (cb)
context->lws_event_sigint_cb = cb;
else
context->lws_event_sigint_cb = &lws_event_sigint_cb;
return 0;
}
LWS_VISIBLE int
lws_event_initloop(struct lws_context *context, struct event_base *loop,
int tsi)
{
if (!loop)
{
context->pt[tsi].io_loop_event_base = event_base_new();
}
else
{
context->pt[tsi].ev_loop_foreign = 1;
context->pt[tsi].io_loop_event_base = loop;
}
/*
* Initialize all events with the listening sockets
* and register a callback for read operations
*/
struct lws_vhost *vh = context->vhost_list;
while (vh)
{
if (vh->lserv_wsi)
{
vh->lserv_wsi->w_read.context = context;
vh->lserv_wsi->w_read.event_watcher = event_new(
loop,
vh->lserv_wsi->desc.sockfd,
(EV_READ | EV_PERSIST),
lws_event_cb,
&vh->lserv_wsi->w_read);
event_add(vh->lserv_wsi->w_read.event_watcher, NULL);
}
vh = vh->vhost_next;
}
/* Register the signal watcher unless the user says not to */
if (context->use_ev_sigint)
{
struct event *w_sigint = evsignal_new(loop, SIGINT,
context->lws_event_sigint_cb, &context->pt[tsi]);
context->pt[tsi].w_sigint.event_watcher = w_sigint;
event_add(w_sigint, NULL);
}
return 0;
}
void
lws_libevent_destroyloop(struct lws_context *context, int tsi)
{
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEVENT))
return;
struct lws_context_per_thread *pt = &context->pt[tsi];
if (!pt->io_loop_event_base)
return;
/*
* Free all events with the listening sockets
*/
struct lws_vhost *vh = context->vhost_list;
while (vh)
{
if (vh->lserv_wsi)
{
event_free(vh->lserv_wsi->w_read.event_watcher);
vh->lserv_wsi->w_read.event_watcher = NULL;
}
vh = vh->vhost_next;
}
if (context->use_ev_sigint)
event_free(pt->w_sigint.event_watcher);
if (!pt->ev_loop_foreign)
event_base_free(pt->io_loop_event_base);
}
LWS_VISIBLE void
lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
{
struct lws_context *context = lws_get_context(new_wsi);
if (!LWS_LIBEVENT_ENABLED(context))
return;
new_wsi->w_read.context = context;
new_wsi->w_write.context = context;
// Initialize the event
struct lws_context_per_thread *pt = &context->pt[(int)new_wsi->tsi];
int fd;
if (new_wsi->mode == LWSCM_RAW_FILEDESC)
fd = desc.filefd;
else
fd = desc.sockfd;
new_wsi->w_read.event_watcher = event_new(pt->io_loop_event_base, fd,
(EV_READ | EV_PERSIST), lws_event_cb, &new_wsi->w_read);
new_wsi->w_write.event_watcher = event_new(pt->io_loop_event_base, fd,
(EV_WRITE | EV_PERSIST), lws_event_cb, &new_wsi->w_write);
}
LWS_VISIBLE void
lws_libevent_io(struct lws *wsi, int flags)
{
struct lws_context *context = lws_get_context(wsi);
if (!LWS_LIBEVENT_ENABLED(context))
return;
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!pt->io_loop_event_base || context->being_destroyed)
return;
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
if (flags & LWS_EV_START)
{
if (flags & LWS_EV_WRITE)
{
event_add(wsi->w_write.event_watcher, NULL);
}
if (flags & LWS_EV_READ)
{
event_add(wsi->w_read.event_watcher, NULL);
}
}
else
{
if (flags & LWS_EV_WRITE)
{
event_del(wsi->w_write.event_watcher);
}
if (flags & LWS_EV_READ)
{
event_del(wsi->w_read.event_watcher);
}
}
}
LWS_VISIBLE int
lws_libevent_init_fd_table(struct lws_context *context)
{
if (!LWS_LIBEVENT_ENABLED(context))
return 0;
int n;
for (n = 0; n < context->count_threads; n++)
{
context->pt[n].w_sigint.context = context;
}
return 1;
}
LWS_VISIBLE void
lws_libevent_run(const struct lws_context *context, int tsi)
{
// Run/Dispatch the event_base loop
if (context->pt[tsi].io_loop_event_base && LWS_LIBEVENT_ENABLED(context))
event_base_dispatch(context->pt[tsi].io_loop_event_base);
}

View file

@ -1,723 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
void
lws_feature_status_libuv(struct lws_context_creation_info *info)
{
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV))
lwsl_notice("libuv support compiled in and enabled\n");
else
lwsl_notice("libuv support compiled in but disabled\n");
}
static void
lws_uv_idle(uv_idle_t *handle
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct lws_context_per_thread *pt = lws_container_of(handle,
struct lws_context_per_thread, uv_idle);
// lwsl_debug("%s\n", __func__);
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(pt->context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
/* yes... come back again later */
// lwsl_debug("%s: done again\n", __func__);
return;
}
/* there is nobody who needs service forcing, shut down idle */
uv_idle_stop(handle);
//lwsl_debug("%s: done stop\n", __func__);
}
static void
lws_io_cb(uv_poll_t *watcher, int status, int revents)
{
struct lws_io_watcher *lws_io = lws_container_of(watcher,
struct lws_io_watcher, uv_watcher);
struct lws *wsi = lws_container_of(lws_io, struct lws, w_read);
struct lws_context *context = wsi->context;
struct lws_pollfd eventfd;
#if defined(WIN32) || defined(_WIN32)
eventfd.fd = watcher->socket;
#else
eventfd.fd = watcher->io_watcher.fd;
#endif
eventfd.events = 0;
eventfd.revents = 0;
if (status < 0) {
/* at this point status will be an UV error, like UV_EBADF,
we treat all errors as LWS_POLLHUP */
/* you might want to return; instead of servicing the fd in some cases */
if (status == UV_EAGAIN)
return;
eventfd.events |= LWS_POLLHUP;
eventfd.revents |= LWS_POLLHUP;
} else {
if (revents & UV_READABLE) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & UV_WRITABLE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
}
lws_service_fd(context, &eventfd);
uv_idle_start(&context->pt[(int)wsi->tsi].uv_idle, lws_uv_idle);
}
LWS_VISIBLE void
lws_uv_sigint_cb(uv_signal_t *watcher, int signum)
{
lwsl_err("internal signal handler caught signal %d\n", signum);
lws_libuv_stop(watcher->data);
}
LWS_VISIBLE int
lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
uv_signal_cb cb)
{
context->use_ev_sigint = use_uv_sigint;
if (cb)
context->lws_uv_sigint_cb = cb;
else
context->lws_uv_sigint_cb = &lws_uv_sigint_cb;
return 0;
}
static void
lws_uv_timeout_cb(uv_timer_t *timer
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct lws_context_per_thread *pt = lws_container_of(timer,
struct lws_context_per_thread, uv_timeout_watcher);
if (pt->context->requested_kill)
return;
lwsl_debug("%s\n", __func__);
lws_service_fd_tsi(pt->context, NULL, pt->tid);
}
static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP };
int
lws_uv_initvhost(struct lws_vhost* vh, struct lws* wsi)
{
struct lws_context_per_thread *pt;
int n;
if (!LWS_LIBUV_ENABLED(vh->context))
return 0;
if (!wsi)
wsi = vh->lserv_wsi;
if (!wsi)
return 0;
if (wsi->w_read.context)
return 0;
pt = &vh->context->pt[(int)wsi->tsi];
if (!pt->io_loop_uv)
return 0;
wsi->w_read.context = vh->context;
n = uv_poll_init_socket(pt->io_loop_uv,
&wsi->w_read.uv_watcher, wsi->desc.sockfd);
if (n) {
lwsl_err("uv_poll_init failed %d, sockfd=%p\n",
n, (void *)(lws_intptr_t)wsi->desc.sockfd);
return -1;
}
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
/*
* This needs to be called after vhosts have been defined.
*
* If later, after server start, another vhost is added, this must be
* called again to bind the vhost
*/
LWS_VISIBLE int
lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_vhost *vh = context->vhost_list;
int status = 0, n, ns, first = 1;
if (!pt->io_loop_uv) {
if (!loop) {
loop = lws_malloc(sizeof(*loop));
if (!loop) {
lwsl_err("OOM\n");
return -1;
}
#if UV_VERSION_MAJOR > 0
uv_loop_init(loop);
#else
lwsl_err("This libuv is too old to work...\n");
return 1;
#endif
pt->ev_loop_foreign = 0;
} else {
lwsl_notice(" Using foreign event loop...\n");
pt->ev_loop_foreign = 1;
}
pt->io_loop_uv = loop;
uv_idle_init(loop, &pt->uv_idle);
ns = ARRAY_SIZE(sigs);
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
ns = 2;
if (pt->context->use_ev_sigint) {
assert(ns <= ARRAY_SIZE(pt->signals));
for (n = 0; n < ns; n++) {
uv_signal_init(loop, &pt->signals[n]);
pt->signals[n].data = pt->context;
uv_signal_start(&pt->signals[n],
context->lws_uv_sigint_cb, sigs[n]);
}
}
} else
first = 0;
/*
* Initialize the accept wsi read watcher with all the listening sockets
* and register a callback for read operations
*
* We have to do it here because the uv loop(s) are not
* initialized until after context creation.
*/
while (vh) {
if (lws_uv_initvhost(vh, vh->lserv_wsi) == -1)
return -1;
vh = vh->vhost_next;
}
if (first) {
uv_timer_init(pt->io_loop_uv, &pt->uv_timeout_watcher);
uv_timer_start(&pt->uv_timeout_watcher, lws_uv_timeout_cb,
10, 1000);
}
return status;
}
static void lws_uv_close_cb(uv_handle_t *handle)
{
//lwsl_err("%s: handle %p\n", __func__, handle);
}
static void lws_uv_walk_cb(uv_handle_t *handle, void *arg)
{
if (!uv_is_closing(handle))
uv_close(handle, lws_uv_close_cb);
}
LWS_VISIBLE void
lws_close_all_handles_in_loop(uv_loop_t *loop)
{
uv_walk(loop, lws_uv_walk_cb, NULL);
}
void
lws_libuv_destroyloop(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
// struct lws_context *ctx;
int m, budget = 100, ns;
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
return;
if (!pt->io_loop_uv)
return;
lwsl_notice("%s: closing signals + timers context %p\n", __func__, context);
if (context->use_ev_sigint) {
uv_signal_stop(&pt->w_sigint.uv_watcher);
ns = ARRAY_SIZE(sigs);
if (lws_check_opt(context->options, LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
ns = 2;
for (m = 0; m < ns; m++) {
uv_signal_stop(&pt->signals[m]);
uv_close((uv_handle_t *)&pt->signals[m], lws_uv_close_cb);
}
}
uv_timer_stop(&pt->uv_timeout_watcher);
uv_close((uv_handle_t *)&pt->uv_timeout_watcher, lws_uv_close_cb);
uv_idle_stop(&pt->uv_idle);
uv_close((uv_handle_t *)&pt->uv_idle, lws_uv_close_cb);
if (pt->ev_loop_foreign)
return;
while (budget-- && uv_run(pt->io_loop_uv, UV_RUN_NOWAIT))
;
lwsl_notice("%s: closing all loop handles context %p\n", __func__, context);
uv_stop(pt->io_loop_uv);
uv_walk(pt->io_loop_uv, lws_uv_walk_cb, NULL);
while (uv_run(pt->io_loop_uv, UV_RUN_NOWAIT))
;
#if UV_VERSION_MAJOR > 0
m = uv_loop_close(pt->io_loop_uv);
if (m == UV_EBUSY)
lwsl_err("%s: uv_loop_close: UV_EBUSY\n", __func__);
#endif
lws_free(pt->io_loop_uv);
}
void
lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (!LWS_LIBUV_ENABLED(context))
return;
lwsl_debug("%s: new wsi %p\n", __func__, wsi);
wsi->w_read.context = context;
if (wsi->mode == LWSCM_RAW_FILEDESC)
uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
(int)desc.filefd);
else
uv_poll_init_socket(pt->io_loop_uv, &wsi->w_read.uv_watcher,
desc.sockfd);
}
void
lws_libuv_io(struct lws *wsi, int flags)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
#if defined(WIN32) || defined(_WIN32)
int current_events = wsi->w_read.uv_watcher.events &
(UV_READABLE | UV_WRITABLE);
#else
int current_events = wsi->w_read.uv_watcher.io_watcher.pevents &
(UV_READABLE | UV_WRITABLE);
#endif
struct lws_io_watcher *w = &wsi->w_read;
if (!LWS_LIBUV_ENABLED(context))
return;
// lwsl_notice("%s: wsi: %p, flags:0x%x\n", __func__, wsi, flags);
// w->context is set after the loop is initialized
if (!pt->io_loop_uv || !w->context) {
lwsl_info("%s: no io loop yet\n", __func__);
return;
}
if (!((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)))) {
lwsl_err("%s: assert: flags %d", __func__, flags);
assert(0);
}
if (flags & LWS_EV_START) {
if (flags & LWS_EV_WRITE)
current_events |= UV_WRITABLE;
if (flags & LWS_EV_READ)
current_events |= UV_READABLE;
uv_poll_start(&w->uv_watcher, current_events, lws_io_cb);
} else {
if (flags & LWS_EV_WRITE)
current_events &= ~UV_WRITABLE;
if (flags & LWS_EV_READ)
current_events &= ~UV_READABLE;
if (!(current_events & (UV_READABLE | UV_WRITABLE)))
uv_poll_stop(&w->uv_watcher);
else
uv_poll_start(&w->uv_watcher, current_events,
lws_io_cb);
}
}
int
lws_libuv_init_fd_table(struct lws_context *context)
{
int n;
if (!LWS_LIBUV_ENABLED(context))
return 0;
for (n = 0; n < context->count_threads; n++)
context->pt[n].w_sigint.context = context;
return 1;
}
LWS_VISIBLE void
lws_libuv_run(const struct lws_context *context, int tsi)
{
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
uv_run(context->pt[tsi].io_loop_uv, 0);
}
LWS_VISIBLE void
lws_libuv_stop_without_kill(const struct lws_context *context, int tsi)
{
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
uv_stop(context->pt[tsi].io_loop_uv);
}
static void
lws_libuv_kill(const struct lws_context *context)
{
int n;
lwsl_notice("%s\n", __func__);
for (n = 0; n < context->count_threads; n++)
if (context->pt[n].io_loop_uv &&
LWS_LIBUV_ENABLED(context) )//&&
//!context->pt[n].ev_loop_foreign)
uv_stop(context->pt[n].io_loop_uv);
}
/*
* This does not actually stop the event loop. The reason is we have to pass
* libuv handle closures through its event loop. So this tries to close all
* wsi, and set a flag; when all the wsi closures are finalized then we
* actually stop the libuv event loops.
*/
LWS_VISIBLE void
lws_libuv_stop(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m;
if (context->requested_kill)
return;
context->requested_kill = 1;
m = context->count_threads;
context->being_destroyed = 1;
while (m--) {
pt = &context->pt[m];
for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) {
struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd);
if (!wsi)
continue;
lws_close_free_wsi(wsi,
LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY
/* no protocol close */);
n--;
}
}
lwsl_info("%s: feels everything closed\n", __func__);
if (context->count_wsi_allocated == 0)
lws_libuv_kill(context);
}
LWS_VISIBLE uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi)
{
if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context))
return context->pt[tsi].io_loop_uv;
return NULL;
}
static void
lws_libuv_closewsi(uv_handle_t* handle)
{
struct lws *n = NULL, *wsi = (struct lws *)(((char *)handle) -
(char *)(&n->w_read.uv_watcher));
struct lws_context *context = lws_get_context(wsi);
int lspd = 0;
if (wsi->mode == LWSCM_SERVER_LISTENER &&
wsi->context->deprecated) {
lspd = 1;
context->deprecation_pending_listen_close_count--;
if (!context->deprecation_pending_listen_close_count)
lspd = 2;
}
lws_close_free_wsi_final(wsi);
if (lspd == 2 && context->deprecation_cb) {
lwsl_notice("calling deprecation callback\n");
context->deprecation_cb();
}
//lwsl_notice("%s: ctx %p: wsi left %d\n", __func__, context, context->count_wsi_allocated);
if (context->requested_kill && context->count_wsi_allocated == 0)
lws_libuv_kill(context);
}
void
lws_libuv_closehandle(struct lws *wsi)
{
struct lws_context *context = lws_get_context(wsi);
/* required to defer actual deletion until libuv has processed it */
uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi);
if (context->requested_kill && context->count_wsi_allocated == 0)
lws_libuv_kill(context);
}
static void
lws_libuv_closewsi_m(uv_handle_t* handle)
{
lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data;
compatible_close(sockfd);
}
void
lws_libuv_closehandle_manually(struct lws *wsi)
{
uv_handle_t *h = (void *)&wsi->w_read.uv_watcher;
h->data = (void *)(lws_intptr_t)wsi->desc.sockfd;
/* required to defer actual deletion until libuv has processed it */
uv_close((uv_handle_t*)&wsi->w_read.uv_watcher, lws_libuv_closewsi_m);
}
int
lws_libuv_check_watcher_active(struct lws *wsi)
{
uv_handle_t *h = (void *)&wsi->w_read.uv_watcher;
return uv_is_active(h);
}
#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0)
LWS_VISIBLE int
lws_plat_plugins_init(struct lws_context *context, const char * const *d)
{
struct lws_plugin_capability lcaps;
struct lws_plugin *plugin;
lws_plugin_init_func initfunc;
int m, ret = 0;
void *v;
uv_dirent_t dent;
uv_fs_t req;
char path[256];
uv_lib_t lib;
int pofs = 0;
#if defined(__MINGW32__) || !defined(WIN32)
pofs = 3;
#endif
lib.errmsg = NULL;
lib.handle = NULL;
uv_loop_init(&context->pu_loop);
lwsl_notice(" Plugins:\n");
while (d && *d) {
lwsl_notice(" Scanning %s\n", *d);
m =uv_fs_scandir(&context->pu_loop, &req, *d, 0, NULL);
if (m < 1) {
lwsl_err("Scandir on %s failed\n", *d);
return 1;
}
while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
if (strlen(dent.name) < 7)
continue;
lwsl_notice(" %s\n", dent.name);
lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, dent.name);
if (uv_dlopen(path, &lib)) {
uv_dlerror(&lib);
lwsl_err("Error loading DSO: %s\n", lib.errmsg);
uv_dlclose(&lib);
goto bail;
}
/* we could open it, can we get his init function? */
#if !defined(WIN32) && !defined(__MINGW32__)
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
dent.name + pofs /* snip lib... */);
path[m - 3] = '\0'; /* snip the .so */
#else
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
dent.name + pofs);
path[m - 4] = '\0'; /* snip the .dll */
#endif
if (uv_dlsym(&lib, path, &v)) {
uv_dlerror(&lib);
lwsl_err("Failed to get %s on %s: %s", path,
dent.name, lib.errmsg);
uv_dlclose(&lib);
goto bail;
}
initfunc = (lws_plugin_init_func)v;
lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
m = initfunc(context, &lcaps);
if (m) {
lwsl_err("Initializing %s failed %d\n", dent.name, m);
goto skip;
}
plugin = lws_malloc(sizeof(*plugin));
if (!plugin) {
uv_dlclose(&lib);
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
strncpy(plugin->name, dent.name, sizeof(plugin->name) - 1);
plugin->name[sizeof(plugin->name) - 1] = '\0';
plugin->lib = lib;
plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols;
context->plugin_extension_count += lcaps.count_extensions;
continue;
skip:
uv_dlclose(&lib);
}
bail:
uv_fs_req_cleanup(&req);
d++;
}
return ret;
}
LWS_VISIBLE int
lws_plat_plugins_destroy(struct lws_context *context)
{
struct lws_plugin *plugin = context->plugin_list, *p;
lws_plugin_destroy_func func;
char path[256];
void *v;
int m;
int pofs = 0;
#if defined(__MINGW32__) || !defined(WIN32)
pofs = 3;
#endif
if (!plugin)
return 0;
// lwsl_notice("%s\n", __func__);
while (plugin) {
p = plugin;
#if !defined(WIN32) && !defined(__MINGW32__)
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + pofs);
path[m - 3] = '\0';
#else
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + pofs);
path[m - 4] = '\0';
#endif
if (uv_dlsym(&plugin->lib, path, &v)) {
uv_dlerror(&plugin->lib);
lwsl_err("Failed to get %s on %s: %s", path,
plugin->name, plugin->lib.errmsg);
} else {
func = (lws_plugin_destroy_func)v;
m = func(context);
if (m)
lwsl_err("Destroying %s failed %d\n",
plugin->name, m);
}
uv_dlclose(&p->lib);
plugin = p->list;
p->list = NULL;
free(p);
}
context->plugin_list = NULL;
while (uv_loop_close(&context->pu_loop))
;
return 0;
}
#endif

3600
lib/libwebsockets.c Executable file → Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,700 +0,0 @@
#include "private-libwebsockets.h"
#include "ip_addr.h"
/* forced into this because new espconn accepted callbacks carry no context ptr */
static struct lws_context *hacky_context;
static unsigned int time_high, ot;
/*
* included from libwebsockets.c for esp8266 builds
*/
unsigned long long time_in_microseconds(void)
{
unsigned int t = system_get_time();
if (ot > t)
time_high++;
ot = t;
return (((long long)time_high) << 32) | t;
}
int gettimeofday(struct timeval *tv, void *tz)
{
unsigned long long t = time_in_microseconds();
tv->tv_sec = t / 1000000;
tv->tv_usec = t % 1000000;
return 0;
}
time_t time(time_t *tloc)
{
unsigned long long t = time_in_microseconds();
if (tloc)
*tloc = t / 1000000;
return 0;
}
LWS_VISIBLE int
lws_get_random(struct lws_context *context, void *buf, int len)
{
// return read(context->fd_random, (char *)buf, len);
return 0;
}
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{
return wsi->pending_send_completion;
}
LWS_VISIBLE struct lws *
wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
{
int n;
for (n = 0; n < context->max_fds; n++)
if (context->connpool[n] == fd)
return (struct lws *)context->connpool[n + context->max_fds];
return NULL;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
//lwsl_notice("%s: wsi %p: len %d\n", __func__, wsi, len);
wsi->pending_send_completion++;
espconn_send(wsi->desc.sockfd, buf, len);
return len;
}
void abort(void)
{
while(1) ;
}
void exit(int n)
{
abort();
}
void _sint(void *s)
{
}
LWS_VISIBLE int
insert_wsi(struct lws_context *context, struct lws *wsi)
{
(void)context;
(void)wsi;
return 0;
}
LWS_VISIBLE int
delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
{
(void)context;
(void)fd;
return 1;
}
struct tm *localtime(const time_t *timep)
{
return NULL;
}
struct tm *localtime_r(const time_t *timep, struct tm *t)
{
return NULL;
}
int atoi(const char *s)
{
int n = 0;
while (*s && (*s >= '0' && *s <= '9'))
n = (n * 10) + ((*s++) - '0');
return n;
}
#undef isxdigit
int isxdigit(int c)
{
if (c >= 'A' && c <= 'F')
return 1;
if (c >= 'a' && c <= 'f')
return 1;
if (c >= '0' && c <= '9')
return 1;
return 0;
}
int strcasecmp(const char *s1, const char *s2)
{
char a, b;
while (*s1 && *s2) {
a = *s1++;
b = *s2++;
if (a == b)
continue;
if (a >= 'a' && a <= 'z')
a -= 'a' - 'A';
if (b >= 'a' && b <= 'z')
b -= 'a' - 'A';
if (a != b)
return 1;
}
return 0;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
{
return 0;
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
extern void output_redirect(const char *str);
output_redirect(line);
}
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
return 0;
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
return 0;
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
// return _lws_plat_service_tsi(context, timeout_ms, 0);
return 0;
}
static int
esp8266_find_free_conn(struct lws_context *context)
{
int n;
for (n = 0; n < context->max_fds; n++)
if (!context->connpool[n]) {
lwsl_info(" using connpool %d\n", n);
return n;
}
lwsl_err("%s: no free conns\n", __func__);
return -1;
}
lws_sockfd_type
esp8266_create_tcp_listen_socket(struct lws_vhost *vh)
{
int n = esp8266_find_free_conn(vh->context);
struct espconn *conn;
if (n < 0)
return NULL;
conn = lws_zalloc(sizeof *conn);
if (!conn)
return NULL;
vh->context->connpool[n] = conn;
conn->type = ESPCONN_TCP;
conn->state = ESPCONN_NONE;
conn->proto.tcp = &vh->tcp;
return conn;
}
const char *
lws_plat_get_peer_simple(struct lws *wsi, char *name, int namelen)
{
unsigned char *p = wsi->desc.sockfd->proto.tcp->remote_ip;
lws_snprintf(name, namelen, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
return name;
}
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
//lwsl_notice("%s\n", __func__);
if (!wsi->context->rxd)
return 0;
if (len < wsi->context->rxd_len)
lwsl_err("trunc read (%d vs %d)\n", len, wsi->context->rxd_len);
else
len = wsi->context->rxd_len;
ets_memcpy(buf, wsi->context->rxd, len);
wsi->context->rxd = NULL;
return len;
}
static void
cb_1Hz(void *arg)
{
struct lws_context *context = arg;
struct lws_context_per_thread *pt = &context->pt[0];
struct lws *wsi;
struct lws_pollfd *pollfd;
int n;
/* Service any ah that has pending rx */
for (n = 0; n < context->max_http_header_pool; n++)
if (pt->ah_pool[n].rxpos != pt->ah_pool[n].rxlen) {
wsi = pt->ah_pool[n].wsi;
pollfd = &pt->fds[wsi->position_in_fds_table];
if (pollfd->events & LWS_POLLIN) {
pollfd->revents |= LWS_POLLIN;
lws_service_fd(context, pollfd);
}
}
/* handle timeouts */
lws_service_fd(context, NULL);
}
static void
esp8266_cb_rx(void *arg, char *data, unsigned short len)
{
struct espconn *conn = arg;
struct lws *wsi = conn->reverse;
struct lws_context_per_thread *pt = &wsi->context->pt[0];
struct lws_pollfd pollfd;
int n = 0;
/*
* if we're doing HTTP headers, and we have no ah, check if there is
* a free ah, if not, have to buffer it
*/
if (!wsi->hdr_parsing_completed && !wsi->u.hdr.ah) {
for (n = 0; n < wsi->context->max_http_header_pool; n++)
if (!pt->ah_pool[n].in_use)
break;
n = n == wsi->context->max_http_header_pool;
}
if (!(pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN) || n) {
wsi->premature_rx = realloc(wsi->premature_rx,
wsi->prem_rx_size + len);
if (!wsi->premature_rx)
return;
os_memcpy((char *)wsi->premature_rx + wsi->prem_rx_size, data, len);
wsi->prem_rx_size += len;
// lwsl_notice("%s: wsi %p: len %d BUFFERING\n", __func__, wsi, len);
if (n) /* we know it will fail, but we will get on the wait list */
n = lws_header_table_attach(wsi, 0);
(void)n;
return;
}
//lwsl_err("%s: wsi %p. len %d\n", __func__, wsi, len);
pollfd.fd = arg;
pollfd.events = LWS_POLLIN;
pollfd.revents = LWS_POLLIN;
wsi->context->rxd = data;
wsi->context->rxd_len = len;
lws_service_fd(lws_get_context(wsi), &pollfd);
}
static void
esp8266_cb_sent(void *arg)
{
struct espconn *conn = arg;
struct lws *wsi = conn->reverse;
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
// lwsl_err("%s: wsi %p (psc %d) wsi->position_in_fds_table=%d\n", __func__, wsi, wsi->pending_send_completion, wsi->position_in_fds_table);
wsi->pending_send_completion--;
if (wsi->close_is_pending_send_completion &&
!wsi->pending_send_completion &&
!lws_partial_buffered(wsi)) {
lwsl_notice("doing delayed close\n");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
}
if (pt->fds[wsi->position_in_fds_table].events & LWS_POLLOUT) {
struct lws_pollfd pollfd;
pollfd.fd = arg;
pollfd.events = LWS_POLLOUT;
pollfd.revents = LWS_POLLOUT;
// lwsl_notice("informing POLLOUT\n");
lws_service_fd(lws_get_context(wsi), &pollfd);
}
}
static void
esp8266_cb_disconnected(void *arg)
{
struct espconn *conn = arg;
struct lws *wsi = conn->reverse;
int n;
lwsl_notice("%s: %p\n", __func__, wsi);
for (n = 0; n < hacky_context->max_fds; n++)
if (hacky_context->connpool[n] == arg) {
hacky_context->connpool[n] = NULL;
lwsl_info(" freed connpool %d\n", n);
}
if (wsi) {
conn->reverse = NULL;
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
lwsl_notice("closed ok\n");
}
}
static void
esp8266_cb_recon(void *arg, signed char err)
{
struct espconn *conn = arg;
lwsl_err("%s: wsi %p. err %d\n", __func__, conn->reverse, err);
conn->state = ESPCONN_CLOSE;
esp8266_cb_disconnected(arg);
}
/*
* there is no reliable indication of which listen socket we were accepted on.
*/
static void
esp8266_cb_connect(void *arg)
{
struct espconn *cs = arg;
// struct ip_addr *ipa = (struct ip_addr *)cs->proto.tcp->remote_ip;
struct lws_vhost *vh = hacky_context->vhost_list;
// struct ip_info info;
struct lws *wsi;
int n;
lwsl_notice("%s: (wsi coming): %p\n", __func__, cs->reverse);
#if 0
wifi_get_ip_info(0, &info);
if (ip_addr_netcmp(ipa, &info.ip, &info.netmask)) {
/* we are on the same subnet as the AP, ie, connected to AP */
while (vh && strcmp(vh->name, "ap"))
vh = vh->vhost_next;
} else
while (vh && !strcmp(vh->name, "ap"))
vh = vh->vhost_next;
if (!vh)
goto bail;
#endif
n = esp8266_find_free_conn(hacky_context);
if (n < 0)
goto bail;
hacky_context->connpool[n] = cs;
espconn_recv_hold(cs);
wsi = lws_adopt_socket_vhost(vh, cs);
if (!wsi)
goto bail;
lwsl_err("%s: wsi %p (using free_conn %d): vh %s\n", __func__, wsi, n, vh->name);
espconn_regist_recvcb(cs, esp8266_cb_rx);
espconn_regist_reconcb(cs, esp8266_cb_recon);
espconn_regist_disconcb(cs, esp8266_cb_disconnected);
espconn_regist_sentcb(cs, esp8266_cb_sent);
espconn_set_opt(cs, ESPCONN_NODELAY | ESPCONN_REUSEADDR);
espconn_regist_time(cs, 7200, 1);
return;
bail:
lwsl_err("%s: bailed]n", __func__);
espconn_disconnect(cs);
}
void
esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi)
{
fd->proto.tcp->local_port = port;
fd->reverse = wsi;
hacky_context = wsi->context;
espconn_regist_connectcb(fd, esp8266_cb_connect);
/* hmmm it means, listen() + accept() */
espconn_accept(fd);
espconn_tcp_set_max_con_allow(fd, 10);
}
void
esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi)
{
int n;
fd->reverse = wsi;
for (n = 0; n < wsi->context->max_fds ; n++)
if (wsi->context->connpool[n] == wsi->desc.sockfd)
wsi->position_in_fds_table = n;
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
{
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
espconn_tcp_set_max_con(12);
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
#if 0
struct lws_context_per_thread *pt = &context->pt[0];
int m = context->count_threads;
if (context->lws_lookup)
lws_free(context->lws_lookup);
while (m--) {
close(pt->dummy_pipe_fds[0]);
close(pt->dummy_pipe_fds[1]);
pt++;
}
#endif
// close(context->fd_random);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
return 0;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
context->connpool[wsi->position_in_fds_table + context->max_fds] = (lws_sockfd_type)wsi;
wsi->desc.sockfd->reverse = wsi;
pt->fds_count++;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n;
for (n = 0; n < wsi->context->max_fds; n++)
if (wsi->context->connpool[n] == wsi->desc.sockfd) {
wsi->context->connpool[n] = NULL;
wsi->context->connpool[n + wsi->context->max_fds] = NULL;
lwsl_notice(" freed connpool %d\n", n);
}
wsi->desc.sockfd->reverse = NULL;
pt->fds_count--;
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
{
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
void *p;
//lwsl_notice("%s: %p: wsi->pift=%d, events %d\n",
// __func__, wsi, wsi->position_in_fds_table, pfd->events);
if (pfd->events & LWS_POLLIN) {
if (wsi->premature_rx) {
lwsl_notice("replaying buffered rx: wsi %p\n", wsi);
p = wsi->premature_rx;
wsi->premature_rx = NULL;
esp8266_cb_rx(wsi->desc.sockfd,
(char *)p + wsi->prem_rx_pos,
wsi->prem_rx_size - wsi->prem_rx_pos);
wsi->prem_rx_size = 0;
wsi->prem_rx_pos = 0;
lws_free(p);
}
if (espconn_recv_unhold(wsi->desc.sockfd) < 0)
return -1;
} else
if (espconn_recv_hold(wsi->desc.sockfd) < 0)
return -1;
if (!(pfd->events & LWS_POLLOUT))
return 0;
if (!wsi->pending_send_completion) {
pfd->revents |= LWS_POLLOUT;
// lwsl_notice("doing POLLOUT\n");
lws_service_fd(lws_get_context(wsi), pfd);
} //else
//lwsl_notice("pending sc\n");
return 0;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
// return inet_ntop(af, src, dst, cnt);
return 0;
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
//return inet_pton(af, src, dst);
return 1;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
// struct lws_context_per_thread *pt = &context->pt[0];
// int n = context->count_threads, fd;
/* master context has the global fd lookup array */
context->connpool = lws_zalloc(sizeof(struct espconn *) *
context->max_fds * 2);
if (context->connpool == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
lwsl_notice(" mem: platform fd map: %5u bytes\n",
sizeof(struct espconn *) * context->max_fds);
// fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
// context->fd_random = fd;
// if (context->fd_random < 0) {
// lwsl_err("Unable to open random device %s %d\n",
// SYSTEM_RANDOM_FILEPATH, context->fd_random);
// return 1;
// }
os_memset(&context->to_timer, 0, sizeof(os_timer_t));
os_timer_disarm(&context->to_timer);
os_timer_setfn(&context->to_timer, (os_timer_func_t *)cb_1Hz, context);
os_timer_arm(&context->to_timer, 1000, 1);
if (!lws_libev_init_fd_table(context) &&
!lws_libuv_init_fd_table(context) &&
!lws_libevent_init_fd_table(context)) {
/* otherwise libev handled it instead */
#if 0
while (n--) {
if (pipe(pt->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
pt->fds[0].fd = pt->dummy_pipe_fds[0];
pt->fds[0].events = LWS_POLLIN;
pt->fds[0].revents = 0;
pt->fds_count = 1;
pt++;
}
#endif
}
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}

View file

@ -1,329 +0,0 @@
#include "private-libwebsockets.h"
/*
* included from libwebsockets.c for OPTEE builds
*/
void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen);
unsigned long long time_in_microseconds(void)
{
return ((unsigned long long)time(NULL)) * 1000000;
}
#if 0
LWS_VISIBLE int
lws_get_random(struct lws_context *context, void *buf, int len)
{
TEE_GenerateRandom(buf, len);
return len;
}
#endif
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{
#if 0
struct lws_pollfd fds;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->trunc_len)
return 1;
fds.fd = wsi->desc.sockfd;
fds.events = POLLOUT;
fds.revents = 0;
if (poll(&fds, 1, 0) != 1)
return 1;
if ((fds.revents & POLLOUT) == 0)
return 1;
#endif
/* okay to send another packet without blocking */
return 0;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
{
// return poll(fd, 1, 0);
return 0;
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
#if 0
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
#endif
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
#if 0
struct lws_context_per_thread *pt = &context->pt[0];
char buf = 0, m = context->count_threads;
while (m--) {
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
pt++;
}
#endif
}
#if 0
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
IMSG("%d: %s\n", level, line);
}
#endif
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt;
int n = -1, m, c;
//char buf;
/* stay dead once we are dead */
if (!context || !context->vhost_list)
return 1;
pt = &context->pt[tsi];
if (timeout_ms < 0)
goto faked_service;
if (!context->service_tid_detected) {
struct lws _lws;
memset(&_lws, 0, sizeof(_lws));
_lws.context = context;
context->service_tid_detected =
context->vhost_list->protocols[0].callback(
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
}
context->service_tid = context->service_tid_detected;
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(context, 1, tsi)) {
lwsl_notice("%s: doing forced service\n", __func__);
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(context, 1, pt->tid))
/* yes... come back again quickly */
timeout_ms = 0;
}
#if 1
n = poll(pt->fds, pt->fds_count, timeout_ms);
#ifdef LWS_OPENSSL_SUPPORT
if (!pt->rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
#endif
lws_service_fd_tsi(context, NULL, tsi);
return 0;
}
#endif
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++) {
if (!pt->fds[n].revents)
continue;
c--;
#if 0
if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
if (read(pt->fds[n].fd, &buf, 1) != 1)
lwsl_err("Cannot read from dummy pipe.");
continue;
}
#endif
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
return 0;
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
return 0;
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
return _lws_plat_service_tsi(context, timeout_ms, 0);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
{
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
{
if (context->lws_lookup)
lws_free(context->lws_lookup);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
return -1;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->fds[pt->fds_count++].revents = 0;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->fds_count--;
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
{
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
return 0;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
//return inet_ntop(af, src, dst, cnt);
return "lws_plat_inet_ntop";
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
//return inet_pton(af, src, dst);
return 1;
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
const char *filename, lws_fop_flags_t *flags)
{
return NULL;
}
LWS_VISIBLE int
_lws_plat_file_close(lws_fop_fd_t *fop_fd)
{
return 0;
}
LWS_VISIBLE lws_fileofs_t
_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
return 0;
}
LWS_VISIBLE int
_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
return 0;
}
LWS_VISIBLE int
_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
/* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
lwsl_notice(" mem: platform fd map: %5lu bytes\n",
(long)sizeof(struct lws *) * context->max_fds);
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}

View file

@ -3,12 +3,6 @@
#include <pwd.h>
#include <grp.h>
#ifdef LWS_WITH_PLUGINS
#include <dlfcn.h>
#endif
#include <dirent.h>
/*
* included from libwebsockets.c for unix builds
*/
@ -20,22 +14,21 @@ unsigned long long time_in_microseconds(void)
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
}
LWS_VISIBLE int
lws_get_random(struct lws_context *context, void *buf, int len)
LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
void *buf, int len)
{
return read(context->fd_random, (char *)buf, len);
}
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
{
struct lws_pollfd fds;
struct libwebsocket_pollfd fds;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->trunc_len)
if (wsi->truncated_send_len)
return 1;
fds.fd = wsi->desc.sockfd;
fds.fd = wsi->sock;
fds.events = POLLOUT;
fds.revents = 0;
@ -51,32 +44,33 @@ lws_send_pipe_choked(struct lws *wsi)
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
lws_poll_listen_fd(struct libwebsocket_pollfd *fd)
{
return poll(fd, 1, 0);
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
/*
* This is just used to interrupt poll waiting
* we don't have to do anything with it.
*/
static void lws_sigusr2(int sig)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
/**
* libwebsocket_cancel_service() - Cancel servicing of pending websocket activity
* @context: Websocket context
*
* This function let a call to libwebsocket_service() waiting for a timeout
* immediately return.
*/
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
libwebsocket_cancel_service(struct libwebsocket_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
char buf = 0, m = context->count_threads;
char buf = 0;
while (m--) {
if (write(pt->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
pt++;
}
if (write(context->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
@ -100,91 +94,89 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
syslog(syslog_level, "%s", line);
}
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
LWS_VISIBLE int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
{
struct lws_context_per_thread *pt;
int n = -1, m, c;
int n;
int m;
char buf;
#ifdef LWS_OPENSSL_SUPPORT
struct libwebsocket *wsi, *wsi_next;
#endif
/* stay dead once we are dead */
if (!context || !context->vhost_list)
if (!context)
return 1;
pt = &context->pt[tsi];
lws_libev_run(context);
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
if (timeout_ms < 0)
goto faked_service;
lws_libev_run(context, tsi);
lws_libuv_run(context, tsi);
lws_libevent_run(context, tsi);
if (!context->service_tid_detected) {
struct lws _lws;
memset(&_lws, 0, sizeof(_lws));
_lws.context = context;
context->service_tid_detected =
context->vhost_list->protocols[0].callback(
&_lws, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
}
context->service_tid = context->service_tid_detected;
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(context, 1, tsi)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(context, 1, pt->tid))
/* yes... come back again quickly */
timeout_ms = 0;
}
n = poll(pt->fds, pt->fds_count, timeout_ms);
context->service_tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
#ifdef LWS_OPENSSL_SUPPORT
if (!pt->rx_draining_ext_list &&
!lws_ssl_anybody_has_buffered_read_tsi(context, tsi) && !n) {
#else
if (!pt->rx_draining_ext_list && !n) /* poll timeout */ {
/* if we know we have non-network pending data, do not wait in poll */
if (lws_ssl_anybody_has_buffered_read(context))
timeout_ms = 0;
#endif
lws_service_fd_tsi(context, NULL, tsi);
n = poll(context->fds, context->fds_count, timeout_ms);
context->service_tid = 0;
#ifdef LWS_OPENSSL_SUPPORT
if (!lws_ssl_anybody_has_buffered_read(context) && n == 0) {
#else
if (n == 0) /* poll timeout */ {
#endif
libwebsocket_service_fd(context, NULL);
return 0;
}
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;
if (n < 0) {
if (LWS_ERRNO != LWS_EINTR)
return -1;
return 0;
}
#ifdef LWS_OPENSSL_SUPPORT
/*
* For all guys with buffered SSL read data already saved up, if they
* are not flowcontrolled, fake their POLLIN status so they'll get
* service to use up the buffered incoming data, even though their
* network socket may have nothing
*/
wsi = context->pending_read_list;
while (wsi) {
wsi_next = wsi->pending_read_list_next;
context->fds[wsi->position_in_fds_table].revents |=
context->fds[wsi->position_in_fds_table].events & POLLIN;
if (context->fds[wsi->position_in_fds_table].revents & POLLIN) {
/*
* he's going to get serviced now, take him off the
* list of guys with buffered SSL. If he still has some
* at the end of the service, he'll get put back on the
* list then.
*/
lws_ssl_remove_wsi_from_buffered_list(context, wsi);
}
wsi = wsi_next;
}
#endif
/* any socket with events to service? */
for (n = 0; n < pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
for (n = 0; n < context->fds_count; n++) {
if (!context->fds[n].revents)
continue;
c--;
if (pt->fds[n].fd == pt->dummy_pipe_fds[0]) {
if (read(pt->fds[n].fd, &buf, 1) != 1)
if (context->fds[n].fd == context->dummy_pipe_fds[0]) {
if (read(context->fds[n].fd, &buf, 1) != 1)
lwsl_err("Cannot read from dummy pipe.");
continue;
}
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
m = libwebsocket_service_fd(context, &context->fds[n]);
if (m < 0)
return -1;
/* if something closed, retry this slot */
@ -196,41 +188,25 @@ faked_service:
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
{
return 0;
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
return _lws_plat_service_tsi(context, timeout_ms, 0);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__OpenBSD__)
struct protoent *tcp_proto;
#endif
if (vhost->ka_time) {
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const void *)&optval, optlen) < 0)
(const void *)&optval, optlen) < 0)
return 1;
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun)
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__CYGWIN__) || defined(__OpenBSD__)
/*
* didn't find a way to set these per-socket, need to
@ -238,43 +214,27 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
*/
#else
/* set the keepalive conditions we want on it too */
optval = vhost->ka_time;
optval = context->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
(const void *)&optval, optlen) < 0)
return 1;
optval = vhost->ka_interval;
optval = context->ka_interval;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
(const void *)&optval, optlen) < 0)
(const void *)&optval, optlen) < 0)
return 1;
optval = vhost->ka_probes;
optval = context->ka_probes;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT,
(const void *)&optval, optlen) < 0)
(const void *)&optval, optlen) < 0)
return 1;
#endif
}
#if defined(SO_BINDTODEVICE)
if (vhost->bind_iface && vhost->iface) {
lwsl_info("binding listen skt to %s using SO_BINDTODEVICE\n", vhost->iface);
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vhost->iface,
strlen(vhost->iface)) < 0) {
lwsl_warn("Failed to bind to device %s\n", vhost->iface);
return 1;
}
}
#endif
/* Disable Nagle */
optval = 1;
#if defined (__sun)
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#elif !defined(__APPLE__) && \
!defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && \
!defined(__NetBSD__) && \
!defined(__OpenBSD__)
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
!defined(__OpenBSD__)
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#else
@ -290,266 +250,111 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
return 0;
}
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
static void
_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
{
cap_t caps;
if (!count)
return;
caps = cap_get_proc();
cap_set_flag(caps, mode, count, cv, CAP_SET);
cap_set_proc(caps);
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
cap_free(caps);
}
#endif
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
int n;
#endif
if (info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
if (info->uid != -1) {
struct passwd *p = getpwuid(info->uid);
if (p) {
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
_lws_plat_apply_caps(CAP_PERMITTED, info->caps, info->count_caps);
#endif
initgroups(p->pw_name, info->gid);
if (setuid(info->uid))
lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
else
lwsl_notice("Set privs to user '%s'\n", p->pw_name);
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
_lws_plat_apply_caps(CAP_EFFECTIVE, info->caps, info->count_caps);
if (info->count_caps)
for (n = 0; n < info->count_caps; n++)
lwsl_notice(" RETAINING CAPABILITY %d\n", (int)info->caps[n]);
#endif
lwsl_notice(" Set privs to user '%s'\n", p->pw_name);
} 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));
#ifdef LWS_WITH_PLUGINS
#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
/* libuv.c implements these in a cross-platform way */
#else
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
LWS_VISIBLE int
lws_plat_plugins_init(struct lws_context * context, const char * const *d)
lws_plat_init_lookup(struct libwebsocket_context *context)
{
struct lws_plugin_capability lcaps;
struct lws_plugin *plugin;
lws_plugin_init_func initfunc;
struct dirent **namelist;
int n, i, m, ret = 0;
char path[256];
void *l;
lwsl_notice(" Plugins:\n");
while (d && *d) {
n = scandir(*d, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on %s failed\n", *d);
return 1;
}
for (i = 0; i < n; i++) {
if (strlen(namelist[i]->d_name) < 7)
goto inval;
lwsl_notice(" %s\n", namelist[i]->d_name);
lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d,
namelist[i]->d_name);
l = dlopen(path, RTLD_NOW);
if (!l) {
lwsl_err("Error loading DSO: %s\n", dlerror());
while (i++ < n)
free(namelist[i]);
goto bail;
}
/* we could open it, can we get his init function? */
m = lws_snprintf(path, sizeof(path) - 1, "init_%s",
namelist[i]->d_name + 3 /* snip lib... */);
path[m - 3] = '\0'; /* snip the .so */
initfunc = dlsym(l, path);
if (!initfunc) {
lwsl_err("Failed to get init on %s: %s",
namelist[i]->d_name, dlerror());
dlclose(l);
}
lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
m = initfunc(context, &lcaps);
if (m) {
lwsl_err("Initializing %s failed %d\n",
namelist[i]->d_name, m);
dlclose(l);
goto skip;
}
plugin = lws_malloc(sizeof(*plugin));
if (!plugin) {
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
plugin->name[sizeof(plugin->name) - 1] = '\0';
plugin->l = l;
plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols;
context->plugin_extension_count += lcaps.count_extensions;
free(namelist[i]);
continue;
skip:
dlclose(l);
inval:
free(namelist[i]);
}
free(namelist);
d++;
context->lws_lookup = lws_zalloc(sizeof(struct libwebsocket *) * context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err(
"Unable to allocate lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
bail:
free(namelist);
return ret;
}
LWS_VISIBLE int
lws_plat_plugins_destroy(struct lws_context * context)
{
struct lws_plugin *plugin = context->plugin_list, *p;
lws_plugin_destroy_func func;
char path[256];
int m;
if (!plugin)
return 0;
lwsl_notice("%s\n", __func__);
while (plugin) {
p = plugin;
m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
path[m - 3] = '\0';
func = dlsym(plugin->l, path);
if (!func) {
lwsl_err("Failed to get destroy on %s: %s",
plugin->name, dlerror());
goto next;
}
m = func(context);
if (m)
lwsl_err("Initializing %s failed %d\n",
plugin->name, m);
next:
dlclose(p->l);
plugin = p->list;
p->list = NULL;
free(p);
}
context->plugin_list = NULL;
return 0;
}
#endif
#endif
#if 0
static void
sigabrt_handler(int x)
LWS_VISIBLE int
lws_plat_init_fd_tables(struct libwebsocket_context *context)
{
printf("%s\n", __func__);
//*(char *)0 = 0;
context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
if (context->fd_random < 0) {
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
return 1;
}
if (lws_libev_init_fd_table(context))
/* libev handled it instead */
return 0;
if (pipe(context->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
context->fds[0].fd = context->dummy_pipe_fds[0];
context->fds[0].events = LWS_POLLIN;
context->fds[0].revents = 0;
context->fds_count = 1;
return 0;
}
#endif
static void sigpipe_handler(int x)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
#if !defined(LWS_AVOID_SIGPIPE_IGN)
signal(SIGPIPE, SIG_IGN);
#endif
sigset_t mask;
// signal(SIGABRT, sigabrt_handler);
signal(SIGUSR2, lws_sigusr2);
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, NULL);
signal(SIGPIPE, sigpipe_handler);
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
lws_plat_context_early_destroy(struct libwebsocket_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
lws_plat_context_late_destroy(struct libwebsocket_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int m = context->count_threads;
#ifdef LWS_WITH_PLUGINS
if (context->plugin_list)
lws_plat_plugins_destroy(context);
#endif
if (context->lws_lookup)
lws_free(context->lws_lookup);
while (m--) {
if (pt->dummy_pipe_fds[0])
close(pt->dummy_pipe_fds[0]);
if (pt->dummy_pipe_fds[1])
close(pt->dummy_pipe_fds[1]);
pt++;
}
if (!context->fd_random)
lwsl_err("ZERO RANDOM FD\n");
if (context->fd_random != LWS_INVALID_FILE)
close(context->fd_random);
close(context->dummy_pipe_fds[0]);
close(context->dummy_pipe_fds[1]);
close(context->fd_random);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
interface_to_sa(struct libwebsocket_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
int rc = -1;
@ -572,7 +377,7 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
switch (ifc->ifa_addr->sa_family) {
case AF_INET:
#ifdef LWS_USE_IPV6
if (ipv6) {
if (LWS_IPV6_ENABLED(context)) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
sizeof(struct in6_addr));
@ -603,7 +408,7 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
freeifaddrs(ifr);
if (rc == -1) {
/* check if bind to IP address */
/* check if bind to IP adddress */
#ifdef LWS_USE_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = 0;
@ -617,212 +422,54 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
pt->fds[pt->fds_count++].revents = 0;
lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ);
context->fds[context->fds_count++].revents = 0;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi, int m)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
pt->fds_count--;
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
lws_plat_service_periodic(struct libwebsocket_context *context)
{
/* if our parent went down, don't linger around */
if (context->started_with_parent &&
kill(context->started_with_parent, 0) < 0)
kill(context->started_with_parent, 0) < 0)
kill(getpid(), SIGTERM);
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
lws_plat_change_pollfd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd)
{
return 0;
}
LWS_VISIBLE int
lws_plat_open_file(const char* filename, unsigned long* filelen)
{
struct stat stat_buf;
int ret = open(filename, O_RDONLY);
if (ret < 0)
return LWS_INVALID_FILE;
if (fstat(ret, &stat_buf) < 0) {
close(ret);
return LWS_INVALID_FILE;
}
*filelen = stat_buf.st_size;
return ret;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
return inet_ntop(af, src, dst, cnt);
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
return inet_pton(af, src, dst);
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
const char *vpath, lws_fop_flags_t *flags)
{
struct stat stat_buf;
int ret = open(filename, (*flags) & LWS_FOP_FLAGS_MASK, 0664);
lws_fop_fd_t fop_fd;
if (ret < 0)
return NULL;
if (fstat(ret, &stat_buf) < 0)
goto bail;
fop_fd = malloc(sizeof(*fop_fd));
if (!fop_fd)
goto bail;
fop_fd->fops = fops;
fop_fd->flags = *flags;
fop_fd->fd = ret;
fop_fd->filesystem_priv = NULL; /* we don't use it */
fop_fd->len = stat_buf.st_size;
fop_fd->pos = 0;
return fop_fd;
bail:
close(ret);
return NULL;
}
LWS_VISIBLE int
_lws_plat_file_close(lws_fop_fd_t *fop_fd)
{
int fd = (*fop_fd)->fd;
free(*fop_fd);
*fop_fd = NULL;
return close(fd);
}
LWS_VISIBLE lws_fileofs_t
_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
lws_fileofs_t r;
if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
offset = fop_fd->len - fop_fd->pos;
if ((lws_fileofs_t)fop_fd->pos + offset < 0)
offset = -fop_fd->pos;
r = lseek(fop_fd->fd, offset, SEEK_CUR);
if (r >= 0)
fop_fd->pos = r;
else
lwsl_err("error seeking from cur %ld, offset %ld\n",
(long)fop_fd->pos, (long)offset);
return r;
}
LWS_VISIBLE int
_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
long n;
n = read((int)fop_fd->fd, buf, len);
if (n == -1) {
*amount = 0;
return -1;
}
fop_fd->pos += n;
lwsl_debug("%s: read %ld of req %ld, pos %ld, len %ld\n", __func__, n,
(long)len, (long)fop_fd->pos, (long)fop_fd->len);
*amount = n;
return 0;
}
LWS_VISIBLE int
_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
long n;
n = write((int)fop_fd->fd, buf, len);
if (n == -1) {
*amount = 0;
return -1;
}
fop_fd->pos += n;
*amount = n;
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads, fd;
/* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
lwsl_notice(" mem: platform fd map: %5lu bytes\n",
(unsigned long)(sizeof(struct lws *) * context->max_fds));
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
context->fd_random = fd;
if (context->fd_random < 0) {
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
return 1;
}
if (!lws_libev_init_fd_table(context) &&
!lws_libuv_init_fd_table(context) &&
!lws_libevent_init_fd_table(context)) {
/* otherwise libev handled it instead */
while (n--) {
if (pipe(pt->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
pt->fds[0].fd = pt->dummy_pipe_fds[0];
pt->fds[0].events = LWS_POLLIN;
pt->fds[0].revents = 0;
pt->fds_count = 1;
pt++;
}
}
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}

View file

@ -1,14 +1,9 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include "private-libwebsockets.h"
unsigned long long
time_in_microseconds()
{
#ifndef DELTA_EPOCH_IN_MICROSECS
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
FILETIME filetime;
ULARGE_INTEGER datetime;
@ -33,33 +28,30 @@ time_in_microseconds()
time_t time(time_t *t)
{
time_t ret = time_in_microseconds() / 1000000;
if(t != NULL)
*t = ret;
*t = ret;
return ret;
}
#endif
/* file descriptor hash management */
struct lws *
wsi_from_fd(const struct lws_context *context, lws_sockfd_type fd)
struct libwebsocket *
wsi_from_fd(struct libwebsocket_context *context, int fd)
{
int h = LWS_FD_HASH(fd);
int n = 0;
for (n = 0; n < context->fd_hashtable[h].length; n++)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd)
if (context->fd_hashtable[h].wsi[n]->sock == fd)
return context->fd_hashtable[h].wsi[n];
return NULL;
}
int
insert_wsi(struct lws_context *context, struct lws *wsi)
insert_wsi(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
int h = LWS_FD_HASH(wsi->desc.sockfd);
int h = LWS_FD_HASH(wsi->sock);
if (context->fd_hashtable[h].length == (getdtablesize() - 1)) {
lwsl_err("hash table overflow\n");
@ -72,16 +64,16 @@ insert_wsi(struct lws_context *context, struct lws *wsi)
}
int
delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
delete_from_fd(struct libwebsocket_context *context, int fd)
{
int h = LWS_FD_HASH(fd);
int n = 0;
for (n = 0; n < context->fd_hashtable[h].length; n++)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == fd) {
if (context->fd_hashtable[h].wsi[n]->sock == fd) {
while (n < context->fd_hashtable[h].length) {
context->fd_hashtable[h].wsi[n] =
context->fd_hashtable[h].wsi[n + 1];
context->fd_hashtable[h].wsi[n + 1];
n++;
}
context->fd_hashtable[h].length--;
@ -94,8 +86,8 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
return 1;
}
LWS_VISIBLE int lws_get_random(struct lws_context *context,
void *buf, int len)
LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
void *buf, int len)
{
int n;
char *p = (char *)buf;
@ -106,21 +98,17 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
return n;
}
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
{
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->trunc_len)
return 1;
return (int)wsi->sock_send_blocking;
return wsi->sock_send_blocking;
}
LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
LWS_VISIBLE int lws_poll_listen_fd(struct libwebsocket_pollfd *fd)
{
fd_set readfds;
struct timeval tv = { 0, 0 };
assert((fd->events & LWS_POLLIN) == LWS_POLLIN);
assert(fd->events == LWS_POLLIN);
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
@ -128,23 +116,17 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
}
/**
* libwebsocket_cancel_service() - Cancel servicing of pending websocket activity
* @context: Websocket context
*
* This function let a call to libwebsocket_service() waiting for a timeout
* immediately return.
*/
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
libwebsocket_cancel_service(struct libwebsocket_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads;
while (n--) {
WSASetEvent(pt->events[0]);
pt++;
}
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
WSASetEvent(pt->events[0]);
WSASetEvent(context->events[0]);
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
@ -152,189 +134,108 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
lwsl_emit_stderr(level, line);
}
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
LWS_VISIBLE int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
{
struct lws_context_per_thread *pt;
WSANETWORKEVENTS networkevents;
struct lws_pollfd *pfd;
struct lws *wsi;
unsigned int i;
int n;
int i;
DWORD ev;
int n, m;
WSANETWORKEVENTS networkevents;
struct libwebsocket_pollfd *pfd;
/* stay dead once we are dead */
if (context == NULL)
return 1;
pt = &context->pt[tsi];
context->service_tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (!context->service_tid_detected) {
struct lws _lws;
memset(&_lws, 0, sizeof(_lws));
_lws.context = context;
context->service_tid_detected = context->vhost_list->
protocols[0].callback(&_lws, LWS_CALLBACK_GET_THREAD_ID,
NULL, NULL, 0);
}
context->service_tid = context->service_tid_detected;
if (timeout_ms < 0)
{
if (lws_service_flag_pending(context, tsi)) {
/* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count; n++) {
if (!pt->fds[n].revents)
continue;
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
}
return 0;
}
for (i = 0; i < pt->fds_count; ++i) {
pfd = &pt->fds[i];
if (!(pfd->events & LWS_POLLOUT))
for (i = 0; i < context->fds_count; ++i) {
pfd = &context->fds[i];
if (pfd->fd == context->listen_service_fd)
continue;
wsi = wsi_from_fd(context, pfd->fd);
if (wsi->listener)
continue;
if (!wsi || wsi->sock_send_blocking)
continue;
pfd->revents = LWS_POLLOUT;
n = lws_service_fd(context, pfd);
if (n < 0)
return -1;
/* if something closed, retry this slot */
if (n)
i--;
if (wsi->trunc_len)
WSASetEvent(pt->events[0]);
}
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(context, 1, tsi)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(context, 1, pt->tid))
/* yes... come back again quickly */
timeout_ms = 0;
}
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
if (ev == WSA_WAIT_EVENT_0) {
unsigned int eIdx;
WSAResetEvent(pt->events[0]);
for (eIdx = 0; eIdx < pt->fds_count; ++eIdx) {
if (WSAEnumNetworkEvents(pt->fds[eIdx].fd, 0, &networkevents) == SOCKET_ERROR) {
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", LWS_ERRNO);
return -1;
}
pfd = &pt->fds[eIdx];
pfd->revents = (short)networkevents.lNetworkEvents;
if ((networkevents.lNetworkEvents & FD_CONNECT) &&
networkevents.iErrorCode[FD_CONNECT_BIT] &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EALREADY &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EINPROGRESS &&
networkevents.iErrorCode[FD_CONNECT_BIT] != LWS_EWOULDBLOCK &&
networkevents.iErrorCode[FD_CONNECT_BIT] != WSAEINVAL) {
lwsl_debug("Unable to connect errno=%d\n",
networkevents.iErrorCode[FD_CONNECT_BIT]);
pfd->revents = LWS_POLLHUP;
} else
pfd->revents = (short)networkevents.lNetworkEvents;
if (pfd->revents & LWS_POLLOUT) {
wsi = wsi_from_fd(context, pfd->fd);
if (wsi)
wsi->sock_send_blocking = 0;
}
/* if something closed, retry this slot */
if (pfd->revents & LWS_POLLHUP)
--eIdx;
if( pfd->revents != 0 ) {
lws_service_fd_tsi(context, pfd, tsi);
}
if (pfd->events & LWS_POLLOUT) {
if (wsi_from_fd(context,pfd->fd)->sock_send_blocking)
continue;
pfd->revents = LWS_POLLOUT;
n = libwebsocket_service_fd(context, pfd);
if (n < 0)
return n;
}
}
ev = WSAWaitForMultipleEvents(context->fds_count + 1,
context->events, FALSE, timeout_ms, FALSE);
context->service_tid = 0;
if (ev == WSA_WAIT_TIMEOUT) {
lws_service_fd(context, NULL);
libwebsocket_service_fd(context, NULL);
return 0;
}
return 0;;
if (ev == WSA_WAIT_EVENT_0) {
WSAResetEvent(context->events[0]);
return 0;
}
if (ev < WSA_WAIT_EVENT_0 || ev > WSA_WAIT_EVENT_0 + context->fds_count)
return -1;
pfd = &context->fds[ev - WSA_WAIT_EVENT_0 - 1];
if (WSAEnumNetworkEvents(pfd->fd,
context->events[ev - WSA_WAIT_EVENT_0],
&networkevents) == SOCKET_ERROR) {
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n",
LWS_ERRNO);
return -1;
}
pfd->revents = networkevents.lNetworkEvents;
if (pfd->revents & LWS_POLLOUT)
wsi_from_fd(context,pfd->fd)->sock_send_blocking = FALSE;
return libwebsocket_service_fd(context, pfd);
}
LWS_VISIBLE int
lws_plat_service(struct lws_context *context, int timeout_ms)
{
return _lws_plat_service_tsi(context, timeout_ms, 0);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
{
int optval = 1;
int optlen = sizeof(optval);
u_long optl = 1;
DWORD dwBytesRet;
struct tcp_keepalive alive;
int protonbr;
#ifndef _WIN32_WCE
struct protoent *tcp_proto;
#endif
if (vhost->ka_time) {
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const char *)&optval, optlen) < 0)
(const char *)&optval, optlen) < 0)
return 1;
alive.onoff = TRUE;
alive.keepalivetime = vhost->ka_time;
alive.keepaliveinterval = vhost->ka_interval;
alive.keepalivetime = context->ka_time;
alive.keepaliveinterval = context->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL))
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL))
return 1;
}
/* 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, protonbr, TCP_NODELAY, (const char *)&optval, optlen);
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen);
/* We are nonblocking... */
ioctlsocket(fd, FIONBIO, &optl);
@ -347,6 +248,40 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
LWS_VISIBLE int
lws_plat_init_lookup(struct libwebsocket_context *context)
{
int i;
for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
context->fd_hashtable[i].wsi = lws_zalloc(sizeof(struct libwebsocket*) * context->max_fds);
if (!context->fd_hashtable[i].wsi) {
return -1;
}
}
return 0;
}
LWS_VISIBLE int
lws_plat_init_fd_tables(struct libwebsocket_context *context)
{
context->events = lws_malloc(sizeof(WSAEVENT) * (context->max_fds + 1));
if (context->events == NULL) {
lwsl_err("Unable to allocate events array for %d connections\n",
context->max_fds);
return 1;
}
context->fds_count = 0;
context->events[0] = WSACreateEvent();
context->fd_random = 0;
return 0;
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
@ -370,22 +305,16 @@ lws_plat_context_early_init(void)
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct lws_context *context)
lws_plat_context_early_destroy(struct libwebsocket_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads;
while (n--) {
if (pt->events) {
WSACloseEvent(pt->events[0]);
lws_free(pt->events);
}
pt++;
if (context->events) {
WSACloseEvent(context->events[0]);
lws_free(context->events);
}
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_context *context)
lws_plat_context_late_destroy(struct libwebsocket_context *context)
{
int n;
@ -397,20 +326,10 @@ lws_plat_context_late_destroy(struct lws_context *context)
WSACleanup();
}
LWS_VISIBLE LWS_EXTERN int
lws_interface_to_sa(int ipv6,
LWS_VISIBLE int
interface_to_sa(struct libwebsocket_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
#ifdef LWS_USE_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
if (ipv6) {
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
return 0;
}
}
#endif
long long address = inet_addr(ifname);
if (address == INADDR_NONE) {
@ -422,69 +341,45 @@ lws_interface_to_sa(int ipv6,
if (address == INADDR_NONE)
return -1;
addr->sin_addr.s_addr = (lws_intptr_t)address;
addr->sin_addr.s_addr = address;
return 0;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->fds[pt->fds_count++].revents = 0;
pt->events[pt->fds_count] = pt->events[0];
WSAEventSelect(wsi->desc.sockfd, pt->events[0],
LWS_POLLIN | LWS_POLLHUP | FD_CONNECT);
context->fds[context->fds_count++].revents = 0;
context->events[context->fds_count] = WSACreateEvent();
WSAEventSelect(wsi->sock, context->events[context->fds_count], LWS_POLLIN);
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct lws_context *context,
struct lws *wsi, int m)
lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi, int m)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
pt->events[m + 1] = pt->events[pt->fds_count--];
WSACloseEvent(context->events[m + 1]);
context->events[m + 1] = context->events[context->fds_count + 1];
}
LWS_VISIBLE void
lws_plat_service_periodic(struct lws_context *context)
lws_plat_service_periodic(struct libwebsocket_context *context)
{
}
LWS_VISIBLE int
lws_plat_check_connection_error(struct lws *wsi)
lws_plat_change_pollfd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd)
{
int optVal;
int optLen = sizeof(int);
if (getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR,
(char*)&optVal, &optLen) != SOCKET_ERROR && optVal &&
optVal != LWS_EALREADY && optVal != LWS_EINPROGRESS &&
optVal != LWS_EWOULDBLOCK && optVal != WSAEINVAL) {
lwsl_debug("Connect failed SO_ERROR=%d\n", optVal);
return 1;
}
return 0;
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct lws_context *context,
struct lws *wsi, struct lws_pollfd *pfd)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
long networkevents = LWS_POLLHUP | FD_CONNECT;
long networkevents = LWS_POLLOUT | LWS_POLLHUP;
if ((pfd->events & LWS_POLLIN))
networkevents |= LWS_POLLIN;
if ((pfd->events & LWS_POLLOUT))
networkevents |= LWS_POLLOUT;
if (WSAEventSelect(wsi->desc.sockfd,
pt->events[0],
networkevents) != SOCKET_ERROR)
if (WSAEventSelect(wsi->sock,
context->events[wsi->position_in_fds_table + 1],
networkevents) != SOCKET_ERROR)
return 0;
lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO);
@ -492,14 +387,31 @@ lws_plat_change_pollfd(struct lws_context *context,
return 1;
}
LWS_VISIBLE HANDLE
lws_plat_open_file(const char* filename, unsigned long* filelen)
{
HANDLE ret;
WCHAR buffer[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, filename, -1, buffer,
sizeof(buffer) / sizeof(buffer[0]));
ret = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (ret != LWS_INVALID_FILE)
*filelen = GetFileSize(ret, NULL);
return ret;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
{
WCHAR *buffer;
DWORD bufferlen = cnt;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2);
buffer = lws_malloc(bufferlen);
if (!buffer) {
lwsl_err("Out of memory\n");
return NULL;
@ -537,209 +449,3 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
lws_free(buffer);
return ok ? dst : NULL;
}
LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
WCHAR *buffer;
DWORD bufferlen = strlen(src) + 1;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2);
if (!buffer) {
lwsl_err("Out of memory\n");
return -1;
}
if (MultiByteToWideChar(CP_ACP, 0, src, bufferlen, buffer, bufferlen) <= 0) {
lwsl_err("Failed to convert multi byte to wide char\n");
lws_free(buffer);
return -1;
}
if (af == AF_INET) {
struct sockaddr_in dstaddr;
int dstaddrlen = sizeof(dstaddr);
bzero(&dstaddr, sizeof(dstaddr));
dstaddr.sin_family = AF_INET;
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
ok = TRUE;
memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
}
#ifdef LWS_USE_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 dstaddr;
int dstaddrlen = sizeof(dstaddr);
bzero(&dstaddr, sizeof(dstaddr));
dstaddr.sin6_family = AF_INET6;
if (!WSAStringToAddressW(buffer, af, 0, (struct sockaddr *) &dstaddr, &dstaddrlen)) {
ok = TRUE;
memcpy(dst, &dstaddr.sin6_addr, sizeof(dstaddr.sin6_addr));
}
#endif
} else
lwsl_err("Unsupported type\n");
if (!ok) {
int rv = WSAGetLastError();
lwsl_err("WSAAddressToString() : %d\n", rv);
}
lws_free(buffer);
return ok ? 1 : -1;
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename,
const char *vpath, lws_fop_flags_t *flags)
{
HANDLE ret;
WCHAR buf[MAX_PATH];
lws_fop_fd_t fop_fd;
LARGE_INTEGER llFileSize = {0};
MultiByteToWideChar(CP_UTF8, 0, filename, -1, buf, ARRAY_SIZE(buf));
if (((*flags) & 7) == _O_RDONLY) {
ret = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
ret = CreateFileW(buf, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
if (ret == LWS_INVALID_FILE)
goto bail;
fop_fd = malloc(sizeof(*fop_fd));
if (!fop_fd)
goto bail;
fop_fd->fops = fops;
fop_fd->fd = ret;
fop_fd->filesystem_priv = NULL; /* we don't use it */
fop_fd->flags = *flags;
fop_fd->len = GetFileSize(ret, NULL);
if(GetFileSizeEx(ret, &llFileSize))
fop_fd->len = llFileSize.QuadPart;
fop_fd->pos = 0;
return fop_fd;
bail:
return NULL;
}
LWS_VISIBLE int
_lws_plat_file_close(lws_fop_fd_t *fop_fd)
{
HANDLE fd = (*fop_fd)->fd;
free(*fop_fd);
*fop_fd = NULL;
CloseHandle((HANDLE)fd);
return 0;
}
LWS_VISIBLE lws_fileofs_t
_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
LARGE_INTEGER l;
l.QuadPart = offset;
return SetFilePointerEx((HANDLE)fop_fd->fd, l, NULL, FILE_CURRENT);
}
LWS_VISIBLE int
_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
DWORD _amount;
if (!ReadFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) {
*amount = 0;
return 1;
}
fop_fd->pos += _amount;
*amount = (unsigned long)_amount;
return 0;
}
LWS_VISIBLE int
_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t* buf, lws_filepos_t len)
{
DWORD _amount;
if (!WriteFile((HANDLE)fop_fd->fd, buf, (DWORD)len, &_amount, NULL)) {
*amount = 0;
return 1;
}
fop_fd->pos += _amount;
*amount = (unsigned long)_amount;
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int i, n = context->count_threads;
for (i = 0; i < FD_HASHTABLE_MODULUS; i++) {
context->fd_hashtable[i].wsi =
lws_zalloc(sizeof(struct lws*) * context->max_fds);
if (!context->fd_hashtable[i].wsi)
return -1;
}
while (n--) {
pt->events = lws_malloc(sizeof(WSAEVENT) *
(context->fd_limit_per_thread + 1));
if (pt->events == NULL) {
lwsl_err("Unable to allocate events array for %d connections\n",
context->fd_limit_per_thread + 1);
return 1;
}
pt->fds_count = 0;
pt->events[0] = WSACreateEvent();
pt++;
}
context->fd_random = 0;
#ifdef LWS_WITH_PLUGINS
if (info->plugin_dirs)
lws_plat_plugins_init(context, info->plugin_dirs);
#endif
return 0;
}
int kill(int pid, int sig)
{
lwsl_err("Sorry Windows doesn't support kill().");
exit(0);
}
int fork(void)
{
lwsl_err("Sorry Windows doesn't support fork().");
exit(0);
}

View file

@ -36,7 +36,7 @@ static struct huf huf_literal[] = {
/* 0x09 */ { 0xffffea, 24 },
/* 0x0a */ { 0x3ffffffc, 30 },
/* 0x0b */ { 0xfffffe9, 28 },
/* 0x0c */ { 0xfffffea, 28 },
/* 0x0d */ { 0x3ffffffd, 30 },
/* 0x0e */ { 0xfffffeb, 28 },
@ -85,7 +85,7 @@ static struct huf huf_literal[] = {
/* 0x39 */ { 0x1f, 6 },
/* 0x3a */ { 0x5c, 7 },
/* 0x3b */ { 0xfb, 8 },
/* 0x3c */ { 0x7ffc, 15 },
/* 0x3d */ { 0x20, 6 },
/* 0x3e */ { 0xffb, 12 },
@ -134,7 +134,7 @@ static struct huf huf_literal[] = {
/* 0x69 */ { 0x6, 5 },
/* 0x6a */ { 0x74, 7 },
/* 0x6b */ { 0x75, 7 },
/* 0x6c */ { 0x28, 6 },
/* 0x6d */ { 0x29, 6 },
@ -184,7 +184,7 @@ static struct huf huf_literal[] = {
/* 0x99 */ { 0x1fffdc, 21 },
/* 0x9a */ { 0x3fffd8, 22 },
/* 0x9b */ { 0x7fffe5, 23 },
/* 0x9c */ { 0x3fffd9, 22 },
/* 0x9d */ { 0x7fffe6, 23 },
/* 0x9e */ { 0x7fffe7, 23 },
@ -233,7 +233,7 @@ static struct huf huf_literal[] = {
/* 0xc9 */ { 0x3ffffe3, 26 },
/* 0xca */ { 0x3ffffe4, 26 },
/* 0xcb */ { 0x7ffffde, 27 },
/* 0xcc */ { 0x7ffffdf, 27 },
/* 0xcd */ { 0x3ffffe5, 26 },
/* 0xce */ { 0xfffff1, 24 },
@ -282,7 +282,7 @@ static struct huf huf_literal[] = {
/* 0xf9 */ { 0xffffffe, 28 },
/* 0xfa */ { 0x7ffffec, 27 },
/* 0xfb */ { 0x7ffffed, 27 },
/* 0xfc */ { 0x7ffffee, 27 },
/* 0xfd */ { 0x7ffffef, 27 },
/* 0xfe */ { 0x7fffff0, 27 },
@ -338,7 +338,7 @@ int main(void)
int pos = 0;
int biggest = 0;
int fails = 0;
m = 0;
while (m < ARRAY_SIZE(state)) {
for (j = 0; j < PARALLEL; j++) {
@ -391,14 +391,14 @@ again:
if (state[n].state[0]) /* nonterminal */
pos += 2;
walk ++;
}
fprintf(stdout, "static unsigned char lextable[] = {\n");
#define TERMINAL_MASK 0x8000
walk = 0;
pos = 0;
q = 0;
@ -435,9 +435,9 @@ again:
walk++;
continue;
}
j = (state[saw].real_pos - q) >> 1;
if (j > biggest)
biggest = j;
@ -447,7 +447,7 @@ again:
state[n].real_pos, state[saw].real_pos);
return 1;
}
fprintf(stdout, " /* %d */ 0x%02X "
"/* (to 0x%04X state %3d) */,\n",
m,
@ -469,7 +469,7 @@ again:
fprintf(stdout, "0x%02x, ", terms[n]);
}
fprintf(stdout, "\n};\n");
/*
* Try to parse every legal input string
*/
@ -494,7 +494,7 @@ again:
y = walk & 0x7fff;
if (y == 0 && m == 29) {
y |= 0x100;
fprintf(stdout,
fprintf(stdout,
"\n/* state that points to "
"0x100 for disambiguation with "
"0x0 */\n"

View file

@ -22,11 +22,11 @@
* b7 = 0 = 1-byte seq
* 0x08 = fail
* 2-byte seq
* 0x00 - 0x07, then terminal as given in 2nd byte
* 0x00 - 0x07, then terminal as given in 2nd byte
3-byte seq
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
* = 1 = 1-byte seq
* no match: die, match go fwd 1 byte
* no match: die, match go fwd 1 byte
*/
unsigned char lextable[] = {
@ -51,6 +51,7 @@ int next = 1;
int lextable_decode(int pos, char c)
{
while (1) {
if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
if ((lextable[pos] & 0x7f) != c)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -22,79 +22,143 @@
#include "private-libwebsockets.h"
int
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
insert_wsi_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
struct lws_context_per_thread *pt;
struct lws_context *context;
int ret = 0, pa_events = 1;
struct lws_pollfd *pfd;
int sampled_tid, tid;
struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 };
if (!wsi || wsi->position_in_fds_table < 0)
return 0;
if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
/*
* Happening alongside service thread handling POLLOUT.
* The danger is when he is finished, he will disable POLLOUT,
* countermanding what we changed here.
*
* Instead of changing the fds, inform the service thread
* what happened, and ask it to leave POLLOUT active on exit
*/
wsi->leave_pollout_active = 1;
/*
* by definition service thread is not in poll wait, so no need
* to cancel service
*/
lwsl_debug("%s: using leave_pollout_active\n", __func__);
return 0;
if (context->fds_count >= context->max_fds) {
lwsl_err("Too many fds (%d)\n", context->max_fds);
return 1;
}
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
assert(wsi->position_in_fds_table >= 0 &&
wsi->position_in_fds_table < pt->fds_count);
pfd = &pt->fds[wsi->position_in_fds_table];
pa->fd = wsi->desc.sockfd;
pa->prev_events = pfd->events;
pa->events = pfd->events = (pfd->events & ~_and) | _or;
//lwsl_notice("%s: wsi %p, posin %d. from %d -> %d\n", __func__, wsi, wsi->position_in_fds_table, pa->prev_events, pa->events);
if (wsi->http2_substream)
return 0;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
ret = -1;
goto bail;
#ifndef _WIN32
if (wsi->sock >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n",
wsi->sock, context->max_fds);
return 1;
}
#endif
if (_and & LWS_POLLIN) {
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ);
assert(wsi);
assert(wsi->sock >= 0);
lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n",
wsi, wsi->sock, context->fds_count);
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0))
return -1;
insert_wsi(context, wsi);
wsi->position_in_fds_table = context->fds_count;
context->fds[context->fds_count].fd = wsi->sock;
context->fds[context->fds_count].events = LWS_POLLIN;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_ADD_POLL_FD, wsi->user_space, (void *) &pa, 0))
return -1;
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *)&pa, 0))
return -1;
return 0;
}
int
remove_wsi_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
int m;
struct libwebsocket_pollargs pa = { wsi->sock, 0, 0 };
lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
--context->fds_count;
#ifndef _WIN32
if (wsi->sock > context->max_fds) {
lwsl_err("Socket fd %d too high (%d)\n",
wsi->sock, context->max_fds);
return 1;
}
if (_or & LWS_POLLIN) {
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
}
if (_and & LWS_POLLOUT) {
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_WRITE);
}
if (_or & LWS_POLLOUT) {
lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_WRITE);
#endif
lwsl_info("%s: wsi=%p, sock=%d, fds pos=%d\n", __func__,
wsi, wsi->sock, wsi->position_in_fds_table);
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *)&pa, 0))
return -1;
m = wsi->position_in_fds_table; /* replace the contents for this */
/* have the last guy take up the vacant slot */
context->fds[m] = context->fds[context->fds_count];
lws_plat_delete_socket_from_fds(context, wsi, m);
/*
* end guy's fds_lookup entry remains unchanged
* (still same fd pointing to same wsi)
*/
/* end guy's "position in fds table" changed */
wsi_from_fd(context,context->fds[context->fds_count].fd)->
position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
delete_from_fd(context,wsi->sock);
/* removed wsi has no position any more */
wsi->position_in_fds_table = -1;
/* remove also from external POLL support via protocol 0 */
if (wsi->sock) {
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_DEL_POLL_FD, wsi->user_space,
(void *) &pa, 0))
return -1;
}
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
return 0;
}
int
lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or)
{
struct libwebsocket_context *context;
int tid;
int sampled_tid;
struct libwebsocket_pollfd *pfd;
struct libwebsocket_pollargs pa;
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
return 1;
context = wsi->protocol->owning_server;
if (!context)
return 1;
pfd = &context->fds[wsi->position_in_fds_table];
pa.fd = wsi->sock;
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0))
return -1;
pa.prev_events = pfd->events;
pa.events = pfd->events = (pfd->events & ~_and) | _or;
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *) &pa, 0))
return -1;
/*
* if we changed something in this pollfd...
@ -103,457 +167,132 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* ... and the service thread is waiting ...
* then cancel it to force a restart with our changed events
*/
#if LWS_POSIX
pa_events = pa->prev_events != pa->events;
#endif
if (pa_events) {
if (pa.prev_events != pa.events) {
if (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__);
ret = -1;
goto bail;
return 1;
}
sampled_tid = context->service_tid;
if (sampled_tid) {
tid = wsi->vhost->protocols[0].callback(wsi,
tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) {
ret = -1;
goto bail;
}
if (tid == -1)
return -1;
if (tid != sampled_tid)
lws_cancel_service_pt(wsi);
libwebsocket_cancel_service(context);
}
}
bail:
return ret;
}
#ifndef LWS_NO_SERVER
static void
lws_accept_modulation(struct lws_context_per_thread *pt, int allow)
{
// multithread listen seems broken
#if 0
struct lws_vhost *vh = context->vhost_list;
struct lws_pollargs pa1;
while (vh) {
if (allow)
_lws_change_pollfd(pt->wsi_listening,
0, LWS_POLLIN, &pa1);
else
_lws_change_pollfd(pt->wsi_listening,
LWS_POLLIN, 0, &pa1);
vh = vh->vhost_next;
}
#endif
}
#endif
int
insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_pollargs pa = { wsi->desc.sockfd, LWS_POLLIN, 0 };
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int ret = 0;
lwsl_debug("%s: %p: tsi=%d, sock=%d, pos-in-fds=%d\n",
__func__, wsi, wsi->tsi, wsi->desc.sockfd, pt->fds_count);
if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) {
lwsl_err("Too many fds (%d vs %d)\n", context->max_fds,
context->fd_limit_per_thread );
return 1;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n",
wsi->desc.sockfd, context->max_fds);
return 1;
}
#endif
assert(wsi);
assert(wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd));
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 1))
if (context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0))
return -1;
lws_pt_lock(pt);
pt->count_conns++;
insert_wsi(context, wsi);
#if defined(LWS_WITH_ESP8266)
if (wsi->position_in_fds_table == -1)
#endif
wsi->position_in_fds_table = pt->fds_count;
// lwsl_notice("%s: %p: setting posinfds %d\n", __func__, wsi, wsi->position_in_fds_table);
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
#if LWS_POSIX
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
#else
pt->fds[wsi->position_in_fds_table].events = 0; // LWS_POLLIN;
#endif
pa.events = pt->fds[pt->fds_count].events;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
/* if no more room, defeat accepts on this thread */
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 0);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1))
ret = -1;
return ret;
}
int
remove_wsi_socket_from_fds(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_pollargs pa = { wsi->desc.sockfd, 0, 0 };
#if !defined(LWS_WITH_ESP8266)
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws *end_wsi;
int v;
#endif
int m, ret = 0;
if (wsi->parent_carries_io) {
lws_same_vh_protocol_remove(wsi);
return 0;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, context->max_fds);
return 1;
}
#endif
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1))
return -1;
lws_same_vh_protocol_remove(wsi);
/* the guy who is to be deleted's slot index in pt->fds */
m = wsi->position_in_fds_table;
#if !defined(LWS_WITH_ESP8266)
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE | LWS_EV_PREPARE_DELETION);
lws_pt_lock(pt);
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
pt->fds_count, pt->fds[pt->fds_count].fd);
/* have the last guy take up the now vacant slot */
pt->fds[m] = pt->fds[pt->fds_count - 1];
#endif
/* this decrements pt->fds_count */
lws_plat_delete_socket_from_fds(context, wsi, m);
#if !defined(LWS_WITH_ESP8266)
v = (int) pt->fds[m].fd;
/* end guy's "position in fds table" is now the deletion guy's old one */
end_wsi = wsi_from_fd(context, v);
if (!end_wsi) {
lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", (int)pt->fds[m].fd, m, pt->fds_count);
assert(0);
} else
end_wsi->position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
delete_from_fd(context, wsi->desc.sockfd);
/* removed wsi has no position any more */
wsi->position_in_fds_table = -1;
/* remove also from external POLL support via protocol 0 */
if (lws_socket_is_valid(wsi->desc.sockfd))
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
if (!context->being_destroyed)
/* if this made some room, accept connects on this thread */
if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 1);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
ret = -1;
#endif
return ret;
return 0;
}
int
lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
struct lws_context_per_thread *pt;
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
return 1;
context = lws_get_context(wsi);
if (!context)
return 1;
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
pt = &context->pt[(int)wsi->tsi];
lws_pt_lock(pt);
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
return ret;
}
/**
* libwebsocket_callback_on_writable() - Request a callback when this socket
* becomes able to be written to without
* blocking
*
* @context: libwebsockets context
* @wsi: Websocket connection instance to get callback for
*/
LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
libwebsocket_callback_on_writable(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
struct lws_context_per_thread *pt;
#ifdef LWS_USE_HTTP2
struct lws *network_wsi, *wsi2;
struct libwebsocket *network_wsi, *wsi2;
int already;
#endif
if (wsi->state == LWSS_SHUTDOWN)
return 0;
if (wsi->socket_is_permanently_unusable)
return 0;
if (wsi->parent_carries_io) {
int n = lws_callback_on_writable(wsi->parent);
if (n < 0)
return n;
wsi->parent_pending_cb_on_writable = 1;
return 1;
}
pt = &wsi->context->pt[(int)wsi->tsi];
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1);
#if defined(LWS_WITH_STATS)
if (!wsi->active_writable_req_us) {
wsi->active_writable_req_us = time_in_microseconds();
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1);
}
#endif
#ifdef LWS_USE_HTTP2
lwsl_info("%s: %p\n", __func__, wsi);
if (wsi->mode != LWSCM_HTTP2_SERVING)
if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING)
goto network_sock;
if (wsi->u.http2.requested_POLLOUT) {
lwsl_info("already pending writable\n");
return 1;
}
if (wsi->u.http2.tx_credit <= 0) {
/*
* other side is not able to cope with us sending
* anything so no matter if we have POLLOUT on our side.
*
*
* Delay waiting for our POLLOUT until peer indicates he has
* space for more using tx window command in http2 layer
*/
lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi,
wsi->u.http2.tx_credit);
lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi, wsi->u.http2.tx_credit);
wsi->u.http2.waiting_tx_credit = 1;
return 0;
}
network_wsi = lws_http2_get_network_wsi(wsi);
already = network_wsi->u.http2.requested_POLLOUT;
/* mark everybody above him as requesting pollout */
wsi2 = wsi;
while (wsi2) {
wsi2->u.http2.requested_POLLOUT = 1;
lwsl_info("mark %p pending writable\n", wsi2);
wsi2 = wsi2->u.http2.parent_wsi;
}
/* for network action, act only on the network wsi */
wsi = network_wsi;
if (already)
return 1;
network_sock:
#endif
if (lws_ext_cb_active(wsi, LWS_EXT_CB_REQUEST_ON_WRITEABLE, NULL, 0))
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
return 1;
if (wsi->position_in_fds_table < 0) {
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock);
return -1;
}
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1;
lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE);
return 1;
}
/*
* stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list
/**
* libwebsocket_callback_on_writable_all_protocol() - Request a callback for
* all connections using the given protocol when it
* becomes possible to write to each socket without
* blocking in turn.
*
* X <-> B
* X <-> pAn <-> pB
*
* Illegal to attach more than once without detach inbetween
* @protocol: Protocol whose connections will get callbacks
*/
void
lws_same_vh_protocol_insert(struct lws *wsi, int n)
{
//lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n",
// __func__,
// wsi->vhost->same_vh_protocol_list[n],
// wsi->same_vh_protocol_prev);
if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) {
lws_same_vh_protocol_remove(wsi);
lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
}
wsi->same_vh_protocol_prev = /* guy who points to us */
&wsi->vhost->same_vh_protocol_list[n];
wsi->same_vh_protocol_next = /* old first guy is our next */
wsi->vhost->same_vh_protocol_list[n];
/* we become the new first guy */
wsi->vhost->same_vh_protocol_list[n] = wsi;
if (wsi->same_vh_protocol_next)
/* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next;
}
void
lws_same_vh_protocol_remove(struct lws *wsi)
{
/*
* detach ourselves from vh protocol list if we're on one
* A -> B -> C
* A -> C , or, B -> C, or A -> B
*
* OK to call on already-detached wsi
*/
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
if (wsi->same_vh_protocol_prev) {
assert (*(wsi->same_vh_protocol_prev) == wsi);
lwsl_info("have prev %p, setting him to our next %p\n",
wsi->same_vh_protocol_prev,
wsi->same_vh_protocol_next);
/* guy who pointed to us should point to our next */
*(wsi->same_vh_protocol_prev) = wsi->same_vh_protocol_next;
}
/* our next should point back to our prev */
if (wsi->same_vh_protocol_next) {
wsi->same_vh_protocol_next->same_vh_protocol_prev =
wsi->same_vh_protocol_prev;
}
wsi->same_vh_protocol_prev = NULL;
wsi->same_vh_protocol_next = NULL;
}
LWS_VISIBLE int
lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
const struct lws_protocols *protocol)
libwebsocket_callback_on_writable_all_protocol(
const struct libwebsocket_protocols *protocol)
{
struct lws *wsi;
if (protocol < vhost->protocols ||
protocol >= (vhost->protocols + vhost->count_protocols)) {
lwsl_err("%s: protocol %p is not from vhost %p (%p - %p)\n",
__func__, protocol, vhost->protocols, vhost,
(vhost->protocols + vhost->count_protocols));
return -1;
}
wsi = vhost->same_vh_protocol_list[protocol - vhost->protocols];
//lwsl_notice("%s: protocol %p, start wsi %p\n", __func__, protocol, wsi);
while (wsi) {
//lwsl_notice("%s: protocol %p, this wsi %p (wsi->protocol=%p)\n",
// __func__, protocol, wsi, wsi->protocol);
assert(wsi->protocol == protocol);
assert(*wsi->same_vh_protocol_prev == wsi);
if (wsi->same_vh_protocol_next) {
// lwsl_err("my next says %p\n", wsi->same_vh_protocol_next);
// lwsl_err("my next's prev says %p\n",
// wsi->same_vh_protocol_next->same_vh_protocol_prev);
assert(wsi->same_vh_protocol_next->same_vh_protocol_prev == &wsi->same_vh_protocol_next);
}
//lwsl_notice(" apv: %p\n", wsi);
lws_callback_on_writable(wsi);
wsi = wsi->same_vh_protocol_next;
}
return 0;
}
LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
struct lws_vhost *vhost = context->vhost_list;
struct libwebsocket_context *context = protocol->owning_server;
int n;
struct libwebsocket *wsi;
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==
vhost->protocols[n].callback &&
!strcmp(protocol->name, vhost->protocols[n].name))
break;
if (n != vhost->count_protocols)
lws_callback_on_writable_all_protocol_vhost(
vhost, &vhost->protocols[n]);
vhost = vhost->vhost_next;
for (n = 0; n < context->fds_count; n++) {
wsi = wsi_from_fd(context,context->fds[n].fd);
if (!wsi)
continue;
if (wsi->protocol == protocol)
libwebsocket_callback_on_writable(context, wsi);
}
return 0;

File diff suppressed because it is too large Load diff

View file

@ -1,211 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* RFC7233 ranges parser
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/*
* RFC7233 examples
*
* o The first 500 bytes (byte offsets 0-499, inclusive):
*
* bytes=0-499
*
* o The second 500 bytes (byte offsets 500-999, inclusive):
*
* bytes=500-999
*
* o The final 500 bytes (byte offsets 9500-9999, inclusive):
*
* bytes=-500
*
* Or:
*
* bytes=9500-
*
* o The first and last bytes only (bytes 0 and 9999):
*
* bytes=0-0,-1
*
* o Other valid (but not canonical) specifications of the second 500
* bytes (byte offsets 500-999, inclusive):
*
* bytes=500-600,601-999
* bytes=500-700,601-999
*/
/*
* returns 1 if the range struct represents a usable range
* if no ranges header, you get one of these for the whole
* file. Otherwise you get one for each valid range in the
* header.
*
* returns 0 if no further valid range forthcoming; rp->state
* may be LWSRS_SYNTAX or LWSRS_COMPLETED
*/
int
lws_ranges_next(struct lws_range_parsing *rp)
{
static const char * const beq = "bytes=";
char c;
while (1) {
c = rp->buf[rp->pos];
switch (rp->state) {
case LWSRS_SYNTAX:
case LWSRS_COMPLETED:
return 0;
case LWSRS_NO_ACTIVE_RANGE:
rp->state = LWSRS_COMPLETED;
return 0;
case LWSRS_BYTES_EQ: // looking for "bytes="
if (c != beq[rp->pos]) {
rp->state = LWSRS_SYNTAX;
return -1;
}
if (rp->pos == 5)
rp->state = LWSRS_FIRST;
break;
case LWSRS_FIRST:
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_STARTING;
// fallthru
case LWSRS_STARTING:
if (c == '-') {
rp->state = LWSRS_ENDING;
break;
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->start = (rp->start * 10) + (c - '0');
rp->start_valid = 1;
break;
case LWSRS_ENDING:
if (c == ',' || c == '\0') {
rp->state = LWSRS_FIRST;
if (c == ',')
rp->pos++;
/* by the end of this, start and end are always valid if the range still is */
if (!rp->start_valid) { /* eg, -500 */
if (rp->end > rp->extent)
rp->end = rp->extent;
rp->start = rp->extent - rp->end;
rp->end = rp->extent - 1;
} else
if (!rp->end_valid)
rp->end = rp->extent - 1;
rp->did_try = 1;
/* end must be >= start or ignore it */
if (rp->end < rp->start) {
if (c == ',')
break;
rp->state = LWSRS_COMPLETED;
return 0;
}
return 1; /* issue range */
}
if (!(c >= '0' && c <= '9')) {
rp->state = LWSRS_SYNTAX;
return 0;
}
rp->end = (rp->end * 10) + (c - '0');
rp->end_valid = 1;
break;
}
rp->pos++;
}
}
void
lws_ranges_reset(struct lws_range_parsing *rp)
{
rp->pos = 0;
rp->ctr = 0;
rp->start = 0;
rp->end = 0;
rp->start_valid = 0;
rp->end_valid = 0;
rp->state = LWSRS_BYTES_EQ;
}
/*
* returns count of valid ranges
*/
int
lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp,
unsigned long long extent)
{
rp->agg = 0;
rp->send_ctr = 0;
rp->inside = 0;
rp->count_ranges = 0;
rp->did_try = 0;
lws_ranges_reset(rp);
rp->state = LWSRS_COMPLETED;
rp->extent = extent;
if (lws_hdr_copy(wsi, (char *)rp->buf, sizeof(rp->buf),
WSI_TOKEN_HTTP_RANGE) <= 0)
return 0;
rp->state = LWSRS_BYTES_EQ;
while (lws_ranges_next(rp)) {
rp->count_ranges++;
rp->agg += rp->end - rp->start + 1;
}
lwsl_debug("%s: count %d\n", __func__, rp->count_ranges);
lws_ranges_reset(rp);
if (rp->did_try && !rp->count_ranges)
return -1; /* "not satisfiable */
lws_ranges_next(rp);
return rp->count_ranges;
}

View file

@ -1,52 +0,0 @@
#include "private-libwebsockets.h"
LWS_EXTERN struct lws_rewrite *
lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to)
{
struct lws_rewrite *r = lws_malloc(sizeof(*r));
if (!r) {
lwsl_err("OOM\n");
return NULL;
}
if (hubbub_parser_create("UTF-8", false, &r->parser) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
r->from = from;
r->from_len = strlen(from);
r->to = to;
r->to_len = strlen(to);
r->params.token_handler.handler = cb;
r->wsi = wsi;
r->params.token_handler.pw = (void *)r;
if (hubbub_parser_setopt(r->parser, HUBBUB_PARSER_TOKEN_HANDLER,
&r->params) != HUBBUB_OK) {
lws_free(r);
return NULL;
}
return r;
}
LWS_EXTERN int
lws_rewrite_parse(struct lws_rewrite *r,
const unsigned char *in, int in_len)
{
if (hubbub_parser_parse_chunk(r->parser, in, in_len) != HUBBUB_OK)
return -1;
return 0;
}
LWS_EXTERN void
lws_rewrite_destroy(struct lws_rewrite *r)
{
hubbub_parser_destroy(r->parser);
lws_free(r);
}

View file

@ -1,224 +0,0 @@
/*
* Copyright (C) 2017 National Institute of Advanced Industrial Science
* and Technology (AIST)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of AIST nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include "romfs.h"
#include "esp_spi_flash.h"
#define RFS_STRING_MAX 96
static u32_be_t cache[(RFS_STRING_MAX + 32) / 4];
static romfs_inode_t ci = (romfs_inode_t)cache;
static romfs_t cr = (romfs_t)cache;
static void
set_cache(romfs_inode_t inode, size_t len)
{
spi_flash_read((uint32_t)inode, cache, len);
}
static uint32_t
ntohl(const u32_be_t be)
{
return ((be >> 24) & 0xff) |
((be >> 16) & 0xff) << 8 |
((be >> 8) & 0xff) << 16 |
(be & 0xff) << 24;
}
static romfs_inode_t
romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path);
static int
plus_padding(const uint8_t *s)
{
int n;
set_cache((romfs_inode_t)s, RFS_STRING_MAX);
n = strlen((const char *)cache);
if (!(n & 15))
n += 0x10;
return (n + 15) & ~15;
}
static romfs_inode_t
skip_and_pad(romfs_inode_t ri)
{
const uint8_t *p = ((const uint8_t *)ri) + sizeof(*ri);
return (romfs_inode_t)(p + plus_padding(p));
}
size_t
romfs_mount_check(romfs_t romfs)
{
set_cache((romfs_inode_t)romfs, sizeof(*romfs));
if (cr->magic1 != 0x6d6f722d ||
cr->magic2 != 0x2d736631)
return 0;
return ntohl(cr->size);
}
static romfs_inode_t
romfs_symlink(romfs_t romfs, romfs_inode_t level, romfs_inode_t i)
{
const char *p = (const char *)skip_and_pad(i);
if (*p == '/') {
level = skip_and_pad((romfs_inode_t)romfs);
p++;
}
return romfs_lookup(romfs, level, p);
}
static romfs_inode_t
dir_link(romfs_t romfs, romfs_inode_t i)
{
set_cache(i, sizeof(*i));
return (romfs_inode_t)((const uint8_t *)romfs +
ntohl(ci->dir_start));
}
static romfs_inode_t
romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
{
romfs_inode_t level, i = start, i_in;
const char *p, *n, *cp;
uint32_t next_be;
if (start == (romfs_inode_t)romfs)
i = skip_and_pad((romfs_inode_t)romfs);
level = i;
while (i != (romfs_inode_t)romfs) {
p = path;
n = ((const char *)i) + sizeof(*i);
i_in = i;
set_cache(i, sizeof(*i));
next_be = ci->next;
cp = (const char *)cache;
set_cache((romfs_inode_t)n, RFS_STRING_MAX);
while (*p && *p != '/' && *cp && *p == *cp && (p - path) < RFS_STRING_MAX) {
p++;
n++;
cp++;
}
if (!*cp && (!*p || *p == '/') &&
(ntohl(next_be) & 7) == RFST_HARDLINK) {
set_cache(i, sizeof(*i));
return (romfs_inode_t)
((const uint8_t *)romfs +
(ntohl(ci->dir_start) & ~15));
}
if (!*p && !*cp) {
set_cache(i, sizeof(*i));
if ((ntohl(ci->next) & 7) == RFST_SYMLINK) {
i = romfs_symlink(romfs, level, i);
continue;
}
return i;
}
if (!*p && *cp == '/')
return NULL;
if (*p == '/' && !*cp) {
set_cache(i, sizeof(*i));
switch (ntohl(ci->next) & 7) {
case RFST_SYMLINK:
i = romfs_symlink(romfs, level, i);
if (!i)
return NULL;
i = dir_link(romfs, i);
while (*path != '/' && *path)
path++;
if (!*path)
return NULL;
path++;
continue;
case RFST_DIR:
path = p + 1;
i = dir_link(romfs, i);
break;
default:
path = p + 1;
i = skip_and_pad(i);
break;
}
level = i;
continue;
}
set_cache(i, sizeof(*i));
if (!(ntohl(ci->next) & ~15))
return NULL;
i = (romfs_inode_t)((const uint8_t *)romfs +
(ntohl(ci->next) & ~15));
if (i == i_in)
return NULL;
}
return NULL;
}
const void *
romfs_get_info(romfs_t romfs, const char *path, size_t *len, size_t *csum)
{
romfs_inode_t i;
if (*path == '/')
path++;
i = romfs_lookup(romfs, (romfs_inode_t)romfs, path);
if (!i)
return NULL;
set_cache(i, sizeof(*i));
*len = ntohl(ci->size);
if (csum)
*csum = ntohl(ci->checksum);
return (void *)skip_and_pad(i);
}

View file

@ -1,63 +0,0 @@
/*
* Copyright (C) 2017 National Institute of Advanced Industrial Science
* and Technology (AIST)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of AIST nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
typedef uint32_t u32_be_t;
struct romfs_superblock {
u32_be_t magic1;
u32_be_t magic2;
u32_be_t size;
u32_be_t checksum;
};
struct romfs_i {
u32_be_t next;
u32_be_t dir_start;
u32_be_t size;
u32_be_t checksum;
};
enum {
RFST_HARDLINK = 0,
RFST_DIR = 1,
RFST_SYMLINK = 3,
};
typedef const struct romfs_i *romfs_inode_t;
typedef const struct romfs_superblock *romfs_t;
const void *
romfs_get_info(romfs_t romfs, const char *path, size_t *len, size_t *csum);
size_t
romfs_mount_check(romfs_t romfs);

View file

@ -22,26 +22,23 @@
#include "private-libwebsockets.h"
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
#ifndef LWS_NO_EXTENSIONS
static int
lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
LWS_VISIBLE int
lws_extension_server_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi, char **p)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char ext_name[64], *args, *end = (*p) + budget - 1;
const struct lws_ext_options *opts, *po;
const struct lws_extension *ext;
struct lws_ext_option_arg oa;
int n, m, more = 1;
int ext_count = 0;
char ignore;
int n;
char *c;
char ext_name[128];
struct libwebsocket_extension *ext;
int ext_count = 0;
int more = 1;
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
return 0;
@ -50,48 +47,24 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
* and go through them
*/
if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
WSI_TOKEN_EXTENSIONS) < 0)
if (lws_hdr_copy(wsi, (char *)context->service_buffer,
sizeof(context->service_buffer),
WSI_TOKEN_EXTENSIONS) < 0)
return 1;
c = (char *)pt->serv_buf;
c = (char *)context->service_buffer;
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
wsi->count_act_ext = 0;
ignore = 0;
wsi->count_active_extensions = 0;
n = 0;
args = NULL;
/*
* We may get a simple request
*
* Sec-WebSocket-Extensions: permessage-deflate
*
* or an elaborated one with requested options
*
* Sec-WebSocket-Extensions: permessage-deflate; \
* server_no_context_takeover; \
* client_no_context_takeover
*/
while (more) {
if (*c && (*c != ',' && *c != '\t')) {
if (*c == ';') {
ignore = 1;
args = c + 1;
}
if (ignore || *c == ' ') {
c++;
continue;
}
if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
ignore = 0;
if (!*c)
more = 0;
else {
@ -100,12 +73,9 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
continue;
}
while (args && *args && *args == ' ')
args++;
/* check a client's extension against our support */
ext = wsi->vhost->extensions;
ext = wsi->protocol->owning_server->extensions;
while (ext && ext->callback) {
@ -115,132 +85,94 @@ lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
}
/*
* oh, we do support this one he asked for... but let's
* confirm he only gave it once
*/
for (m = 0; m < wsi->count_act_ext; m++)
if (wsi->active_extensions[m] == ext) {
lwsl_info("extension mentioned twice\n");
return 1; /* shenanigans */
}
/*
* ask user code if it's OK to apply it on this
* oh, we do support this one he
* asked for... but let's ask user
* code if it's OK to apply it on this
* particular connection + protocol
*/
m = (wsi->protocol->callback)(wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
n = wsi->protocol->owning_server->
protocols[0].callback(
wsi->protocol->owning_server,
wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
/*
* zero return from callback means go ahead and allow
* the extension, it's what we get if the callback is
* zero return from callback means
* go ahead and allow the extension,
* it's what we get if the callback is
* unhandled
*/
if (m) {
if (n) {
ext++;
continue;
}
/* 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 */
wsi->active_extensions[wsi->count_act_ext] = ext;
wsi->active_extensions_user[
wsi->count_active_extensions] =
lws_zalloc(ext->per_session_data_size);
if (wsi->active_extensions_user[
wsi->count_active_extensions] == NULL) {
lwsl_err("Out of mem\n");
return 1;
}
wsi->active_extensions[
wsi->count_active_extensions] = ext;
/* allow him to construct his context */
if (ext->callback(lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_CONSTRUCT,
(void *)&wsi->act_ext_user[
wsi->count_act_ext],
(void *)&opts, 0)) {
lwsl_notice("ext %s failed construction\n",
ext_name);
ext_count--;
ext++;
ext->callback(wsi->protocol->owning_server,
ext, wsi,
LWS_EXT_CALLBACK_CONSTRUCT,
wsi->active_extensions_user[
wsi->count_active_extensions], NULL, 0);
continue;
}
if (ext_count > 1)
*(*p)++ = ',';
else
LWS_CPYAPP(*p,
"\x0d\x0aSec-WebSocket-Extensions: ");
*p += lws_snprintf(*p, (end - *p), "%s", ext_name);
/*
* go through the options trying to apply the
* recognized ones
*/
lwsl_debug("ext args %s", args);
while (args && *args && *args != ',') {
while (*args == ' ')
args++;
po = opts;
while (po->name) {
lwsl_debug("'%s' '%s'\n", po->name, args);
/* only support arg-less options... */
if (po->type == EXTARG_NONE &&
!strncmp(args, po->name,
strlen(po->name))) {
oa.option_name = NULL;
oa.option_index = po - opts;
oa.start = NULL;
lwsl_debug("setting %s\n", po->name);
if (!ext->callback(
lws_get_context(wsi), ext, wsi,
LWS_EXT_CB_OPTION_SET,
wsi->act_ext_user[
wsi->count_act_ext],
&oa, (end - *p))) {
*p += lws_snprintf(*p, (end - *p), "; %s", po->name);
lwsl_debug("adding option %s\n", po->name);
}
}
po++;
}
while (*args && *args != ',' && *args != ';')
args++;
}
wsi->count_act_ext++;
lwsl_parser("count_act_ext <- %d\n",
wsi->count_act_ext);
wsi->count_active_extensions++;
lwsl_parser("count_active_extensions <- %d\n",
wsi->count_active_extensions);
ext++;
}
n = 0;
args = NULL;
}
return 0;
}
#endif
int
handshake_0405(struct lws_context *context, struct lws *wsi)
handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
unsigned char hash[20];
int n, accept_len;
int n;
char *response;
char *p;
int accept_len;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
lwsl_parser("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
MAX_WEBSOCKET_04_KEY_LEN) {
lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
goto bail;
}
@ -249,54 +181,51 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
* since key length is restricted above (currently 128), cannot
* overflow
*/
n = sprintf((char *)pt->serv_buf,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
n = sprintf((char *)context->service_buffer,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
lws_SHA1(pt->serv_buf, n, hash);
libwebsockets_SHA1(context->service_buffer, n, hash);
accept_len = lws_b64_encode_string((char *)hash, 20,
(char *)pt->serv_buf, context->pt_serv_buf_size);
(char *)context->service_buffer,
sizeof(context->service_buffer));
if (accept_len < 0) {
lwsl_warn("Base64 encoded hash too long\n");
goto bail;
}
/* allocate the per-connection user memory (if any) */
if (lws_ensure_user_space(wsi))
if (libwebsocket_ensure_user_space(wsi))
goto bail;
/* create the response packet */
/* make a buffer big enough for everything */
response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN + LWS_SEND_BUFFER_PRE_PADDING;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Accept: ");
strcpy(p, (char *)pt->serv_buf);
strcpy(p, (char *)context->service_buffer);
p += accept_len;
/* we can only return the protocol header if:
* - one came in, and ... */
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
/* - it is not an empty string */
wsi->protocol->name &&
wsi->protocol->name[0]) {
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
if (n < 0)
goto bail;
p += n;
}
#ifndef LWS_NO_EXTENSIONS
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list.
*
* Give him a limited write bugdet
* enable on this connection, and give him back the list
*/
if (lws_extension_server_handshake(wsi, &p, 192))
if (lws_extension_server_handshake(context, wsi, &p))
goto bail;
#endif
@ -305,18 +234,19 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
/* end of response packet */
LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
response, p - response)) {
if (!lws_any_extension_handled(context, wsi,
LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
response, p - response)) {
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
#ifdef DEBUG
fwrite(response, 1, p - response, stderr);
#endif
n = lws_write(wsi, (unsigned char *)response,
p - response, LWS_WRITE_HTTP_HEADERS);
n = libwebsocket_write(wsi, (unsigned char *)response,
p - response, LWS_WRITE_HTTP_HEADERS);
if (n != (p - response)) {
lwsl_debug("handshake_0405: ERROR writing to socket\n");
goto bail;
@ -326,26 +256,22 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
/* alright clean up and set ourselves into established state */
wsi->state = LWSS_ESTABLISHED;
wsi->state = WSI_STATE_ESTABLISHED;
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
{
const char * uri_ptr =
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
const struct lws_http_mount *hit =
lws_find_mount(wsi, uri_ptr, uri_len);
if (hit && hit->cgienv &&
wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
wsi->user_space, (void *)hit->cgienv, 0))
return 1;
}
/* notify user code that we're ready to roll */
if (wsi->protocol->callback)
wsi->protocol->callback(wsi->protocol->owning_server,
wsi, LWS_CALLBACK_ESTABLISHED,
wsi->user_space, NULL, 0);
return 0;
bail:
/* caller will free up his parsing allocations */
/* free up his parsing allocations */
lws_free_header_table(wsi);
return -1;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -99,6 +99,7 @@ static const unsigned int _K[] =
sha1_step(ctxt); \
}
static void sha1_step __P((struct sha1_ctxt *));
static void
sha1_step(struct sha1_ctxt *ctxt)
@ -189,7 +190,7 @@ sha1_step(struct sha1_ctxt *ctxt)
/*------------------------------------------------------------*/
static void
_sha1_init(struct sha1_ctxt *ctxt)
sha1_init(struct sha1_ctxt *ctxt)
{
bzero(ctxt, sizeof(struct sha1_ctxt));
H(0) = 0x67452301;
@ -211,14 +212,14 @@ sha1_pad(struct sha1_ctxt *ctxt)
padlen = 64 - padstart;
if (padlen < 8) {
bzero(&ctxt->m.b8[padstart], padlen);
COUNT += (unsigned char)padlen;
COUNT += padlen;
COUNT %= 64;
sha1_step(ctxt);
padstart = COUNT % 64; /* should be 0 */
padlen = 64 - padstart; /* should be 64 */
}
bzero(&ctxt->m.b8[padstart], padlen - 8);
COUNT += ((unsigned char)padlen - 8);
COUNT += (padlen - 8);
COUNT %= 64;
#if BYTE_ORDER == BIG_ENDIAN
PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
@ -249,7 +250,7 @@ sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len)
copysiz = (gaplen < len - off) ? gaplen : len - off;
memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz);
COUNT += (unsigned char)copysiz;
COUNT += copysiz;
COUNT %= 64;
ctxt->c.b64[0] += copysiz * 8;
if (COUNT % 64 == 0)
@ -286,11 +287,11 @@ sha1_result(struct sha1_ctxt *ctxt, void *digest0)
*/
LWS_VISIBLE unsigned char *
lws_SHA1(const unsigned char *d, size_t n, unsigned char *md)
libwebsockets_SHA1(const unsigned char *d, size_t n, unsigned char *md)
{
struct sha1_ctxt ctx;
_sha1_init(&ctx);
sha1_init(&ctx);
sha1_loop(&ctx, d, n);
sha1_result(&ctx, (void *)md);

View file

@ -1,241 +0,0 @@
/*
* SMTP support for libwebsockets
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
static unsigned int
lwsgs_now_secs(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec;
}
static void
ccb(uv_handle_t* handle)
{
}
static void
alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
struct lws_email *email = (struct lws_email *)handle->data;
*buf = uv_buf_init(email->email_buf, sizeof(email->email_buf) - 1);
}
static void
on_write_end(uv_write_t *req, int status) {
lwsl_notice("%s\n", __func__);
if (status == -1) {
fprintf(stderr, "error on_write_end");
return;
}
}
static void
lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
{
struct lws_email *email = (struct lws_email *)s->data;
static const short retcodes[] = {
0, /* idle */
0, /* connecting */
220, /* connected */
250, /* helo */
250, /* from */
250, /* to */
354, /* data */
250, /* body */
221, /* quit */
};
uv_write_t write_req;
uv_buf_t wbuf;
int n;
if (nread >= 0)
email->email_buf[nread] = '\0';
lwsl_notice("%s: %s\n", __func__, buf->base);
if (nread == -1) {
lwsl_err("%s: failed\n", __func__);
return;
}
n = atoi(buf->base);
if (n != retcodes[email->estate]) {
lwsl_err("%s: bad response from server\n", __func__);
goto close_conn;
}
switch (email->estate) {
case LGSSMTP_CONNECTED:
n = sprintf(email->content, "HELO %s\n", email->email_helo);
email->estate = LGSSMTP_SENT_HELO;
break;
case LGSSMTP_SENT_HELO:
n = sprintf(email->content, "MAIL FROM: <%s>\n", email->email_from);
email->estate = LGSSMTP_SENT_FROM;
break;
case LGSSMTP_SENT_FROM:
n = sprintf(email->content, "RCPT TO: <%s>\n", email->email_to);
email->estate = LGSSMTP_SENT_TO;
break;
case LGSSMTP_SENT_TO:
n = sprintf(email->content, "DATA\n");
email->estate = LGSSMTP_SENT_DATA;
break;
case LGSSMTP_SENT_DATA:
if (email->on_get_body(email, email->content, email->max_content_size))
return;
n = strlen(email->content);
email->estate = LGSSMTP_SENT_BODY;
break;
case LGSSMTP_SENT_BODY:
n = sprintf(email->content, "quit\n");
email->estate = LGSSMTP_SENT_QUIT;
break;
case LGSSMTP_SENT_QUIT:
lwsl_notice("%s: done\n", __func__);
email->on_sent(email);
email->estate = LGSSMTP_IDLE;
goto close_conn;
default:
return;
}
puts(email->content);
wbuf = uv_buf_init(email->content, n);
uv_write(&write_req, s, &wbuf, 1, on_write_end);
return;
close_conn:
uv_close((uv_handle_t *)s, ccb);
}
static void
lwsgs_email_on_connect(uv_connect_t *req, int status)
{
struct lws_email *email = (struct lws_email *)req->data;
lwsl_notice("%s\n", __func__);
if (status == -1) {
lwsl_err("%s: failed\n", __func__);
return;
}
uv_read_start(req->handle, alloc_buffer, lwsgs_email_read);
email->estate = LGSSMTP_CONNECTED;
}
static void
uv_timeout_cb_email(uv_timer_t *w
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct lws_email *email = lws_container_of(w, struct lws_email,
timeout_email);
time_t now = lwsgs_now_secs();
struct sockaddr_in req_addr;
switch (email->estate) {
case LGSSMTP_IDLE:
if (email->on_next(email))
break;
email->estate = LGSSMTP_CONNECTING;
uv_tcp_init(email->loop, &email->email_client);
if (uv_ip4_addr(email->email_smtp_ip, 25, &req_addr)) {
lwsl_err("Unable to convert mailserver ads\n");
return;
}
lwsl_notice("LGSSMTP_IDLE: connecting\n");
email->email_connect_started = now;
email->email_connect_req.data = email;
email->email_client.data = email;
uv_tcp_connect(&email->email_connect_req, &email->email_client,
(struct sockaddr *)&req_addr,
lwsgs_email_on_connect);
uv_timer_start(&email->timeout_email,
uv_timeout_cb_email, 5000, 0);
break;
case LGSSMTP_CONNECTING:
if (email->email_connect_started - now > 5) {
lwsl_err("mail session timed out\n");
/* !!! kill the connection */
uv_close((uv_handle_t *) &email->email_connect_req, ccb);
email->estate = LGSSMTP_IDLE;
}
break;
default:
break;
}
}
LWS_VISIBLE LWS_EXTERN int
lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content)
{
email->content = lws_malloc(max_content);
if (!email->content)
return 1;
email->max_content_size = max_content;
uv_timer_init(loop, &email->timeout_email);
email->loop = loop;
/* trigger him one time in a bit */
uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 2000, 0);
return 0;
}
LWS_VISIBLE LWS_EXTERN void
lws_email_check(struct lws_email *email)
{
uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 1000, 0);
}
LWS_VISIBLE LWS_EXTERN void
lws_email_destroy(struct lws_email *email)
{
if (email->content)
lws_free_set_NULL(email->content);
uv_timer_stop(&email->timeout_email);
uv_close((uv_handle_t *)&email->timeout_email, NULL);
}

View file

@ -1,593 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
extern int lws_ssl_get_error(struct lws *wsi, int n);
#if defined(USE_WOLFSSL)
#else
static int
OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
#if defined(LWS_WITH_ESP32)
// long gvr = ssl_pm_get_verify_result(
lwsl_notice("%s\n", __func__);
return 0;
#else
SSL *ssl;
int n;
struct lws *wsi;
/* keep old behaviour accepting self-signed server certs */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) {
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
if ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED) {
lwsl_notice("accepting self-signed certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
} else if ((err == X509_V_ERR_CERT_NOT_YET_VALID ||
err == X509_V_ERR_CERT_HAS_EXPIRED) &&
wsi->use_ssl & LCCSCF_ALLOW_EXPIRED) {
if (err == X509_V_ERR_CERT_NOT_YET_VALID)
lwsl_notice("accepting not yet valid certificate (verify_callback)\n");
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
lwsl_notice("accepting expired certificate (verify_callback)\n");
X509_STORE_CTX_set_error(x509_ctx, X509_V_OK);
return 1; // ok
}
}
}
ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = lws_get_context_protocol(wsi->context, 0).callback(wsi, LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION, x509_ctx, ssl, preverify_ok);
/* keep old behaviour if something wrong with server certs */
/* if ssl error is overruled in callback and cert is ok,
* X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); must be set and
* return value is 0 from callback */
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(x509_ctx);
if (err != X509_V_OK) { /* cert validation error was not handled in callback */
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
const char* msg = X509_verify_cert_error_string(err);
lwsl_err("SSL error: %s (preverify_ok=%d;err=%d;depth=%d)\n", msg, preverify_ok, err, depth);
return preverify_ok; // not ok
}
}
/* convert callback return code from 0 = OK to verify callback return value 1 = OK */
return !n;
#endif
}
#endif
int
lws_ssl_client_bio_create(struct lws *wsi)
{
char hostname[128], *p;
if (lws_hdr_copy(wsi, hostname, sizeof(hostname),
_WSI_TOKEN_CLIENT_HOST) <= 0) {
lwsl_err("%s: Unable to get hostname\n", __func__);
return -1;
}
/*
* remove any :port part on the hostname... necessary for network
* connection but typical certificates do not contain it
*/
p = hostname;
while (*p) {
if (*p == ':') {
*p = '\0';
break;
}
p++;
}
wsi->ssl = SSL_new(wsi->vhost->ssl_client_ctx);
if (!wsi->ssl) {
lwsl_err("SSL_new failed: %s\n",
ERR_error_string(lws_ssl_get_error(wsi, 0), NULL));
lws_ssl_elaborate_error();
return -1;
}
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
if (wsi->vhost->ssl_info_event_mask)
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
#endif
#if defined LWS_HAVE_X509_VERIFY_PARAM_set1_host
X509_VERIFY_PARAM *param;
(void)param;
if (!(wsi->use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) {
param = SSL_get0_param(wsi->ssl);
/* Enable automatic hostname checks */
X509_VERIFY_PARAM_set_hostflags(param,
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
}
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_ESP32)
#ifndef USE_OLD_CYASSL
/* OpenSSL_client_verify_callback will be called @ SSL_connect() */
SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#endif
#endif
#if !defined(USE_WOLFSSL) && !defined(LWS_WITH_ESP32)
SSL_set_mode(wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
/*
* use server name indication (SNI), if supported,
* when establishing connection
*/
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
#ifdef CYASSL_SNI_HOST_NAME
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#else
#ifdef WOLFSSL_SNI_HOST_NAME
wolfSSL_UseSNI(wsi->ssl, WOLFSSL_SNI_HOST_NAME, hostname, strlen(hostname));
#endif
#endif
#else
#if defined(LWS_WITH_ESP32)
// esp-idf openssl shim does not seem ready for this
// SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#endif
#endif
#ifdef USE_WOLFSSL
/*
* wolfSSL/CyaSSL does certificate verification differently
* from OpenSSL.
* If we should ignore the certificate, we need to set
* this before SSL_new and SSL_connect is called.
* Otherwise the connect will simply fail with error code -155
*/
#ifdef USE_OLD_CYASSL
if (wsi->use_ssl == 2)
CyaSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#else
if (wsi->use_ssl == 2)
wolfSSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, NULL);
#endif
#endif /* USE_WOLFSSL */
#if !defined(LWS_WITH_ESP32)
wsi->client_bio = BIO_new_socket(wsi->desc.sockfd, BIO_NOCLOSE);
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
#else
SSL_set_fd(wsi->ssl, wsi->desc.sockfd);
#endif
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
wolfSSL_set_using_nonblock(wsi->ssl, 1);
#endif
#else
#if !defined(LWS_WITH_ESP32)
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
#endif
#if !defined(LWS_WITH_ESP32)
SSL_set_ex_data(wsi->ssl, openssl_websocket_private_data_index,
wsi);
#endif
return 0;
}
#if defined(LWS_WITH_ESP32)
int ERR_get_error(void)
{
return 0;
}
#endif
int
lws_ssl_client_connect1(struct lws *wsi)
{
struct lws_context *context = wsi->context;
int n = 0;
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ)
goto some_wait;
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("%s: WANT_WRITE... retrying\n", __func__);
lws_callback_on_writable(wsi);
some_wait:
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
}
return 1;
}
int
lws_ssl_client_connect2(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
int n = 0;
if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
lws_latency(context, wsi,
"SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
if (n < 0) {
n = lws_ssl_get_error(wsi, n);
if (n == SSL_ERROR_WANT_READ) {
lwsl_info("SSL_connect WANT_READ... retrying\n");
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
if (n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info("SSL_connect WANT_WRITE... retrying\n");
lws_callback_on_writable(wsi);
wsi->mode = LWSCM_WSCL_WAITING_SSL;
return 0; /* no error */
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
unsigned long error = ERR_get_error();
if (error != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
error, ERR_error_string(error, sb));
return -1;
}
}
}
#if defined(LWS_WITH_ESP32)
{
X509 *peer = SSL_get_peer_certificate(wsi->ssl);
if (!peer) {
lwsl_notice("peer did not provide cert\n");
return -1;
}
lwsl_notice("peer provided cert\n");
}
#endif
#ifndef USE_WOLFSSL
/*
* See comment above about wolfSSL certificate
* verification
*/
lws_latency_pre(context, wsi);
n = SSL_get_verify_result(wsi->ssl);
lws_latency(context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0);
lwsl_debug("get_verify says %d\n", n);
if (n != X509_V_OK) {
if ((n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) &&
(wsi->use_ssl & LCCSCF_ALLOW_SELFSIGNED)) {
lwsl_notice("accepting self-signed certificate\n");
} else if ((n == X509_V_ERR_CERT_NOT_YET_VALID ||
n == X509_V_ERR_CERT_HAS_EXPIRED) &&
(wsi->use_ssl & LCCSCF_ALLOW_EXPIRED)) {
lwsl_notice("accepting expired certificate\n");
} else if (n == X509_V_ERR_CERT_NOT_YET_VALID) {
lwsl_notice("Cert is from the future... "
"probably our clock... accepting...\n");
} else {
lwsl_err("server's cert didn't look good, X509_V_ERR = %d: %s\n",
n, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
return -1;
}
}
#endif /* USE_WOLFSSL */
return 1;
}
int lws_context_init_client_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
SSL_METHOD *method = NULL;
struct lws wsi;
unsigned long error;
#if !defined(LWS_WITH_ESP32)
const char *cipher_list = info->ssl_cipher_list;
const char *ca_filepath = info->ssl_ca_filepath;
const char *private_key_filepath = info->ssl_private_key_filepath;
const char *cert_filepath = info->ssl_cert_filepath;
int n;
/*
* for backwards-compatibility default to using ssl_... members, but
* if the newer client-specific ones are given, use those
*/
if (info->client_ssl_cipher_list)
cipher_list = info->client_ssl_cipher_list;
if (info->client_ssl_ca_filepath)
ca_filepath = info->client_ssl_ca_filepath;
if (info->client_ssl_cert_filepath)
cert_filepath = info->client_ssl_cert_filepath;
if (info->client_ssl_private_key_filepath)
private_key_filepath = info->client_ssl_private_key_filepath;
#endif
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return 0;
if (vhost->ssl_client_ctx)
return 0;
if (info->provided_client_ssl_ctx) {
/* use the provided OpenSSL context if given one */
vhost->ssl_client_ctx = info->provided_client_ssl_ctx;
/* nothing for lib to delete */
vhost->user_supplied_ssl_ctx = 1;
return 0;
}
/* basic openssl init already happened in context init */
/* choose the most recent spin of the api */
#if defined(LWS_HAVE_TLS_CLIENT_METHOD)
method = (SSL_METHOD *)TLS_client_method();
#elif defined(LWS_HAVE_TLSV1_2_CLIENT_METHOD)
method = (SSL_METHOD *)TLSv1_2_client_method();
#else
method = (SSL_METHOD *)SSLv23_client_method();
#endif
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
/* create context */
vhost->ssl_client_ctx = SSL_CTX_new(method);
if (!vhost->ssl_client_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)vhost->context->pt[0].serv_buf));
return 1;
}
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_client_ctx, SSL_OP_NO_COMPRESSION);
#endif
#if !defined(LWS_WITH_ESP32)
SSL_CTX_set_options(vhost->ssl_client_ctx,
SSL_OP_CIPHER_SERVER_PREFERENCE);
if (cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_client_ctx, cipher_list);
#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS))
/* loads OS default CA certs */
SSL_CTX_set_default_verify_paths(vhost->ssl_client_ctx);
#endif
/* openssl init for cert verification (for client sockets) */
if (!ca_filepath) {
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, NULL,
LWS_OPENSSL_CLIENT_CERTS))
lwsl_err(
"Unable to load SSL Client certs from %s "
"(set by --with-client-cert-dir= "
"in configure) -- client ssl isn't "
"going to work\n", LWS_OPENSSL_CLIENT_CERTS);
} else
if (!SSL_CTX_load_verify_locations(
vhost->ssl_client_ctx, ca_filepath, NULL)) {
lwsl_err(
"Unable to load SSL Client certs "
"file from %s -- client ssl isn't "
"going to work\n", info->client_ssl_ca_filepath);
lws_ssl_elaborate_error();
}
else
lwsl_info("loaded ssl_ca_filepath\n");
#endif
/*
* callback allowing user code to load extra verification certs
* helping the client to verify server identity
*/
#if !defined(LWS_WITH_ESP32)
/* support for client-side certificate authentication */
if (cert_filepath) {
lwsl_notice("%s: doing cert filepath\n", __func__);
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_client_ctx,
cert_filepath);
if (n < 1) {
lwsl_err("problem %d getting cert '%s'\n", n,
cert_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert %s\n", cert_filepath);
}
if (private_key_filepath) {
lwsl_notice("%s: doing private key filepath\n", __func__);
lws_ssl_bind_passphrase(vhost->ssl_client_ctx, info);
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_client_ctx,
private_key_filepath, SSL_FILETYPE_PEM) != 1) {
lwsl_err("use_PrivateKey_file '%s'\n",
private_key_filepath);
lws_ssl_elaborate_error();
return 1;
}
lwsl_notice("Loaded client cert private key %s\n",
private_key_filepath);
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_client_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
}
#endif
/*
* give him a fake wsi with context set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = vhost->context;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS,
vhost->ssl_client_ctx, NULL, 0);
return 0;
}

View file

@ -17,12 +17,12 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
*
* Some or all of this file is based on code from nghttp2, which has the
* following license. Since it's more liberal than lws license, you're also
* at liberty to get the original code from
* https://github.com/tatsuhiro-t/nghttp2 under his liberal terms alone.
*
*
* nghttp2 - HTTP/2.0 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
@ -59,8 +59,7 @@ struct alpn_ctx {
unsigned short len;
};
static int
npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
static int npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
{
struct alpn_ctx *alpn_ctx = arg;
@ -71,15 +70,15 @@ npn_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg)
return SSL_TLSEXT_ERR_OK;
}
static int
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
static int alpn_cb(SSL *s, const unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg)
{
struct alpn_ctx *alpn_ctx = arg;
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
if (SSL_select_next_proto((unsigned char **)out, outlen,
alpn_ctx->data, alpn_ctx->len, in, inlen) !=
OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_NOACK;
return SSL_TLSEXT_ERR_OK;
@ -87,62 +86,63 @@ alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
#endif
LWS_VISIBLE void
lws_context_init_http2_ssl(struct lws_vhost *vhost)
lws_context_init_http2_ssl(struct libwebsocket_context *context)
{
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
static struct alpn_ctx protos = { (unsigned char *)"\x02h2"
"\x08http/1.1", 6 + 9 };
SSL_CTX_set_next_protos_advertised_cb(vhost->ssl_ctx, npn_cb, &protos);
static struct alpn_ctx protos = { (unsigned char *)
"\x05h2-14"
"\x08http/1.1",
6 + 9 };
SSL_CTX_set_next_protos_advertised_cb(context->ssl_ctx, npn_cb, &protos);
// ALPN selection callback
SSL_CTX_set_alpn_select_cb(vhost->ssl_ctx, alpn_cb, &protos);
SSL_CTX_set_alpn_select_cb(context->ssl_ctx, alpn_cb, &protos);
lwsl_notice(" HTTP2 / ALPN enabled\n");
#else
lwsl_notice(
" HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n",
" HTTP2 / ALPN configured but not supported by OpenSSL 0x%x\n",
OPENSSL_VERSION_NUMBER);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
}
void lws_http2_configure_if_upgraded(struct lws *wsi)
void lws_http2_configure_if_upgraded(struct libwebsocket *wsi)
{
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
struct allocated_headers *ah;
const char *method = "alpn";
const unsigned char *name;
unsigned len;
const char *method = "alpn";
SSL_get0_alpn_selected(wsi->ssl, &name, &len);
if (!len) {
SSL_get0_next_proto_negotiated(wsi->ssl, &name, &len);
method = "npn";
}
if (!len) {
lwsl_info("no npn/alpn upgrade\n");
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)
return;
/* http2 */
/* adopt the header info */
ah = wsi->u.hdr.ah;
lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
lws_union_transition(wsi, LWS_CONNMODE_HTTP2_SERVING);
wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
/* http2 union member has http union struct at start */
wsi->u.http.ah = ah;
lws_http2_init(&wsi->u.http2.peer_settings);
lws_http2_init(&wsi->u.http2.my_settings);
@ -151,4 +151,4 @@ void lws_http2_configure_if_upgraded(struct lws *wsi)
}
#endif
#endif
#endif

View file

@ -1,439 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
extern void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, struct lws_context_creation_info *info);
#if !defined(LWS_WITH_ESP32)
static int
OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
SSL *ssl;
int n;
struct lws *wsi;
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
/*
* !!! nasty openssl requires the index to come as a library-scope
* static
*/
wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index);
n = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION,
x509_ctx, ssl, preverify_ok);
/* convert return code from 0 = OK to 1 = OK */
return !n;
}
#endif
static int
lws_context_ssl_init_ecdh(struct lws_vhost *vhost)
{
#ifdef LWS_SSL_SERVER_WITH_ECDH_CERT
EC_KEY *EC_key = NULL;
EVP_PKEY *pkey;
int KeyType;
X509 *x;
if (!lws_check_opt(vhost->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(vhost->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(vhost->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_vhost *vhost)
{
#ifdef LWS_HAVE_OPENSSL_ECDH_H
EC_KEY *ecdh;
int ecdh_nid;
const char *ecdh_curve = "prime256v1";
if (info->ecdh_curve)
ecdh_curve = info->ecdh_curve;
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(vhost->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve);
#else
#if !defined(LWS_WITH_ESP32)
lwsl_notice(" OpenSSL doesn't support ECDH\n");
#endif
#endif
return 0;
}
#ifndef OPENSSL_NO_TLSEXT
static int
lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
{
struct lws_context *context;
struct lws_vhost *vhost, *vh;
const char *servername;
int port;
if (!ssl)
return SSL_TLSEXT_ERR_NOACK;
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX(ssl),
openssl_SSL_CTX_private_data_index);
/*
* We can only get ssl accepted connections by using a vhost's ssl_ctx
* find out which listening one took us and only match vhosts on the
* same port.
*/
vh = context->vhost_list;
while (vh) {
if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */
port = vh->listen_port;
servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (servername) {
vhost = lws_select_vhost(context, port, servername);
if (vhost) {
lwsl_debug("SNI: Found: %s (port %d)\n",
servername, port);
SSL_set_SSL_CTX(ssl, vhost->ssl_ctx);
return SSL_TLSEXT_ERR_OK;
}
lwsl_err("SNI: Unknown ServerName: %s\n", servername);
}
return SSL_TLSEXT_ERR_OK;
}
#endif
LWS_VISIBLE int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
struct lws_vhost *vhost)
{
struct lws_context *context = vhost->context;
struct lws wsi;
unsigned long error;
int n;
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
vhost->use_ssl = 0;
return 0;
}
if (info->port != CONTEXT_PORT_NO_LISTEN) {
vhost->use_ssl = info->ssl_cert_filepath != NULL;
if (vhost->use_ssl && info->ssl_cipher_list)
lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list);
if (vhost->use_ssl)
lwsl_notice(" Using SSL mode\n");
else
lwsl_notice(" Using non-SSL mode\n");
}
/*
* give him a fake wsi with context + vhost set, so he can use
* lws_get_context() in the callback
*/
memset(&wsi, 0, sizeof(wsi));
wsi.vhost = vhost;
wsi.context = context;
(void)n;
(void)error;
/*
* Firefox insists on SSLv23 not SSLv3
* Konq disables SSLv2 by default now, SSLv23 works
*
* SSLv23_server_method() is the openssl method for "allow all TLS
* versions", compared to e.g. TLSv1_2_server_method() which only allows
* tlsv1.2. Unwanted versions must be disabled using SSL_CTX_set_options()
*/
#if !defined(LWS_WITH_ESP32)
{
SSL_METHOD *method;
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
}
#else
{
const SSL_METHOD *method = TLSv1_2_server_method();
vhost->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!vhost->ssl_ctx) {
lwsl_err("problem creating ssl context\n");
return 1;
}
}
#endif
#if !defined(LWS_WITH_ESP32)
/* associate the lws context with the SSL_CTX */
SSL_CTX_set_ex_data(vhost->ssl_ctx,
openssl_SSL_CTX_private_data_index, (char *)vhost->context);
/* Disable SSLv2 and SSLv3 */
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_NO_COMPRESSION);
#endif
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(vhost->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (info->ssl_cipher_list)
SSL_CTX_set_cipher_list(vhost->ssl_ctx,
info->ssl_cipher_list);
#endif
/* as a server, are we requiring clients to identify themselves? */
if (lws_check_opt(info->options,
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) {
int verify_options = SSL_VERIFY_PEER;
if (!lws_check_opt(info->options,
LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED))
verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
#if !defined(LWS_WITH_ESP32)
SSL_CTX_set_session_id_context(vhost->ssl_ctx,
(unsigned char *)context, sizeof(void *));
/* absolutely require the client cert */
SSL_CTX_set_verify(vhost->ssl_ctx,
verify_options, OpenSSL_verify_callback);
#endif
}
#ifndef OPENSSL_NO_TLSEXT
SSL_CTX_set_tlsext_servername_callback(vhost->ssl_ctx,
lws_ssl_server_name_cb);
#endif
/*
* give user code a chance to load certs into the server
* allowing it to verify incoming client certs
*/
#if !defined(LWS_WITH_ESP32)
if (info->ssl_ca_filepath &&
!SSL_CTX_load_verify_locations(vhost->ssl_ctx,
info->ssl_ca_filepath, NULL)) {
lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__);
}
#endif
if (vhost->use_ssl) {
if (lws_context_ssl_init_ecdh_curve(info, vhost))
return -1;
vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS,
vhost->ssl_ctx, NULL, 0);
}
if (lws_check_opt(info->options, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT))
/* Normally SSL listener rejects non-ssl, optionally allow */
vhost->allow_non_ssl_on_ssl_port = 1;
if (info->ssl_options_set)
SSL_CTX_set_options(vhost->ssl_ctx, info->ssl_options_set);
/* SSL_clear_options introduced in 0.9.8m */
#if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL)
if (info->ssl_options_clear)
SSL_CTX_clear_options(vhost->ssl_ctx, info->ssl_options_clear);
#endif
lwsl_info(" SSL options 0x%lX\n",
SSL_CTX_get_options(vhost->ssl_ctx));
if (vhost->use_ssl) {
/* openssl init for server sockets */
#if !defined(LWS_WITH_ESP32)
/* set the local certificate from CertFile */
n = SSL_CTX_use_certificate_chain_file(vhost->ssl_ctx,
info->ssl_cert_filepath);
if (n != 1) {
error = ERR_get_error();
lwsl_err("problem getting cert '%s' %lu: %s\n",
info->ssl_cert_filepath,
error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
lws_ssl_bind_passphrase(vhost->ssl_ctx, info);
#else
uint8_t *p;
lws_filepos_t flen;
int err;
if (alloc_pem_to_der_file(vhost->context, info->ssl_cert_filepath, &p,
&flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_certificate_ASN1(vhost->ssl_ctx, flen, p);
if (!err) {
lwsl_err("Problem loading cert\n");
return 1;
}
if (alloc_pem_to_der_file(vhost->context,
info->ssl_private_key_filepath, &p, &flen)) {
lwsl_err("couldn't find cert file %s\n",
info->ssl_cert_filepath);
return 1;
}
err = SSL_CTX_use_PrivateKey_ASN1(0, vhost->ssl_ctx, p, flen);
if (!err) {
lwsl_err("Problem loading key\n");
return 1;
}
// free(p);
#endif
if (info->ssl_private_key_filepath != NULL) {
#if !defined(LWS_WITH_ESP32)
/* set the private key from KeyFile */
if (SSL_CTX_use_PrivateKey_file(vhost->ssl_ctx,
info->ssl_private_key_filepath,
SSL_FILETYPE_PEM) != 1) {
error = ERR_get_error();
lwsl_err("ssl problem getting key '%s' %lu: %s\n",
info->ssl_private_key_filepath, error,
ERR_error_string(error,
(char *)context->pt[0].serv_buf));
return 1;
}
#endif
} else
if (vhost->protocols[0].callback(&wsi,
LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY,
vhost->ssl_ctx, NULL, 0)) {
lwsl_err("ssl private key not set\n");
return 1;
}
#if !defined(LWS_WITH_ESP32)
/* verify private key */
if (!SSL_CTX_check_private_key(vhost->ssl_ctx)) {
lwsl_err("Private SSL key doesn't match cert\n");
return 1;
}
#endif
if (lws_context_ssl_init_ecdh(vhost))
return 1;
/*
* SSL is happy and has a cert it's content with
* If we're supporting HTTP2, initialize that
*/
lws_context_init_http2_ssl(vhost);
}
return 0;
}

1064
lib/ssl.c

File diff suppressed because it is too large Load diff

1063
libwebsockets-api-doc.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,317 +0,0 @@
# Doxyfile 1.8.11
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "libwebsockets"
PROJECT_NUMBER =
PROJECT_BRIEF = "Lightweight C library for HTML5 websockets"
PROJECT_LOGO = "./test-server/libwebsockets.org-logo.png"
OUTPUT_DIRECTORY = "doc"
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = YES
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = YES
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = YES
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = lib/libwebsockets.h mainpage.md README.build.md README.problems.md README.lwsws.md README.coding.md README.generic-sessions.md README.generic-table.md README.test-apps.md doc-assets
INPUT_ENCODING = UTF-8
FILE_PATTERNS = lib/*.c *.md *.png
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH = doc-assets
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = mainpage.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = YES
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = NO
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

View file

@ -1,19 +1,19 @@
Name: libwebsockets
Version: 2.3.0
Release: 1%{?dist}
Summary: Websocket Server and Client Library
Version: 1.4
Release: 48.gmaster_16fb0132%{?dist}
Summary: Websocket Server Library
Group: System Environment/Libraries
License: LGPLv2 with exceptions
URL: https://libwebsockets.org
Group: System
License: GPL
URL: http://warmcat.com
Source0: %{name}-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: openssl-devel cmake
BuildRequires: openssl-devel
Requires: openssl
%description
Webserver server and client library
Webserver server library
%package devel
Summary: Development files for libwebsockets
@ -38,30 +38,22 @@ rm -rf $RPM_BUILD_ROOT
cd build
make install DESTDIR=$RPM_BUILD_ROOT
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%attr(755,root,root)
/usr/bin/libwebsockets-test-server
/usr/bin/libwebsockets-test-server-extpoll
/usr/bin/libwebsockets-test-client
/usr/bin/libwebsockets-test-ping
/usr/bin/libwebsockets-test-echo
/usr/bin/libwebsockets-test-fraggle
/usr/bin/libwebsockets-test-fuzxy
/%{_libdir}/libwebsockets.so.11
%attr(755,root,root) /usr/bin/libwebsockets-test-server
%attr(755,root,root) /usr/bin/libwebsockets-test-server-extpoll
%attr(755,root,root) /usr/bin/libwebsockets-test-client
%attr(755,root,root) /usr/bin/libwebsockets-test-ping
%attr(755,root,root) /usr/bin/libwebsockets-test-echo
%attr(755,root,root) /usr/bin/libwebsockets-test-fraggle
%attr(755,root,root)
/%{_libdir}/libwebsockets.so.5
/%{_libdir}/libwebsockets.so
/%{_libdir}/cmake/libwebsockets/LibwebsocketsConfig.cmake
/%{_libdir}/cmake/libwebsockets/LibwebsocketsConfigVersion.cmake
/%{_libdir}/cmake/libwebsockets/LibwebsocketsTargets.cmake
/%{_libdir}/cmake/libwebsockets/LibwebsocketsTargets-release.cmake
/usr/share/libwebsockets-test-server
%attr(755,root,root) /usr/share/libwebsockets-test-server
%doc
%files devel
%defattr(-,root,root,-)
@ -69,23 +61,6 @@ rm -rf $RPM_BUILD_ROOT
%attr(755,root,root)
/%{_libdir}/libwebsockets.a
/%{_libdir}/pkgconfig/libwebsockets.pc
/%{_libdir}/pkgconfig/libwebsockets_static.pc
%changelog
* Fri Jul 28 2017 Andy Green <andy@warmcat.com> 2.3.0-1
- MAJOR SONAMEBUMP APICHANGES Upstream 2.3.0 release
* Mon Mar 06 2017 Andy Green <andy@warmcat.com> 2.2.0-1
- MAJOR SONAMEBUMP APICHANGES Upstream 2.2.0 release
* Thu Oct 06 2016 Andy Green <andy@warmcat.com> 2.1.0-1
- MAJOR SONAMEBUMP APICHANGES Upstream 2.1.0 release
* Thu May 05 2016 Andy Green <andy@warmcat.com> 2.0.0-1
- MAJOR SONAMEBUMP APICHANGES Upstream 2.0.0 release
* Tue Feb 16 2016 Andy Green <andy@warmcat.com> 1.7.0-1
- MAJOR SONAMEBUMP APICHANGES Upstream 1.7.0 release
* Sun Jan 17 2016 Andrew Cooks <acooks@linux.com> 1.6.0-1
- Bump version to 1.6.0

View file

@ -6,8 +6,6 @@
#endif
#endif
#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share"
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
#cmakedefine USE_WOLFSSL
@ -15,25 +13,10 @@
/* Also define to 1 (in addition to USE_WOLFSSL) when using the
(older) CyaSSL library */
#cmakedefine USE_OLD_CYASSL
#cmakedefine LWS_USE_BORINGSSL
#cmakedefine LWS_USE_MBEDTLS
#cmakedefine LWS_USE_POLARSSL
#cmakedefine LWS_WITH_ESP8266
#cmakedefine LWS_WITH_ESP32
#cmakedefine LWS_WITH_PLUGINS
#cmakedefine LWS_WITH_NO_LOGS
/* The Libwebsocket version */
#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}"
#define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR}
#define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR}
#define LWS_LIBRARY_VERSION_PATCH ${LWS_LIBRARY_VERSION_PATCH}
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */
#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
@ -52,18 +35,9 @@
/* Enable libev io loop */
#cmakedefine LWS_USE_LIBEV
/* Enable libuv io loop */
#cmakedefine LWS_USE_LIBUV
/* Enable libevent io loop */
#cmakedefine LWS_USE_LIBEVENT
/* Build with support for ipv6 */
#cmakedefine LWS_USE_IPV6
/* Build with support for UNIX domain socket */
#cmakedefine LWS_USE_UNIX_SOCK
/* Build with support for HTTP2 */
#cmakedefine LWS_USE_HTTP2
@ -88,67 +62,4 @@
/* use SHA1() not internal libwebsockets_SHA1 */
#cmakedefine LWS_SHA1_USE_OPENSSL_NAME
/* SSL server using ECDH certificate */
#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
#cmakedefine LWS_HAVE_SSL_CTX_set1_param
#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host
#cmakedefine LWS_HAVE_UV_VERSION_H
/* CGI apis */
#cmakedefine LWS_WITH_CGI
/* whether the Openssl is recent enough, and / or built with, ecdh */
#cmakedefine LWS_HAVE_OPENSSL_ECDH_H
/* HTTP Proxy support */
#cmakedefine LWS_WITH_HTTP_PROXY
/* HTTP Ranges support */
#cmakedefine LWS_WITH_RANGES
/* Http access log support */
#cmakedefine LWS_WITH_ACCESS_LOG
#cmakedefine LWS_WITH_SERVER_STATUS
#cmakedefine LWS_WITH_STATEFUL_URLDECODE
/* Maximum supported service threads */
#define LWS_MAX_SMP ${LWS_MAX_SMP}
/* Lightweight JSON Parser */
#cmakedefine LWS_WITH_LEJP
/* SMTP */
#cmakedefine LWS_WITH_SMTP
/* OPTEE */
#cmakedefine LWS_PLAT_OPTEE
/* ZIP FOPS */
#cmakedefine LWS_WITH_ZIP_FOPS
#cmakedefine LWS_HAVE_STDINT_H
#cmakedefine LWS_AVOID_SIGPIPE_IGN
#cmakedefine LWS_FALLBACK_GETHOSTBYNAME
#cmakedefine LWS_WITH_STATS
#cmakedefine LWS_WITH_SOCKS5
#cmakedefine LWS_HAVE_SYS_CAPABILITY_H
#cmakedefine LWS_HAVE_LIBCAP
#cmakedefine LWS_HAVE_ATOLL
#cmakedefine LWS_HAVE__ATOI64
#cmakedefine LWS_HAVE__STAT32I64
/* OpenSSL various APIs */
#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
#cmakedefine LWS_HAS_INTPTR_T
${LWS_SIZEOFPTR_CODE}

View file

@ -75,9 +75,6 @@
/* Define to 1 if you have the <sys/socket.h> header file. */
#cmakedefine LWS_HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
#cmakedefine LWS_HAVE_SYS_SOCKIO_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#cmakedefine LWS_HAVE_SYS_STAT_H
@ -99,14 +96,9 @@
/* Define to 1 if `vfork' works. */
#cmakedefine LWS_HAVE_WORKING_VFORK
/* Define to 1 if execvpe() exists */
#cmakedefine LWS_HAVE_EXECVPE
/* Define to 1 if you have the <zlib.h> header file. */
#cmakedefine LWS_HAVE_ZLIB_H
#cmakedefine LWS_HAVE_GETLOADAVG
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR // We're not using libtool

View file

@ -1,10 +0,0 @@
/var/log/lwsws/*log {
copytruncate
missingok
notifempty
delaycompress
postrotate
/bin/systemctl reload lwsws.service > /dev/null 2>/dev/null || true
endscript
}

View file

@ -1,16 +0,0 @@
# these are the server global settings
# stuff related to vhosts should go in one
# file per vhost in ../conf.d/
{
"global": {
"uid": "48",
"gid": "48",
"interface": "eth0",
"count-threads": "1",
"server-string": "lwsws",
"ws-pingpong-secs": "200",
"init-ssl": "yes"
}
}

View file

@ -1,58 +0,0 @@
{
"vhosts": [ {
"name": "localhost",
"port": "7681",
"interface": "lo",
# "host-ssl-key": "/etc/pki/tls/private/libwebsockets.org.key",
# "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
# "host-ssl-ca": "/etc/pki/tls/certs/libwebsockets.org.cer",
"access-log": "/var/log/lwsws/test-access-log",
# "sts": "on",
"mounts": [{
"mountpoint": "/",
"origin": "file://_lws_ddir_/libwebsockets-test-server",
"default": "test.html",
"cache-max-age": "60",
"cache-reuse": "1",
"cache-revalidate": "1",
"cache-intermediaries": "0"
}, {
"mountpoint": "/server-status",
"origin": "file://_lws_ddir_/libwebsockets-test-server/server-status",
"default": "server-status.html"
}, {
"mountpoint": "/testcgi",
"origin": "cgi://_lws_ddir_/libwebsockets-test-server/lws-cgi-test.sh"
}, {
"mountpoint": "/formtest",
"origin": "callback://protocol-post-demo"
}],
# which protocols are enabled for this vhost, and optional
# vhost-specific config options for the protocol
#
"ws-protocols": [{
"lws-meta": {
"status": "ok"
},
"dumb-increment-protocol": {
"status": "ok"
},
"lws-mirror-protocol": {
"status": "ok"
},
"lws-status": {
"status": "ok"
},
"protocol-post-demo": {
"status": "ok"
},
"lws-server-status": {
"status": "ok",
"update-ms": "5000"
}
}]
}
]
}

View file

@ -1,325 +0,0 @@
/*
* libwebsockets web server application
*
* 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 "lws_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#ifndef _WIN32
#include <dirent.h>
#include <syslog.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/wait.h>
#else
#include <io.h>
#include "gettimeofday.h"
int fork(void)
{
fprintf(stderr, "Sorry Windows doesn't support fork().\n");
return 0;
}
#endif
#include "../lib/libwebsockets.h"
#include <uv.h>
static struct lws_context *context;
static char config_dir[128];
static int opts = 0, do_reload = 1;
static uv_loop_t loop;
static uv_signal_t signal_outer;
static int pids[32];
#define LWSWS_CONFIG_STRING_SIZE (32 * 1024)
static const struct lws_extension exts[] = {
#if !defined(LWS_NO_EXTENSIONS)
{
"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
},
#endif
{ NULL, NULL, NULL /* terminator */ }
};
static const char * const plugin_dirs[] = {
INSTALL_DATADIR"/libwebsockets-test-server/plugins/",
NULL
};
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", required_argument, NULL, 'd' },
{ "configdir", required_argument, NULL, 'c' },
{ NULL, 0, 0, 0 }
};
void signal_cb(uv_signal_t *watcher, int signum)
{
switch (watcher->signum) {
case SIGTERM:
case SIGINT:
break;
case SIGHUP:
if (lws_context_is_deprecated(context))
return;
lwsl_notice("Dropping listen sockets\n");
lws_context_deprecate(context, NULL);
return;
default:
signal(SIGABRT, SIG_DFL);
abort();
break;
}
lwsl_err("Signal %d caught\n", watcher->signum);
lws_libuv_stop(context);
}
static int
context_creation(void)
{
int cs_len = LWSWS_CONFIG_STRING_SIZE - 1;
struct lws_context_creation_info info;
char *cs, *config_strings;
cs = config_strings = malloc(LWSWS_CONFIG_STRING_SIZE);
if (!config_strings) {
lwsl_err("Unable to allocate config strings heap\n");
return -1;
}
memset(&info, 0, sizeof(info));
info.external_baggage_free_on_destroy = config_strings;
info.max_http_header_pool = 16;
info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 |
LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_LIBUV;
info.plugin_dirs = plugin_dirs;
lwsl_notice("Using config dir: \"%s\"\n", config_dir);
/*
* first go through the config for creating the outer context
*/
if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len))
goto init_failed;
context = lws_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
goto init_failed;
}
lws_uv_sigint_cfg(context, 1, signal_cb);
lws_uv_initloop(context, &loop, 0);
/*
* then create the vhosts... protocols are entirely coming from
* plugins, so we leave it NULL
*/
info.extensions = exts;
if (lwsws_get_config_vhosts(context, &info, config_dir,
&cs, &cs_len))
return 1;
return 0;
init_failed:
free(config_strings);
return 1;
}
/*
* root-level sighup handler
*/
static void
reload_handler(int signum)
{
#ifndef _WIN32
int m;
switch (signum) {
case SIGHUP: /* reload */
fprintf(stderr, "root process receives reload\n");
if (!do_reload) {
fprintf(stderr, "passing HUP to child processes\n");
for (m = 0; m < ARRAY_SIZE(pids); m++)
if (pids[m])
kill(pids[m], SIGHUP);
sleep(1);
}
do_reload = 1;
break;
case SIGINT:
case SIGTERM:
case SIGKILL:
fprintf(stderr, "killing service processes\n");
for (m = 0; m < ARRAY_SIZE(pids); m++)
if (pids[m])
kill(pids[m], SIGTERM);
exit(0);
}
#else
// kill() implementation needed for WIN32
#endif
}
int main(int argc, char **argv)
{
int n = 0, debug_level = 7;
#ifndef _WIN32
int m;
int status, syslog_options = LOG_PID | LOG_PERROR;
#endif
strcpy(config_dir, "/etc/lwsws");
while (n >= 0) {
n = getopt_long(argc, argv, "hd:c:", options, NULL);
if (n < 0)
continue;
switch (n) {
case 'd':
debug_level = atoi(optarg);
break;
case 'c':
strncpy(config_dir, optarg, sizeof(config_dir) - 1);
config_dir[sizeof(config_dir) - 1] = '\0';
break;
case 'h':
fprintf(stderr, "Usage: lwsws [-c <config dir>] "
"[-d <log bitfield>] [--help]\n");
exit(1);
}
}
#ifndef _WIN32
/*
* We leave our original process up permanently, because that
* suits systemd.
*
* Otherwise we get into problems when reload spawns new processes and
* the original one dies randomly.
*/
signal(SIGHUP, reload_handler);
signal(SIGINT, reload_handler);
fprintf(stderr, "Root process is %u\n", getpid());
while (1) {
if (do_reload) {
do_reload = 0;
n = fork();
if (n == 0) /* new */
break;
/* old */
if (n > 0)
for (m = 0; m < ARRAY_SIZE(pids); m++)
if (!pids[m]) {
// fprintf(stderr, "added child pid %d\n", n);
pids[m] = n;
break;
}
}
#ifndef _WIN32
sleep(2);
n = waitpid(-1, &status, WNOHANG);
if (n > 0)
for (m = 0; m < ARRAY_SIZE(pids); m++)
if (pids[m] == n) {
// fprintf(stderr, "reaped child pid %d\n", pids[m]);
pids[m] = 0;
break;
}
#else
// !!! implemenation needed
#endif
}
#endif
/* child process */
#ifndef _WIN32
/* we will only try to log things according to our debug_level */
setlogmask(LOG_UPTO (LOG_DEBUG));
openlog("lwsws", syslog_options, LOG_DAEMON);
#endif
lws_set_log_level(debug_level, lwsl_emit_syslog);
lwsl_notice("lwsws libwebsockets web server - license CC0 + LGPL2.1\n");
lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
#if (UV_VERSION_MAJOR > 0) // Travis...
uv_loop_init(&loop);
#else
fprintf(stderr, "Your libuv is too old!\n");
return 0;
#endif
uv_signal_init(&loop, &signal_outer);
uv_signal_start(&signal_outer, signal_cb, SIGINT);
uv_signal_start(&signal_outer, signal_cb, SIGHUP);
if (context_creation()) {
lwsl_err("Context creation failed\n");
return 1;
}
lws_libuv_run(context, 0);
uv_signal_stop(&signal_outer);
lws_context_destroy(context);
#if (UV_VERSION_MAJOR > 0) // Travis...
lws_close_all_handles_in_loop(&loop);
n = 0;
while (n++ < 4096 && uv_loop_close(&loop))
uv_run(&loop, UV_RUN_NOWAIT);
#endif
lws_context_destroy2(context);
fprintf(stderr, "lwsws exited cleanly\n");
#ifndef _WIN32
closelog();
#endif
context = NULL;
return 0;
}

View file

@ -1,13 +0,0 @@
[Unit]
Description=Libwebsockets Web Server
After=syslog.target
[Service]
ExecStart=/usr/local/bin/lwsws
ExecReload=/usr/bin/kill -HUP $MAINPID
ExecStop=/usr/bin/killall lwsws
StandardError=null
[Install]
WantedBy=multi-user.target

View file

@ -1,16 +0,0 @@
##Libwebsockets API introduction
Libwebsockets covers a lot of interesting features for people making embedded servers or clients
- http(s) serving and client operation
- ws(s) serving and client operation
- http(s) apis for file transfer and upload
- http POST form handling (including multipart)
- cookie-based sessions
- account management (including registration, email verification, lost pw etc)
- strong ssl PFS support (A+ on SSLlabs test)
You can browse by api category <a href="modules.html">here</a>
A collection of READMEs for build, coding, lwsws etc are <a href="pages.html">here</a>

View file

@ -1,21 +0,0 @@
{
"name": "websockets",
"version": "1.6.0",
"description": "Libwebsockets",
"keywords": [
"lws",
"libwebsockets",
"websockets",
"ws"
],
"author": "Andy Green <andy@warmcat.com>",
"homepage": "https://libwebsockets.org",
"license": "LGPL2.1-SLE",
"extraIncludes": [ "build/frdm-k64f-gcc/generated/include" ],
"dependencies": {
"mbed-drivers": "",
"sal-stack-lwip": "",
"sockets": ""
}
}

View file

@ -1,73 +0,0 @@
cmake_minimum_required(VERSION 2.8)
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type")
endif()
# This shows one way to build a standalone plugin
# outside of lws itself
project(lws-protocol-plugin-example C)
set(PACKAGE "lws-protocol-plugin-example")
set(CPACK_PACKAGE_NAME "${PACKAGE}")
set(CPACK_PACKAGE_VERSION "0.1")
set(CPACK_PACKAGE_VENDOR "andy@warmcat.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PACKAGE} ${PACKAGE_VERSION}")
set(SOVERSION "1")
set(VERSION "0.1")
set(PLUGIN_NAME "protocol_example_standalone")
# space-separated list of sources
set(PLUGIN_SRCS protocol_example_standalone.c)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
message(STATUS "CMAKE_TOOLCHAIN_FILE='${CMAKE_TOOLCHAIN_FILE}'")
# Try to find the current Git hash.
find_package(Git)
if(GIT_EXECUTABLE)
execute_process(
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMAND "${GIT_EXECUTABLE}" describe
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
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
)
string(REGEX REPLACE "([^\\])[\\]([^\\])" "\\1\\\\\\\\\\2" GIT_USER ${GIT_USER})
set(LWS_BUILD_HASH ${GIT_USER}@${GIT_HOST}-${GIT_HASH})
message("Git commit hash: ${LWS_BUILD_HASH}")
endif()
set(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
source_group("Headers Private" FILES ${PLUGIN_HDR})
source_group("Sources" FILES ${PLUGIN_SRCS})
add_library(${PLUGIN_NAME} SHARED ${PLUGIN_SRCS} ${PLUGIN_HDR})
target_link_libraries(${PLUGIN_NAME} -lwebsockets)
# Set test app specific defines.
set_property(TARGET ${PLUGIN_NAME}
PROPERTY COMPILE_DEFINITIONS
INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/plugins"
)
list(APPEND PLUGINS_LIST ${PLUGIN_NAME})
install(TARGETS ${PLUGINS_LIST}
PERMISSIONS OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ
DESTINATION share/libwebsockets-test-server/plugins
COMPONENT plugins)

View file

@ -1,152 +0,0 @@
/*
* ws protocol handler plugin for "dumb increment"
*
* 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.
*
* These test plugins are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*
* This is a copy of dumb_increment adapted slightly to serve as the
* "example-standalone-protocol", to show how to build protocol plugins
* outside the library easily.
*/
#define LWS_DLL
#define LWS_INTERNAL
#include "../lib/libwebsockets.h"
#include <string.h>
struct per_vhost_data__dumb_increment {
uv_timer_t timeout_watcher;
struct lws_context *context;
struct lws_vhost *vhost;
const struct lws_protocols *protocol;
};
struct per_session_data__dumb_increment {
int number;
};
static void
uv_timeout_cb_dumb_increment(uv_timer_t *w
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct per_vhost_data__dumb_increment *vhd = lws_container_of(w,
struct per_vhost_data__dumb_increment, timeout_watcher);
lws_callback_on_writable_all_protocol_vhost(vhd->vhost, vhd->protocol);
}
static int
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__dumb_increment *pss =
(struct per_session_data__dumb_increment *)user;
struct per_vhost_data__dumb_increment *vhd =
(struct per_vhost_data__dumb_increment *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
unsigned char buf[LWS_PRE + 512];
unsigned char *p = &buf[LWS_PRE];
int n, m;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi),
sizeof(struct per_vhost_data__dumb_increment));
vhd->context = lws_get_context(wsi);
vhd->protocol = lws_get_protocol(wsi);
vhd->vhost = lws_get_vhost(wsi);
uv_timer_init(lws_uv_getloop(vhd->context, 0),
&vhd->timeout_watcher);
uv_timer_start(&vhd->timeout_watcher,
uv_timeout_cb_dumb_increment, 50, 50);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
if (!vhd)
break;
uv_timer_stop(&vhd->timeout_watcher);
break;
case LWS_CALLBACK_ESTABLISHED:
pss->number = 0;
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
n = sprintf((char *)p, "%d", pss->number++);
m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
if (m < n) {
lwsl_err("ERROR %d writing to di socket\n", n);
return -1;
}
break;
case LWS_CALLBACK_RECEIVE:
if (len < 6)
break;
if (strcmp((const char *)in, "reset\n") == 0)
pss->number = 0;
if (strcmp((const char *)in, "closeme\n") == 0) {
lwsl_notice("dumb_inc: closing as requested\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);
return -1;
}
break;
default:
break;
}
return 0;
}
static const struct lws_protocols protocols[] = {
{
"example-standalone-protocol",
callback_dumb_increment,
sizeof(struct per_session_data__dumb_increment),
10, /* rx buf size must be >= permessage-deflate rx size */
},
};
LWS_VISIBLE int
init_protocol_example_standalone(struct lws_context *context,
struct lws_plugin_capability *c)
{
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
c->api_magic);
return 1;
}
c->protocols = protocols;
c->count_protocols = ARRAY_SIZE(protocols);
c->extensions = NULL;
c->count_extensions = 0;
return 0;
}
LWS_VISIBLE int
destroy_protocol_example_standalone(struct lws_context *context)
{
return 0;
}

View file

@ -1,5 +0,0 @@
<html>
This is an example destination that will appear after successful Admin login.
This URL cannot be served if you're not logged in as admin.
</html>

View file

@ -1,3 +0,0 @@
<html>
This is an example destination that will appear after a failed login
</html>

View file

@ -1,164 +0,0 @@
<html>
<head>
<script src="/lws-common.js" nonce="lwscaro"></script>
<script src="lwsgs.js" nonce="lwscaro"></script>
<style>
.body { font-size: 12 }
.gstitle { font-size: 18 }
.group1 { vertical-align:middle;text-align:center;background:#f0f0e0;
padding:12px; -webkit-border-radius:10px;
-moz-border-radius:10px;border-radius:10px; }
.group2 { vertical-align:middle; font-size: 22;text-align:center;
margin:auto; align:center;
background-color: rgba(255, 255, 255, 0.8); padding:12px;
display:inline-block; -webkit-border-radius:10px;
-moz-border-radius:10px; border-radius:10px; }
</style>
</head>
<body style="background-image:url(seats.jpg)">
<table style="width:100%;height:100%;transition: max-height 2s;">
<tr>
<td style="vertical-align:top;text-align:left;width=200px">
<img src="lwsgs-logo.png">
</td>
<td style="vertical-align:top;float:right">
<div id=lwsgs style="zIndex: 1000;text-align:right;background-color: rgba(255, 255, 255, 0.8);"></div>
</td>
</tr>
<tr><td colspan=2 style="height:99%;vertical-align:middle;">
<table style="text-align:center;width:100%"><tr>
<td style="margin:auto;align:center">
<span id="nolog" class="group2" style="display:none;">
This is a demo application for lws generic-sessions.<br><br>
It's a simple messageboard.<br><br>
What's interesting about it is there is <b>no serverside scripting</b>,<br>
instead client js makes a wss:// connection back to the server<br>
and then reacts to JSON from the ws protocol. Sessions stuff is <br>
handled by lws generic sessions, making the <a href="https://github.com/warmcat/libwebsockets/blob/master/plugins/generic-sessions/protocol_lws_messageboard.c">actual<br>
test application</a> <a href="https://github.com/warmcat/libwebsockets/blob/master/plugins/generic-sessions/index.html">very small</a>.<br><br>
And because it's natively websocket, it's naturally connected<br>
for dynamic events and easy to maintain.
<br><br>
Register / Login at the top right to see and create new messages.
</span>
<span id="logged" class="group2" style="display:none">
<div id="newmsg">
<form action="msg" method="post" target="hidden">
New message<br>
<textarea id="msg" placeholder="type your message here" cols="40" rows="5" name="msg"></textarea><br>
<input type="submit" id="send" name="send" disabled=1>
</form>
</div>
</span>
<div id="dmessages">
<span id="messages" ></span>
</div>
<span id="debug" class="group2"></span>
</td></tr></table>
</td></tr>
</table>
</form>
<iframe name="hidden" style="display:none"></iframe>
<script nonce="lwscaro">lwsgs_initial();
document.getElementById("nolog").style.display = !!lwsgs_user ? "none" : "inline-block";
document.getElementById("logged").style.display = !lwsgs_user ? "none" : "inline-block";
document.getElementById("msg").onkeyup = mupd;
document.getElementById("msg").onchange = mupd;
var ws;
function mb_format(s)
{
var r = "", n, wos = 0;
for (n = 0; n < s.length; n++) {
if (s[n] == ' ')
wos = 0;
else {
wos++;
if (wos == 40) {
wos = 0;
r = r + ' ';
}
}
if (s[n] == '<') {
r = r + "&lt;";
continue;
}
if (s[n] == '\n') {
r = r + "<br>";
continue;
}
r = r + s[n];
}
return r;
}
function add_div(n, m)
{
var q = document.getElementById(n);
var d = new Date(m.time * 1000);
q.innerHTML = "<br><div style=\"margin:2px\" class=\"group2\"><table style=\"table-layout: fixed;\"><tr><td>" +
"<img src=\"https://www.gravatar.com/avatar/" + md5(m.email) +
"?d=identicon\"><br>" +
"<b>" + lwsgs_san(m.username) + "</b><br>" +
"<span style=\"font-size:8pt\">" + d.toDateString() +
"<br>" + d.toTimeString() + "</span><br>" +
"IP: " + lwsgs_san(m.ip) +
"</td><td style=\"display:inline-block;vertical-align:top;word-wrap:break-word;\"><span>" +
mb_format(m.content) +
"</span></td></tr></table></div><br>" + q.innerHTML;
}
function get_appropriate_ws_url()
{
var pcol;
var u = document.URL;
if (u.substring(0, 5) == "https") {
pcol = "wss://";
u = u.substr(8);
} else {
pcol = "ws://";
if (u.substring(0, 4) == "http")
u = u.substr(7);
}
u = u.split('/');
return pcol + u[0] + "/xxx";
}
if (lwsgs_user) {
if (typeof MozWebSocket != "undefined")
ws = new MozWebSocket(get_appropriate_ws_url(),
"protocol-lws-messageboard");
else
ws = new WebSocket(get_appropriate_ws_url(),
"protocol-lws-messageboard");
try {
ws.onopen = function() {
document.getElementById("debug").textContent = "ws opened";
}
ws.onmessage =function got_packet(msg) {
add_div("messages", JSON.parse(msg.data));
}
ws.onclose = function(){
}
} catch(exception) {
alert('<p>Error' + exception);
}
}
function mupd()
{
document.getElementById("send").disabled = !document.getElementById("msg").value;
}
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Some files were not shown because too many files have changed in this diff Show more