Compare commits

..

1 commit

556 changed files with 32612 additions and 81413 deletions

12
.gitignore vendored
View file

@ -1,12 +1,4 @@
#Ignore build files
CMakeCache.txt
CMakeFiles
build
cmake_install.cmake
lws-minimal*
Makefile
.cproject
.project
config.h
config.log
config.status
@ -46,7 +38,3 @@ ar-lib
libwebsockets.pc
build/
*.swp
doc
/build2/
/build3/
/cov-int/

View file

@ -4,35 +4,26 @@ env:
global:
- secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
matrix:
- LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=lwsws2 CMAKE_ARGS="-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=default CMAKE_ARGS="-DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=mbedtls CMAKE_ARGS="-DLWS_WITH_MBEDTLS=1 -DLWS_WITH_HTTP2=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG"
- LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=noext CMAKE_ARGS="-DLWS_WITHOUT_EXTENSIONS=ON -DLWS_WITH_MINIMAL_EXAMPLES=1"
- 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=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"
- LWS_METHOD=smp CMAKE_ARGS="-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1"
- LWS_METHOD=nows CMAKE_ARGS="-DLWS_ROLE_WS=0"
os:
- linux
- osx
language: generic
install:
- ./scripts/travis_install.sh
# - ./travis-tool.sh github_package jimhester/covr
#after_success:
# - Rscript -e 'covr::coveralls()'
- ./travis_install.sh
script:
- ./scripts/travis_control.sh
- if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "osx" ]; then mkdir build && cd build && cmake -DOPENSSL_ROOT_DIR="/usr/local/opt/openssl" $CMAKE_ARGS .. && cmake --build .; else if [ "$COVERITY_SCAN_BRANCH" != 1 -a "$TRAVIS_OS_NAME" = "linux" ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi ; fi
sudo: required
dist: trusty
addons:

File diff suppressed because it is too large Load diff

14
LICENSE
View file

@ -33,21 +33,19 @@ to get original sources with the liberal terms.
Original liberal license retained
- lib/misc/sha-1.c - 3-clause BSD license retained, link to original
- lib/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h)
- lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls)
Relicensed to libwebsocket license
- lib/misc/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/misc/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version
- 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-apps/*.c
- test-apps/*.h
- minimal-examples/*
- test-server/*.c
- test-server/*.h
- lwsws/*
------ end of exceptions

View file

@ -1 +0,0 @@
CPPFLAGS += -I$(BUILD_DIR_BASE)/libwebsockets/include

469
README.build.md Normal file
View file

@ -0,0 +1,469 @@
Notes about building lws
========================
@section cm 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
CMake also allows/recommends you to do "out of source"-builds, that is,
the build files are separated from your sources, so there is no need to
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):
- Windows (Visual Studio)
- Windows (MinGW)
- Linux (x86 and ARM)
- OSX
- NetBSD
@section build1 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
output a Visual Studio project or Make file for instance. To see a list of
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:
1. Install CMake 2.8 or greater: http://cmake.org/cmake/resources/software.html
(Most Unix distributions comes with a packaged version also)
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 ".."
**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 .
```
**NOTE3**:
On machines that want libraries in lib64, you can also add the
following to the cmake line
```
-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,
```
$ 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
```
$ 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
$ 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
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)
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
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
**NOTE2**:
Be sure that OPENSSL_CONF environment variable is defined and points at
<OpenSSL install location>\bin\openssl.cfg
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" ..
```
(**NOTE**: There is also a cmake-gui available on Windows if you prefer that)
**NOTE2**:
See this link to find out the version number corresponding to your Visual Studio edition:
http://superuser.com/a/194065
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)
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)
typedef struct pollfd {
SOCKET fd;
SHORT events;
SHORT revents;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
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;
```
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
3. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
4. Install OpenSSL binaries. http://www.openssl.org/related/binaries.html
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
**NOTE2**:
Be sure that OPENSSL_CONF environment variable is defined and points at
<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 ..
```
(**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 "..")
**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 ..
```
6. Finally you can build using the generated Makefile and get the results deployed into your MinGW installation:
```
$ 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
To set compile time flags you can either use one of the CMake gui applications
or do it via the command line.
@subsection cmcocl Command line
To list available options (omit the H if you don't want the help text):
cmake -LH ..
Then to set an option and build (for example turn off SSL support):
cmake -DLWS_WITH_SSL=0 ..
or
cmake -DLWS_WITH_SSL:BOOL=OFF ..
@subsection cmcoug 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
On windows CMake comes with a gui application:
Start -> Programs -> CMake -> CMake (cmake-gui)
@section wolf wolfSSL/CyaSSL replacement for OpenSSL
wolfSSL/CyaSSL is a lightweight SSL library targeted at embedded systems:
https://www.wolfssl.com/wolfSSL/Products-wolfssl.html
It contains a OpenSSL compatibility layer which makes it possible to pretty
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
```
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
```
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
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
```
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
```
$ nghttp -nvasu http://localhost:7681/test.htm
```
For SSL / ALPN HTTP2.0 upgrade
```
$ nghttp -nvas https://localhost:7681/test.html
```
@section cross 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
to look for dependencies and such.
**Libwebsockets** includes an example toolchain file [cross-arm-linux-gnueabihf.cmake](cross-arm-linux-gnueabihf.cmake)
you can use as a starting point.
The commandline to configure for cross with this would look like
```
$ 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.
**NOTE**: start from an EMPTY build directory if you had a non-cross build in there
before the settings will be cached and your changes ignored.
Additional information on cross compilation with CMake:
http://www.vtk.org/Wiki/CMake_Cross_Compiling
@section mem 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
.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.
These are accounting for static allocations from the library elf, there are
additional dynamic allocations via malloc. These are a bit old now but give
the right idea for relative "expense" of features.
Static allocations, ARM9
| | .text | .rodata | .data | .bss |
|--------------------------------|---------|---------|-------|------|
| All (no without) | 35024 | 9940 | 336 | 4104 |
| without client | 25684 | 7144 | 336 | 4104 |
| without client, exts | 21652 | 6288 | 288 | 4104 |
| without client, exts, debug[1] | 19756 | 3768 | 288 | 4104 |
| without server | 30304 | 8160 | 336 | 4104 |
| without server, exts | 25382 | 7204 | 288 | 4104 |
| without server, exts, debug[1] | 23712 | 4256 | 288 | 4104 |
[1] `--disable-debug` only removes messages below `lwsl_notice`. Since that is
the default logging level the impact is not noticeable, error, warn and notice
logs are all still there.
[2] `1024` fd per process is the default limit (set by ulimit) in at least Fedora
and Ubuntu. You can make significant savings tailoring this to actual expected
peak fds, ie, at a limit of `20`, context creation allocation reduces to `4432 +
240 = 4672`)
[3] known header content is freed after connection establishment

View file

@ -1,104 +1,6 @@
Notes about coding with lws
===========================
@section era Old lws and lws v2.0
Originally lws only supported the "manual" method of handling everything in the
user callback found in test-server.c / test-server-http.c.
Since v2.0, the need for most or all of this manual boilerplate has been
eliminated: the protocols[0] http stuff is provided by a generic lib export
`lws_callback_http_dummy()`. You can serve parts of your filesystem at part of
the URL space using mounts, the dummy http callback will do the right thing.
It's much preferred to use the "automated" v2.0 type scheme, because it's less
code and it's easier to support.
You can see an example of the new way in test-server-v2.0.c.
If you just need generic serving capability, without the need to integrate lws
to some other app, consider not writing any server code at all, and instead use
the generic server `lwsws`, and writing your special user code in a standalone
"plugin". The server is configured for mounts etc using JSON, see
./READMEs/README.lwsws.md.
Although the "plugins" are dynamically loaded if you use lwsws or lws built
with libuv, actually they may perfectly well be statically included if that
suits your situation better, eg, ESP32 test server, where the platform does
not support processes or dynamic loading, just #includes the plugins
one after the other and gets the same benefit from the same code.
Isolating and collating the protocol code in one place also makes it very easy
to maintain and understand.
So it if highly recommended you put your protocol-specific code into the
form of a "plugin" at the source level, even if you have no immediate plan to
use it dynamically-loaded.
@section 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
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(wsi)` for a specific `wsi`, or
- `lws_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
loop, but if your peer is slow or temporarily inactive the callback will be
delayed accordingly. Generating what to write and sending it should be done
in the ...WRITEABLE callback.
See the test server code for an example of how to do this.
Otherwise evolved libs like libuv get this wrong, they will allow you to "send"
anything you want but it only uses up your local memory (and costs you
memcpys) until the socket can actually accept it. It is much better to regulate
your send action by the downstream peer readiness to take new data in the first
place, avoiding all the wasted buffering.
Libwebsockets' concept is that the downstream peer is truly the boss, if he,
or our connection to him, cannot handle anything new, we should not generate
anything new for him. This is how unix shell piping works, you may have
`cat a.txt | grep xyz > remote", but actually that does not cat anything from
a.txt while remote cannot accept anything new.
@section oneper Only one lws_write per WRITEABLE callback
From v2.5, lws strictly enforces only one lws_write() per WRITEABLE callback.
You will receive a message about "Illegal back-to-back write of ... detected"
if there is a second lws_write() before returning to the event loop.
This is because with http/2, the state of the network connection carrying a
wsi is unrelated to any state of the wsi. The situation on http/1 where a
new request implied a new tcp connection and new SSL buffer, so you could
assume some window for writes is no longer true. Any lws_write() can fail
and be buffered for completion by lws; it will be auto-completed by the
event loop.
Note that if you are handling your own http responses, writing the headers
needs to be done with a separate lws_write() from writing any payload. That
means after writing the headers you must call `lws_callback_on_writable(wsi)`
and send any payload from the writable callback.
@section otherwr 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.
So your code for `LWS_CALLBACK_CLIENT_WRITEABLE` needs to own the decision
about what to send, it can't assume that just because the writeable callback
came it really is time to send something.
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 dae Daemonization
There's a helper api `lws_daemonize` built by default that does everything you
@ -108,7 +10,8 @@ headless background process and exit the starting process.
Notice stdout, stderr, stdin are all redirected to /dev/null to enforce your
daemon is headless, so you'll need to sort out alternative logging, by, eg,
syslog via `lws_set_log_level(..., lwsl_emit_syslog)`.
syslog.
@section conns Maximum number of connections
@ -122,153 +25,81 @@ 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
**libwebsockets** will adapt accordingly.
@section peer_limits optional LWS_WITH_PEER_LIMITS
If you select `LWS_WITH_PEER_LIMITS` at cmake, then lws will track peer IPs
and monitor how many connections and ah resources they are trying to use
at one time. You can choose to limit these at context creation time, using
`info.ip_limit_ah` and `info.ip_limit_wsi`.
Note that although the ah limit is 'soft', ie, the connection will just wait
until the IP is under the ah limit again before attaching a new ah, the
wsi limit is 'hard', lws will drop any additional connections from the
IP until it's under the limit again.
If you use these limits, you should consider multiple clients may simultaneously
try to access the site through NAT, etc. So the limits should err on the side
of being generous, while still making it impossible for one IP to exhaust
all the server resources.
@section evtloop Libwebsockets is singlethreaded
Libwebsockets works in a serialized event loop, in a single thread. It supports
not only the default poll() backend, but libuv, libev, and libevent event loop
libraries that also take this locking-free, nonblocking event loop approach that
is not threadsafe. There are several advantages to this technique, but one
disadvantage, it doesn't integrate easily if there are multiple threads that
want to use libwebsockets.
Libwebsockets works in a serialized event loop, in a single thread.
However integration to multithreaded apps is possible if you follow some guidelines.
Directly performing websocket actions from other threads is not allowed.
Aside from the internal data being inconsistent in `forked()` processes,
the scope of a `wsi` (`struct websocket`) can end at any time during service
with the socket closing and the `wsi` freed.
1) Aside from two APIs, directly calling lws apis from other threads is not allowed.
Websocket write activities should only take place in the
`LWS_CALLBACK_SERVER_WRITEABLE` callback as described below.
2) If you want to keep a list of live wsi, you need to use lifecycle callbacks on
the protocol in the service thread to manage the list, with your own locking.
Typically you use an ESTABLISHED callback to add ws wsi to your list and a CLOSED
callback to remove them.
[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:
3) LWS regulates your write activity by being able to let you know when you may
write more on a connection. That reflects the reality that you cannot succeed to
send data to a peer that has no room for it, so you should not generate or buffer
write data until you know the peer connection can take more.
***ONLY DO LWS_WRITE FROM THE WRITEABLE CALLBACK***
Other libraries pretend that the guy doing the writing is the boss who decides
what happens, and absorb as much as you want to write to local buffering. That does
not scale to a lot of connections, because it will exhaust your memory and waste
time copying data around in memory needlessly.
There is another network-programming truism that surprises some people which
is if the sink for the data cannot accept more:
The truth is the receiver, along with the network between you, is the boss who
decides what will happen. If he stops accepting data, no data will move. LWS is
designed to reflect that.
If you have something to send, you call `lws_callback_on_writable()` on the
connection, and when it is writeable, you will get a `LWS_CALLBACK_SERVER_WRITEABLE`
callback, where you should generate the data to send and send it with `lws_write()`.
You cannot send data using `lws_write()` outside of the WRITEABLE callback.
4) For multithreaded apps, this corresponds to a need to be able to provoke the
`lws_callback_on_writable()` action and to wake the service thread from its event
loop wait (sleeping in `poll()` or `epoll()` or whatever). The rules above
mean directly sending data on the connection from another thread is out of the
question.
Therefore the two apis mentioned above that may be used from another thread are
- For LWS using the default poll() event loop, `lws_callback_on_writable()`
- For LWS using libuv/libev/libevent event loop, `lws_cancel_service()`
If you are using the default poll() event loop, one "foreign thread" at a time may
call `lws_callback_on_writable()` directly for a wsi. You need to use your own
locking around that to serialize multiple thread access to it.
If you implement LWS_CALLBACK_GET_THREAD_ID in protocols[0], then LWS will detect
when it has been called from a foreign thread and automatically use
`lws_cancel_service()` to additionally wake the service loop from its wait.
For libuv/libev/libevent event loop, they cannot handle being called from other
threads. So there is a slightly different scheme, you may call `lws_cancel_service()`
to force the event loop to end immediately. This then broadcasts a callback (in the
service thread context) `LWS_CALLBACK_EVENT_WAIT_CANCELLED`, to all protocols on all
vhosts, where you can perform your own locking and walk a list of wsi that need
`lws_callback_on_writable()` calling on them.
`lws_cancel_service()` is very cheap to call.
5) The obverse of this truism about the receiver being the boss is the case where
we are receiving. If we get into a situation we actually can't usefully
receive any more, perhaps because we are passing the data on and the guy we want
to send to can't receive any more, then we should "turn off RX" by using the
RX flow control API, `lws_rx_flow_control(wsi, 0)`. When something happens where we
can accept more RX, (eg, we learn our onward connection is writeable) we can call
it again to re-enable it on the incoming wsi.
LWS stops calling back about RX immediately you use flow control to disable RX, it
buffers the data internally if necessary. So you will only see RX when you can
handle it. When flow control is disabled, LWS stops taking new data in... this makes
the situation known to the sender by TCP "backpressure", the tx window fills and the
sender finds he cannot write any more to the connection.
***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.
If you need to service other socket or file descriptors as well as the
websocket ones, you can combine them together with the websocket ones
in one poll loop, see "External Polling Loop support" below, and
still do it all in one thread / process context. If the need is less
architectural, you can also create RAW mode client and serving sockets; this
is how the lws plugin for the ssh server works.
still do it all in one thread / process context.
@section anonprot Working without a protocol name
If you insist on trying to use it from multiple threads, take special care if
you might simultaneously create more than one context from different threads.
Websockets allows connections to negotiate without a protocol name...
in that case by default it will bind to the first protocol in your
vhost protocols[] array.
SSL_library_init() is called from the context create api and it also is not
reentrant. So at least create the contexts sequentially.
You can tell the vhost to use a different protocol by attaching a
pvo (per-vhost option) to the
```
/*
* this sets a per-vhost, per-protocol option name:value pair
* the effect is to set this protocol to be the default one for the vhost,
* ie, selected if no Protocol: header is sent with the ws upgrade.
*/
@section writeable Only send data when socket writeable
static const struct lws_protocol_vhost_options pvo_opt = {
NULL,
NULL,
"default",
"1"
};
You should only send data on a websocket connection from the user callback
`LWS_CALLBACK_SERVER_WRITEABLE` (or `LWS_CALLBACK_CLIENT_WRITEABLE` for
clients).
static const struct lws_protocol_vhost_options pvo = {
NULL,
&pvo_opt,
"my-protocol",
""
};
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
using that protocol to get a callback when next writeable.
context_info.pvo = &pvo;
...
Usually you will get called back immediately next time around the service
loop, but if your peer is slow or temporarily inactive the callback will be
delayed accordingly. Generating what to write and sending it should be done
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
Libwebsockets may generate additional `LWS_CALLBACK_CLIENT_WRITEABLE` events
if it met network conditions where it had to buffer your send data internally.
So your code for `LWS_CALLBACK_CLIENT_WRITEABLE` needs to own the decision
about what to send, it can't assume that just because the writeable callback
came it really is time to send something.
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.
Will select "my-protocol" from your protocol list (even if it came
in by plugin) as being the target of client connections that don't
specify a protocol.
@section closing Closing connections from the user side
@ -389,9 +220,10 @@ external polling array. That's needed if **libwebsockets** will
cooperate with an existing poll array maintained by another
server.
Three callbacks `LWS_CALLBACK_ADD_POLL_FD`, `LWS_CALLBACK_DEL_POLL_FD`
and `LWS_CALLBACK_CHANGE_MODE_POLL_FD` appear in the callback for protocol 0
and allow interface code to manage socket descriptors in other poll loops.
Four callbacks `LWS_CALLBACK_ADD_POLL_FD`, `LWS_CALLBACK_DEL_POLL_FD`,
`LWS_CALLBACK_SET_MODE_POLL_FD` and `LWS_CALLBACK_CLEAR_MODE_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
if the socket or file does not belong to **libwebsockets** it is safe.
@ -412,26 +244,6 @@ reflecting the real event:
- use LWS_POLLHUP / LWS_POLLIN / LWS_POLLOUT from libwebsockets.h to avoid
losing windows compatibility
You also need to take care about "forced service" somehow... these are cases
where the network event was consumed, incoming data was all read, for example,
but the work arising from it was not completed. There will not be any more
network event to trigger the remaining work, Eg, we read compressed data, but
we did not use up all the decompressed data before returning to the event loop
because we had to write some of it.
Lws provides an API to determine if anyone is waiting for forced service,
`lws_service_adjust_timeout(context, 1, tsi)`, normally tsi is 0. If it returns
0, then at least one connection has pending work you can get done by calling
`lws_service_tsi(context, -1, tsi)`, again normally tsi is 0.
For eg, the default poll() event loop, or libuv/ev/event, lws does this
checking for you and handles it automatically. But in the external polling
loop case, you must do it explicitly. Handling it after every normal service
triggered by the external poll fd should be enough, since the situations needing
it are initially triggered by actual network events.
An example of handling it is shown in the test-server code specific to
external polling.
@section cpp Using with in c++ apps
@ -439,7 +251,7 @@ The library is ready for use by C++ apps. You can get started quickly by
copying the test server
```
$ cp test-apps/test-server.c test.cpp
$ cp test-server/test-server.c test.cpp
```
and building it in C++ like this
@ -466,75 +278,6 @@ 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.
@section http2compat Code Requirements for HTTP/2 compatibility
Websocket connections only work over http/1, so there is nothing special to do
when you want to enable -DLWS_WITH_HTTP2=1.
The internal http apis already follow these requirements and are compatible with
http/2 already. So if you use stuff like mounts and serve stuff out of the
filesystem, there's also nothing special to do.
However if you are getting your hands dirty with writing response headers, or
writing bulk data over http/2, you need to observe these rules so that it will
work over both http/1.x and http/2 the same.
1) LWS_PRE requirement applies on ALL lws_write(). For http/1, you don't have
to take care of LWS_PRE for http data, since it is just sent straight out.
For http/2, it will write up to LWS_PRE bytes behind the buffer start to create
the http/2 frame header.
This has implications if you treated the input buffer to lws_write() as const...
it isn't any more with http/2, up to 9 bytes behind the buffer will be trashed.
2) Headers are encoded using a sophisticated scheme in http/2. The existing
header access apis are already made compatible for incoming headers,
for outgoing headers you must:
- observe the LWS_PRE buffer requirement mentioned above
- Use `lws_add_http_header_status()` to add the transaction status (200 etc)
- use lws apis `lws_add_http_header_by_name()` and `lws_add_http_header_by_token()`
to put the headers into the buffer (these will translate what is actually
written to the buffer depending on if the connection is in http/2 mode or not)
- use the `lws api lws_finalize_http_header()` api after adding the last
response header
- write the header using lws_write(..., `LWS_WRITE_HTTP_HEADERS`);
3) http/2 introduces per-stream transmit credit... how much more you can send
on a stream is decided by the peer. You start off with some amount, as the
stream sends stuff lws will reduce your credit accordingly, when it reaches
zero, you must not send anything further until lws receives "more credit" for
that stream the peer. Lws will suppress writable callbacks if you hit 0 until
more credit for the stream appears, and lws built-in file serving (via mounts
etc) already takes care of observing the tx credit restrictions. However if
you write your own code that wants to send http data, you must consult the
`lws_get_peer_write_allowance()` api to find out the state of your tx credit.
For http/1, it will always return (size_t)-1, ie, no limit.
This is orthogonal to the question of how much space your local side's kernel
will make to buffer your send data on that connection. So although the result
from `lws_get_peer_write_allowance()` is "how much you can send" logically,
and may be megabytes if the peer allows it, you should restrict what you send
at one time to whatever your machine will generally accept in one go, and
further reduce that amount if `lws_get_peer_write_allowance()` returns
something smaller. If it returns 0, you should not consume or send anything
and return having asked for callback on writable, it will only come back when
more tx credit has arrived for your stream.
4) Header names with captital letters are illegal in http/2. Header names in
http/1 are case insensitive. So if you generate headers by name, change all
your header name strings to lower-case to be compatible both ways.
5) Chunked Transfer-encoding is illegal in http/2, http/2 peers will actively
reject it. Lws takes care of removing the header and converting CGIs that
emit chunked into unchunked automatically for http/2 connections.
If you follow these rules, your code will automatically work with both http/1.x
and http/2.
@section ka TCP Keepalive
@ -578,20 +321,6 @@ 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).
@section sslcerts Passing your own cert information direct to SSL_CTX
For most users it's enough to pass the SSL certificate and key information by
giving filepaths to the info.ssl_cert_filepath and info.ssl_private_key_filepath
members when creating the vhost.
If you want to control that from your own code instead, you can do so by leaving
the related info members NULL, and setting the info.options flag
LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX at vhost creation time. That will create
the vhost SSL_CTX without any certificate, and allow you to use the callback
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS to add your certificate to
the SSL_CTX directly. The vhost SSL_CTX * is in the user parameter in that
callback.
@section clientasync Async nature of client connections
When you call `lws_client_connect_info(..)` and get a `wsi` back, it does not
@ -608,7 +337,7 @@ other reasons, if any of that happens you'll get a
After attempting the connection and getting back a non-`NULL` `wsi` you should
loop calling `lws_service()` until one of the above callbacks occurs.
As usual, see [test-client.c](../test-apps/test-client.c) for example code.
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
@ -743,9 +472,7 @@ callbacks on the named protocol
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
The minimal example `raw/minimal-raw-file` demonstrates how to use it.
`protocol-lws-raw-test` plugin also provides a method for testing this with
`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"
@ -829,46 +556,6 @@ and in another window, connect to it using the test client
The connection should succeed, and text typed in the netcat window (including a CRLF)
will be received in the client.
@section rawudp RAW UDP socket integration
Lws provides an api to create, optionally bind, and adopt a RAW UDP
socket (RAW here means an uninterpreted normal UDP socket, not a
"raw socket").
```
LWS_VISIBLE LWS_EXTERN struct lws *
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
const char *protocol_name, struct lws *parent_wsi);
```
`flags` should be `LWS_CAUDP_BIND` if the socket will receive packets.
The callbacks `LWS_CALLBACK_RAW_ADOPT`, `LWS_CALLBACK_RAW_CLOSE`,
`LWS_CALLBACK_RAW_RX` and `LWS_CALLBACK_RAW_WRITEABLE` apply to the
wsi. But UDP is different than TCP in some fundamental ways.
For receiving on a UDP connection, data becomes available at
`LWS_CALLBACK_RAW_RX` as usual, but because there is no specific
connection with UDP, it is necessary to also get the source address of
the data separately, using `struct lws_udp * lws_get_udp(wsi)`.
You should take a copy of the `struct lws_udp` itself (not the
pointer) and save it for when you want to write back to that peer.
Writing is also a bit different for UDP. By default, the system has no
idea about the receiver state and so asking for a `callback_on_writable()`
always believes that the socket is writeable... the callback will
happen next time around the event loop.
With UDP, there is no single "connection". You need to write with sendto() and
direct the packets to a specific destination. To return packets to a
peer who sent something earlier and you copied his `struct lws_udp`, you
use the .sa and .salen members as the last two parameters of the sendto().
The kernel may not accept to buffer / write everything you wanted to send.
So you are responsible to watch the result of sendto() and resend the
unsent part next time (which may involve adding new protocol headers to
the remainder depending on what you are doing).
@section ecdh ECDH Support
ECDH Certs are now supported. Enable the CMake option
@ -911,7 +598,7 @@ Returning nonzero from the callback will close the wsi.
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.
use -j <n> argument there to control the number of service threads up to 32.
Two new members are added to the info struct
@ -941,74 +628,35 @@ 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 1. The serv_buf allocation
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.
See the test-server-pthreads.c sample for how to use.
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.
@section smplocking SMP Locking Helpers
Lws provide a set of pthread mutex helpers that reduce to no code or
variable footprint in the case that LWS_MAX_SMP == 1.
Define your user mutex like this
```
lws_pthread_mutex(name);
```
If LWS_MAX_SMP > 1, this produces `pthread_mutex_t name;`. In the case
LWS_MAX_SMP == 1, it produces nothing.
Likewise these helpers for init, destroy, lock and unlock
There is no knowledge or dependency in lws itself about pthreads. How the
locking is implemented is entirely up to the user code.
```
void lws_pthread_mutex_init(pthread_mutex_t *lock)
void lws_pthread_mutex_destroy(pthread_mutex_t *lock)
void lws_pthread_mutex_lock(pthread_mutex_t *lock)
void lws_pthread_mutex_unlock(pthread_mutex_t *lock)
```
resolve to nothing if LWS_MAX_SMP == 1, otherwise produce the equivalent
pthread api.
pthreads is required in lws only if LWS_MAX_SMP > 1.
@section libevuv libev / libuv / libevent support
@section libevuv Libev / Libuv support
You can select either or both
-DLWS_WITH_LIBEV=1
-DLWS_WITH_LIBUV=1
-DLWS_WITH_LIBEVENT=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
LWS_SERVER_OPTION_LIBEVENT
to indicate it will use one of the event libraries at runtime.
to indicate it will use either of the event libraries.
libev has some problems, its headers conflict with libevent, they both define
critical constants like EV_READ to different values. Attempts
to discuss clearing that up with libevent and libev did not get anywhere useful.
In addition building anything with libev using gcc spews warnings, the
maintainer is aware of this for many years, and blames gcc. We worked
around this by disabling -Werror on the parts of lws that use libev.
For these reasons and the response I got trying to raise these issues with
them, if you have a choice about event loop, I would gently encourage you
to avoid libev. Where lws uses an event loop itself, eg in lwsws, we use
libuv.
@section extopts Extension option control from user code
@ -1080,41 +728,7 @@ 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 clipipe Pipelining Client Requests to same host
If you are opening more client requests to the same host and port, you
can give the flag LCCSCF_PIPELINE on `info.ssl_connection` to indicate
you wish to pipeline them.
Without the flag, the client connections will occur concurrently using a
socket and tls wrapper if requested for each connection individually.
That is fast, but resource-intensive.
With the flag, lws will queue subsequent client connections on the first
connection to the same host and port. When it has confirmed from the
first connection that pipelining / keep-alive is supported by the server,
it lets the queued client pipeline connections send their headers ahead
of time to create a pipeline of requests on the server side.
In this way only one tcp connection and tls wrapper is required to transfer
all the transactions sequentially. It takes a little longer but it
can make a significant difference to resources on both sides.
If lws learns from the first response header that keepalive is not possible,
then it marks itself with that information and detaches any queued clients
to make their own individual connections as a fallback.
Lws can also intelligently combine multiple ongoing client connections to
the same host and port into a single http/2 connection with multiple
streams if the server supports it.
Unlike http/1 pipelining, with http/2 the client connections all occur
simultaneously using h2 stream multiplexing inside the one tcp + tls
connection.
You can turn off the h2 client support either by not building lws with
`-DLWS_WITH_HTTP2=1` or giving the `LCCSCF_NOT_H2` flag in the client
connection info struct `ssl_connection` member.
@section vhosts Using lws vhosts
@ -1274,15 +888,6 @@ 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).
This "binding connection to a protocol" lifecycle in managed by
`LWS_CALLBACK_HTTP_BIND_PROTOCOL` and `LWS_CALLBACK_HTTP_DROP_PROTOCOL`.
Because of HTTP/1.1 connection pipelining, one connection may perform
many transactions, each of which may map to different URLs and need
binding to different protocols. So these messages are used to
create the binding of the wsi to your protocol including any
allocations, and to destroy the binding, at which point you should
destroy any related allocations.
@section BINDTODEV SO_BIND_TO_DEVICE
The .bind_iface flag in the context / vhost creation struct lets you
@ -1325,7 +930,7 @@ also add this to your own html easily
- include lws-common.js from your HEAD section
\<script src="/lws-common.js">\</script>
<script src="/lws-common.js"></script>
- dim the page during initialization, in a script section on your page

View file

@ -1,8 +1,6 @@
ESP32 Support
=============
See \ref esp32 for details on how to build lws as a component in an ESP-IDF project.
Lws provides a "factory" application
https://github.com/warmcat/lws-esp32-factory
@ -23,15 +21,3 @@ Factory Reset or Uninitialized|Factory|AP: ESP_012345|80|http://192.168.4.1|fact
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
## Basic Auth
The lws-esp32-test-server-demos app also demos basic auth.
On a normal platform this is done by binding a mount to a text file somewhere in the filesystem, which
contains user:password information one per line.
On ESP32 there is not necessarily any generic VFS in use. So instead, the basic auth lookup is bound to
a given nvs domain, where the username is the key and the password the value. main/main.c in the test
demos app shows how to both make the mount use basic auth, and how to set a user:password combination
using nvs.

34
README.esp8266.md Normal file
View file

@ -0,0 +1,34 @@
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

@ -79,12 +79,6 @@ on port 7681, non-SSL is provided. To set it up
# sudo lwsws
```
@section lwswsacme Using Letsencrypt or other ACME providers
Lws supports automatic provisioning and renewal of TLS certificates.
See ./READMEs/README.plugin-acme.md for examples of how to set it up on an lwsws vhost.
@section lwsogo Other Global Options
- `reject-service-keywords` allows you to return an HTTP error code and message of your choice
@ -188,7 +182,7 @@ Vhosts can select which plugins they want to offer and give them per-vhost setti
```
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
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
@ -202,18 +196,6 @@ by the client, you can use "default": "1"
}]
```
Similarly, if your vhost is serving a raw protocol, you can mark the protocol
to be selected using "raw": "1"
```
"ws-protocols": [{
"warmcat-timezoom": {
"status": "ok",
"raw": "1"
}
}]
```
See also "rawonly" below.
@section lwswsovo Lwsws Other vhost options
@ -223,7 +205,7 @@ See also "rawonly" below.
- `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. If the network interface is not usable (eg, ethernet cable out) it will be logged at startup with such vhost not listening, and lws will poll for it and bind a listen socket to the interface if and when it becomes available.
- `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
@ -275,8 +257,6 @@ recommended vhost headers for good client security are
```
- "`rawonly`": "on" This vhost only serves a raw protocol, disable HTTP on it
@section lwswsm Lwsws Mounts
Where mounts are given in the vhost definition, then directory contents may
@ -341,7 +321,7 @@ provide them using "pmo"
}]
}
2) When using a cgi:// protocol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
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",
@ -436,17 +416,6 @@ 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 lwswscc Requiring a Client Cert on a vhost
You can make a vhost insist to get a client certificate from the peer before
allowing the connection with
```
"client-cert-required": "1"
```
the connection will only proceed if the client certificate was signed by the
same CA as the server has been told to trust.
@section lwswspl Lwsws Plugins
@ -621,7 +590,7 @@ this will give nice backtraces in lwsws itself and in plugins, if they were buil
@section lwswsvgd Running lwsws under valgrind
You can just run lwsws under valgrind as usual and get valid results. However the results / analysis part of valgrind runs
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.

135
README.md
View file

@ -2,91 +2,19 @@
[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/qfasji8mnfnd2r8t?svg=true)](https://ci.appveyor.com/project/lws-team/libwebsockets)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3576/badge.svg)](https://scan.coverity.com/projects/3576)
![lws-overview](./doc-assets/lws-overview.png)
libwebsockets
-------------
News
----
## v3.0.0 released
See the changelog for info https://github.com/warmcat/libwebsockets/blob/v3.0-stable/changelog
## Major CI improvements for QA
The Travis build of lws done on every commit now runs
Tests|Count|Explanation
---|---|---
Build / Linux / gcc|14|-Wall -Werror cmake config variants
Build / Mac / Clang|14|-Wall -Werror cmake config variants
Build / Windows / MSVC|7|default
Selftests|openssl:33, mbedtls:33|minimal examples built and run against each other and remote server
attack.sh|225|Correctness, robustness and security tests for http parser
Autobahn Server|480|Testing lws ws client, including permessage-deflate
Autobahn Client|480|Testing lws ws server, including permaessage-deflate
h2spec|openssl:146, mbedtls:146|Http/2 server compliance suite (in strict mode)
h2load|openssl:6, mbedtls:6|Http/2 server load tool (checks 10K / 100K in h1 and h2, at 1, 10, 100 concurrency)
h2load SMP|6|Http/2 and http/1.1 server load checks on SMP server build
The over 1,500 tests run on every commit take most of an hour to complete.
If any problems are found, it breaks the travis build, generating an email.
Current master passes all the tests and these new CI arrangements will help
keep it that way.
## Lws has the first official ws-over-h2 server support
![wss-over-h2](https://libwebsockets.org/sc-wss-over-h2.png)
There's a new standard on the RFC track that enables multiplexing ws connections
over an http/2 link. Compared to making individual tcp and tls connections for
each ws link back to the same server, this makes your site start up radically
faster, and since all the connections are in one tls tunnel, with considerable memory
reduction serverside.
To enable it on master you just need -DLWS_WITH_HTTP2=1 at cmake. No changes to
existing code are necessary for either http/2 (if you use the official header creation
apis if you return your own headers, as shown in the test apps for several versions)
or to take advantage of ws-over-h2. When built with http/2 support, it automatically
falls back to http/1 and traditional ws upgrade if that's all the client can handle.
Currently only Chrome Canary v67 supports this ws-over-h2 encapsulation (chrome
must be started with `--enable-websocket-over-http2` switch to enable it currently)
but the other browsers will catch up soon.
## New "minimal examples"
https://github.com/warmcat/libwebsockets/tree/master/minimal-examples
These are like the test apps, but focus on doing one thing, the best way, with the minimum amount of code. For example the minimal-http-server serves the cwd on http/1 or http/2 in 50 LOC. Same thing with tls is just three more lines.
They build standalone, so it's easier to copy them directly to start your own project; they
are CC0 licensed (public domain) to facilitate that.
## Windows binary builds
32- and 64-bit Windows binary builds are available via Appveyor. Visit [lws on Appveyor](https://ci.appveyor.com/project/lws-team/libwebsockets),
click on a build, the ARTIFACTS, and unzip the zip file at `C:\Program Files (x86)/libwebsockets`.
## Latest Stable
- v2.4.2 is out... HTTP/2 server support and mbedTLS as a TLS backend.
see the changelog https://github.com/warmcat/libwebsockets/blob/v2.4-stable/changelog
Please note the additional READMEs have moved to ./READMEs/
## ESP32 is supported
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
The ESP32 stuff has my dynamic mbedtls buffer allocation patches applied,
which reduce allocation for small payload TLS links by around 26KiB per connection.
## Support
This is the libwebsockets C library for lightweight websocket clients and
servers. For support, visit
@ -105,3 +33,60 @@ You can get the latest version of the library from git:
Doxygen API docs for master: https://libwebsockets.org/lws-api-doc-master/html/index.html
After libwebsockets 1.3, tags will be signed using a key corresponding to this public key
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQINBFRe35QBEADZA7snW7MoEXkT2deDYZeggVD3694dg1o5G4q36NWjC8Pn/b2V
d+L9Nmw8ydKIv8PLJW762rnveQpPYRqCRD8X4bVTYzYz3qsOl5BrYf6cuVn0ZrPB
13TVRg+NZwUaVxc7O+tdOvvEBdA9OCIygctPNK9Nyh53xs5gPHhghZrKVrt0xM1A
2LYsgoHmMBCCY25SHb1nuapvhA3LvuJb4cNNVRCukCoA6yx0uhSEz2AUPJSLqnZ9
XnNBMKq+1a9C+y7jo4O78upTTmuOmRmNEVAu7pxCSUXDrNa87T8n6vFkV/MiW8nv
VmhppKJrKPJ0KxJF9b7uG6eKosfoK2PKyE7pAoDN1fuNyBTB0dkFAwyTCN8hmhOg
z71QrCltotq/AxSCsKzgFkDBL7D3KUM10QR5kmznjcm8tFWHoSttPR334z/1Yepf
ATqH/tfYydW42qeeHgKjfeegnlI65nTDtwYW6lSqZsXg+/ABg0ki9m5HA6l713ig
gRbVHSNkiz56O+UOqBtfcJZBc8QZqqixq8rbP2Is0HBBEtD+aFMuKx/sQ3ULkQs2
8dZ5qsGTBT/xHmqpHJsIFX/jwjY5zeEiFbnO5bMH7YLmkjynVsn5zxTyXKQJe29C
Uq0Yd9+JpDhHnZoiz/1hIIBsr89Z4Yy6c59YNJ3yJEOast0ODERcKSaUAQARAQAB
tC9BbmR5IEdyZWVuIChMaW5hcm8ga2V5KSA8YW5keS5ncmVlbkBsaW5hcm8ub3Jn
PokCPQQTAQoAJwUCVF7flAIbAwUJBaOagAULCQgHAwUVCgkICwUWAwIBAAIeAQIX
gAAKCRA8ZxoDS3lTexApD/9WT7JWy3tK33OIACYV40XwLEhRam4Xku4rhtsoIeJK
P0k/wa7J2PpceX6gKV+QBsOx3UbUfpqZ/Mu7ff3M0J6W87XpKRROAmP43zyiBkmM
A6v0pJXozknmCU28p3DuLC8spVDFg9N52xV7Qb+9TDHcTYiVi4swKYuDEuHBC/qa
M69+ANgsGbrMFRypxtU7OEhls3AEo3Cq03xD8QvLjFyPpYp1f0vNRFm2Jjgm2CRe
YLVsCGxG35Dz7DpJHekHNxje6xsZ2w9Q38M0rLQ0ICOVQ+E1Dir3hwmZQWASzMMi
+R0P+MVYpVt5y7KtiLywJ4BzNogS7gY3wQxksJOFA1uuk5h/hO54a361mcdA0Ta5
HHhGKRw87lVjEQSaRjFZmHFClB+Sb8MuWR51JTzVS5HtJlcNqcWhF63vZ8bZ7b6y
Aj8cXNjH6ULXyX3QnTUWXX/QU3an3yh8iPONWOGP5d5Hi/qejHGIhP2L5H+h05CP
aZQYFLjjebYgEHijuA28eKWsBsoBPFSLpLloHTDkiycgFdV2AkQcxZN9ZElAqURP
xUkEIscQg3YhExGiVEtaxBp1/p/WctMxs5HNoi0Oc97ZUcKvSCz9FDGXX9wYBpRf
gzjNn055Xn4QyxBDnp5DrYT0ft/8BEnRK0JP6z3gNfnhOxZo4XA+M6w4Hjh3tI2A
3rkCDQRUXt+UARAA0yHmONtW3L1HpvWFR+VgVNHa1HBWWk7lMsI6ajeiUK/lN3F/
+vNbux46bPj/sNT9twbWmYhv6c0yVzCpmv5M5ztefS7mW/zPNLJmCmH32kAvVFr1
Z90R/X+Z1Uh8wCCU72S2pSIXQFza3LF53pbpKi5m1F2icYcx+35egAvvZVZtcrMu
TjHUa+N9mFKxa7tb5PI8Lv93nRLwB7aKkp5PKy9Yvse0jACrAAGeIpI73H467/wO
ujermKlyPOOv+Lpjd7kedWKdaweitva7FVI20K/afn4AwCI8HJUIqVbil0Yrg9Le
M1TRsRydzMQQejsb/cWi3fQ3U3HxvSJijKltckPMqjJaXbqmrLz3FOA5Km0ciIOB
WW0Qq0WREcS3rc5FHU29duS9OAieAWFYyLDieug4nQ29KQE6I0lMqLnz8vWYtbmw
6AHk9i2GsXOZiPnztuADgt9o9Os8fm7ZiacA1LISl86P7wpFk+Gf4LRvv8Fk08NV
b2K1BY4YC9KP+AynyYQmxmyB1YQCh/dZHiD4ikGKttHAy4ZsMW6IRL5bRP0Z97pA
lyBtXP0cGTJtuPt2feh0zaiA7blZ/IDXkB1UqH6jnTa71d1FeNKtVFi8FhPIREN6
Rc5imyRxubZEgsxhdjqGgdT5k6Qr42SewAN391ygutpgGizGQtTwzvmKa0UAEQEA
AYkCJQQYAQoADwUCVF7flAIbDAUJBaOagAAKCRA8ZxoDS3lTewuBD/9/rakAMCRC
+WmbUVpCbJSWP5ViH87Xko4ku437gq56whcGjQpxfCYt8oeVgS8fZetUOHs2gspJ
CEc8TYLUFntfyt2AzKU29oQoizPm33W9S1u7aRGWsVVutd2sqUaQUDsl9z35+Ka9
YcWoATJSWBgnhSAmNcM60OG0P5qrZloTlbRSlDZTSZT3RvY4JWtWCubGsjEpXO4h
ZqbKCu3KgV/6NOuTLciriSOZ/iyva3WsCP2S8mRRvma7x04oMTEWX80zozTCK8gG
XqqS9eDhCkRbdmMyUQbHIhc/ChYchO5+fQ1o0zMS5cv6xgkhWI3NJRUkNdXolH9a
5F9q4CmCTcdEZkqpnjsLNiQLIENfHbgC0A5IjR6YgN6qAP8ZJ5hBgyTfyKkwB7bW
DcCnuoC9R79hkI8nWkoRVou9tdzKxo0bGR6O4CfLj+4d3hpWkv9Rw7Xxygo5JOqN
4cNZGtHkmIFFk9fSXul5rkjfF/XmThIwoI8aHSBZ7j3IMtmkKVkBjNjiTfbgW8RT
XIIR+QQdVLOyJqq+NZC/SrKVQITg0ToYJutRTUJViqyz5b3psJo5o2SW6jcexQpE
cX6tdPyGz3o0aywfJ9dcN6izleSV1gYmXmIoS0cQyezVqTUkT8C12zeRB7mtWsDa
+AWJGq/WfB7N6pPh8S/XMW4e6ptuUodjiA==
=HV8t
-----END PGP PUBLIC KEY BLOCK-----
```

View file

@ -2,7 +2,7 @@ Overview of lws test apps
=========================
Are you building a client? You just need to look at the test client
[libwebsockets-test-client](../test-apps/test-client.c).
[libwebsockets-test-client](test-server/test-client.c).
If you are building a standalone server, there are three choices, in order of
preferability.
@ -13,12 +13,12 @@ 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
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](../READMEs/README.lwsws.md) for information on how to configure
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
@ -28,13 +28,11 @@ implementations of timers, dynamic lib loading etc for plugins and lwsws.
This method lets you configure web serving in code, instead of using lwsws.
Plugins are still used, but you have a choice whether to dynamically load
them or statically include them. In this example, they are dynamically
loaded.
Plugins are still used, which implies libuv needed.
$ cmake .. -DLWS_WITH_PLUGINS=1
See [test-server-v2.0.c](../test-apps/test-server-v2.0.c)
See [test-server-v2.0.c](test-server/test-server-v2.0.c)
3) protocols in the server app
@ -45,23 +43,13 @@ 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.
You can simply include the plugin contents and have it buit statically into
your server, just define this before including the plugin source
```
#define LWS_PLUGIN_STATIC
```
This gets you most of the advantages without needing dynamic loading +
libuv.
Notes about lws test apps
=========================
@section tsb Testing server with a browser
If you run [libwebsockets-test-server](../test-apps/test-server.c) and point your 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
@ -86,7 +74,7 @@ terminates.
To stop the daemon, do
```
$ 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.
@ -94,60 +82,6 @@ any more) it will delete the lock and create a new one during startup.
If the lock is valid, the daemon will exit with a note on stderr that
it was already running.
@section clicert Testing Client Certs
Here is a very quick way to create a CA, and a client and server cert from it,
for testing.
```
$ cp -rp ./scripts/client-ca /tmp
$ cd /tmp/client-ca
$ ./create-ca.sh
$ ./create-server-cert.sh server
$ ./create-client-cert.sh client
```
The last step wants an export password, you will need this password again to
import the p12 format certificate into your browser.
This will get you the following
|name|function|
|----|--------|
|ca.pem|Your Certificate Authority cert|
|ca.key|Private key for the CA cert|
|client.pem|Client certificate, signed by your CA|
|client.key|Client private key|
|client.p12|combined client.pem + client.key in p12 format for browsers|
|server.pem|Server cert, signed by your CA|
|server.key|Server private key|
You can confirm yourself the client and server certs are signed by the CA.
```
$ openssl verify -verbose -trusted ca.pem server.pem
$ openssl verify -verbose -trusted ca.pem client.pem
```
Import the client.p12 file into your browser. In FFOX57 it's
- preferences
- Privacy & Security
- Certificates | View Certificates
- Certificate Manager | Your Certificates | Import...
- Enter the password you gave when creating client1.p12
- Click OK.
You can then run the test server like this:
```
$ libwebsockets-test-server -s -A ca.pem -K server.key -C server.pem -v
```
When you connect your browser to https://localhost:7681 after accepting the
selfsigned server cert, your browser will pop up a prompt to send the server
your client cert (the -v switch enables this). The server will only accept
a client cert that has been signed by ca.pem.
@section sssl Using SSL on the server side
@ -166,7 +100,7 @@ certificates in the browser and the connection will proceed
in first https and then websocket wss, acting exactly the
same.
[test-server.c](../test-apps/test-server.c) is all that is needed to use libwebsockets for
[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
@ -177,28 +111,6 @@ 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 unixskt Testing Unix Socket Server support
Start the test server with -U and the path to create the unix domain socket
```
$ libwebsockets-test-server -U /tmp/uds
```
On exit, lws will delete the socket inode.
To test the client side, eg
```
$ nc -C -U /tmp/uds -i 30
```
and type
`GET / HTTP/1.1`
followed by two ENTER. The contents of test.html should be returned.
@section wscl Testing websocket client support
If you run the test server as described above, you can also
@ -238,6 +150,30 @@ For those two options libuv is needed to support the protocol plugins, if
that's not possible then the other variations with their own protocol code
should be considered.
@section echo Testing simple echo
You can test against `echo.websockets.org` as a sanity test like
this (the client connects to port `80` by default):
```
$ libwebsockets-test-echo --client echo.websocket.org
```
This echo test is of limited use though because it doesn't
negotiate any protocol. You can run the same test app as a
local server, by default on localhost:7681
```
$ libwebsockets-test-echo
```
and do the echo test against the local echo server
```
$ libwebsockets-test-echo --client localhost --port 7681
```
If you add the `--ssl` switch to both the client and server, you can also test
with an encrypted link.
@section tassl Testing SSL on the client side
To test SSL/WSS client action, just run the client test with
@ -412,25 +348,37 @@ treatment to the other app during that call.
@section autobahn Autobahn Test Suite
Lws can be tested against the autobahn websocket fuzzer in both client and
server modes
Lws can be tested against the autobahn websocket fuzzer.
1) pip install autobahntestsuite
2) From your build dir: cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1 && make
2) wstest -m fuzzingserver
3) ../scripts/autobahn-test.sh
3) Run tests like this
4) In a browser go to the directory you ran wstest in (eg, /projects/libwebsockets)
libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=20&agent=libwebsockets" -v -d 65535 -n 1
file:///projects/libwebsockets/build/reports/clients/index.html
(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) Two of the tests make no sense for Libwebsockets to support and we fail them.
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.
@ -438,7 +386,5 @@ The autobahn test itself admits this is not part of the standard, just someone's
random opinion about how they think a ws server should act. So we will fail
this by design and it is no problem about RFC6455 compliance.
2) Currently two parts of autobahn are broken and we skip them
https://github.com/crossbario/autobahn-testsuite/issues/71

View file

@ -1,250 +0,0 @@
ssh-base Plugin
================
## Introduction
lws-ssh-base is a protcol plugin for libwebsockets that implements a
generic, abstract, ssh server.
- very small footprint in code and memory, takes up small part of ESP32
- written with security in mind: valgrind and Coverity -clean
- binds to one or more vhosts, that controls listen port(s)
- all IO and settings abstracted through a single "ops" struct from user code
- each instance on a vhost has its own "ops" struct, defining server keys,
auth method and functions to implement IO and other operations
- The plugin has no built-in behaviours like check ~/.ssh/authorized_keys,
treat auth usernames as system usernames, or spawn the user's shell.
Everything potentially dangerous is left to the user ops code to decide
how to handle. It's NOT like sshd where running it implies it will accept
existing keys for any system user, will spawn a shell, etc, unless you
implement those parts in the ops callbacks.
- The plugin requires extra code around it in the form of the ops struct
handlers. So it's role is something like an abstract base class for an ssh
server. All the crypto, protocol sequencing and state machine are inside,
but all the IO except the network connection is outside.
- Built as part of libwebsockets, like all plugins may be dynamically loaded
at runtime or built statically. Test app `libwebsockets-test-sshd` provided
- Uses hash and RSA functions from either mbedTLS or OpenSSL automatically,
according to which library libwebsockets was built for
To maintain its small size, it implements a single "best of breed" crypto for
the following functions:
|Function|Crypto|
|---|---|
|KEX|curve25519-sha256@libssh.org|
|Server host key|ssh-rsa (4096b)|
|Encryption|chacha20-poly1305@openssh.com|
|Compression|None|
## License
lws-ssh-base is Free Software, available under libwebsocket's LGPLv2 +
static linking exception license.
The crypto parts are available elsewhere under a BSD license. But for
simplicity the whole plugin is under LGPLv2.
## Generating your own keys
```
$ ssh-keygen -t rsa -b 4096 -f mykeys
```
will ask for a passphrase and generate the private key in `mykeys` and the
public key in `mykeys.pub`. If you already have a suitable RSA key you use
with ssh, you can just use that directly.
lws installs a test keypair in /usr[/local]/share/libwebsockets-test-server
that the test apps will accept.
## Example code
1) There's a working example app `libwebsockets-test-sshd` included that
spawns a bash shell when an ssh client authenticates. The username used on
the remote ssh has no meaning, it spawns the shell under the credentials of
"lws-test-sshd" was run under. It accepts the lws ssh test key which is
installed into /usr[/local]/share/libwebsockets-test-server.
Start the server like this (it wants root only because the server key is stored
in /etc)
```
$ sudo libwebsockets-test-sshd
```
Connect to it using the test private key like this
```
$ ssh -p 2200 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys anyuser@127.0.0.1
```
2) There's also a working example plugin `lws-sshd-demo` that "subclasses" the
abstract `lws-ssh-base` plugin to make a protocol which can be used from,
eg, lwsws. For an lwsws vhost that listens on port 2222 and responds with
the lws-sshd-demo ssh server, the related config is:
```
{
"name": "sshd",
"port": "2222",
"onlyraw": "1",
"ws-protocols": [{
"lws-ssh-base": {
"status": "ok",
"ops-from": "lws-sshd-demo"
},
"lws-sshd-demo": {
"status": "ok",
"raw": "1"
}
}]
}
```
## Integration to other apps
### Step 0: Build and install libwebsockets
For the `libwebsockets-test-sshd` example, you will need CMake options
`LWS_WITH_CGI`, since it uses lws helpers to spawn a shell.
lws-ssh-base itself doesn't require CGI support in libwebsockets.
### Step 1: make the code available in your app
Include `lws-plugin-ssh-base` in your app, either as a runtime plugin or by using
the lws static include scheme.
To bring in the whole of the ssh-base plugin
into your app in one step, statically, just include
`plugins/ssh-base/include/lws-plugin-sshd-static-build-includes.h`, you can see
an example of this in `./test-apps/test-sshd.c`.
### Step 2: define your `struct lws_ssh_ops`
`plugins/ssh-base/include/lws-plugin-ssh.h` defines
`struct lws_ssh_ops` which is used for all customization and integration
of the plugin per vhost. Eg,
```
static const struct lws_ssh_ops ssh_ops = {
.channel_create = ssh_ops_channel_create,
.channel_destroy = ssh_ops_channel_destroy,
.tx_waiting = ssh_ops_tx_waiting,
.tx = ssh_ops_tx,
.rx = ssh_ops_rx,
.get_server_key = ssh_ops_get_server_key,
.set_server_key = ssh_ops_set_server_key,
.set_env = ssh_ops_set_env,
.pty_req = ssh_ops_pty_req,
.child_process_io = ssh_ops_child_process_io,
.child_process_terminated = ssh_ops_child_process_terminated,
.exec = ssh_ops_exec,
.shell = ssh_ops_shell,
.is_pubkey_authorized = ssh_ops_is_pubkey_authorized,
.banner = ssh_ops_banner,
.disconnect_reason = ssh_ops_disconnect_reason,
.server_string = "SSH-2.0-Libwebsockets",
.api_version = 1,
};
```
The `ssh_ops_...()` functions are your implementations for the operations
needed by the plugin for your purposes.
### Step 3: enable `lws-ssh-base` protocol to a vhost and configure using pvo
A pointer to your struct lws_ssh_ops is passed into the vhost instance of the
protocol using per-vhost options
```
static const struct lws_protocol_vhost_options pvo_ssh_ops = {
NULL,
NULL,
"ops",
(void *)&ssh_ops
};
static const struct lws_protocol_vhost_options pvo_ssh = {
NULL,
&pvo_ssh_ops,
"lws-sshd-base",
"" /* ignored, just matches the protocol name above */
};
...
info.port = 22;
info.options = LWS_SERVER_OPTION_ONLY_RAW;
info.vhost_name = "sshd";
info.protocols = protocols_sshd;
info.pvo = &pvo_ssh;
vh_sshd = lws_create_vhost(context, &info);
```
There are two possible pvos supported, "ops", shown above, directly passes the
ops structure in using the value on the "ops" pvo.
To support other protocols that want to provide ops to lws-ssh-base themselves
for a particular vhost, you can also provide a pvo `"ops-from"` whose value is
the name of the protocol also enabled on this vhost, whose protocol ".user"
pointer points to the ops struct lws-ssh-base should use.
## Integration to other plugins
A worked example of using the abstract `lws-ssh-base` plugin from another
plugin that provides the ops struct is in `./plugins/protocol_lws_sshd_demo`.
The key points to note
- the plugin sets the ops struct for the vhost instantiation of `lws-ssh-base`
by passing a pointer to the ops struct in its `lws_protocols` struct `user`
member.
- the config for the vhost tells `lws-ssh-base` to pick up the ops struct
pointer using an "ops-from" pvo that indicates the protocol name.
```
"lws-ssh-base": {
"status": "ok",
"ops-from": "lws-sshd-demo"
},
```
- the config for the vhost tells lws this vhost only serves RAW (ie, no http)
```
{
"name": "sshd",
"port": "2222",
"onlyraw": "1",
...
```
- the config for the vhost marks the protocol that uses `lws-ssh-base`, not
`lws-ssh-base` itself, as the protocol to be served for raw connections
```
"lws-sshd-demo": {
"status": "ok",
"raw": "1"
...
```
## Notes
You can have the vhost it binds to listen on a nonstandard port. The ssh
commandline app cane be told to connect to a non-22 port with
`ssh -p portnum user@hostname`

View file

@ -1,711 +0,0 @@
Notes about building lws
========================
@section cm 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
CMake also allows/recommends you to do "out of source"-builds, that is,
the build files are separated from your sources, so there is no need to
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):
- Windows (Visual Studio)
- Windows (MinGW)
- Linux (x86 and ARM)
- OSX
- NetBSD
@section build1 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
output a Visual Studio project or Make file for instance. To see a list of
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:
1. Install CMake 2.8 or greater: http://cmake.org/cmake/resources/software.html
(Most Unix distributions comes with a packaged version also)
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 ".."
**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 .
```
**NOTE3**:
On machines that want libraries in lib64, you can also add the
following to the cmake line
```
-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,
```
$ 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
```
$ 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
$ 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
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)
1. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
2. Install OpenSSL binaries. https://wiki.openssl.org/index.php/Binaries
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
**NOTE2**:
Be sure that OPENSSL_CONF environment variable is defined and points at
<OpenSSL install location>\bin\openssl.cfg
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" ..
```
(**NOTE**: There is also a cmake-gui available on Windows if you prefer that)
**NOTE2**:
See this link to find out the version number corresponding to your Visual Studio edition:
http://superuser.com/a/194065
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)
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)
typedef struct pollfd {
SOCKET fd;
SHORT events;
SHORT revents;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
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;
```
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
3. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html
4. Install OpenSSL binaries. https://wiki.openssl.org/index.php/Binaries
(**NOTE**: Preferably in the default location to make it easier for CMake to find them)
**NOTE2**:
Be sure that OPENSSL_CONF environment variable is defined and points at
<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 ..
```
(**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 "..")
**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 ..
```
6. Finally you can build using the generated Makefile and get the results deployed into your MinGW installation:
```
$ make
$ make install
```
@section distro Selecting CMake options useful for distros
Distro packagers should select the CMake option "LWS_WITH_DISTRO_RECOMMENDED",
which selects common additional options like support for various event libraries,
plugins and lwsws.
@section ssllib Choosing Your TLS Poison
- If you are really restricted on memory, code size, or don't care about TLS
speed, mbedTLS is a good choice: `cmake .. -DLWS_WITH_MBEDTLS=1`
- If cpu and memory is not super restricted and you care about TLS speed,
OpenSSL or a directly compatible variant like Boring SSL is a good choice.
Just building lws against stock Fedora OpenSSL or stock Fedora mbedTLS, for
SSL handhake mbedTLS takes ~36ms and OpenSSL takes ~1ms on the same x86_64
build machine here, with everything else the same. Over the 144 connections of
h2spec compliance testing for example, this ends up completing in 400ms for
OpenSSL and 5.5sec for mbedTLS on x86_64. In other words mbedTLS is very slow
compared to OpenSSL under the (fairly typical) conditions I tested it.
This isn't an inefficiency in the mbedtls interface implementation, it's just
mbedTLS doing the crypto much slower than OpenSSL, which has accelerated
versions of common crypto operations it automatically uses for platforms
supporting it. As of Oct 2017 mbedTLS itself has no such optimizations for any
platform that I could find. It's just pure C running on the CPU.
Lws supports both almost the same, so instead of taking my word for it you are
invited to try it both ways and see which the results (including, eg, binary
size and memory usage as well as speed) suggest you use.
NOTE: one major difference with mbedTLS is it does not load the system trust
store by default. That has advantages and disadvantages, but the disadvantage
is you must provide the CA cert to lws built against mbedTLS for it to be able
to validate it, ie, use -A with the test client. The minimal test clients
have the CA cert for warmcat.com and libwebsockets.org and use it if they see
they were built with mbedTLS.
@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
To set compile time flags you can either use one of the CMake gui applications
or do it via the command line.
@subsection cmcocl Command line
To list available options (omit the H if you don't want the help text):
cmake -LH ..
Then to set an option and build (for example turn off SSL support):
cmake -DLWS_WITH_SSL=0 ..
or
cmake -DLWS_WITH_SSL:BOOL=OFF ..
@subsection cmcoug 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
On windows CMake comes with a gui application:
Start -> Programs -> CMake -> CMake (cmake-gui)
@section wolf wolfSSL/CyaSSL replacement for OpenSSL
wolfSSL/CyaSSL is a lightweight SSL library targeted at embedded systems:
https://www.wolfssl.com/wolfSSL/Products-wolfssl.html
It contains a OpenSSL compatibility layer which makes it possible to pretty
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
```
cmake .. -DLWS_WITH_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
```
cmake .. -DLWS_WITH_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
Building for ESP32 requires the ESP-IDF framework. It can be built under Linux, OSX or Windows (MSYS2).
1. Install ESP-IDF, follow the getting started guide here - http://esp-idf.readthedocs.io/en/latest/get-started/
2. Set ESP-IDF to last known working version (assuming ESP-IDF is in `~/esp/esp-idf`) :
```
cd ~/esp/esp-idf
git checkout 0c50b65a34cd6b3954f7435193411a88adb49cb0
git submodule update --recursive
```
3. Add `libwebsockets` as a submodule in the `components` folder of your ESP-IDF project:
```
git submodule add https://github.com/warmcat/libwebsockets.git components/libwebsockets
```
4. If on Windows (MSYS2) you will need to install CMake in the MSYS2 environment:
```
pacman -S mingw-w64-i686-cmake
```
If you're on Linux or OSX ensure CMake version is at least 3.7.
@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 HTTP/2 tests
Enable `-DLWS_WITH_HTTP2=1` in cmake to build with http/2 support enabled.
You must have built and be running lws against a version of openssl that has
ALPN. At the time of writing, recent distros have started upgrading to OpenSSL
1.1+ that supports this already. You'll know it's right by seeing
```
lwsts[4752]: Compiled with OpenSSL support
lwsts[4752]: Using SSL mode
lwsts[4752]: HTTP2 / ALPN enabled
```
at lws startup.
Recent Firefox and Chrome also support HTTP/2 by ALPN, so these should just work
with the test server running in -s / ssl mode.
For testing with nghttp client:
```
$ nghttp -nvas https://localhost:7681/test.html
```
Testing with h2spec (https://github.com/summerwind/h2spec)
```
$ h2spec -h 127.0.0.1 -p 7681 -t -k -v -o 1
```
```
145 tests, 145 passed, 0 skipped, 0 failed
```
@section coverage Automated Coverage Testing
./test-apps/attack.sh contains scripted tests that are the basis
of the automated test coverage assessment available for gcc and clang.
To reproduce
$ cd build
$ cmake .. -DLWS_WITH_GCOV=1 -DCMAKE_BUILD_TYPE=DEBUG
$ ../scripts/build-gcov.sh
$ ../test-apps/attack.sh
$ ../scripts/gcov.sh
...
Lines executed:51.24% of 8279
@section windowsprebuilt Using Windows binary builds on Appveyor
The CI builds on Appveyor now produce usable binary outputs. Visit
[lws on Appveyor](https://ci.appveyor.com/project/lws-team/libwebsockets)
and select one of the builds, then click on ARTIFACTS at the top right. The zip file
want to be unpacked into `C:\Program Files (x86)/libwebsockets`, after that, you should be able to run the test server, by running it from `bin/Release/libwebsockets-test-server.exe` and opening a browser on http://127.0.0.1:7681
@section cross 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
to look for dependencies and such.
**Libwebsockets** includes an example toolchain file [cross-arm-linux-gnueabihf.cmake](../contrib/cross-arm-linux-gnueabihf.cmake)
you can use as a starting point.
The commandline to configure for cross with this would look like
```
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr/lib/my-cross-root \
-DCMAKE_TOOLCHAIN_FILE=../contrib/cross-arm-linux-gnueabihf.cmake \
-DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_SSL=0 \
-DLWS_WITH_ZIP_FOPS=0 -DLWS_WITH_ZLIB=0
```
The example shows how to build with no external cross lib dependencies, you
need to provide the cross libraries otherwise.
**NOTE**: start from an EMPTY build directory if you had a non-cross build in there
before the settings will be cached and your changes ignored.
Delete `build/CMakeCache.txt` at least before trying a new cmake config
to ensure you are really building the options you think you are.
Additional information on cross compilation with CMake:
http://www.vtk.org/Wiki/CMake_Cross_Compiling
@section cross_example Complex Cross compiling example
Here are step by step instructions for cross-building the external projects needed for lws with lwsws + mbedtls as an example.
In the example, my toolchain lives in `/projects/aist-tb/arm-tc` and is named `arm-linux-gnueabihf`. So you will need to adapt those to where your toolchain lives and its name where you see them here.
Likewise I do all this in /tmp but it has no special meaning, you can adapt that to somewhere else.
All "foreign" cross-built binaries are sent into `/tmp/cross` so they cannot be confused for 'native' x86_64 stuff on your host machine in /usr/[local/]....
## Prepare the cmake toolchain file
1) `cd /tmp`
2) `wget -O mytoolchainfile https://raw.githubusercontent.com/warmcat/libwebsockets/master/contrib/cross-arm-linux-gnueabihf.cmake`
3) Edit `/tmp/mytoolchainfile` adapting `CROSS_PATH`, `CMAKE_C_COMPILER` and `CMAKE_CXX_COMPILER` to reflect your toolchain install dir and path to your toolchain C and C++ compilers respectively. For my case:
```
set(CROSS_PATH /projects/aist-tb/arm-tc/)
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/arm-linux-gnueabihf-g++")
```
## 1/4: Building libuv cross:
1) `export PATH=/projects/aist-tb/arm-tc/bin:$PATH` Notice there is a **/bin** on the end of the toolchain path
2) `cd /tmp ; mkdir cross` we will put the cross-built libs in /tmp/cross
3) `git clone https://github.com/libuv/libuv.git` get libuv
4) `cd libuv`
5) `./autogen.sh`
```
+ libtoolize --copy
libtoolize: putting auxiliary files in '.'.
libtoolize: copying file './ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
libtoolize: copying file 'm4/libtool.m4'
libtoolize: copying file 'm4/ltoptions.m4'
libtoolize: copying file 'm4/ltsugar.m4'
libtoolize: copying file 'm4/ltversion.m4'
libtoolize: copying file 'm4/lt~obsolete.m4'
+ aclocal -I m4
+ autoconf
+ automake --add-missing --copy
configure.ac:38: installing './ar-lib'
configure.ac:25: installing './compile'
configure.ac:22: installing './config.guess'
configure.ac:22: installing './config.sub'
configure.ac:21: installing './install-sh'
configure.ac:21: installing './missing'
Makefile.am: installing './depcomp'
```
If it has problems, you will need to install `automake`, `libtool` etc.
6) `./configure --host=arm-linux-gnueabihf --prefix=/tmp/cross`
7) `make && make install` this will install to `/tmp/cross/...`
8) `file /tmp/cross/lib/libuv.so.1.0.0` Check it's really built for ARM
```
/tmp/cross/lib/libuv.so.1.0.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=cdde0bc945e51db6001a9485349c035baaec2b46, with debug_info, not stripped
```
## 2/4: Building zlib cross
1) `cd /tmp`
2) `git clone https://github.com/madler/zlib.git`
3) `CC=arm-linux-gnueabihf-gcc ./configure --prefix=/tmp/cross`
```
Checking for shared library support...
Building shared library libz.so.1.2.11 with arm-linux-gnueabihf-gcc.
Checking for size_t... Yes.
Checking for off64_t... Yes.
Checking for fseeko... Yes.
Checking for strerror... Yes.
Checking for unistd.h... Yes.
Checking for stdarg.h... Yes.
Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf().
Checking for vsnprintf() in stdio.h... Yes.
Checking for return value of vsnprintf()... Yes.
Checking for attribute(visibility) support... Yes.
```
4) `make && make install`
```
arm-linux-gnueabihf-gcc -O3 -D_LARGEFILE64_SOURCE=1 -DHAVE_HIDDEN -I. -c -o example.o test/example.c
...
rm -f /tmp/cross/include/zlib.h /tmp/cross/include/zconf.h
cp zlib.h zconf.h /tmp/cross/include
chmod 644 /tmp/cross/include/zlib.h /tmp/cross/include/zconf.h
```
5) `file /tmp/cross/lib/libz.so.1.2.11` This is just to confirm we built an ARM lib as expected
```
/tmp/cross/lib/libz.so.1.2.11: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=6f8ffef84389b1417d2fd1da1bd0c90f748f300d, with debug_info, not stripped
```
## 3/4: Building mbedtls cross
1) `cd /tmp`
2) `git clone https://github.com/ARMmbed/mbedtls.git`
3) `cd mbedtls ; mkdir build ; cd build`
3) `cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile -DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross -DCMAKE_BUILD_TYPE=RELEASE -DUSE_SHARED_MBEDTLS_LIBRARY=1` mbedtls also uses cmake, so you can simply reuse the toolchain file you used for libwebsockets. That is why you shouldn't put project-specific options in the toolchain file, it should just describe the toolchain.
4) `make && make install`
5) `file /tmp/cross/lib/libmbedcrypto.so.2.6.0`
```
/tmp/cross/lib/libmbedcrypto.so.2.6.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=bcca195e78bd4fd2fb37f36ab7d72d477d609d87, with debug_info, not stripped
```
## 4/4: Building libwebsockets with everything
1) `cd /tmp`
2) `git clone ssh://git@github.com/warmcat/libwebsockets`
3) `cd libwebsockets ; mkdir build ; cd build`
4) (this is all one line on the commandline)
```
cmake .. -DCMAKE_TOOLCHAIN_FILE=/tmp/mytoolchainfile \
-DCMAKE_INSTALL_PREFIX:PATH=/tmp/cross \
-DLWS_WITH_LWSWS=1 \
-DLWS_WITH_MBEDTLS=1 \
-DLWS_MBEDTLS_LIBRARIES="/tmp/cross/lib/libmbedcrypto.so;/tmp/cross/lib/libmbedtls.so;/tmp/cross/lib/libmbedx509.so" \
-DLWS_MBEDTLS_INCLUDE_DIRS=/tmp/cross/include \
-DLWS_LIBUV_LIBRARIES=/tmp/cross/lib/libuv.so \
-DLWS_LIBUV_INCLUDE_DIRS=/tmp/cross/include \
-DLWS_ZLIB_LIBRARIES=/tmp/cross/lib/libz.so \
-DLWS_ZLIB_INCLUDE_DIRS=/tmp/cross/include
```
3) `make && make install`
4) `file /tmp/cross/lib/libwebsockets.so.11`
```
/tmp/cross/lib/libwebsockets.so.11: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=81e59c6534f8e9629a9fc9065c6e955ce96ca690, with debug_info, not stripped
```
5) `arm-linux-gnueabihf-objdump -p /tmp/cross/lib/libwebsockets.so.11 | grep NEEDED` Confirm that the lws library was linked against everything we expect (libm / libc are provided by your toolchain)
```
NEEDED libz.so.1
NEEDED libmbedcrypto.so.0
NEEDED libmbedtls.so.10
NEEDED libmbedx509.so.0
NEEDED libuv.so.1
NEEDED libm.so.6
NEEDED libc.so.6
```
You will also find the lws test apps in `/tmp/cross/bin`... to run lws on the target you will need to copy the related things from /tmp/cross... all the .so from /tmp/cross/lib and anything from /tmp/cross/bin you want.
@section mem 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
.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.
These are accounting for static allocations from the library elf, there are
additional dynamic allocations via malloc. These are a bit old now but give
the right idea for relative "expense" of features.
Static allocations, ARM9
| | .text | .rodata | .data | .bss |
|--------------------------------|---------|---------|-------|------|
| All (no without) | 35024 | 9940 | 336 | 4104 |
| without client | 25684 | 7144 | 336 | 4104 |
| without client, exts | 21652 | 6288 | 288 | 4104 |
| without client, exts, debug[1] | 19756 | 3768 | 288 | 4104 |
| without server | 30304 | 8160 | 336 | 4104 |
| without server, exts | 25382 | 7204 | 288 | 4104 |
| without server, exts, debug[1] | 23712 | 4256 | 288 | 4104 |
[1] `--disable-debug` only removes messages below `lwsl_notice`. Since that is
the default logging level the impact is not noticeable, error, warn and notice
logs are all still there.
[2] `1024` fd per process is the default limit (set by ulimit) in at least Fedora
and Ubuntu. You can make significant savings tailoring this to actual expected
peak fds, ie, at a limit of `20`, context creation allocation reduces to `4432 +
240 = 4672`)
[3] known header content is freed after connection establishment

View file

@ -1,180 +0,0 @@
lws-acme-client Plugin
======================
## Introduction
lws-acme-client is a protcol plugin for libwebsockets that implements an
ACME client able to communicate with let's encrypt and other certificate
providers.
It implements `tls-sni-01` challenge, and is able to provision tls certificates
"from thin air" that are accepted by all the major browsers. It also manages
re-requesting the certificate when it only has two weeks left to run.
It works with both the OpenSSL and mbedTLS backends.
## Overview for use
You need to:
- Provide name resolution to the IP with your server, ie, myserver.com needs to
resolve to the IP that hosts your server
- Enable port forwarding / external firewall access to your port, usually 443
- Enable the "lws-acme-client" plugin on the vhosts you want it to manage
certs for
- Add per-vhost options describing what should be in the certificate
After that the plugin will sort everything else out.
## Example lwsws setup
```
"vhosts": [ {
"name": "home.warmcat.com",
"port": "443",
"host-ssl-cert": "/etc/lwsws/acme/home.warmcat.com.crt.pem",
"host-ssl-key": "/etc/lwsws/acme/home.warmcat.com.key.pem",
"ignore-missing-cert": "1",
"access-log": "/var/log/lwsws/test-access-log",
"ws-protocols": [{
"lws-acme-client": {
"auth-path": "/etc/lwsws/acme/auth.jwk",
"cert-path": "/etc/lwsws/acme/home.warmcat.com.crt.pem",
"key-path": "/etc/lwsws/acme/home.warmcat.com.key.pem",
"directory-url": "https://acme-staging.api.letsencrypt.org/directory",
"country": "TW",
"state": "Taipei",
"locality": "Xiaobitan",
"organization": "Crash Barrier Ltd",
"common-name": "home.warmcat.com",
"email": "andy@warmcat.com"
},
...
```
## Required PVOs
Notice that the `"host-ssl-cert"` and `"host-ssl-key"` entries have the same
meaning as usual, they point to your certificate and private key. However
because the ACME plugin can provision these, you should also mark the vhost with
`"ignore-missing-cert" : "1"`, so lwsws will ignore what will initially be
missing certificate / keys on that vhost, and will set about creating the
necessary certs and keys instead of erroring out.
You must make sure the directories mentioned here exist, lws doesn't create them
for you. They should be 0700 root:root, even if you drop lws privileges.
If you are implementing support in code, this corresponds to making sure the
vhost creating `info.options` has the `LWS_SERVER_OPTION_IGNORE_MISSING_CERT`
bit set.
Similarly, in code, the each of the per-vhost options shown above can be
provided in a linked-list of structs at vhost creation time. See
`./test-apps/test-server-v2.0.c` for example code for providing pvos.
### auth-path
This is where the plugin will store the auth keys it generated.
### cert-path
Where the plugin will store the certificate file. Should match `host-ssl-cert`
that the vhost wants to use.
The path should include at least one 0700 root:root directory.
### key-path
Where the plugin will store the certificate keys. Again it should match
`host-ssl-key` the vhost is trying to use.
The path should include at least one 0700 root:root directory.
### directory-url
This defines the URL of the certification server you will get your
certificates from. For let's encrypt, they have a "practice" one
- `https://acme-staging.api.letsencrypt.org/directory`
and they have a "real" one
- `https://acme-v01.api.letsencrypt.org/directory`
the main difference is the CA certificate for the real one is in most browsers
already, but the staging one's CA certificate isn't. The staging server will
also let you abuse it more in terms of repeated testing etc.
It's recommended you confirm expected operation with the staging directory-url,
and then switch to the "real" URL.
### common-name
Your server DNS name, like "libwebsockets.org". The remote ACME server will
use this to find your server to perform the SNI challenges.
### email
The contact email address for the certificate.
## Optional PVOs
These are not included in the cert by letsencrypt
### country
Two-letter country code for the certificate
### state
State "or province" for the certificate
### locality
Locality for the certificate
### organization
Your company name
## Security / Key storage considerations
The `lws-acme-client` plugin is able to provision and update your certificate
and keys in an entirely root-only storage environment, even though lws runs
as a different uid / gid with no privileges to access the storage dir.
It does this by opening and holding two WRONLY fds on "update paths" inside the
root directory structure for each cert and key it manages; these are the normal
cert and key paths with `.upd` appended. If during the time the server is up
the certs become within two weeks of expiry, the `lws-acme-client` plugin will
negotiate new certs and write them to the file descriptors.
Next time the server starts, if it sees `.upd` cert and keys, it will back up
the old ones and copy them into place as the new ones, before dropping privs.
To also handle the long-uptime server case, lws will update the vhost with the
new certs using in-memory temporary copies of the cert and key after updating
the cert.
In this way the cert and key live in root-only storage but the vhost is kept up
to date dynamically with any cert changes as well.
## Multiple vhosts using same cert
In the case you have multiple vhosts using of the same cert, just attach
the `lws-acme-client` plugin to one instance. When the cert updates, all the
vhosts are informed and vhosts using the same filepath to access the cert will
be able to update their cert.
## Implementation point
You will need to remove the auth keys when switching from OpenSSL to
mbedTLS. They will be regenerated automatically. It's the file at this
path:
```
"auth-path": "/etc/lwsws/acme/auth.jwk",
```

View file

@ -1,20 +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
- HTTP/2 support for serving
- WS(S) serving and client operation
- HTTP(S) apis for file transfer and upload
- HTTP 1 + 2 POST form handling (including multipart / file upload)
- cookie-based sessions
- account management (including registration, email verification, lost pw etc)
- strong SSL / TLS PFS support (A+ on SSLlabs test)
- ssh server integration
- serving gzipped files directly from inside zip files, without conversion
- support for linux, bsd, windows etc... and very small nonlinux targets like ESP32
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,8 +1,5 @@
environment:
matrix:
- LWS_METHOD: x64
CMAKE_ARGS: -DCMAKE_GENERATOR_PLATFORM=x64 -DLWS_WITH_HTTP2=1 -DLWS_WITH_PLUGINS=1 -DLIBUV_INCLUDE_DIRS=C:\assets\libuv64\include -DLIBUV_LIBRARIES=C:\assets\libuv64\libuv.lib
- 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
@ -19,25 +16,21 @@ 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://libwebsockets.org:444/win-libuv64.zip
- mkdir c:\assets\libuv64
- 7z x -oc:\assets\libuv64 win-libuv64.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%
build:
parallel: true
verbosity: minimal
build_script:
- md build
@ -45,33 +38,21 @@ 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:
- cd %APPVEYOR_BUILD_FOLDER%
- mkdir staging
- mkdir staging\include
- cp -r %APPVEYOR_BUILD_FOLDER%\build\bin %APPVEYOR_BUILD_FOLDER%\build\lib staging
- if EXIST staging\bin\share mv staging\bin\share staging
- if NOT EXIST staging\share\libwebsockets-test-server mkdir staging\share\libwebsockets-test-server
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.pem staging\share\libwebsockets-test-server
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem cp %APPVEYOR_BUILD_FOLDER%\build\libwebsockets-test-server.key.pem staging\share\libwebsockets-test-server
- IF EXIST %APPVEYOR_BUILD_FOLDER%\build\lws_config.h cp %APPVEYOR_BUILD_FOLDER%\build\lws_config.h staging\include
- cp %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h staging\include
- 7z a build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip %APPVEYOR_BUILD_FOLDER%\staging\*
- 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
# - cd ..
# - cd win32port
# - makensis -DVERSION=%APPVEYOR_BUILD_VERSION% libwebsockets.nsi
artifacts:
- path: build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip
- path: lws.zip
name: lws.zip
type: Zip
deploy:
- provider: BinTray
username: lws-team
api_key:
secure: nDpZ7P/wrk98DwJPMC6KpCC23QrVP8f3RxvKzBaqOmb9LiVrg1IyO1cc5vcgShZC
subject: lws-team
repo: libwebsockets
package: windows
publish: true
override: true
explode: false
#cache:
# - C:\OpenSSL-Win32
matrix:
fast_finish: true

41
autobahn-test.sh Executable file
View file

@ -0,0 +1,41 @@
#!/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

200
changelog
View file

@ -1,206 +1,6 @@
Changelog
---------
v3.0.0
======
- CHANGE: Clients used to call LWS_CALLBACK_CLOSED same as servers...
LWS_CALLBACK_CLIENT_CLOSED has been introduced and is called for clients
now.
- CHANGE: LWS_CALLBACK_CLIENT_CONNECTION_ERROR used to only be directed at
protocols[0]. However in many cases, the protocol to bind to was provided
at client connection info time and the wsi bound accordingly. In those
cases, CONNECTION_ERROR is directed at the bound protocol, not protcols[0]
any more.
- CHANGE: CMAKE: the following cmake defaults have changed with this version:
- LWS_WITH_ZIP_FOPS: now defaults OFF
- LWS_WITH_RANGES: now defaults OFF
- LWS_WITH_ZLIB: now defaults OFF
- LWS_WITHOUT_EXTENSIONS: now defaults ON
- CHANGE: REMOVED: lws_alloc_vfs_file() (read a file to malloc buffer)
- CHANGE: REMOVED: lws_read() (no longer useful outside of lws internals)
- CHANGE: REMOVED: ESP8266... ESP32 is now within the same price range and much
more performant
- CHANGE: soname bump... don't forget to `ldconfig`
- NEW: all event libraries support "foreign" loop integration where lws itself
if just a temporary user of the loop unrelated to the actual loop lifecycle.
See `minimal-http-server-eventlib-foreign` for example code demonstrating
this for all the event libraries.
Internal loop in lws is also supported and demonstrated by
`minimal-http-server-eventlib`.
- NEW: ws-over-h2 support. This is a new RFC-on-the-way supported by Chrome
and shortly firefox that allows ws connections to be multiplexed back to the
server on the same tcp + tls wrapper h2 connection that the html and scripts
came in on. This is hugely faster that discrete connections.
- NEW: UDP socket adoption and related event callbacks
- NEW: Multi-client connection binding, queuing and pipelining support.
Lws detects multiple client connections to the same server and port, and
optimizes how it handles them according to the server type and provided
flags. For http/1.0, all occur with individual parallel connections. For
http/1.1, you can enable keepalive pipelining, so the connections occur
sequentially on a single network connection. For http/2, they all occur
as parallel streams within a single h2 network connection.
See minimal-http-client-multi for example code.
- NEW: High resolution timer API for wsi, get a callback on your wsi with
LWS_CALLBACK_TIMER, set and reset the timer with lws_set_timer_usecs(wsi, us)
Actual resolution depends on event backend. Works with all backends, poll,
libuv, libevent, and libev.
- NEW: Protocols can arrange vhost-protocol instance specific callbacks with
second resolution using `lws_timed_callback_vh_protocol()`
- NEW: ACME client plugin for self-service TLS certificates
- NEW: RFC7517 JSON Web Keys RFC7638 JWK thumbprint, and RFC7515 JSON Web
signatures support
- NEW: lws_cancel_service() now provides a generic way to synchronize events
from other threads, which appear as a LWS_CALLBACK_EVENT_WAIT_CANCELLED
callback on all protocols. This is compatible with all the event libraries.
- NEW: support BSD poll() where changes to the poll wait while waiting are
undone.
- NEW: Introduce generic hash, hmac and RSA apis that operate the same
regardless of OpenSSL or mbedTLS tls backend
- NEW: Introduce X509 element query api that works the same regardless of
OpenSSL or mbedTLS tls backend
- NEW: Introduce over 30 "minimal examples" in ./minimal-examples... these
replace most of the old test servers
- test-echo -> minimal-ws-server-echo and minimal-ws-client-echo
- test-server-libuv / -libevent / -libev ->
minimal-https-server-eventlib / -eventlib-foreign / -eventlib-demos
- test-server-v2.0 -> folded into all the minimal servers
- test-server direct http serving -> minimal-http-server-dynamic
The minimal examples allow individual standalone build using their own
small CMakeLists.txt.
- NEW: lws now detects any back-to-back writes that did not go through the
event loop inbetween and reports them. This will flag any possibility of
failure rather than wait until the problem happens.
- NEW: CMake has LWS_WITH_DISTRO_RECOMMENDED to select features that are
appropriate for distros
- NEW: Optional vhost URL `error_document_404` if given causes a redirect there
instead of serve the default 404 page.
- NEW: lws_strncpy() wrapper guarantees NUL in copied string even if it was
truncated to fit.
- NEW: for client connections, local protocol binding name can be separated
from the ws subprotocol name if needed, using .local_protocol_name
- NEW: Automatic detection of time discontiguities
- NEW: Applies TCP_USER_TIMEOUT for Linux tcp keepalive where available
- QA: 1600 tests run on each commit in Travis CI, including almost all
Autobahn in client and server mode, various h2load tests, h2spec, attack.sh
the minimal example selftests and others.
- QA: fix small warnings introduced on gcc8.x (eg, Fedora 28)
- QA: Add most of -Wextra on gcc (-Wsign-compare, -Wignored-qualifiers,
-Wtype-limits, -Wuninitialized)
- QA: clean out warnings on windows
- QA: pass all 146 h2spec tests now on strict
- QA: introduce 35 selftests that operate different minimal examples against
each other and confirm the results.
- QA: LWS_WITH_MINIMAL_EXAMPLES allows mass build of all relevant minimal-
examples with the LWS build, for CI and to make all the example binaries
available from the lws build dir ./bin
- REFACTOR: the lws source directory layout in ./lib has been radically
improved, and there are now README.md files in selected subdirs with extra
documentation of interest to people working on lws itself.
- REFACTOR: pipelined transactions return to the event loop before starting the
next part.
- REFACTOR: TLS: replace all TLS library constants with generic LWS ones and
adapt all the TLS library code to translate to these common ones.
Isolated all the tls-related private stuff in `./lib/tls/private.h`, and all
the mbedTLS stuff in `./lib/tls/mbedtls` + openSSL stuff in
`./lib/tls/openssl`
- REFACTOR: the various kinds of wsi possible with lws have been extracted
from the main code and isolated into "roles" in `./lib/roles` which
communicate with the core code via an ops struct. Everything related to
ah is migrated to the http role.
wsi modes are eliminated and replaced by the ops pointer for the role the
wsi is performing. Generic states for wsi are available to control the
lifecycle using core code.
Adding new "roles" is now much easier with the changes and ops struct to
plug into.
- REFACTOR: reduce four different kinds of buffer management in lws into a
generic scatter-gather struct lws_buflist.
- REFACTOR: close notifications go through event loop
v2.4.0
======
- HTTP/2 server support is now mature and usable! LWS_WITH_HTTP2=1 enables it.
Uses ALPN to serve HTTP/2, HTTP/1 and ws[s] connections all from the same
listen port seamlessly. (Requires ALPN-capable OpenSSL 1.1 or mbedTLS).
- LWS_WITH_MBEDTLS=1 at CMake now builds and works against mbedTLS instead of
OpenSSL. Most things work identically, although on common targets where
OpenSSL has acceleration, mbedTLS is many times slower in operation. However
it is a lot smaller codewise.
- Generic hash apis introduced that work the same on mbedTLS or OpenSSL backend
- LWS_WITH_PEER_LIMITS tracks IPs across all vhosts and allows restrictions on
both the number of simultaneous connections and wsi in use for any single IP
- lws_ring apis provide a generic single- or multi-tail ringbuffer... mirror
protocol now uses this. Features include ring elements may be sized to fit
structs in the ringbuffer, callback when no tail any longer needs an element
and it can be deleted, and zerocopy options to write new members directly
into the ringbuffer, and use the ringbuffer element by address too.
- abstract ssh 2 server plugin included, with both plugin and standalone
demos provided. You can bind the plugin to a vhost and also serve full-
strength ssh from the vhost. IO from the ssh server is controlled by an
"ops" struct of callbacks for tx, rx, auth etc.
- Many fixes, cleanups, source refactors and other improvements.
v2.3.0
======

View file

@ -111,7 +111,7 @@ rm -rf build_tree
mkdir build_tree
cd build_tree
cmake -DCMAKE_INSTALL_PREFIX=%{rpmprefix} ../%{srcdirname}
make %{?_smp_mflags}
make
%install
cd ../build_tree

View file

@ -1,20 +1,19 @@
COMPONENT_DEPENDS := mbedtls openssl
#COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
COMPONENT_OWNBUILDTARGET := 1
COMPONENT_OWNBUILDTARGET:= 1
CROSS_PATH1 := $(shell which xtensa-esp32-elf-gcc )
CROSS_PATH := $(shell dirname $(CROSS_PATH1) )/..
CROSS_PATH1:=$(shell which xtensa-esp32-elf-gcc )
CROSS_PATH:= $(shell dirname $(CROSS_PATH1) )/..
# detect MSYS2 environment and set generator flag if found
# also set executable extension to .exe so that tools can be properly found
# and disable bundled zlib
MSYS_VERSION = $(if $(findstring Msys, $(shell uname -o)),$(word 1, $(subst ., ,$(shell uname -r))),0)
ifneq ($(MSYS_VERSION),0)
MSYS_FLAGS = -DLWS_WITH_BUNDLED_ZLIB=0 -DEXECUTABLE_EXT=.exe -G'MSYS Makefiles'
endif
#-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) \
# -DNDEBUG=1 after cflags stops debug etc being built
.PHONY: build
build:
cd $(COMPONENT_BUILD_DIR) ; \
@ -23,19 +22,13 @@ build:
-DIDF_PATH=$(IDF_PATH) \
-DCROSS_PATH=$(CROSS_PATH) \
-DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/contrib/cross-esp32.cmake \
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/cross-esp32.cmake \
-DCMAKE_BUILD_TYPE=RELEASE \
-DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/include" \
-DOPENSSL_INCLUDE_DIR=${IDF_PATH}/components/openssl/include \
-DLWS_WITH_STATS=0 \
-DLWS_WITH_HTTP2=1 \
-DLWS_WITH_RANGES=1 \
-DLWS_WITH_ACME=1 \
-DLWS_WITH_ZLIB=1 \
-DLWS_WITH_ZIP_FOPS=1 \
-DZLIB_LIBRARY=$(BUILD_DIR_BASE)/zlib/libzlib.a \
-DZLIB_INCLUDE_DIR=$(COMPONENT_PATH)/../zlib \
-DLWS_WITH_ESP32=1 \
$(MSYS_FLAGS) ; \
-DLWS_WITH_ESP32=1 ;\
make && \
cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a
@ -43,3 +36,6 @@ clean: myclean
myclean:
rm -rf ./build
INCLUDES := $(INCLUDES) -I build/

View file

@ -20,13 +20,7 @@ LGPL2 / GPL2 at your choice.
Installation
------------
The abi monitoring stuff is now packaged in, eg, fedora, which is a lot
easier than using the helper script.
```
# dnf install abi-tracker vtable-dumper
Otherwise, the author provides an "easy way" to install the various tools he provides:
The author provides an easy way to install the various tools he provides:
git clone https://github.com/lvc/installer
cd installer

View file

@ -101,7 +101,7 @@ PATH=$TOOLCHAIN_PATH:$PATH cmake \
-DLWS_WITHOUT_DAEMONIZE=ON \
-DLWS_WITHOUT_TESTAPPS=ON \
-DLWS_IPV6=OFF \
-DLWS_WITH_BUNDLED_ZLIB=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" \

View file

@ -1,29 +0,0 @@
#
# CMake Toolchain file for crosscompiling on 32bit Windows platforms.
#
# This can be used when running cmake in the following way:
# cd build/
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w32.cmake -DLWS_WITH_SSL=0
#
set(CROSS_PATH /opt/mingw32)
# Target operating system name.
set(CMAKE_SYSTEM_NAME Windows)
# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-gcc")
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-g++")
set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/i686-w64-mingw32-windres")
set(CMAKE_C_FLAGS "-Wno-error")
# 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)

View file

@ -1,29 +0,0 @@
#
# CMake Toolchain file for crosscompiling on 64bit Windows platforms.
#
# This can be used when running cmake in the following way:
# cd build/
# cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w64.cmake -DLWS_WITH_SSL=0
#
set(CROSS_PATH /opt/mingw64)
# Target operating system name.
set(CMAKE_SYSTEM_NAME Windows)
# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-gcc")
set(CMAKE_CXX_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-g++")
set(CMAKE_RC_COMPILER "${CROSS_PATH}/bin/x86_64-w64-mingw32-windres")
set(CMAKE_C_FLAGS "-Wno-error")
# 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)

View file

@ -10,15 +10,14 @@
set(CMAKE_SYSTEM_NAME Linux)
# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc${EXECUTABLE_EXT}")
set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar${EXECUTABLE_EXT}")
set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib${EXECUTABLE_EXT}")
set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld${EXECUTABLE_EXT}")
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/heap/include \
-I${IDF_PATH}/components/driver/include \
-I${IDF_PATH}/components/spi_flash/include \
-I${IDF_PATH}/components/nvs_flash/include \
@ -30,8 +29,6 @@ SET(CMAKE_C_FLAGS "-nostdlib -Wall -Werror \
-I${IDF_PATH}/components/bootloader_support/include/ \
-I${IDF_PATH}/components/app_update/include/ \
-I$(IDF_PATH)/components/soc/esp32/include/ \
-I$(IDF_PATH)/components/soc/include/ \
-I$(IDF_PATH)/components/vfs/include/ \
${LWS_C_FLAGS} -Os \
-I${IDF_PATH}/components/nvs_flash/test_nvs_host \
-I${IDF_PATH}/components/freertos/include" CACHE STRING "" FORCE)

View file

@ -25,13 +25,13 @@ CMAKE_OPTIONS += -DLWS_WITHOUT_TESTAPPS=$(if $(CONFIG_PACKAGE_libwebsockets-exam
# for wolfssl, define these in addition to LWS_OPENSSL_SUPPORT and
# edit package/libs/wolfssl/Makefile to include --enable-opensslextra
# CMAKE_OPTIONS += -DLWS_WITH_WOLFSSL=ON
# CMAKE_OPTIONS += -DLWS_USE_WOLFSSL=ON
# CMAKE_OPTIONS += -DLWS_WOLFSSL_LIBRARIES=$(STAGING_DIR)/usr/lib/libwolfssl.so
# CMAKE_OPTIONS += -DLWS_WOLFSSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include
# for cyassl, define these in addition to LWS_OPENSSL_SUPPORT and
# edit package/libs/wolfssl/Makefile to include --enable-opensslextra
# CMAKE_OPTIONS += -DLWS_WITH_CYASSL=ON
# CMAKE_OPTIONS += -DLWS_USE_CYASSL=ON
# CMAKE_OPTIONS += -DLWS_CYASSL_LIBRARIES=$(STAGING_DIR)/usr/lib/libcyassl.so
# CMAKE_OPTIONS += -DLWS_CYASSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

8
lib/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
#Ignore build files
Makefile
*.o
*.lo
*.la
.libs
.deps

View file

@ -1,14 +0,0 @@
## Library sources layout
Code that goes in the libwebsockets library itself lives down ./lib
Path|Sources
---|---
lib/core|Core lws code related to generic fd and wsi servicing and management
lib/event-libs|Code containing optional event-lib specific adaptations
lib/misc|Code for various mostly optional miscellaneous features
lib/plat|Platform-specific adaptation code
lib/roles|Code for specific optional wsi roles, eg, http/1, h2, ws, raw, etc
lib/tls|Code supporting the various TLS libraries
libwebsockets.h|Public API header for the whole of lws

83
lib/alloc.c Normal file
View file

@ -0,0 +1,83 @@
#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
else if (ptr)
free(ptr);
return NULL;
}
void *(*_lws_realloc)(void *ptr, size_t size) = _realloc;
void *lws_realloc(void *ptr, size_t size)
{
return _lws_realloc(ptr, size);
}
void *lws_zalloc(size_t size)
{
void *ptr = _lws_realloc(NULL, size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
{
_lws_realloc = cb;
}
#endif

View file

@ -40,18 +40,15 @@
#include <stdio.h>
#include <string.h>
#include "core/private.h"
#include "private-libwebsockets.h"
static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789-_";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
static int
_lws_b64_encode_string(const char *encode, const char *in, int in_len,
char *out, int out_size)
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
unsigned char triple[3];
int i;
@ -92,47 +89,26 @@ _lws_b64_encode_string(const char *encode, const char *in, int in_len,
return done;
}
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
}
LWS_VISIBLE int
lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
}
/*
* returns length of decoded string in out, or -1 if out was too small
* according to out_size
*
* Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
* the first NUL in the input.
*/
static int
_lws_b64_decode_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];
while (in_len && *in) {
while (*in) {
len = 0;
for (i = 0; i < 4 && in_len && *in; i++) {
for (i = 0; i < 4 && *in; i++) {
v = 0;
c = 0;
while (in_len && *in && !v) {
while (*in && !v) {
c = v = *in++;
in_len--;
/* support the url base64 variant too */
if (v == '-')
c = v = '+';
if (v == '_')
c = v = '/';
v = (v < 43 || v > 122) ? 0 : decode[v - 43];
if (v)
v = (v == '$') ? 0 : v - 61;
@ -155,7 +131,7 @@ _lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
* bytes." (wikipedia)
*/
if ((!in_len || !*in) && c == '=')
if (!*in && c == '=')
len--;
if (len >= 2)
@ -176,18 +152,6 @@ _lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
return done;
}
LWS_VISIBLE int
lws_b64_decode_string(const char *in, char *out, int out_size)
{
return _lws_b64_decode_string(in, -1, out, out_size);
}
LWS_VISIBLE int
lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_decode_string(in, in_len, out, out_size);
}
#if 0
int
lws_b64_selftest(void)

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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
@ -19,38 +19,31 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
/*
* parsers.c: lws_ws_rx_sm() needs to be roughly kept in
* parsers.c: lws_rx_sm() needs to be roughly kept in
* sync with changes here, esp related to ext draining
*/
int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
int lws_client_rx_sm(struct lws *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
int handled, m;
int handled, n, m, rx_draining_ext = 0;
unsigned short close_code;
struct lws_tokens ebuf;
struct lws_tokens eff_buf;
unsigned char *pp;
#if !defined(LWS_WITHOUT_EXTENSIONS)
int rx_draining_ext = 0, n;
#endif
ebuf.token = NULL;
ebuf.len = 0;
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext) {
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;
}
#endif
if (wsi->socket_is_permanently_unusable)
return -1;
@ -58,38 +51,35 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
/* control frames (PING) may interrupt checkable sequences */
wsi->ws->defeat_check_utf8 = 0;
wsi->u.ws.defeat_check_utf8 = 0;
switch (wsi->ws->ietf_spec_revision) {
switch (wsi->ietf_spec_revision) {
case 13:
wsi->ws->opcode = c & 0xf;
wsi->u.ws.opcode = c & 0xf;
/* revisit if an extension wants them... */
switch (wsi->ws->opcode) {
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
wsi->ws->rsv_first_msg = (c & 0x70);
wsi->ws->continuation_possible = 1;
wsi->ws->check_utf8 = lws_check_opt(
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->ws->utf8 = 0;
wsi->ws->first_fragment = 1;
wsi->u.ws.utf8 = 0;
break;
case LWSWSOPC_BINARY_FRAME:
wsi->ws->rsv_first_msg = (c & 0x70);
wsi->ws->check_utf8 = 0;
wsi->ws->continuation_possible = 1;
wsi->ws->first_fragment = 1;
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->ws->continuation_possible) {
if (!wsi->u.ws.continuation_possible) {
lwsl_info("disordered continuation\n");
return -1;
}
wsi->ws->first_fragment = 0;
break;
case LWSWSOPC_CLOSE:
wsi->ws->check_utf8 = 0;
wsi->ws->utf8 = 0;
wsi->u.ws.check_utf8 = 0;
wsi->u.ws.utf8 = 0;
break;
case 3:
case 4:
@ -104,45 +94,44 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
lwsl_info("illegal opcode\n");
return -1;
default:
wsi->ws->defeat_check_utf8 = 1;
wsi->u.ws.defeat_check_utf8 = 1;
break;
}
wsi->ws->rsv = (c & 0x70);
wsi->u.ws.rsv = (c & 0x70);
/* revisit if an extension wants them... */
if (
#if !defined(LWS_WITHOUT_EXTENSIONS)
!wsi->ws->count_act_ext &&
#ifndef LWS_NO_EXTENSIONS
!wsi->count_act_ext &&
#endif
wsi->ws->rsv) {
wsi->u.ws.rsv) {
lwsl_info("illegal rsv bits set\n");
return -1;
}
wsi->ws->final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__,
wsi->ws->final);
wsi->u.ws.final = !!((c >> 7) & 1);
lwsl_ext("%s: This RX frame Final %d\n", __func__, wsi->u.ws.final);
if (wsi->ws->owed_a_fin &&
(wsi->ws->opcode == LWSWSOPC_TEXT_FRAME ||
wsi->ws->opcode == LWSWSOPC_BINARY_FRAME)) {
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->ws->opcode & 8)) && wsi->ws->final) {
wsi->ws->continuation_possible = 0;
wsi->ws->owed_a_fin = 0;
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->ws->opcode & 8) && !wsi->ws->final) {
lwsl_info("control msg can't be fragmented\n");
if ((wsi->u.ws.opcode & 8) && !wsi->u.ws.final) {
lwsl_info("control message cannot be fragmented\n");
return -1;
}
if (!wsi->ws->final)
wsi->ws->owed_a_fin = 1;
if (!wsi->u.ws.final)
wsi->u.ws.owed_a_fin = 1;
switch (wsi->ws->opcode) {
switch (wsi->u.ws.opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
wsi->ws->frame_is_binary = wsi->ws->opcode ==
wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
LWSWSOPC_BINARY_FRAME;
break;
}
@ -151,38 +140,38 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
default:
lwsl_err("unknown spec version %02d\n",
wsi->ws->ietf_spec_revision);
wsi->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->ws->this_frame_masked = !!(c & 0x80);
wsi->u.ws.this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
if (wsi->ws->opcode & 8)
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
if (wsi->ws->opcode & 8)
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
wsi->ws->rx_packet_length = c & 0x7f;
if (wsi->ws->this_frame_masked)
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;
else {
if (wsi->ws->rx_packet_length) {
if (c)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
} else {
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
@ -192,18 +181,18 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->ws->rx_packet_length = c << 8;
wsi->u.ws.rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->ws->rx_packet_length |= c;
if (wsi->ws->this_frame_masked)
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;
else {
if (wsi->ws->rx_packet_length)
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@ -218,58 +207,58 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
return -1;
}
#if defined __LP64__
wsi->ws->rx_packet_length = ((size_t)c) << 56;
wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
#else
wsi->ws->rx_packet_length = 0;
wsi->u.ws.rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 48;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 40;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
wsi->ws->rx_packet_length |= ((size_t)c) << 32;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->ws->rx_packet_length |= ((size_t)c) << 24;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->ws->rx_packet_length |= ((size_t)c) << 16;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->ws->rx_packet_length |= ((size_t)c) << 8;
wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->ws->rx_packet_length |= (size_t)c;
if (wsi->ws->this_frame_masked)
wsi->u.ws.rx_packet_length |= (size_t)c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->ws->rx_packet_length)
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@ -278,53 +267,53 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->ws->mask[0] = c;
wsi->u.ws.mask[0] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
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->ws->mask[1] = c;
wsi->u.ws.mask[1] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
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->ws->mask[2] = c;
wsi->u.ws.mask[2] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
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->ws->mask[3] = c;
wsi->u.ws.mask[3] = c;
if (c)
wsi->ws->all_zero_nonce = 0;
wsi->u.ws.all_zero_nonce = 0;
if (wsi->ws->rx_packet_length)
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_WS_FRAME_PAYLOAD;
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
case LWS_RXPS_WS_FRAME_PAYLOAD:
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
assert(wsi->ws->rx_ubuf);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (wsi->ws->rx_draining_ext)
assert(wsi->u.ws.rx_ubuf);
if (wsi->u.ws.rx_draining_ext)
goto drain_extension;
#endif
if (wsi->ws->this_frame_masked && !wsi->ws->all_zero_nonce)
c ^= wsi->ws->mask[(wsi->ws->mask_idx++) & 3];
wsi->ws->rx_ubuf[LWS_PRE + (wsi->ws->rx_ubuf_head++)] = c;
if (wsi->u.ws.this_frame_masked && !wsi->u.ws.all_zero_nonce)
c ^= wsi->u.ws.mask[(wsi->u.ws.mask_idx++) & 3];
if (--wsi->ws->rx_packet_length == 0) {
wsi->u.ws.rx_ubuf[LWS_PRE + (wsi->u.ws.rx_ubuf_head++)] = c;
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
@ -335,11 +324,11 @@ int lws_ws_client_rx_sm(struct lws *wsi, unsigned char c)
* supposed to default to context->pt_serv_buf_size
*/
if (!wsi->protocol->rx_buffer_size &&
wsi->ws->rx_ubuf_head != wsi->context->pt_serv_buf_size)
wsi->u.ws.rx_ubuf_head != wsi->context->pt_serv_buf_size)
break;
if (wsi->protocol->rx_buffer_size &&
wsi->ws->rx_ubuf_head != wsi->protocol->rx_buffer_size)
wsi->u.ws.rx_ubuf_head != wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
@ -352,18 +341,18 @@ spill:
* layer? If so service it and hide it from the user callback
*/
switch (wsi->ws->opcode) {
switch (wsi->u.ws.opcode) {
case LWSWSOPC_CLOSE:
pp = (unsigned char *)&wsi->ws->rx_ubuf[LWS_PRE];
pp = (unsigned char *)&wsi->u.ws.rx_ubuf[LWS_PRE];
if (lws_check_opt(wsi->context->options,
LWS_SERVER_OPTION_VALIDATE_UTF8) &&
wsi->ws->rx_ubuf_head > 2 &&
lws_check_utf8(&wsi->ws->utf8, pp + 2,
wsi->ws->rx_ubuf_head - 2))
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;
/* is this an acknowledgment of our close? */
if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
/* is this an acknowledgement of our close? */
if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
@ -373,8 +362,8 @@ spill:
}
lwsl_parser("client sees server close len = %d\n",
wsi->ws->rx_ubuf_head);
if (wsi->ws->rx_ubuf_head >= 2) {
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 ||
@ -394,32 +383,38 @@ spill:
wsi->protocol->callback, wsi,
LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
wsi->user_space, pp,
wsi->ws->rx_ubuf_head))
wsi->u.ws.rx_ubuf_head))
return -1;
memcpy(wsi->ws->ping_payload_buf + LWS_PRE, pp,
wsi->ws->rx_ubuf_head);
wsi->ws->close_in_ping_buffer_len = wsi->ws->rx_ubuf_head;
lwsl_info("%s: scheduling return close as ack\n", __func__);
__lws_change_pollfd(wsi, LWS_POLLIN, 0);
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 3);
wsi->waiting_to_send_close_frame = 1;
wsi->close_needs_ack = 0;
lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
lws_callback_on_writable(wsi);
handled = 1;
break;
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;
/* close the connection */
return -1;
case LWSWSOPC_PING:
lwsl_info("received %d byte ping, sending pong\n",
wsi->ws->rx_ubuf_head);
wsi->u.ws.rx_ubuf_head);
/* he set a close reason on this guy, ignore PING */
if (wsi->ws->close_in_ping_buffer_len)
if (wsi->u.ws.close_in_ping_buffer_len)
goto ping_drop;
if (wsi->ws->ping_pending_flag) {
if (wsi->u.ws.ping_pending_flag) {
/*
* there is already a pending ping payload
* we should just log and drop
@ -429,34 +424,33 @@ spill:
}
/* control packets can only be < 128 bytes long */
if (wsi->ws->rx_ubuf_head > 128 - 3) {
if (wsi->u.ws.rx_ubuf_head > 128 - 3) {
lwsl_parser("DROP PING payload too large\n");
goto ping_drop;
}
/* stash the pong payload */
memcpy(wsi->ws->ping_payload_buf + LWS_PRE,
&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
memcpy(wsi->u.ws.ping_payload_buf + LWS_PRE,
&wsi->u.ws.rx_ubuf[LWS_PRE],
wsi->u.ws.rx_ubuf_head);
wsi->ws->ping_payload_len = wsi->ws->rx_ubuf_head;
wsi->ws->ping_pending_flag = 1;
wsi->u.ws.ping_payload_len = wsi->u.ws.rx_ubuf_head;
wsi->u.ws.ping_pending_flag = 1;
/* get it sent as soon as possible */
lws_callback_on_writable(wsi);
ping_drop:
wsi->ws->rx_ubuf_head = 0;
wsi->u.ws.rx_ubuf_head = 0;
handled = 1;
break;
case LWSWSOPC_PONG:
lwsl_info("client received pong\n");
lwsl_hexdump(&wsi->ws->rx_ubuf[LWS_PRE],
wsi->ws->rx_ubuf_head);
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("%p: received expected PONG\n", wsi);
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);
}
@ -470,11 +464,29 @@ ping_drop:
break;
default:
/* not handled or failed */
lwsl_ext("Unhandled ext opc 0x%x\n", wsi->ws->opcode);
wsi->ws->rx_ubuf_head = 0;
return -1;
lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode);
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* 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, 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;
return 0;
}
handled = 1;
break;
}
/*
@ -485,66 +497,49 @@ ping_drop:
if (handled)
goto already_done;
ebuf.token = &wsi->ws->rx_ubuf[LWS_PRE];
ebuf.len = wsi->ws->rx_ubuf_head;
eff_buf.token = &wsi->u.ws.rx_ubuf[LWS_PRE];
eff_buf.token_len = wsi->u.ws.rx_ubuf_head;
#if !defined(LWS_WITHOUT_EXTENSIONS)
drain_extension:
lwsl_ext("%s: passing %d to ext\n", __func__, ebuf.len);
lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &ebuf, 0);
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;
return -1;
}
#endif
lwsl_debug("post inflate ebuf len %d\n", ebuf.len);
#if !defined(LWS_WITHOUT_EXTENSIONS)
if (rx_draining_ext && !ebuf.len) {
lwsl_debug(" --- ending drain on 0 read result\n");
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;
}
#endif
if (wsi->ws->check_utf8 && !wsi->ws->defeat_check_utf8) {
if (lws_check_utf8(&wsi->ws->utf8,
(unsigned char *)ebuf.token,
ebuf.len)) {
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"bad utf8", 8);
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->ws->rx_packet_length && wsi->ws->final &&
wsi->ws->utf8
#if !defined(LWS_WITHOUT_EXTENSIONS)
&& !n
#endif
) {
if (!wsi->u.ws.rx_packet_length && wsi->u.ws.final &&
wsi->u.ws.utf8 && !n) {
lwsl_info("FINAL utf8 error\n");
lws_close_reason(wsi,
LWS_CLOSE_STATUS_INVALID_PAYLOAD,
(uint8_t *)"partial utf8", 12);
utf8_fail:
lwsl_info("utf8 error\n");
lwsl_hexdump_info(ebuf.token, ebuf.len);
utf8_fail: lwsl_info("utf8 error\n");
return -1;
}
}
if (ebuf.len < 0 &&
if (eff_buf.token_len < 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
if (!ebuf.token)
if (!eff_buf.token)
goto already_done;
ebuf.token[ebuf.len] = '\0';
eff_buf.token[eff_buf.token_len] = '\0';
if (!wsi->protocol->callback)
goto already_done;
@ -552,12 +547,7 @@ utf8_fail:
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n");
if (
/* coverity says dead code otherwise */
#if !defined(LWS_WITHOUT_EXTENSIONS)
n &&
#endif
ebuf.len)
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
@ -567,26 +557,21 @@ utf8_fail:
else
lws_remove_wsi_from_draining_ext_list(wsi);
if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
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, ebuf.token, ebuf.len);
wsi->ws->first_fragment = 0;
// lwsl_notice("%s: bulk ws rx: input used %d, output %d\n",
// __func__, wsi->ws->rx_ubuf_head, ebuf.len);
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->ws->rx_ubuf_head = 0;
wsi->u.ws.rx_ubuf_head = 0;
break;
default:
lwsl_err("client rx illegal state\n");
@ -597,7 +582,6 @@ already_done:
illegal_ctl_length:
lwsl_warn("Control frame asking for extended length is illegal\n");
/* kill the connection */
return -1;
}

1402
lib/client.c Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,92 +0,0 @@
#include "core/private.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, const char *reason)
{
return TEE_Realloc(ptr, size);
}
void *lws_malloc(size_t size, const char *reason)
{
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, const char *reason)
{
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, const char *reason))
{
(void)cb;
}
#else
static void *_realloc(void *ptr, size_t size, const char *reason)
{
if (size) {
#if defined(LWS_WITH_ESP32)
lwsl_notice("%s: size %lu: %s (free heap %d)\n", __func__,
(unsigned long)size, reason, (unsigned int)esp_get_free_heap_size() - (int)size);
#else
lwsl_debug("%s: size %lu: %s\n", __func__,
(unsigned long)size, reason);
#endif
#if defined(LWS_PLAT_OPTEE)
return (void *)TEE_Realloc(ptr, size);
#else
return (void *)realloc(ptr, size);
#endif
}
if (ptr)
free(ptr);
return NULL;
}
void *(*_lws_realloc)(void *ptr, size_t size, const char *reason) = _realloc;
void *lws_realloc(void *ptr, size_t size, const char *reason)
{
return _lws_realloc(ptr, size, reason);
}
void *lws_zalloc(size_t size, const char *reason)
{
void *ptr = _lws_realloc(NULL, size, reason);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void lws_set_allocator(void *(*cb)(void *ptr, size_t size, const char *reason))
{
_lws_realloc = cb;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,308 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-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 "core/private.h"
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
size_t real_len = len;
unsigned int n;
// lwsl_hexdump_err(buf, len);
/*
* Detect if we got called twice without going through the
* event loop to handle pending. This would be caused by either
* back-to-back writes in one WRITABLE (illegal) or calling lws_write()
* from outside the WRITABLE callback (illegal).
*/
if (wsi->could_have_pending) {
lwsl_hexdump_level(LLL_ERR, buf, len);
lwsl_err("** %p: vh: %s, prot: %s, role %s: "
"Illegal back-to-back write of %lu detected...\n",
wsi, wsi->vhost->name, wsi->protocol->name,
wsi->role_ops->name,
(unsigned long)len);
// assert(0);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
return (int)len;
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
lwsl_hexdump_level(LLL_ERR, buf, len);
lwsl_err("** %p: vh: %s, prot: %s, Sending new %lu, pending truncated ...\n"
" It's illegal to do an lws_write outside of\n"
" the writable callback: fix your code\n",
wsi, wsi->vhost->name, wsi->protocol->name,
(unsigned long)len);
assert(0);
return -1;
}
if (!wsi->http2_substream && !lws_socket_is_valid(wsi->desc.sockfd))
lwsl_warn("** error invalid sock but expected to send\n");
/* limit sending */
if (wsi->protocol->tx_packet_size)
n = (int)wsi->protocol->tx_packet_size;
else {
n = (int)wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
}
n += LWS_PRE + 4;
if (n > len)
n = (int)len;
/* nope, send it on the socket directly */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, n);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
/* something got written, it can have been truncated now */
wsi->could_have_pending = 1;
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
/* we're going to close, let close know sends aren't possible */
wsi->socket_is_permanently_unusable = 1;
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/*
* nothing got sent, not fatal. Retry the whole thing later,
* ie, implying treat it was a truncated send so it gets
* retried
*/
n = 0;
break;
}
/*
* we were already handling a truncated send?
*/
if (wsi->trunc_len) {
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
wsi->trunc_offset += n;
wsi->trunc_len -= n;
if (!wsi->trunc_len) {
lwsl_info("** %p partial send completed\n", wsi);
/* done with it, but don't free it */
n = (int)real_len;
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
lwsl_info("** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
return n;
}
if ((unsigned int)n == real_len)
/* what we just sent went out cleanly */
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable).
*/
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
(unsigned long)real_len);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
lws_free(wsi->trunc_alloc);
wsi->trunc_alloc_len = (unsigned int)(real_len - n);
wsi->trunc_alloc = lws_malloc(real_len - n,
"truncated send alloc");
if (!wsi->trunc_alloc) {
lwsl_err("truncated send: unable to malloc %lu\n",
(unsigned long)(real_len - n));
return -1;
}
}
wsi->trunc_offset = 0;
wsi->trunc_len = (unsigned int)(real_len - n);
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
#if !defined(LWS_WITH_ESP32)
if (lws_wsi_is_udp(wsi)) {
/* stash original destination for fulfilling UDP partials */
wsi->udp->sa_pending = wsi->udp->sa;
wsi->udp->salen_pending = wsi->udp->salen;
}
#endif
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
return (int)real_len;
}
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->parent_carries_io) {
struct lws_write_passthru pas;
pas.buf = buf;
pas.len = len;
pas.wp = wp;
pas.wsi = wsi;
if (wsi->parent->protocol->callback(wsi->parent,
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
wsi->parent->user_space,
(void *)&pas, 0))
return 1;
return (int)len;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
if ((int)len < 0) {
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
(int)len, (unsigned long)len);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
#ifdef LWS_WITH_ACCESS_LOG
wsi->http.access_log.sent += len;
#endif
if (wsi->vhost)
wsi->vhost->conn_stats.tx += len;
assert(wsi->role_ops);
if (!wsi->role_ops->write_role_protocol)
return lws_issue_raw(wsi, buf, len);
return wsi->role_ops->write_role_protocol(wsi, buf, len, &wp);
}
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
wsi->udp->salen = sizeof(wsi->udp->sa);
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
&wsi->udp->sa, &wsi->udp->salen);
#endif
} else
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
return n;
}
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR)
return LWS_SSL_CAPABLE_MORE_SERVICE;
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
int n = 0;
if (lws_wsi_is_udp(wsi)) {
#if !defined(LWS_WITH_ESP32)
if (wsi->trunc_len)
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
else
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
#endif
} else
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
lws_set_blocking_send(wsi);
}
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_pending_no_ssl(struct lws *wsi)
{
(void)wsi;
#if defined(LWS_WITH_ESP32)
return 100;
#else
return 0;
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -1,987 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
int
lws_callback_as_writeable(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n, m;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB, 1);
#if defined(LWS_WITH_STATS)
if (wsi->active_writable_req_us) {
uint64_t ul = time_in_microseconds() -
wsi->active_writable_req_us;
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_MS_WRITABLE_DELAY, ul);
lws_stats_atomic_max(wsi->context, pt,
LWSSTATS_MS_WORST_WRITABLE_DELAY, ul);
wsi->active_writable_req_us = 0;
}
#endif
n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)];
m = user_callback_handle_rxflow(wsi->protocol->callback,
wsi, (enum lws_callback_reasons) n,
wsi->user_space, NULL, 0);
return m;
}
LWS_VISIBLE int
lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
{
volatile struct lws *vwsi = (volatile struct lws *)wsi;
int n;
//lwsl_notice("%s: %p\n", __func__, wsi);
vwsi->leave_pollout_active = 0;
vwsi->handling_pollout = 1;
/*
* if another thread wants POLLOUT on us, from here on while
* handling_pollout is set, he will only set leave_pollout_active.
* If we are going to disable POLLOUT, we will check that first.
*/
wsi->could_have_pending = 0; /* clear back-to-back write detection */
/*
* user callback is lowest priority to get these notifications
* actually, since other pending things cannot be disordered
*
* Priority 1: pending truncated sends are incomplete ws fragments
* If anything else sent first the protocol would be
* corrupted.
*/
if (wsi->trunc_len) {
//lwsl_notice("%s: completing partial\n", __func__);
if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s signalling to close\n", __func__);
goto bail_die;
}
/* leave POLLOUT active either way */
goto bail_ok;
} else
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
wsi->socket_is_permanently_unusable = 1;
goto bail_die; /* retry closing now */
}
#ifdef LWS_WITH_CGI
/*
* A cgi master's wire protocol remains h1 or h2. He is just getting
* his data from his child cgis.
*/
if (wsi->http.cgi) {
/* also one shot */
if (pollfd)
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
return 1;
}
goto user_service_go_again;
}
#endif
/* if we got here, we should have wire protocol ops set on the wsi */
assert(wsi->role_ops);
if (!wsi->role_ops->handle_POLLOUT)
goto bail_ok;
switch ((wsi->role_ops->handle_POLLOUT)(wsi)) {
case LWS_HP_RET_BAIL_OK:
goto bail_ok;
case LWS_HP_RET_BAIL_DIE:
goto bail_die;
case LWS_HP_RET_USER_SERVICE:
break;
default:
assert(0);
}
/* one shot */
if (wsi->parent_carries_io) {
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return lws_callback_as_writeable(wsi);
}
if (pollfd) {
int eff = vwsi->leave_pollout_active;
if (!eff) {
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
goto bail_die;
}
}
vwsi->handling_pollout = 0;
/* cannot get leave_pollout_active set after the above */
if (!eff && wsi->leave_pollout_active) {
/*
* got set inbetween sampling eff and clearing
* handling_pollout, force POLLOUT on
*/
lwsl_debug("leave_pollout_active\n");
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) {
lwsl_info("failed at set pollfd\n");
goto bail_die;
}
}
vwsi->leave_pollout_active = 0;
}
if (lwsi_role_client(wsi) &&
!wsi->hdr_parsing_completed &&
lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS &&
lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY
)
goto bail_ok;
#ifdef LWS_WITH_CGI
user_service_go_again:
#endif
if (wsi->role_ops->perform_user_POLLOUT) {
if (wsi->role_ops->perform_user_POLLOUT(wsi) == -1)
goto bail_die;
else
goto bail_ok;
}
lwsl_debug("%s: %p: non mux: wsistate 0x%x, ops %s\n", __func__, wsi,
wsi->wsistate, wsi->role_ops->name);
vwsi = (volatile struct lws *)wsi;
vwsi->leave_pollout_active = 0;
n = lws_callback_as_writeable(wsi);
vwsi->handling_pollout = 0;
if (vwsi->leave_pollout_active)
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return n;
/*
* since these don't disable the POLLOUT, they are always doing the
* right thing for leave_pollout_active whether it was set or not.
*/
bail_ok:
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return 0;
bail_die:
vwsi->handling_pollout = 0;
vwsi->leave_pollout_active = 0;
return -1;
}
static int
__lws_service_timeout_check(struct lws *wsi, time_t sec)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int n = 0;
(void)n;
/*
* if we went beyond the allowed time, kill the
* connection
*/
if (wsi->dll_timeout.prev &&
lws_compare_time_t(wsi->context, sec, wsi->pending_timeout_set) >
wsi->pending_timeout_limit) {
if (wsi->desc.sockfd != LWS_SOCK_INVALID &&
wsi->position_in_fds_table >= 0)
n = pt->fds[wsi->position_in_fds_table].events;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_TIMEOUTS, 1);
/* no need to log normal idle keepalive timeout */
if (wsi->pending_timeout != PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE)
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_info("wsi %p: TIMEDOUT WAITING on %d "
"(did hdr %d, ah %p, wl %d, pfd "
"events %d) %llu vs %llu\n",
(void *)wsi, wsi->pending_timeout,
wsi->hdr_parsing_completed, wsi->http.ah,
pt->http.ah_wait_list_length, n,
(unsigned long long)sec,
(unsigned long long)wsi->pending_timeout_limit);
#if defined(LWS_WITH_CGI)
if (wsi->http.cgi)
lwsl_notice("CGI timeout: %s\n", wsi->http.cgi->summary);
#endif
#else
lwsl_info("wsi %p: TIMEDOUT WAITING on %d ", (void *)wsi,
wsi->pending_timeout);
#endif
/*
* Since he failed a timeout, he already had a chance to do
* something and was unable to... that includes situations like
* half closed connections. So process this "failed timeout"
* close as a violent death and don't try to do protocol
* cleanup like flush partials.
*/
wsi->socket_is_permanently_unusable = 1;
if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
wsi->protocol->callback(wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space,
(void *)"Timed out waiting SSL", 21);
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout");
return 1;
}
return 0;
}
int lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
uint8_t *buffered;
size_t blen;
int ret = 0, m;
/* his RX is flowcontrolled, don't send remaining now */
blen = lws_buflist_next_segment_len(&wsi->buflist, &buffered);
if (blen) {
if (buf >= buffered && buf + len <= buffered + blen) {
/* rxflow while we were spilling prev rxflow */
lwsl_info("%s: staying in rxflow buf\n", __func__);
return 1;
}
ret = 1;
}
/* a new rxflow, buffer it and warn caller */
m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n);
if (m < 0)
return -1;
if (m) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
return ret;
}
/* this is used by the platform service code to stop us waiting for network
* activity in poll() when we have something that already needs service
*/
LWS_VISIBLE LWS_EXTERN int
lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
/* Figure out if we really want to wait in poll()
* We only need to wait if really nothing already to do and we have
* to wait for something from network
*/
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
/* 1) if we know we are draining rx ext, do not wait in poll */
if (pt->ws.rx_draining_ext_list)
return 0;
#endif
/* 2) if we know we have non-network pending data, do not wait in poll */
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
if (pt->context->tls_ops->fake_POLLIN_for_buffered(pt))
return 0;
/* 3) If there is any wsi with rxflow buffered and in a state to process
* it, we should not wait in poll
*/
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION)
return 0;
} lws_end_foreach_dll(d);
return timeout_ms;
}
/*
* POLLIN said there is something... we must read it, and either use it; or
* if other material already in the buflist append it and return the buflist
* head material.
*/
int
lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_tokens *ebuf)
{
int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL);
ebuf->token = (char *)pt->serv_buf;
ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf,
wsi->context->pt_serv_buf_size);
if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior)
goto get_from_buflist;
if (ebuf->len <= 0)
return 0;
/* nothing in buflist already? Then just use what we read */
if (!prior)
return 0;
/* stash what we read */
n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token,
ebuf->len);
if (n < 0)
return -1;
if (n) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
/* get the first buflist guy in line */
get_from_buflist:
ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist,
(uint8_t **)&ebuf->token);
return 1; /* came from buflist */
}
int
lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used,
int buffered)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int m;
/* it's in the buflist; we didn't use any */
if (!used && buffered)
return 0;
if (used && buffered) {
m = lws_buflist_use_segment(&wsi->buflist, used);
lwsl_info("%s: draining rxflow: used %d, next %d\n",
__func__, used, m);
if (m)
return 0;
lwsl_info("%s: removed %p from dll_buflist\n", __func__, wsi);
lws_dll_lws_remove(&wsi->dll_buflist);
return 0;
}
/* any remainder goes on the buflist */
if (used != ebuf->len) {
m = lws_buflist_append_segment(&wsi->buflist,
(uint8_t *)ebuf->token + used,
ebuf->len - used);
if (m < 0)
return 1; /* OOM */
if (m) {
lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi);
lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist);
}
}
return 0;
}
void
lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt)
{
struct lws_pollfd pfd;
if (!pt->dll_head_buflist.next)
return;
/*
* service all guys with pending rxflow that reached a state they can
* accept the pending data
*/
lws_pt_lock(pt, __func__);
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
pfd.events = LWS_POLLIN;
pfd.revents = LWS_POLLIN;
pfd.fd = -1;
lwsl_debug("%s: rxflow processing: %p 0x%x\n", __func__, wsi,
wsi->wsistate);
if (!lws_is_flowcontrolled(wsi) &&
lwsi_state(wsi) != LRS_DEFERRING_ACTION &&
(wsi->role_ops->handle_POLLIN)(pt, wsi, &pfd) ==
LWS_HPI_RET_PLEASE_CLOSE_ME)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled");
} lws_end_foreach_dll_safe(d, d1);
lws_pt_unlock(pt);
}
/*
* guys that need POLLIN service again without waiting for network action
* can force POLLIN here if not flowcontrolled, so they will get service.
*
* Return nonzero if anybody got their POLLIN faked
*/
int
lws_service_flag_pending(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
#if defined(LWS_WITH_TLS)
struct lws *wsi, *wsi_next;
#endif
int forced = 0;
lws_pt_lock(pt, __func__);
/*
* 1) If there is any wsi with a buflist and in a state to process
* it, we should not wait in poll
*/
lws_start_foreach_dll(struct lws_dll_lws *, d, pt->dll_head_buflist.next) {
struct lws *wsi = lws_container_of(d, struct lws, dll_buflist);
if (lwsi_state(wsi) != LRS_DEFERRING_ACTION) {
forced = 1;
break;
}
} lws_end_foreach_dll(d);
#if defined(LWS_ROLE_WS)
forced |= role_ops_ws.service_flag_pending(context, tsi);
#endif
#if defined(LWS_WITH_TLS)
/*
* 2) 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 = pt->tls.pending_read_list;
while (wsi) {
wsi_next = wsi->tls.pending_read_list_next;
pt->fds[wsi->position_in_fds_table].revents |=
pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN;
if (pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN) {
forced = 1;
/*
* 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(wsi);
}
wsi = wsi_next;
}
#endif
lws_pt_unlock(pt);
return forced;
}
static int
lws_service_periodic_checks(struct lws_context *context,
struct lws_pollfd *pollfd, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
lws_sockfd_type our_fd = 0, tmp_fd;
struct lws *wsi;
int timed_out = 0;
time_t now;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
struct allocated_headers *ah;
int m;
#endif
if (!context->protocol_init_done)
if (lws_protocol_init(context))
return -1;
time(&now);
/*
* handle case that system time was uninitialized when lws started
* at boot, and got initialized a little later
*/
if (context->time_up < 1464083026 && now > 1464083026)
context->time_up = now;
if (context->last_timeout_check_s &&
now - context->last_timeout_check_s > 100) {
/*
* There has been a discontiguity. Any stored time that is
* less than context->time_discontiguity should have context->
* time_fixup added to it.
*
* Some platforms with no RTC will experience this as a normal
* event when ntp sets their clock, but we can have started
* long before that with a 0-based unix time.
*/
context->time_discontiguity = now;
context->time_fixup = now - context->last_timeout_check_s;
lwsl_notice("time discontiguity: at old time %llus, "
"new time %llus: +%llus\n",
(unsigned long long)context->last_timeout_check_s,
(unsigned long long)context->time_discontiguity,
(unsigned long long)context->time_fixup);
context->last_timeout_check_s = now - 1;
}
if (!lws_compare_time_t(context, context->last_timeout_check_s, now))
return 0;
context->last_timeout_check_s = now;
#if defined(LWS_WITH_STATS)
if (!tsi && now - context->last_dump > 10) {
lws_stats_log_dump(context);
context->last_dump = now;
}
#endif
lws_plat_service_periodic(context);
lws_check_deferred_free(context, 0);
#if defined(LWS_WITH_PEER_LIMITS)
lws_peer_cull_peer_wait_list(context);
#endif
/* retire unused deprecated context */
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_ESP32)
#if !defined(_WIN32)
if (context->deprecated && !context->count_wsi_allocated) {
lwsl_notice("%s: ending deprecated context\n", __func__);
kill(getpid(), SIGINT);
return 0;
}
#endif
#endif
/* global timeout check once per second */
if (pollfd)
our_fd = pollfd->fd;
/*
* Phase 1: check every wsi on the timeout check list
*/
lws_pt_lock(pt, __func__);
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
context->pt[tsi].dll_head_timeout.next) {
wsi = lws_container_of(d, struct lws, dll_timeout);
tmp_fd = wsi->desc.sockfd;
if (__lws_service_timeout_check(wsi, now)) {
/* he did time out... */
if (tmp_fd == our_fd)
/* it was the guy we came to service! */
timed_out = 1;
/* he's gone, no need to mark as handled */
}
} lws_end_foreach_dll_safe(d, d1);
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
/*
* Phase 2: double-check active ah timeouts independent of wsi
* timeout status
*/
ah = pt->http.ah_list;
while (ah) {
int len;
char buf[256];
const unsigned char *c;
if (!ah->in_use || !ah->wsi || !ah->assigned ||
(ah->wsi->vhost &&
lws_compare_time_t(context, now, ah->assigned) <
ah->wsi->vhost->timeout_secs_ah_idle + 360)) {
ah = ah->next;
continue;
}
/*
* a single ah session somehow got held for
* an unreasonable amount of time.
*
* Dump info on the connection...
*/
wsi = ah->wsi;
buf[0] = '\0';
#if !defined(LWS_PLAT_OPTEE)
lws_get_peer_simple(wsi, buf, sizeof(buf));
#else
buf[0] = '\0';
#endif
lwsl_notice("ah excessive hold: wsi %p\n"
" peer address: %s\n"
" ah pos %u\n",
wsi, buf, ah->pos);
buf[0] = '\0';
m = 0;
do {
c = lws_token_to_string(m);
if (!c)
break;
if (!(*c))
break;
len = lws_hdr_total_length(wsi, m);
if (!len || len > (int)sizeof(buf) - 1) {
m++;
continue;
}
if (lws_hdr_copy(wsi, buf,
sizeof buf, m) > 0) {
buf[sizeof(buf) - 1] = '\0';
lwsl_notice(" %s = %s\n",
(const char *)c, buf);
}
m++;
} while (1);
/* explicitly detach the ah */
lws_header_table_detach(wsi, 0);
/* ... and then drop the connection */
m = 0;
if (wsi->desc.sockfd == our_fd) {
m = timed_out;
/* it was the guy we came to service! */
timed_out = 1;
}
if (!m) /* if he didn't already timeout */
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"excessive ah");
ah = pt->http.ah_list;
}
#endif
lws_pt_unlock(pt);
#if 0
{
char s[300], *p = s;
for (n = 0; n < context->count_threads; n++)
p += sprintf(p, " %7lu (%5d), ",
context->pt[n].count_conns,
context->pt[n].fds_count);
lwsl_notice("load: %s\n", s);
}
#endif
/*
* Phase 3: vhost / protocol timer callbacks
*/
wsi = NULL;
lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
struct lws_timed_vh_protocol *nx;
if (v->timed_vh_protocol_list) {
lws_start_foreach_ll(struct lws_timed_vh_protocol *,
q, v->timed_vh_protocol_list) {
if (now >= q->time) {
if (!wsi)
wsi = lws_zalloc(sizeof(*wsi), "cbwsi");
wsi->context = context;
wsi->vhost = v;
wsi->protocol = q->protocol;
lwsl_debug("timed cb: vh %s, protocol %s, reason %d\n", v->name, q->protocol->name, q->reason);
q->protocol->callback(wsi, q->reason, NULL, NULL, 0);
nx = q->next;
lws_timed_callback_remove(v, q);
q = nx;
continue; /* we pointed ourselves to the next from the now-deleted guy */
}
} lws_end_foreach_ll(q, next);
}
} lws_end_foreach_ll(v, vhost_next);
if (wsi)
lws_free(wsi);
/*
* Phase 4: check for unconfigured vhosts due to required
* interface missing before
*/
lws_context_lock(context);
lws_start_foreach_llp(struct lws_vhost **, pv,
context->no_listener_vhost_list) {
struct lws_vhost *v = *pv;
lwsl_debug("deferred iface: checking if on vh %s\n", (*pv)->name);
if (_lws_vhost_init_server(NULL, *pv) == 0) {
/* became happy */
lwsl_notice("vh %s: became connected\n", v->name);
*pv = v->no_listener_vhost_list;
v->no_listener_vhost_list = NULL;
break;
}
} lws_end_foreach_llp(pv, no_listener_vhost_list);
lws_context_unlock(context);
/*
* Phase 5: role periodic checks
*/
#if defined(LWS_ROLE_WS)
role_ops_ws.periodic_checks(context, tsi, now);
#endif
#if defined(LWS_ROLE_CGI)
role_ops_cgi.periodic_checks(context, tsi, now);
#endif
/*
* Phase 6: check the remaining cert lifetime daily
*/
if (context->tls_ops &&
context->tls_ops->periodic_housekeeping)
context->tls_ops->periodic_housekeeping(context, now);
return timed_out;
}
LWS_VISIBLE int
lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws *wsi;
if (!context || context->being_destroyed1)
return -1;
/* the socket we came to service timed out, nothing to do */
if (lws_service_periodic_checks(context, pollfd, tsi) || !pollfd)
return 0;
/* no, here to service a socket descriptor */
wsi = wsi_from_fd(context, pollfd->fd);
if (!wsi)
/* not lws connection ... leave revents alone and return */
return 0;
/*
* so that caller can tell we handled, past here we need to
* zero down pollfd->revents after handling
*/
/* handle session socket closed */
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
(pollfd->revents & LWS_POLLHUP)) {
wsi->socket_is_permanently_unusable = 1;
lwsl_debug("Session Socket %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
goto close_and_handled;
}
#ifdef _WIN32
if (pollfd->revents & LWS_POLLOUT)
wsi->sock_send_blocking = FALSE;
#endif
if ((!(pollfd->revents & pollfd->events & LWS_POLLIN)) &&
(pollfd->revents & LWS_POLLHUP)) {
lwsl_debug("pollhup\n");
wsi->socket_is_permanently_unusable = 1;
goto close_and_handled;
}
#if defined(LWS_WITH_TLS)
if (lwsi_state(wsi) == LRS_SHUTDOWN &&
lws_is_ssl(wsi) && wsi->tls.ssl) {
switch (__lws_tls_shutdown(wsi)) {
case LWS_SSL_CAPABLE_DONE:
case LWS_SSL_CAPABLE_ERROR:
goto close_and_handled;
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
case LWS_SSL_CAPABLE_MORE_SERVICE_WRITE:
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto handled;
}
}
#endif
wsi->could_have_pending = 0; /* clear back-to-back write detection */
/* okay, what we came here to do... */
/* if we got here, we should have wire protocol ops set on the wsi */
assert(wsi->role_ops);
// lwsl_notice("%s: %s: wsistate 0x%x\n", __func__, wsi->role_ops->name,
// wsi->wsistate);
switch ((wsi->role_ops->handle_POLLIN)(pt, wsi, pollfd)) {
case LWS_HPI_RET_WSI_ALREADY_DIED:
return 1;
case LWS_HPI_RET_HANDLED:
break;
case LWS_HPI_RET_PLEASE_CLOSE_ME:
close_and_handled:
lwsl_debug("%p: Close and handled\n", wsi);
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled");
#if defined(_DEBUG) && defined(LWS_WITH_LIBUV)
/*
* confirm close has no problem being called again while
* it waits for libuv service to complete the first async
* close
*/
if (context->event_loop_ops == &event_loop_ops_uv)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"close_and_handled uv repeat test");
#endif
/*
* pollfd may point to something else after the close
* due to pollfd swapping scheme on delete on some platforms
* we can't clear revents now because it'd be the wrong guy's
* revents
*/
return 1;
default:
assert(0);
}
#if defined(LWS_WITH_TLS)
handled:
#endif
pollfd->revents = 0;
lws_pt_lock(pt, __func__);
__lws_hrtimer_service(pt);
lws_pt_unlock(pt);
return 0;
}
LWS_VISIBLE int
lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd)
{
return lws_service_fd_tsi(context, pollfd, 0);
}
LWS_VISIBLE int
lws_service(struct lws_context *context, int timeout_ms)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n;
if (!context)
return 1;
pt->inside_service = 1;
if (context->event_loop_ops->run_pt) {
/* we are configured for an event loop */
context->event_loop_ops->run_pt(context, 0);
pt->inside_service = 0;
return 1;
}
n = lws_plat_service(context, timeout_ms);
pt->inside_service = 0;
return n;
}
LWS_VISIBLE int
lws_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
int n;
pt->inside_service = 1;
if (context->event_loop_ops->run_pt) {
/* we are configured for an event loop */
context->event_loop_ops->run_pt(context, tsi);
pt->inside_service = 0;
return 1;
}
n = _lws_plat_service_tsi(context, timeout_ms, tsi);
pt->inside_service = 0;
return n;
}

View file

@ -22,7 +22,7 @@
#include <unistd.h>
#include <errno.h>
#include "core/private.h"
#include "private-libwebsockets.h"
int pid_daemon;
static char *lock_path;
@ -126,7 +126,7 @@ lws_daemonize(const char *_lock_path)
}
n = strlen(_lock_path) + 1;
lock_path = lws_malloc(n, "daemonize lock");
lock_path = lws_malloc(n);
if (!lock_path) {
fprintf(stderr, "Out of mem in lws_daemonize\n");
return 1;

View file

@ -1,124 +0,0 @@
## Information for new event lib implementers
### Introduction
By default lws has built-in support for POSIX poll() as the event loop.
However either to get access to epoll() or other platform specific better
poll waits, or to integrate with existing applications already using a
specific event loop, it can be desirable for lws to use another external
event library, like libuv, libevent or libev.
### Code placement
The code specific to the event library should live in `./lib/event-libs/**lib name**`
### Allowing control over enabling event libs
All event libs should add a cmake define `LWS_WITH_**lib name**` and make its build
dependent on it in CMakeLists.txt. Export the cmakedefine in `./cmake/lws_config.h.in`
as well so user builds can understand if the event lib is available in the lws build it is
trying to bind to.
If the event lib is disabled in cmake, nothing in its directory is built or referenced.
### Event loop ops struct
The event lib support is defined by `struct lws_event_loop_ops` in `lib/private/libwebsockets.h`,
each event lib support instantiates one of these and fills in the appropriate ops
callbacks to perform its job. By convention that lives in
`./lib/event-libs/**lib name**/**lib_name**.c`.
### Private event lib declarations
Truly private declarations for the event lib can go in the event-libs directory as you like.
However when the declarations must be accessible to other things in lws build, eg,
the event lib support adds members to `struct lws` when enabled, they should be in the
event lib supporr directory in a file `private.h`.
Search for "bring in event libs private declarations" in `./lib/private-libwebsockets.h
and add your private event lib support file there following the style used for the other
event libs, eg,
```
#if defined(LWS_WITH_LIBUV)
#include "event-libs/libuv/private.h"
#endif
```
If the event lib support is disabled at cmake, nothing from its private.h should be used anywhere.
### Integrating event lib assets to lws
If your event lib needs special storage in lws objects, that's no problem. But to keep
things sane, there are some rules.
- declare a "container struct" in your private.h for everything, eg, the libuv event
lib support need to add its own assets in the perthread struct, it declares in its private.h
```
struct lws_pt_eventlibs_libuv {
uv_loop_t *io_loop;
uv_signal_t signals[8];
uv_timer_t timeout_watcher;
uv_timer_t hrtimer;
uv_idle_t idle;
};
```
- add your event lib content in one place in the related lws struct, protected by `#if defined(LWS_WITH_**lib name**)`,
eg, again for LWS_WITH_LIBUV
```
struct lws_context_per_thread {
...
#if defined(LWS_WITH_LIBUV)
struct lws_pt_eventlibs_libuv uv;
#endif
...
```
### Adding to lws available event libs list
Edit the NULL-terminated array `available_event_libs` at the top of `./lib/context.c` to include
a pointer to your new event lib support's ops struct, following the style already there.
```
const struct lws_event_loop_ops *available_event_libs[] = {
#if defined(LWS_WITH_POLL)
&event_loop_ops_poll,
#endif
#if defined(LWS_WITH_LIBUV)
&event_loop_ops_uv,
#endif
...
```
This is used to provide a list of avilable configured backends.
### Enabling event lib adoption
You need to add a `LWS_SERVER_OPTION...` flag as necessary in `./lib/libwebsockets.h`
`enum lws_context_options`, and follow the existing code in `lws_create_context()`
to convert the flag into binding your ops struct to the context.
### Implementation of the event lib bindings
Study eg libuv implementation, using the available ops in the struct lws_event_loop_ops
as a guide.
### Destruction
Ending the event loop is generally a bit tricky, because if the event loop is internal
to the lws context, you cannot destroy it while the event loop is running.
Don't add special exports... we tried that, it's a huge mess. The same user code should be able
work with any of the event loops including poll.
The solution we found was hide the different processing necessary for the different cases in
lws_destroy_context(). To help with that there are ops available at two different places in
the context destroy processing.

View file

@ -1,378 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
static void
lws_ev_hrtimer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents)
{
struct lws_context_per_thread *pt =
(struct lws_context_per_thread *)watcher->data;
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT) {
ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0);
ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer);
}
lws_pt_unlock(pt);
}
static void
lws_ev_idle_cb(struct ev_loop *loop, struct ev_idle *handle, int revents)
{
struct lws_context_per_thread *pt = lws_container_of(handle,
struct lws_context_per_thread, ev.idle);
lws_usec_t us;
lws_service_do_ripe_rxflow(pt);
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(pt->context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
/* yes... come back again later */
return;
}
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT) {
ev_timer_set(&pt->ev.hrtimer, ((float)us) / 1000000.0, 0);
ev_timer_start(pt->ev.io_loop, &pt->ev.hrtimer);
}
lws_pt_unlock(pt);
/* there is nobody who needs service forcing, shut down idle */
ev_idle_stop(loop, handle);
}
static void
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct lws_context_per_thread *pt;
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 lws *wsi;
if (revents & EV_ERROR)
return;
eventfd.fd = watcher->fd;
eventfd.events = 0;
eventfd.revents = EV_NONE;
if (revents & EV_READ) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & EV_WRITE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
wsi = wsi_from_fd(context, watcher->fd);
pt = &context->pt[(int)wsi->tsi];
lws_service_fd_tsi(context, &eventfd, (int)wsi->tsi);
ev_idle_start(pt->ev.io_loop, &pt->ev.idle);
}
LWS_VISIBLE void
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
{
struct lws_context *context = watcher->data;
if (context->eventlib_signal_cb) {
context->eventlib_signal_cb((void *)watcher, watcher->signum);
return;
}
ev_break(loop, EVBREAK_ALL);
}
static int
elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev.watcher;
struct lws_vhost *vh = context->vhost_list;
const char *backend_name;
struct ev_loop *loop = (struct ev_loop *)_loop;
int status = 0;
int backend;
lwsl_info("%s: loop %p\n", __func__, _loop);
if (!loop)
loop = ev_loop_new(0);
else
context->pt[tsi].event_loop_foreign = 1;
if (!loop) {
lwsl_err("%s: creating event base failed\n", __func__);
return -1;
}
pt->ev.io_loop = loop;
/*
* Initialize the accept w_accept with all the listening sockets
* and register a callback for read operations
*/
while (vh) {
if (vh->lserv_wsi) {
vh->lserv_wsi->w_read.context = context;
vh->w_accept.context = context;
ev_io_init(&vh->w_accept.ev.watcher, lws_accept_cb,
vh->lserv_wsi->desc.sockfd, EV_READ);
ev_io_start(loop, &vh->w_accept.ev.watcher);
}
vh = vh->vhost_next;
}
/* Register the signal watcher unless it's a foreign loop */
if (!context->pt[tsi].event_loop_foreign) {
ev_signal_init(w_sigint, lws_ev_sigint_cb, SIGINT);
w_sigint->data = context;
ev_signal_start(loop, w_sigint);
}
backend = ev_backend(loop);
switch (backend) {
case EVBACKEND_SELECT:
backend_name = "select";
break;
case EVBACKEND_POLL:
backend_name = "poll";
break;
case EVBACKEND_EPOLL:
backend_name = "epoll";
break;
case EVBACKEND_KQUEUE:
backend_name = "kqueue";
break;
case EVBACKEND_DEVPOLL:
backend_name = "/dev/poll";
break;
case EVBACKEND_PORT:
backend_name = "Solaris 10 \"port\"";
break;
default:
backend_name = "Unknown libev backend";
break;
}
lwsl_info(" libev backend: %s\n", backend_name);
(void)backend_name;
ev_timer_init(&pt->ev.hrtimer, lws_ev_hrtimer_cb, 0, 0);
pt->ev.hrtimer.data = pt;
ev_idle_init(&pt->ev.idle, lws_ev_idle_cb);
return status;
}
static void
elops_destroy_pt_ev(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_vhost *vh = context->vhost_list;
while (vh) {
if (vh->lserv_wsi)
ev_io_stop(pt->ev.io_loop, &vh->w_accept.ev.watcher);
vh = vh->vhost_next;
}
/* static assets */
ev_timer_stop(pt->ev.io_loop, &pt->ev.hrtimer);
ev_idle_stop(pt->ev.io_loop, &pt->ev.idle);
if (!pt->event_loop_foreign) {
ev_signal_stop(pt->ev.io_loop, &pt->w_sigint.ev.watcher);
ev_loop_destroy(pt->ev.io_loop);
}
}
static int
elops_init_context_ev(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
context->pt[n].w_sigint.context = context;
return 0;
}
static void
elops_accept_ev(struct lws *wsi)
{
int fd;
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
wsi->w_read.context = wsi->context;
wsi->w_write.context = wsi->context;
ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ);
ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE);
}
static void
elops_io_ev(struct lws *wsi, int flags)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!pt->ev.io_loop)
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)
ev_io_start(pt->ev.io_loop, &wsi->w_write.ev.watcher);
if (flags & LWS_EV_READ)
ev_io_start(pt->ev.io_loop, &wsi->w_read.ev.watcher);
} else {
if (flags & LWS_EV_WRITE)
ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher);
if (flags & LWS_EV_READ)
ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher);
}
}
static void
elops_run_pt_ev(struct lws_context *context, int tsi)
{
if (context->pt[tsi].ev.io_loop)
ev_run(context->pt[tsi].ev.io_loop, 0);
}
static int
elops_destroy_context2_ev(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m;
lwsl_debug("%s\n", __func__);
for (n = 0; n < context->count_threads; n++) {
int budget = 1000;
pt = &context->pt[n];
/* only for internal loops... */
if (pt->event_loop_foreign || !pt->ev.io_loop)
continue;
if (!context->finalize_destroy_after_internal_loops_stopped) {
ev_break(pt->ev.io_loop, EVBREAK_ONE);
continue;
}
while (budget-- &&
(m = ev_run(pt->ev.io_loop, 0)))
;
ev_loop_destroy(pt->ev.io_loop);
}
return 0;
}
static int
elops_init_vhost_listen_wsi_ev(struct lws *wsi)
{
int fd;
if (!wsi) {
assert(0);
return 0;
}
wsi->w_read.context = wsi->context;
wsi->w_write.context = wsi->context;
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ);
ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE);
elops_io_ev(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static void
elops_destroy_wsi_ev(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
ev_io_stop(pt->ev.io_loop, &wsi->w_read.ev.watcher);
ev_io_stop(pt->ev.io_loop, &wsi->w_write.ev.watcher);
}
struct lws_event_loop_ops event_loop_ops_ev = {
/* name */ "libev",
/* init_context */ elops_init_context_ev,
/* destroy_context1 */ NULL,
/* destroy_context2 */ elops_destroy_context2_ev,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_ev,
/* init_pt */ elops_init_pt_ev,
/* wsi_logical_close */ NULL,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_ev,
/* io */ elops_io_ev,
/* run_pt */ elops_run_pt_ev,
/* destroy_pt */ elops_destroy_pt_ev,
/* destroy wsi */ elops_destroy_wsi_ev,
/* periodic_events_available */ 0,
};

View file

@ -1,53 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* This is included from core/private.h if LWS_WITH_LIBEV
*/
#include <ev.h>
#define LWS_EV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \
{ (_x)->data = _ctx; \
_ctx->count_event_loop_static_asset_handles++; }
#define LWS_EV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \
((struct lws_context *)(_x)->data)))
#define LWS_EV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \
count_event_loop_static_asset_handles))
struct lws_pt_eventlibs_libev {
struct ev_loop *io_loop;
struct ev_timer hrtimer;
struct ev_idle idle;
};
struct lws_io_watcher_libev {
ev_io watcher;
};
struct lws_signal_watcher_libev {
ev_signal watcher;
};
struct lws_context_eventlibs_libev {
int placeholder;
};
extern struct lws_event_loop_ops event_loop_ops_ev;

View file

@ -1,412 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
static void
lws_event_hrtimer_cb(int fd, short event, void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
struct timeval tv;
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT) {
tv.tv_sec = us / 1000000;
tv.tv_usec = us - (tv.tv_sec * 1000000);
evtimer_add(pt->event.hrtimer, &tv);
}
lws_pt_unlock(pt);
}
static void
lws_event_idle_timer_cb(int fd, short event, void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
struct timeval tv;
lws_usec_t us;
lws_service_do_ripe_rxflow(pt);
/*
* 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 */
tv.tv_sec = 0;
tv.tv_usec = 1000;
evtimer_add(pt->event.idle_timer, &tv);
return;
}
}
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT) {
tv.tv_sec = us / 1000000;
tv.tv_usec = us - (tv.tv_sec * 1000000);
evtimer_add(pt->event.hrtimer, &tv);
}
lws_pt_unlock(pt);
}
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_context_per_thread *pt;
struct lws_pollfd eventfd;
struct timeval tv;
struct lws *wsi;
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;
}
wsi = wsi_from_fd(context, sock_fd);
pt = &context->pt[(int)wsi->tsi];
lws_service_fd_tsi(context, &eventfd, wsi->tsi);
/* set the idle timer for 1ms ahead */
tv.tv_sec = 0;
tv.tv_usec = 1000;
evtimer_add(pt->event.idle_timer, &tv);
}
LWS_VISIBLE void
lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx)
{
struct lws_context_per_thread *pt = ctx;
struct event *signal = (struct event *)ctx;
if (pt->context->eventlib_signal_cb) {
pt->context->eventlib_signal_cb((void *)(lws_intptr_t)sock_fd,
event_get_signal(signal));
return;
}
if (!pt->event_loop_foreign)
event_base_loopbreak(pt->event.io_loop);
}
static int
elops_init_pt_event(struct lws_context *context, void *_loop, int tsi)
{
struct lws_vhost *vh = context->vhost_list;
struct event_base *loop = (struct event_base *)_loop;
struct lws_context_per_thread *pt = &context->pt[tsi];
lwsl_info("%s: loop %p\n", __func__, _loop);
if (!loop)
loop = event_base_new();
else
context->pt[tsi].event_loop_foreign = 1;
if (!loop) {
lwsl_err("%s: creating event base failed\n", __func__);
return -1;
}
pt->event.io_loop = loop;
/*
* Initialize all events with the listening sockets
* and register a callback for read operations
*/
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;
}
/* static event loop objects */
pt->event.hrtimer = event_new(loop, -1, EV_PERSIST,
lws_event_hrtimer_cb, pt);
pt->event.idle_timer = event_new(loop, -1, EV_PERSIST,
lws_event_idle_timer_cb, pt);
/* Register the signal watcher unless it's a foreign loop */
if (pt->event_loop_foreign)
return 0;
pt->w_sigint.event.watcher = evsignal_new(loop, SIGINT,
lws_event_sigint_cb, pt);
event_add(pt->w_sigint.event.watcher, NULL);
return 0;
}
static int
elops_init_context_event(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
context->pt[n].w_sigint.context = context;
return 0;
}
static void
elops_accept_event(struct lws *wsi)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt;
int fd;
wsi->w_read.context = context;
wsi->w_write.context = context;
// Initialize the event
pt = &context->pt[(int)wsi->tsi];
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd,
(EV_READ | EV_PERSIST), lws_event_cb, &wsi->w_read);
wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd,
(EV_WRITE | EV_PERSIST), lws_event_cb, &wsi->w_write);
}
static void
elops_io_event(struct lws *wsi, int flags)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (!pt->event.io_loop || wsi->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);
}
}
static void
elops_run_pt_event(struct lws_context *context, int tsi)
{
/* Run / Dispatch the event_base loop */
if (context->pt[tsi].event.io_loop)
event_base_dispatch(context->pt[tsi].event.io_loop);
}
static void
elops_destroy_pt_event(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_vhost *vh = context->vhost_list;
lwsl_info("%s\n", __func__);
if (!pt->event.io_loop)
return;
/*
* Free all events with the listening sockets
*/
while (vh) {
if (vh->lserv_wsi) {
event_free(vh->lserv_wsi->w_read.event.watcher);
vh->lserv_wsi->w_read.event.watcher = NULL;
event_free(vh->lserv_wsi->w_write.event.watcher);
vh->lserv_wsi->w_write.event.watcher = NULL;
}
vh = vh->vhost_next;
}
event_free(pt->event.hrtimer);
event_free(pt->event.idle_timer);
if (!pt->event_loop_foreign) {
event_del(pt->w_sigint.event.watcher);
event_free(pt->w_sigint.event.watcher);
event_base_free(pt->event.io_loop);
}
}
static void
elops_destroy_wsi_event(struct lws *wsi)
{
if (!wsi)
return;
if (wsi->w_read.event.watcher)
event_free(wsi->w_read.event.watcher);
if (wsi->w_write.event.watcher)
event_free(wsi->w_write.event.watcher);
}
static int
elops_init_vhost_listen_wsi_event(struct lws *wsi)
{
struct lws_context_per_thread *pt;
int fd;
if (!wsi) {
assert(0);
return 0;
}
wsi->w_read.context = wsi->context;
wsi->w_write.context = wsi->context;
pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
wsi->w_read.event.watcher = event_new(pt->event.io_loop, fd,
(EV_READ | EV_PERSIST),
lws_event_cb, &wsi->w_read);
wsi->w_write.event.watcher = event_new(pt->event.io_loop, fd,
(EV_WRITE | EV_PERSIST),
lws_event_cb, &wsi->w_write);
elops_io_event(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static int
elops_destroy_context2_event(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m;
lwsl_debug("%s\n", __func__);
for (n = 0; n < context->count_threads; n++) {
int budget = 1000;
pt = &context->pt[n];
/* only for internal loops... */
if (pt->event_loop_foreign || !pt->event.io_loop)
continue;
if (!context->finalize_destroy_after_internal_loops_stopped) {
event_base_loopexit(pt->event.io_loop, NULL);
continue;
}
while (budget-- &&
(m = event_base_loop(pt->event.io_loop, EVLOOP_NONBLOCK)))
;
#if 0
if (m) {
lwsl_err("%s: tsi %d: NOT everything closed\n",
__func__, n);
event_base_dump_events(pt->event.io_loop, stderr);
} else
lwsl_debug("%s: %d: everything closed OK\n", __func__, n);
#endif
event_base_free(pt->event.io_loop);
}
return 0;
}
struct lws_event_loop_ops event_loop_ops_event = {
/* name */ "libevent",
/* init_context */ elops_init_context_event,
/* destroy_context1 */ NULL,
/* destroy_context2 */ elops_destroy_context2_event,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_event,
/* init_pt */ elops_init_pt_event,
/* wsi_logical_close */ NULL,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_event,
/* io */ elops_io_event,
/* run_pt */ elops_run_pt_event,
/* destroy_pt */ elops_destroy_pt_event,
/* destroy wsi */ elops_destroy_wsi_event,
/* periodic_events_available */ 0,
};

View file

@ -1,44 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* This is included from core/private.h if LWS_WITH_LIBEVENT
*/
#include <event2/event.h>
struct lws_pt_eventlibs_libevent {
struct event_base *io_loop;
struct event *hrtimer;
struct event *idle_timer;
};
struct lws_io_watcher_libevent {
struct event *watcher;
};
struct lws_signal_watcher_libevent {
struct event *watcher;
};
struct lws_context_eventlibs_libevent {
int placeholder;
};
extern struct lws_event_loop_ops event_loop_ops_event;

View file

@ -1,929 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
static void
lws_uv_hrtimer_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.hrtimer);
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT)
uv_timer_start(&pt->uv.hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
lws_pt_unlock(pt);
}
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);
lws_usec_t us;
lws_service_do_ripe_rxflow(pt);
/*
* is there anybody with pending stuff that needs service forcing?
*/
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
/* -1 timeout means just do forced service */
_lws_plat_service_tsi(pt->context, -1, pt->tid);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
/* yes... come back again later */
return;
}
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_hrtimer_service(pt);
if (us != LWS_HRTIMER_NOWAIT)
uv_timer_start(&pt->uv.hrtimer, lws_uv_hrtimer_cb, us / 1000, 0);
lws_pt_unlock(pt);
/* there is nobody who needs service forcing, shut down idle */
uv_idle_stop(handle);
}
static void
lws_io_cb(uv_poll_t *watcher, int status, int revents)
{
struct lws_io_watcher *lws_io = 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_context_per_thread *pt = &context->pt[(int)wsi->tsi];
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_tsi(context, &eventfd, wsi->tsi);
uv_idle_start(&pt->uv.idle, lws_uv_idle);
}
/*
* 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.
*/
static void
lws_libuv_stop(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m;
lwsl_err("%s\n", __func__);
if (context->requested_kill) {
lwsl_err("%s: ignoring\n", __func__);
return;
}
context->requested_kill = 1;
m = context->count_threads;
context->being_destroyed = 1;
/*
* Phase 1: start the close of every dynamic uv handle
*/
while (m--) {
pt = &context->pt[m];
if (pt->pipe_wsi) {
uv_poll_stop(&pt->pipe_wsi->w_read.uv.watcher);
lws_destroy_event_pipe(pt->pipe_wsi);
pt->pipe_wsi = NULL;
}
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, __func__
/* no protocol close */);
n--;
}
}
lwsl_info("%s: started closing all wsi\n", __func__);
/* we cannot have completed... there are at least the cancel pipes */
}
static void
lws_uv_signal_handler(uv_signal_t *watcher, int signum)
{
struct lws_context *context = watcher->data;
if (context->eventlib_signal_cb) {
context->eventlib_signal_cb((void *)watcher, signum);
return;
}
lwsl_err("internal signal handler caught signal %d\n", signum);
lws_libuv_stop(watcher->data);
}
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 };
/*
* Closing Phase 2: Close callback for a static UV asset
*/
static void
lws_uv_close_cb_sa(uv_handle_t *handle)
{
struct lws_context *context =
LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(handle);
int n;
lwsl_info("%s: sa left %d: dyn left: %d\n", __func__,
context->count_event_loop_static_asset_handles,
context->count_wsi_allocated);
/* any static assets left? */
if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) ||
context->count_wsi_allocated)
return;
/*
* That's it... all wsi were down, and now every
* static asset lws had a UV handle for is down.
*
* Stop the loop so we can get out of here.
*/
for (n = 0; n < context->count_threads; n++) {
struct lws_context_per_thread *pt = &context->pt[n];
if (pt->uv.io_loop && !pt->event_loop_foreign)
uv_stop(pt->uv.io_loop);
}
if (!context->pt[0].event_loop_foreign) {
lwsl_info("%s: calling lws_context_destroy2\n", __func__);
lws_context_destroy2(context);
}
lwsl_info("%s: all done\n", __func__);
}
/*
* These must be called by protocols that want to use libuv objects directly...
*
* .... when the libuv object is created...
*/
LWS_VISIBLE void
lws_libuv_static_refcount_add(uv_handle_t *h, struct lws_context *context)
{
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(h, context);
}
/*
* ... and in the close callback when the object is closed.
*/
LWS_VISIBLE void
lws_libuv_static_refcount_del(uv_handle_t *h)
{
lws_uv_close_cb_sa(h);
}
static void lws_uv_close_cb(uv_handle_t *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);
}
LWS_VISIBLE void
lws_libuv_stop_without_kill(const struct lws_context *context, int tsi)
{
if (context->pt[tsi].uv.io_loop)
uv_stop(context->pt[tsi].uv.io_loop);
}
LWS_VISIBLE uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi)
{
if (context->pt[tsi].uv.io_loop)
return context->pt[tsi].uv.io_loop;
return NULL;
}
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);
}
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->uv.loop);
lwsl_notice(" Plugins:\n");
while (d && *d) {
lwsl_notice(" Scanning %s\n", *d);
m =uv_fs_scandir(&context->uv.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("Init %s failed %d\n", dent.name, m);
goto skip;
}
plugin = lws_malloc(sizeof(*plugin), "plugin");
if (!plugin) {
uv_dlclose(&lib);
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
lws_strncpy(plugin->name, dent.name, sizeof(plugin->name));
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];
int pofs = 0;
void *v;
int m;
#if defined(__MINGW32__) || !defined(WIN32)
pofs = 3;
#endif
if (!plugin)
return 0;
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->uv.loop))
;
return 0;
}
#endif
static int
elops_init_context_uv(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
context->pt[n].w_sigint.context = context;
return 0;
}
static int
elops_destroy_context1_uv(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m;
for (n = 0; n < context->count_threads; n++) {
int budget = 10000;
pt = &context->pt[n];
/* only for internal loops... */
if (!pt->event_loop_foreign) {
while (budget-- && (m = uv_run(pt->uv.io_loop,
UV_RUN_NOWAIT)))
;
if (m)
lwsl_err("%s: tsi %d: not all closed\n",
__func__, n);
}
}
/* call destroy2 if internal loop */
return !context->pt[0].event_loop_foreign;
}
static int
elops_destroy_context2_uv(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, internal = 0;
for (n = 0; n < context->count_threads; n++) {
pt = &context->pt[n];
/* only for internal loops... */
if (!pt->event_loop_foreign && pt->uv.io_loop) {
internal = 1;
if (!context->finalize_destroy_after_internal_loops_stopped)
uv_stop(pt->uv.io_loop);
else {
#if UV_VERSION_MAJOR > 0
uv_loop_close(pt->uv.io_loop);
#endif
lws_free_set_NULL(pt->uv.io_loop);
}
}
}
return internal;
}
static int
elops_wsi_logical_close_uv(struct lws *wsi)
{
if (wsi->parent_carries_io || !lws_socket_is_valid(wsi->desc.sockfd))
return 0;
if (wsi->listener || wsi->event_pipe) {
lwsl_debug("%s: %p: %d %d stop listener / pipe poll\n",
__func__, wsi, wsi->listener, wsi->event_pipe);
uv_poll_stop(&wsi->w_read.uv.watcher);
}
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
/*
* libuv has to do his own close handle processing asynchronously
*/
lws_libuv_closehandle(wsi);
return 1; /* do not complete the wsi close, uv close cb will do it */
}
static int
elops_check_client_connect_ok_uv(struct lws *wsi)
{
if (lws_libuv_check_watcher_active(wsi)) {
lwsl_warn("Waiting for libuv watcher to close\n");
return 1;
}
return 0;
}
static void
elops_close_handle_manually_uv(struct lws *wsi)
{
uv_handle_t *h = (void *)&wsi->w_read.uv.watcher;
lwsl_debug("%s: lws_libuv_closehandle: wsi %p\n", __func__, wsi);
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);
}
static void
elops_accept_uv(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
wsi->w_read.context = wsi->context;
if (wsi->role_ops->file_handle)
uv_poll_init(pt->uv.io_loop, &wsi->w_read.uv.watcher,
(int)(long long)wsi->desc.filefd);
else
uv_poll_init_socket(pt->uv.io_loop, &wsi->w_read.uv.watcher,
wsi->desc.sockfd);
}
static void
elops_io_uv(struct lws *wsi, int flags)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct lws_io_watcher *w = &wsi->w_read;
int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE);
lwsl_debug("%s: %p: %d\n", __func__, wsi, flags);
/* w->context is set after the loop is initialized */
if (!pt->uv.io_loop || !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);
}
w->actual_events = current_events;
}
static int
elops_init_vhost_listen_wsi_uv(struct lws *wsi)
{
struct lws_context_per_thread *pt;
int n;
if (!wsi)
return 0;
if (wsi->w_read.context)
return 0;
pt = &wsi->context->pt[(int)wsi->tsi];
if (!pt->uv.io_loop)
return 0;
wsi->w_read.context = wsi->context;
n = uv_poll_init_socket(pt->uv.io_loop,
&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;
}
elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static void
elops_run_pt_uv(struct lws_context *context, int tsi)
{
if (context->pt[tsi].uv.io_loop)
uv_run(context->pt[tsi].uv.io_loop, 0);
}
static void
elops_destroy_pt_uv(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
int m, ns;
lwsl_info("%s: %d\n", __func__, tsi);
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
return;
if (!pt->uv.io_loop)
return;
if (pt->event_loop_destroy_processing_done)
return;
pt->event_loop_destroy_processing_done = 1;
if (!pt->event_loop_foreign) {
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->uv.signals[m]);
uv_close((uv_handle_t *)&pt->uv.signals[m],
lws_uv_close_cb_sa);
}
} else
lwsl_debug("%s: not closing pt signals\n", __func__);
uv_timer_stop(&pt->uv.timeout_watcher);
uv_close((uv_handle_t *)&pt->uv.timeout_watcher, lws_uv_close_cb_sa);
uv_timer_stop(&pt->uv.hrtimer);
uv_close((uv_handle_t *)&pt->uv.hrtimer, lws_uv_close_cb_sa);
uv_idle_stop(&pt->uv.idle);
uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa);
}
/*
* 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
elops_init_pt_uv(struct lws_context *context, void *_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;
uv_loop_t *loop = (uv_loop_t *)_loop;
if (!pt->uv.io_loop) {
if (!loop) {
loop = lws_malloc(sizeof(*loop), "libuv 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->event_loop_foreign = 0;
} else {
lwsl_notice(" Using foreign event loop...\n");
pt->event_loop_foreign = 1;
}
pt->uv.io_loop = loop;
uv_idle_init(loop, &pt->uv.idle);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.idle, context);
ns = ARRAY_SIZE(sigs);
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
ns = 2;
if (!pt->event_loop_foreign) {
assert(ns <= (int)ARRAY_SIZE(pt->uv.signals));
for (n = 0; n < ns; n++) {
uv_signal_init(loop, &pt->uv.signals[n]);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.signals[n],
context);
pt->uv.signals[n].data = pt->context;
uv_signal_start(&pt->uv.signals[n],
lws_uv_signal_handler, 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 (elops_init_vhost_listen_wsi_uv(vh->lserv_wsi) == -1)
return -1;
vh = vh->vhost_next;
}
if (!first)
return status;
uv_timer_init(pt->uv.io_loop, &pt->uv.timeout_watcher);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.timeout_watcher, context);
uv_timer_start(&pt->uv.timeout_watcher, lws_uv_timeout_cb, 10, 1000);
uv_timer_init(pt->uv.io_loop, &pt->uv.hrtimer);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&pt->uv.hrtimer, context);
return status;
}
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);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int lspd = 0, m;
lwsl_info("%s: %p\n", __func__, wsi);
/*
* We get called back here for every wsi that closes
*/
if (wsi->role_ops == &role_ops_listen && wsi->context->deprecated) {
lspd = 1;
context->deprecation_pending_listen_close_count--;
if (!context->deprecation_pending_listen_close_count)
lspd = 2;
}
lws_pt_lock(pt, __func__);
__lws_close_free_wsi_final(wsi);
lws_pt_unlock(pt);
if (lspd == 2 && context->deprecation_cb) {
lwsl_notice("calling deprecation callback\n");
context->deprecation_cb();
}
lwsl_info("%s: sa left %d: dyn left: %d (rk %d)\n", __func__,
context->count_event_loop_static_asset_handles,
context->count_wsi_allocated, context->requested_kill);
/*
* eventually, we closed all the wsi...
*/
if (context->requested_kill && !context->count_wsi_allocated) {
struct lws_vhost *vh = context->vhost_list;
/*
* Start Closing Phase 2: close of static handles
*/
lwsl_info("%s: all lws dynamic handles down, closing static\n",
__func__);
for (m = 0; m < context->count_threads; m++)
elops_destroy_pt_uv(context, m);
/* protocols may have initialized libuv objects */
while (vh) {
lws_vhost_destroy1(vh);
vh = vh->vhost_next;
}
if (!context->count_event_loop_static_asset_handles &&
context->pt[0].event_loop_foreign) {
lwsl_info("%s: call lws_context_destroy2\n", __func__);
lws_context_destroy2(context);
}
}
}
void
lws_libuv_closehandle(struct lws *wsi)
{
if (wsi->told_event_loop_closed) {
assert(0);
return;
}
lwsl_debug("%s: %p\n", __func__, wsi);
wsi->told_event_loop_closed = 1;
/* required to defer actual deletion until libuv has processed it */
uv_close((uv_handle_t*)&wsi->w_read.uv.watcher, lws_libuv_closewsi);
}
struct lws_event_loop_ops event_loop_ops_uv = {
/* name */ "libuv",
/* init_context */ elops_init_context_uv,
/* destroy_context1 */ elops_destroy_context1_uv,
/* destroy_context2 */ elops_destroy_context2_uv,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uv,
/* init_pt */ elops_init_pt_uv,
/* wsi_logical_close */ elops_wsi_logical_close_uv,
/* check_client_connect_ok */ elops_check_client_connect_ok_uv,
/* close_handle_manually */ elops_close_handle_manually_uv,
/* accept */ elops_accept_uv,
/* io */ elops_io_uv,
/* run_pt */ elops_run_pt_uv,
/* destroy_pt */ elops_destroy_pt_uv,
/* destroy wsi */ NULL,
/* periodic_events_available */ 0,
};

View file

@ -1,71 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* This is included from core/private.h if LWS_WITH_LIBUV
*/
#include <uv.h>
/*
* libuv's async destroy cb means that asking to close something doesn't mean
* you can destroy it or parent things until after the close completes.
*
* So we must reference-count creation and close completions with libuv.
*
* All "static" (per-pt or per-context) uv handles must
*
* - have their .data set to point to the context
*
* - contribute to context->uv_count_static_asset_handles
* counting
*/
#define LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \
{ uv_handle_t *_uht = (uv_handle_t *)(_x); _uht->data = _ctx; \
_ctx->count_event_loop_static_asset_handles++; }
#define LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \
((struct lws_context *)((uv_handle_t *)((_x)->data)))
#define LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \
count_event_loop_static_asset_handles))
struct lws_pt_eventlibs_libuv {
uv_loop_t *io_loop;
uv_signal_t signals[8];
uv_timer_t timeout_watcher;
uv_timer_t hrtimer;
uv_idle_t idle;
};
struct lws_context_eventlibs_libuv {
uv_loop_t loop;
};
struct lws_io_watcher_libuv {
uv_poll_t watcher;
};
struct lws_signal_watcher_libuv {
uv_signal_t watcher;
};
extern struct lws_event_loop_ops event_loop_ops_uv;
LWS_VISIBLE uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi);

View file

@ -1,43 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* This is included from core/private.h if LWS_ROLE_WS
*/
#include <core/private.h>
struct lws_event_loop_ops event_loop_ops_poll = {
/* name */ "poll",
/* init_context */ NULL,
/* destroy_context1 */ NULL,
/* destroy_context2 */ NULL,
/* init_vhost_listen_wsi */ NULL,
/* init_pt */ NULL,
/* wsi_logical_close */ NULL,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ NULL,
/* io */ NULL,
/* run */ NULL,
/* destroy_pt */ NULL,
/* destroy wsi */ NULL,
/* periodic_events_available */ 1,
};

View file

@ -1,23 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
*/
extern struct lws_event_loop_ops event_loop_ops_poll;

View file

@ -1,74 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2018 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* This is included from core/private.h
*/
struct lws_event_loop_ops {
const char *name;
/* event loop-specific context init during context creation */
int (*init_context)(struct lws_context *context,
const struct lws_context_creation_info *info);
/* called during lws_destroy_context */
int (*destroy_context1)(struct lws_context *context);
/* called during lws_destroy_context2 */
int (*destroy_context2)(struct lws_context *context);
/* init vhost listening wsi */
int (*init_vhost_listen_wsi)(struct lws *wsi);
/* init the event loop for a pt */
int (*init_pt)(struct lws_context *context, void *_loop, int tsi);
/* called at end of first phase of close_free_wsi() */
int (*wsi_logical_close)(struct lws *wsi);
/* return nonzero if client connect not allowed */
int (*check_client_connect_ok)(struct lws *wsi);
/* close handle manually */
void (*close_handle_manually)(struct lws *wsi);
/* event loop accept processing */
void (*accept)(struct lws *wsi);
/* control wsi active events */
void (*io)(struct lws *wsi, int flags);
/* run the event loop for a pt */
void (*run_pt)(struct lws_context *context, int tsi);
/* called before pt is destroyed */
void (*destroy_pt)(struct lws_context *context, int tsi);
/* called just before wsi is freed */
void (*destroy_wsi)(struct lws *wsi);
unsigned int periodic_events_available:1;
};
/* bring in event libs private declarations */
#if defined(LWS_WITH_POLL)
#include "event-libs/poll/private.h"
#endif
#if defined(LWS_WITH_LIBUV)
#include "event-libs/libuv/private.h"
#endif
#if defined(LWS_WITH_LIBEVENT)
#include "event-libs/libevent/private.h"
#endif
#if defined(LWS_WITH_LIBEV)
#include "event-libs/libev/private.h"
#endif

View file

@ -1,7 +1,7 @@
/*
* ./lib/extension-permessage-deflate.c
*
* Copyright (C) 2016 - 2018 Andy Green <andy@warmcat.com>
* 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
@ -19,7 +19,7 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
#include <stdio.h>
#include <string.h>
@ -51,7 +51,7 @@ lws_extension_pmdeflate_restrict_args(struct lws *wsi,
n = wsi->context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = (int)wsi->protocol->rx_buffer_size;
n = wsi->protocol->rx_buffer_size;
extra = 7;
while (n >= 1 << (extra + 1))
@ -59,7 +59,7 @@ lws_extension_pmdeflate_restrict_args(struct lws *wsi,
if (extra < priv->args[PMD_RX_BUF_PWR2]) {
priv->args[PMD_RX_BUF_PWR2] = extra;
lwsl_info(" Capping pmd rx to %d\n", 1 << extra);
lwsl_err(" Capping pmd rx to %d\n", 1 << extra);
}
}
@ -72,7 +72,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
{
struct lws_ext_pm_deflate_priv *priv =
(struct lws_ext_pm_deflate_priv *)user;
struct lws_tokens *ebuf = (struct lws_tokens *)in;
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;
@ -82,13 +82,11 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
oa = in;
if (!oa->option_name)
break;
lwsl_ext("%s: named option set: %s\n", __func__, oa->option_name);
for (n = 0; n < (int)ARRAY_SIZE(lws_ext_pm_deflate_options); n++)
if (!strcmp(lws_ext_pm_deflate_options[n].name,
oa->option_name))
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 == (int)ARRAY_SIZE(lws_ext_pm_deflate_options))
if (n == ARRAY_SIZE(lws_ext_pm_deflate_options))
break;
oa->option_index = n;
@ -96,8 +94,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
case LWS_EXT_CB_OPTION_SET:
oa = in;
lwsl_ext("%s: option set: idx %d, %s, len %d\n", __func__,
oa->option_index, oa->start, oa->len);
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
@ -122,17 +120,16 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
n = context->pt_serv_buf_size;
if (wsi->protocol->rx_buffer_size)
n = (int)wsi->protocol->rx_buffer_size;
n = wsi->protocol->rx_buffer_size;
if (n < 128) {
lwsl_info(" permessage-deflate requires the protocol "
"(%s) to have an RX buffer >= 128\n",
wsi->protocol->name);
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), "pmd priv");
priv = lws_zalloc(sizeof(*priv));
*((void **)user) = priv;
lwsl_ext("%s: LWS_EXT_CB_*CONSTRUCT\n", __func__);
memset(priv, 0, sizeof(*priv));
@ -176,23 +173,27 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
case LWS_EXT_CB_PAYLOAD_RX:
lwsl_ext(" %s: LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d\n",
__func__, ebuf->len, priv->rx.avail_in);
if (!(wsi->ws->rsv_first_msg & 0x40))
__func__, eff_buf->token_len, priv->rx.avail_in);
if (!(wsi->u.ws.rsv_first_msg & 0x40))
return 0;
// lwsl_hexdump_debug(ebuf->token, ebuf->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
if (!priv->rx_init)
if (inflateInit2(&priv->rx,
-priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
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]),
"pmd rx inflate buf");
(1 << priv->args[PMD_RX_BUF_PWR2]));
if (!priv->buf_rx_inflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
@ -204,13 +205,12 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
* 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 && ebuf->token &&
ebuf->len) {
priv->rx.next_in = (unsigned char *)ebuf->token;
priv->rx.avail_in = ebuf->len;
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;
ebuf->token = (char *)priv->rx.next_out;
eff_buf->token = (char *)priv->rx.next_out;
priv->rx.avail_out = 1 << priv->args[PMD_RX_BUF_PWR2];
if (priv->rx_held_valid) {
@ -229,8 +229,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
* ...then put back the 00 00 FF FF the sender stripped as our
* input to zlib
*/
if (!priv->rx.avail_in && wsi->ws->final &&
!wsi->ws->rx_packet_length) {
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;
@ -239,13 +239,13 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
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->ws->final);
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_notice("zlib error inflate %d: %s\n",
lwsl_info("zlib error inflate %d: %s\n",
n, priv->rx.msg);
return -1;
}
@ -257,8 +257,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
* 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->ws->final &&
!wsi->ws->rx_packet_length && !was_fin &&
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");
@ -266,8 +266,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
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);
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:
@ -302,26 +302,27 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
priv->rx_held_valid = 1;
}
ebuf->len = lws_ptr_diff(priv->rx.next_out,
ebuf->token);
priv->count_rx_between_fin += ebuf->len;
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__, ebuf->len, priv->rx_held_valid,
__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]) {
lwsl_ext("PMD_SERVER_NO_CONTEXT_TAKEOVER\n");
(void)inflateEnd(&priv->rx);
priv->rx_init = 0;
}
}
// lwsl_hexdump_debug(ebuf->token, ebuf->len);
#if 0
for (n = 0; n < eff_buf->token_len; n++)
putchar(eff_buf->token[n]);
puts("\n");
#endif
return priv->rx_held_valid;
@ -342,23 +343,22 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
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]),
"pmd tx deflate buf");
(1 << priv->args[PMD_TX_BUF_PWR2]));
if (!priv->buf_tx_deflated) {
lwsl_err("%s: OOM\n", __func__);
return -1;
}
if (ebuf->token) {
lwsl_ext("%s: TX: ebuf length %d\n", __func__,
ebuf->len);
priv->tx.next_in = (unsigned char *)ebuf->token;
priv->tx.avail_in = ebuf->len;
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 < ebuf->len; n++) {
printf("%02X ", (unsigned char)ebuf->token[n]);
for (n = 0; n < eff_buf->token_len; n++) {
printf("%02X ", (unsigned char)eff_buf->token[n]);
if ((n & 15) == 15)
printf("\n");
}
@ -366,7 +366,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
#endif
priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
ebuf->token = (char *)priv->tx.next_out;
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);
@ -377,7 +377,7 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
if (priv->tx_held_valid) {
priv->tx_held_valid = 0;
if ((int)priv->tx.avail_out == 1 << priv->args[PMD_TX_BUF_PWR2])
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
@ -388,18 +388,18 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
* place the pending trailer 00 00 FF FF, just
* the 1 byte of live data
*/
*(--ebuf->token) = priv->tx_held[0];
*(--eff_buf->token) = priv->tx_held[0];
else {
/* he generated data, prepend whole pending */
ebuf->token -= 5;
eff_buf->token -= 5;
for (n = 0; n < 5; n++)
ebuf->token[n] = priv->tx_held[n];
eff_buf->token[n] = priv->tx_held[n];
}
}
priv->compressed_out = 1;
ebuf->len = lws_ptr_diff(priv->tx.next_out,
ebuf->token);
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
@ -424,15 +424,15 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
extra = !!(len & LWS_WRITE_NO_FIN) || !priv->tx.avail_out;
if (ebuf->len >= 4 + extra) {
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);
ebuf->len -= 4 + extra;
eff_buf->token_len -= 4 + extra;
}
lwsl_ext(" TX rewritten with new effbuff len %d, ret %d\n",
ebuf->len, !priv->tx.avail_out);
eff_buf->token_len, !priv->tx.avail_out);
return !priv->tx.avail_out; /* 1 == have more tx pending */
@ -441,27 +441,27 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
break;
priv->compressed_out = 0;
if ((*(ebuf->token) & 0x80) &&
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 = *(ebuf->token) & 15;
n = *(eff_buf->token) & 15;
/* set RSV1, but not on CONTINUATION */
if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
*ebuf->token |= 0x40;
*eff_buf->token |= 0x40;
#if 0
for (n = 0; n < ebuf->len; n++) {
printf("%02X ", (unsigned char)ebuf->token[n]);
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)*ebuf->token);
(unsigned char)*eff_buf->token);
break;
default:

View file

@ -1,9 +1,9 @@
#include "core/private.h"
#include "private-libwebsockets.h"
#include "extension-permessage-deflate.h"
LWS_VISIBLE void
lws_context_init_extensions(const struct lws_context_creation_info *info,
lws_context_init_extensions(struct lws_context_creation_info *info,
struct lws_context *context)
{
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
@ -121,7 +121,7 @@ lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
return -1;
leap = LEAPS_SEEK_ARG_TERM;
if (oa.start)
oa.len = lws_ptr_diff(in, oa.start);
oa.len = in - oa.start;
if (len == 1)
oa.len++;
@ -163,21 +163,18 @@ int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
{
int n, m, handled = 0;
if (!wsi->ws)
return 0;
for (n = 0; n < wsi->ws->count_act_ext; n++) {
m = wsi->ws->active_extensions[n]->callback(lws_get_context(wsi),
wsi->ws->active_extensions[n], wsi, reason,
wsi->ws->act_ext_user[n], arg, len);
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);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->ws->active_extensions[n]->name, reason);
wsi->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->ws->act_ext_user[n] = NULL;
wsi->act_ext_user[n] = NULL;
if (m > handled)
handled = m;
}
@ -191,17 +188,17 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int n = 0, m, handled = 0;
const struct lws_extension *ext;
if (!wsi || !wsi->vhost || !wsi->ws)
if (!wsi || !wsi->vhost)
return 0;
ext = wsi->vhost->ws.extensions;
ext = wsi->vhost->extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(lws_intptr_t)n, arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->ws->active_extensions[n]->name, reason);
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m)
@ -217,11 +214,11 @@ int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_tokens ebuf;
struct lws_tokens eff_buf;
int ret, m, n = 0;
ebuf.token = (char *)buf;
ebuf.len = (int)len;
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
/*
* while we have original buf to spill ourselves, or extensions report
@ -237,31 +234,31 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
/* show every extension the new incoming data */
m = lws_ext_cb_active(wsi,
LWS_EXT_CB_PACKET_TX_PRESEND, &ebuf, 0);
LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if ((char *)buf != ebuf.token)
if ((char *)buf != eff_buf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->ws->clean_buffer = 0;
wsi->u.ws.clean_buffer = 0;
/* assuming they left us something to send, send it */
if (ebuf.len) {
n = lws_issue_raw(wsi, (unsigned char *)ebuf.token,
ebuf.len);
if (eff_buf.token_len) {
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
lwsl_info("closing from ext access\n");
return -1;
}
/* always either sent it all or privately buffered */
if (wsi->ws->clean_buffer)
if (wsi->u.ws.clean_buffer)
len = n;
}
@ -274,8 +271,8 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
/* we used up what we had */
ebuf.token = NULL;
ebuf.len = 0;
eff_buf.token = NULL;
eff_buf.token_len = 0;
/*
* Did that leave the pipe choked?
@ -293,11 +290,11 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
* when he is ready to send and take care of it there
*/
lws_callback_on_writable(wsi);
wsi->ws->extension_data_pending = 1;
wsi->extension_data_pending = 1;
ret = 0;
}
return (int)len;
return len;
}
int
@ -307,18 +304,15 @@ lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r
struct lws_context *context = wsi->context;
int n, handled = 0;
if (!wsi->ws)
return 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
if (!wsi->ws->active_extensions[n]->callback)
for (n = 0; n < wsi->count_act_ext && !handled; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
handled |= wsi->ws->active_extensions[n]->callback(context,
wsi->ws->active_extensions[n], wsi,
r, wsi->ws->act_ext_user[n], v, len);
handled |= wsi->active_extensions[n]->callback(context,
wsi->active_extensions[n], wsi,
r, wsi->act_ext_user[n], v, len);
}
return handled;
@ -331,15 +325,12 @@ lws_set_extension_option(struct lws *wsi, const char *ext_name,
struct lws_ext_option_arg oa;
int idx = 0;
if (!wsi->ws)
return 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->ws->count_act_ext &&
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
while (idx < wsi->count_act_ext &&
strcmp(wsi->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->ws->count_act_ext)
if (idx == wsi->count_act_ext)
return -1; /* request ext not active on this wsi */
oa.option_name = opt_name;
@ -347,7 +338,7 @@ lws_set_extension_option(struct lws *wsi, const char *ext_name,
oa.start = opt_val;
oa.len = 0;
return wsi->ws->active_extensions[idx]->callback(
wsi->context, wsi->ws->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx], &oa, 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

@ -51,7 +51,7 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
#include <zlib.h>
@ -241,13 +241,13 @@ lws_fops_zip_scan(lws_fops_zip_t priv, const char *name, int len)
if (priv->hdr.filename_len != len)
goto next;
if (len >= (int)sizeof(buf) - 1)
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 ((int)amount != len)
if (amount != len)
return LWS_FZ_ERR_NAME_READ;
buf[len] = '\0';
@ -340,7 +340,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
* will come pointing at "/index.html"
*/
priv = lws_zalloc(sizeof(*priv), "fops_zip priv");
priv = lws_zalloc(sizeof(*priv));
if (!priv)
return NULL;
@ -348,8 +348,9 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
m = sizeof(rp) - 1;
if ((vpath - vfs_path - 1) < m)
m = lws_ptr_diff(vpath, vfs_path) - 1;
lws_strncpy(rp, vfs_path, m + 1);
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 */
@ -362,7 +363,7 @@ lws_fops_zip_open(const struct lws_plat_file_ops *fops, const char *vfs_path,
if (*vpath == '/')
vpath++;
m = lws_fops_zip_scan(priv, vpath, (int)strlen(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;
@ -564,7 +565,7 @@ spin:
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
/* fallthru */
/* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:

View file

@ -1,3 +1,8 @@
/*
* downloaded from
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
*/
#if !LWS_HAVE_GETIFADDRS
/*
* Copyright (c) 2000 - 2001 Kungliga Tekniska H<EFBFBD>gskolan
* (Royal Institute of Technology, Stockholm, Sweden).
@ -29,10 +34,6 @@
* 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.
*
* originally downloaded from
*
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
*/
#include <errno.h>
@ -43,7 +44,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "core/private.h"
#include "private-libwebsockets.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
@ -83,7 +84,7 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
buf_size = 8192;
for (;;) {
buf = lws_zalloc(buf_size, "getifaddrs2");
buf = lws_zalloc(buf_size);
if (buf == NULL) {
ret = ENOMEM;
goto error_out;
@ -135,12 +136,12 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
goto error_out;
}
*end = lws_malloc(sizeof(**end), "getifaddrs");
*end = lws_malloc(sizeof(**end));
(*end)->ifa_next = NULL;
(*end)->ifa_name = strdup(ifr->ifr_name);
(*end)->ifa_flags = ifreq.ifr_flags;
(*end)->ifa_addr = lws_malloc(salen, "getifaddrs");
(*end)->ifa_addr = lws_malloc(salen);
memcpy((*end)->ifa_addr, sa, salen);
(*end)->ifa_netmask = NULL;
@ -148,12 +149,12 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
/* fix these when we actually need them */
if (ifreq.ifr_flags & IFF_BROADCAST) {
(*end)->ifa_broadaddr =
lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs");
lws_malloc(sizeof(ifr->ifr_broadaddr));
memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
sizeof(ifr->ifr_broadaddr));
} else if (ifreq.ifr_flags & IFF_POINTOPOINT) {
(*end)->ifa_dstaddr =
lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs");
lws_malloc(sizeof(ifr->ifr_dstaddr));
memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
sizeof(ifr->ifr_dstaddr));
} else
@ -268,3 +269,4 @@ main()
return 0;
}
#endif
#endif

262
lib/handshake.c Normal file
View file

@ -0,0 +1,262 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2015 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"
/*
* -04 of the protocol (actually the 80th version) has a radically different
* handshake. The 04 spec gives the following idea
*
* The handshake from the client looks as follows:
*
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 4
*
* The handshake from the server looks as follows:
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
* Sec-WebSocket-Protocol: chat
*/
#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)
{
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);
switch (wsi->state) {
#ifdef LWS_USE_HTTP2
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
n = 0;
while (n < len) {
/*
* we were accepting input but now we stopped doing so
*/
if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
lws_rxflow_cache(wsi, buf, n, len);
return 1;
}
/* 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__);
goto bail;
}
}
break;
#endif
case LWSS_HTTP_ISSUING_FILE:
return 0;
case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
case LWSS_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);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)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:
*/
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:
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;
default:
break;
}
break;
case LWSS_HTTP_BODY:
http_postbody:
while (len && wsi->u.http.content_remain) {
/* Copy as much as possible, up to the limit of:
* what we have in the read buffer (len)
* remaining portion of the POST body (content_remain)
*/
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,
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);
if (n)
goto bail;
}
break;
}
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))
goto bail;
switch (wsi->mode) {
case LWSCM_WS_SERVING:
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
break;
}
break;
default:
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
break;
}
read_ok:
/* Nothing more to do for now */
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
return buf - oldbuf;
bail:
//lwsl_notice("closing connection at lws_read bail:\n");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2017 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
@ -19,12 +19,12 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
#include "lextable-strings.h"
const unsigned char *
lws_token_to_string(enum lws_token_indexes token)
const unsigned char *lws_token_to_string(enum lws_token_indexes token)
{
if ((unsigned int)token >= ARRAY_SIZE(set))
return NULL;
@ -37,8 +37,8 @@ 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)
{
#ifdef LWS_WITH_HTTP2
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_name(wsi, name,
value, length, p, end);
#else
@ -65,8 +65,8 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
unsigned char *end)
{
#ifdef LWS_WITH_HTTP2
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return 0;
#else
(void)wsi;
@ -79,40 +79,19 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
return 0;
}
int
lws_finalize_write_http_header(struct lws *wsi, unsigned char *start,
unsigned char **pp, unsigned char *end)
{
unsigned char *p;
int len;
if (lws_finalize_http_header(wsi, pp, end))
return 1;
p = *pp;
len = lws_ptr_diff(p, start);
if (lws_write(wsi, start, len, LWS_WRITE_HTTP_HEADERS) != len)
return 1;
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)
{
const unsigned char *name;
#ifdef LWS_WITH_HTTP2
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
return lws_add_http2_header_by_token(wsi, token, value,
length, p, end);
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_by_token(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);
}
@ -127,31 +106,8 @@ int lws_add_http_header_content_length(struct lws *wsi,
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH,
(unsigned char *)b, n, p, end))
return 1;
wsi->http.tx_content_length = content_length;
wsi->http.tx_content_remain = content_length;
lwsl_info("%s: wsi %p: tx_content_length/remain %llu\n", __func__,
wsi, (unsigned long long)content_length);
return 0;
}
int
lws_add_http_common_headers(struct lws *wsi, unsigned int code,
const char *content_type, lws_filepos_t content_len,
unsigned char **p, unsigned char *end)
{
if (lws_add_http_header_status(wsi, code, p, end))
return 1;
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)content_type,
(int)strlen(content_type), p, end))
return 1;
if (content_len != LWS_ILLEGAL_HTTP_CONTENT_LEN &&
lws_add_http_header_content_length(wsi, content_len, p, end))
return 1;
wsi->u.http.content_length = content_length;
wsi->u.http.content_remain = content_length;
return 0;
}
@ -200,11 +156,11 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
int n;
#ifdef LWS_WITH_ACCESS_LOG
wsi->http.access_log.response = code;
wsi->access_log.response = code;
#endif
#ifdef LWS_WITH_HTTP2
if (lwsi_role_h2(wsi) || lwsi_role_h2_ENCAPSULATION(wsi))
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING)
return lws_add_http2_header_status(wsi, code, p, end);
#endif
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
@ -212,18 +168,17 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
description = err500[code - 500];
if (code == 100)
description = "Continue";
if (code == 200)
description = "OK";
if (code == 304)
description = "Not Modified";
else
if (code >= 300 && code < 400)
description = "Redirect";
if (wsi->http.request_version < ARRAY_SIZE(hver))
p1 = hver[wsi->http.request_version];
if (wsi->u.http.request_version < ARRAY_SIZE(hver))
p1 = hver[wsi->u.http.request_version];
else
p1 = hver[0];
@ -237,7 +192,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
if (lws_add_http_header_by_name(wsi,
(const unsigned char *)headers->name,
(unsigned char *)headers->value,
(int)strlen(headers->value), p, end))
strlen(headers->value), p, end))
return 1;
headers = headers->next;
@ -269,29 +224,9 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
unsigned char *p = pt->serv_buf + LWS_PRE;
unsigned char *start = p;
unsigned char *end = p + context->pt_serv_buf_size - LWS_PRE;
int n = 0, m = 0, len;
int n = 0, m, len;
char slen[20];
if (!wsi->vhost) {
lwsl_err("%s: wsi not bound to vhost\n", __func__);
return 1;
}
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
if (!wsi->handling_404 &&
wsi->vhost->http.error_document_404 &&
code == HTTP_STATUS_NOT_FOUND)
/* we should do a redirect, and do the 404 there */
if (lws_http_redirect(wsi, HTTP_STATUS_FOUND,
(uint8_t *)wsi->vhost->http.error_document_404,
(int)strlen(wsi->vhost->http.error_document_404),
&p, end) > 0)
return 0;
#endif
/* if the redirect failed, just do a simple status */
p = start;
if (!html_body)
html_body = "";
@ -303,73 +238,41 @@ lws_return_http_status(struct lws *wsi, unsigned int code,
&p, end))
return 1;
len = 35 + (int)strlen(html_body) + sprintf(slen, "%d", code);
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))
(unsigned char *)slen, n,
&p, end))
return 1;
if (lws_finalize_http_header(wsi, &p, end))
return 1;
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream) {
#if defined(LWS_USE_HTTP2)
{
unsigned char *body = p + 512;
/*
* for HTTP/2, the headers must be sent separately, since they
* go out in their own frame. That puts us in a bind that
* we won't always be able to get away with two lws_write()s in
* sequence, since the first may use up the writability due to
* the pipe being choked or SSL_WANT_.
*
* However we do need to send the human-readable body, and the
* END_STREAM.
*
* Solve it by writing the headers now...
*/
m = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
if (m != lws_ptr_diff(p, start))
if (m != (int)(p - start))
return 1;
/*
* ... but stash the body and send it as a priority next
* handle_POLLOUT
*/
len = sprintf((char *)body, "<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
len = sprintf((char *)body,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
wsi->http.tx_content_length = len;
wsi->http.tx_content_remain = len;
wsi->h2.pending_status_body = lws_malloc(len + LWS_PRE + 1,
"pending status body");
if (!wsi->h2.pending_status_body)
return -1;
strcpy(wsi->h2.pending_status_body + LWS_PRE,
(const char *)body);
lws_callback_on_writable(wsi);
return 0;
} else
#endif
{
/*
* for http/1, we can just append the body after the finalized
* headers and send it all in one go.
*/
p += lws_snprintf((char *)p, end - p - 1,
"<html><body><h1>%u</h1>%s</body></html>",
code, html_body);
n = lws_ptr_diff(p, start);
m = lws_write(wsi, start, n, LWS_WRITE_HTTP);
if (m != n)
return 1;
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
return m != n;
}
@ -379,29 +282,36 @@ 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))
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
* 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))
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))
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;
return lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS |
LWS_WRITE_H2_STREAM_END);
n = lws_write(wsi, start, *p - start,
LWS_WRITE_HTTP_HEADERS);
return n;
}

704
lib/hpack.c Normal file
View file

@ -0,0 +1,704 @@
/*
* lib/hpack.c
*
* Copyright (C) 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"
/*
* Official static header table for HPACK
* +-------+-----------------------------+---------------+
| 1 | :authority | |
| 2 | :method | GET |
| 3 | :method | POST |
| 4 | :path | / |
| 5 | :path | /index.html |
| 6 | :scheme | http |
| 7 | :scheme | https |
| 8 | :status | 200 |
| 9 | :status | 204 |
| 10 | :status | 206 |
| 11 | :status | 304 |
| 12 | :status | 400 |
| 13 | :status | 404 |
| 14 | :status | 500 |
| 15 | accept-charset | |
| 16 | accept-encoding | gzip, deflate |
| 17 | accept-language | |
| 18 | accept-ranges | |
| 19 | accept | |
| 20 | access-control-allow-origin | |
| 21 | age | |
| 22 | allow | |
| 23 | authorization | |
| 24 | cache-control | |
| 25 | content-disposition | |
| 26 | content-encoding | |
| 27 | content-language | |
| 28 | content-length | |
| 29 | content-location | |
| 30 | content-range | |
| 31 | content-type | |
| 32 | cookie | |
| 33 | date | |
| 34 | etag | |
| 35 | expect | |
| 36 | expires | |
| 37 | from | |
| 38 | host | |
| 39 | if-match | |
| 40 | if-modified-since | |
| 41 | if-none-match | |
| 42 | if-range | |
| 43 | if-unmodified-since | |
| 44 | last-modified | |
| 45 | link | |
| 46 | location | |
| 47 | max-forwards | |
| 48 | proxy-authenticate | |
| 49 | proxy-authorization | |
| 50 | range | |
| 51 | referer | |
| 52 | refresh | |
| 53 | retry-after | |
| 54 | server | |
| 55 | set-cookie | |
| 56 | strict-transport-security | |
| 57 | transfer-encoding | |
| 58 | user-agent | |
| 59 | vary | |
| 60 | via | |
| 61 | www-authenticate | |
+-------+-----------------------------+---------------+
*/
static const unsigned char static_token[] = {
0,
WSI_TOKEN_HTTP_COLON_AUTHORITY,
WSI_TOKEN_HTTP_COLON_METHOD,
WSI_TOKEN_HTTP_COLON_METHOD,
WSI_TOKEN_HTTP_COLON_PATH,
WSI_TOKEN_HTTP_COLON_PATH,
WSI_TOKEN_HTTP_COLON_SCHEME,
WSI_TOKEN_HTTP_COLON_SCHEME,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_COLON_STATUS,
WSI_TOKEN_HTTP_ACCEPT_CHARSET,
WSI_TOKEN_HTTP_ACCEPT_ENCODING,
WSI_TOKEN_HTTP_ACCEPT_LANGUAGE,
WSI_TOKEN_HTTP_ACCEPT_RANGES,
WSI_TOKEN_HTTP_ACCEPT,
WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN,
WSI_TOKEN_HTTP_AGE,
WSI_TOKEN_HTTP_ALLOW,
WSI_TOKEN_HTTP_AUTHORIZATION,
WSI_TOKEN_HTTP_CACHE_CONTROL,
WSI_TOKEN_HTTP_CONTENT_DISPOSITION,
WSI_TOKEN_HTTP_CONTENT_ENCODING,
WSI_TOKEN_HTTP_CONTENT_LANGUAGE,
WSI_TOKEN_HTTP_CONTENT_LENGTH,
WSI_TOKEN_HTTP_CONTENT_LOCATION,
WSI_TOKEN_HTTP_CONTENT_RANGE,
WSI_TOKEN_HTTP_CONTENT_TYPE,
WSI_TOKEN_HTTP_COOKIE,
WSI_TOKEN_HTTP_DATE,
WSI_TOKEN_HTTP_ETAG,
WSI_TOKEN_HTTP_EXPECT,
WSI_TOKEN_HTTP_EXPIRES,
WSI_TOKEN_HTTP_FROM,
WSI_TOKEN_HOST,
WSI_TOKEN_HTTP_IF_MATCH,
WSI_TOKEN_HTTP_IF_MODIFIED_SINCE,
WSI_TOKEN_HTTP_IF_NONE_MATCH,
WSI_TOKEN_HTTP_IF_RANGE,
WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE,
WSI_TOKEN_HTTP_LAST_MODIFIED,
WSI_TOKEN_HTTP_LINK,
WSI_TOKEN_HTTP_LOCATION,
WSI_TOKEN_HTTP_MAX_FORWARDS,
WSI_TOKEN_HTTP_PROXY_AUTHENTICATE,
WSI_TOKEN_HTTP_PROXY_AUTHORIZATION,
WSI_TOKEN_HTTP_RANGE,
WSI_TOKEN_HTTP_REFERER,
WSI_TOKEN_HTTP_REFRESH,
WSI_TOKEN_HTTP_RETRY_AFTER,
WSI_TOKEN_HTTP_SERVER,
WSI_TOKEN_HTTP_SET_COOKIE,
WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY,
WSI_TOKEN_HTTP_TRANSFER_ENCODING,
WSI_TOKEN_HTTP_USER_AGENT,
WSI_TOKEN_HTTP_VARY,
WSI_TOKEN_HTTP_VIA,
WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
};
/* some of the entries imply values as well as header names */
static const char * const http2_canned[] = {
"",
"",
"GET",
"POST",
"/",
"/index.html",
"http",
"https",
"200",
"204",
"206",
"304",
"400",
"404",
"500",
"",
"gzip, deflate"
};
/* see minihuf.c */
#include "huftable.h"
static int huftable_decode(int pos, char c)
{
int q = pos + !!c;
if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */
return lextable[q] | 0x8000;
return pos + (lextable[q] << 1);
}
static int lws_hpack_update_table_size(struct lws *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)
{
struct allocated_headers * ah = wsi->u.http2.http.ah;
if (!hdr_token_idx) {
lwsl_err("%s: zero hdr_token_idx\n", __func__);
return 1;
}
if (ah->nfrag >= ARRAY_SIZE(ah->frag_index)) {
lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag);
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;
return 0;
}
static int lws_frag_append(struct lws *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;
}
static int lws_frag_end(struct lws *wsi)
{
if (lws_frag_append(wsi, 0))
return 1;
wsi->u.http2.http.ah->nfrag++;
return 0;
}
static void lws_dump_header(struct lws *wsi, int hdr)
{
char s[200];
int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
s[len] = '\0';
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)
{
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))
return static_token[index];
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)
{
struct hpack_dynamic_table *dyn;
int ret = 1;
wsi = lws_http2_get_network_wsi(wsi);
dyn = wsi->u.http2.hpack_dyn_table;
if (!dyn) {
dyn = lws_zalloc(sizeof(*dyn));
if (!dyn)
return 1;
wsi->u.http2.hpack_dyn_table = dyn;
dyn->args = lws_malloc(1024);
if (!dyn->args)
goto bail1;
dyn->args_length = 1024;
dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20);
if (!dyn->entries)
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);
dyn->pos += len;
dyn->next++;
return 0;
bail2:
lws_free(dyn->args);
bail1:
lws_free(dyn);
wsi->u.http2.hpack_dyn_table = NULL;
return ret;
}
static int lws_write_indexed_hdr(struct lws *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));
if (lws_frag_start(wsi, tok))
return 1;
if (idx < ARRAY_SIZE(http2_canned)) {
p = http2_canned[idx];
while (*p)
if (lws_frag_append(wsi, *p++))
return 1;
}
if (lws_frag_end(wsi))
return 1;
lws_dump_header(wsi, tok);
return 0;
}
int lws_hpack_interpret(struct lws *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;
lwsl_info("padding %d\n", c);
if (wsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
wsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
wsi->u.http2.hpack_m = 4;
} else
wsi->u.http2.hpack = HPKS_TYPE;
break;
case HKPS_OPT_E_DEPENDENCY:
wsi->u.http2.hpack_e_dep <<= 8;
wsi->u.http2.hpack_e_dep |= c;
if (! --wsi->u.http2.hpack_m) {
lwsl_info("hpack_e_dep = 0x%x\n", wsi->u.http2.hpack_e_dep);
wsi->u.http2.hpack = HKPS_OPT_WEIGHT;
}
break;
case HKPS_OPT_WEIGHT:
/* 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;
wsi->u.http2.hpack_m = 0;
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 */
break;
}
if (c & 0x40) { /* literal header incr idx */
/*
* [possibly-extended hdr idx (6) | new literal hdr name]
* 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;
wsi->u.http2.value = 0;
wsi->u.http2.hpack = HPKS_HLEN;
break;
}
/* indexed name */
wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR;
if ((c & 0x3f) == 0x3f) {
wsi->u.http2.hpack_len = c & 0x3f;
wsi->u.http2.hpack_m = 0;
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;
break;
}
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
*/
if (c == 0) { /* literal name */
wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE;
wsi->u.http2.hpack = HPKS_HLEN;
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;
if ((c & 0xf) == 0xf) {
wsi->u.http2.hpack_len = c & 0xf;
wsi->u.http2.hpack_m = 0;
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;
break;
case 0x20:
case 0x30: /* header table size update */
/* possibly-extended size value (5) */
wsi->u.http2.hpack_type = HPKT_SIZE_5;
if ((c & 0x1f) == 0x1f) {
wsi->u.http2.hpack_len = c & 0x1f;
wsi->u.http2.hpack_m = 0;
wsi->u.http2.hpack = HPKS_IDX_EXT;
break;
}
lws_hpack_update_table_size(wsi, c & 0x1f);
/* stay at HPKS_TYPE state */
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;
break;
}
}
break;
case HPKS_HLEN: /* [ H | 7+ ] */
wsi->u.http2.huff = !!(c & 0x80);
wsi->u.http2.hpack_pos = 0;
wsi->u.http2.hpack_len = c & 0x7f;
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__);
return 1;
}
} else
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.http2.hpack = HPKS_DATA;
break;
}
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;
wsi->u.http2.hpack_m += 7;
if (!(c & 0x80))
goto pre_data;
break;
case HPKS_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,
(c >> 7) & 1);
c <<= 1;
if (wsi->u.http2.hpack_pos == 0xffff)
return 1;
if (!(wsi->u.http2.hpack_pos & 0x8000))
continue;
c1 = wsi->u.http2.hpack_pos & 0x7fff;
wsi->u.http2.hpack_pos = 0;
if (!c1 && prev == HUFTABLE_0x100_PREV)
; /* EOT */
} else {
n = 8;
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))
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))
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)
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)
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
}
}
break;
case HKPS_OPT_DISCARD_PADDING:
lwsl_info("eating padding %x\n", c);
if (! --wsi->u.http2.padding)
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)
{
int mask = (1 << starting_bits) - 1;
if (num < mask) {
*((*p)++) |= num;
return *p >= end;
}
*((*p)++) |= mask;
if (*p >= end)
return 1;
num -= mask;
while (num >= 128) {
*((*p)++) = 0x80 | (num & 0x7f);
if (*p >= end)
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 len;
lwsl_info("%s: %p %s:%s\n", __func__, *p, name, value);
len = strlen((char *)name);
if (len)
if (name[len - 1] == ':')
len--;
if (end - *p < len + length + 8)
return 1;
*((*p)++) = 0; /* not indexed, literal name */
**p = 0; /* non-HUF */
if (lws_http2_num(7, len, p, end))
return 1;
memcpy(*p, name, len);
*p += len;
*(*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)
{
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);
}
int lws_add_http2_header_status(struct lws *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))
return 1;
return 0;
}

536
lib/http2.c Normal file
View file

@ -0,0 +1,536 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* 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
* 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"
const struct http2_settings lws_http2_default_settings = { {
0,
/* LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE */ 4096,
/* LWS_HTTP2_SETTINGS__ENABLE_PUSH */ 1,
/* LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS */ 100,
/* LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE */ 65535,
/* LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE */ 16384,
/* LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE */ ~0,
}};
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)
{
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 lws *wsi = lws_create_new_server_wsi(vhost);
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;
lws_http2_init(&wsi->u.http2.peer_settings);
lws_http2_init(&wsi->u.http2.my_settings);
wsi->u.http2.stream_id = sid;
wsi->u.http2.my_stream_id = sid;
wsi->u.http2.parent_wsi = 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->mode = parent_wsi->mode;
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);
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)
{
struct lws **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)
{
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) {
b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5];
settings->setting[a] = b;
lwsl_info("http2 settings %d <- 0x%x\n", a, b);
}
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)
{
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)
{
struct lws *wsi_eff = lws_http2_get_network_wsi(wsi);
unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
int n;
*p++ = len >> 16;
*p++ = len >> 8;
*p++ = len;
*p++ = type;
*p++ = flags;
*p++ = sid >> 24;
*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);
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);
wsi->u.http2.tx_credit -= len;
}
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)
{
*buf++ = n >> 8;
*buf++ = n;
*buf++ = wsi->u.http2.my_settings.setting[n] >> 24;
*buf++ = wsi->u.http2.my_settings.setting[n] >> 16;
*buf++ = wsi->u.http2.my_settings.setting[n] >> 8;
*buf = wsi->u.http2.my_settings.setting[n];
}
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)
{
struct lws *swsi;
int n;
switch (wsi->state) {
case LWSS_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->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);
}
break;
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_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;
/* applies to wsi->u.http2.stream_wsi which may be wsi*/
switch(wsi->u.http2.type) {
case LWS_HTTP2_FRAME_TYPE_SETTINGS:
wsi->u.http2.stream_wsi->u.http2.one_setting[wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH] = c;
if (wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH == LWS_HTTP2_SETTINGS_LENGTH - 1)
if (lws_http2_interpret_settings_payload(
&wsi->u.http2.stream_wsi->u.http2.peer_settings,
wsi->u.http2.one_setting,
LWS_HTTP2_SETTINGS_LENGTH))
return 1;
break;
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__);
return 1;
}
break;
case LWS_HTTP2_FRAME_TYPE_GOAWAY:
if (wsi->u.http2.count >= 5 && wsi->u.http2.count <= 8) {
wsi->u.http2.hpack_e_dep <<= 8;
wsi->u.http2.hpack_e_dep |= c;
if (wsi->u.http2.count == 8) {
lwsl_info("goaway err 0x%x\n", wsi->u.http2.hpack_e_dep);
}
}
wsi->u.http2.GOING_AWAY = 1;
break;
case LWS_HTTP2_FRAME_TYPE_DATA:
break;
case LWS_HTTP2_FRAME_TYPE_PRIORITY:
break;
case LWS_HTTP2_FRAME_TYPE_RST_STREAM:
break;
case LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE:
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 */
if (wsi->u.http2.count > 8)
return 1;
wsi->u.http2.ping_payload[wsi->u.http2.count - 1] = c;
}
break;
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
wsi->u.http2.hpack_e_dep <<= 8;
wsi->u.http2.hpack_e_dep |= c;
break;
}
if (wsi->u.http2.count != wsi->u.http2.length)
break;
/* end of frame */
wsi->u.http2.frame_state = 0;
wsi->u.http2.count = 0;
swsi = wsi->u.http2.stream_wsi;
/* set our initial window size */
if (!wsi->u.http2.initialized) {
wsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE];
lwsl_info("initial tx credit on master conn %p: %d\n", wsi, wsi->u.http2.tx_credit);
wsi->u.http2.initialized = 1;
}
switch (wsi->u.http2.type) {
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);
(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);
}
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)))
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);
}
break;
}
break;
}
switch (wsi->u.http2.frame_state++) {
case 0:
wsi->u.http2.length = c;
break;
case 1:
case 2:
wsi->u.http2.length <<= 8;
wsi->u.http2.length |= c;
break;
case 3:
wsi->u.http2.type = c;
break;
case 4:
wsi->u.http2.flags = c;
break;
case 5:
case 6:
case 7:
case 8:
wsi->u.http2.stream_id <<= 8;
wsi->u.http2.stream_id |= c;
break;
}
if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { /* frame header complete */
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);
break;
case LWS_HTTP2_FRAME_TYPE_PING:
if (wsi->u.http2.stream_id)
return 1;
if (wsi->u.http2.length != 8)
return 1;
break;
case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
if (wsi->u.http2.END_HEADERS)
return 1;
goto update_end_headers;
case LWS_HTTP2_FRAME_TYPE_HEADERS:
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;
}
/* 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;
if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PADDED)
swsi->u.http2.hpack = HPKS_OPT_PADDING;
else
if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
swsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
swsi->u.http2.hpack_m = 4;
} else
swsi->u.http2.hpack = HPKS_TYPE;
lwsl_info("initial hpack state %d\n", swsi->u.http2.hpack);
break;
case LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE:
if (wsi->u.http2.length != 4)
return 1;
break;
}
if (wsi->u.http2.length == 0)
wsi->u.http2.frame_state = 0;
}
break;
}
return 0;
}
int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
{
unsigned char settings[LWS_PRE + 6 * LWS_HTTP2_SETTINGS__COUNT];
struct lws *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]);
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]);
if (n != m) {
lwsl_info("send %d %d\n", n, m);
return 1;
}
break;
case LWS_PPS_HTTP2_ACK_SETTINGS:
/* 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]);
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 (lws_is_ssl(lws_http2_get_network_wsi(wsi))) {
lwsl_info("skipping nonexistent 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);
/* 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);
swsi->u.http2.initialized = 1;
/* demanded by HTTP2 */
swsi->u.http2.END_STREAM = 1;
lwsl_info("servicing initial http request\n");
return lws_http_action(swsi);
}
break;
case LWS_PPS_HTTP2_PONG:
memcpy(&settings[LWS_PRE], 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]);
if (n != 8) {
lwsl_info("send %d %d\n", n, m);
return 1;
}
break;
default:
break;
}
return 0;
}
struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
{
do {
wsi = wsi->u.http2.next_child_wsi;
if (!wsi)
return NULL;
} while (n--);
return wsi;
}

View file

@ -1,7 +1,7 @@
/*
* libwebsockets web server application
*
* Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
* 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
@ -19,7 +19,8 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
#include "lejp.h"
#ifndef _WIN32
/* this is needed for Travis CI */
@ -39,7 +40,6 @@ static const char * const paths_global[] = {
"global.timeout-secs",
"global.reject-service-keywords[].*",
"global.reject-service-keywords[]",
"global.default-alpn",
};
enum lejp_global_paths {
@ -52,8 +52,7 @@ enum lejp_global_paths {
LWJPGP_PINGPONG_SECS,
LWJPGP_TIMEOUT_SECS,
LWJPGP_REJECT_SERVICE_KEYWORDS_NAME,
LWJPGP_REJECT_SERVICE_KEYWORDS,
LWJPGP_DEFAULT_ALPN,
LWJPGP_REJECT_SERVICE_KEYWORDS
};
static const char * const paths_vhosts[] = {
@ -100,11 +99,6 @@ static const char * const paths_vhosts[] = {
"vhosts[].client-ssl-cert",
"vhosts[].client-ssl-ca",
"vhosts[].client-ssl-ciphers",
"vhosts[].onlyraw",
"vhosts[].client-cert-required",
"vhosts[].ignore-missing-cert",
"vhosts[].error-document-404",
"vhosts[].alpn",
};
enum lejp_vhost_paths {
@ -151,11 +145,6 @@ enum lejp_vhost_paths {
LEJPVP_CLIENT_SSL_CERT,
LEJPVP_CLIENT_SSL_CA,
LEJPVP_CLIENT_CIPHERS,
LEJPVP_FLAG_ONLYRAW,
LEJPVP_FLAG_CLIENT_CERT_REQUIRED,
LEJPVP_IGNORE_MISSING_CERT,
LEJPVP_ERROR_DOCUMENT_404,
LEJPVP_ALPN,
};
static const char * const parser_errs[] = {
@ -225,14 +214,14 @@ arg_to_bool(const char *s)
if (n)
return 1;
for (n = 0; n < (int)ARRAY_SIZE(on); n++)
for (n = 0; n < ARRAY_SIZE(on); n++)
if (!strcasecmp(s, on[n]))
return 1;
return 0;
}
static signed char
static char
lejp_globals_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
@ -294,10 +283,6 @@ lejp_globals_cb(struct lejp_ctx *ctx, char reason)
a->info->timeout_secs = atoi(ctx->buf);
return 0;
case LWJPGP_DEFAULT_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}
@ -309,7 +294,7 @@ dostring:
return 0;
}
static signed char
static char
lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
@ -325,39 +310,20 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
#endif
if (reason == LEJPCB_OBJECT_START && ctx->path_match == LEJPVP + 1) {
uint32_t i[4];
const char *ss;
/* set the defaults for this vhost */
a->valid = 1;
a->head = NULL;
a->last = NULL;
i[0] = a->info->count_threads;
i[1] = a->info->options & (
LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME |
LWS_SERVER_OPTION_LIBUV |
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN |
LWS_SERVER_OPTION_LIBEVENT |
LWS_SERVER_OPTION_LIBEV
);
ss = a->info->server_string;
i[2] = a->info->ws_ping_pong_interval;
i[3] = a->info->timeout_secs;
memset(a->info, 0, sizeof(*a->info));
a->info->count_threads = i[0];
a->info->options = i[1];
a->info->server_string = ss;
a->info->ws_ping_pong_interval = i[2];
a->info->timeout_secs = i[3];
a->info->port = 0;
a->info->iface = NULL;
a->info->protocols = a->protocols;
a->info->extensions = a->extensions;
#if defined(LWS_WITH_TLS)
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:"
@ -371,7 +337,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!DHE-RSA-AES256-SHA256:"
"!AES256-GCM-SHA384:"
"!AES256-SHA256";
#endif
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:"
@ -385,7 +351,13 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
"!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 &&
@ -405,7 +377,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->pvo->next = a->info->pvo;
a->info->pvo = a->pvo;
a->pvo->name = a->p;
lwsl_info(" adding protocol %s\n", a->p);
lwsl_notice(" adding protocol %s\n", a->p);
a->p += n;
a->pvo->value = a->p;
a->pvo->options = NULL;
@ -457,7 +429,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
}
a->any_vhosts = 1;
#if defined(LWS_WITH_TLS)
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;
@ -471,7 +442,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
lws_init_vhost_client_ssl(a->info, vhost);
}
#endif
return 0;
}
@ -502,17 +472,17 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
if (a->last)
a->last->mount_next = m;
for (n = 0; n < (int)ARRAY_SIZE(mount_protocols); n++)
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
if (!strncmp(a->m.origin, mount_protocols[n],
strlen(mount_protocols[n]))) {
lwsl_info("----%s\n", a->m.origin);
lwsl_err("----%s\n", a->m.origin);
m->origin_protocol = n;
m->origin = a->m.origin +
strlen(mount_protocols[n]);
break;
}
if (n == (int)ARRAY_SIZE(mount_protocols)) {
if (n == ARRAY_SIZE(mount_protocols)) {
lwsl_err("unsupported protocol:// %s\n", a->m.origin);
return 1;
}
@ -601,11 +571,9 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_KEEPALIVE_TIMEOUT:
a->info->keepalive_timeout = atoi(ctx->buf);
return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_CIPHERS:
a->info->client_ssl_cipher_list = a->p;
break;
#endif
case LEJPVP_CIPHERS:
a->info->ssl_cipher_list = a->p;
break;
@ -681,7 +649,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_ENABLE_CLIENT_SSL:
a->enable_client_ssl = arg_to_bool(ctx->buf);
return 0;
#if defined(LWS_WITH_TLS)
case LEJPVP_CLIENT_SSL_KEY:
a->info->client_ssl_private_key_filepath = a->p;
break;
@ -691,7 +658,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
case LEJPVP_CLIENT_SSL_CA:
a->info->client_ssl_ca_filepath = a->p;
break;
#endif
case LEJPVP_NOIPV6:
if (arg_to_bool(ctx->buf))
@ -700,13 +666,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6);
return 0;
case LEJPVP_FLAG_ONLYRAW:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_ONLY_RAW;
else
a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW);
return 0;
case LEJPVP_IPV6ONLY:
a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY;
if (arg_to_bool(ctx->buf))
@ -715,24 +674,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE);
return 0;
case LEJPVP_FLAG_CLIENT_CERT_REQUIRED:
if (arg_to_bool(ctx->buf))
a->info->options |=
LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
return 0;
case LEJPVP_IGNORE_MISSING_CERT:
if (arg_to_bool(ctx->buf))
a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT;
else
a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT);
return 0;
case LEJPVP_ERROR_DOCUMENT_404:
a->info->error_document_404 = a->p;
break;
case LEJPVP_SSL_OPTION_SET:
a->info->ssl_options_set |= atol(ctx->buf);
return 0;
@ -740,10 +681,6 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
a->info->ssl_options_clear |= atol(ctx->buf);
return 0;
case LEJPVP_ALPN:
a->info->alpn = a->p;
break;
default:
return 0;
}
@ -755,7 +692,7 @@ dostring:
n = p1 - p;
if (n > a->end - a->p)
n = a->end - a->p;
lws_strncpy(a->p, p, n + 1);
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);
@ -808,7 +745,7 @@ lwsws_get_config(void *user, const char *f, const char * const *paths,
return 0;
}
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 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,
@ -869,12 +806,9 @@ lwsws_get_config_d(void *user, const char *d, const char * const *paths,
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 (strchr(namelist[i]->d_name, '~'))
goto skip;
lws_snprintf(path, sizeof(path) - 1, "%s/%s", d,
namelist[i]->d_name);
ret = lwsws_get_config(user, path, paths, count_paths, cb);
@ -883,7 +817,6 @@ lwsws_get_config_d(void *user, const char *d, const char * const *paths,
free(namelist[i]);
goto bail;
}
skip:
free(namelist[i]);
}

View file

@ -1,26 +1,14 @@
/*
* Lightweight Embedded JSON Parser
*
* Copyright (C) 2013-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
* 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 <libwebsockets.h>
#include <string.h>
#include "lejp.h"
#include <stdio.h>
/**
@ -37,7 +25,7 @@
void
lejp_construct(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char count_paths)
{
ctx->st[0].s = 0;
@ -98,7 +86,7 @@ lejp_destruct(struct lejp_ctx *ctx)
void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason))
char (*callback)(struct lejp_ctx *ctx, char reason))
{
ctx->callback(ctx, LEJPCB_DESTRUCTED);
ctx->callback = callback;
@ -160,8 +148,7 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
n = ctx->wild[wildcard];
while (--len && n < ctx->ppos &&
(n == ctx->wild[wildcard] || ctx->path[n] != '.'))
while (--len && n < ctx->ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++];
*dest = '\0';
@ -199,6 +186,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
while (len--) {
c = *json++;
s = ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */
@ -423,26 +411,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
}
goto add_stack_level;
case ']':
/* 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;
goto array_end;
case 't': /* true */
ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
@ -476,7 +444,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto append_npos;
}
if (c == '.') {
if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
@ -614,10 +582,8 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
}
/* drop the path [n] bit */
if (ctx->sp) {
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
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)
@ -643,10 +609,8 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
}
/* pop */
ctx->sp--;
if (ctx->sp) {
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
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)
@ -667,7 +631,6 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
case LEJP_MP_ARRAY_END:
array_end:
ctx->path[ctx->ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */

232
lib/lejp.h Normal file
View file

@ -0,0 +1,232 @@
#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

@ -96,12 +96,6 @@ STORE_IN_ROM static const char * const set[] = {
"x-forwarded-for",
"connect ",
"head ",
"te:", /* http/2 wants it to reject it */
"replay-nonce:", /* ACME */
":protocol", /* defined in mcmanus-httpbis-h2-ws-02 */
"x-auth-token:",
"", /* not matchable */

796
lib/lextable.h Normal file
View file

@ -0,0 +1,796 @@
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x40, 0x00 /* (to 0x0040 state 1) */,
0x70 /* 'p' */, 0x42, 0x00 /* (to 0x0045 state 5) */,
0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0057 state 10) */,
0x68 /* 'h' */, 0x5D, 0x00 /* (to 0x0066 state 18) */,
0x63 /* 'c' */, 0x66, 0x00 /* (to 0x0072 state 23) */,
0x75 /* 'u' */, 0x87, 0x00 /* (to 0x0096 state 34) */,
0x73 /* 's' */, 0x9D, 0x00 /* (to 0x00AF state 48) */,
0x0D /* '.' */, 0xD6, 0x00 /* (to 0x00EB state 68) */,
0x61 /* 'a' */, 0x2E, 0x01 /* (to 0x0146 state 129) */,
0x69 /* 'i' */, 0x6D, 0x01 /* (to 0x0188 state 163) */,
0x64 /* 'd' */, 0x16, 0x02 /* (to 0x0234 state 265) */,
0x72 /* 'r' */, 0x1F, 0x02 /* (to 0x0240 state 270) */,
0x3A /* ':' */, 0x50, 0x02 /* (to 0x0274 state 299) */,
0x65 /* 'e' */, 0xDC, 0x02 /* (to 0x0303 state 409) */,
0x66 /* 'f' */, 0xF8, 0x02 /* (to 0x0322 state 425) */,
0x6C /* 'l' */, 0x1A, 0x03 /* (to 0x0347 state 458) */,
0x6D /* 'm' */, 0x3D, 0x03 /* (to 0x036D state 484) */,
0x74 /* 't' */, 0xAC, 0x03 /* (to 0x03DF state 578) */,
0x76 /* 'v' */, 0xC7, 0x03 /* (to 0x03FD state 606) */,
0x77 /* 'w' */, 0xD4, 0x03 /* (to 0x040D state 614) */,
0x78 /* 'x' */, 0xFB, 0x03 /* (to 0x0437 state 650) */,
0x08, /* fail */
/* pos 0040: 1 */ 0xE5 /* 'e' -> */,
/* pos 0041: 2 */ 0xF4 /* 't' -> */,
/* pos 0042: 3 */ 0xA0 /* ' ' -> */,
/* pos 0043: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
/* pos 0045: 5 */ 0x6F /* 'o' */, 0x0D, 0x00 /* (to 0x0052 state 6) */,
0x72 /* 'r' */, 0x92, 0x01 /* (to 0x01DA state 211) */,
0x61 /* 'a' */, 0xD4, 0x03 /* (to 0x041F state 631) */,
0x75 /* 'u' */, 0xD6, 0x03 /* (to 0x0424 state 635) */,
0x08, /* fail */
/* pos 0052: 6 */ 0xF3 /* 's' -> */,
/* pos 0053: 7 */ 0xF4 /* 't' -> */,
/* pos 0054: 8 */ 0xA0 /* ' ' -> */,
/* pos 0055: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
/* pos 0057: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x005E state 11) */,
0x72 /* 'r' */, 0x4E, 0x00 /* (to 0x00A8 state 42) */,
0x08, /* fail */
/* pos 005e: 11 */ 0xF4 /* 't' -> */,
/* pos 005f: 12 */ 0xE9 /* 'i' -> */,
/* pos 0060: 13 */ 0xEF /* 'o' -> */,
/* pos 0061: 14 */ 0xEE /* 'n' -> */,
/* pos 0062: 15 */ 0xF3 /* 's' -> */,
/* pos 0063: 16 */ 0xA0 /* ' ' -> */,
/* pos 0064: 17 */ 0x00, 0x02 /* - terminal marker 2 - */,
/* pos 0066: 18 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x006D state 19) */,
0x74 /* 't' */, 0xBC, 0x00 /* (to 0x0125 state 110) */,
0x08, /* fail */
/* pos 006d: 19 */ 0xF3 /* 's' -> */,
/* pos 006e: 20 */ 0xF4 /* 't' -> */,
/* pos 006f: 21 */ 0xBA /* ':' -> */,
/* pos 0070: 22 */ 0x00, 0x03 /* - terminal marker 3 - */,
/* pos 0072: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0079 state 24) */,
0x61 /* 'a' */, 0x72, 0x01 /* (to 0x01E7 state 217) */,
0x08, /* fail */
/* pos 0079: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0080 state 25) */,
0x6F /* 'o' */, 0x87, 0x01 /* (to 0x0203 state 243) */,
0x08, /* fail */
/* pos 0080: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0087 state 26) */,
0x74 /* 't' */, 0x86, 0x01 /* (to 0x0209 state 248) */,
0x08, /* fail */
/* pos 0087: 26 */ 0xE5 /* 'e' -> */,
/* pos 0088: 27 */ 0xE3 /* 'c' -> */,
/* pos 0089: 28 */ 0xF4 /* 't' -> */,
/* pos 008a: 29 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0091 state 30) */,
0x20 /* ' ' */, 0xCC, 0x03 /* (to 0x0459 state 675) */,
0x08, /* fail */
/* pos 0091: 30 */ 0xEF /* 'o' -> */,
/* pos 0092: 31 */ 0xEE /* 'n' -> */,
/* pos 0093: 32 */ 0xBA /* ':' -> */,
/* pos 0094: 33 */ 0x00, 0x04 /* - terminal marker 4 - */,
/* pos 0096: 34 */ 0x70 /* 'p' */, 0x0A, 0x00 /* (to 0x00A0 state 35) */,
0x73 /* 's' */, 0x59, 0x03 /* (to 0x03F2 state 596) */,
0x72 /* 'r' */, 0x91, 0x03 /* (to 0x042D state 642) */,
0x08, /* fail */
/* pos 00a0: 35 */ 0xE7 /* 'g' -> */,
/* pos 00a1: 36 */ 0xF2 /* 'r' -> */,
/* pos 00a2: 37 */ 0xE1 /* 'a' -> */,
/* pos 00a3: 38 */ 0xE4 /* 'd' -> */,
/* pos 00a4: 39 */ 0xE5 /* 'e' -> */,
/* pos 00a5: 40 */ 0xBA /* ':' -> */,
/* pos 00a6: 41 */ 0x00, 0x05 /* - terminal marker 5 - */,
/* pos 00a8: 42 */ 0xE9 /* 'i' -> */,
/* pos 00a9: 43 */ 0xE7 /* 'g' -> */,
/* pos 00aa: 44 */ 0xE9 /* 'i' -> */,
/* pos 00ab: 45 */ 0xEE /* 'n' -> */,
/* pos 00ac: 46 */ 0xBA /* ':' -> */,
/* pos 00ad: 47 */ 0x00, 0x06 /* - terminal marker 6 - */,
/* pos 00af: 48 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x00B6 state 49) */,
0x74 /* 't' */, 0x13, 0x03 /* (to 0x03C5 state 553) */,
0x08, /* fail */
/* pos 00b6: 49 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x00C0 state 50) */,
0x72 /* 'r' */, 0xFC, 0x02 /* (to 0x03B5 state 539) */,
0x74 /* 't' */, 0xFF, 0x02 /* (to 0x03BB state 544) */,
0x08, /* fail */
/* pos 00c0: 50 */ 0xAD /* '-' -> */,
/* pos 00c1: 51 */ 0xF7 /* 'w' -> */,
/* pos 00c2: 52 */ 0xE5 /* 'e' -> */,
/* pos 00c3: 53 */ 0xE2 /* 'b' -> */,
/* pos 00c4: 54 */ 0xF3 /* 's' -> */,
/* pos 00c5: 55 */ 0xEF /* 'o' -> */,
/* pos 00c6: 56 */ 0xE3 /* 'c' -> */,
/* pos 00c7: 57 */ 0xEB /* 'k' -> */,
/* pos 00c8: 58 */ 0xE5 /* 'e' -> */,
/* pos 00c9: 59 */ 0xF4 /* 't' -> */,
/* pos 00ca: 60 */ 0xAD /* '-' -> */,
/* pos 00cb: 61 */ 0x64 /* 'd' */, 0x19, 0x00 /* (to 0x00E4 state 62) */,
0x65 /* 'e' */, 0x20, 0x00 /* (to 0x00EE state 70) */,
0x6B /* 'k' */, 0x29, 0x00 /* (to 0x00FA state 81) */,
0x70 /* 'p' */, 0x38, 0x00 /* (to 0x010C state 88) */,
0x61 /* 'a' */, 0x3F, 0x00 /* (to 0x0116 state 97) */,
0x6E /* 'n' */, 0x44, 0x00 /* (to 0x011E state 104) */,
0x76 /* 'v' */, 0x86, 0x01 /* (to 0x0263 state 284) */,
0x6F /* 'o' */, 0x8C, 0x01 /* (to 0x026C state 292) */,
0x08, /* fail */
/* pos 00e4: 62 */ 0xF2 /* 'r' -> */,
/* pos 00e5: 63 */ 0xE1 /* 'a' -> */,
/* pos 00e6: 64 */ 0xE6 /* 'f' -> */,
/* pos 00e7: 65 */ 0xF4 /* 't' -> */,
/* pos 00e8: 66 */ 0xBA /* ':' -> */,
/* pos 00e9: 67 */ 0x00, 0x07 /* - terminal marker 7 - */,
/* pos 00eb: 68 */ 0x8A /* '.' -> */,
/* pos 00ec: 69 */ 0x00, 0x08 /* - terminal marker 8 - */,
/* pos 00ee: 70 */ 0xF8 /* 'x' -> */,
/* pos 00ef: 71 */ 0xF4 /* 't' -> */,
/* pos 00f0: 72 */ 0xE5 /* 'e' -> */,
/* pos 00f1: 73 */ 0xEE /* 'n' -> */,
/* pos 00f2: 74 */ 0xF3 /* 's' -> */,
/* pos 00f3: 75 */ 0xE9 /* 'i' -> */,
/* pos 00f4: 76 */ 0xEF /* 'o' -> */,
/* pos 00f5: 77 */ 0xEE /* 'n' -> */,
/* pos 00f6: 78 */ 0xF3 /* 's' -> */,
/* pos 00f7: 79 */ 0xBA /* ':' -> */,
/* pos 00f8: 80 */ 0x00, 0x09 /* - terminal marker 9 - */,
/* pos 00fa: 81 */ 0xE5 /* 'e' -> */,
/* pos 00fb: 82 */ 0xF9 /* 'y' -> */,
/* pos 00fc: 83 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0106 state 84) */,
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0109 state 86) */,
0x3A /* ':' */, 0x5F, 0x01 /* (to 0x0261 state 283) */,
0x08, /* fail */
/* pos 0106: 84 */ 0xBA /* ':' -> */,
/* pos 0107: 85 */ 0x00, 0x0A /* - terminal marker 10 - */,
/* pos 0109: 86 */ 0xBA /* ':' -> */,
/* pos 010a: 87 */ 0x00, 0x0B /* - terminal marker 11 - */,
/* pos 010c: 88 */ 0xF2 /* 'r' -> */,
/* pos 010d: 89 */ 0xEF /* 'o' -> */,
/* pos 010e: 90 */ 0xF4 /* 't' -> */,
/* pos 010f: 91 */ 0xEF /* 'o' -> */,
/* pos 0110: 92 */ 0xE3 /* 'c' -> */,
/* pos 0111: 93 */ 0xEF /* 'o' -> */,
/* pos 0112: 94 */ 0xEC /* 'l' -> */,
/* pos 0113: 95 */ 0xBA /* ':' -> */,
/* pos 0114: 96 */ 0x00, 0x0C /* - terminal marker 12 - */,
/* pos 0116: 97 */ 0xE3 /* 'c' -> */,
/* pos 0117: 98 */ 0xE3 /* 'c' -> */,
/* pos 0118: 99 */ 0xE5 /* 'e' -> */,
/* pos 0119: 100 */ 0xF0 /* 'p' -> */,
/* pos 011a: 101 */ 0xF4 /* 't' -> */,
/* pos 011b: 102 */ 0xBA /* ':' -> */,
/* pos 011c: 103 */ 0x00, 0x0D /* - terminal marker 13 - */,
/* pos 011e: 104 */ 0xEF /* 'o' -> */,
/* pos 011f: 105 */ 0xEE /* 'n' -> */,
/* pos 0120: 106 */ 0xE3 /* 'c' -> */,
/* pos 0121: 107 */ 0xE5 /* 'e' -> */,
/* pos 0122: 108 */ 0xBA /* ':' -> */,
/* pos 0123: 109 */ 0x00, 0x0E /* - terminal marker 14 - */,
/* pos 0125: 110 */ 0xF4 /* 't' -> */,
/* pos 0126: 111 */ 0xF0 /* 'p' -> */,
/* pos 0127: 112 */ 0x2F /* '/' */, 0x07, 0x00 /* (to 0x012E state 113) */,
0x32 /* '2' */, 0x10, 0x00 /* (to 0x013A state 118) */,
0x08, /* fail */
/* pos 012e: 113 */ 0xB1 /* '1' -> */,
/* pos 012f: 114 */ 0xAE /* '.' -> */,
/* pos 0130: 115 */ 0x31 /* '1' */, 0x07, 0x00 /* (to 0x0137 state 116) */,
0x30 /* '0' */, 0x15, 0x03 /* (to 0x0448 state 660) */,
0x08, /* fail */
/* pos 0137: 116 */ 0xA0 /* ' ' -> */,
/* pos 0138: 117 */ 0x00, 0x0F /* - terminal marker 15 - */,
/* pos 013a: 118 */ 0xAD /* '-' -> */,
/* pos 013b: 119 */ 0xF3 /* 's' -> */,
/* pos 013c: 120 */ 0xE5 /* 'e' -> */,
/* pos 013d: 121 */ 0xF4 /* 't' -> */,
/* pos 013e: 122 */ 0xF4 /* 't' -> */,
/* pos 013f: 123 */ 0xE9 /* 'i' -> */,
/* pos 0140: 124 */ 0xEE /* 'n' -> */,
/* pos 0141: 125 */ 0xE7 /* 'g' -> */,
/* pos 0142: 126 */ 0xF3 /* 's' -> */,
/* pos 0143: 127 */ 0xBA /* ':' -> */,
/* pos 0144: 128 */ 0x00, 0x10 /* - terminal marker 16 - */,
/* pos 0146: 129 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0153 state 130) */,
0x75 /* 'u' */, 0xAC, 0x00 /* (to 0x01F5 state 230) */,
0x67 /* 'g' */, 0x7D, 0x01 /* (to 0x02C9 state 358) */,
0x6C /* 'l' */, 0x7E, 0x01 /* (to 0x02CD state 361) */,
0x08, /* fail */
/* pos 0153: 130 */ 0xE3 /* 'c' -> */,
/* pos 0154: 131 */ 0xE5 /* 'e' -> */,
/* pos 0155: 132 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x015C state 133) */,
0x73 /* 's' */, 0x0E, 0x00 /* (to 0x0166 state 136) */,
0x08, /* fail */
/* pos 015c: 133 */ 0xF4 /* 't' -> */,
/* pos 015d: 134 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0164 state 135) */,
0x2D /* '-' */, 0x59, 0x00 /* (to 0x01B9 state 192) */,
0x08, /* fail */
/* pos 0164: 135 */ 0x00, 0x11 /* - terminal marker 17 - */,
/* pos 0166: 136 */ 0xF3 /* 's' -> */,
/* pos 0167: 137 */ 0xAD /* '-' -> */,
/* pos 0168: 138 */ 0xE3 /* 'c' -> */,
/* pos 0169: 139 */ 0xEF /* 'o' -> */,
/* pos 016a: 140 */ 0xEE /* 'n' -> */,
/* pos 016b: 141 */ 0xF4 /* 't' -> */,
/* pos 016c: 142 */ 0xF2 /* 'r' -> */,
/* pos 016d: 143 */ 0xEF /* 'o' -> */,
/* pos 016e: 144 */ 0xEC /* 'l' -> */,
/* pos 016f: 145 */ 0xAD /* '-' -> */,
/* pos 0170: 146 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0177 state 147) */,
0x61 /* 'a' */, 0x48, 0x01 /* (to 0x02BB state 345) */,
0x08, /* fail */
/* pos 0177: 147 */ 0xE5 /* 'e' -> */,
/* pos 0178: 148 */ 0xF1 /* 'q' -> */,
/* pos 0179: 149 */ 0xF5 /* 'u' -> */,
/* pos 017a: 150 */ 0xE5 /* 'e' -> */,
/* pos 017b: 151 */ 0xF3 /* 's' -> */,
/* pos 017c: 152 */ 0xF4 /* 't' -> */,
/* pos 017d: 153 */ 0xAD /* '-' -> */,
/* pos 017e: 154 */ 0xE8 /* 'h' -> */,
/* pos 017f: 155 */ 0xE5 /* 'e' -> */,
/* pos 0180: 156 */ 0xE1 /* 'a' -> */,
/* pos 0181: 157 */ 0xE4 /* 'd' -> */,
/* pos 0182: 158 */ 0xE5 /* 'e' -> */,
/* pos 0183: 159 */ 0xF2 /* 'r' -> */,
/* pos 0184: 160 */ 0xF3 /* 's' -> */,
/* pos 0185: 161 */ 0xBA /* ':' -> */,
/* pos 0186: 162 */ 0x00, 0x12 /* - terminal marker 18 - */,
/* pos 0188: 163 */ 0xE6 /* 'f' -> */,
/* pos 0189: 164 */ 0xAD /* '-' -> */,
/* pos 018a: 165 */ 0x6D /* 'm' */, 0x0D, 0x00 /* (to 0x0197 state 166) */,
0x6E /* 'n' */, 0x20, 0x00 /* (to 0x01AD state 181) */,
0x72 /* 'r' */, 0x9E, 0x01 /* (to 0x032E state 435) */,
0x75 /* 'u' */, 0xA2, 0x01 /* (to 0x0335 state 441) */,
0x08, /* fail */
/* pos 0197: 166 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x019E state 167) */,
0x61 /* 'a' */, 0x8E, 0x01 /* (to 0x0328 state 430) */,
0x08, /* fail */
/* pos 019e: 167 */ 0xE4 /* 'd' -> */,
/* pos 019f: 168 */ 0xE9 /* 'i' -> */,
/* pos 01a0: 169 */ 0xE6 /* 'f' -> */,
/* pos 01a1: 170 */ 0xE9 /* 'i' -> */,
/* pos 01a2: 171 */ 0xE5 /* 'e' -> */,
/* pos 01a3: 172 */ 0xE4 /* 'd' -> */,
/* pos 01a4: 173 */ 0xAD /* '-' -> */,
/* pos 01a5: 174 */ 0xF3 /* 's' -> */,
/* pos 01a6: 175 */ 0xE9 /* 'i' -> */,
/* pos 01a7: 176 */ 0xEE /* 'n' -> */,
/* pos 01a8: 177 */ 0xE3 /* 'c' -> */,
/* pos 01a9: 178 */ 0xE5 /* 'e' -> */,
/* pos 01aa: 179 */ 0xBA /* ':' -> */,
/* pos 01ab: 180 */ 0x00, 0x13 /* - terminal marker 19 - */,
/* pos 01ad: 181 */ 0xEF /* 'o' -> */,
/* pos 01ae: 182 */ 0xEE /* 'n' -> */,
/* pos 01af: 183 */ 0xE5 /* 'e' -> */,
/* pos 01b0: 184 */ 0xAD /* '-' -> */,
/* pos 01b1: 185 */ 0xED /* 'm' -> */,
/* pos 01b2: 186 */ 0xE1 /* 'a' -> */,
/* pos 01b3: 187 */ 0xF4 /* 't' -> */,
/* pos 01b4: 188 */ 0xE3 /* 'c' -> */,
/* pos 01b5: 189 */ 0xE8 /* 'h' -> */,
/* pos 01b6: 190 */ 0xBA /* ':' -> */,
/* pos 01b7: 191 */ 0x00, 0x14 /* - terminal marker 20 - */,
/* pos 01b9: 192 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x01C6 state 193) */,
0x6C /* 'l' */, 0x14, 0x00 /* (to 0x01D0 state 202) */,
0x63 /* 'c' */, 0xEB, 0x00 /* (to 0x02AA state 330) */,
0x72 /* 'r' */, 0xF1, 0x00 /* (to 0x02B3 state 338) */,
0x08, /* fail */
/* pos 01c6: 193 */ 0xEE /* 'n' -> */,
/* pos 01c7: 194 */ 0xE3 /* 'c' -> */,
/* pos 01c8: 195 */ 0xEF /* 'o' -> */,
/* pos 01c9: 196 */ 0xE4 /* 'd' -> */,
/* pos 01ca: 197 */ 0xE9 /* 'i' -> */,
/* pos 01cb: 198 */ 0xEE /* 'n' -> */,
/* pos 01cc: 199 */ 0xE7 /* 'g' -> */,
/* pos 01cd: 200 */ 0xBA /* ':' -> */,
/* pos 01ce: 201 */ 0x00, 0x15 /* - terminal marker 21 - */,
/* pos 01d0: 202 */ 0xE1 /* 'a' -> */,
/* pos 01d1: 203 */ 0xEE /* 'n' -> */,
/* pos 01d2: 204 */ 0xE7 /* 'g' -> */,
/* pos 01d3: 205 */ 0xF5 /* 'u' -> */,
/* pos 01d4: 206 */ 0xE1 /* 'a' -> */,
/* pos 01d5: 207 */ 0xE7 /* 'g' -> */,
/* pos 01d6: 208 */ 0xE5 /* 'e' -> */,
/* pos 01d7: 209 */ 0xBA /* ':' -> */,
/* pos 01d8: 210 */ 0x00, 0x16 /* - terminal marker 22 - */,
/* pos 01da: 211 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01E1 state 212) */,
0x6F /* 'o' */, 0x9E, 0x01 /* (to 0x037B state 497) */,
0x08, /* fail */
/* pos 01e1: 212 */ 0xE7 /* 'g' -> */,
/* pos 01e2: 213 */ 0xED /* 'm' -> */,
/* pos 01e3: 214 */ 0xE1 /* 'a' -> */,
/* pos 01e4: 215 */ 0xBA /* ':' -> */,
/* pos 01e5: 216 */ 0x00, 0x17 /* - terminal marker 23 - */,
/* pos 01e7: 217 */ 0xE3 /* 'c' -> */,
/* pos 01e8: 218 */ 0xE8 /* 'h' -> */,
/* pos 01e9: 219 */ 0xE5 /* 'e' -> */,
/* pos 01ea: 220 */ 0xAD /* '-' -> */,
/* pos 01eb: 221 */ 0xE3 /* 'c' -> */,
/* pos 01ec: 222 */ 0xEF /* 'o' -> */,
/* pos 01ed: 223 */ 0xEE /* 'n' -> */,
/* pos 01ee: 224 */ 0xF4 /* 't' -> */,
/* pos 01ef: 225 */ 0xF2 /* 'r' -> */,
/* pos 01f0: 226 */ 0xEF /* 'o' -> */,
/* pos 01f1: 227 */ 0xEC /* 'l' -> */,
/* pos 01f2: 228 */ 0xBA /* ':' -> */,
/* pos 01f3: 229 */ 0x00, 0x18 /* - terminal marker 24 - */,
/* pos 01f5: 230 */ 0xF4 /* 't' -> */,
/* pos 01f6: 231 */ 0xE8 /* 'h' -> */,
/* pos 01f7: 232 */ 0xEF /* 'o' -> */,
/* pos 01f8: 233 */ 0xF2 /* 'r' -> */,
/* pos 01f9: 234 */ 0xE9 /* 'i' -> */,
/* pos 01fa: 235 */ 0xFA /* 'z' -> */,
/* pos 01fb: 236 */ 0xE1 /* 'a' -> */,
/* pos 01fc: 237 */ 0xF4 /* 't' -> */,
/* pos 01fd: 238 */ 0xE9 /* 'i' -> */,
/* pos 01fe: 239 */ 0xEF /* 'o' -> */,
/* pos 01ff: 240 */ 0xEE /* 'n' -> */,
/* pos 0200: 241 */ 0xBA /* ':' -> */,
/* pos 0201: 242 */ 0x00, 0x19 /* - terminal marker 25 - */,
/* pos 0203: 243 */ 0xEB /* 'k' -> */,
/* pos 0204: 244 */ 0xE9 /* 'i' -> */,
/* pos 0205: 245 */ 0xE5 /* 'e' -> */,
/* pos 0206: 246 */ 0xBA /* ':' -> */,
/* pos 0207: 247 */ 0x00, 0x1A /* - terminal marker 26 - */,
/* pos 0209: 248 */ 0xE5 /* 'e' -> */,
/* pos 020a: 249 */ 0xEE /* 'n' -> */,
/* pos 020b: 250 */ 0xF4 /* 't' -> */,
/* pos 020c: 251 */ 0xAD /* '-' -> */,
/* pos 020d: 252 */ 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x021D state 253) */,
0x74 /* 't' */, 0x1E, 0x00 /* (to 0x022E state 260) */,
0x64 /* 'd' */, 0xC0, 0x00 /* (to 0x02D3 state 366) */,
0x65 /* 'e' */, 0xCA, 0x00 /* (to 0x02E0 state 378) */,
0x72 /* 'r' */, 0xE3, 0x00 /* (to 0x02FC state 403) */,
0x08, /* fail */
/* pos 021d: 253 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x0227 state 254) */,
0x61 /* 'a' */, 0xCA, 0x00 /* (to 0x02EA state 387) */,
0x6F /* 'o' */, 0xD0, 0x00 /* (to 0x02F3 state 395) */,
0x08, /* fail */
/* pos 0227: 254 */ 0xEE /* 'n' -> */,
/* pos 0228: 255 */ 0xE7 /* 'g' -> */,
/* pos 0229: 256 */ 0xF4 /* 't' -> */,
/* pos 022a: 257 */ 0xE8 /* 'h' -> */,
/* pos 022b: 258 */ 0xBA /* ':' -> */,
/* pos 022c: 259 */ 0x00, 0x1B /* - terminal marker 27 - */,
/* pos 022e: 260 */ 0xF9 /* 'y' -> */,
/* pos 022f: 261 */ 0xF0 /* 'p' -> */,
/* pos 0230: 262 */ 0xE5 /* 'e' -> */,
/* pos 0231: 263 */ 0xBA /* ':' -> */,
/* pos 0232: 264 */ 0x00, 0x1C /* - terminal marker 28 - */,
/* pos 0234: 265 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x023B state 266) */,
0x65 /* 'e' */, 0xF0, 0x01 /* (to 0x0427 state 637) */,
0x08, /* fail */
/* pos 023b: 266 */ 0xF4 /* 't' -> */,
/* pos 023c: 267 */ 0xE5 /* 'e' -> */,
/* pos 023d: 268 */ 0xBA /* ':' -> */,
/* pos 023e: 269 */ 0x00, 0x1D /* - terminal marker 29 - */,
/* pos 0240: 270 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0247 state 271) */,
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x024D state 276) */,
0x08, /* fail */
/* pos 0247: 271 */ 0xEE /* 'n' -> */,
/* pos 0248: 272 */ 0xE7 /* 'g' -> */,
/* pos 0249: 273 */ 0xE5 /* 'e' -> */,
/* pos 024a: 274 */ 0xBA /* ':' -> */,
/* pos 024b: 275 */ 0x00, 0x1E /* - terminal marker 30 - */,
/* pos 024d: 276 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0254 state 277) */,
0x74 /* 't' */, 0x5A, 0x01 /* (to 0x03AA state 529) */,
0x08, /* fail */
/* pos 0254: 277 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x025B state 278) */,
0x72 /* 'r' */, 0x4D, 0x01 /* (to 0x03A4 state 524) */,
0x08, /* fail */
/* pos 025b: 278 */ 0xF2 /* 'r' -> */,
/* pos 025c: 279 */ 0xE5 /* 'e' -> */,
/* pos 025d: 280 */ 0xF2 /* 'r' -> */,
/* pos 025e: 281 */ 0xBA /* ':' -> */,
/* pos 025f: 282 */ 0x00, 0x1F /* - terminal marker 31 - */,
/* pos 0261: 283 */ 0x00, 0x20 /* - terminal marker 32 - */,
/* pos 0263: 284 */ 0xE5 /* 'e' -> */,
/* pos 0264: 285 */ 0xF2 /* 'r' -> */,
/* pos 0265: 286 */ 0xF3 /* 's' -> */,
/* pos 0266: 287 */ 0xE9 /* 'i' -> */,
/* pos 0267: 288 */ 0xEF /* 'o' -> */,
/* pos 0268: 289 */ 0xEE /* 'n' -> */,
/* pos 0269: 290 */ 0xBA /* ':' -> */,
/* pos 026a: 291 */ 0x00, 0x21 /* - terminal marker 33 - */,
/* pos 026c: 292 */ 0xF2 /* 'r' -> */,
/* pos 026d: 293 */ 0xE9 /* 'i' -> */,
/* pos 026e: 294 */ 0xE7 /* 'g' -> */,
/* pos 026f: 295 */ 0xE9 /* 'i' -> */,
/* pos 0270: 296 */ 0xEE /* 'n' -> */,
/* pos 0271: 297 */ 0xBA /* ':' -> */,
/* pos 0272: 298 */ 0x00, 0x22 /* - terminal marker 34 - */,
/* pos 0274: 299 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0281 state 300) */,
0x6D /* 'm' */, 0x14, 0x00 /* (to 0x028B state 309) */,
0x70 /* 'p' */, 0x18, 0x00 /* (to 0x0292 state 315) */,
0x73 /* 's' */, 0x1A, 0x00 /* (to 0x0297 state 319) */,
0x08, /* fail */
/* pos 0281: 300 */ 0xF5 /* 'u' -> */,
/* pos 0282: 301 */ 0xF4 /* 't' -> */,
/* pos 0283: 302 */ 0xE8 /* 'h' -> */,
/* pos 0284: 303 */ 0xEF /* 'o' -> */,
/* pos 0285: 304 */ 0xF2 /* 'r' -> */,
/* pos 0286: 305 */ 0xE9 /* 'i' -> */,
/* pos 0287: 306 */ 0xF4 /* 't' -> */,
/* pos 0288: 307 */ 0xF9 /* 'y' -> */,
/* pos 0289: 308 */ 0x00, 0x23 /* - terminal marker 35 - */,
/* pos 028b: 309 */ 0xE5 /* 'e' -> */,
/* pos 028c: 310 */ 0xF4 /* 't' -> */,
/* pos 028d: 311 */ 0xE8 /* 'h' -> */,
/* pos 028e: 312 */ 0xEF /* 'o' -> */,
/* pos 028f: 313 */ 0xE4 /* 'd' -> */,
/* pos 0290: 314 */ 0x00, 0x24 /* - terminal marker 36 - */,
/* pos 0292: 315 */ 0xE1 /* 'a' -> */,
/* pos 0293: 316 */ 0xF4 /* 't' -> */,
/* pos 0294: 317 */ 0xE8 /* 'h' -> */,
/* pos 0295: 318 */ 0x00, 0x25 /* - terminal marker 37 - */,
/* pos 0297: 319 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x029E state 320) */,
0x74 /* 't' */, 0x0A, 0x00 /* (to 0x02A4 state 325) */,
0x08, /* fail */
/* pos 029e: 320 */ 0xE8 /* 'h' -> */,
/* pos 029f: 321 */ 0xE5 /* 'e' -> */,
/* pos 02a0: 322 */ 0xED /* 'm' -> */,
/* pos 02a1: 323 */ 0xE5 /* 'e' -> */,
/* pos 02a2: 324 */ 0x00, 0x26 /* - terminal marker 38 - */,
/* pos 02a4: 325 */ 0xE1 /* 'a' -> */,
/* pos 02a5: 326 */ 0xF4 /* 't' -> */,
/* pos 02a6: 327 */ 0xF5 /* 'u' -> */,
/* pos 02a7: 328 */ 0xF3 /* 's' -> */,
/* pos 02a8: 329 */ 0x00, 0x27 /* - terminal marker 39 - */,
/* pos 02aa: 330 */ 0xE8 /* 'h' -> */,
/* pos 02ab: 331 */ 0xE1 /* 'a' -> */,
/* pos 02ac: 332 */ 0xF2 /* 'r' -> */,
/* pos 02ad: 333 */ 0xF3 /* 's' -> */,
/* pos 02ae: 334 */ 0xE5 /* 'e' -> */,
/* pos 02af: 335 */ 0xF4 /* 't' -> */,
/* pos 02b0: 336 */ 0xBA /* ':' -> */,
/* pos 02b1: 337 */ 0x00, 0x28 /* - terminal marker 40 - */,
/* pos 02b3: 338 */ 0xE1 /* 'a' -> */,
/* pos 02b4: 339 */ 0xEE /* 'n' -> */,
/* pos 02b5: 340 */ 0xE7 /* 'g' -> */,
/* pos 02b6: 341 */ 0xE5 /* 'e' -> */,
/* pos 02b7: 342 */ 0xF3 /* 's' -> */,
/* pos 02b8: 343 */ 0xBA /* ':' -> */,
/* pos 02b9: 344 */ 0x00, 0x29 /* - terminal marker 41 - */,
/* pos 02bb: 345 */ 0xEC /* 'l' -> */,
/* pos 02bc: 346 */ 0xEC /* 'l' -> */,
/* pos 02bd: 347 */ 0xEF /* 'o' -> */,
/* pos 02be: 348 */ 0xF7 /* 'w' -> */,
/* pos 02bf: 349 */ 0xAD /* '-' -> */,
/* pos 02c0: 350 */ 0xEF /* 'o' -> */,
/* pos 02c1: 351 */ 0xF2 /* 'r' -> */,
/* pos 02c2: 352 */ 0xE9 /* 'i' -> */,
/* pos 02c3: 353 */ 0xE7 /* 'g' -> */,
/* pos 02c4: 354 */ 0xE9 /* 'i' -> */,
/* pos 02c5: 355 */ 0xEE /* 'n' -> */,
/* pos 02c6: 356 */ 0xBA /* ':' -> */,
/* pos 02c7: 357 */ 0x00, 0x2A /* - terminal marker 42 - */,
/* pos 02c9: 358 */ 0xE5 /* 'e' -> */,
/* pos 02ca: 359 */ 0xBA /* ':' -> */,
/* pos 02cb: 360 */ 0x00, 0x2B /* - terminal marker 43 - */,
/* pos 02cd: 361 */ 0xEC /* 'l' -> */,
/* pos 02ce: 362 */ 0xEF /* 'o' -> */,
/* pos 02cf: 363 */ 0xF7 /* 'w' -> */,
/* pos 02d0: 364 */ 0xBA /* ':' -> */,
/* pos 02d1: 365 */ 0x00, 0x2C /* - terminal marker 44 - */,
/* pos 02d3: 366 */ 0xE9 /* 'i' -> */,
/* pos 02d4: 367 */ 0xF3 /* 's' -> */,
/* pos 02d5: 368 */ 0xF0 /* 'p' -> */,
/* pos 02d6: 369 */ 0xEF /* 'o' -> */,
/* pos 02d7: 370 */ 0xF3 /* 's' -> */,
/* pos 02d8: 371 */ 0xE9 /* 'i' -> */,
/* pos 02d9: 372 */ 0xF4 /* 't' -> */,
/* pos 02da: 373 */ 0xE9 /* 'i' -> */,
/* pos 02db: 374 */ 0xEF /* 'o' -> */,
/* pos 02dc: 375 */ 0xEE /* 'n' -> */,
/* pos 02dd: 376 */ 0xBA /* ':' -> */,
/* pos 02de: 377 */ 0x00, 0x2D /* - terminal marker 45 - */,
/* pos 02e0: 378 */ 0xEE /* 'n' -> */,
/* pos 02e1: 379 */ 0xE3 /* 'c' -> */,
/* pos 02e2: 380 */ 0xEF /* 'o' -> */,
/* pos 02e3: 381 */ 0xE4 /* 'd' -> */,
/* pos 02e4: 382 */ 0xE9 /* 'i' -> */,
/* pos 02e5: 383 */ 0xEE /* 'n' -> */,
/* pos 02e6: 384 */ 0xE7 /* 'g' -> */,
/* pos 02e7: 385 */ 0xBA /* ':' -> */,
/* pos 02e8: 386 */ 0x00, 0x2E /* - terminal marker 46 - */,
/* pos 02ea: 387 */ 0xEE /* 'n' -> */,
/* pos 02eb: 388 */ 0xE7 /* 'g' -> */,
/* pos 02ec: 389 */ 0xF5 /* 'u' -> */,
/* pos 02ed: 390 */ 0xE1 /* 'a' -> */,
/* pos 02ee: 391 */ 0xE7 /* 'g' -> */,
/* pos 02ef: 392 */ 0xE5 /* 'e' -> */,
/* pos 02f0: 393 */ 0xBA /* ':' -> */,
/* pos 02f1: 394 */ 0x00, 0x2F /* - terminal marker 47 - */,
/* pos 02f3: 395 */ 0xE3 /* 'c' -> */,
/* pos 02f4: 396 */ 0xE1 /* 'a' -> */,
/* pos 02f5: 397 */ 0xF4 /* 't' -> */,
/* pos 02f6: 398 */ 0xE9 /* 'i' -> */,
/* pos 02f7: 399 */ 0xEF /* 'o' -> */,
/* pos 02f8: 400 */ 0xEE /* 'n' -> */,
/* pos 02f9: 401 */ 0xBA /* ':' -> */,
/* pos 02fa: 402 */ 0x00, 0x30 /* - terminal marker 48 - */,
/* pos 02fc: 403 */ 0xE1 /* 'a' -> */,
/* pos 02fd: 404 */ 0xEE /* 'n' -> */,
/* pos 02fe: 405 */ 0xE7 /* 'g' -> */,
/* pos 02ff: 406 */ 0xE5 /* 'e' -> */,
/* pos 0300: 407 */ 0xBA /* ':' -> */,
/* pos 0301: 408 */ 0x00, 0x31 /* - terminal marker 49 - */,
/* pos 0303: 409 */ 0x74 /* 't' */, 0x07, 0x00 /* (to 0x030A state 410) */,
0x78 /* 'x' */, 0x09, 0x00 /* (to 0x030F state 414) */,
0x08, /* fail */
/* pos 030a: 410 */ 0xE1 /* 'a' -> */,
/* pos 030b: 411 */ 0xE7 /* 'g' -> */,
/* pos 030c: 412 */ 0xBA /* ':' -> */,
/* pos 030d: 413 */ 0x00, 0x32 /* - terminal marker 50 - */,
/* pos 030f: 414 */ 0xF0 /* 'p' -> */,
/* pos 0310: 415 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0317 state 416) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x031C state 420) */,
0x08, /* fail */
/* pos 0317: 416 */ 0xE3 /* 'c' -> */,
/* pos 0318: 417 */ 0xF4 /* 't' -> */,
/* pos 0319: 418 */ 0xBA /* ':' -> */,
/* pos 031a: 419 */ 0x00, 0x33 /* - terminal marker 51 - */,
/* pos 031c: 420 */ 0xF2 /* 'r' -> */,
/* pos 031d: 421 */ 0xE5 /* 'e' -> */,
/* pos 031e: 422 */ 0xF3 /* 's' -> */,
/* pos 031f: 423 */ 0xBA /* ':' -> */,
/* pos 0320: 424 */ 0x00, 0x34 /* - terminal marker 52 - */,
/* pos 0322: 425 */ 0xF2 /* 'r' -> */,
/* pos 0323: 426 */ 0xEF /* 'o' -> */,
/* pos 0324: 427 */ 0xED /* 'm' -> */,
/* pos 0325: 428 */ 0xBA /* ':' -> */,
/* pos 0326: 429 */ 0x00, 0x35 /* - terminal marker 53 - */,
/* pos 0328: 430 */ 0xF4 /* 't' -> */,
/* pos 0329: 431 */ 0xE3 /* 'c' -> */,
/* pos 032a: 432 */ 0xE8 /* 'h' -> */,
/* pos 032b: 433 */ 0xBA /* ':' -> */,
/* pos 032c: 434 */ 0x00, 0x36 /* - terminal marker 54 - */,
/* pos 032e: 435 */ 0xE1 /* 'a' -> */,
/* pos 032f: 436 */ 0xEE /* 'n' -> */,
/* pos 0330: 437 */ 0xE7 /* 'g' -> */,
/* pos 0331: 438 */ 0xE5 /* 'e' -> */,
/* pos 0332: 439 */ 0xBA /* ':' -> */,
/* pos 0333: 440 */ 0x00, 0x37 /* - terminal marker 55 - */,
/* pos 0335: 441 */ 0xEE /* 'n' -> */,
/* pos 0336: 442 */ 0xED /* 'm' -> */,
/* pos 0337: 443 */ 0xEF /* 'o' -> */,
/* pos 0338: 444 */ 0xE4 /* 'd' -> */,
/* pos 0339: 445 */ 0xE9 /* 'i' -> */,
/* pos 033a: 446 */ 0xE6 /* 'f' -> */,
/* pos 033b: 447 */ 0xE9 /* 'i' -> */,
/* pos 033c: 448 */ 0xE5 /* 'e' -> */,
/* pos 033d: 449 */ 0xE4 /* 'd' -> */,
/* pos 033e: 450 */ 0xAD /* '-' -> */,
/* pos 033f: 451 */ 0xF3 /* 's' -> */,
/* pos 0340: 452 */ 0xE9 /* 'i' -> */,
/* pos 0341: 453 */ 0xEE /* 'n' -> */,
/* pos 0342: 454 */ 0xE3 /* 'c' -> */,
/* pos 0343: 455 */ 0xE5 /* 'e' -> */,
/* pos 0344: 456 */ 0xBA /* ':' -> */,
/* pos 0345: 457 */ 0x00, 0x38 /* - terminal marker 56 - */,
/* pos 0347: 458 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0351 state 459) */,
0x69 /* 'i' */, 0x15, 0x00 /* (to 0x035F state 472) */,
0x6F /* 'o' */, 0x17, 0x00 /* (to 0x0364 state 476) */,
0x08, /* fail */
/* pos 0351: 459 */ 0xF3 /* 's' -> */,
/* pos 0352: 460 */ 0xF4 /* 't' -> */,
/* pos 0353: 461 */ 0xAD /* '-' -> */,
/* pos 0354: 462 */ 0xED /* 'm' -> */,
/* pos 0355: 463 */ 0xEF /* 'o' -> */,
/* pos 0356: 464 */ 0xE4 /* 'd' -> */,
/* pos 0357: 465 */ 0xE9 /* 'i' -> */,
/* pos 0358: 466 */ 0xE6 /* 'f' -> */,
/* pos 0359: 467 */ 0xE9 /* 'i' -> */,
/* pos 035a: 468 */ 0xE5 /* 'e' -> */,
/* pos 035b: 469 */ 0xE4 /* 'd' -> */,
/* pos 035c: 470 */ 0xBA /* ':' -> */,
/* pos 035d: 471 */ 0x00, 0x39 /* - terminal marker 57 - */,
/* pos 035f: 472 */ 0xEE /* 'n' -> */,
/* pos 0360: 473 */ 0xEB /* 'k' -> */,
/* pos 0361: 474 */ 0xBA /* ':' -> */,
/* pos 0362: 475 */ 0x00, 0x3A /* - terminal marker 58 - */,
/* pos 0364: 476 */ 0xE3 /* 'c' -> */,
/* pos 0365: 477 */ 0xE1 /* 'a' -> */,
/* pos 0366: 478 */ 0xF4 /* 't' -> */,
/* pos 0367: 479 */ 0xE9 /* 'i' -> */,
/* pos 0368: 480 */ 0xEF /* 'o' -> */,
/* pos 0369: 481 */ 0xEE /* 'n' -> */,
/* pos 036a: 482 */ 0xBA /* ':' -> */,
/* pos 036b: 483 */ 0x00, 0x3B /* - terminal marker 59 - */,
/* pos 036d: 484 */ 0xE1 /* 'a' -> */,
/* pos 036e: 485 */ 0xF8 /* 'x' -> */,
/* pos 036f: 486 */ 0xAD /* '-' -> */,
/* pos 0370: 487 */ 0xE6 /* 'f' -> */,
/* pos 0371: 488 */ 0xEF /* 'o' -> */,
/* pos 0372: 489 */ 0xF2 /* 'r' -> */,
/* pos 0373: 490 */ 0xF7 /* 'w' -> */,
/* pos 0374: 491 */ 0xE1 /* 'a' -> */,
/* pos 0375: 492 */ 0xF2 /* 'r' -> */,
/* pos 0376: 493 */ 0xE4 /* 'd' -> */,
/* pos 0377: 494 */ 0xF3 /* 's' -> */,
/* pos 0378: 495 */ 0xBA /* ':' -> */,
/* pos 0379: 496 */ 0x00, 0x3C /* - terminal marker 60 - */,
/* pos 037b: 497 */ 0xF8 /* 'x' -> */,
/* pos 037c: 498 */ 0xF9 /* 'y' -> */,
/* pos 037d: 499 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0384 state 500) */,
0x20 /* ' ' */, 0xB5, 0x00 /* (to 0x0435 state 649) */,
0x08, /* fail */
/* pos 0384: 500 */ 0xE1 /* 'a' -> */,
/* pos 0385: 501 */ 0xF5 /* 'u' -> */,
/* pos 0386: 502 */ 0xF4 /* 't' -> */,
/* pos 0387: 503 */ 0xE8 /* 'h' -> */,
/* pos 0388: 504 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x038F state 505) */,
0x6F /* 'o' */, 0x0E, 0x00 /* (to 0x0399 state 514) */,
0x08, /* fail */
/* pos 038f: 505 */ 0xEE /* 'n' -> */,
/* pos 0390: 506 */ 0xF4 /* 't' -> */,
/* pos 0391: 507 */ 0xE9 /* 'i' -> */,
/* pos 0392: 508 */ 0xE3 /* 'c' -> */,
/* pos 0393: 509 */ 0xE1 /* 'a' -> */,
/* pos 0394: 510 */ 0xF4 /* 't' -> */,
/* pos 0395: 511 */ 0xE5 /* 'e' -> */,
/* pos 0396: 512 */ 0xBA /* ':' -> */,
/* pos 0397: 513 */ 0x00, 0x3D /* - terminal marker 61 - */,
/* pos 0399: 514 */ 0xF2 /* 'r' -> */,
/* pos 039a: 515 */ 0xE9 /* 'i' -> */,
/* pos 039b: 516 */ 0xFA /* 'z' -> */,
/* pos 039c: 517 */ 0xE1 /* 'a' -> */,
/* pos 039d: 518 */ 0xF4 /* 't' -> */,
/* pos 039e: 519 */ 0xE9 /* 'i' -> */,
/* pos 039f: 520 */ 0xEF /* 'o' -> */,
/* pos 03a0: 521 */ 0xEE /* 'n' -> */,
/* pos 03a1: 522 */ 0xBA /* ':' -> */,
/* pos 03a2: 523 */ 0x00, 0x3E /* - terminal marker 62 - */,
/* pos 03a4: 524 */ 0xE5 /* 'e' -> */,
/* pos 03a5: 525 */ 0xF3 /* 's' -> */,
/* pos 03a6: 526 */ 0xE8 /* 'h' -> */,
/* pos 03a7: 527 */ 0xBA /* ':' -> */,
/* pos 03a8: 528 */ 0x00, 0x3F /* - terminal marker 63 - */,
/* pos 03aa: 529 */ 0xF2 /* 'r' -> */,
/* pos 03ab: 530 */ 0xF9 /* 'y' -> */,
/* pos 03ac: 531 */ 0xAD /* '-' -> */,
/* pos 03ad: 532 */ 0xE1 /* 'a' -> */,
/* pos 03ae: 533 */ 0xE6 /* 'f' -> */,
/* pos 03af: 534 */ 0xF4 /* 't' -> */,
/* pos 03b0: 535 */ 0xE5 /* 'e' -> */,
/* pos 03b1: 536 */ 0xF2 /* 'r' -> */,
/* pos 03b2: 537 */ 0xBA /* ':' -> */,
/* pos 03b3: 538 */ 0x00, 0x40 /* - terminal marker 64 - */,
/* pos 03b5: 539 */ 0xF6 /* 'v' -> */,
/* pos 03b6: 540 */ 0xE5 /* 'e' -> */,
/* pos 03b7: 541 */ 0xF2 /* 'r' -> */,
/* pos 03b8: 542 */ 0xBA /* ':' -> */,
/* pos 03b9: 543 */ 0x00, 0x41 /* - terminal marker 65 - */,
/* pos 03bb: 544 */ 0xAD /* '-' -> */,
/* pos 03bc: 545 */ 0xE3 /* 'c' -> */,
/* pos 03bd: 546 */ 0xEF /* 'o' -> */,
/* pos 03be: 547 */ 0xEF /* 'o' -> */,
/* pos 03bf: 548 */ 0xEB /* 'k' -> */,
/* pos 03c0: 549 */ 0xE9 /* 'i' -> */,
/* pos 03c1: 550 */ 0xE5 /* 'e' -> */,
/* pos 03c2: 551 */ 0xBA /* ':' -> */,
/* pos 03c3: 552 */ 0x00, 0x42 /* - terminal marker 66 - */,
/* pos 03c5: 553 */ 0xF2 /* 'r' -> */,
/* pos 03c6: 554 */ 0xE9 /* 'i' -> */,
/* pos 03c7: 555 */ 0xE3 /* 'c' -> */,
/* pos 03c8: 556 */ 0xF4 /* 't' -> */,
/* pos 03c9: 557 */ 0xAD /* '-' -> */,
/* pos 03ca: 558 */ 0xF4 /* 't' -> */,
/* pos 03cb: 559 */ 0xF2 /* 'r' -> */,
/* pos 03cc: 560 */ 0xE1 /* 'a' -> */,
/* pos 03cd: 561 */ 0xEE /* 'n' -> */,
/* pos 03ce: 562 */ 0xF3 /* 's' -> */,
/* pos 03cf: 563 */ 0xF0 /* 'p' -> */,
/* pos 03d0: 564 */ 0xEF /* 'o' -> */,
/* pos 03d1: 565 */ 0xF2 /* 'r' -> */,
/* pos 03d2: 566 */ 0xF4 /* 't' -> */,
/* pos 03d3: 567 */ 0xAD /* '-' -> */,
/* pos 03d4: 568 */ 0xF3 /* 's' -> */,
/* pos 03d5: 569 */ 0xE5 /* 'e' -> */,
/* pos 03d6: 570 */ 0xE3 /* 'c' -> */,
/* pos 03d7: 571 */ 0xF5 /* 'u' -> */,
/* pos 03d8: 572 */ 0xF2 /* 'r' -> */,
/* pos 03d9: 573 */ 0xE9 /* 'i' -> */,
/* pos 03da: 574 */ 0xF4 /* 't' -> */,
/* pos 03db: 575 */ 0xF9 /* 'y' -> */,
/* pos 03dc: 576 */ 0xBA /* ':' -> */,
/* pos 03dd: 577 */ 0x00, 0x43 /* - terminal marker 67 - */,
/* pos 03df: 578 */ 0xF2 /* 'r' -> */,
/* pos 03e0: 579 */ 0xE1 /* 'a' -> */,
/* pos 03e1: 580 */ 0xEE /* 'n' -> */,
/* pos 03e2: 581 */ 0xF3 /* 's' -> */,
/* pos 03e3: 582 */ 0xE6 /* 'f' -> */,
/* pos 03e4: 583 */ 0xE5 /* 'e' -> */,
/* pos 03e5: 584 */ 0xF2 /* 'r' -> */,
/* pos 03e6: 585 */ 0xAD /* '-' -> */,
/* pos 03e7: 586 */ 0xE5 /* 'e' -> */,
/* pos 03e8: 587 */ 0xEE /* 'n' -> */,
/* pos 03e9: 588 */ 0xE3 /* 'c' -> */,
/* pos 03ea: 589 */ 0xEF /* 'o' -> */,
/* pos 03eb: 590 */ 0xE4 /* 'd' -> */,
/* pos 03ec: 591 */ 0xE9 /* 'i' -> */,
/* pos 03ed: 592 */ 0xEE /* 'n' -> */,
/* pos 03ee: 593 */ 0xE7 /* 'g' -> */,
/* pos 03ef: 594 */ 0xBA /* ':' -> */,
/* pos 03f0: 595 */ 0x00, 0x44 /* - terminal marker 68 - */,
/* pos 03f2: 596 */ 0xE5 /* 'e' -> */,
/* pos 03f3: 597 */ 0xF2 /* 'r' -> */,
/* pos 03f4: 598 */ 0xAD /* '-' -> */,
/* pos 03f5: 599 */ 0xE1 /* 'a' -> */,
/* pos 03f6: 600 */ 0xE7 /* 'g' -> */,
/* pos 03f7: 601 */ 0xE5 /* 'e' -> */,
/* pos 03f8: 602 */ 0xEE /* 'n' -> */,
/* pos 03f9: 603 */ 0xF4 /* 't' -> */,
/* pos 03fa: 604 */ 0xBA /* ':' -> */,
/* pos 03fb: 605 */ 0x00, 0x45 /* - terminal marker 69 - */,
/* pos 03fd: 606 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0404 state 607) */,
0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0409 state 611) */,
0x08, /* fail */
/* pos 0404: 607 */ 0xF2 /* 'r' -> */,
/* pos 0405: 608 */ 0xF9 /* 'y' -> */,
/* pos 0406: 609 */ 0xBA /* ':' -> */,
/* pos 0407: 610 */ 0x00, 0x46 /* - terminal marker 70 - */,
/* pos 0409: 611 */ 0xE1 /* 'a' -> */,
/* pos 040a: 612 */ 0xBA /* ':' -> */,
/* pos 040b: 613 */ 0x00, 0x47 /* - terminal marker 71 - */,
/* pos 040d: 614 */ 0xF7 /* 'w' -> */,
/* pos 040e: 615 */ 0xF7 /* 'w' -> */,
/* pos 040f: 616 */ 0xAD /* '-' -> */,
/* pos 0410: 617 */ 0xE1 /* 'a' -> */,
/* pos 0411: 618 */ 0xF5 /* 'u' -> */,
/* pos 0412: 619 */ 0xF4 /* 't' -> */,
/* pos 0413: 620 */ 0xE8 /* 'h' -> */,
/* pos 0414: 621 */ 0xE5 /* 'e' -> */,
/* pos 0415: 622 */ 0xEE /* 'n' -> */,
/* pos 0416: 623 */ 0xF4 /* 't' -> */,
/* pos 0417: 624 */ 0xE9 /* 'i' -> */,
/* pos 0418: 625 */ 0xE3 /* 'c' -> */,
/* pos 0419: 626 */ 0xE1 /* 'a' -> */,
/* pos 041a: 627 */ 0xF4 /* 't' -> */,
/* pos 041b: 628 */ 0xE5 /* 'e' -> */,
/* pos 041c: 629 */ 0xBA /* ':' -> */,
/* pos 041d: 630 */ 0x00, 0x48 /* - terminal marker 72 - */,
/* pos 041f: 631 */ 0xF4 /* 't' -> */,
/* pos 0420: 632 */ 0xE3 /* 'c' -> */,
/* pos 0421: 633 */ 0xE8 /* 'h' -> */,
/* pos 0422: 634 */ 0x00, 0x49 /* - terminal marker 73 - */,
/* pos 0424: 635 */ 0xF4 /* 't' -> */,
/* pos 0425: 636 */ 0x00, 0x4A /* - terminal marker 74 - */,
/* pos 0427: 637 */ 0xEC /* 'l' -> */,
/* pos 0428: 638 */ 0xE5 /* 'e' -> */,
/* pos 0429: 639 */ 0xF4 /* 't' -> */,
/* pos 042a: 640 */ 0xE5 /* 'e' -> */,
/* pos 042b: 641 */ 0x00, 0x4B /* - terminal marker 75 - */,
/* pos 042d: 642 */ 0xE9 /* 'i' -> */,
/* pos 042e: 643 */ 0xAD /* '-' -> */,
/* pos 042f: 644 */ 0xE1 /* 'a' -> */,
/* pos 0430: 645 */ 0xF2 /* 'r' -> */,
/* pos 0431: 646 */ 0xE7 /* 'g' -> */,
/* pos 0432: 647 */ 0xF3 /* 's' -> */,
/* pos 0433: 648 */ 0x00, 0x4C /* - terminal marker 76 - */,
/* pos 0435: 649 */ 0x00, 0x4D /* - terminal marker 77 - */,
/* pos 0437: 650 */ 0xAD /* '-' -> */,
/* pos 0438: 651 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x043F state 652) */,
0x66 /* 'f' */, 0x10, 0x00 /* (to 0x044B state 662) */,
0x08, /* fail */
/* pos 043f: 652 */ 0xE5 /* 'e' -> */,
/* pos 0440: 653 */ 0xE1 /* 'a' -> */,
/* pos 0441: 654 */ 0xEC /* 'l' -> */,
/* pos 0442: 655 */ 0xAD /* '-' -> */,
/* pos 0443: 656 */ 0xE9 /* 'i' -> */,
/* pos 0444: 657 */ 0xF0 /* 'p' -> */,
/* pos 0445: 658 */ 0xBA /* ':' -> */,
/* pos 0446: 659 */ 0x00, 0x4E /* - terminal marker 78 - */,
/* pos 0448: 660 */ 0xA0 /* ' ' -> */,
/* pos 0449: 661 */ 0x00, 0x4F /* - terminal marker 79 - */,
/* pos 044b: 662 */ 0xEF /* 'o' -> */,
/* pos 044c: 663 */ 0xF2 /* 'r' -> */,
/* pos 044d: 664 */ 0xF7 /* 'w' -> */,
/* pos 044e: 665 */ 0xE1 /* 'a' -> */,
/* pos 044f: 666 */ 0xF2 /* 'r' -> */,
/* pos 0450: 667 */ 0xE4 /* 'd' -> */,
/* pos 0451: 668 */ 0xE5 /* 'e' -> */,
/* pos 0452: 669 */ 0xE4 /* 'd' -> */,
/* pos 0453: 670 */ 0xAD /* '-' -> */,
/* pos 0454: 671 */ 0xE6 /* 'f' -> */,
/* pos 0455: 672 */ 0xEF /* 'o' -> */,
/* pos 0456: 673 */ 0xF2 /* 'r' -> */,
/* pos 0457: 674 */ 0x00, 0x50 /* - terminal marker 80 - */,
/* pos 0459: 675 */ 0x00, 0x51 /* - terminal marker 81 - */,
/* total size 1115 bytes */

233
lib/libev.c Normal file
View file

@ -0,0 +1,233 @@
/*
* 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_libev(struct lws_context_creation_info *info)
{
if (lws_check_opt(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)
{
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;
if (revents & EV_ERROR)
return;
eventfd.fd = watcher->fd;
eventfd.events = 0;
eventfd.revents = EV_NONE;
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_ev_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)
{
context->use_ev_sigint = use_ev_sigint;
if (cb)
context->lws_ev_sigint_cb = cb;
else
context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
return 0;
}
LWS_VISIBLE int
lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi)
{
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;
if (!loop)
loop = ev_loop_new(0);
else
context->pt[tsi].ev_loop_foreign = 1;
context->pt[tsi].io_loop_ev = loop;
/*
* Initialize the accept w_accept with all the listening sockets
* 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);
/* Register the signal watcher unless the user says not to */
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);
}
backend = ev_backend(loop);
switch (backend) {
case EVBACKEND_SELECT:
backend_name = "select";
break;
case EVBACKEND_POLL:
backend_name = "poll";
break;
case EVBACKEND_EPOLL:
backend_name = "epoll";
break;
case EVBACKEND_KQUEUE:
backend_name = "kqueue";
break;
case EVBACKEND_DEVPOLL:
backend_name = "/dev/poll";
break;
case EVBACKEND_PORT:
backend_name = "Solaris 10 \"port\"";
break;
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)
{
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;
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);
}
LWS_VISIBLE void
lws_libev_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 (!LWS_LIBEV_ENABLED(context))
return;
if (!pt->io_loop_ev)
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)
ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher);
if (flags & LWS_EV_READ)
ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher);
} else {
if (flags & LWS_EV_WRITE)
ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher);
if (flags & LWS_EV_READ)
ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher);
}
}
LWS_VISIBLE int
lws_libev_init_fd_table(struct lws_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;
}
return 1;
}
LWS_VISIBLE void
lws_libev_run(const struct lws_context *context, int tsi)
{
if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context))
ev_run(context->pt[tsi].io_loop_ev, 0);
}

249
lib/libevent.c Normal file
View file

@ -0,0 +1,249 @@
/*
* 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);
}

723
lib/libuv.c Normal file
View file

@ -0,0 +1,723 @@
/*
* 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

3687
lib/libwebsockets.c Executable 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

700
lib/lws-plat-esp8266.c Normal file
View file

@ -0,0 +1,700 @@
#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,32 +1,9 @@
#include "core/private.h"
#include "private-libwebsockets.h"
/*
* included from libwebsockets.c for OPTEE builds
*/
int
lws_plat_socket_offset(void)
{
return 0;
}
int
lws_plat_pipe_create(struct lws *wsi)
{
return 1;
}
int
lws_plat_pipe_signal(struct lws *wsi)
{
return 1;
}
void
lws_plat_pipe_close(struct lws *wsi)
{
}
void TEE_GenerateRandom(void *randomBuffer, uint32_t randomBufferLen);
unsigned long long time_in_microseconds(void)
@ -45,19 +22,6 @@ lws_get_random(struct lws_context *context, void *buf, int len)
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{
struct lws *wsi_eff = wsi;
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi);
#endif
/* the fact we checked implies we avoided back-to-back writes */
wsi_eff->could_have_pending = 0;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len)
return 1;
#if 0
struct lws_pollfd fds;
@ -88,6 +52,32 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
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)
{
@ -121,9 +111,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
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;
context->service_tid_detected = 1;
}
context->service_tid = context->service_tid_detected;
/*
* is there anybody with pending stuff that needs service forcing?
@ -137,20 +126,19 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
/* yes... come back again quickly */
timeout_ms = 0;
}
#if 1
n = poll(pt->fds, pt->fds_count, timeout_ms);
m = 0;
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
m = pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
if (/*!pt->ws.rx_draining_ext_list && */!m && !n) { /* nothing to do */
#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)
@ -164,7 +152,7 @@ faked_service:
c = n;
/* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count && c; n++) {
for (n = 0; n < pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
continue;
@ -206,7 +194,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
@ -281,8 +269,8 @@ lws_plat_inet_pton(int af, const char *src, void *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)
_lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
const char *filename, lws_fop_flags_t *flags)
{
return NULL;
}
@ -318,11 +306,11 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
const struct lws_context_creation_info *info)
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, "lws_lookup");
context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
@ -339,28 +327,3 @@ lws_plat_init(struct lws_context *context,
return 0;
}
LWS_VISIBLE int
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
int len)
{
return 1;
}
LWS_VISIBLE int
lws_plat_write_file(const char *filename, void *buf, int len)
{
return 1;
}
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
return -1;
}
LWS_VISIBLE int
lws_plat_recommended_rsa_bits(void)
{
return 4096;
}

View file

@ -1,26 +1,4 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-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
*/
#define _GNU_SOURCE
#include "core/private.h"
#include "private-libwebsockets.h"
#include <pwd.h>
#include <grp.h>
@ -30,60 +8,14 @@
#endif
#include <dirent.h>
int
lws_plat_socket_offset(void)
{
return 0;
}
int
lws_plat_pipe_create(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
#if defined(LWS_HAVE_PIPE2)
return pipe2(pt->dummy_pipe_fds, O_NONBLOCK);
#else
return pipe(pt->dummy_pipe_fds);
#endif
}
int
lws_plat_pipe_signal(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char buf = 0;
int n;
n = write(pt->dummy_pipe_fds[1], &buf, 1);
return n != 1;
}
void
lws_plat_pipe_close(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
if (pt->dummy_pipe_fds[0] && pt->dummy_pipe_fds[0] != -1)
close(pt->dummy_pipe_fds[0]);
if (pt->dummy_pipe_fds[1] && pt->dummy_pipe_fds[1] != -1)
close(pt->dummy_pipe_fds[1]);
pt->dummy_pipe_fds[0] = pt->dummy_pipe_fds[1] = -1;
}
#ifdef __QNX__
# include "netinet/tcp_var.h"
# define TCP_KEEPINTVL TCPCTL_KEEPINTVL
# define TCP_KEEPIDLE TCPCTL_KEEPIDLE
# define TCP_KEEPCNT TCPCTL_KEEPCNT
#endif
/*
* included from libwebsockets.c for unix builds
*/
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
}
@ -98,20 +30,12 @@ LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{
struct lws_pollfd fds;
struct lws *wsi_eff = wsi;
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi);
#endif
/* the fact we checked implies we avoided back-to-back writes */
wsi_eff->could_have_pending = 0;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len)
if (wsi->trunc_len)
return 1;
fds.fd = wsi_eff->desc.sockfd;
fds.fd = wsi->desc.sockfd;
fds.events = POLLOUT;
fds.revents = 0;
@ -132,6 +56,29 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
return poll(fd, 1, 0);
}
LWS_VISIBLE void
lws_cancel_service_pt(struct lws *wsi)
{
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");
}
LWS_VISIBLE void
lws_cancel_service(struct lws_context *context)
{
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++;
}
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
int syslog_level = LOG_DEBUG;
@ -156,10 +103,9 @@ LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
LWS_VISIBLE LWS_EXTERN int
_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
{
volatile struct lws_foreign_thread_pollfd *ftp, *next;
volatile struct lws_context_per_thread *vpt;
struct lws_context_per_thread *pt;
int n = -1, m, c;
char buf;
/* stay dead once we are dead */
@ -167,15 +113,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 1;
pt = &context->pt[tsi];
vpt = (volatile struct lws_context_per_thread *)pt;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_SERVICE_ENTRY, 1);
if (timeout_ms < 0)
goto faked_service;
if (context->event_loop_ops->run_pt)
context->event_loop_ops->run_pt(context, tsi);
lws_libev_run(context, tsi);
lws_libuv_run(context, tsi);
lws_libevent_run(context, tsi);
if (!context->service_tid_detected) {
struct lws _lws;
@ -186,9 +132,8 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
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;
context->service_tid_detected = 1;
}
context->service_tid = context->service_tid_detected;
/*
* is there anybody with pending stuff that needs service forcing?
@ -202,73 +147,15 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
if (timeout_ms) {
lws_pt_lock(pt, __func__);
/* don't stay in poll wait longer than next hr timeout */
lws_usec_t t = __lws_hrtimer_service(pt);
if ((lws_usec_t)timeout_ms * 1000 > t)
timeout_ms = t / 1000;
lws_pt_unlock(pt);
}
vpt->inside_poll = 1;
lws_memory_barrier();
n = poll(pt->fds, pt->fds_count, timeout_ms);
vpt->inside_poll = 0;
lws_memory_barrier();
/* Collision will be rare and brief. Just spin until it completes */
while (vpt->foreign_spinlock)
;
/*
* At this point we are not inside a foreign thread pollfd change,
* and we have marked ourselves as outside the poll() wait. So we
* are the only guys that can modify the lws_foreign_thread_pollfd
* list on the pt. Drain the list and apply the changes to the
* affected pollfds in the correct order.
*/
lws_pt_lock(pt, __func__);
ftp = vpt->foreign_pfd_list;
//lwsl_notice("cleared list %p\n", ftp);
while (ftp) {
struct lws *wsi;
struct lws_pollfd *pfd;
next = ftp->next;
pfd = &vpt->fds[ftp->fd_index];
if (lws_socket_is_valid(pfd->fd)) {
wsi = wsi_from_fd(context, pfd->fd);
if (wsi)
__lws_change_pollfd(wsi, ftp->_and, ftp->_or);
}
lws_free((void *)ftp);
ftp = next;
}
vpt->foreign_pfd_list = NULL;
lws_memory_barrier();
/* we have come out of a poll wait... check the hrtimer list */
__lws_hrtimer_service(pt);
lws_pt_unlock(pt);
m = 0;
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
m |= !!pt->ws.rx_draining_ext_list;
#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
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
m |= pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
if (!m && !n) { /* nothing to do */
lws_service_fd_tsi(context, NULL, tsi);
lws_service_do_ripe_rxflow(pt);
return 0;
}
@ -285,12 +172,18 @@ faked_service:
c = n;
/* any socket with events to service? */
for (n = 0; n < (int)pt->fds_count && c; n++) {
for (n = 0; n < pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
continue;
c--;
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;
}
m = lws_service_fd_tsi(context, &pt->fds[n], tsi);
if (m < 0)
return -1;
@ -299,8 +192,6 @@ faked_service:
n--;
}
lws_service_do_ripe_rxflow(pt);
return 0;
}
@ -325,8 +216,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__) || \
defined(__HAIKU__)
defined(__OpenBSD__)
struct protoent *tcp_proto;
#endif
@ -340,8 +230,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \
defined(__HAIKU__)
defined(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun)
/*
* didn't find a way to set these per-socket, need to
@ -349,14 +238,6 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
*/
#else
/* set the keepalive conditions we want on it too */
#if defined(LWS_HAVE_TCP_USER_TIMEOUT)
optval = 1000 * (vhost->ka_time +
(vhost->ka_interval * vhost->ka_probes));
if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
(const void *)&optval, optlen) < 0)
return 1;
#endif
optval = vhost->ka_time;
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
@ -387,14 +268,13 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
/* Disable Nagle */
optval = 1;
#if defined (__sun) || defined(__QNX__)
#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__) && \
!defined(__HAIKU__)
!defined(__OpenBSD__)
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#else
@ -412,7 +292,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
static void
_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
{
cap_t caps;
@ -429,17 +309,17 @@ _lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
#endif
LWS_VISIBLE void
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
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 && info->gid != -1)
if (info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
if (info->uid && info->uid != -1) {
if (info->uid != -1) {
struct passwd *p = getpwuid(info->uid);
if (p) {
@ -469,7 +349,7 @@ lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
#ifdef LWS_WITH_PLUGINS
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
/* libuv.c implements these in a cross-platform way */
@ -537,15 +417,15 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
goto skip;
}
plugin = lws_malloc(sizeof(*plugin), "plugin");
plugin = lws_malloc(sizeof(*plugin));
if (!plugin) {
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
lws_strncpy(plugin->name, namelist[i]->d_name,
sizeof(plugin->name));
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;
@ -617,6 +497,7 @@ static void
sigabrt_handler(int x)
{
printf("%s\n", __func__);
//*(char *)0 = 0;
}
#endif
@ -627,6 +508,8 @@ lws_plat_context_early_init(void)
signal(SIGPIPE, SIG_IGN);
#endif
// signal(SIGABRT, sigabrt_handler);
return 0;
}
@ -638,6 +521,9 @@ lws_plat_context_early_destroy(struct lws_context *context)
LWS_VISIBLE void
lws_plat_context_late_destroy(struct lws_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);
@ -646,6 +532,13 @@ lws_plat_context_late_destroy(struct lws_context *context)
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)
@ -658,11 +551,11 @@ LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
int rc = LWS_ITOSA_NOT_EXIST;
int rc = -1;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
#endif
@ -671,21 +564,14 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
if (!ifc->ifa_addr)
continue;
lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
if (strcmp(ifc->ifa_name, ifname))
continue;
switch (ifc->ifa_addr->sa_family) {
#if defined(AF_PACKET)
case AF_PACKET:
/* interface exists but is not usable */
rc = LWS_ITOSA_NOT_USABLE;
continue;
#endif
case AF_INET:
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
if (ipv6) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
@ -701,7 +587,7 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
(struct sockaddr_in *)ifc->ifa_addr,
sizeof(struct sockaddr_in));
break;
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
case AF_INET6:
memcpy(&addr6->sin6_addr,
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
@ -711,20 +597,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
default:
continue;
}
rc = LWS_ITOSA_USABLE;
rc = 0;
}
freeifaddrs(ifr);
if (rc) {
if (rc == -1) {
/* check if bind to IP address */
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = LWS_ITOSA_USABLE;
rc = 0;
else
#endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
rc = LWS_ITOSA_USABLE;
rc = 0;
}
return rc;
@ -735,8 +621,9 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (context->event_loop_ops->io)
context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
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;
}
@ -747,9 +634,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
if (context->event_loop_ops->io)
context->event_loop_ops->io(wsi,
LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
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--;
}
@ -830,8 +717,7 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
lws_fileofs_t r;
if (offset > 0 &&
offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
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)
@ -885,22 +771,24 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
return 0;
}
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
const struct lws_context_creation_info *info)
struct lws_context_creation_info *info)
{
int fd;
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, "lws_lookup");
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_info(" mem: platform fd map: %5lu bytes\n",
lwsl_notice(" mem: platform fd map: %5lu bytes\n",
(unsigned long)(sizeof(struct lws *) * context->max_fds));
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
@ -911,6 +799,26 @@ lws_plat_init(struct lws_context *context,
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);
@ -918,52 +826,3 @@ lws_plat_init(struct lws_context *context,
return 0;
}
LWS_VISIBLE int
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
int len)
{
int n;
n = write(fd, buf, len);
fsync(fd);
lseek(fd, 0, SEEK_SET);
return n != len;
}
LWS_VISIBLE int
lws_plat_write_file(const char *filename, void *buf, int len)
{
int m, fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
return 1;
m = write(fd, buf, len);
close(fd);
return m != len;
}
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
int n, fd = open(filename, O_RDONLY);
if (fd == -1)
return -1;
n = read(fd, buf, len);
close(fd);
return n;
}
LWS_VISIBLE int
lws_plat_recommended_rsa_bits(void)
{
return 4096;
}

View file

@ -1,34 +1,7 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include "core/private.h"
int
lws_plat_socket_offset(void)
{
return 0;
}
int
lws_plat_pipe_create(struct lws *wsi)
{
return 1;
}
int
lws_plat_pipe_signal(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
WSASetEvent(pt->events[0]); /* trigger the cancel event */
return 0;
}
void
lws_plat_pipe_close(struct lws *wsi)
{
}
#include "private-libwebsockets.h"
unsigned long long
time_in_microseconds()
@ -46,10 +19,9 @@ time_in_microseconds()
#endif
/*
* As per Windows documentation for FILETIME, copy the resulting
* FILETIME structure to a ULARGE_INTEGER structure using memcpy
* (using memcpy instead of direct assignment can prevent alignment
* faults on 64-bit Windows).
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
* prevent alignment faults on 64-bit Windows).
*/
memcpy(&datetime, &filetime, sizeof(datetime));
@ -109,7 +81,7 @@ delete_from_fd(struct lws_context *context, lws_sockfd_type fd)
if (context->fd_hashtable[h].wsi[n]->desc.sockfd == 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--;
@ -122,8 +94,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 lws_get_random(struct lws_context *context,
void *buf, int len)
{
int n;
char *p = (char *)buf;
@ -134,25 +106,16 @@ lws_get_random(struct lws_context *context, void *buf, int len)
return n;
}
LWS_VISIBLE int
lws_send_pipe_choked(struct lws *wsi)
{ struct lws *wsi_eff = wsi;
#if defined(LWS_WITH_HTTP2)
wsi_eff = lws_get_network_wsi(wsi);
#endif
/* the fact we checked implies we avoided back-to-back writes */
wsi_eff->could_have_pending = 0;
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
{
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi_eff->trunc_len)
if (wsi->trunc_len)
return 1;
return (int)wsi_eff->sock_send_blocking;
return (int)wsi->sock_send_blocking;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct lws_pollfd *fd)
LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
{
fd_set readfds;
struct timeval tv = { 0, 0 };
@ -162,11 +125,29 @@ lws_poll_listen_fd(struct lws_pollfd *fd)
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
}
LWS_VISIBLE void
lwsl_emit_syslog(int level, const char *line)
lws_cancel_service(struct lws_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]);
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
lwsl_emit_stderr(level, line);
}
@ -183,7 +164,7 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
int n, m;
/* stay dead once we are dead */
if (context == NULL || !context->vhost_list)
if (context == NULL)
return 1;
pt = &context->pt[tsi];
@ -197,12 +178,12 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
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;
context->service_tid_detected = 1;
}
context->service_tid = context->service_tid_detected;
if (timeout_ms < 0) {
if (lws_service_flag_pending(context, tsi)) {
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)
@ -219,9 +200,6 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
return 0;
}
if (context->event_loop_ops->run_pt)
context->event_loop_ops->run_pt(context, tsi);
for (i = 0; i < pt->fds_count; ++i) {
pfd = &pt->fds[i];
@ -241,9 +219,6 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
if (n)
i--;
/*
* any wsi has truncated, force him signalled
*/
if (wsi->trunc_len)
WSASetEvent(pt->events[0]);
}
@ -260,48 +235,32 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
if (timeout_ms) {
lws_usec_t t;
lws_pt_lock(pt, __func__);
/* don't stay in poll wait longer than next hr timeout */
t = __lws_hrtimer_service(pt);
if ((lws_usec_t)timeout_ms * 1000 > t)
timeout_ms = (int)(t / 1000);
lws_pt_unlock(pt);
}
ev = WSAWaitForMultipleEvents(1, pt->events, FALSE, timeout_ms, FALSE);
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
if (ev == WSA_WAIT_EVENT_0) {
unsigned int eIdx, err;
unsigned int eIdx;
WSAResetEvent(pt->events[0]);
if (pt->context->tls_ops &&
pt->context->tls_ops->fake_POLLIN_for_buffered)
pt->context->tls_ops->fake_POLLIN_for_buffered(pt);
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);
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;
err = networkevents.iErrorCode[FD_CONNECT_BIT];
if ((networkevents.lNetworkEvents & FD_CONNECT) &&
err && err != LWS_EALREADY &&
err != LWS_EINPROGRESS && err != LWS_EWOULDBLOCK &&
err != WSAEINVAL) {
lwsl_debug("Unable to connect errno=%d\n", err);
pfd->revents |= LWS_POLLHUP;
}
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);
@ -310,19 +269,21 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
}
/* if something closed, retry this slot */
if (pfd->revents & LWS_POLLHUP)
--eIdx;
--eIdx;
if (pfd->revents)
if( pfd->revents != 0 ) {
lws_service_fd_tsi(context, pfd, tsi);
}
}
}
context->service_tid = 0;
if (ev == WSA_WAIT_TIMEOUT)
if (ev == WSA_WAIT_TIMEOUT) {
lws_service_fd(context, NULL);
return 0;
}
return 0;;
}
LWS_VISIBLE int
@ -348,7 +309,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
/* 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;
@ -356,7 +317,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
alive.keepaliveinterval = vhost->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL))
NULL, 0, &dwBytesRet, NULL, NULL))
return 1;
}
@ -382,7 +343,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
@ -440,12 +401,12 @@ LWS_VISIBLE LWS_EXTERN int
lws_interface_to_sa(int ipv6,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
#ifdef LWS_WITH_IPV6
#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 LWS_ITOSA_USABLE;
return 0;
}
}
#endif
@ -459,11 +420,11 @@ lws_interface_to_sa(int ipv6,
}
if (address == INADDR_NONE)
return LWS_ITOSA_NOT_EXIST;
return -1;
addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
addr->sin_addr.s_addr = (lws_intptr_t)address;
return LWS_ITOSA_USABLE;
return 0;
}
LWS_VISIBLE void
@ -538,7 +499,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
DWORD bufferlen = cnt;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_ntop");
buffer = lws_malloc(bufferlen * 2);
if (!buffer) {
lwsl_err("Out of memory\n");
return NULL;
@ -552,7 +513,7 @@ lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
ok = TRUE;
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
@ -581,10 +542,10 @@ LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
WCHAR *buffer;
DWORD bufferlen = (int)strlen(src) + 1;
DWORD bufferlen = strlen(src) + 1;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2, "inet_pton");
buffer = lws_malloc(bufferlen * 2);
if (!buffer) {
lwsl_err("Out of memory\n");
return -1;
@ -606,7 +567,7 @@ lws_plat_inet_pton(int af, const char *src, void *dst)
ok = TRUE;
memcpy(dst, &dstaddr.sin_addr, sizeof(dstaddr.sin_addr));
}
#ifdef LWS_WITH_IPV6
#ifdef LWS_USE_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 dstaddr;
int dstaddrlen = sizeof(dstaddr);
@ -731,14 +692,14 @@ _lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
const struct lws_context_creation_info *info)
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, "win hashtable");
lws_zalloc(sizeof(struct lws*) * context->max_fds);
if (!context->fd_hashtable[i].wsi)
return -1;
@ -746,7 +707,7 @@ lws_plat_init(struct lws_context *context,
while (n--) {
pt->events = lws_malloc(sizeof(WSAEVENT) *
(context->fd_limit_per_thread + 1), "event table");
(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);
@ -754,7 +715,7 @@ lws_plat_init(struct lws_context *context,
}
pt->fds_count = 0;
pt->events[0] = WSACreateEvent(); /* the cancel event */
pt->events[0] = WSACreateEvent();
pt++;
}
@ -782,50 +743,3 @@ int fork(void)
exit(0);
}
LWS_VISIBLE int
lws_plat_write_cert(struct lws_vhost *vhost, int is_key, int fd, void *buf,
int len)
{
int n;
n = write(fd, buf, len);
lseek(fd, 0, SEEK_SET);
return n != len;
}
LWS_VISIBLE int
lws_plat_write_file(const char *filename, void *buf, int len)
{
int m, fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
return -1;
m = write(fd, buf, len);
close(fd);
return m != len;
}
LWS_VISIBLE int
lws_plat_read_file(const char *filename, void *buf, int len)
{
int n, fd = open(filename, O_RDONLY);
if (fd == -1)
return -1;
n = read(fd, buf, len);
close(fd);
return n;
}
LWS_VISIBLE int
lws_plat_recommended_rsa_bits(void)
{
return 4096;
}

View file

@ -1,323 +0,0 @@
/*
* libwebsockets - JSON Web Key support
*
* 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 "core/private.h"
#include <fcntl.h>
#include <unistd.h>
static const char * const jwk_tok[] = {
"e", "n", "d", "p", "q", "dp", "dq", "qi", "kty", "k",
};
static int
_lws_jwk_set_element(struct lws_genrsa_element *e, char *in, int len)
{
int dec_size = ((len * 3) / 4) + 4, n;
e->buf = lws_malloc(dec_size, "jwk");
if (!e->buf)
return -1;
n = lws_b64_decode_string_len(in, len, (char *)e->buf, dec_size - 1);
if (n < 0)
return -1;
e->len = n;
return 0;
}
struct cb_lws_jwk {
struct lws_jwk *s;
char *b64;
int b64max;
int pos;
};
static signed char
cb_jwk(struct lejp_ctx *ctx, char reason)
{
struct cb_lws_jwk *cbs = (struct cb_lws_jwk *)ctx->user;
struct lws_jwk *s = cbs->s;
int idx;
if (reason == LEJPCB_VAL_STR_START)
cbs->pos = 0;
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
case JWK_KTY:
lws_strncpy(s->keytype, ctx->buf, sizeof(s->keytype));
if (!strcmp(ctx->buf, "oct")) {
break;
}
if (!strcmp(ctx->buf, "RSA")) {
break;
}
return -1;
case JWK_KEY:
// if (strcmp(s->keytype, "oct"))
// return -1;
idx = JWK_KEY_E;
goto read_element1;
case JWK_KEY_N:
case JWK_KEY_E:
case JWK_KEY_D:
case JWK_KEY_P:
case JWK_KEY_Q:
case JWK_KEY_DP:
case JWK_KEY_DQ:
case JWK_KEY_QI:
idx = ctx->path_match - 1;
goto read_element;
}
return 0;
read_element:
/* kty is no longer first in lex order */
// if (strcmp(s->keytype, "RSA"))
// return -1;
read_element1:
if (cbs->pos + ctx->npos >= cbs->b64max)
return -1;
memcpy(cbs->b64 + cbs->pos, ctx->buf, ctx->npos);
cbs->pos += ctx->npos;
if (reason == LEJPCB_VAL_STR_CHUNK)
return 0;
if (_lws_jwk_set_element(&s->el.e[idx], cbs->b64, cbs->pos) < 0) {
lws_jwk_destroy_genrsa_elements(&s->el);
return -1;
}
return 0;
}
LWS_VISIBLE int
lws_jwk_import(struct lws_jwk *s, const char *in, size_t len)
{
struct lejp_ctx jctx;
struct cb_lws_jwk cbs;
const int b64max = (((8192 / 8) * 4) / 3) + 1; /* enough for 8K key */
char b64[b64max];
int m;
memset(s, 0, sizeof(*s));
cbs.s = s;
cbs.b64 = b64;
cbs.b64max = b64max;
cbs.pos = 0;
lejp_construct(&jctx, cb_jwk, &cbs, jwk_tok, ARRAY_SIZE(jwk_tok));
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)in, len);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("%s: parse got %d\n", __func__, m);
return -1;
}
return 0;
}
LWS_VISIBLE void
lws_jwk_destroy(struct lws_jwk *s)
{
lws_jwk_destroy_genrsa_elements(&s->el);
}
LWS_VISIBLE int
lws_jwk_export(struct lws_jwk *s, int private, char *p, size_t len)
{
char *start = p, *end = &p[len - 1];
int n, m, limit = LWS_COUNT_RSA_ELEMENTS;
/* RFC7638 lexicographic order requires
* RSA: e -> kty -> n
* oct: k -> kty
*/
p += lws_snprintf(p, end - p, "{");
if (!strcmp(s->keytype, "oct")) {
if (!s->el.e[JWK_KEY_E].buf)
return -1;
p += lws_snprintf(p, end - p, "\"k\":\"");
n = lws_jws_base64_enc((const char *)s->el.e[JWK_KEY_E].buf,
s->el.e[JWK_KEY_E].len, p,
end - p - 4);
if (n < 0) {
lwsl_notice("%s: enc failed\n", __func__);
return -1;
}
p += n;
p += lws_snprintf(p, end - p, "\",\"kty\":\"%s\"}", s->keytype);
return p - start;
}
if (!strcmp(s->keytype, "RSA")) {
if (!s->el.e[JWK_KEY_E].buf ||
!s->el.e[JWK_KEY_N].buf ||
(private && (!s->el.e[JWK_KEY_D].buf ||
!s->el.e[JWK_KEY_P].buf ||
!s->el.e[JWK_KEY_Q].buf))
) {
lwsl_notice("%s: not enough elements filled\n",
__func__);
return -1;
}
if (!private)
limit = JWK_KEY_N + 1;
for (n = 0; n < limit; n++) {
if (!s->el.e[n].buf)
continue;
lwsl_info("%d: len %d\n", n, s->el.e[n].len);
if (n)
p += lws_snprintf(p, end - p, ",");
p += lws_snprintf(p, end - p, "\"%s\":\"", jwk_tok[n]);
m = lws_jws_base64_enc((const char *)s->el.e[n].buf,
s->el.e[n].len, p,
end - p - 4);
if (m < 0) {
lwsl_notice("%s: enc fail inlen %d outlen %d\n",
__func__, (int)s->el.e[n].len,
lws_ptr_diff(end, p) - 4);
return -1;
}
p += m;
*p++ = '\"';
if (!n) /* RFC7638 lexicographic order */
p += lws_snprintf(p, end - p, ",\"kty\":\"%s\"",
s->keytype);
}
p += lws_snprintf(p, end - p, "}");
return p - start;
}
lwsl_err("%s: unknown key type %s\n", __func__, s->keytype);
return -1;
}
LWS_VISIBLE int
lws_jwk_rfc7638_fingerprint(struct lws_jwk *s, char *digest32)
{
struct lws_genhash_ctx hash_ctx;
int tmpsize = 2536, n;
char *tmp;
tmp = lws_malloc(tmpsize, "rfc7638 tmp");
n = lws_jwk_export(s, 0, tmp, tmpsize);
if (n < 0)
goto bail;
if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256))
goto bail;
if (lws_genhash_update(&hash_ctx, tmp, n)) {
lws_genhash_destroy(&hash_ctx, NULL);
goto bail;
}
lws_free(tmp);
if (lws_genhash_destroy(&hash_ctx, digest32))
return -1;
return 0;
bail:
lws_free(tmp);
return -1;
}
LWS_VISIBLE int
lws_jwk_load(struct lws_jwk *s, const char *filename)
{
int buflen = 4096;
char *buf = lws_malloc(buflen, "jwk-load");
int n;
if (!buf)
return -1;
n = lws_plat_read_file(filename, buf, buflen);
if (n < 0)
goto bail;
n = lws_jwk_import(s, buf, n);
lws_free(buf);
return n;
bail:
lws_free(buf);
return -1;
}
LWS_VISIBLE int
lws_jwk_save(struct lws_jwk *s, const char *filename)
{
int buflen = 4096;
char *buf = lws_malloc(buflen, "jwk-save");
int n, m;
if (!buf)
return -1;
n = lws_jwk_export(s, 1, buf, buflen);
if (n < 0)
goto bail;
m = lws_plat_write_file(filename, buf, n);
lws_free(buf);
if (m)
return -1;
return 0;
bail:
lws_free(buf);
return -1;
}

View file

@ -1,642 +0,0 @@
/*
* libwebsockets - JSON Web Signature support
*
* 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 "core/private.h"
/*
* JSON Web Signature is defined in RFC7515
*
* https://tools.ietf.org/html/rfc7515
*
* It's basically a way to wrap some JSON with a JSON "header" describing the
* crypto, and a signature, all in a BASE64 wrapper with elided terminating '='.
*
* The signature stays with the content, it serves a different purpose than eg
* a TLS tunnel to transfer it.
*
* RFC7518 (JSON Web Algorithms) says for the "alg" names
*
* | HS256 | HMAC using SHA-256 | Required |
* | HS384 | HMAC using SHA-384 | Optional |
* | HS512 | HMAC using SHA-512 | Optional |
* | RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
* | RS384 | RSASSA-PKCS1-v1_5 using | Optional |
* | | SHA-384 | |
* | RS512 | RSASSA-PKCS1-v1_5 using | Optional |
* | | SHA-512 | |
* | ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
* | ES384 | ECDSA using P-384 and SHA-384 | Optional |
* | ES512 | ECDSA using P-521 and SHA-512 | Optional |
*
* Boulder (FOSS ACME provider) supports RS256, ES256, ES384 and ES512
* currently. The "Recommended+" just means it is recommended but will likely
* be "very recommended" soon.
*
* We support HS256/384/512 for symmetric crypto, but the choice for the
* asymmetric crypto isn't as easy to make.
*
* Normally you'd choose the EC option but these are defined to use the
* "NIST curves" (RFC7518 3.4) which are believed to be insecure.
*
* https://safecurves.cr.yp.to/
*
* For that reason we implement RS256/384/512 for asymmetric.
*/
#if defined(LWS_WITH_SELFTESTS)
static const char
*test1 = "{\"typ\":\"JWT\",\r\n \"alg\":\"HS256\"}",
*test1_enc = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9",
*test2 = "{\"iss\":\"joe\",\r\n \"exp\":1300819380,\r\n"
" \"http://example.com/is_root\":true}",
*test2_enc = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ"
"ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
*key_jwk = "{\"kty\":\"oct\",\r\n"
" \"k\":\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQ"
"Lr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\"}",
*hash_enc = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
/* the key from worked example in RFC7515 A-1, as a JWK */
*rfc7515_rsa_key =
"{\"kty\":\"RSA\","
" \"n\":\"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx"
"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs"
"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH"
"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV"
"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8"
"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ\","
"\"e\":\"AQAB\","
"\"d\":\"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I"
"jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0"
"BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn"
"439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT"
"CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh"
"BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ\","
"\"p\":\"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdi"
"YrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPG"
"BY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc\","
"\"q\":\"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxa"
"ewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA"
"-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc\","
"\"dp\":\"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3Q"
"CLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb"
"34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0\","
"\"dq\":\"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa"
"7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-ky"
"NlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU\","
"\"qi\":\"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2o"
"y26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLU"
"W0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U\""
"}",
*rfc7515_rsa_a1 = /* the signed worked example in RFC7515 A-1 */
"eyJhbGciOiJSUzI1NiJ9"
".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt"
"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
".cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7"
"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4"
"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K"
"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv"
"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB"
"p0igcN_IoypGlUPQGe77Rw";
#endif
LWS_VISIBLE int
lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max)
{
int n;
n = lws_b64_encode_string_url(in, in_len, out, out_max - 1);
if (n < 0)
return n; /* too large for output buffer */
/* trim the terminal = */
while (n && out[n - 1] == '=')
n--;
out[n] = '\0';
return n;
}
LWS_VISIBLE int
lws_jws_encode_section(const char *in, size_t in_len, int first, char **p,
char *end)
{
int n, len = (end - *p) - 1;
char *p_entry = *p;
if (len < 3)
return -1;
if (!first)
*(*p)++ = '.';
n = lws_jws_base64_enc(in, in_len, *p, len - 1);
if (n < 0)
return -1;
*p += n;
return (*p) - p_entry;
}
static int
lws_jws_find_sig(const char *in, size_t len)
{
const char *p = in + len - 1;
while (len--)
if (*p == '.')
return (p + 1) - in;
else
p--;
lwsl_notice("%s failed\n", __func__);
return -1;
}
static const char * const jhdr_tok[] = {
"typ",
"alg",
};
enum enum_jhdr_tok {
JHP_TYP,
JHP_ALG
};
struct cb_hdr_s {
enum lws_genhash_types hash_type;
enum lws_genhmac_types hmac_type;
char alg[10];
int is_rsa:1;
};
static signed char
cb_hdr(struct lejp_ctx *ctx, char reason)
{
struct cb_hdr_s *s = (struct cb_hdr_s *)ctx->user;
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
switch (ctx->path_match - 1) {
case JHP_TYP: /* it is optional */
if (strcmp(ctx->buf, "JWT"))
return -1;
break;
case JHP_ALG:
lws_strncpy(s->alg, ctx->buf, sizeof(s->alg));
if (!strcmp(ctx->buf, "HS256")) {
s->hmac_type = LWS_GENHMAC_TYPE_SHA256;
break;
}
if (!strcmp(ctx->buf, "HS384")) {
s->hmac_type = LWS_GENHMAC_TYPE_SHA384;
break;
}
if (!strcmp(ctx->buf, "HS512")) {
s->hmac_type = LWS_GENHMAC_TYPE_SHA512;
break;
}
if (!strcmp(ctx->buf, "RS256")) {
s->hash_type = LWS_GENHASH_TYPE_SHA256;
s->is_rsa = 1;
break;
}
if (!strcmp(ctx->buf, "RS384")) {
s->hash_type = LWS_GENHASH_TYPE_SHA384;
s->is_rsa = 1;
break;
}
if (!strcmp(ctx->buf, "RS512")) {
s->hash_type = LWS_GENHASH_TYPE_SHA512;
s->is_rsa = 1;
break;
}
return -1;
}
return 0;
}
LWS_VISIBLE int
lws_jws_confirm_sig(const char *in, size_t len, struct lws_jwk *jwk)
{
int sig_pos = lws_jws_find_sig(in, len), pos = 0, n, m, h_len;
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_genrsa_ctx rsactx;
struct lws_genhmac_ctx ctx;
struct cb_hdr_s args;
struct lejp_ctx jctx;
char buf[2048];
/* 1) there has to be a signature */
if (sig_pos < 0)
return -1;
/* 2) find length of first, hdr, block */
while (in[pos] != '.' && pos < (int)len)
pos++;
if (pos == (int)len)
return -1;
/* 3) Decode the header block */
n = lws_b64_decode_string_len(in, pos, buf, sizeof(buf) - 1);
if (n < 0)
return -1;
/* 4) Require either:
* typ: JWT (if present) and alg: HS256/384/512
* typ: JWT (if present) and alg: RS256/384/512
*/
args.alg[0] = '\0';
args.is_rsa = 0;
lejp_construct(&jctx, cb_hdr, &args, jhdr_tok, ARRAY_SIZE(jhdr_tok));
m = (int)(signed char)lejp_parse(&jctx, (uint8_t *)buf, n);
lejp_destruct(&jctx);
if (m < 0) {
lwsl_notice("parse got %d: alg %s\n", m, args.alg);
return -1;
}
/* 5) decode the B64URL signature part into buf / m */
m = lws_b64_decode_string_len(in + sig_pos, len - sig_pos,
buf, sizeof(buf) - 1);
if (args.is_rsa) {
/* RSASSA-PKCS1-v1_5 using SHA-256/384/512 */
/* 6(RSA): compute the hash of the payload into "digest" */
if (lws_genhash_init(&hash_ctx, args.hash_type))
return -1;
if (lws_genhash_update(&hash_ctx, (uint8_t *)in, sig_pos - 1)) {
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
if (lws_genhash_destroy(&hash_ctx, digest))
return -1;
h_len = lws_genhash_size(args.hash_type);
if (lws_genrsa_create(&rsactx, &jwk->el)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = lws_genrsa_public_verify(&rsactx, digest, args.hash_type,
(uint8_t *)buf, m);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lwsl_notice("decrypt fail\n");
return -1;
}
return 0;
}
/* SHA256/384/512 HMAC */
h_len = lws_genhmac_size(args.hmac_type);
if (m < 0 || m != h_len)
return -1;
/* 6) compute HMAC over payload */
if (lws_genhmac_init(&ctx, args.hmac_type, jwk->el.e[JWK_KEY_E].buf,
jwk->el.e[JWK_KEY_E].len))
return -1;
if (lws_genhmac_update(&ctx, (uint8_t *)in, sig_pos - 1)) {
lws_genhmac_destroy(&ctx, NULL);
return -1;
}
if (lws_genhmac_destroy(&ctx, digest))
return -1;
/* 7) Compare the computed and decoded hashes */
if (memcmp(digest, buf, h_len)) {
lwsl_notice("digest mismatch\n");
return -1;
}
return 0;
}
LWS_VISIBLE int
lws_jws_sign_from_b64(const char *b64_hdr, size_t hdr_len, const char *b64_pay,
size_t pay_len, char *b64_sig, size_t sig_len,
enum lws_genhash_types hash_type, struct lws_jwk *jwk)
{
uint8_t digest[LWS_GENHASH_LARGEST];
struct lws_genhash_ctx hash_ctx;
struct lws_genrsa_ctx rsactx;
uint8_t *buf;
int n;
if (lws_genhash_init(&hash_ctx, hash_type))
return -1;
if (b64_hdr) {
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_hdr, hdr_len))
goto hash_fail;
if (lws_genhash_update(&hash_ctx, (uint8_t *)".", 1))
goto hash_fail;
}
if (lws_genhash_update(&hash_ctx, (uint8_t *)b64_pay, pay_len))
goto hash_fail;
if (lws_genhash_destroy(&hash_ctx, digest))
return -1;
if (!strcmp(jwk->keytype, "RSA")) {
if (lws_genrsa_create(&rsactx, &jwk->el)) {
lwsl_notice("%s: lws_genrsa_public_decrypt_create\n",
__func__);
return -1;
}
n = jwk->el.e[JWK_KEY_N].len;
buf = lws_malloc(n, "jws sign");
if (!buf)
return -1;
n = lws_genrsa_public_sign(&rsactx, digest, hash_type, buf, n);
lws_genrsa_destroy(&rsactx);
if (n < 0) {
lws_free(buf);
return -1;
}
n = lws_jws_base64_enc((char *)buf, n, b64_sig, sig_len);
lws_free(buf);
return n;
}
if (!strcmp(jwk->keytype, "oct"))
return lws_jws_base64_enc((char *)digest,
lws_genhash_size(hash_type),
b64_sig, sig_len);
/* unknown key type */
return -1;
hash_fail:
lws_genhash_destroy(&hash_ctx, NULL);
return -1;
}
LWS_VISIBLE int
lws_jws_create_packet(struct lws_jwk *jwk, const char *payload, size_t len,
const char *nonce, char *out, size_t out_len)
{
char *buf, *start, *p, *end, *p1, *end1, *b64_hdr, *b64_pay;
int n, b64_hdr_len, b64_pay_len;
/*
* This buffer is local to the function, the actual output
* is prepared into vhd->buf. Only the plaintext protected header
* (which contains the public key, 512 bytes for 4096b) goes in
* here temporarily.
*/
n = LWS_PRE + 2048;
buf = malloc(n);
if (!buf) {
lwsl_notice("%s: malloc %d failed\n", __func__, n);
return -1;
}
p = start = buf + LWS_PRE;
end = buf + n - LWS_PRE - 1;
/*
* temporary JWS protected header plaintext
*/
p += lws_snprintf(p, end - p, "{\"alg\":\"RS256\",\"jwk\":");
n = lws_jwk_export(jwk, 0, p, end - p);
if (n < 0) {
lwsl_notice("failed to export jwk\n");
goto bail;
}
p += n;
p += lws_snprintf(p, end - p, ",\"nonce\":\"%s\"}", nonce);
/*
* prepare the signed outer JSON with all the parts in
*/
p1 = out;
end1 = out + out_len - 1;
p1 += lws_snprintf(p1, end1 - p1, "{\"protected\":\"");
b64_hdr = p1;
n = lws_jws_base64_enc(start, p - start, p1, end1 - p1);
if (n < 0) {
lwsl_notice("%s: failed to encode protected\n", __func__);
goto bail;
}
b64_hdr_len = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"payload\":\"");
b64_pay = p1;
n = lws_jws_base64_enc(payload, len, p1, end1 - p1);
if (n < 0) {
lwsl_notice("%s: failed to encode payload\n", __func__);
goto bail;
}
b64_pay_len = n;
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\",\"signature\":\"");
/*
* taking the b64 protected header and the b64 payload, sign them
* and place the signature into the packet
*/
n = lws_jws_sign_from_b64(b64_hdr, b64_hdr_len, b64_pay, b64_pay_len,
p1, end1 - p1, LWS_GENHASH_TYPE_SHA256, jwk);
if (n < 0) {
lwsl_notice("sig gen failed\n");
goto bail;
}
p1 += n;
p1 += lws_snprintf(p1, end1 - p1, "\"}");
free(buf);
return p1 - out;
bail:
free(buf);
return -1;
}
#if defined(LWS_WITH_SELFTESTS)
/*
* These are the inputs and outputs from the worked example in RFC7515
* Appendix A.1.
*
* 1) has a fixed header + payload, and a fixed SHA256 HMAC key, and must give
* a fixed BASE64URL result.
*
* 2) has a fixed header + payload and is signed with a key given in JWK format
*/
int
lws_jws_selftest(void)
{
struct lws_genhmac_ctx ctx;
struct lws_jwk jwk;
char buf[2048], *p = buf, *end = buf + sizeof(buf) - 1, *enc_ptr, *p1;
uint8_t digest[LWS_GENHASH_LARGEST];
int n;
/* Test 1: SHA256 on RFC7515 worked example */
/* 1.1: decode the JWK oct key */
if (lws_jwk_import(&jwk, key_jwk, strlen(key_jwk)) < 0) {
lwsl_notice("Failed to decode JWK test key\n");
return -1;
}
/* 1.2: create JWS known hdr + known payload */
n = lws_jws_encode_section(test1, strlen(test1), 1, &p, end);
if (n < 0)
goto bail;
if (strcmp(buf, test1_enc))
goto bail;
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section(test2, strlen(test2), 0, &p, end);
if (n < 0)
goto bail;
if (strcmp(enc_ptr, test2_enc))
goto bail;
/* 1.3: use HMAC SHA-256 with known key on the hdr . payload */
if (lws_genhmac_init(&ctx, LWS_GENHMAC_TYPE_SHA256,
jwk.el.e[JWK_KEY_E].buf, jwk.el.e[JWK_KEY_E].len))
goto bail;
if (lws_genhmac_update(&ctx, (uint8_t *)buf, p - buf))
goto bail_destroy_hmac;
lws_genhmac_destroy(&ctx, digest);
/* 1.4: append a base64 encode of the computed HMAC digest */
enc_ptr = p + 1; /* + 1 skips the . */
n = lws_jws_encode_section((const char *)digest, 32, 0, &p, end);
if (n < 0)
goto bail;
if (strcmp(enc_ptr, hash_enc)) /* check against known B64URL hash */
goto bail;
/* 1.5: Check we can agree the signature matches the payload */
if (lws_jws_confirm_sig(buf, p - buf, &jwk) < 0) {
lwsl_notice("confirm sig failed\n");
goto bail;
}
lws_jwk_destroy(&jwk); /* finished with the key from the first test */
/* Test 2: RSA256 on RFC7515 worked example */
/* 2.1: turn the known JWK key for the RSA test into a lws_jwk */
if (lws_jwk_import(&jwk, rfc7515_rsa_key, strlen(rfc7515_rsa_key))) {
lwsl_notice("Failed to read JWK key\n");
goto bail2;
}
/* 2.2: check the signature on the test packet from RFC7515 A-1 */
if (lws_jws_confirm_sig(rfc7515_rsa_a1, strlen(rfc7515_rsa_a1),
&jwk) < 0) {
lwsl_notice("confirm rsa sig failed\n");
goto bail;
}
/* 2.3: generate our own signature for a copy of the test packet */
memcpy(buf, rfc7515_rsa_a1, strlen(rfc7515_rsa_a1));
/* set p to second . */
p = strchr(buf + 1, '.');
p1 = strchr(p + 1, '.');
n = lws_jws_sign_from_b64(buf, p - buf, p + 1, p1 - (p + 1),
p1 + 1, sizeof(buf) - (p1 - buf) - 1,
LWS_GENHASH_TYPE_SHA256, &jwk);
if (n < 0)
goto bail;
puts(buf);
/* 2.4: confirm our signature can be verified */
if (lws_jws_confirm_sig(buf, (p1 + 1 + n) - buf, &jwk) < 0) {
lwsl_notice("confirm rsa sig 2 failed\n");
goto bail;
}
lws_jwk_destroy(&jwk);
/* end */
lwsl_notice("%s: selftest OK\n", __func__);
return 0;
bail_destroy_hmac:
lws_genhmac_destroy(&ctx, NULL);
bail:
lws_jwk_destroy(&jwk);
bail2:
lwsl_err("%s: selftest failed ++++++++++++++++++++\n", __func__);
return 1;
}
#endif

View file

@ -1,294 +0,0 @@
/*
* libwebsockets - lws-ring multi-tail abstract ringbuffer api
*
* 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 "core/private.h"
LWS_VISIBLE LWS_EXTERN struct lws_ring *
lws_ring_create(size_t element_len, size_t count,
void (*destroy_element)(void *))
{
struct lws_ring *ring = lws_malloc(sizeof(*ring), "ring create");
if (!ring)
return NULL;
ring->buflen = (uint32_t)(count * element_len);
ring->element_len = (uint32_t)element_len;
ring->head = 0;
ring->oldest_tail = 0;
ring->destroy_element = destroy_element;
ring->buf = lws_malloc(ring->buflen, "ring buf");
if (!ring->buf) {
lws_free(ring);
return NULL;
}
return ring;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_destroy(struct lws_ring *ring)
{
if (ring->destroy_element)
while (ring->oldest_tail != ring->head) {
ring->destroy_element((uint8_t *)ring->buf +
ring->oldest_tail);
ring->oldest_tail =
(ring->oldest_tail + ring->element_len) %
ring->buflen;
}
if (ring->buf)
lws_free_set_NULL(ring->buf);
lws_free(ring);
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_free_elements(struct lws_ring *ring)
{
int f;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == ring->oldest_tail)
f = ring->buflen - ring->element_len;
else
if (ring->head < ring->oldest_tail)
f = (ring->oldest_tail - ring->head) -
ring->element_len;
else
f = (ring->buflen - ring->head) + ring->oldest_tail -
ring->element_len;
if (f < 2)
return 0;
return f / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail)
{ int f;
if (!tail)
tail = &ring->oldest_tail;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == *tail)
f = 0;
else
if (ring->head > *tail)
f = (ring->head - *tail);
else
f = (ring->buflen - *tail) + ring->head;
return f / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN int
lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
size_t *bytes)
{
int n;
/* n is how many bytes the whole fifo can take */
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
if (!n)
return 1;
if (ring->head + n > ring->buflen) {
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = ring->buflen - ring->head;
return 0;
}
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = n;
return 0;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_bump_head(struct lws_ring *ring, size_t bytes)
{
ring->head = (ring->head + (uint32_t)bytes) % ring->buflen;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count)
{
const uint8_t *osrc = src;
int m, n;
/* n is how many bytes the whole fifo can take */
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
/* restrict n to how much we want to insert */
if ((uint32_t)n > max_count * ring->element_len)
n = (int)(max_count * ring->element_len);
/*
* n is legal to insert, but as an optimization we can cut the
* insert into one or two memcpys, depending on if it wraps
*/
if (ring->head + n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = ring->buflen - ring->head;
memcpy(((uint8_t *)ring->buf) + ring->head, src, m);
/* we know it will wrap exactly back to zero */
ring->head = 0;
/* adapt the second memcpy for what we already did */
src = ((uint8_t *)src) + m;
n -= m;
}
memcpy(((uint8_t *)ring->buf) + ring->head, src, n);
ring->head = (ring->head + n) % ring->buflen;
return (((uint8_t *)src + n) - osrc) / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
size_t max_count)
{
uint8_t *odest = dest;
void *orig_tail = tail;
uint32_t fake_tail;
int m, n;
if (!tail) {
fake_tail = ring->oldest_tail;
tail = &fake_tail;
}
/* n is how many bytes the whole fifo has for us */
n = (int)(lws_ring_get_count_waiting_elements(ring, tail) *
ring->element_len);
/* restrict n to how much we want to insert */
if ((size_t)n > max_count * ring->element_len)
n = (int)(max_count * ring->element_len);
if (!dest) {
*tail = ((*tail) + n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return n / ring->element_len;
}
if (*tail + n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = ring->buflen - *tail;
memcpy(dest, ((uint8_t *)ring->buf) + *tail, m);
/* we know it will wrap exactly back to zero */
*tail = 0;
/* adapt the second memcpy for what we already did */
dest = ((uint8_t *)dest) + m;
n -= m;
}
memcpy(dest, ((uint8_t *)ring->buf) + *tail, n);
*tail = ((*tail) + n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return (((uint8_t *)dest + n) - odest) / ring->element_len;
}
LWS_VISIBLE LWS_EXTERN const void *
lws_ring_get_element(struct lws_ring *ring, uint32_t *tail)
{
if (!tail)
tail = &ring->oldest_tail;
if (*tail == ring->head)
return NULL;
return ((uint8_t *)ring->buf) + *tail;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail)
{
if (!ring->destroy_element) {
ring->oldest_tail = tail;
return;
}
while (ring->oldest_tail != tail) {
ring->destroy_element((uint8_t *)ring->buf + ring->oldest_tail);
ring->oldest_tail = (ring->oldest_tail + ring->element_len) %
ring->buflen;
}
}
LWS_VISIBLE LWS_EXTERN uint32_t
lws_ring_get_oldest_tail(struct lws_ring *ring)
{
return ring->oldest_tail;
}
LWS_VISIBLE LWS_EXTERN void
lws_ring_dump(struct lws_ring *ring, uint32_t *tail)
{
if (tail == NULL)
tail = &ring->oldest_tail;
lwsl_notice("ring %p: buflen %u, elem_len %u, head %u, oldest_tail %u\n"
" free_elems: %u; for tail %u, waiting elements: %u\n",
ring, ring->buflen, ring->element_len, ring->head,
ring->oldest_tail,
(int)lws_ring_get_count_free_elements(ring), *tail,
(int)lws_ring_get_count_waiting_elements(ring, tail));
}

View file

@ -1,281 +0,0 @@
/*
* libwebsockets - peer limits tracking
*
* Copyright (C) 2010-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 "core/private.h"
/* requires context->lock */
static void
__lws_peer_remove_from_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
struct lws_peer *df;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (*p == peer) {
df = *p;
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
return;
}
} lws_end_foreach_llp(p, peer_wait_list);
}
/* requires context->lock */
static void
__lws_peer_add_to_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
__lws_peer_remove_from_peer_wait_list(context, peer);
peer->peer_wait_list = context->peer_wait_list;
context->peer_wait_list = peer;
}
struct lws_peer *
lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
{
struct lws_context *context = vhost->context;
socklen_t rlen = 0;
void *q;
uint8_t *q8;
struct lws_peer *peer;
uint32_t hash = 0;
int n, af = AF_INET;
struct sockaddr_storage addr;
#ifdef LWS_WITH_IPV6
if (LWS_IPV6_ENABLED(vhost)) {
af = AF_INET6;
}
#endif
rlen = sizeof(addr);
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen))
/* eg, udp doesn't have to have a peer */
return NULL;
if (af == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
q = &s->sin_addr;
rlen = sizeof(s->sin_addr);
} else
#ifdef LWS_WITH_IPV6
{
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
q = &s->sin6_addr;
rlen = sizeof(s->sin6_addr);
}
#else
return NULL;
#endif
q8 = q;
for (n = 0; n < (int)rlen; n++)
hash = (((hash << 4) | (hash >> 28)) * n) ^ q8[n];
hash = hash % context->pl_hash_elements;
lws_context_lock(context); /* <====================================== */
lws_start_foreach_ll(struct lws_peer *, peerx,
context->pl_hash_table[hash]) {
if (peerx->af == af && !memcmp(q, peerx->addr, rlen)) {
lws_context_unlock(context); /* === */
return peerx;
}
} lws_end_foreach_ll(peerx, next);
lwsl_info("%s: creating new peer\n", __func__);
peer = lws_zalloc(sizeof(*peer), "peer");
if (!peer) {
lws_context_unlock(context); /* === */
lwsl_err("%s: OOM for new peer\n", __func__);
return NULL;
}
context->count_peers++;
peer->next = context->pl_hash_table[hash];
peer->hash = hash;
peer->af = af;
context->pl_hash_table[hash] = peer;
memcpy(peer->addr, q, rlen);
time(&peer->time_created);
/*
* On creation, the peer has no wsi attached, so is created on the
* wait list. When a wsi is added it is removed from the wait list.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
return peer;
}
/* requires context->lock */
static int
__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer)
{
lws_start_foreach_llp(struct lws_peer **, p,
context->pl_hash_table[peer->hash]) {
if (*p == peer) {
struct lws_peer *df = *p;
*p = df->next;
lws_free(df);
context->count_peers--;
return 0;
}
} lws_end_foreach_llp(p, next);
return 1;
}
void
lws_peer_cull_peer_wait_list(struct lws_context *context)
{
struct lws_peer *df;
time_t t;
time(&t);
if (context->next_cull && t < context->next_cull)
return;
lws_context_lock(context); /* <====================================== */
context->next_cull = t + 5;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (t - (*p)->time_closed_all > 10) {
df = *p;
/* remove us from the peer wait list */
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
__lws_peer_destroy(context, df);
continue; /* we already point to next, if any */
}
} lws_end_foreach_llp(p, peer_wait_list);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
struct lws *wsi)
{
if (!peer)
return;
lws_context_lock(context); /* <====================================== */
peer->count_wsi++;
wsi->peer = peer;
__lws_peer_remove_from_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_dump_from_wsi(struct lws *wsi)
{
struct lws_peer *peer;
if (!wsi || !wsi->peer)
return;
peer = wsi->peer;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d, ah %d/%d\n", __func__,
wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi,
peer->http.count_ah, peer->http.total_ah);
#else
lwsl_notice("%s: wsi %p: created %llu: wsi: %d/%d\n", __func__,
wsi, (unsigned long long)peer->time_created, peer->count_wsi, peer->total_wsi);
#endif
}
void
lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
lws_context_lock(context); /* <====================================== */
assert(peer->count_wsi);
peer->count_wsi--;
if (!peer->count_wsi
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
&& !peer->http.count_ah
#endif
) {
/*
* in order that we can accumulate peer activity correctly
* allowing for periods when the peer has no connections,
* we don't synchronously destroy the peer when his last
* wsi closes. Instead we mark the time his last wsi
* closed and add him to a peer_wait_list to be reaped
* later if no further activity is coming.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
}
lws_context_unlock(context); /* ====================================> */
}
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
int
lws_peer_confirm_ah_attach_ok(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return 0;
if (context->ip_limit_ah && peer->http.count_ah >= context->ip_limit_ah) {
lwsl_info("peer reached ah limit %d, deferring\n",
context->ip_limit_ah);
return 1;
}
return 0;
}
void
lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
lws_context_lock(context); /* <====================================== */
assert(peer->http.count_ah);
peer->http.count_ah--;
lws_context_unlock(context); /* ====================================> */
}
#endif

871
lib/output.c Normal file
View file

@ -0,0 +1,871 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2015 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"
static int
lws_0405_frame_mask_generate(struct lws *wsi)
{
#if 0
wsi->u.ws.mask[0] = 0;
wsi->u.ws.mask[1] = 0;
wsi->u.ws.mask[2] = 0;
wsi->u.ws.mask[3] = 0;
#else
int n;
/* fetch the per-frame nonce */
n = lws_get_random(lws_get_context(wsi), wsi->u.ws.mask, 4);
if (n != 4) {
lwsl_parser("Unable to read from random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, n);
return 1;
}
#endif
/* start masking from first byte of masking key buffer */
wsi->u.ws.mask_idx = 0;
return 0;
}
#ifdef _DEBUG
LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len)
{
unsigned char *buf = (unsigned char *)vbuf;
unsigned int n, m, start;
char line[80];
char *p;
lwsl_parser("\n");
for (n = 0; n < len;) {
start = n;
p = line;
p += sprintf(p, "%04X: ", start);
for (m = 0; m < 16 && n < len; m++)
p += sprintf(p, "%02X ", buf[n++]);
while (m++ < 16)
p += sprintf(p, " ");
p += sprintf(p, " ");
for (m = 0; m < 16 && (start + m) < len; m++) {
if (buf[start + m] >= ' ' && buf[start + m] < 127)
*p++ = buf[start + m];
else
*p++ = '.';
}
while (m++ < 16)
*p++ = ' ';
*p++ = '\n';
*p = '\0';
lwsl_debug("%s", line);
}
lwsl_debug("\n");
}
#endif
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
size_t real_len = len;
unsigned int n;
int m;
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_WRITE, 1);
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
!wsi->trunc_len)
return len;
if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
buf > (wsi->trunc_alloc + wsi->trunc_len + wsi->trunc_offset))) {
char dump[20];
strncpy(dump, (char *)buf, sizeof(dump) - 1);
dump[sizeof(dump) - 1] = '\0';
#if defined(LWS_WITH_ESP8266)
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n",
wsi, (unsigned long)len, dump);
#else
lwsl_err("****** %p: Sending new %lu (%s), pending truncated ...\n"
" It's illegal to do an lws_write outside of\n"
" the writable callback: fix your code",
wsi, (unsigned long)len, dump);
#endif
assert(0);
return -1;
}
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_DO_SEND, &buf, len);
if (m < 0)
return -1;
if (m) /* handled */ {
n = m;
goto handle_truncated_send;
}
if (!lws_socket_is_valid(wsi->desc.sockfd))
lwsl_warn("** error invalid sock but expected to send\n");
/* limit sending */
if (wsi->protocol->tx_packet_size)
n = wsi->protocol->tx_packet_size;
else {
n = wsi->protocol->rx_buffer_size;
if (!n)
n = context->pt_serv_buf_size;
}
n += LWS_PRE + 4;
if (n > len)
n = len;
#if defined(LWS_WITH_ESP8266)
if (wsi->pending_send_completion) {
n = 0;
goto handle_truncated_send;
}
#endif
/* nope, send it on the socket directly */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, n);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
//lwsl_notice("lws_ssl_capable_write: %d\n", n);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
/* we're going to close, let close know sends aren't possible */
wsi->socket_is_permanently_unusable = 1;
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/* nothing got sent, not fatal, retry the whole thing later */
n = 0;
break;
}
handle_truncated_send:
/*
* we were already handling a truncated send?
*/
if (wsi->trunc_len) {
lwsl_info("%p partial adv %d (vs %ld)\n", wsi, n, (long)real_len);
wsi->trunc_offset += n;
wsi->trunc_len -= n;
if (!wsi->trunc_len) {
lwsl_info("***** %p partial send completed\n", wsi);
/* done with it, but don't free it */
n = real_len;
if (wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
lwsl_info("***** %p signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
lws_callback_on_writable(wsi);
return n;
}
if ((unsigned int)n == real_len)
/* what we just sent went out cleanly */
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable)
*/
lwsl_debug("%p new partial sent %d from %lu total\n", wsi, n,
(unsigned long)real_len);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITE_PARTIALS, 1);
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, n);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->trunc_alloc || real_len - n > wsi->trunc_alloc_len) {
lws_free(wsi->trunc_alloc);
wsi->trunc_alloc_len = real_len - n;
wsi->trunc_alloc = lws_malloc(real_len - n);
if (!wsi->trunc_alloc) {
lwsl_err("truncated send: unable to malloc %lu\n",
(unsigned long)(real_len - n));
return -1;
}
}
wsi->trunc_offset = 0;
wsi->trunc_len = real_len - n;
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
/* since something buffered, force it to get another chance to send */
lws_callback_on_writable(wsi);
return real_len;
}
LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol wp)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
unsigned char is_masked_bit = 0;
unsigned char *dropmask = NULL;
struct lws_tokens eff_buf;
int pre = 0, n;
size_t orig_len = len;
if (wsi->parent_carries_io) {
struct lws_write_passthru pas;
pas.buf = buf;
pas.len = len;
pas.wp = wp;
pas.wsi = wsi;
if (wsi->parent->protocol->callback(wsi->parent,
LWS_CALLBACK_CHILD_WRITE_VIA_PARENT,
wsi->parent->user_space,
(void *)&pas, 0))
return 1;
return len;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1);
if ((int)len < 0) {
lwsl_err("%s: suspicious len int %d, ulong %lu\n", __func__,
(int)len, (unsigned long)len);
return -1;
}
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_B_WRITE, len);
#ifdef LWS_WITH_ACCESS_LOG
wsi->access_log.sent += len;
#endif
if (wsi->vhost)
wsi->vhost->conn_stats.tx += len;
if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
/* remove us from the list */
struct lws **w = &pt->tx_draining_ext_list;
// lwsl_notice("%s: TX EXT DRAINING: Remove from list\n", __func__);
wsi->u.ws.tx_draining_ext = 0;
/* remove us from context draining ext list */
while (*w) {
if (*w == wsi) {
*w = wsi->u.ws.tx_draining_ext_list;
break;
}
w = &((*w)->u.ws.tx_draining_ext_list);
}
wsi->u.ws.tx_draining_ext_list = NULL;
wp = (wsi->u.ws.tx_draining_stashed_wp & 0xc0) |
LWS_WRITE_CONTINUATION;
lwsl_ext("FORCED draining wp to 0x%02X\n", wp);
}
lws_restart_ws_ping_pong_timer(wsi);
if (wp == LWS_WRITE_HTTP ||
wp == LWS_WRITE_HTTP_FINAL ||
wp == LWS_WRITE_HTTP_HEADERS)
goto send_raw;
/* if not in a state to send stuff, then just send nothing */
if (wsi->state != LWSS_ESTABLISHED &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
wp != LWS_WRITE_CLOSE))
return 0;
/* if we are continuing a frame that already had its header done */
if (wsi->u.ws.inside_frame) {
lwsl_debug("INSIDE FRAME\n");
goto do_more_inside_frame;
}
wsi->u.ws.clean_buffer = 1;
/*
* give a chance to the extensions to modify payload
* the extension may decide to produce unlimited payload erratically
* (eg, compression extension), so we require only that if he produces
* something, it will be a complete fragment of the length known at
* the time (just the fragment length known), and if he has
* more we will come back next time he is writeable and allow him to
* produce more fragments until he's drained.
*
* This allows what is sent each time it is writeable to be limited to
* a size that can be sent without partial sends or blocking, allows
* interleaving of control frames and other connection service.
*/
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
switch ((int)wp) {
case LWS_WRITE_PING:
case LWS_WRITE_PONG:
case LWS_WRITE_CLOSE:
break;
default:
lwsl_debug("LWS_EXT_CB_PAYLOAD_TX\n");
n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_TX, &eff_buf, wp);
if (n < 0)
return -1;
if (n && eff_buf.token_len) {
lwsl_debug("drain len %d\n", (int)eff_buf.token_len);
/* extension requires further draining */
wsi->u.ws.tx_draining_ext = 1;
wsi->u.ws.tx_draining_ext_list = pt->tx_draining_ext_list;
pt->tx_draining_ext_list = wsi;
/* we must come back to do more */
lws_callback_on_writable(wsi);
/*
* keep a copy of the write type for the overall
* action that has provoked generation of these
* fragments, so the last guy can use its FIN state.
*/
wsi->u.ws.tx_draining_stashed_wp = wp;
/* this is definitely not actually the last fragment
* because the extension asserted he has more coming
* So make sure this intermediate one doesn't go out
* with a FIN.
*/
wp |= LWS_WRITE_NO_FIN;
}
if (eff_buf.token_len && wsi->u.ws.stashed_write_pending) {
wsi->u.ws.stashed_write_pending = 0;
wp = (wp &0xc0) | (int)wsi->u.ws.stashed_write_type;
}
}
/*
* an extension did something we need to keep... for example, if
* compression extension, it has already updated its state according
* to this being issued
*/
if ((char *)buf != eff_buf.token) {
/*
* ext might eat it, but not have anything to issue yet.
* In that case we have to follow his lead, but stash and
* replace the write type that was lost here the first time.
*/
if (len && !eff_buf.token_len) {
if (!wsi->u.ws.stashed_write_pending)
wsi->u.ws.stashed_write_type = (char)wp & 0x3f;
wsi->u.ws.stashed_write_pending = 1;
return len;
}
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
}
buf = (unsigned char *)eff_buf.token;
len = eff_buf.token_len;
lwsl_debug("%p / %d\n", buf, (int)len);
if (!buf) {
lwsl_err("null buf (%d)\n", (int)len);
return -1;
}
switch (wsi->ietf_spec_revision) {
case 13:
if (masked7) {
pre += 4;
dropmask = &buf[0 - pre];
is_masked_bit = 0x80;
}
switch (wp & 0xf) {
case LWS_WRITE_TEXT:
n = LWSWSOPC_TEXT_FRAME;
break;
case LWS_WRITE_BINARY:
n = LWSWSOPC_BINARY_FRAME;
break;
case LWS_WRITE_CONTINUATION:
n = LWSWSOPC_CONTINUATION;
break;
case LWS_WRITE_CLOSE:
n = LWSWSOPC_CLOSE;
break;
case LWS_WRITE_PING:
n = LWSWSOPC_PING;
break;
case LWS_WRITE_PONG:
n = LWSWSOPC_PONG;
break;
default:
lwsl_warn("lws_write: unknown write opc / wp\n");
return -1;
}
if (!(wp & LWS_WRITE_NO_FIN))
n |= 1 << 7;
if (len < 126) {
pre += 2;
buf[-pre] = n;
buf[-pre + 1] = (unsigned char)(len | is_masked_bit);
} else {
if (len < 65536) {
pre += 4;
buf[-pre] = n;
buf[-pre + 1] = 126 | is_masked_bit;
buf[-pre + 2] = (unsigned char)(len >> 8);
buf[-pre + 3] = (unsigned char)len;
} else {
pre += 10;
buf[-pre] = n;
buf[-pre + 1] = 127 | is_masked_bit;
#if defined __LP64__
buf[-pre + 2] = (len >> 56) & 0x7f;
buf[-pre + 3] = len >> 48;
buf[-pre + 4] = len >> 40;
buf[-pre + 5] = len >> 32;
#else
buf[-pre + 2] = 0;
buf[-pre + 3] = 0;
buf[-pre + 4] = 0;
buf[-pre + 5] = 0;
#endif
buf[-pre + 6] = (unsigned char)(len >> 24);
buf[-pre + 7] = (unsigned char)(len >> 16);
buf[-pre + 8] = (unsigned char)(len >> 8);
buf[-pre + 9] = (unsigned char)len;
}
}
break;
}
do_more_inside_frame:
/*
* Deal with masking if we are in client -> server direction and
* the wp demands it
*/
if (masked7) {
if (!wsi->u.ws.inside_frame)
if (lws_0405_frame_mask_generate(wsi)) {
lwsl_err("frame mask generation failed\n");
return -1;
}
/*
* in v7, just mask the payload
*/
if (dropmask) { /* never set if already inside frame */
for (n = 4; n < (int)len + 4; n++)
dropmask[n] = dropmask[n] ^ wsi->u.ws.mask[
(wsi->u.ws.mask_idx++) & 3];
/* copy the frame nonce into place */
memcpy(dropmask, wsi->u.ws.mask, 4);
}
}
send_raw:
switch ((int)wp) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:
case LWS_WRITE_HTTP_FINAL:
case LWS_WRITE_HTTP_HEADERS:
case LWS_WRITE_PONG:
case LWS_WRITE_PING:
#ifdef LWS_USE_HTTP2
if (wsi->mode == LWSCM_HTTP2_SERVING) {
unsigned char flags = 0;
n = LWS_HTTP2_FRAME_TYPE_DATA;
if (wp == LWS_WRITE_HTTP_HEADERS) {
n = LWS_HTTP2_FRAME_TYPE_HEADERS;
flags = LWS_HTTP2_FLAG_END_HEADERS;
if (wsi->u.http2.send_END_STREAM)
flags |= LWS_HTTP2_FLAG_END_STREAM;
}
if ((wp == LWS_WRITE_HTTP ||
wp == LWS_WRITE_HTTP_FINAL) &&
wsi->u.http.content_length) {
wsi->u.http.content_remain -= len;
lwsl_info("%s: content_remain = %llu\n", __func__,
(unsigned long long)wsi->u.http.content_remain);
if (!wsi->u.http.content_remain) {
lwsl_info("%s: selecting final write mode\n", __func__);
wp = LWS_WRITE_HTTP_FINAL;
}
}
if (wp == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
lwsl_info("%s: setting END_STREAM\n", __func__);
flags |= LWS_HTTP2_FLAG_END_STREAM;
}
return lws_http2_frame_write(wsi, n, flags,
wsi->u.http2.my_stream_id, len, buf);
}
#endif
return lws_issue_raw(wsi, (unsigned char *)buf - pre, len + pre);
default:
break;
}
/*
* give any active extensions a chance to munge the buffer
* before send. We pass in a pointer to an lws_tokens struct
* prepared with the default buffer and content length that's in
* there. Rather than rewrite the default buffer, extensions
* that expect to grow the buffer can adapt .token to
* point to their own per-connection buffer in the extension
* user allocation. By default with no extensions or no
* extension callback handling, just the normal input buffer is
* used then so it is efficient.
*
* callback returns 1 in case it wants to spill more buffers
*
* This takes care of holding the buffer if send is incomplete, ie,
* if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
* the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
* return to the user code how much OF THE USER BUFFER was consumed.
*/
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre);
wsi->u.ws.inside_frame = 1;
if (n <= 0)
return n;
if (n == (int)len + pre) {
/* everything in the buffer was handled (or rebuffered...) */
wsi->u.ws.inside_frame = 0;
return orig_len;
}
/*
* it is how many bytes of user buffer got sent... may be < orig_len
* in which case callback when writable has already been arranged
* and user code can call lws_write() again with the rest
* later.
*/
return n - pre;
}
LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_process_html_args args;
lws_filepos_t amount, poss;
unsigned char *p;
#if defined(LWS_WITH_RANGES)
unsigned char finished = 0;
#endif
int n, m;
// lwsl_notice("%s (trunc len %d)\n", __func__, wsi->trunc_len);
while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) {
if (wsi->trunc_len) {
if (lws_issue_raw(wsi, wsi->trunc_alloc +
wsi->trunc_offset,
wsi->trunc_len) < 0) {
lwsl_info("%s: closing\n", __func__);
goto file_had_it;
}
continue;
}
if (wsi->u.http.filepos == wsi->u.http.filelen)
goto all_sent;
n = 0;
p = pt->serv_buf;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges && !wsi->u.http.range.inside) {
lwsl_notice("%s: doing range start %llu\n", __func__, wsi->u.http.range.start);
if ((long long)lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
wsi->u.http.range.start -
wsi->u.http.filepos) < 0)
goto file_had_it;
wsi->u.http.filepos = wsi->u.http.range.start;
if (wsi->u.http.range.count_ranges > 1) {
n = lws_snprintf((char *)p, context->pt_serv_buf_size,
"_lws\x0d\x0a"
"Content-Type: %s\x0d\x0a"
"Content-Range: bytes %llu-%llu/%llu\x0d\x0a"
"\x0d\x0a",
wsi->u.http.multipart_content_type,
wsi->u.http.range.start,
wsi->u.http.range.end,
wsi->u.http.range.extent);
p += n;
}
wsi->u.http.range.budget = wsi->u.http.range.end -
wsi->u.http.range.start + 1;
wsi->u.http.range.inside = 1;
}
#endif
poss = context->pt_serv_buf_size - n;
/*
* if there is a hint about how much we will do well to send at one time,
* restrict ourselves to only trying to send that.
*/
if (wsi->protocol->tx_packet_size && poss > wsi->protocol->tx_packet_size)
poss = wsi->protocol->tx_packet_size;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges) {
if (wsi->u.http.range.count_ranges > 1)
poss -= 7; /* allow for final boundary */
if (poss > wsi->u.http.range.budget)
poss = wsi->u.http.range.budget;
}
#endif
if (wsi->sending_chunked) {
/* we need to drop the chunk size in here */
p += 10;
/* allow for the chunk to grow by 128 in translation */
poss -= 10 + 128;
}
if (lws_vfs_file_read(wsi->u.http.fop_fd, &amount, p, poss) < 0)
goto file_had_it; /* caller will close */
//lwsl_notice("amount %ld\n", amount);
if (wsi->sending_chunked)
n = (int)amount;
else
n = (p - pt->serv_buf) + (int)amount;
if (n) {
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
context->timeout_secs);
if (wsi->sending_chunked) {
args.p = (char *)p;
args.len = n;
args.max_len = (unsigned int)poss + 128;
args.final = wsi->u.http.filepos + n ==
wsi->u.http.filelen;
if (user_callback_handle_rxflow(
wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
LWS_CALLBACK_PROCESS_HTML,
wsi->user_space, &args, 0) < 0)
goto file_had_it;
n = args.len;
p = (unsigned char *)args.p;
} else
p = pt->serv_buf;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.send_ctr + 1 ==
wsi->u.http.range.count_ranges && // last range
wsi->u.http.range.count_ranges > 1 && // was 2+ ranges (ie, multipart)
wsi->u.http.range.budget - amount == 0) {// final part
n += lws_snprintf((char *)pt->serv_buf + n, 6,
"_lws\x0d\x0a"); // append trailing boundary
lwsl_debug("added trailing boundary\n");
}
#endif
m = lws_write(wsi, p, n,
wsi->u.http.filepos == wsi->u.http.filelen ?
LWS_WRITE_HTTP_FINAL :
LWS_WRITE_HTTP
);
if (m < 0)
goto file_had_it;
wsi->u.http.filepos += amount;
#if defined(LWS_WITH_RANGES)
if (wsi->u.http.range.count_ranges >= 1) {
wsi->u.http.range.budget -= amount;
if (wsi->u.http.range.budget == 0) {
lwsl_notice("range budget exhausted\n");
wsi->u.http.range.inside = 0;
wsi->u.http.range.send_ctr++;
if (lws_ranges_next(&wsi->u.http.range) < 1) {
finished = 1;
goto all_sent;
}
}
}
#endif
if (m != n) {
/* adjust for what was not sent */
if (lws_vfs_file_seek_cur(wsi->u.http.fop_fd,
m - n) ==
(unsigned long)-1)
goto file_had_it;
}
}
all_sent:
if ((!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen)
#if defined(LWS_WITH_RANGES)
|| finished)
#else
)
#endif
{
wsi->state = LWSS_HTTP;
/* we might be in keepalive, so close it off here */
lws_vfs_file_close(&wsi->u.http.fop_fd);
lwsl_debug("file completed\n");
if (wsi->protocol->callback)
/* ignore callback returned value */
if (user_callback_handle_rxflow(
wsi->protocol->callback, wsi,
LWS_CALLBACK_HTTP_FILE_COMPLETION,
wsi->user_space, NULL, 0) < 0)
return -1;
return 1; /* >0 indicates completed */
}
}
lws_callback_on_writable(wsi);
return 0; /* indicates further processing must be done */
file_had_it:
lws_vfs_file_close(&wsi->u.http.fop_fd);
return -1;
}
#if LWS_POSIX
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
struct lws_context *context = wsi->context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n;
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
if (n >= 0) {
if (wsi->vhost)
wsi->vhost->conn_stats.rx += n;
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
lws_restart_ws_ping_pong_timer(wsi);
return n;
}
#if LWS_POSIX
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR)
return LWS_SSL_CAPABLE_MORE_SERVICE;
#endif
lwsl_notice("error on reading from skt : %d\n", LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
{
int n = 0;
#if LWS_POSIX
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK) {
lws_set_blocking_send(wsi);
}
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
#else
(void)n;
(void)wsi;
(void)buf;
(void)len;
// !!!
#endif
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n", len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
#endif
LWS_VISIBLE int
lws_ssl_pending_no_ssl(struct lws *wsi)
{
(void)wsi;
#if defined(LWS_WITH_ESP32)
return 100;
#else
return 0;
#endif
}

1634
lib/parsers.c Normal file

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-2017 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2015 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
@ -19,31 +19,21 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
int
_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
{
#if !defined(LWS_WITH_LIBUV) && !defined(LWS_WITH_LIBEV) && !defined(LWS_WITH_LIBEVENT)
volatile struct lws_context_per_thread *vpt;
#endif
struct lws_context_per_thread *pt;
struct lws_context *context;
int ret = 0, pa_events = 1;
struct lws_pollfd *pfd;
int sampled_tid, tid;
if (!wsi)
if (!wsi || wsi->position_in_fds_table < 0)
return 0;
assert(wsi->position_in_fds_table == LWS_NO_FDS_POS ||
wsi->position_in_fds_table >= 0);
if (wsi->position_in_fds_table == LWS_NO_FDS_POS)
return 0;
if (((volatile struct lws *)wsi)->handling_pollout &&
!_and && _or == LWS_POLLOUT) {
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,
@ -52,7 +42,7 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
* Instead of changing the fds, inform the service thread
* what happened, and ask it to leave POLLOUT active on exit
*/
((volatile struct lws *)wsi)->leave_pollout_active = 1;
wsi->leave_pollout_active = 1;
/*
* by definition service thread is not in poll wait, so no need
* to cancel service
@ -65,106 +55,45 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
context = wsi->context;
pt = &context->pt[(int)wsi->tsi];
assert(wsi->position_in_fds_table < (int)pt->fds_count);
#if !defined(LWS_WITH_LIBUV) && \
!defined(LWS_WITH_LIBEV) && \
!defined(LWS_WITH_LIBEVENT)
/*
* This only applies when we use the default poll() event loop.
*
* BSD can revert pa->events at any time, when the kernel decides to
* exit from poll(). We can't protect against it using locking.
*
* Therefore we must check first if the service thread is in poll()
* wait; if so, we know we must be being called from a foreign thread,
* and we must keep a strictly ordered list of changes we made instead
* of trying to apply them, since when poll() exits, which may happen
* at any time it would revert our changes.
*
* The plat code will apply them when it leaves the poll() wait
* before doing anything else.
*/
vpt = (volatile struct lws_context_per_thread *)pt;
vpt->foreign_spinlock = 1;
lws_memory_barrier();
if (vpt->inside_poll) {
struct lws_foreign_thread_pollfd *ftp, **ftp1;
/*
* We are certainly a foreign thread trying to change events
* while the service thread is in the poll() wait.
*
* Create a list of changes to be applied after poll() exit,
* instead of trying to apply them now.
*/
ftp = lws_malloc(sizeof(*ftp), "ftp");
if (!ftp) {
vpt->foreign_spinlock = 0;
lws_memory_barrier();
ret = -1;
goto bail;
}
ftp->_and = _and;
ftp->_or = _or;
ftp->fd_index = wsi->position_in_fds_table;
ftp->next = NULL;
/* place at END of list to maintain order */
ftp1 = (struct lws_foreign_thread_pollfd **)
&vpt->foreign_pfd_list;
while (*ftp1)
ftp1 = &((*ftp1)->next);
*ftp1 = ftp;
vpt->foreign_spinlock = 0;
lws_memory_barrier();
lws_cancel_service_pt(wsi);
return 0;
}
vpt->foreign_spinlock = 0;
lws_memory_barrier();
#endif
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;
lwsl_debug("%s: wsi %p: fd %d events %d -> %d\n", __func__, wsi, pa->fd, pfd->events, (pfd->events & ~_and) | _or);
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 &&
wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
ret = -1;
goto bail;
}
if (context->event_loop_ops->io) {
if (_and & LWS_POLLIN)
context->event_loop_ops->io(wsi,
LWS_EV_STOP | LWS_EV_READ);
if (_or & LWS_POLLIN)
context->event_loop_ops->io(wsi,
LWS_EV_START | LWS_EV_READ);
if (_and & LWS_POLLOUT)
context->event_loop_ops->io(wsi,
LWS_EV_STOP | LWS_EV_WRITE);
if (_or & LWS_POLLOUT)
context->event_loop_ops->io(wsi,
LWS_EV_START | LWS_EV_WRITE);
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);
}
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);
}
/*
@ -174,16 +103,20 @@ _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 (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__);
ret = -1;
goto bail;
}
sampled_tid = context->service_tid;
if (sampled_tid && wsi->vhost) {
if (sampled_tid) {
tid = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) {
@ -194,39 +127,34 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa)
lws_cancel_service_pt(wsi);
}
}
bail:
return ret;
}
#ifndef LWS_NO_SERVER
/*
* Enable or disable listen sockets on this pt globally...
* it's modulated according to the pt having space for a new accept.
*/
static void
lws_accept_modulation(struct lws_context *context,
struct lws_context_per_thread *pt, int allow)
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 (vh->lserv_wsi) {
if (allow)
_lws_change_pollfd(vh->lserv_wsi,
if (allow)
_lws_change_pollfd(pt->wsi_listening,
0, LWS_POLLIN, &pa1);
else
_lws_change_pollfd(vh->lserv_wsi,
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)
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];
@ -242,46 +170,54 @@ __insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
return 1;
}
#if !defined(_WIN32)
if (wsi->desc.sockfd - lws_plat_socket_offset() >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d) offset %d\n",
wsi->desc.sockfd, context->max_fds, lws_plat_socket_offset());
#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->event_pipe || wsi->vhost);
assert(wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd));
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 1))
return -1;
lws_pt_lock(pt);
pt->count_conns++;
insert_wsi(context, wsi);
wsi->position_in_fds_table = pt->fds_count;
#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 &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
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(context, pt, 0);
lws_accept_modulation(pt, 0);
#endif
lws_pt_unlock(pt);
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1))
ret = -1;
@ -289,13 +225,15 @@ __insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
}
int
__remove_wsi_socket_from_fds(struct lws *wsi)
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) {
@ -303,16 +241,14 @@ __remove_wsi_socket_from_fds(struct lws *wsi)
return 0;
}
#if !defined(_WIN32)
if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd,
context->max_fds);
#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 &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1))
return -1;
@ -321,89 +257,53 @@ __remove_wsi_socket_from_fds(struct lws *wsi)
/* the guy who is to be deleted's slot index in pt->fds */
m = wsi->position_in_fds_table;
/* these are the only valid possibilities for position_in_fds_table */
assert(m == LWS_NO_FDS_POS || (m >= 0 &&
(unsigned int)m < pt->fds_count));
#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);
if (context->event_loop_ops->io)
context->event_loop_ops->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);
if (m != LWS_NO_FDS_POS) {
/* 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;
/* have the last guy take up the now vacant slot */
pt->fds[m] = pt->fds[pt->fds_count - 1];
/* this decrements pt->fds_count */
lws_plat_delete_socket_from_fds(context, wsi, m);
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 for 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 = LWS_NO_FDS_POS;
}
/* 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) && wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
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 */
(unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(context, pt, 1);
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 &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
ret = -1;
return ret;
}
int
__lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
if (!wsi || (!wsi->protocol && !wsi->event_pipe) ||
wsi->position_in_fds_table == LWS_NO_FDS_POS)
return 0;
context = lws_get_context(wsi);
if (!context)
return 1;
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0))
return -1;
ret = _lws_change_pollfd(wsi, _and, _or, &pa);
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#endif
return ret;
}
@ -411,13 +311,29 @@ 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;
pt = &wsi->context->pt[(int)wsi->tsi];
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
return 1;
lws_pt_lock(pt, __func__);
ret = __lws_change_pollfd(wsi, _and, _or);
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;
}
@ -426,25 +342,20 @@ LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
{
struct lws_context_per_thread *pt;
int n;
#ifdef LWS_USE_HTTP2
struct lws *network_wsi, *wsi2;
int already;
#endif
if (lwsi_state(wsi) == LRS_SHUTDOWN)
if (wsi->state == LWSS_SHUTDOWN)
return 0;
if (wsi->socket_is_permanently_unusable)
return 0;
pt = &wsi->context->pt[(int)wsi->tsi];
if (wsi->parent_carries_io) {
#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
n = lws_callback_on_writable(wsi->parent);
int n = lws_callback_on_writable(wsi->parent);
if (n < 0)
return n;
@ -452,35 +363,74 @@ lws_callback_on_writable(struct lws *wsi)
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);
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->role_ops->callback_on_writable) {
if (wsi->role_ops->callback_on_writable(wsi))
return 1;
wsi = lws_get_network_wsi(wsi);
if (wsi->mode != LWSCM_HTTP2_SERVING)
goto network_sock;
if (wsi->u.http2.requested_POLLOUT) {
lwsl_info("already pending writable\n");
return 1;
}
if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
lwsl_debug("%s: failed to find socket %d\n", __func__,
wsi->desc.sockfd);
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);
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))
return 1;
if (wsi->position_in_fds_table < 0) {
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->desc.sockfd);
return -1;
}
if (__lws_change_pollfd(wsi, 0, LWS_POLLOUT))
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1;
return 1;
}
/*
* stitch protocol choice into the vh protocol linked list
* We always insert ourselves at the start of the list
@ -493,16 +443,20 @@ lws_callback_on_writable(struct lws *wsi)
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");
}
lws_vhost_lock(wsi->vhost);
wsi->same_vh_protocol_prev = &wsi->vhost->same_vh_protocol_list[n];
/* old first guy is our next */
wsi->same_vh_protocol_next = wsi->vhost->same_vh_protocol_list[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;
@ -510,10 +464,6 @@ lws_same_vh_protocol_insert(struct lws *wsi, int n)
/* old first guy points back to us now */
wsi->same_vh_protocol_next->same_vh_protocol_prev =
&wsi->same_vh_protocol_next;
wsi->on_same_vh_list = 1;
lws_vhost_unlock(wsi->vhost);
}
void
@ -528,11 +478,6 @@ lws_same_vh_protocol_remove(struct lws *wsi)
*/
lwsl_info("%s: removing same prot wsi %p\n", __func__, wsi);
if (!wsi->vhost || !wsi->on_same_vh_list)
return;
lws_vhost_lock(wsi->vhost);
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",
@ -544,15 +489,13 @@ lws_same_vh_protocol_remove(struct lws *wsi)
}
/* our next should point back to our prev */
if (wsi->same_vh_protocol_next)
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;
wsi->on_same_vh_list = 0;
lws_vhost_unlock(wsi->vhost);
}
@ -564,6 +507,7 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
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));
@ -572,14 +516,19 @@ lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
}
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)
assert(wsi->same_vh_protocol_next->
same_vh_protocol_prev ==
&wsi->same_vh_protocol_next);
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;
}
@ -591,14 +540,9 @@ LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
struct lws_vhost *vhost;
struct lws_vhost *vhost = context->vhost_list;
int n;
if (!context)
return 0;
vhost = context->vhost_list;
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==

2273
lib/private-libwebsockets.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,7 @@
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "private-libwebsockets.h"
/*
* RFC7233 examples
@ -121,10 +121,7 @@ lws_ranges_next(struct lws_range_parsing *rp)
if (c == ',')
rp->pos++;
/*
* By the end of this, start and end are
* always valid if the range still is
*/
/* 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)

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