Compare commits

..

459 commits

Author SHA1 Message Date
fba43d5bce rpm: add missing build-time dependencies 2018-05-26 00:52:52 +02:00
848085226b rpm: do parallel compilation during rpm builds 2018-05-26 00:52:52 +02:00
e8c187ffd2 rpm: add missing test-apps to spec file 2018-05-26 00:52:52 +02:00
72d0386632 rpm: fix bogus date in changelog 2018-05-26 00:52:52 +02:00
1d50362a1e cmake: CPACK_SOURCE_IGNORE_FILES is a list of regular expressions 2018-05-26 00:52:51 +02:00
Andy Green
49c7e8238b cancel pipes: treat nonzero return code from platform only as indication not to use dummy_pipe_fds
https://github.com/warmcat/libwebsockets/issues/1291
2018-05-25 21:45:36 +08:00
Andy Green
2c2969cdac travis: handle reduced max run time by splitting autobahn tests across two instances 2018-05-25 21:45:27 +08:00
Andy Green
ad078faaaf client: fix SOCKS5 compilation
https://github.com/warmcat/libwebsockets/issues/1292
2018-05-25 21:44:59 +08:00
Andy Green
227c1a0df2 h2 client: no close callback for network stream 2018-05-24 11:27:56 +08:00
Andy Green
a2e0dc92ce sshd: fix timingsafe_bcmp
https://github.com/warmcat/libwebsockets/issues/1288
2018-05-23 17:22:45 +08:00
Andy Green
34d07125e7 ws: pingpong: server wants to send pings too
During the role refactoring pingpong accidentally
became something only a client wsi could initiate.

https://github.com/warmcat/libwebsockets/issues/1283
2018-05-22 16:31:18 +08:00
Andy Green
82fc4756be cgi: fix QUERY_STRING 2018-05-18 14:45:43 +08:00
Silas Parker
fde8c823ac cmake: align include used for LWS_HAVE_TCP_USER_TIMEOUT 2018-05-15 08:28:21 +08:00
Andy Green
56eef571a0 mbedtls: allow build with LWS_ROLE_WS=0 2018-05-11 16:43:10 +08:00
Andy Green
bc409bbc2e lws_read_h1: handle LRS_RETURNED_CLOSE 2018-05-11 10:26:52 +08:00
Andy Green
a77e2a7fbf role ws: allow zero length PONG 2018-05-09 18:55:26 +08:00
Andy Green
24c216fac5 minimal-ws-client-ping 2018-05-09 17:02:03 +08:00
Andy Green
d6e3346763 LWS_INLINE: replace all inline 2018-05-09 09:24:17 +08:00
Andy Green
0ef3eae50b minimal-http-server-form-get: fix redirect api usage 2018-05-07 16:33:14 +08:00
Andy Green
eaa935a80a v3.0.0 2018-05-07 13:38:53 +08:00
Andy Green
1d3cafe179 autobahn: 12.3.1 and 12.3.2 also affected by autobahn bug 2018-05-07 13:37:25 +08:00
Andy Green
2f5f1125f7 esp32: align hrtimer and avoid compiler warning 2018-05-07 13:34:14 +08:00
Andy Green
1f6cbbe7a5 update LICENSE against refactors 2018-05-07 11:27:50 +08:00
Andy Green
412ff64be9 lws_ring: implement lagging client culling 2018-05-06 07:36:31 +08:00
Andy Green
c4dc102a0b windows: cleanup wrong and duplicated socket validity helpers
https://github.com/warmcat/libwebsockets/issues/1259
2018-05-06 07:22:25 +08:00
jas39
d48be760c0 Update protocol_esp32_lws_scan.c
Consistent use of make constant throughout files
#define CONFIG_LWS_IS_FACTORY_APPLICATION==1
2018-05-05 10:29:00 +08:00
jas39
57f7f54555 Update protocol_esp32_lws_ota.c
Corrected partition range check
Use of library constant
2018-05-05 10:17:59 +08:00
Andy Green
e77dafba6f win32: align hrtimer support with unix plat
Workaround last build warnings on win32.
2018-05-05 07:18:00 +08:00
Andy Green
e7673b4c1d coverity: check argv 2018-05-05 06:58:52 +08:00
Andy Green
eeb76e822a minimal-http-server-mimetypes 2018-05-05 06:03:16 +08:00
Andy Green
1c08a96b47 prepare for new release 2018-05-05 06:03:15 +08:00
Andy Green
c99a99e9b4 LRS_DOING_TRANSACTION 2018-05-04 12:05:56 +08:00
Andy Green
de064fd65a refactor: core code in lib/core and private-libwebsockets.h to core/private.h
This commit is coverity-clean as tested

cmake .. -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_ACME=1 -DLWS_WITH_LWSWS=1 -DLWS_WITH_LIBUV=1 -DLWS_WITH_HTTP2=1 -DLWS_WITHOUT_CLIENT=0 -DLWS_WITHOUT_SERVER=0 -DLWS_UNIX_SOCK=1 -DLWS_WITH_TLS=0 -DLWS_WITH_MBEDTLS=0 -DLWS_WITH_CGI=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_GENERIC_SESSIONS=1 -DLWS_WITH_RANGES=1 -DLWS_ROLE_WS=1 -DLWS_MAX_SMP=16 -DLWS_ROLE_H1=1 -DLWS_WITH_WOLFSSL=0 -DLWS_WITH_LIBEV=0 -DLWS_WITH_LIBEVENT=1
2018-05-03 10:49:36 +08:00
Andy Green
a01ad0dd20 hrtimer: add support for libevent and libev 2018-05-02 19:27:33 +08:00
Andy Green
9cce1874b0 context_destroy: figure out if anything still in event loop 2018-05-02 19:27:29 +08:00
Andy Green
bce8cca042 refactor: also migrate tls to the ops struct and private.h pattern
Several new ops are planned for tls... so better to bite the bullet and
clean it out to the same level as roles + event-libs first.

Also adds a new travis target "mbedtls" and all the tests except
autobahn against mbedtls build.
2018-05-02 12:10:36 +08:00
Andy Green
da0be64f68 minimal-raw-netcat
Adapt attack.sh to use it instead of OS netcat and fox various bugs that
exposed.
2018-05-02 08:46:16 +08:00
Andy Green
f1c56bc233 alpn: allow NULL alpn in role struct when scanning for match
https://github.com/warmcat/libwebsockets/issues/1262
2018-05-01 18:15:52 +08:00
Andy Green
f497562a62 refactor: change event lib minimal examples to serve as the test apps 2018-04-30 19:17:32 +08:00
Andy Green
0b52d92d12 refactor: remove more test apps and replace with minimal-examples 2018-04-30 09:16:04 +08:00
Andy Green
d37b383edc refactor: apply ops structs to event loop handlers 2018-04-29 10:44:36 +08:00
Andy Green
d05b408cde refactor: clean out test-apps 2018-04-29 08:35:24 +08:00
Andy Green
8d213f8295 refactor: isolate event_loop struct content same way as roles 2018-04-29 08:34:19 +08:00
Andy Green
91a47f4fab refactor: also constify CAPS usage of context info 2018-04-28 08:01:01 +08:00
Andy Green
6e1caa5c56 buflist: debug 2018-04-28 07:55:31 +08:00
kaleb-himes
65b68bdc70 wolfSSL updates 2018-04-28 07:55:27 +08:00
Andy Green
ac6c48d98f refactor: most preparation for -DLWS_ROLE_H1=0 2018-04-27 19:16:50 +08:00
Andy Green
16a907180c refactor: move all ah and http specific vars to http private and conditional inclusion from there 2018-04-27 15:20:56 +08:00
Andy Green
9c9ef9cea2 refactor: move everything related to HTTP_PROXY out of struct lws and private-libwebsockets.h into http role private 2018-04-27 14:36:10 +08:00
Andy Green
25e27d76d1 travis: refactor the yml and add smp-specific tests 2018-04-27 14:22:58 +08:00
Andy Green
7c0a2ae633 smp: h2 children must go on same tsi as parent
We can't have a situation where stream wsis inside an h2 connection
are serviced by different threads than the actual parent connection.
2018-04-27 12:49:42 +08:00
Andy Green
ae688609a0 qa: add 6 x h2load tests 2018-04-27 11:19:09 +08:00
Andy Green
82adc07c0a fix minimal-server-tls 2018-04-27 09:36:03 +08:00
Andy Green
c9fb42bb8f lws_get_vhost_listen_port 2018-04-27 09:36:02 +08:00
Andy Green
7ff8f023d1 context info struct: make lws usage all const 2018-04-27 08:37:20 +08:00
Andy Green
800cd40f88 client: support pipelining / h2 multi for POST
https://github.com/warmcat/libwebsockets/issues/1256
2018-04-27 07:25:45 +08:00
Andy Green
5d06f610a9 travis: h2spec integration 2018-04-26 15:27:02 +08:00
Andy Green
2e3ddeedb6 lws_get_peer_write_allowance: return lws_fileofs_t 2018-04-26 08:30:12 +08:00
Silas Parker
87bb121b47 cmake: avoid killing CMAKE_C_FLAGS
https://github.com/warmcat/libwebsockets/issues/1252
2018-04-25 16:56:23 +08:00
Andy Green
27e86e2641 cmake: allow setting LWS_ROLE_WS
You can build lws without support for ws, with -DLWS_ROLE_WS=0.

This is thanks to the role ops isolating all the ws-specific business
in the ws role.

Also retire more test apps replaced by minmal-examples.
2018-04-25 08:42:18 +08:00
Andy Green
8e9751e26e refactor: move out extension lws members into ws and establish role structs for vhost content 2018-04-25 06:53:30 +08:00
Andy Green
7b227eb333 autobahn fixes
This replaces the old test-app for echo with separate client and server
minimal versions.

The autobahn test script is made more autonomous and tests both
client and server.
2018-04-22 06:45:46 +08:00
Labunsky Artem
3459d4fe43 Removing rudimental n1 variable
There is no real need in n1, since it used in only at one place once.
Also it will prevent "variable ‘n1’ set but not used [-Werror=unused-but-set-variable]" error on build with -DLWS_MAX_SMP=N, where N > 1.

AG: also add LWS_MAP_SMP != 1 test to travis to catch this kind of thing sooner
2018-04-21 10:24:44 +08:00
Andy Green
4c5138eade proxy: allow use of at char in passwords
Applies to both http proxy and socks proxy

https://github.com/warmcat/libwebsockets/issues/1247
2018-04-20 10:49:29 +08:00
Andy Green
97e36d8901 minimal examples for Server Side Events 2018-04-20 07:15:42 +08:00
Andy Green
658c752998 lws_add_http_common_headers: provide WSI_TOKEN_HTTP_CONTENT_TYPE helper 2018-04-20 07:13:05 +08:00
Andy Green
3f683351b3 refactor: split out private role header content
Private header stuff specific to roles should go in the
role dir and only be included if the role is enabled for
build.

Only definitions related to lws core should go in the actual
private-libwebsockets.h
2018-04-20 07:13:05 +08:00
Andy Green
1d2094996e refactor: absorb other caches into buflist
1) Remove the whole ah rxbuf and put things on to the wsi buflist

This eliminates the whole detachability thing based on ah rxbuf
state... ah can always be detached.

2) Remove h2 scratch and put it on the wsi buflist

3) Remove preamble_rx and use the wsi buflist

This was used in the case adopted sockets had already been read.


Basically there are now only three forced service scenarios

 - something in buflist (and not in state LRS_DEFERRING_ACTION)

 - tls layer has buffered rx

 - extension has buffered rx

This is a net removal of around 400 lines of special-casing.
2018-04-20 07:13:05 +08:00
Andy Green
1bf30c8620 debug: with debug build and uv, repeat some closes
The closes have to complete after libuv returned to the
event loop.  So make sure to catch any problem that gets
introduced where another close entry in the meanwhile is
unsafe.

https://github.com/warmcat/libwebsockets/issues/1245
2018-04-19 16:16:49 +08:00
Andy Green
6059c965dd minimal-http-server-basicauth 2018-04-19 16:16:48 +08:00
Andy Green
ae4897f58a smp: add some overview docs on the related minimal example 2018-04-19 16:16:48 +08:00
Andy Green
a9390874c7 tcp keepalive: user TCP_USER_TIMEOUT on linux if extant
https://github.com/warmcat/libwebsockets/issues/1223
2018-04-19 16:16:48 +08:00
Andy Green
b9b100bdfd minimal-http-server-post: redirect does its own header write
https://github.com/warmcat/libwebsockets/issues/1218
2018-04-19 16:16:48 +08:00
Andy Green
8829c2f365 selftests
This converts several of the selftests to return a status in their exit code
about whether they 'worked'.

A small bash script framework is added, with a selftest.sh in the mininmal
example dirs that support it, and a ./minimal-examples/selftests.sh script
that can be run from the build dir with no args that discovers and runs all
the selftest.sh scripts underneath.

That is also integrated into travis and the enabled tests must pass now for
travis to pass.  Travis does not have a modern libuv so it can't run a
couple of tests which are nulled out if it sees it's running in travis env.
2018-04-19 16:16:48 +08:00
Andy Green
f0048acbee minimal: add switches and exit results 2018-04-19 16:16:48 +08:00
Andy Green
62af7934c8 rxflow buflist: handle forced service 2018-04-19 16:15:10 +08:00
Andy Green
654adaf82a spa: fix potential overrun 2018-04-19 16:15:10 +08:00
Andy Green
92277ad576 lws overview 2018-04-19 16:15:10 +08:00
Mark Adler
74b4775908 zlib: Avoid pre-decrement of pointer in big-endian CRC calculation
There was a small optimization for PowerPCs to pre-increment a
pointer when accessing a word, instead of post-incrementing. This
required prefacing the loop with a decrement of the pointer,
possibly pointing before the object passed. This is not compliant
with the C standard, for which decrementing a pointer before its
allocated memory is undefined. When tested on a modern PowerPC
with a modern compiler, the optimization no longer has any effect.
Due to all that, and per the recommendation of a security audit of
the zlib code by Trail of Bits and TrustInSoft, in support of the
Mozilla Foundation, this "optimization" was removed, in order to
avoid the possibility of undefined behavior.

d1d577490c.patch
2018-04-19 16:15:10 +08:00
Mark Adler
b807ccf261 Subject: zlib: Avoid shifts of negative values inflateMark
The C standard says that bit shifts of negative integers is
undefined.  This casts to unsigned values to assure a known
result.

e54e129940.patch
2018-04-19 16:15:10 +08:00
Andy Green
4b7144f763 lws_buflist
For h1 / ws, a combination of removing POLLIN wait and
stashing any unused rx lets us immediately respond to
rx flow control requests in a simple and effective way,
because the tcp socket is the stream.

But for muxed protocols like h2, that technique cannot
be used because we cannot silence the whole bundle of
streams because one can't handle any more rx dynamically.

There are control frames and content for other streams
serialized inbetween the flow controlled stream content.
We have no choice but to read to so we can see the other
things.  Therefore for muxed protocols like h2 and spdy,
rx flow control boils down to tx credit manipulation
on individual streams to staunch the flow at the peer.

However that requires a round trip to take effect, any
transmitted packets that were in flight before the tx credit
reduction arrives at the remote peer are still going to come
and have to be dealt with by adding them to the stash.

This patch introduces lws_buflist scatter-gather type
buffer management for rxflow handling, so we can append
buffer segments in a linked-list to handle whatever rx
is unavoidably in flight on a stream that is trying to
assert rx flow control.
2018-04-19 16:15:10 +08:00
Andy Green
7812ffabcc build: put back -O3 2018-04-19 16:15:10 +08:00
Andy Green
8d79eaf124 404 processing 2018-04-19 16:15:10 +08:00
Andy Green
65f87efca9 pipe2 where possible 2018-04-19 16:15:10 +08:00
Andy Green
aa816e98a9 alpn: assemble defaults from roles and allow override
Since new roles may be incompatible with http, add support for
alpn names at the role struct, automatic generation of the
default list of alpn names that servers advertise, and the
ability to override the used alpn names per-vhost and per-
client connection.

This not only lets you modulate visibility or use of h2,
but also enables vhosts that only offer non-http roles,
as well as restricting http role vhosts to only alpn
identifiers related to http roles.
2018-04-19 16:15:10 +08:00
Hasan-Jawaheri
f978ea8658 Fixed authorization condition in 101 server response 2018-04-19 16:15:10 +08:00
Andy Green
126be3ccf3 refactor role ops
This only refactors internal architecture and representations, the user
api is unaffected.
2018-04-11 13:39:42 +08:00
Andy Green
16e2f09710 refactor mode and states into roles and states 2018-04-06 12:22:19 +08:00
Andy Green
a2210d1bb1 docs: rearrange callback list in libwebsockets.h into sections 2018-04-06 12:22:19 +08:00
Andy Green
67cfdfaeb2 coverity 189199: messageboard: confirm vhd non-NULL 2018-04-06 12:22:05 +08:00
Andy Green
bf5457af69 coverity 185654: acme: confirm vhd non-NULL 2018-04-06 12:16:51 +08:00
Andy Green
d830185df9 coverity 189197: workaround false cov detect
There's nothing wrong with ignoring the ringbuf filling.

But coverity infers there is because elsewhere under other
circumstances we report it.
2018-04-06 11:31:24 +08:00
Andy Green
e9931680c8 sshd: fixes 2018-04-06 10:44:02 +08:00
Andy Green
9a51bd0a63 LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY 2018-04-06 10:38:04 +08:00
Andy Green
c8af76c07c minimal-ws-server-pmd-bulk: add echo 2018-04-06 10:38:04 +08:00
Andy Green
de4c1303f5 Coverity 189186: hrtimer calculation overflow 2018-04-06 10:38:04 +08:00
Andy Green
2b180b21ce Coverity 189188: cover for meaningless coverity warning 2018-04-06 10:38:04 +08:00
Andy Green
9586acb560 Coverity 189189: client: new openssl alpn handling off-by-one 2018-04-06 10:38:04 +08:00
Andy Green
a7db6e4fd4 Coverity 189190: client: deal with NULL piggyback wsi 2018-04-06 10:38:04 +08:00
Andy Green
3c9924bf73 Coverity 189191: client: cope with missing :status on h2 client 2018-04-06 10:38:04 +08:00
Andy Green
43f9a8de80 Coverity 189185: access_log: guarantee no null wsi vhost 2018-04-06 10:38:04 +08:00
Andy Green
ece243f961 Coverity 189187: hide coverity false positive 2018-04-06 10:38:04 +08:00
Andy Green
fddebfcfae QA 2018-04-06 10:38:04 +08:00
Andy Green
719f735309 minimal-http-client-post 2018-04-06 10:38:04 +08:00
Andy Green
a017c4b0eb h2: tx END_STREAM should only set HALF_CLOSED_LOCAL 2018-04-06 10:38:04 +08:00
Andy Green
af88a889d0 DROP_PROTOCOL: guarantee only one DROP per BIND 2018-04-06 10:38:04 +08:00
Andy Green
419a6af38d spa: determine final boundary
By itself the HTTP layer can track transaction sizes only with
content-length, including on POST.

However it's also possible for whatever logically interprets
the payload to determine its length, for example with multipart,
the multipart headers can do that job.

This allows the spa stuff to drive the content length tracking,
so lws can interpret multipart POST client payload without needing
an overall content-length.
2018-04-06 10:38:03 +08:00
Andy Green
2a9b6f54c6 client: h2
This adds h2 http support for the client api.

The public client api requires no changes, it will detect by
ALPN if the server can handle http/2, if so, it will use it.

Multiple client connections using the lws api will be mapped on
to the same single http/2 + tls socket using http/2 streams
that are serviced simultaneously where possible.
2018-04-06 10:38:03 +08:00
Andy Green
2d3fc52b73 h2: allow control of rx scratch buffer from vhost info 2018-04-06 10:38:03 +08:00
Andy Green
3038af5b07 hexdump: add some smaller helpers 2018-04-06 10:38:03 +08:00
Andy Green
caaf26c717 libuv: foreign loop detach doesn not require lws running the loop at all
This completely removes the loop self-running stuff.

Static allocations (uv_idle, timers etc) are referenced-counted in the context
same as the wsi are.  When lws wants to close, he first closes all his wsi, then
when that is completed in the uv close callbacks, he closes all of his static
uv handles.  When that is also completed in the uv callbacks, he stops the loop
so the lws context can destroy and exit.

Any direct libuv allocations in protocol handlers must participate in the
reference counting.  Two new apis are provided

 - lws_libuv_static_refcount_add(handle, context) to mark the handle with
   a pointer to the context and increment the global uv object counter

 - lws_libuv_static_refcount_del() which should be used as the close callback
   for your own libuv objects declared in the protocol scope.
2018-04-06 10:38:03 +08:00
Andy Green
7d466ba98f minimal-http-client-hugeurl 2018-04-06 10:38:03 +08:00
Andy Green
8f19a3fa9a network interface: defer bindings to absent network interfaces
Previously down network interfaces without an IPv4 address are
removed from the posix api that lists network interfaces.

That means if you bound a vhost listen socket to a particular
interface, it will fail at startup time.

This patch adds these vhosts to a list, starts the vhost without
a listen socket, and checks to see if the vhost's network interface
has appeared while the rest of lws is running.

If it appears, the listen socket is opened on the network interface
and the vhost becomes reachable.
2018-04-06 10:38:03 +08:00
Andy Green
e052edb14f minimal-http-server-libuv-foreign 2018-04-06 10:38:03 +08:00
Andy Green
b9c7f3df92 libuv: run loop to clean up per-pt objects before exiting 2018-04-06 10:38:03 +08:00
Andy Green
80d84896f1 minimal-http-server-form-post-file 2018-04-06 10:38:03 +08:00
Andy Green
3a020c1eab lws_filename_purify_inplace 2018-04-06 10:38:03 +08:00
Andy Green
764d0d3b45 minimal-http-server-form-get 2018-04-06 10:38:03 +08:00
Andy Green
020a8a2c38 minimal-http-server-form-post 2018-04-06 10:38:03 +08:00
Andy Green
cd30656e01 logging: reduce logs related to POST and spa 2018-04-06 10:38:03 +08:00
Andy Green
865b2439ce mingw: also workaround ENOTCONN 2018-04-06 10:38:03 +08:00
Andy Green
b45956fcb9 client: add alpn processing on mbedtls and openssl
This just supports "http/1.1" alpn and lets the client know it
can use keepalive earlier if it affirmitively negotiated
"http/1.1" on alpn.

mbedTLS wrapper needed a small adaptation to also allow
per-client-ssl control of the alpn negotiation list.
2018-04-06 10:38:03 +08:00
Andy Green
04e1661411 client: http1.1 pipelining 2018-04-06 10:38:03 +08:00
Andy Green
3647cd8968 minimal: client multi 2018-04-06 10:38:03 +08:00
Andy Green
bd6fc106d9 lws_write_ws_flags helper 2018-04-06 10:38:03 +08:00
Fabio Alessandrelli
94f3981bef mbedtls: wrapper: client: Force mbedTLS to attemp to verify cert
AG: unlike openssl, mbedtls does not load the system trust store.
So this change will make client tls operations that work OK on openssl fail on
mbedtls unless you provide the correct CA cert.

This allows lws to distinguish between untrusted CAs, hostname
mismatches, expired certificates.

NOTE: LCCSCF_ALLOW_SELFSIGNED actually allows for untrusted CAs, and
will also skip hostname verification. This is somewhat a limitiation of
the current lws verification process.

AG: improve error reporting up to the CLIENT_CONNECTION_ERROR argument
and add a note specific to mbedtls in the test client.  Adapt the test
client to note the CA requirement if built with mbedTLS.  Adapt the
minimal test clients to have the CAs available and use them if mbedTLS.
2018-04-06 10:38:03 +08:00
Andy Green
d5bb8ecbc6 minimal: raw udp 2018-04-06 10:38:03 +08:00
Andy Green
7cef6fcc7b udp 2018-04-06 10:38:03 +08:00
Andy Green
1820212724 lws_set_timer_usecs: change to usec resolution
This replaces the existing, unreleased lws_set_timer(wsi, secs) with
lws_set_timer_usecs(wsi, usecs).

wsi with a timer waiting are added to a linked-list sorted by the
timer trigger time.

1) poll() timeout (ie, poll wait) is trimmed to the nearest ms of the
first waiting timer if the default poll wait is longer than the
interval until the first waiting timer.

The linked-list of waiting timers is checked every entry and exit
from poll()... if no timers waiting or none reached their time
this costs almost nothing.

2) libuv: the earliest hrtimer is checked after every IO, again this
is costing nothing if the list head is NULL.  If the case there
are hrtimers on the list, it costs a getimeofday (a VDSO in linux)
and more only if any of the timers have fired.

In addition on entry to libuv idle, if there are any waiting hrtimers
on the list, a libuv timer is used to force a wake in case we stay
idle (the libuv timer has ms resolution).

3) libev: not implemented

4) libevent: not implemented

Warnings are logged in the api is used on an event backend without
support.  Patches welcome to add support similarly to libuv.
2018-04-06 10:38:03 +08:00
Andy Green
9cf641dece minimal: minimal-raw-adopt-tcp 2018-04-06 10:38:03 +08:00
Andy Green
feeca915b9 lwsws: fix inherited vhost info 2018-04-06 10:38:03 +08:00
Andy Green
eedee9c0d3 cmake: remove unused check on inttypes.h 2018-04-06 10:38:03 +08:00
Andy Green
7aa511d8c5 minimal: minimal raw vhost 2018-04-06 10:38:03 +08:00
Andy Green
03acd5a24d valgrind: context creation error path 2018-04-06 10:38:03 +08:00
Andy Green
b80cef6919 minimal: minimal-raw-file 2018-04-06 10:38:03 +08:00
Andy Green
a4161780df wss-over-h2: show feedback in test page if active 2018-04-06 10:38:03 +08:00
Andy Green
3e4a1f3b0e client: choose right vh client ssl_ctx 2018-04-06 10:38:03 +08:00
Andy Green
b04708178a minimal: http-server-tls 2018-04-06 10:38:03 +08:00
Andy Green
5ecec970b2 minimal: dynamic http server 2018-04-06 10:38:03 +08:00
Andy Green
2519ac9ced coverity 188316: supposed deadcode 2018-04-06 10:38:03 +08:00
Andy Green
5fe9335b36 coverity 188416: possibility of NULL iface 2018-04-06 10:38:03 +08:00
Andy Green
0b65112ec4 coverity 188317: more fiddling around NO_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
5acbb04b51 unixskt: add test server support and cleanup inode on exit 2018-04-06 10:38:03 +08:00
Andy Green
422cbf24bd align private LWS_NO_EXTENSIONS to be same as public LWS_WITHOUT_EXTENSIONS
This is just an internal mass change of LWS_NO_EXTENSIONS to
LWS_WITHOUT_EXTENSIONS to match the public name and eliminate
all instances of LWS_NO_EXTENSIONS.
2018-04-06 10:38:03 +08:00
Andy Green
80e3e723e3 coverity 188326: hide supposed dead code from coverity with NO_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
95f8328ffc coverity 188320: hide supposed dead code from coverity with NO_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
e157fcfe37 coverity 188316: hide supposed dead code from coverity with NO_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
b0b10001fe coverity 188318: try to hide fake unused var from coverity 2018-04-06 10:38:03 +08:00
Andy Green
396b42ac6d coverity 188310: mirror plugin: when reach max mirror instances release lock before disconnect 2018-04-06 10:38:03 +08:00
Andy Green
b9a3b808fb coverity 188319: hide logically deaf code from coverity when WITHOUT_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
313cbb5350 coverity 188323: hide logically deaf code from coverity when WITHOUT_EXTENSIONS 2018-04-06 10:38:03 +08:00
Andy Green
d237ac1b95 coverity 188312: hide logically deaf code from coverity when WITHOUT_EXTENSIONS
There's nothing wrong with this but coverity chokes on it.

Also 188317 "solved" by this.
2018-04-06 10:38:03 +08:00
Andy Green
775f7bce09 coverity 188313: client: avoid possibility of strlen on NULL cce 2018-04-06 10:38:03 +08:00
Andy Green
bfc8a5cb1b coverity 188325: client: require at least once vhost 2018-04-06 10:38:03 +08:00
Andy Green
5a8fca6f79 coverity 188322: confirm wsi->protocol non-NULL before CONNECTION_ERROR 2018-04-06 10:38:03 +08:00
Andy Green
da38a17a31 coverity 188321: reject wsi with NULL vhost earlier 2018-04-06 10:38:03 +08:00
Andy Green
ea8c44d799 coverity 188315: reject wsi with NULL vhost earlier 2018-04-06 10:38:03 +08:00
Andy Green
613ae7921a coverity 188314: reject wsi with NULL vhost earlier 2018-04-06 10:38:03 +08:00
Andy Green
3775ac9d2e coverity 169270: client: handle NULL protocol 2018-04-06 10:38:03 +08:00
Andy Green
0e8b3fed12 coverity 188324: dead code when no extensions 2018-04-06 10:38:03 +08:00
Andy Green
f63f4e56aa lwsws: change the example to not bind to lo
https://github.com/warmcat/libwebsockets/issues/1211
2018-04-06 10:38:03 +08:00
Andy Green
e4a3e8c4d4 LWS_WITH_MINIMAL_EXAMPLES: default-off build all minimal samples after lws for QA
This adds an lws cmake option that builds all the minimal examples as part of lws,
it's useful for QA.

It adds a macro to examples that depend on a particular lws configuration to understand
they should just null out their project definition in builds where the lws configuration
requirement is not met, and we are building as part of lws.

It also adapts all the example library additions to select the just-built-but-not-yet-installed
library in the case it is built as part of lws.  If built standalone, it now uses the cmake
platform-abstracted way to add the library requirement too.
2018-04-06 10:38:03 +08:00
Andy Green
876878a2af minimal: ws-client-tx and change dir names to match project name 2018-04-06 10:38:03 +08:00
Andy Green
49a694bd1e minimal: add helpers and adapt all users 2018-04-06 10:38:03 +08:00
Andy Green
b8fb8c5b15 minimal: multivhost + refactor 2018-04-06 10:38:03 +08:00
Andy Green
081c9f7678 minimal-http-server-libuv 2018-04-06 10:38:02 +08:00
Andy Green
db8cbb3b61 minimal-ws-broker 2018-04-06 10:38:02 +08:00
Andy Green
6232f5a2b7 minimal: refactor directories and add README.md 2018-04-06 10:38:02 +08:00
Andy Green
9db35aa1bf minimal-ws-proxy 2018-04-06 10:38:02 +08:00
Andy Green
a91ed1fa4c client: CONNECTION_ERROR: target at local protocol if possible
Until now LWS_CALLBACK_CLIENT_CONNECTION_ERROR handling could only
take place on protocols[0].

This patch changes LWS_CALLBACK_CLIENT_CONNECTION_ERROR to be sent
to the protocol the client connection was bound to... if nothing
better that is still protocols[0], but if you created the client
connection using info.local_protocol_name, it will now be sent to
the bound protocol handler instead.
2018-04-06 10:38:02 +08:00
Andy Green
3ed755e52d client: add local_protocol_name binding to info
In the case you are creating a client connection, there may be
no relationship between the ws protocol you want to bind to at
the server, and the local protocol name you want the wsi to
bind to at the client.

This introduces a new client info struct member .local_protocol_name,
if it is NULL then all is as before, otherwise it binds the client
wsi to the named protocol early in the process, and .protocol is used
for the negotiation with the ws server.

This allows you to bind client wsi to local protocol handlers that
don't share the name of the ws protocol the connection will try
to negotiate.
2018-04-06 10:38:02 +08:00
Andy Green
2b932c500f LWS_CALLBACK_CLIENT_CLOSED: split out client and server CLOSE callback
Until now LWS_CALLBACK_CLOSED has served the same for
client and server connections.  This introduces a new
LWS_CALLBACK_CLIENT_CLOSE which is sent on established
ws client connections, insread of LWS_CALLBACK_CLOSED.

LWS_CALLBACK_CLOSED continues to be sent when server
ws connections close.
2018-04-06 10:38:02 +08:00
Andy Green
302f8fad82 h2 ws: fixes against chrome 2018-04-06 10:38:02 +08:00
Andy Green
0e39e7f5c6 LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT: protect against zero length packet causing spin when deciding if tls or not tls
https://github.com/warmcat/libwebsockets/pull/1206
2018-04-06 10:38:02 +08:00
Peter Pentchev
1b5d772c4a Subject: echo client: Fix an off-by-one error in --times handling 2018-04-06 10:38:02 +08:00
Peter Pentchev
2c93b25c73 Subject: echo: exit if --times is specified 2018-04-06 10:38:02 +08:00
Andy Green
6055411928 minimal-ws-client 2018-04-06 10:38:02 +08:00
Andy Green
7ad8332838 minimal-ws-server-threads 2018-04-06 10:38:02 +08:00
Andy Green
61376bd734 minimal: updates 2018-04-06 10:38:02 +08:00
Andy Green
aae2c24678 lws_snprintf 2018-03-12 09:28:42 +08:00
Andy Green
5fc2598eac smp: force cancel on pt that was assigned new wsi 2018-03-11 18:07:16 +08:00
Andy Green
ad5dbda120 access log: only produce when vh log file set 2018-03-11 18:07:16 +08:00
Andy Green
862ab64d62 vhost: handle creation failure without unbalanced call of PROTOCOL_DESTROY 2018-03-11 18:07:16 +08:00
Andy Green
dc15a77dfa openssl: avoid warning on versions with ECDH header 2018-03-11 18:07:16 +08:00
Andy Green
cb70b6e633 win32: remove snprintf mangling
Everything in lws outside esp32 was changed to use lws_snprintf() a while ago.

This fixes a couple of stragglers and removes the preprocessor mangling.
2018-03-09 08:49:59 +08:00
Andy Green
7f830195d6 docs: add note on lws_http_client_http_response about when to call it
https://github.com/warmcat/libwebsockets/issues/1202
2018-03-09 08:38:48 +08:00
Joe Littlejohn
e333d1a751 Add 'HS: ws upgrade unauthorized' on receiving 401
In the case that the connection cannot be established because the caller
is unauthorized, it's likely they have to do something to gain
authorization before retrying. This change introduces a new message that
can be checked for to understand more about why the connection has
failed to establish.

Closes #1200
2018-03-08 09:02:53 +08:00
Joe Littlejohn
f1ad1c6b1a Check status before checking for Sec-WebSocket-Accept
If the server rejects the attempt to establish a connection by returning
a response status other than 101, then it will not include the
Sec-WebSocket-Accept header. We need to check for 101 status (and return
an appropriate error message) before looking for the accept header.

See #1200
2018-03-08 09:02:53 +08:00
Andy Green
b490079b47 vhost: add 404 handler url option
This allows you to set a 404 handler URL on a vhost.

The necessary user code looks like...

    info.error_document_404 = "/404.html";

... at vhost-creation time.

In the existing lws_return_http_status() api, if it sees
the vhost has an "error_document_404" path set and that
we are trying to report a 404, it changes the action
instead to a redirect to the error_document_404 path.

The redirect target is returned using 404 status code.

If the redirect target doesn't exist, then it falls back
to just reporting the simple canned 404.
2018-03-08 09:01:14 +08:00
Andy Green
46f10cade7 gcc 8: fixes for warnings on sshd.c 2018-03-08 09:01:10 +08:00
Andy Green
1aed8c8127 h1: header parsing optimizations 2018-03-07 18:19:10 +08:00
Andy Green
b49630e515 minimal: add Wall and cleanups 2018-03-07 07:05:39 +08:00
Andy Green
388c0677ee minimal-examples: add ws pmd example 2018-03-06 09:39:27 +08:00
Andy Green
4d36bc1f46 smp: include pthreads with LWS_MAX_SMP gt 1 2018-03-06 06:37:56 +08:00
Andy Green
0d5ca2d87b pthreads: more locking 2018-03-05 16:49:28 +08:00
Andy Green
cce9711653 minimal-examples: http server 2018-03-04 16:56:40 +08:00
Andy Green
d39ecd814a smp: fixes 2018-03-02 19:19:36 +08:00
Andy Green
2203a5f019 smp: vhost lock 2018-03-02 19:19:35 +08:00
Andy Green
e5150b7cd9 mirror: use smp pthreads wrappers
This allows mirror protocol to work in the case of multiple
service threads.  If LWS_MAX_SMP == 1 though, the additional
lock members and locking code reduces to nothing.
2018-03-02 19:19:35 +08:00
Andy Green
0d8b11d250 smp: add user pthread helpers that are NOP for LWS_SMP_MAX == 1 2018-03-02 19:19:35 +08:00
Andy Green
3de2e9aa8a pthreads: build test server based on pthreads.h availability
... and fix signed / unsigned comparison
2018-03-02 09:01:22 +08:00
Andy Green
658b86ed9f esp32: fix basic auth 2018-03-01 10:27:12 +08:00
Andy Green
7bc6383759 LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT: fixes after tls refactor
https://github.com/warmcat/libwebsockets/issues/1191
2018-02-27 07:48:25 +08:00
Andy Green
e02a999863 client: stop stash leak on error path
https://github.com/warmcat/libwebsockets/issues/1187
2018-02-26 08:35:50 +08:00
Andy Green
a0581a926b esp32: map basic auth to nvs 2018-02-24 08:14:17 +08:00
Martin Milata
27115c3258 Subject: [PATCH] do not pass AI_CANONNAME to getaddrinfo()
In some cases this flag causes libc to do reverse DNS lookup the result
of which is not used.
2018-02-23 07:49:05 +08:00
Andy Green
341f280662 README.codng.md: add libevent as a peer and suggest avoid libev 2018-02-21 07:42:11 +08:00
Andy Green
714ba5c9df async POLLOUT: pollfd change during end of POLLOUT handling change to explicit event reenable
On ESP32 where the timer is on a different thread, otherwise
threw occasional back-to-back writes.
2018-02-20 12:07:10 +08:00
Andy Green
faef0b047f h2: clear back-to-back detection when looping through h2 child POLLOUT
The do {} while() checks each time for choked on the parent connection.
2018-02-20 07:19:38 +08:00
Andy Green
ff2f5f601d libev: libevent conflicts at buildtime 2018-02-19 10:42:31 +08:00
Andy Green
73b0147b40 libev: move accept into vh 2018-02-19 10:19:40 +08:00
Andy Green
640620dbdc mbedtls: adapt to deprecated api in 2.7 2018-02-19 06:24:30 +08:00
Andy Green
67db15a84a h2: POST: restrict block handling to end of frame 2018-02-19 05:31:14 +08:00
Andy Green
a663aefebd test-server-v2.0: remove comment about now filescope init memset 2018-02-14 08:44:58 +08:00
Andy Green
99154ae223 mbedtls: dont build lws_get_ssl 2018-02-13 10:29:49 +08:00
Andy Green
afafc135cd h2: move window mangament into DATA body handling 2018-02-06 09:58:48 +08:00
Andy Green
4e9d13bf50 openssl: defeat spin on failed accept 2018-02-06 07:58:13 +08:00
Andy Green
9ac452a87d appveyor: add build ID to artifact paths 2018-02-06 07:14:38 +08:00
krismattheus
5366a766f4 windows: add version resource 2018-02-06 07:07:28 +08:00
Andy Green
34255ff54e h2: explicit credit on connect 2018-02-05 10:54:18 +08:00
Andy Green
5c00887054 h2: bump window for network stream too 2018-02-04 08:34:45 +08:00
Andy Green
d6de3de732 h2: fix warning on window update check 2018-02-03 15:04:02 +08:00
Andy Green
113d623957 internal close api: add caller id 2018-02-03 13:54:29 +08:00
Andy Green
0c55e5ad4c h2: extend network stream timeout during body processing 2018-02-03 13:53:45 +08:00
Andy Green
6072829df6 h2: scale rx window according to frame size 2018-02-03 13:52:17 +08:00
Andy Green
33d373c300 esp32: defeat scan during upload 2018-02-03 13:49:49 +08:00
Andy Green
9556ad77fb esp32: bump ssid and pw fields to 64 2018-02-02 08:09:08 +08:00
Andy Green
948408b36a appveyor: bintray 2018-01-31 06:54:39 +08:00
Andy Green
8e9871d995 remove u_int64_t
https://github.com/warmcat/libwebsockets/issues/1167
2018-01-30 09:28:13 +08:00
Andy Green
93ac0b671c appveyor: make sure libwebsockets.h in the artifact as well 2018-01-30 08:41:24 +08:00
Andy Green
65a67d72f1 esp32: adapt to esp-idf mdns changes 2018-01-29 15:17:33 +08:00
Andy Green
44d2664ece appveyor: add includes to artifacts 2018-01-29 09:19:14 +08:00
Andy Green
1d9d04ff9d LWS_WITH_DISTRO_RECOMMENDED
Idea from Per Bothner
https://libwebsockets.org/pipermail/libwebsockets/2018-January/003635.html
2018-01-27 08:01:33 +08:00
Andy Green
1b43ed1cba mbedtls wrapper: check for malloc.h before using
https://github.com/warmcat/libwebsockets/issues/1163
2018-01-26 07:22:35 +08:00
Andy Green
a8a484eb53 back-to-back: clear pending flag for all variants of server write 2018-01-25 09:00:08 +08:00
mulander
b3b6bcdf64 READMEs: CHANGE callback replaces SET/CLEAR_MODE
LWS_CALLBACK_SET_MODE_POLL_FD and LWS_CALLBACK_CLEAR_MODE_POLL_FD no longer exist.

Updating the documentation to reflect that and point to LWS_CALLBACK_CHANGE_MODE_POLL_FD.
2018-01-24 08:24:04 +08:00
Andy Green
bd3d8d9de0 close: add LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION allowed to send
https://github.com/warmcat/libwebsockets/issues/1159
2018-01-23 07:01:21 +08:00
Sungtae Kim
93f904f601 libevent: fix memory leak
- Added event free function for accept.
2018-01-17 09:23:37 +08:00
Andy Green
8c07636dfd lwsgs: update messageboard to avoid back-to-back writes 2018-01-14 20:57:34 +08:00
Andy Green
87aff27ab1 lwsgs: handle NULL pss at ADD_HEADERS 2018-01-14 20:37:45 +08:00
Andy Green
a3bbb0a374 interpreter: support unchunked mode when h2 connection 2018-01-14 20:09:41 +08:00
Andy Green
673e3aa549 attack.sh fixes 2018-01-14 11:32:45 +08:00
Andy Green
93bc409ca1 POST: handle http/1 pipelined after body
Re-use wsi->preamble_rx to also hold leftover rx after dealing with POST
body.  Ensure ah->rx is always big enough to cope with what may have
been read into the pt->serv_buf.

Update the check for forced needed to also accept non-NULL wsi->preamble
as well as ah->rxpos != ah->rxlen as indication forced needed.

Disable autoservice on ah reset during transaction completed... it may
close the wsi underneath us when it sees and processes the pending
wsi->preamble_rx recursively otherwise.
2018-01-14 10:25:43 +08:00
Andy Green
0e24969f53 more_rx_waiting: replace by check ah rxpos rxlen and wsi preamble_rx
No need to hold the state when we can check it directly
2018-01-14 10:24:56 +08:00
Andy Green
5a1f0b7c4f excessive ah hold: explicitly detach ah 2018-01-13 05:14:43 +08:00
Andy Green
715847cc8c generic-sessions: drop after 404 2018-01-12 10:26:24 +08:00
Andy Green
b13bff84fa h2: avoid problems with ffox by setting max hpack size to 65536 2018-01-12 10:19:46 +08:00
Andy Green
5494128faf test-echo: handle WRITABLE callback when nothing to send 2018-01-12 09:52:14 +08:00
Andy Green
3846986851 excessive ah hold: stop trying to list headers at sentinel
One header earlier than before

https://github.com/warmcat/libwebsockets/issues/1152
2018-01-12 08:30:23 +08:00
Gabriele Mondada
efbd6c1b50 win32: fix comparison between unsigned and signed integer 2018-01-08 06:37:07 +08:00
Andy Green
3eae5b1363 openssl: treat SYSCALL at connect as always fatal 2018-01-08 06:35:12 +08:00
Andy Green
0c40f0b7bd ev: signed-unsigned warning in test app 2018-01-08 06:17:12 +08:00
Andy Green
5451457738 cgi: log CGI timeout clearly with extra related info 2018-01-04 12:07:56 +08:00
Andy Green
6b3b32e98a ACCESS_LOG: log with NOTICE if no file set up 2018-01-04 10:48:43 +08:00
Anzey
08d36dbe43 qnx: add support
https://libwebsockets.org/pipermail/libwebsockets/2018-January/003600.html
2018-01-04 10:43:20 +08:00
Anzey
a903d73ab7 lejp-conf: support builds without TLS 2018-01-04 10:42:07 +08:00
Andy Green
2e4ac9defe lejp: fix float
https://libwebsockets.org/pipermail/libwebsockets/2018-January/003599.html
2018-01-04 10:28:06 +08:00
Andy Green
48be844e5b travis: use trusty 2018-01-03 09:28:42 +08:00
Andy Green
265db34286 close: only tell user closed once 2018-01-03 09:22:32 +08:00
Andy Green
cfb076c8a0 hs: close immediately if negotation failed 2018-01-03 09:11:27 +08:00
Andy Green
15820da8ba docs: spell out how to set the default protocol when client doesnt give any 2018-01-03 08:29:29 +08:00
Andy Green
f95f689146 libevent test server Werror sign-compare
https://github.com/warmcat/libwebsockets/issues/1131
2017-12-30 08:47:16 +08:00
Andy Green
5a6764f218 after excessive ah hold detect restart scan
ah is potentially freed after the close
2017-12-29 13:30:37 +08:00
Andy Green
4cee34c0d0 h2: adapt oversize POST test now we handle blocks in parser not byte-at-a-time 2017-12-29 02:41:05 +08:00
Andy Green
b94091130b sshd: support async exec or shell close 2017-12-27 10:02:34 +08:00
Sergio Hernández
f3c6fac3cb Detect dead socket on iOS when server closed connection 2017-12-27 10:02:34 +08:00
Andy Green
e8e448df3e plat optee: updates 2017-12-20 10:44:21 +08:00
Andy Green
350af2394c gencerts: fix install 2017-12-20 10:42:39 +08:00
Andy Green
e7c7794cd4 treat write failed as permanently fatal 2017-12-20 10:42:31 +08:00
angelfromhell
1e7f5b5b2b pthreads: fix context destroy api ref 2017-12-20 10:42:23 +08:00
Andy Green
d58d749b30 acme: adaptations through plat for esp32 2017-12-11 13:30:12 +08:00
Alexander Couzens
d7b6c04aaf CMakeLists: build reproducible by default
Using the build user and build hostname as part of the git hash breaks
reproducible builds. Make this part optional, but build reproducible by
default.
2017-12-10 08:48:09 +08:00
Andy Green
8ff7dce46d sshd: protect against NULL kex 2017-12-08 06:26:54 +08:00
Andy Green
248b089ad4 h1: confirm POLLOUT again before starting next pipelined transaction 2017-12-07 18:53:25 +08:00
Andy Green
83af0716c5 h2: ensure only one path to close
Add a flag to protect the case both the recursed lws_read()
and the outer, grandfather lws_read() don't both try to
close the wsi.
2017-12-07 18:53:25 +08:00
Andy Green
1da0197798 truncated: detect and flag any write on a wsi that could have pending truncated 2017-12-07 10:19:48 +08:00
Andy Green
9039b7c9c6 client: convert stash to exactly sized mallocs 2017-12-07 10:19:20 +08:00
Andy Green
db04a40b5c lws_timed_callback_vh_protocol 2017-12-07 10:19:15 +08:00
Andy Green
7d59122b5f lws_set_timer
This adds a new api lws_set_timer(wsi, secs), which schedules
a callback LWS_CALLBACK_TIMER secs seconds into the future.

The timer can be continuously deferred by calling lws_set_timer()
again before it expires.

Calling lws_set_timer(wsi, -1) cancels any pending timer.
2017-12-05 20:14:00 +08:00
Andy Green
8487279e64 plat: remove esp8266
ESP32 module price is now within range of 8266 price.

ESP32 capability and OS support is hugely better than 8266,
we can support mbedtls tls, http/2 etc with ESP32.

I'm no longer testing on ESP8266... there's no more
user traffic... it's time to go.
2017-12-01 11:37:36 +08:00
Andy Green
2643775f18 mbedtls: genralize cert template for arbitrary key sizes 2017-12-01 11:37:36 +08:00
Andy Green
e83860d1bc plat: add extra helpers hiding ESP32 quirks 2017-12-01 11:37:36 +08:00
Andy Green
91a821c793 esp32: align with esp-idf changes
LWIP_SOCKET_OFFSET is now nonzero, which I handled a while back.

But the C api support for it is broken in esp-idf.

select() takes unmodified fds, but FD_SET / FD_ISSET etc must have the
offset subtracted on their args.

With this we are working on current HEAD esp-idf.
2017-12-01 11:37:36 +08:00
Andy Green
f6de7465ec lws_compare_time_t: conceal time discontiguities on all platforms
This provides platform-independent support for time discontiguities.

On embedded without battery RTC, commonly we only get time after
NTP completes.  This makes the cert checking happen when we have
a reasonable time and introduces lws_compare_time_t() to correctly
compare time_t s that may sit on either side of a time discontiguity.
2017-12-01 11:37:36 +08:00
Andy Green
e1cdb1f328 close: make sure all paths capture original state 2017-12-01 11:37:36 +08:00
Tomas Chmelar
3031666dc7 parser: add x-auth-token 2017-12-01 11:37:36 +08:00
Andy Green
6c484b2b31 h2: provide parser buffer at a time 2017-12-01 11:37:35 +08:00
Andy Green
2a30cd8ba7 h2-ws-draft-02
This implements:

https://www.ietf.org/id/draft-mcmanus-httpbis-h2-websockets-02.txt

it's untested since no compatible clients/browsers available yet.
2017-12-01 11:37:35 +08:00
Andy Green
a4148a13ba refactor-eliminate-lws-union
The union used to make a lot of sense to save space between
mutually exclusive modes.  But the fact the http2 struct
contains the http1 struct as well as it appearing in the
union means the http1 struct belongs outside the union.

This patch

 - eliminates the union
 - puts the http_related struct directly in struct lws
 - removes http_related from h2
 - puts h2 directly in struct lws if enabled for build
 - changes ws to be a pointer, allocated if we upgrade to ws
   (the ws part contains a 135 byte char array for ping / close)


Again all of this is entirely private / internal and doesn't affect
any apis.
2017-12-01 11:37:35 +08:00
Andy Green
df46d8827c refactor-struct-lws-hdr
Migrate what used to be in lws_hdr_related into either the ah
or the wsi, and eliminate it along with the three different ways
used to access things inside it.

Eg wsi->u.hdr.ah and wsi->u.http.ah become wsi->ah

These changes are internal-only, in private-libwebsockets.h and lib.
2017-12-01 11:37:35 +08:00
Alexey Reznichenko
93846135f2 Add a flag to disable CMake exports
Add LWS_EXPORT_LWSTARGETS (defaults to ON) that can be used to disable
  CMake export targets as well as install export for libwebsockets.

AG: add _WITH_
2017-12-01 11:37:35 +08:00
Andy Green
5b74d7108b gcov: add in cmake and adapt defaults
1) Introduce LWS_WITH_GCOV to build with gcc / clang coverage instrumentation.

$ cd build
$ make clean && rm -f `find . -name "*.gcno" -o -name "*.gcda"` && make -j16 && sudo make install && sudo /usr/local/bin/libwebsockets-test-server -s
...
$ gcov `find . -name *.c.gcno | grep -v test-apps` -b | sed "/\.h.\$/,/^$/d"

The above are available in two helper scripts

 - scripts/build-gcov.sh
 - scripts/gcov.sh

2)

CMake defaults changed:

 - LWS_WITH_ZIP_FOPS: OFF
 - LWS_WITH_RANGES: OFF
 - LWS_WITHOUT_EXTENSIONS: ON
 - LWS_WITH_ZLIB: OFF

New CMake controls that default-OFF:

 - LWS_WITH_GENHASH
 - LWS_WITH_GENRSA

these are implied by LWS_WITH_JWS (which is implied by LWS_WITH_ACME)

3) rename ./lib/tls/XXX/server.c and client.c to XXX-server.c / XXX-client.c.

This is because gcov dumps its results using the .c filename part only,
the copies overwrite each other if there are different .c files in the tree
with the same filename part.

4) Add onetime test-client mode and test to ./test-apps/attack.sh

5) Add gcov howto in READMEs/README.build.md using attack.sh
2017-12-01 11:37:35 +08:00
Andy Green
516001db8a esp32-selfsigned 2017-12-01 11:37:35 +08:00
Ben Delarre
946bfa2f18 Uses PROJECT_NAME instead of DIRNAME for binary filename 2017-12-01 11:37:35 +08:00
Ben Delarre
3db1ce0953 Esp32: allow build on windows 2017-12-01 11:37:35 +08:00
Ben Delarre
3160605462 docs: whitespace and add existing READMEs to doxygen list 2017-12-01 11:37:35 +08:00
Ben Delarre
dcf5a83ee4 esp32: move check for xxd and genromfs from CMake to esp32.mk 2017-12-01 11:37:35 +08:00
Andy Green
37e8ee2c00 windows: CHECK_FUNCTION_EXISTS workaround 2017-12-01 11:37:35 +08:00
Andy Green
c5f6d180dd mbedtls: handle NET_SEND_FAILED
Under the condition the associated client went away (turn off WLAN at the
client), we can spin forever mistaking NET_SEND_FAILED for WANT_WRITE,
which was also true.  This makes sure we understand that was fatal
immediately.
2017-12-01 11:37:35 +08:00
Andy Green
b06665b851 mbedtls: improve SNI for client certs 2017-12-01 11:37:35 +08:00
Andy Green
ad07d95026 cleanups 2017-12-01 11:37:35 +08:00
Andy Green
79d2038fdf esp32 fixes
This add support for a platform socketfd offset.
2017-12-01 11:37:35 +08:00
Andy Green
3ec7c1ab21 ACME client plugin
This adds support for a plugin that can be attached to a vhost
to acquire and maintain its TLS cert automatically.

It works the same with both OpenSSL and mbedTLS backends, but
they can't share auth keys, delete the 'auth.jwk' file as it is
in the example JSON when switching between libs
2017-12-01 11:37:35 +08:00
Andy Green
813b019bd1 lws_tls_server_certs_load: separate to allow cert attach later 2017-12-01 11:37:35 +08:00
Andy Green
a5514d2b2b LWS_SERVER_OPTION_IGNORE_MISSING_CERT 2017-12-01 11:37:35 +08:00
Andy Green
0c254aba87 tls: add csr generation 2017-12-01 11:37:35 +08:00
Andy Green
713aab17f8 additional vhost member accessors 2017-12-01 11:37:35 +08:00
Ralph Lessmann
33b40e0ae4 mingw: fixes 2017-12-01 11:37:33 +08:00
Andy Green
0724f8982a h2: fix multiple cookie header handling
Firefox 57 at least still sends multiple cookie: headers over h2.
Correctly aggregate them with a ';' delimiter.
2017-12-01 11:12:37 +08:00
Andy Green
53bbc6be33 cmake: dont build ssh stuff if no SSL enabled 2017-11-28 07:57:36 +08:00
Pavel Shramov
66916394ed lib: Spelling fixes based on debian packaging 2017-11-28 07:46:22 +08:00
Andy Green
2b30c82b9e lejp: dont underflow with anonymous braces 2017-11-26 19:17:11 +08:00
Andy Green
a76e9aad11 lejp: handle empty arrays 2017-11-26 19:17:11 +08:00
Andy Green
09f59ba3b8 lwjp-conf: quieten init logging 2017-11-26 19:17:11 +08:00
Andy Green
1f37ec0be9 add selfsigned cert generation api 2017-11-26 19:17:11 +08:00
Andy Green
a798db0e2b vhost: check cert validity dates
After startup, and once per day, check the validity dates on our ssl certs,
and broadcast callbacks with the information so interested plugins can
know.

If our clock is < May 2016, we don't try to judge the certs, because clearly
we don't know what time it is.
2017-11-26 19:17:10 +08:00
Andy Green
fb4397f841 cleanups 2017-11-26 19:17:10 +08:00
Adam Wolk
2befafca77 cmake: only generate test cert when SERVER and TESTAPPS enabled
apart from LWS_WITHOUT_TEST_SERVER the test server will also not be
built if LWS_WITHOUT_TESTAPPS or LWS_WITHOUT_SERVER is defined,
we update the CMake configuration to skip cert generation and
installation in those cases.

Signed-off-by: Adam Wolk <adam.wolk@koparo.com>
2017-11-26 19:17:06 +08:00
Andy Green
0ed131e6a7 post-demo: some platforms cant handle NULL vsnprintf strings 2017-11-26 19:16:58 +08:00
Petar Paradzik
0060af62c5 mbedtls: add support for optional peer certificate
Check for LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED...

AG: Fix missing stanza needed to confirm client cert needed at all

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-11-26 19:16:17 +08:00
Petar Paradzik
121d5d5940 mbedtls: client cert workaround
https://github.com/warmcat/libwebsockets/pull/1075#issuecomment-340730880
2017-11-26 19:16:17 +08:00
Petar Paradzik
a552de400a ssl: add support for checking cert existance and verification
Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-11-26 19:16:17 +08:00
Petar Paradzik
2f7bd10487 Subject: ssl: fix source and destination overlap in strcpy
Use memmove instead...

AG add explanation for union name[] member length.

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-11-26 19:16:17 +08:00
Petar Paradzik
939436b793 Subject: ssl: fix x509 memory leaks
Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-11-26 19:16:17 +08:00
Andy Green
00ffebfd24 tls: add x509 query api
This adds a single api on lws that allows querying elements from the
peer certificate on a connection.

The api works the same regardless of the TLS backend.
2017-11-26 19:16:17 +08:00
Andy Green
41d1326da0 appveyor artifacts debug 2017-11-26 19:16:17 +08:00
Andy Green
be525cb624 appveyor: Enable 64-bit build with OpenSSL and HTTP2
Fix warnings found from that

Introduce lws_ptr_diff(head, tail) helper to normalize
pointers to char *, do the subtraction and cast the
result to int.
2017-11-26 19:16:17 +08:00
Andy Green
ae8915861a client: handle bio_create failing
Plus some line length cleaning
2017-11-26 19:16:17 +08:00
Andy Green
1d5bd23268 bsd: support foreign thread event changes while in poll wait
https://github.com/warmcat/libwebsockets/issues/314
2017-11-26 19:16:17 +08:00
Andy Green
74fddbc09e cancel_service: change to event-loop agnostic implementation
- Add platform helpers for pipe creation.

 - Change the direct-to-fds implementation to create a wsi for each
   pt and use the normal apis to bind it to the event loop.

 - Modifiy context creation and destroy to create and remove the
   event pipe wsis.

 - Create the event pipe wsis during context create if using the
   default poll() event loop, or when the other event loops start
   otherwise.

 - Add handler that calls back user code with
   LWS_CALLBACK_EVENT_WAIT_CANCELLED

This patch allows you to call `lws_cancel_service(struct lws_context *context)`
from another thread.

It's very cheap for the other thread to call and is safe without
locking.

Every use protocol receives a LWS_CALLBACK_EVENT_WAIT_CANCELLED from
the main thread serialized normally in the event loop.
2017-11-26 19:16:17 +08:00
Andy Green
97cd393649 cmake: refactor options into groups and move to top of file 2017-11-26 19:16:17 +08:00
Andy Green
c2488dd4a3 client-cert-required 2017-11-26 19:16:17 +08:00
Andy Green
fa73d31558 docs: update discussion in test-apps README about plugin inclusion 2017-11-26 19:16:14 +08:00
Andy Green
4ff8e866d5 docs: fixup links broken by move to READMEs 2017-11-26 19:16:06 +08:00
Andy Green
b1934d6b64 client-certs: add related docs 2017-11-26 19:07:12 +08:00
Andy Green
dbb6c34cf2 RFC7638: JWK thumbprint 2017-11-26 19:07:12 +08:00
Andy Green
278e520479 JWK + JWS: JSON Web Keys RFC7517 + Signatures support RFC7515
This adds some optional apis around JSON Web Keys and JSON Web Signatures.

HMAC SHA256/384/512 and RSA + SHA256/384/512 are supported.

This also add a new "LWS_WITH_SELFTESTS" that enables the selftests.
2017-11-26 19:07:12 +08:00
Andy Green
c32a22c20b lws-genrsa: add resuable backend-independent rsa decrypt in tls/ and migrate sshd to it 2017-11-10 16:56:44 +08:00
Andy Green
dd3e5619cb lws_genhash: add HMAC
Like the simple hashes, these work the same regardless of if the backend is
OpenSSL or mbedTLS.

Also move into ./lib/tls/ and split into two specific to mbedtls or openssl
backends.
2017-11-10 16:56:44 +08:00
Andy Green
aebf187b98 base64: add decode variant that uses in_len instead of NUL term 2017-11-10 16:56:44 +08:00
Andy Green
2639b276f4 base64: add URL encode variant and allow decode of it
The URL encode variant is the same, except + is coded as -, and / is coded as _
to avoid urlencoding when the base64 is used in situations that are urlencoded.
2017-11-10 16:56:44 +08:00
Andy Green
46ee0713de hpack: pseudoheader check improvement 2017-11-10 11:01:25 +08:00
Andy Green
6bc92f7592 wrapper: untrash partial sends before WANT
1) The original wrapper logic for accounting for partial
sends on mbedtls is broken... adapt it

2) mbedtls has an additional restriction you MUST come
back to retry with EXACTLY the unsent part that you
originally asked for, or it loses coherency in the TLS
tunnel.

3) the wrapper is inconsistent between read WANT state
dynamically reported from mbedtls apis and the
SSL_want() api results.  Check both on read.
2017-11-10 10:59:58 +08:00
Andy Green
7e4c67e29d h2: never send more than file length 2017-11-10 10:59:58 +08:00
Andy Green
78ad6f9705 hpack: dont reinterpret lws index for idx hdr 6 inc 2017-11-10 10:59:58 +08:00
Andy Green
c7b536764f coverity-fixes 2017-11-10 10:59:58 +08:00
Andy Green
8c35e14965 test-apps: mirror echo mode 2017-11-05 07:09:06 +08:00
Andy Green
b119f2c471 service: only apply trunc holds rx rule for http2 2017-11-05 07:08:50 +08:00
Andy Green
91ed21d487 rxflow: fix second draining path 2017-11-05 07:08:41 +08:00
Andy Green
7e1c8876e6 service: stop trying to find service TID after first time 2017-11-05 07:08:29 +08:00
Andy Green
fac510fbc2 lws_ring: add lws_ring_dump() api 2017-11-05 07:08:20 +08:00
Andy Green
95f1cd6ea8 WITH_STATS: fix signed build warnings 2017-11-05 07:07:16 +08:00
Andy Green
a1ce8feba2 mirror: default to empty name if mirror with no arg 2017-11-03 11:48:05 +08:00
Andy Green
dba00cff6f mirror: fix spin on close 2017-11-03 11:47:57 +08:00
Andy Green
462e449cd6 lws_hdr_copy: protect against garbage 2017-11-02 08:11:06 +08:00
Andy Green
d30597f728 ah: increase timeout sanity check to ah_idle + 60 2017-10-31 07:02:51 +08:00
Andy Green
7594bc585c win: move LEAN_AND_MEAN before tls includes
It's always mean... we define LEAN_AND_MEAN in libwebsockets.h
but we include libwebsockets.h partway through private-libwebsockets.h.

Reorder for https://github.com/warmcat/libwebsockets/issues/1080
2017-10-31 06:45:03 +08:00
Andy Green
491fd183f8 lws_timingsafe_bcmp 2017-10-28 13:24:41 +08:00
Petar Paradzik
413631632b ssl: fix segfault when using SSL_CTX that is not created
vhost->ssl_ctx is created in 'lws_tls_server_vhost_backend_init', but it
is used before in 'lws_tls_server_client_cert_verify_config' when
calling SSL_CTX_set_session_id_context...

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-10-26 18:55:12 +08:00
Andy Green
5a90bb36d1 lejp: add test app to parse stdin 2017-10-26 18:55:12 +08:00
Andy Green
f9421f084b lejp: integrate header into libwebsockets.h 2017-10-26 18:55:11 +08:00
Andy Green
505a3fc1fc windows: finally fix gzip redefinition warnings 2017-10-26 18:55:11 +08:00
Andy Green
d286e990f0 windows: fix missing declatations for open etc
Due to windows needing nonstandard include io.h for
standard apis...
2017-10-26 18:55:11 +08:00
Andy Green
52d82ad83a cgi: make sure compiler cant see uninitialized use 2017-10-26 18:54:52 +08:00
Andy Green
5a0b5299d2 lejp: use explicit signed char return 2017-10-25 07:54:18 +08:00
Andy Green
4afeefbb3c build: strict-aliasing on old compiler workaround
https://github.com/warmcat/libwebsockets/issues/1067
2017-10-25 07:54:18 +08:00
Andy Green
1c70181ca2 build: enable signed vs unsigned warnings on gcc
This enables selected things from -Wextra, can't use -Wextra because it is
fussy enough to complain about unused params on functions... they are
there for a reason.

-Wsign-compare
-Wignored-qualifiers
not -Wimplicit-fallthrough=3 ... only on gcc 7
-Wtype-limits
-Wuninitialized
not -Wclobbered ... only on gcc 7ish

fix the warnings everywhere they were found.
2017-10-25 07:17:29 +08:00
Andy Green
89cb55ea58 tls: split out common, openssl and mbedtls code
- introduce lib/tls/mbedtls lib/tls/openssl
 - move wrapper into lib/tls/mbedtls/wrapper
 - introduce private helpers to hide backend

This patch doesn't replace or remove the wrapper, it moves it
to lib/tls/mbedtls/wrapper.

But it should be now that the ONLY functions directly consuming
wrapper apis are isolated in

  - lib/tls/mbedtls/client.c (180 lines)
  - lib/tls/mbedtls/server.c (317 lines)
  - lib/tls/mbedtls/ssl.c    (325 lines)

In particular there are no uses of openssl or mbedtls-related
constants outside of ./lib/tls any more.
2017-10-25 07:17:29 +08:00
Andy Green
bb04883097 travis: add mbedtls + http2 variant 2017-10-25 07:17:29 +08:00
Andy Green
ce68d38794 LWS_PRE: make int 2017-10-25 07:17:22 +08:00
Andy Green
4a2dd2bdaa http2: missing return when openssl too old 2017-10-24 20:19:24 +08:00
Andy Green
af8f2d3616 protocol init: allow use of client apis during per-vhost protocol init 2017-10-24 20:19:24 +08:00
Sebastian Mueller
28d820ba0a getifaddrs-fix-include-reference
https://github.com/warmcat/libwebsockets/issues/1068
2017-10-24 20:19:17 +08:00
Andy Green
c86488be9c getifaddrs: fix conditional build 2017-10-24 06:39:37 +08:00
Andy Green
11afda24ed client: allow failout when adding client headers 2017-10-23 13:44:35 +08:00
Andy Green
07d4cf7628 client: do not treat selected response codes as connection failures
There was not really any "connection failure" with some of these like 404.

They may have body content that is important.

The client should use lws_http_client_http_response(wsi) to discover
the response code the client action received and decide for itself
what to do about that.
2017-10-23 13:44:27 +08:00
Andy Green
10828b1ea0 mirror: fix multiple mirror instances hang 2017-10-20 21:10:29 +08:00
Andy Green
8528de580a getifaddrs: fix second lws_malloc 2017-10-19 21:00:50 +08:00
Andy Green
001b3010a6 sshd: fix NULL vhd protection 2017-10-19 11:26:29 +08:00
Andy Green
79326e4f07 daemonize: fix missing lws_malloc reason param 2017-10-18 20:19:46 +08:00
Andy Green
fcf5b2c25a v2.4.0 2017-10-17 13:21:12 +08:00
Andy Green
dfacbc2071 mbedtls: fix client 2017-10-17 13:21:10 +08:00
Andy Green
1799b02a43 windows: POLLHUP is or-ed on other events
https://github.com/warmcat/libwebsockets/issues/1054
2017-10-16 17:28:37 +08:00
Andy Green
c83afc66e6 refactor: subdirs for source in lib
Split out some optional code into own sources to
shrink down libwebsockets.c and server.c a bit
2017-10-16 17:28:37 +08:00
Andy Green
904a9c0920 http2: make usable
HTTP/2 support is now able to serve the test server, complete with
websockets, from a single vhost.

 - This works the same with both OpenSSL and mbedTLS.

 - POST is now wired up and works (also for file upload).

 - CGI is wired up and works.

 - Redirect is adapted and works

 - lwsws works.

 - URI urldecode, sanitation and argument parsing wired up for :path

valgrind clean (aside from openssl-style false uninit data usage in mbedtls send occasionally)

h2spec reports:

$ h2spec  -h 127.0.0.1 -p 7681 -t -k -o 1
...
145 tests, 145 passed, 0 skipped, 0 failed"

Incorporates:

 - "https://github.com/warmcat/libwebsockets/pull/1039
	Fixes issue with -Werror=unused-variable flag

 - 2c843a1395
	ssl: fix infinite loop on client cert verification failure

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>"

Caused and fixes Coverity 184887 - 184892
2017-10-16 17:13:49 +08:00
Andy Green
028551271e client: reorder close callbacks
This will make sure you get

 - DROP_PROTOCOL
 - CONNECTION_ERROR
 - CLOSED_CLIENT_HTTP

as expected for failed client connections.
2017-10-16 17:13:49 +08:00
Andy Green
e67ae24d41 doxygen cleanup 2017-10-16 17:13:49 +08:00
Andy Green
6f3f2df7ca lejp-conf: skip conf.d files containing tilde
https://github.com/warmcat/libwebsockets/issues/1051
2017-10-16 17:13:49 +08:00
Andy Green
1673554c6b vh protocol destroy: dont miss vhosts 2017-10-16 17:13:49 +08:00
Andy Green
f66b84778c windows: defeat service if no vhosts 2017-10-16 17:13:49 +08:00
Andy Green
46b04f4ba4 wrapper: make SSL_accept nonblocking
The mbedTLS / OpenSSL wrapper spins for the duration of the
handshake, which is a slow 31ms on my x86_64 box (by comparison
it's ~1ms on actual OpenSSL on the same box).

This doesn't change the 31ms but it stops us spinning during the
accept and has us retry on POLLIN instead like OpenSSL.

Note this also fixes the endemic mismatches in returncode
between mbedTLS and OpenSSL semantics...
2017-10-16 17:13:49 +08:00
Claudi Martinez
df2dc99c14 Subject: toolchain: Create cross-w64.cmake
Toolchain file for crosscompiling on 64bit Windows platforms.
2017-10-16 17:13:49 +08:00
Claudi Martinez
d3fb641cdb Subject: toolchain: Create cross-w32.cmake
Toolchain file for crosscompiling on 32bit Windows platforms from Linux's MinGW platform.
2017-10-16 17:13:49 +08:00
Andy Green
9c2a7dd58b ah pool: change to dynamic linked list
For some targets like ESP32, the ah pool is mainly sitting idle wasting memory.

For HTTP/2, if the client sends a series of pipelined headers on different SIDs
that exist simultaneously, there is no way to stall the headers to wait for an
ah, because we must read the stream for stuff like WINDOW_UPDATE on the other
streams.

In both these cases having the ability to free unused ah completely and allocate
more dynamically if there is memory is useful, so this patch makes the ah pool
an initially-empty linked list that allocates on demand up to the "max pool
size" limit from the context info.  When nobody wants an ah, it is freed (if
someone was waiting for it, it is directly reused).

For ESP32 it means no large, permanent alloc when lws starts and dynamic alloc
according to how many streams the client opens, which can be controlled by
SETTINGS.
2017-10-16 17:13:49 +08:00
Andy Green
4f99ccd6a8 heap instrumentation 2017-10-16 17:13:49 +08:00
Andy Green
4e55c1c452 docs: add complex cross compile worked example 2017-10-16 17:13:49 +08:00
Andy Green
5fd9ad2dc3 wrapper: force mbedtls ssl rx buf size to 2048 if 0 2017-10-16 17:13:49 +08:00
Andy Green
7cff6b0cf9 add 100 Continue
https://github.com/warmcat/libwebsockets/issues/1041
2017-10-16 17:13:49 +08:00
Andy Green
8b5eaa0534 SSL_shutdown spin fix 2017-10-16 17:13:49 +08:00
Andy Green
a9843c3c26 openssl-wrapper: fix SSL_shutdown 2017-10-16 17:13:49 +08:00
Andy Green
796a5edb6c openssl-wrapper: add APLN accessors
This adds the necessary OpenSSL Apis to the mbedTLS openssl wrapper
to allow ALPN negotiation OpenSSL-style.

The OpenSSL upgrade list format is supported and converted to mbedtls
format at runtime.
2017-10-16 17:13:48 +08:00
Andy Green
fc995df480 CMake: convert all LWS_USE_... to LWS_WITH_...
Almost all the CMake options begin with LWS_WITH_..., but many of the
symbols passed to lws are LWS_USE_... , this causes neededless confusion,
compounded by the fact that a few CMake options also begin with
LWS_USE_.

This patch globally converts all LWS_USE_... to LWS_WITH_..., so there
is only one prefix to remember in both CMake and the code.

The affected public CMake options are

LWS_USE_BORINGSSL     ->  LWS_WITH_BORINGSSL
LWS_USE_CYASSL        ->  LWS_WITH_CYASSL
LWS_USE_WOLFSSL       ->  LWS_WITH_WOLFSSL
LWS_USE_MBEDTLS       ->  LWS_WITH_MBEDTLS
LWS_USE_BUNDLED_ZLIB  ->  LWS_WITH_BUNDLED_ZLIB
2017-10-16 17:13:48 +08:00
Andy Green
7597ac3766 Plugins: add ssh-base ssh server plugin 2017-10-16 16:59:57 +08:00
Andy Green
d383b94dbf README.build: update openssl link to their new wiki location 2017-09-27 08:24:05 +08:00
Alexander von Gluck IV
8bea6c144e Subject: Haiku: initial platform support
https://github.com/warmcat/libwebsockets/pull/1033
2017-09-27 08:24:05 +08:00
Andy Green
495342c2ae libuv: use shadow active events mask
libuv .events or .pevents in the io watcher don't seem to hold the POLLIN / POLLOUT
state correctly.  When POLLIN is defeated, using the rx flow control, POLLOUT seems
to go away somewhere along the line when trying to OR on to the active mask.

This has us track what we enabled outside in a container struct and use that for
the mask instead.  With this rx flow control works properly with the latest mirror
using libuv (it worked fine with poll() event loop without libuv).
2017-09-27 08:24:05 +08:00
Andy Green
bc451afb30 lws-meta: add var in js to enable and disable by default 2017-09-27 08:24:05 +08:00
Andy Green
49f8029677 coverity fixes 2017-09-27 08:24:05 +08:00
Andy Green
c52a6267ab clean 2017-09-27 08:24:05 +08:00
Andy Green
91166ecb2a pmd: handle zero-length rx 2017-09-27 08:24:05 +08:00
Andy Green
93f05d6eff test-client: add justmirror flag 2017-09-27 08:24:05 +08:00
Andy Green
4f267c515e clean up top level of project 2017-09-27 08:24:05 +08:00
Andy Green
66a402cc96 cgi: also allow for generic spawn 2017-09-27 08:24:04 +08:00
Andy Green
4a0db7fbf1 LWS_CALLBACK_ADD_HEADERS: also on upgrade headers
https://github.com/warmcat/libwebsockets/issues/1028
2017-09-27 08:24:04 +08:00
Andy Green
12adb39542 lws_ring: introduce generic flexible ringbuffer abstraction 2017-09-21 08:45:56 +08:00
Andy Green
f85a1d42d8 docs: README.coding.md: add explicit note about external poll loop forced service 2017-09-21 08:45:56 +08:00
Andy Green
d1859ad4fd logging: move most everything during startup to info 2017-09-21 08:45:56 +08:00
Andy Green
217dfe7c6b drop_privs: ignore if uid or gid is zero
0 is the natural default for the info struct.

If you are not 0 (root) then you can't change to root, it will fail
without having done anything.  What the user probably wanted is
just leave it as the uid/gid it was started as.

If you are 0 (root) you are already at 0 and the change has no
meaning.

Net effect is get rid of two warning logs if you left it at 0 in
the info struct and start as non-root.
2017-09-21 08:45:56 +08:00
Andy Green
69e4433f4e add PENDING_TIMEOUT_USER_REASON_BASE 2017-09-21 08:45:56 +08:00
Andy Green
6750365393 lwsl_hexdump: always export even if debug level disabled 2017-09-21 08:45:56 +08:00
Andy Green
59687e04e0 lws_protocol_vh_priv_get: fail gracefully with NULL vhost 2017-09-21 08:45:56 +08:00
Andy Green
98df6722ea LWS_WITH_PEER_LIMITS
Introduces an optional, settable restriction on the number of
simultaneous wsi connections and ah that a single peer IP can
obtain.

The default is disabled for build, and if enabled, unlimited.

However when enabled at CMake, setting info.ip_limit_ah and / or
info.ip_limit_wsi at context creation time will enforce the limits.

Connections past the info.ip_limit_wsi limit are dropped, and
connections needing an ah are forced to stay in the ah waiting list
even when ah are available, while the peer is at the number of
ah in info.ip_limit_ah.
2017-09-19 12:09:17 +08:00
Andy Green
4efc35db58 LWS_WITH_STATS: dont add latency when start point not set
Also fix lws_meta child connections to also measure their
own latency.
2017-09-19 12:09:17 +08:00
Andy Green
9b484ff507 docs: clarify difference between client and server ssl members in creation_info struct
Update test-client from the deprecated server ssl members that still work for
backwards compatibility to the preferred client-specific ones.

Remove .client_ssl_... member dependency on OPENSSL

https://github.com/warmcat/libwebsockets/issues/1022
2017-09-19 12:09:17 +08:00
Petar Paradzik
7379e5ad6b Subject: mbedtls_wrapper: fix using uninitialized variable
Fixes issues when compiling with "-Werror=maybe-uninitialized".

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-09-19 12:09:17 +08:00
Andy Green
dee2d68b9c cmake: add confirm_command macro and use for genromfs + xxd on ESP32 build
Solves the exact issue in

https://github.com/warmcat/libwebsockets/issues/1021
2017-09-19 12:09:17 +08:00
Andy Green
ca3013ca38 enable -Wundef for UNIX to catch dependencies on preprocessor defines that do not actually exist in the build 2017-09-19 12:08:31 +08:00
Andy Green
b4673d871b docs: fix typo in lws_vhost_destroy docs 2017-09-19 12:08:31 +08:00
Andy Green
a3cc4a368e esp32: report heap changes 2017-09-19 12:08:31 +08:00
Andy Green
0186490fad lws-genhash
This gives you a thin api on top of your TLS library digest functions
which abstracts away whether you are using mbedtls apis or openssl
ones.  Using lws_genhash... apis the same code works either way and
the dependencies on which TLS lib is hidden.
2017-09-10 14:44:12 +08:00
Andy Green
37b994cc69 mbedtls: find LWS_HAVE_X509_VERIFY_PARAM_set1_host in CMake 2017-09-09 08:54:58 +08:00
Andy Green
b8d3f51ebd wrapper: introduce X509_VERIFY_PARAM_set/clear_hostflags
This defines the OpenSSL X509_CHECK_FLAG_...s and the set/clear
accessors.  Since none of them are supported, the set / clear
accessor always does nothing and returns error.
2017-09-09 08:54:58 +08:00
Andy Green
14cc31fe7d wrapper: introduce X509_VERIFY_PARAM_set1_host
This lets the user code set the mbedtls hostname using the standard OpenSSL
api semantics.
2017-09-09 08:54:58 +08:00
Andy Green
43b3141f93 wrapper: introduce SSL_get0_param
This adds the standard OpenSSL api to get a pointer to the SSL struct's
X509_VERIFY_PARAM.  We need this for the OpenSSL api to set the peer
hostname introduced in the next patch.
2017-09-09 07:56:25 +08:00
Andy Green
d78c93254b cgi: stdout POLLHUP holy grail
This a) directly discovers cgi stdout POLLUP and b) modulates rx flow control on CGI STDOUT
according to the outgoing writeable service.  When the outgoing writeable service finally sees
0 read() waiting for it even though it was signalled for POLLIN, it knows it is a POLLHUP.

Critically when it sees POLLHUP like that, it leaves the rx flow control defeating any
further stdout POLLIN signalling while the rest of the CGI lifecycle completes, eliminating
busywaiting during the CGI.
2017-09-08 20:57:53 +08:00
Petar Paradzik
04ed8d2673 Subject: cgi: fix uint64_t formating
On some platforms 'llu' format is needed for uint64_t instead of 'lu'.
PRIu64 format specifier fixes these platform specific issues.

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-09-08 20:56:05 +08:00
Andy Green
138c8523f9 docs: add missing description of tsi on lws_service_tsi
Fixes https://github.com/warmcat/libwebsockets/issues/1017
2017-09-08 20:56:04 +08:00
Andy Green
3ea4fcf8a5 esp32: unbreak including mbedtls bits from mbedtls integration 2017-09-08 20:56:04 +08:00
Andy Green
8cb341314a esp32: unbreak cert gen from mbedtls integration 2017-09-08 20:56:04 +08:00
Andy Green
d38f630519 logging: reduce timeout and detach logs to info 2017-09-08 20:56:04 +08:00
Andy Green
f06967fa84 cgi: if we ask for writable to send chunk end, defer close 2017-09-07 11:45:35 +08:00
Andy Green
cad115bf6b docs: add mising CALLBACK docs and some extra info
https://github.com/warmcat/libwebsockets/issues/1015
2017-09-07 09:29:09 +08:00
Andy Green
f5058fa9b7 server TLS: allow user to provide cert without using filepaths
This is a rewrite of the patch from HarishSRIB here:

https://github.com/warmcat/libwebsockets/pull/1013
2017-09-06 16:31:53 +08:00
Andy Green
8d63fbb6aa ACCESS_LOG: allow early rejected accesses to log 2017-09-06 09:30:32 +08:00
Andy Green
4046239bc8 ACCESS_LOG: truncate referrer always leaving enough room for at least an empty useragent and delimiters 2017-09-06 08:39:58 +08:00
Andy Green
f8fad0d350 mbedtls: client provide CA
WIP https://github.com/warmcat/libwebsockets/issues/1011
2017-09-02 10:50:54 +08:00
Andy Green
32eaf83f24 socks5: style and exit handling 2017-09-01 19:09:47 +08:00
Andy Green
df5c143b89 socks5: remove dodgy casts
Change the last arg of socks_generate_msg() to be ssize_t * to match
signedness of original caller type of int, then convert the caller
type also to ssize_t so the pointer needs no cast.

This keeps stuff in the caller checking for len < 0 happy.

https://github.com/warmcat/libwebsockets/issues/1010
2017-09-01 15:37:53 +08:00
Andy Green
d6e8459c07 vhost: allow user pointer to be attached at creation time
https://github.com/warmcat/libwebsockets/issues/1008
2017-08-29 15:37:16 +08:00
unprompted
f994a4f728 free vs lws_free: lws_vhost_destroy2 2017-08-29 08:45:36 +08:00
Andy Green
6d25763d10 esp32: fixes for mbedtls breakout 2017-08-29 08:45:36 +08:00
Andy Green
ec5e0e3e51 mbedtls: log if we are using it 2017-08-29 08:45:36 +08:00
Andy Green
c0c217bd5a mbedtls: isgnore ECDH_H existence and try to find mbedtls libs + includes by default 2017-08-29 08:45:36 +08:00
Juraj Vijtiuk
41909eac97 mbedtls: Add mbedTLS backend support 2017-08-28 20:22:20 +08:00
Juraj Vijtiuk
77a71e94d1 mbedtls: Add mbedTLS wrapper
This wrapper has come from Espressif's esp-idf

https://github.com/espressif/esp-idf/tree/master/components/openssl

under Apache 2.0, at or around commit

3e4627479b6fff6b652de6d2f0302101e51ee5c2
2017-08-28 20:13:58 +08:00
Juraj Vijtiuk
ea5638cb2c mbedtls: Force mbedTLS usage when built for ESP32 2017-08-28 20:13:48 +08:00
Juraj Vijtiuk
3aaf38d3d8 mbedtls: Add mbedTLS support to CMakeLists.txt 2017-08-28 20:12:32 +08:00
Andy Green
ae817cc993 remove debugging line 2017-08-28 08:21:56 +08:00
68b2b3f269 Subject: fix Spec file by adding / removing new files to the package 2017-08-28 08:21:06 +08:00
556 changed files with 81400 additions and 32599 deletions

12
.gitignore vendored
View file

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

View file

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

File diff suppressed because it is too large Load diff

14
LICENSE
View file

@ -33,19 +33,21 @@ to get original sources with the liberal terms.
Original liberal license retained
- lib/sha-1.c - 3-clause BSD license retained, link to original
- lib/misc/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/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
- 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
Public Domain (CC-zero) to simplify reuse
- test-server/*.c
- test-server/*.h
- test-apps/*.c
- test-apps/*.h
- minimal-examples/*
- lwsws/*
------ end of exceptions

1
Makefile.projbuild Normal file
View file

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

View file

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

135
README.md
View file

@ -2,19 +2,91 @@
[![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)
libwebsockets
-------------
![lws-overview](./doc-assets/lws-overview.png)
News
----
v2.3 is out... see the changelog https://github.com/warmcat/libwebsockets/blob/v2.3-stable/changelog
## 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
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
@ -33,60 +105,3 @@ You can get the latest version of the library from git:
Doxygen API docs for master: https://libwebsockets.org/lws-api-doc-master/html/index.html
After libwebsockets 1.3, tags will be signed using a key corresponding to this public key
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQINBFRe35QBEADZA7snW7MoEXkT2deDYZeggVD3694dg1o5G4q36NWjC8Pn/b2V
d+L9Nmw8ydKIv8PLJW762rnveQpPYRqCRD8X4bVTYzYz3qsOl5BrYf6cuVn0ZrPB
13TVRg+NZwUaVxc7O+tdOvvEBdA9OCIygctPNK9Nyh53xs5gPHhghZrKVrt0xM1A
2LYsgoHmMBCCY25SHb1nuapvhA3LvuJb4cNNVRCukCoA6yx0uhSEz2AUPJSLqnZ9
XnNBMKq+1a9C+y7jo4O78upTTmuOmRmNEVAu7pxCSUXDrNa87T8n6vFkV/MiW8nv
VmhppKJrKPJ0KxJF9b7uG6eKosfoK2PKyE7pAoDN1fuNyBTB0dkFAwyTCN8hmhOg
z71QrCltotq/AxSCsKzgFkDBL7D3KUM10QR5kmznjcm8tFWHoSttPR334z/1Yepf
ATqH/tfYydW42qeeHgKjfeegnlI65nTDtwYW6lSqZsXg+/ABg0ki9m5HA6l713ig
gRbVHSNkiz56O+UOqBtfcJZBc8QZqqixq8rbP2Is0HBBEtD+aFMuKx/sQ3ULkQs2
8dZ5qsGTBT/xHmqpHJsIFX/jwjY5zeEiFbnO5bMH7YLmkjynVsn5zxTyXKQJe29C
Uq0Yd9+JpDhHnZoiz/1hIIBsr89Z4Yy6c59YNJ3yJEOast0ODERcKSaUAQARAQAB
tC9BbmR5IEdyZWVuIChMaW5hcm8ga2V5KSA8YW5keS5ncmVlbkBsaW5hcm8ub3Jn
PokCPQQTAQoAJwUCVF7flAIbAwUJBaOagAULCQgHAwUVCgkICwUWAwIBAAIeAQIX
gAAKCRA8ZxoDS3lTexApD/9WT7JWy3tK33OIACYV40XwLEhRam4Xku4rhtsoIeJK
P0k/wa7J2PpceX6gKV+QBsOx3UbUfpqZ/Mu7ff3M0J6W87XpKRROAmP43zyiBkmM
A6v0pJXozknmCU28p3DuLC8spVDFg9N52xV7Qb+9TDHcTYiVi4swKYuDEuHBC/qa
M69+ANgsGbrMFRypxtU7OEhls3AEo3Cq03xD8QvLjFyPpYp1f0vNRFm2Jjgm2CRe
YLVsCGxG35Dz7DpJHekHNxje6xsZ2w9Q38M0rLQ0ICOVQ+E1Dir3hwmZQWASzMMi
+R0P+MVYpVt5y7KtiLywJ4BzNogS7gY3wQxksJOFA1uuk5h/hO54a361mcdA0Ta5
HHhGKRw87lVjEQSaRjFZmHFClB+Sb8MuWR51JTzVS5HtJlcNqcWhF63vZ8bZ7b6y
Aj8cXNjH6ULXyX3QnTUWXX/QU3an3yh8iPONWOGP5d5Hi/qejHGIhP2L5H+h05CP
aZQYFLjjebYgEHijuA28eKWsBsoBPFSLpLloHTDkiycgFdV2AkQcxZN9ZElAqURP
xUkEIscQg3YhExGiVEtaxBp1/p/WctMxs5HNoi0Oc97ZUcKvSCz9FDGXX9wYBpRf
gzjNn055Xn4QyxBDnp5DrYT0ft/8BEnRK0JP6z3gNfnhOxZo4XA+M6w4Hjh3tI2A
3rkCDQRUXt+UARAA0yHmONtW3L1HpvWFR+VgVNHa1HBWWk7lMsI6ajeiUK/lN3F/
+vNbux46bPj/sNT9twbWmYhv6c0yVzCpmv5M5ztefS7mW/zPNLJmCmH32kAvVFr1
Z90R/X+Z1Uh8wCCU72S2pSIXQFza3LF53pbpKi5m1F2icYcx+35egAvvZVZtcrMu
TjHUa+N9mFKxa7tb5PI8Lv93nRLwB7aKkp5PKy9Yvse0jACrAAGeIpI73H467/wO
ujermKlyPOOv+Lpjd7kedWKdaweitva7FVI20K/afn4AwCI8HJUIqVbil0Yrg9Le
M1TRsRydzMQQejsb/cWi3fQ3U3HxvSJijKltckPMqjJaXbqmrLz3FOA5Km0ciIOB
WW0Qq0WREcS3rc5FHU29duS9OAieAWFYyLDieug4nQ29KQE6I0lMqLnz8vWYtbmw
6AHk9i2GsXOZiPnztuADgt9o9Os8fm7ZiacA1LISl86P7wpFk+Gf4LRvv8Fk08NV
b2K1BY4YC9KP+AynyYQmxmyB1YQCh/dZHiD4ikGKttHAy4ZsMW6IRL5bRP0Z97pA
lyBtXP0cGTJtuPt2feh0zaiA7blZ/IDXkB1UqH6jnTa71d1FeNKtVFi8FhPIREN6
Rc5imyRxubZEgsxhdjqGgdT5k6Qr42SewAN391ygutpgGizGQtTwzvmKa0UAEQEA
AYkCJQQYAQoADwUCVF7flAIbDAUJBaOagAAKCRA8ZxoDS3lTewuBD/9/rakAMCRC
+WmbUVpCbJSWP5ViH87Xko4ku437gq56whcGjQpxfCYt8oeVgS8fZetUOHs2gspJ
CEc8TYLUFntfyt2AzKU29oQoizPm33W9S1u7aRGWsVVutd2sqUaQUDsl9z35+Ka9
YcWoATJSWBgnhSAmNcM60OG0P5qrZloTlbRSlDZTSZT3RvY4JWtWCubGsjEpXO4h
ZqbKCu3KgV/6NOuTLciriSOZ/iyva3WsCP2S8mRRvma7x04oMTEWX80zozTCK8gG
XqqS9eDhCkRbdmMyUQbHIhc/ChYchO5+fQ1o0zMS5cv6xgkhWI3NJRUkNdXolH9a
5F9q4CmCTcdEZkqpnjsLNiQLIENfHbgC0A5IjR6YgN6qAP8ZJ5hBgyTfyKkwB7bW
DcCnuoC9R79hkI8nWkoRVou9tdzKxo0bGR6O4CfLj+4d3hpWkv9Rw7Xxygo5JOqN
4cNZGtHkmIFFk9fSXul5rkjfF/XmThIwoI8aHSBZ7j3IMtmkKVkBjNjiTfbgW8RT
XIIR+QQdVLOyJqq+NZC/SrKVQITg0ToYJutRTUJViqyz5b3psJo5o2SW6jcexQpE
cX6tdPyGz3o0aywfJ9dcN6izleSV1gYmXmIoS0cQyezVqTUkT8C12zeRB7mtWsDa
+AWJGq/WfB7N6pPh8S/XMW4e6ptuUodjiA==
=HV8t
-----END PGP PUBLIC KEY BLOCK-----
```

View file

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

711
READMEs/README.build.md Normal file
View file

@ -0,0 +1,711 @@
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,70 +1,39 @@
Notes about coding with lws
===========================
@section dae Daemonization
@section era Old lws and lws v2.0
There's a helper api `lws_daemonize` built by default that does everything you
need to daemonize well, including creating a lock file. If you're making
what's basically a daemon, just call this early in your init to fork to a
headless background process and exit the starting process.
Originally lws only supported the "manual" method of handling everything in the
user callback found in test-server.c / test-server-http.c.
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.
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.
@section conns Maximum number of connections
You can see an example of the new way in test-server-v2.0.c.
The maximum number of connections the library can deal with is decided when
it starts by querying the OS to find out how many file descriptors it is
allowed to open (1024 on Fedora for example). It then allocates arrays that
allow up to that many connections, minus whatever other file descriptors are
in use by the user code.
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.
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.
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.
@section evtloop Libwebsockets is singlethreaded
Libwebsockets works in a serialized event loop, in a single thread.
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.
Websocket write activities should only take place in the
`LWS_CALLBACK_SERVER_WRITEABLE` callback as described below.
[This network-programming necessity to link the issue of new data to
the peer taking the previous data is not obvious to all users so let's
repeat that in other words:
***ONLY DO LWS_WRITE FROM THE WRITEABLE CALLBACK***
There is another network-programming truism that surprises some people which
is if the sink for the data cannot accept more:
***YOU MUST PERFORM RX FLOW CONTROL***
See the mirror protocol implementations for example code.
Only live connections appear in the user callbacks, so this removes any
possibility of trying to used closed and freed wsis.
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 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.
SSL_library_init() is called from the context create api and it also is not
reentrant. So at least create the contexts sequentially.
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
@ -72,10 +41,10 @@ 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
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(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.
@ -87,6 +56,36 @@ 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
@ -100,6 +99,176 @@ 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
need to daemonize well, including creating a lock file. If you're making
what's basically a daemon, just call this early in your init to fork to a
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)`.
@section conns Maximum number of connections
The maximum number of connections the library can deal with is decided when
it starts by querying the OS to find out how many file descriptors it is
allowed to open (1024 on Fedora for example). It then allocates arrays that
allow up to that many connections, minus whatever other file descriptors are
in use by the user code.
If you want to restrict that allocation, or increase it, you can use ulimit or
similar to change the available number of file descriptors, and when restarted
**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.
However integration to multithreaded apps is possible if you follow some guidelines.
1) Aside from two APIs, directly calling lws apis from other threads is not allowed.
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.
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.
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.
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.
See the mirror protocol implementations for example code.
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.
@section anonprot Working without a protocol name
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.
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.
*/
static const struct lws_protocol_vhost_options pvo_opt = {
NULL,
NULL,
"default",
"1"
};
static const struct lws_protocol_vhost_options pvo = {
NULL,
&pvo_opt,
"my-protocol",
""
};
...
context_info.pvo = &pvo;
...
```
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
@ -220,10 +389,9 @@ external polling array. That's needed if **libwebsockets** will
cooperate with an existing poll array maintained by another
server.
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.
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.
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.
@ -244,6 +412,26 @@ 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
@ -251,7 +439,7 @@ The library is ready for use by C++ apps. You can get started quickly by
copying the test server
```
$ cp test-server/test-server.c test.cpp
$ cp test-apps/test-server.c test.cpp
```
and building it in C++ like this
@ -278,6 +466,75 @@ 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
@ -321,6 +578,20 @@ 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
@ -337,7 +608,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-server/test-client.c) for example code.
As usual, see [test-client.c](../test-apps/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
@ -472,7 +743,9 @@ callbacks on the named protocol
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
`protocol-lws-raw-test` plugin provides a method for testing this with
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
`libwebsockets-test-server-v2.0`:
The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
@ -556,6 +829,46 @@ 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
@ -598,7 +911,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
@ -628,35 +941,74 @@ You can set fd_limit_per_thread to a nonzero number to control this manually, eg
the overall supported fd limit is less than the process allowance.
You can control the context basic data allocation for multithreading from Cmake
using -DLWS_MAX_SMP=, if not given it's set to 32. The serv_buf allocation
using -DLWS_MAX_SMP=, if not given it's set to 1. The serv_buf allocation
for the threads (currently 4096) is made at runtime only for active threads.
Because lws will limit the requested number of actual threads supported
according to LWS_MAX_SMP, there is an api lws_get_count_threads(context) to
discover how many threads were actually allowed when the context was created.
It's required to implement locking in the user code in the same way that
libwebsockets-test-server-pthread does it, for the FD locking callbacks.
See the test-server-pthreads.c sample for how to use.
There is no knowledge or dependency in lws itself about pthreads. How the
locking is implemented is entirely up to the user code.
@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
@section libevuv Libev / Libuv support
```
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
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 either of the event libraries.
to indicate it will use one of the event libraries at runtime.
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
@ -728,7 +1080,41 @@ 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
@ -888,6 +1274,15 @@ 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
@ -930,7 +1325,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,6 +1,8 @@
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
@ -21,3 +23,15 @@ 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.

View file

@ -79,6 +79,12 @@ 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
@ -182,7 +188,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
@ -196,6 +202,18 @@ 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
@ -205,7 +223,7 @@ by the client, you can use "default": "1"
- `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
- `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.
- "`unix-socket`": "1" causes the unix socket specified in the interface option to be used instead of an INET socket
@ -257,6 +275,8 @@ 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
@ -321,7 +341,7 @@ provide them using "pmo"
}]
}
2) When using a cgi:// protcol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
2) When using a cgi:// protocol origin at a mountpoint, you may also give cgi environment variables specific to the mountpoint like this
```
{
"mountpoint": "/git",
@ -416,6 +436,17 @@ 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
@ -590,7 +621,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 galgrind as usual and get valid results. However the results / analysis part of valgrind runs
You can just run lwsws under valgrind 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.

View file

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

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

20
READMEs/mainpage.md Normal file
View file

@ -0,0 +1,20 @@
##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,11 +1,13 @@
Release Checklist
-----------------
0) QA
1) QA
a) ab
a) ab / h2load / h2spec
$ ab -n 100000 -c 200 http://localhost:7681/
$ ab -n 100000 -c 200 https://127.0.0.1:7681/
$ h2load -n 100000 -c 200 https://localhost:7681
$ h2spec -h 127.0.0.1 -p 7681 -t -k -o 1
b) coverity
@ -16,24 +18,6 @@ Release Checklist
d) valgrind test servers + client + browser
e) attack.sh
$ ./test-server/attack.sh
f) Autobahn
$ wstest -m fuzzingserver &
$ ./autobahn-test.sh
Force update by browser using agent "libwebsockets"
http://localhost:8080/test_browser.html
rsync -av ./reports/* root@warmcat.com:/var/www/libwebsockets.org
1) api
$ cp build/doc/* .
2) soname bump?
a) We need one if we added / changed / removed apis
@ -42,7 +26,7 @@ Release Checklist
set(SOVERSION "6")
libwebsockets.spec
scripts/libwebsockets.spec
-/%{_libdir}/libwebsockets.so.6
+/%{_libdir}/libwebsockets.so.7
@ -69,23 +53,20 @@ Release Checklist
a) rpm version bump to match CMake one
libwebsockets.spec
scripts/libwebsockets.spec
Version: 1.6.0
b) Summarize changelog
libwebsockets.spec
scripts/libwebsockets.spec
%changelog
* Sun Jan 17 2016 Andrew Cooks <acooks@linux.com> 1.6.4-1
- Bump version to 1.6.4
- MINOR fix xyz
6) update api docs
$ cmake ..
$ cp doc/* ..
6) Announce latest version on README.md
7) signed tag
@ -97,6 +78,6 @@ Release Checklist
b) final CI check, if fail delete tag, kill pushed tags, restart flow
8) website
9) website
a) update latest tag for release branch

View file

@ -1,5 +1,8 @@
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
@ -16,21 +19,25 @@ environment:
- LWS_METHOD: nossl
CMAKE_ARGS: -DLWS_WITH_SSL=OFF
install:
- appveyor DownloadFile https://libwebsockets.org:444/win-libuv.zip
- mkdir c:\assets
- mkdir c:\assets\libuv
- 7z x -oc:\assets\libuv win-libuv.zip
# - appveyor DownloadFile https://slproweb.com/download/Win32OpenSSL-1_0_2h.exe
# - appveyor DownloadFile https://libwebsockets.org:444/Win32OpenSSL-1_0_2L.exe
# - Win32OpenSSL-1_0_2L.exe /silent /verysilent /sp- /suppressmsgboxes
- appveyor DownloadFile https://libwebsockets.org:444/win-libuv64.zip
- mkdir c:\assets\libuv64
- 7z x -oc:\assets\libuv64 win-libuv64.zip
- 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
@ -38,21 +45,33 @@ build_script:
- cmake -DCMAKE_BUILD_TYPE=Release %CMAKE_ARGS% ..
- cmake --build . --config Release
# TODO: Keeps breaking Windows build, should be rewritten using CPack properly instead...
after_build:
- 7z a lws.zip %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.lib %APPVEYOR_BUILD_FOLDER%\build\lib\Release\websockets.exp %APPVEYOR_BUILD_FOLDER%\build\bin\Release\websockets.dll %APPVEYOR_BUILD_FOLDER%\lib\libwebsockets.h %APPVEYOR_BUILD_FOLDER%\build\lws_config.h %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe
# - cd ..
# - cd win32port
# - makensis -DVERSION=%APPVEYOR_BUILD_VERSION% libwebsockets.nsi
- 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\*
artifacts:
- path: lws.zip
name: lws.zip
type: Zip
- path: build\lws-%LWS_METHOD%-%APPVEYOR_BUILD_ID%.zip
#cache:
# - C:\OpenSSL-Win32
deploy:
- provider: BinTray
username: lws-team
api_key:
secure: nDpZ7P/wrk98DwJPMC6KpCC23QrVP8f3RxvKzBaqOmb9LiVrg1IyO1cc5vcgShZC
subject: lws-team
repo: libwebsockets
package: windows
publish: true
override: true
explode: false
matrix:
fast_finish: true

View file

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

200
changelog
View file

@ -1,6 +1,206 @@
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
make %{?_smp_mflags}
%install
cd ../build_tree

View file

@ -8,6 +8,12 @@
#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share"
#cmakedefine LWS_ROLE_H1
#cmakedefine LWS_ROLE_WS
#cmakedefine LWS_ROLE_RAW
#cmakedefine LWS_ROLE_H2
#cmakedefine LWS_ROLE_CGI
/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
#cmakedefine USE_WOLFSSL
@ -15,11 +21,10 @@
/* Also define to 1 (in addition to USE_WOLFSSL) when using the
(older) CyaSSL library */
#cmakedefine USE_OLD_CYASSL
#cmakedefine LWS_USE_BORINGSSL
#cmakedefine LWS_WITH_BORINGSSL
#cmakedefine LWS_USE_MBEDTLS
#cmakedefine LWS_USE_POLARSSL
#cmakedefine LWS_WITH_ESP8266
#cmakedefine LWS_WITH_MBEDTLS
#cmakedefine LWS_WITH_POLARSSL
#cmakedefine LWS_WITH_ESP32
#cmakedefine LWS_WITH_PLUGINS
@ -37,8 +42,9 @@
/* The current git commit hash that we're building from */
#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}"
/* Build with OpenSSL support */
/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/
#cmakedefine LWS_OPENSSL_SUPPORT
#cmakedefine LWS_WITH_TLS
/* The client should load and trust CA root certs it finds in the OS */
#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS
@ -47,25 +53,31 @@
#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}"
/* Turn off websocket extensions */
#cmakedefine LWS_NO_EXTENSIONS
#cmakedefine LWS_WITHOUT_EXTENSIONS
/* notice if client or server gone */
#cmakedefine LWS_WITHOUT_SERVER
#cmakedefine LWS_WITHOUT_CLIENT
#cmakedefine LWS_WITH_POLL
/* Enable libev io loop */
#cmakedefine LWS_USE_LIBEV
#cmakedefine LWS_WITH_LIBEV
/* Enable libuv io loop */
#cmakedefine LWS_USE_LIBUV
#cmakedefine LWS_WITH_LIBUV
/* Enable libevent io loop */
#cmakedefine LWS_USE_LIBEVENT
#cmakedefine LWS_WITH_LIBEVENT
/* Build with support for ipv6 */
#cmakedefine LWS_USE_IPV6
#cmakedefine LWS_WITH_IPV6
/* Build with support for UNIX domain socket */
#cmakedefine LWS_USE_UNIX_SOCK
#cmakedefine LWS_WITH_UNIX_SOCK
/* Build with support for HTTP2 */
#cmakedefine LWS_USE_HTTP2
#cmakedefine LWS_WITH_HTTP2
/* Turn on latency measuring code */
#cmakedefine LWS_LATENCY
@ -92,8 +104,12 @@
#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT
#cmakedefine LWS_HAVE_SSL_CTX_set1_param
#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host
#cmakedefine LWS_HAVE_RSA_SET0_KEY
#cmakedefine LWS_HAVE_X509_get_key_usage
#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate
#cmakedefine LWS_HAVE_UV_VERSION_H
#cmakedefine LWS_HAVE_PTHREAD_H
/* CGI apis */
#cmakedefine LWS_WITH_CGI
@ -112,6 +128,7 @@
#cmakedefine LWS_WITH_SERVER_STATUS
#cmakedefine LWS_WITH_STATEFUL_URLDECODE
#cmakedefine LWS_WITH_PEER_LIMITS
/* Maximum supported service threads */
#define LWS_MAX_SMP ${LWS_MAX_SMP}
@ -143,11 +160,22 @@
#cmakedefine LWS_HAVE__ATOI64
#cmakedefine LWS_HAVE__STAT32I64
#cmakedefine LWS_WITH_JWS
#cmakedefine LWS_WITH_ACME
#cmakedefine LWS_WITH_SELFTESTS
#cmakedefine LWS_HAVE_MALLOC_H
#cmakedefine LWS_HAVE_PIPE2
/* OpenSSL various APIs */
#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD
#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD
#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK
#cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS
#cmakedefine LWS_HAVE_SSL_get0_alpn_selected
#cmakedefine LWS_HAVE_SSL_set_alpn_protos
#cmakedefine LWS_HAS_INTPTR_T

View file

@ -28,12 +28,6 @@
/* Define to 1 if you have the <in6addr.h> header file. */
#cmakedefine LWS_HAVE_IN6ADDR_H
/* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine LWS_HAVE_INTTYPES_H
/* Define to 1 if you have the `ssl' library (-lssl). */
//#cmakedefine LWS_HAVE_LIBSSL
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#cmakedefine LWS_HAVE_MALLOC
@ -87,6 +81,8 @@
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine LWS_HAVE_UNISTD_H
#cmakedefine LWS_HAVE_TCP_USER_TIMEOUT
/* Define to 1 if you have the `vfork' function. */
#cmakedefine LWS_HAVE_VFORK

View file

@ -1,19 +1,20 @@
COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include
COMPONENT_DEPENDS := mbedtls openssl
#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) )/..
#-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) \
# 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
# -DNDEBUG=1 after cflags stops debug etc being built
.PHONY: build
build:
cd $(COMPONENT_BUILD_DIR) ; \
@ -22,13 +23,19 @@ build:
-DIDF_PATH=$(IDF_PATH) \
-DCROSS_PATH=$(CROSS_PATH) \
-DBUILD_DIR_BASE=$(BUILD_DIR_BASE) \
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/cross-esp32.cmake \
-DCMAKE_TOOLCHAIN_FILE=$(COMPONENT_PATH)/contrib/cross-esp32.cmake \
-DCMAKE_BUILD_TYPE=RELEASE \
-DOPENSSL_INCLUDE_DIR=${IDF_PATH}/components/openssl/include \
-DLWS_MBEDTLS_INCLUDE_DIRS="${IDF_PATH}/components/openssl/include;${IDF_PATH}/components/mbedtls/include;${IDF_PATH}/components/mbedtls/port/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 ;\
-DLWS_WITH_ESP32=1 \
$(MSYS_FLAGS) ; \
make && \
cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a
@ -36,6 +43,3 @@ clean: myclean
myclean:
rm -rf ./build
INCLUDES := $(INCLUDES) -I build/

View file

@ -20,7 +20,13 @@ LGPL2 / GPL2 at your choice.
Installation
------------
The author provides an easy way to install the various tools he provides:
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:
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_USE_BUNDLED_ZLIB=OFF \
-DLWS_WITH_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

@ -10,14 +10,15 @@
set(CMAKE_SYSTEM_NAME Linux)
# Name of C compiler.
set(CMAKE_C_COMPILER "${CROSS_PATH}/bin/xtensa-esp32-elf-gcc")
set(CMAKE_AR "${CROSS_PATH}/bin/xtensa-esp32-elf-ar")
set(CMAKE_RANLIB "${CROSS_PATH}/bin/xtensa-esp32-elf-ranlib")
set(CMAKE_LINKER "${CROSS_PATH}/bin/xtensa-esp32-elf-ld")
set(CMAKE_C_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_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 \
@ -29,6 +30,8 @@ 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_USE_WOLFSSL=ON
# CMAKE_OPTIONS += -DLWS_WITH_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_USE_CYASSL=ON
# CMAKE_OPTIONS += -DLWS_WITH_CYASSL=ON
# CMAKE_OPTIONS += -DLWS_CYASSL_LIBRARIES=$(STAGING_DIR)/usr/lib/libcyassl.so
# CMAKE_OPTIONS += -DLWS_CYASSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include

29
contrib/cross-w32.cmake Normal file
View file

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

29
contrib/cross-w64.cmake Normal file
View file

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

BIN
doc-assets/lws-overview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
doc-assets/lws-smp-ov.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

8
lib/.gitignore vendored
View file

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

14
lib/README.md Normal file
View file

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

View file

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

File diff suppressed because it is too large Load diff

92
lib/core/alloc.c Normal file
View file

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

3452
lib/core/libwebsockets.c Normal file

File diff suppressed because it is too large Load diff

308
lib/core/output.c Normal file
View file

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

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-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
@ -19,21 +19,31 @@
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "core/private.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 || wsi->position_in_fds_table < 0)
if (!wsi)
return 0;
if (wsi->handling_pollout && !_and && _or == LWS_POLLOUT) {
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) {
/*
* Happening alongside service thread handling POLLOUT.
* The danger is when he is finished, he will disable POLLOUT,
@ -42,7 +52,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
*/
wsi->leave_pollout_active = 1;
((volatile struct lws *)wsi)->leave_pollout_active = 1;
/*
* by definition service thread is not in poll wait, so no need
* to cancel service
@ -55,45 +65,106 @@ _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 >= 0 &&
wsi->position_in_fds_table < pt->fds_count);
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
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->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *)pa, 0)) {
ret = -1;
goto bail;
}
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);
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);
}
/*
@ -103,20 +174,16 @@ _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) {
if (sampled_tid && wsi->vhost) {
tid = wsi->vhost->protocols[0].callback(wsi,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid == -1) {
@ -127,34 +194,39 @@ _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_per_thread *pt, int allow)
lws_accept_modulation(struct lws_context *context,
struct lws_context_per_thread *pt, int allow)
{
// multithread listen seems broken
#if 0
struct lws_vhost *vh = context->vhost_list;
struct lws_pollargs pa1;
while (vh) {
if (allow)
_lws_change_pollfd(pt->wsi_listening,
if (vh->lserv_wsi) {
if (allow)
_lws_change_pollfd(vh->lserv_wsi,
0, LWS_POLLIN, &pa1);
else
_lws_change_pollfd(pt->wsi_listening,
else
_lws_change_pollfd(vh->lserv_wsi,
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];
@ -170,54 +242,46 @@ insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi)
return 1;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n",
wsi->desc.sockfd, context->max_fds);
#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());
return 1;
}
#endif
assert(wsi);
assert(wsi->vhost);
assert(wsi->event_pipe || wsi->vhost);
assert(lws_socket_is_valid(wsi->desc.sockfd));
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
if (wsi->vhost &&
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);
#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);
wsi->position_in_fds_table = pt->fds_count;
pt->fds[wsi->position_in_fds_table].fd = wsi->desc.sockfd;
#if LWS_POSIX
pt->fds[wsi->position_in_fds_table].events = LWS_POLLIN;
#else
pt->fds[wsi->position_in_fds_table].events = 0; // LWS_POLLIN;
#endif
pa.events = pt->fds[pt->fds_count].events;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
/* if no more room, defeat accepts on this thread */
if ((unsigned int)pt->fds_count == context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 0);
lws_accept_modulation(context, pt, 0);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 1))
ret = -1;
@ -225,15 +289,13 @@ 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) {
@ -241,14 +303,16 @@ remove_wsi_socket_from_fds(struct lws *wsi)
return 0;
}
#if !defined(_WIN32) && !defined(LWS_WITH_ESP8266)
if (wsi->desc.sockfd > context->max_fds) {
lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, context->max_fds);
#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);
return 1;
}
#endif
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
if (wsi->vhost &&
wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 1))
return -1;
@ -257,53 +321,89 @@ 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;
#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);
/* 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));
lws_pt_lock(pt);
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);
lwsl_debug("%s: wsi=%p, sock=%d, fds pos=%d, end guy pos=%d, endfd=%d\n",
__func__, wsi, wsi->desc.sockfd, wsi->position_in_fds_table,
pt->fds_count, pt->fds[pt->fds_count].fd);
/* have the last guy take up the now vacant slot */
pt->fds[m] = pt->fds[pt->fds_count - 1];
#endif
/* this decrements pt->fds_count */
lws_plat_delete_socket_from_fds(context, wsi, m);
#if !defined(LWS_WITH_ESP8266)
v = (int) pt->fds[m].fd;
/* end guy's "position in fds table" is now the deletion guy's old one */
end_wsi = wsi_from_fd(context, v);
if (!end_wsi) {
lwsl_err("no wsi found for sock fd %d at pos %d, pt->fds_count=%d\n", (int)pt->fds[m].fd, m, pt->fds_count);
assert(0);
} else
end_wsi->position_in_fds_table = m;
if (m != 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;
/* 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;
}
/* remove also from external POLL support via protocol 0 */
if (lws_socket_is_valid(wsi->desc.sockfd))
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_DEL_POLL_FD,
wsi->user_space, (void *) &pa, 0))
ret = -1;
#ifndef LWS_NO_SERVER
if (!context->being_destroyed)
/* if this made some room, accept connects on this thread */
if ((unsigned int)pt->fds_count < context->fd_limit_per_thread - 1)
lws_accept_modulation(pt, 1);
#endif
lws_pt_unlock(pt);
if (wsi->vhost->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 1))
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;
#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);
#endif
if (wsi->vhost &&
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;
return ret;
}
@ -311,29 +411,13 @@ int
lws_change_pollfd(struct lws *wsi, int _and, int _or)
{
struct lws_context_per_thread *pt;
struct lws_context *context;
struct lws_pollargs pa;
int ret = 0;
if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
return 1;
pt = &wsi->context->pt[(int)wsi->tsi];
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_lock(pt, __func__);
ret = __lws_change_pollfd(wsi, _and, _or);
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;
}
@ -342,20 +426,25 @@ LWS_VISIBLE int
lws_callback_on_writable(struct lws *wsi)
{
struct lws_context_per_thread *pt;
#ifdef LWS_USE_HTTP2
struct lws *network_wsi, *wsi2;
int already;
#endif
int n;
if (wsi->state == LWSS_SHUTDOWN)
if (lwsi_state(wsi) == LRS_SHUTDOWN)
return 0;
if (wsi->socket_is_permanently_unusable)
return 0;
if (wsi->parent_carries_io) {
int n = lws_callback_on_writable(wsi->parent);
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);
if (n < 0)
return n;
@ -363,74 +452,35 @@ 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->mode != LWSCM_HTTP2_SERVING)
goto network_sock;
if (wsi->u.http2.requested_POLLOUT) {
lwsl_info("already pending writable\n");
return 1;
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->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);
if (wsi->position_in_fds_table == LWS_NO_FDS_POS) {
lwsl_debug("%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
@ -443,20 +493,16 @@ network_sock:
void
lws_same_vh_protocol_insert(struct lws *wsi, int n)
{
//lwsl_err("%s: pre insert vhost start wsi %p, that wsi prev == %p\n",
// __func__,
// wsi->vhost->same_vh_protocol_list[n],
// wsi->same_vh_protocol_prev);
if (wsi->same_vh_protocol_prev || wsi->same_vh_protocol_next) {
lws_same_vh_protocol_remove(wsi);
lwsl_notice("Attempted to attach wsi twice to same vh prot\n");
}
wsi->same_vh_protocol_prev = /* guy who points to us */
&wsi->vhost->same_vh_protocol_list[n];
wsi->same_vh_protocol_next = /* old first guy is our next */
wsi->vhost->same_vh_protocol_list[n];
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];
/* we become the new first guy */
wsi->vhost->same_vh_protocol_list[n] = wsi;
@ -464,6 +510,10 @@ 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
@ -478,6 +528,11 @@ 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",
@ -489,13 +544,15 @@ 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);
}
@ -507,7 +564,6 @@ 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));
@ -516,19 +572,14 @@ 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) {
// 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);
if (wsi->same_vh_protocol_next)
assert(wsi->same_vh_protocol_next->
same_vh_protocol_prev ==
&wsi->same_vh_protocol_next);
lws_callback_on_writable(wsi);
wsi = wsi->same_vh_protocol_next;
}
@ -540,9 +591,14 @@ LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
struct lws_vhost *vhost = context->vhost_list;
struct lws_vhost *vhost;
int n;
if (!context)
return 0;
vhost = context->vhost_list;
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==

1756
lib/core/private.h Normal file

File diff suppressed because it is too large Load diff

987
lib/core/service.c Normal file
View file

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

124
lib/event-libs/README.md Normal file
View file

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

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

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

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

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

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

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

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

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

74
lib/event-libs/private.h Normal file
View file

@ -0,0 +1,74 @@
/*
* 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,262 +0,0 @@
/*
* 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,704 +0,0 @@
/*
* 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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

@ -40,15 +40,18 @@
#include <stdio.h>
#include <string.h>
#include "private-libwebsockets.h"
#include "core/private.h"
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789-_";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
static int
_lws_b64_encode_string(const char *encode, const char *in, int in_len,
char *out, int out_size)
{
unsigned char triple[3];
int i;
@ -89,26 +92,47 @@ lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
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.
*/
LWS_VISIBLE int
lws_b64_decode_string(const char *in, char *out, int out_size)
static int
_lws_b64_decode_string(const char *in, int in_len, char *out, int out_size)
{
int len, i, c = 0, done = 0;
unsigned char v, quad[4];
while (*in) {
while (in_len && *in) {
len = 0;
for (i = 0; i < 4 && *in; i++) {
for (i = 0; i < 4 && in_len && *in; i++) {
v = 0;
c = 0;
while (*in && !v) {
while (in_len && *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;
@ -131,7 +155,7 @@ lws_b64_decode_string(const char *in, char *out, int out_size)
* bytes." (wikipedia)
*/
if (!*in && c == '=')
if ((!in_len || !*in) && c == '=')
len--;
if (len >= 2)
@ -152,6 +176,18 @@ lws_b64_decode_string(const char *in, 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

@ -22,7 +22,7 @@
#include <unistd.h>
#include <errno.h>
#include "private-libwebsockets.h"
#include "core/private.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);
lock_path = lws_malloc(n, "daemonize lock");
if (!lock_path) {
fprintf(stderr, "Out of mem in lws_daemonize\n");
return 1;

View file

@ -1,8 +1,3 @@
/*
* 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).
@ -34,6 +29,10 @@
* 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>
@ -44,7 +43,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "private-libwebsockets.h"
#include "core/private.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
@ -84,7 +83,7 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
buf_size = 8192;
for (;;) {
buf = lws_zalloc(buf_size);
buf = lws_zalloc(buf_size, "getifaddrs2");
if (buf == NULL) {
ret = ENOMEM;
goto error_out;
@ -136,12 +135,12 @@ getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
goto error_out;
}
*end = lws_malloc(sizeof(**end));
*end = lws_malloc(sizeof(**end), "getifaddrs");
(*end)->ifa_next = NULL;
(*end)->ifa_name = strdup(ifr->ifr_name);
(*end)->ifa_flags = ifreq.ifr_flags;
(*end)->ifa_addr = lws_malloc(salen);
(*end)->ifa_addr = lws_malloc(salen, "getifaddrs");
memcpy((*end)->ifa_addr, sa, salen);
(*end)->ifa_netmask = NULL;
@ -149,12 +148,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));
lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs");
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));
lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs");
memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
sizeof(ifr->ifr_dstaddr));
} else
@ -269,4 +268,3 @@ main()
return 0;
}
#endif
#endif

323
lib/misc/jws/jwk.c Normal file
View file

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

642
lib/misc/jws/jws.c Normal file
View file

@ -0,0 +1,642 @@
/*
* 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,14 +1,26 @@
/*
* Lightweight Embedded JSON Parser
*
* Copyright (C) 2013 Andy Green <andy@warmcat.com>
* This code is licensed under LGPL 2.1
* http://www.gnu.org/licenses/lgpl-2.1.html
* 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
*/
#include <libwebsockets.h>
#include <string.h>
#include "lejp.h"
#include <stdio.h>
/**
@ -25,7 +37,7 @@
void
lejp_construct(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char count_paths)
{
ctx->st[0].s = 0;
@ -86,7 +98,7 @@ lejp_destruct(struct lejp_ctx *ctx)
void
lejp_change_callback(struct lejp_ctx *ctx,
char (*callback)(struct lejp_ctx *ctx, char reason))
signed char (*callback)(struct lejp_ctx *ctx, char reason))
{
ctx->callback(ctx, LEJPCB_DESTRUCTED);
ctx->callback = callback;
@ -148,7 +160,8 @@ 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';
@ -186,7 +199,6 @@ 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 */
@ -411,6 +423,26 @@ 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;
@ -444,7 +476,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;
}
@ -582,8 +614,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto reject;
}
/* drop the path [n] bit */
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
if (ctx->sp) {
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
@ -609,8 +643,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
}
/* pop */
ctx->sp--;
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
if (ctx->sp) {
ctx->ppos = ctx->st[ctx->sp - 1].p;
ctx->ipos = ctx->st[ctx->sp - 1].i;
}
ctx->path[ctx->ppos] = '\0';
if (ctx->path_match &&
ctx->ppos <= ctx->path_match_len)
@ -631,6 +667,7 @@ 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 */

294
lib/misc/lws-ring.c Normal file
View file

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

281
lib/misc/peer-limits.c Normal file
View file

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

View file

@ -142,6 +142,9 @@ romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
cp++;
}
while (*p && *p == '/' && p[1] == '/')
p++;
if (!*cp && (!*p || *p == '/') &&
(ntohl(next_be) & 7) == RFST_HARDLINK) {
set_cache(i, sizeof(*i));
@ -162,6 +165,9 @@ romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
if (!*p && *cp == '/')
return NULL;
while (*p && *p == '/' && p[1] == '/')
p++;
if (*p == '/' && !*cp) {
set_cache(i, sizeof(*i));
switch (ntohl(ci->next) & 7) {

View file

@ -32,7 +32,7 @@
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/
#include "private-libwebsockets.h"
#include "core/private.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
@ -45,7 +45,7 @@ struct sha1_ctxt {
} h;
union {
unsigned char b8[8];
u_int64_t b64[1];
uint64_t b64[1];
} c;
union {
unsigned char b8[64];

View file

@ -1,7 +1,7 @@
/*
* SMTP support for libwebsockets
*
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
* Copyright (C) 2016-2017 Andy Green <andy@warmcat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,7 +19,7 @@
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#include "core/private.h"
static unsigned int
lwsgs_now_secs(void)
@ -93,7 +93,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
email->estate = LGSSMTP_SENT_HELO;
break;
case LGSSMTP_SENT_HELO:
n = sprintf(email->content, "MAIL FROM: <%s>\n", email->email_from);
n = sprintf(email->content, "MAIL FROM: <%s>\n",
email->email_from);
email->estate = LGSSMTP_SENT_FROM;
break;
case LGSSMTP_SENT_FROM:
@ -105,7 +106,8 @@ lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
email->estate = LGSSMTP_SENT_DATA;
break;
case LGSSMTP_SENT_DATA:
if (email->on_get_body(email, email->content, email->max_content_size))
if (email->on_get_body(email, email->content,
email->max_content_size))
return;
n = strlen(email->content);
email->estate = LGSSMTP_SENT_BODY;
@ -208,7 +210,7 @@ uv_timeout_cb_email(uv_timer_t *w
LWS_VISIBLE LWS_EXTERN int
lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content)
{
email->content = lws_malloc(max_content);
email->content = lws_malloc(max_content, "email content");
if (!email->content)
return 1;
@ -238,4 +240,3 @@ lws_email_destroy(struct lws_email *email)
uv_timer_stop(&email->timeout_email);
uv_close((uv_handle_t *)&email->timeout_email, NULL);
}

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,32 @@
#include "private-libwebsockets.h"
#include "core/private.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)
@ -22,6 +45,19 @@ 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;
@ -52,32 +88,6 @@ 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)
{
@ -111,8 +121,9 @@ _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?
@ -126,19 +137,20 @@ _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);
#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
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 */
lws_service_fd_tsi(context, NULL, tsi);
return 0;
}
#endif
faked_service:
m = lws_service_flag_pending(context, tsi);
if (m)
@ -152,7 +164,7 @@ faked_service:
c = n;
/* any socket with events to service? */
for (n = 0; n < pt->fds_count && c; n++) {
for (n = 0; n < (int)pt->fds_count && c; n++) {
if (!pt->fds[n].revents)
continue;
@ -194,7 +206,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{
}
@ -269,8 +281,8 @@ lws_plat_inet_pton(int af, const char *src, void *dst)
}
LWS_VISIBLE lws_fop_fd_t
_lws_plat_file_open(lws_plat_file_open(struct lws_plat_file_ops *fops,
const char *filename, lws_fop_flags_t *flags)
_lws_plat_file_open(const struct lws_plat_file_ops *fops,
const char *filename, const char *vpath, lws_fop_flags_t *flags)
{
return NULL;
}
@ -306,11 +318,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,
struct lws_context_creation_info *info)
const 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);
context->max_fds, "lws_lookup");
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
@ -327,3 +339,28 @@ 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,4 +1,26 @@
#include "private-libwebsockets.h"
/*
* 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 <pwd.h>
#include <grp.h>
@ -8,14 +30,60 @@
#endif
#include <dirent.h>
int
lws_plat_socket_offset(void)
{
return 0;
}
/*
* included from libwebsockets.c for unix builds
*/
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
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((unsigned long long)tv.tv_sec * 1000000LL) + tv.tv_usec;
}
@ -30,12 +98,20 @@ 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->trunc_len)
if (wsi_eff->trunc_len)
return 1;
fds.fd = wsi->desc.sockfd;
fds.fd = wsi_eff->desc.sockfd;
fds.events = POLLOUT;
fds.revents = 0;
@ -56,29 +132,6 @@ 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;
@ -103,9 +156,10 @@ 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 */
@ -113,15 +167,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;
lws_libev_run(context, tsi);
lws_libuv_run(context, tsi);
lws_libevent_run(context, tsi);
if (context->event_loop_ops->run_pt)
context->event_loop_ops->run_pt(context, tsi);
if (!context->service_tid_detected) {
struct lws _lws;
@ -132,8 +186,9 @@ _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?
@ -147,15 +202,73 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
n = poll(pt->fds, pt->fds_count, timeout_ms);
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);
}
#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 */ {
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;
#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;
}
@ -172,18 +285,12 @@ faked_service:
c = n;
/* any socket with events to service? */
for (n = 0; n < pt->fds_count && c; n++) {
for (n = 0; n < (int)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;
@ -192,6 +299,8 @@ faked_service:
n--;
}
lws_service_do_ripe_rxflow(pt);
return 0;
}
@ -216,7 +325,8 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
#if defined(__APPLE__) || \
defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__)
defined(__OpenBSD__) || \
defined(__HAIKU__)
struct protoent *tcp_proto;
#endif
@ -230,7 +340,8 @@ 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(__CYGWIN__) || defined(__OpenBSD__) || defined (__sun) || \
defined(__HAIKU__)
/*
* didn't find a way to set these per-socket, need to
@ -238,6 +349,14 @@ 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)
@ -268,13 +387,14 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
/* Disable Nagle */
optval = 1;
#if defined (__sun)
#if defined (__sun) || defined(__QNX__)
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(__OpenBSD__) && \
!defined(__HAIKU__)
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0)
return 1;
#else
@ -292,7 +412,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, cap_value_t *cv, int count)
_lws_plat_apply_caps(int mode, const cap_value_t *cv, int count)
{
cap_t caps;
@ -309,17 +429,17 @@ _lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
#endif
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{
#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
int n;
#endif
if (info->gid != -1)
if (info->gid && info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
if (info->uid != -1) {
if (info->uid && info->uid != -1) {
struct passwd *p = getpwuid(info->uid);
if (p) {
@ -349,7 +469,7 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
#ifdef LWS_WITH_PLUGINS
#if defined(LWS_USE_LIBUV) && UV_VERSION_MAJOR > 0
#if defined(LWS_WITH_LIBUV) && UV_VERSION_MAJOR > 0
/* libuv.c implements these in a cross-platform way */
@ -417,15 +537,15 @@ lws_plat_plugins_init(struct lws_context * context, const char * const *d)
goto skip;
}
plugin = lws_malloc(sizeof(*plugin));
plugin = lws_malloc(sizeof(*plugin), "plugin");
if (!plugin) {
lwsl_err("OOM\n");
goto bail;
}
plugin->list = context->plugin_list;
context->plugin_list = plugin;
strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
plugin->name[sizeof(plugin->name) - 1] = '\0';
lws_strncpy(plugin->name, namelist[i]->d_name,
sizeof(plugin->name));
plugin->l = l;
plugin->caps = lcaps;
context->plugin_protocol_count += lcaps.count_protocols;
@ -497,7 +617,6 @@ static void
sigabrt_handler(int x)
{
printf("%s\n", __func__);
//*(char *)0 = 0;
}
#endif
@ -508,8 +627,6 @@ lws_plat_context_early_init(void)
signal(SIGPIPE, SIG_IGN);
#endif
// signal(SIGABRT, sigabrt_handler);
return 0;
}
@ -521,9 +638,6 @@ 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);
@ -532,13 +646,6 @@ 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)
@ -551,11 +658,11 @@ LWS_VISIBLE int
lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
size_t addrlen)
{
int rc = -1;
int rc = LWS_ITOSA_NOT_EXIST;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
#ifdef LWS_USE_IPV6
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
#endif
@ -564,14 +671,21 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
if (!ifc->ifa_addr)
continue;
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
lwsl_debug(" interface %s vs %s (fam %d) ipv6 %d\n", ifc->ifa_name, ifname, ifc->ifa_addr->sa_family, ipv6);
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_USE_IPV6
#ifdef LWS_WITH_IPV6
if (ipv6) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
@ -587,7 +701,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_USE_IPV6
#ifdef LWS_WITH_IPV6
case AF_INET6:
memcpy(&addr6->sin6_addr,
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
@ -597,20 +711,20 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
default:
continue;
}
rc = 0;
rc = LWS_ITOSA_USABLE;
}
freeifaddrs(ifr);
if (rc == -1) {
if (rc) {
/* check if bind to IP address */
#ifdef LWS_USE_IPV6
#ifdef LWS_WITH_IPV6
if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1)
rc = 0;
rc = LWS_ITOSA_USABLE;
else
#endif
if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1)
rc = 0;
rc = LWS_ITOSA_USABLE;
}
return rc;
@ -621,9 +735,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi)
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ);
lws_libevent_io(wsi, LWS_EV_START | LWS_EV_READ);
if (context->event_loop_ops->io)
context->event_loop_ops->io(wsi, LWS_EV_START | LWS_EV_READ);
pt->fds[pt->fds_count++].revents = 0;
}
@ -634,9 +747,9 @@ lws_plat_delete_socket_from_fds(struct lws_context *context,
{
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
lws_libevent_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
if (context->event_loop_ops->io)
context->event_loop_ops->io(wsi,
LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
pt->fds_count--;
}
@ -717,7 +830,8 @@ _lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset)
{
lws_fileofs_t r;
if (offset > 0 && offset > fop_fd->len - fop_fd->pos)
if (offset > 0 &&
offset > (lws_fileofs_t)fop_fd->len - (lws_fileofs_t)fop_fd->pos)
offset = fop_fd->len - fop_fd->pos;
if ((lws_fileofs_t)fop_fd->pos + offset < 0)
@ -771,24 +885,22 @@ _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,
struct lws_context_creation_info *info)
const struct lws_context_creation_info *info)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n = context->count_threads, fd;
int fd;
/* master context has the global fd lookup array */
context->lws_lookup = lws_zalloc(sizeof(struct lws *) *
context->max_fds);
context->max_fds, "lws_lookup");
if (context->lws_lookup == NULL) {
lwsl_err("OOM on lws_lookup array for %d connections\n",
context->max_fds);
return 1;
}
lwsl_notice(" mem: platform fd map: %5lu bytes\n",
lwsl_info(" mem: platform fd map: %5lu bytes\n",
(unsigned long)(sizeof(struct lws *) * context->max_fds));
fd = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
@ -799,26 +911,6 @@ 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);
@ -826,3 +918,52 @@ 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,7 +1,34 @@
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include "private-libwebsockets.h"
#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)
{
}
unsigned long long
time_in_microseconds()
@ -19,9 +46,10 @@ 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));
@ -81,7 +109,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--;
@ -94,8 +122,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;
@ -106,16 +134,25 @@ LWS_VISIBLE int lws_get_random(struct lws_context *context,
return n;
}
LWS_VISIBLE int lws_send_pipe_choked(struct lws *wsi)
{
LWS_VISIBLE int
lws_send_pipe_choked(struct 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->trunc_len)
if (wsi_eff->trunc_len)
return 1;
return (int)wsi->sock_send_blocking;
return (int)wsi_eff->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 };
@ -125,29 +162,11 @@ LWS_VISIBLE int lws_poll_listen_fd(struct lws_pollfd *fd)
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
return select(((int)fd->fd) + 1, &readfds, NULL, NULL, &tv);
}
LWS_VISIBLE void
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_syslog(int level, const char *line)
{
lwsl_emit_stderr(level, line);
}
@ -164,7 +183,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)
if (context == NULL || !context->vhost_list)
return 1;
pt = &context->pt[tsi];
@ -178,12 +197,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)
@ -200,6 +219,9 @@ _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];
@ -219,6 +241,9 @@ _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]);
}
@ -235,32 +260,48 @@ _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi)
timeout_ms = 0;
}
ev = WSAWaitForMultipleEvents( 1, pt->events , FALSE, timeout_ms, FALSE);
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);
if (ev == WSA_WAIT_EVENT_0) {
unsigned int eIdx;
unsigned int eIdx, err;
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) &&
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;
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;
}
if (pfd->revents & LWS_POLLOUT) {
wsi = wsi_from_fd(context, pfd->fd);
@ -269,21 +310,19 @@ _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 != 0 ) {
if (pfd->revents)
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
@ -309,7 +348,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;
@ -317,7 +356,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;
}
@ -343,7 +382,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd)
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
lws_plat_drop_app_privileges(const struct lws_context_creation_info *info)
{
}
@ -401,12 +440,12 @@ LWS_VISIBLE LWS_EXTERN int
lws_interface_to_sa(int ipv6,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
#ifdef LWS_USE_IPV6
#ifdef LWS_WITH_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
if (ipv6) {
if (lws_plat_inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) {
return 0;
return LWS_ITOSA_USABLE;
}
}
#endif
@ -420,11 +459,11 @@ lws_interface_to_sa(int ipv6,
}
if (address == INADDR_NONE)
return -1;
return LWS_ITOSA_NOT_EXIST;
addr->sin_addr.s_addr = (lws_intptr_t)address;
addr->sin_addr.s_addr = (unsigned long)(lws_intptr_t)address;
return 0;
return LWS_ITOSA_USABLE;
}
LWS_VISIBLE void
@ -499,7 +538,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);
buffer = lws_malloc(bufferlen * 2, "inet_ntop");
if (!buffer) {
lwsl_err("Out of memory\n");
return NULL;
@ -513,7 +552,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_USE_IPV6
#ifdef LWS_WITH_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
@ -542,10 +581,10 @@ LWS_VISIBLE int
lws_plat_inet_pton(int af, const char *src, void *dst)
{
WCHAR *buffer;
DWORD bufferlen = strlen(src) + 1;
DWORD bufferlen = (int)strlen(src) + 1;
BOOL ok = FALSE;
buffer = lws_malloc(bufferlen * 2);
buffer = lws_malloc(bufferlen * 2, "inet_pton");
if (!buffer) {
lwsl_err("Out of memory\n");
return -1;
@ -567,7 +606,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_USE_IPV6
#ifdef LWS_WITH_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 dstaddr;
int dstaddrlen = sizeof(dstaddr);
@ -692,14 +731,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,
struct lws_context_creation_info *info)
const 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);
lws_zalloc(sizeof(struct lws*) * context->max_fds, "win hashtable");
if (!context->fd_hashtable[i].wsi)
return -1;
@ -707,7 +746,7 @@ lws_plat_init(struct lws_context *context,
while (n--) {
pt->events = lws_malloc(sizeof(WSAEVENT) *
(context->fd_limit_per_thread + 1));
(context->fd_limit_per_thread + 1), "event table");
if (pt->events == NULL) {
lwsl_err("Unable to allocate events array for %d connections\n",
context->fd_limit_per_thread + 1);
@ -715,7 +754,7 @@ lws_plat_init(struct lws_context *context,
}
pt->fds_count = 0;
pt->events[0] = WSACreateEvent();
pt->events[0] = WSACreateEvent(); /* the cancel event */
pt++;
}
@ -743,3 +782,50 @@ 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;
}

File diff suppressed because it is too large Load diff

161
lib/roles/README.md Normal file
View file

@ -0,0 +1,161 @@
## Information for new role implementers
### Introduction
In lws the "role" is the job the wsi is doing in the system, eg,
being an http1 or h2, or ws connection, or being a listen socket, etc.
This is different than, eg, a new ws protocol or a different callback
for an existing role. A new role is needed when you want to add support for
something completely new, like a completely new wire protocol that
doesn't use http or ws.
So... what's the point of implementing the protocol inside the lws role framework?
You inherit all the well-maintained lws core functionality around:
- connection lifecycle sequencing in a valgrind-clean way
- proxy support, HTTP and Socks5
- tls support working equally on mbedTLS and OpenSSL and derivatives without any code in the role
- apis for cert lifecycle management and parsing
- event loop support working on all the lws event loops (poll, libuv , ev, and event)
- clean connection tracking and closing even on advanced event loops
- user code follows the same simple callbacks on wsi
- multi-vhost support
- core multithreaded service support with usually no locking requirement on the role code
- direct compatibility with all other lws roles + protocols in the same event loop
- compatibility with higher-level stuff like lwsws as the server application
### Code placement
The code specific to that role should live in `./lib/roles/**role name**`
If a role is asymmetic between a client and server side, like http is, it
should generally be implemented as a single role.
### Allowing control over enabling roles
All roles should add a cmake define `LWS_ROLE_**role 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 role is available in the lws build it is
trying to bind to.
If the role is disabled in cmake, nothing in its directory is built.
### Role ops struct
The role is defined by `struct lws_role_ops` in `lib/private/libwebsockets.h`,
each role instantiates one of these and fills in the appropriate ops
callbacks to perform its job. By convention that lives in
`./lib/roles/**role name**/ops-**role_name**.c`.
### Private role declarations
Truly private declarations for the role can go in the role directory as you like.
However when the declarations must be accessible to other things in lws build, eg,
the role adds members to `struct lws` when enabled, they should be in the role
directory in a file `private.h`.
Search for "bring in role private declarations" in `./lib/roles/private.h
and add your private role file there following the style used for the other roles,
eg,
```
#if defined(LWS_ROLE_WS)
#include "roles/ws/private.h"
#else
#define lwsi_role_ws(wsi) (0)
#endif
```
If the role is disabled at cmake, nothing from its private.h should be used anywhere.
### Integrating role assets to lws
If your role 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 ws role wants
to add storage in lws_vhost for enabled extensions, it declares in its private.h
```
struct lws_vhost_role_ws {
#if !defined(LWS_WITHOUT_EXTENSIONS)
const struct lws_extension *extensions;
#endif
};
```
- add your role content in one place in the lws struct, protected by `#if defined(LWS_ROLE_**role name**)`,
eg, again for LWS_ROLE_WS
```
struct lws_vhost {
...
#if defined(LWS_ROLE_WS)
struct lws_vhost_role_ws ws;
#endif
...
```
### Adding to lws available roles list
Edit the NULL-terminated array `available_roles` at the top of `./lib/context.c` to include
a pointer to your new role's ops struct, following the style already there.
```
const struct lws_role_ops * available_roles[] = {
#if defined(LWS_ROLE_H2)
&role_ops_h2,
#endif
...
```
This makes lws aware that your role exists, and it can auto-generate some things like
ALPN lists, and call your role ops callbacks for things like hooking vhost creation.
### Enabling role adoption
The primary way wsi get bound to a specific role is via the lws adoption api
`lws_adopt_descriptor_vhost()`. Add flags as necessary in `./lib/libwebsockets.h`
`enum lws_adoption_type` and follow the existing code in `lws_adopt_descriptor_vhost()`
to bind a wsi with suitable flags to your role ops.
### Implementation of the role
After that plumbing-in is completed, the role ops you declare are "live" on a wsi
bound to them via the adoption api.
The core support for wsis in lws has some generic concepts
- the wsi holds a pointer member `role_ops` that indicates which role ops the
wsi is bound to
- the wsi holds a generic uint32 `wsistate` that contains role flags and wsi state
- role flags are provided (LWSIFR_CLIENT, LWSIFR_SERVER) to differentiate between
client and server connections inside a wsi, along with helpers `lwsi_role_client(wsi)`
and `lwsi_role_server(wsi)`.
- lws provides around 30 generic states for the wsi starting from 'unconnected' through
various proxy or tunnel states, to 'established', and then various states shutting
down until 'dead socket'. The states have testable flags and helpers to discover if
the wsi state is before establishment `lwsi_state_est(wsi)` and if in the state it is
in, it can handle pollout `lwsi_state_can_handle_POLLOUT(wsi)`.
- You set the initial binding, role flags and state using `lws_role_transition()`. Afterwards
you can adjust the state using `lwsi_set_state()`.

1123
lib/roles/cgi/cgi-server.c Normal file

File diff suppressed because it is too large Load diff

102
lib/roles/cgi/ops-cgi.c Normal file
View file

@ -0,0 +1,102 @@
/*
* 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 int
rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
struct lws_cgi_args args;
assert(wsi->role_ops == &role_ops_cgi);
if (wsi->cgi_channel >= LWS_STDOUT &&
!(pollfd->revents & pollfd->events & LWS_POLLIN))
return LWS_HPI_RET_HANDLED;
if (wsi->cgi_channel == LWS_STDIN &&
!(pollfd->revents & pollfd->events & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
if (wsi->cgi_channel == LWS_STDIN &&
lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
args.ch = wsi->cgi_channel;
args.stdwsi = &wsi->parent->http.cgi->stdwsi[0];
args.hdr_state = wsi->hdr_state;
lwsl_debug("CGI LWS_STDOUT %p wsistate 0x%x\n",
wsi->parent, wsi->wsistate);
if (user_callback_handle_rxflow(wsi->parent->protocol->callback,
wsi->parent, LWS_CALLBACK_CGI,
wsi->parent->user_space,
(void *)&args, 0))
return 1;
return LWS_HPI_RET_HANDLED;
}
static int
rops_handle_POLLOUT_cgi(struct lws *wsi)
{
return LWS_HP_RET_USER_SERVICE;
}
static int
rops_periodic_checks_cgi(struct lws_context *context, int tsi, time_t now)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
lws_cgi_kill_terminated(pt);
return 0;
}
struct lws_role_ops role_ops_cgi = {
/* role name */ "cgi",
/* alpn id */ NULL,
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ rops_periodic_checks_cgi,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_cgi,
/* handle_POLLOUT */ rops_handle_POLLOUT_cgi,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ NULL,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ NULL,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ NULL,
/* writeable cb clnt, srv */ { 0, 0 },
/* close cb clnt, srv */ { 0, 0 },
/* file_handle */ 0,
};

69
lib/roles/cgi/private.h Normal file
View file

@ -0,0 +1,69 @@
/*
* 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
*/
extern struct lws_role_ops role_ops_cgi;
#define lwsi_role_cgi(wsi) (wsi->role_ops == &role_ops_cgi)
#define LWS_HTTP_CHUNK_HDR_SIZE 16
enum {
SIGNIFICANT_HDR_CONTENT_LENGTH,
SIGNIFICANT_HDR_LOCATION,
SIGNIFICANT_HDR_STATUS,
SIGNIFICANT_HDR_TRANSFER_ENCODING,
SIGNIFICANT_HDR_COUNT
};
struct lws;
/* wsi who is master of the cgi points to an lws_cgi */
struct lws_cgi {
struct lws_cgi *cgi_list;
struct lws *stdwsi[3]; /* points to the associated stdin/out/err wsis */
struct lws *wsi; /* owner */
unsigned char *headers_buf;
unsigned char *headers_start;
unsigned char *headers_pos;
unsigned char *headers_dumped;
unsigned char *headers_end;
char summary[128];
lws_filepos_t content_length;
lws_filepos_t content_length_seen;
int pipe_fds[3][2];
int match[SIGNIFICANT_HDR_COUNT];
char l[12];
int pid;
int response_code;
int lp;
unsigned char being_closed:1;
unsigned char explicitly_chunked:1;
unsigned char chunked_grace;
};

688
lib/roles/h1/ops-h1.c Normal file
View file

@ -0,0 +1,688 @@
/*
* 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>
#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)
*/
int
lws_read_h1(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_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
switch (lwsi_state(wsi)) {
case LRS_ISSUING_FILE:
return 0;
case LRS_ESTABLISHED:
if (lwsi_role_ws(wsi))
goto ws_mode;
if (lwsi_role_client(wsi))
break;
wsi->hdr_parsing_completed = 0;
/* fallthru */
case LRS_HEADERS:
if (!wsi->http.ah) {
lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
assert(0);
}
lwsl_parser("issuing %d bytes to parser\n", (int)len);
#if defined(LWS_ROLE_WS) && !defined(LWS_NO_CLIENT)
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
last_char = buf;
if (lws_handshake_server(wsi, &buf, (size_t)len))
/* Handshake indicates this session is done. */
goto bail;
/* we might have transitioned to RAW */
if (wsi->role_ops == &role_ops_raw_skt ||
wsi->role_ops == &role_ops_raw_file)
/* 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 (lwsi_state(wsi)) {
case LRS_ESTABLISHED:
case LRS_HEADERS:
goto read_ok;
case LRS_ISSUING_FILE:
goto read_ok;
case LRS_BODY:
wsi->http.rx_content_remain =
wsi->http.rx_content_length;
if (wsi->http.rx_content_remain)
goto http_postbody;
/* there is no POST content */
goto postbody_completion;
default:
break;
}
break;
case LRS_BODY:
http_postbody:
lwsl_debug("%s: http post body: remain %d\n", __func__,
(int)wsi->http.rx_content_remain);
while (len && wsi->http.rx_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->http.rx_content_remain, len);
wsi->http.rx_content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->http.cgi) {
struct lws_cgi_args args;
args.ch = LWS_STDIN;
args.stdwsi = &wsi->http.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->http.rx_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->http.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->http.cgi)
#endif
{
lwsl_info("HTTP_BODY_COMPLETION: %p (%s)\n",
wsi, wsi->protocol->name);
n = wsi->protocol->callback(wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
if (wsi->http2_substream)
lwsi_set_state(wsi, LRS_ESTABLISHED);
}
break;
}
break;
case LRS_RETURNED_CLOSE:
case LRS_AWAITING_CLOSE_ACK:
case LRS_WAITING_TO_SEND_CLOSE:
case LRS_SHUTDOWN:
ws_mode:
#if !defined(LWS_NO_CLIENT) && defined(LWS_ROLE_WS)
// lwsl_notice("%s: ws_mode\n", __func__);
if (lws_ws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
#endif
#if defined(LWS_ROLE_WS)
if (lwsi_role_ws(wsi) && lwsi_role_server(wsi) &&
/*
* for h2 we are on the swsi
*/
lws_parse_ws(wsi, &buf, (size_t)len) < 0) {
lwsl_info("%s: lws_parse_ws bailed\n", __func__);
goto bail;
}
#endif
// lwsl_notice("%s: ws_mode: buf moved on by %d\n", __func__,
// lws_ptr_diff(buf, oldbuf));
break;
case LRS_DEFERRING_ACTION:
lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
break;
case LRS_SSL_ACK_PENDING:
break;
case LRS_DEAD_SOCKET:
lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
goto bail;
// assert(0);
/* fallthru */
default:
lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
assert(0);
goto bail;
}
read_ok:
/* Nothing more to do for now */
// lwsl_info("%s: %p: read_ok, used %ld (len %d, state %d)\n", __func__,
// wsi, (long)(buf - oldbuf), (int)len, wsi->state);
return lws_ptr_diff(buf, oldbuf);
bail:
/*
* h2 / h2-ws calls us recursively in
*
* lws_read_h1()->
* lws_h2_parser()->
* lws_read_h1()
*
* pattern, having stripped the h2 framing in the middle.
*
* When taking down the whole connection, make sure that only the
* outer lws_read() does the wsi close.
*/
if (!wsi->outer_will_close)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"lws_read_h1 bail");
return -1;
}
#if !defined(LWS_NO_SERVER)
static int
lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct lws_tokens ebuf;
int n, buffered;
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
goto try_pollout;
/* any incoming data ready? */
if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
goto try_pollout;
/*
* If we previously just did POLLIN when IN and OUT were signaled
* (because POLLIN processing may have used up the POLLOUT), don't let
* that happen twice in a row... next time we see the situation favour
* POLLOUT
*/
if (wsi->favoured_pollin &&
(pollfd->revents & pollfd->events & LWS_POLLOUT)) {
// lwsl_notice("favouring pollout\n");
wsi->favoured_pollin = 0;
goto try_pollout;
}
/*
* We haven't processed that the tunnel is set up yet, so
* defer reading
*/
if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
return LWS_HPI_RET_HANDLED;
/* these states imply we MUST have an ah attached */
if ((lwsi_state(wsi) == LRS_ESTABLISHED ||
lwsi_state(wsi) == LRS_ISSUING_FILE ||
lwsi_state(wsi) == LRS_HEADERS ||
lwsi_state(wsi) == LRS_BODY)) {
if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
goto try_pollout;
}
/*
* We got here because there was specifically POLLIN...
* regardless of our buflist state, we need to get it,
* and either use it, or append to the buflist and use
* buflist head material.
*
* We will not notice a connection close until the buflist is
* exhausted and we tried to do a read of some kind.
*/
buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
switch (ebuf.len) {
case 0:
lwsl_info("%s: read 0 len a\n", __func__);
wsi->seen_zero_length_recv = 1;
lws_change_pollfd(wsi, LWS_POLLIN, 0);
#if !defined(LWS_WITHOUT_EXTENSIONS)
/*
* autobahn requires us to win the race between close
* and draining the extensions
*/
if (wsi->ws &&
(wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
goto try_pollout;
#endif
/*
* normally, we respond to close with logically closing
* our side immediately
*/
goto fail;
case LWS_SSL_CAPABLE_ERROR:
goto fail;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto try_pollout;
}
/* just ignore incoming if waiting for close */
if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
lwsl_notice("%s: just ignoring\n", __func__);
goto try_pollout;
}
if (lwsi_state(wsi) == LRS_ISSUING_FILE) {
// lwsl_notice("stashing: wsi %p: bd %d\n", wsi, buffered);
if (lws_buflist_aware_consume(wsi, &ebuf, 0, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
goto try_pollout;
}
/*
* Otherwise give it to whoever wants it according to the
* connection state
*/
#if defined(LWS_ROLE_H2)
if (lwsi_role_h2(wsi) && lwsi_state(wsi) != LRS_BODY)
n = lws_read_h2(wsi, (uint8_t *)ebuf.token, ebuf.len);
else
#endif
n = lws_read_h1(wsi, (uint8_t *)ebuf.token, ebuf.len);
if (n < 0) /* we closed wsi */
return LWS_HPI_RET_WSI_ALREADY_DIED;
lwsl_debug("%s: consumed %d\n", __func__, n);
if (lws_buflist_aware_consume(wsi, &ebuf, n, buffered))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
/*
* during the parsing our role changed to something non-http,
* so the ah has no further meaning
*/
if (wsi->http.ah &&
!lwsi_role_h1(wsi) &&
!lwsi_role_h2(wsi) &&
!lwsi_role_cgi(wsi))
lws_header_table_detach(wsi, 0);
/*
* He may have used up the writability above, if we will defer
* POLLOUT processing in favour of POLLIN, note it
*/
if (pollfd->revents & LWS_POLLOUT)
wsi->favoured_pollin = 1;
return LWS_HPI_RET_HANDLED;
}
/*
* He may have used up the writability above, if we will defer POLLOUT
* processing in favour of POLLIN, note it
*/
if (pollfd->revents & LWS_POLLOUT)
wsi->favoured_pollin = 1;
try_pollout:
/* this handles POLLOUT for http serving fragments */
if (!(pollfd->revents & LWS_POLLOUT))
return LWS_HPI_RET_HANDLED;
/* one shot */
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_notice("%s a\n", __func__);
goto fail;
}
/* clear back-to-back write detection */
wsi->could_have_pending = 0;
if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n", __func__);
lwsi_set_state(wsi, LRS_ESTABLISHED);
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
lwsl_info("failed at set pollfd\n");
goto fail;
}
}
if (!wsi->hdr_parsing_completed)
return LWS_HPI_RET_HANDLED;
if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
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 = user_callback_handle_rxflow(wsi->protocol->callback, wsi,
LWS_CALLBACK_HTTP_WRITEABLE,
wsi->user_space, NULL, 0);
if (n < 0) {
lwsl_info("writeable_fail\n");
goto fail;
}
return LWS_HPI_RET_HANDLED;
}
/* >0 == completion, <0 == error
*
* We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
* it's done. That's the case even if we just completed the
* send, so wait for that.
*/
n = lws_serve_http_file_fragment(wsi);
if (n < 0)
goto fail;
return LWS_HPI_RET_HANDLED;
fail:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"server socket svc fail");
return LWS_HPI_RET_WSI_ALREADY_DIED;
}
#endif
static int
rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi,
struct lws_pollfd *pollfd)
{
// lwsl_notice("%s: %p: wsistate 0x%x %s, revents 0x%x\n", __func__, wsi,
// wsi->wsistate, wsi->role_ops->name, pollfd->revents);
#ifdef LWS_WITH_CGI
if (wsi->http.cgi && (pollfd->revents & LWS_POLLOUT)) {
if (lws_handle_POLLOUT_event(wsi, pollfd))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
#endif
if (lws_is_flowcontrolled(wsi))
/* We cannot deal with any kind of new RX because we are
* RX-flowcontrolled.
*/
return LWS_HPI_RET_HANDLED;
#if !defined(LWS_NO_SERVER)
if (!lwsi_role_client(wsi)) {
int n;
lwsl_debug("%s: %p: wsistate 0x%x\n", __func__, wsi, wsi->wsistate);
n = lws_h1_server_socket_service(wsi, pollfd);
if (n != LWS_HPI_RET_HANDLED)
return n;
if (lwsi_state(wsi) != LRS_SSL_INIT)
if (lws_server_socket_service_ssl(wsi, LWS_SOCK_INVALID))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
return LWS_HPI_RET_HANDLED;
}
#endif
#ifndef LWS_NO_CLIENT
if ((pollfd->revents & LWS_POLLIN) &&
wsi->hdr_parsing_completed && !wsi->told_user_closed) {
/*
* In SSL mode we get POLLIN notification about
* encrypted data in.
*
* But that is not necessarily related to decrypted
* data out becoming available; in may need to perform
* other in or out before that happens.
*
* simply mark ourselves as having readable data
* and turn off our POLLIN
*/
wsi->client_rx_avail = 1;
lws_change_pollfd(wsi, LWS_POLLIN, 0);
//lwsl_notice("calling back %s\n", wsi->protocol->name);
/* let user code know, he'll usually ask for writeable
* callback and drain / re-enable it there
*/
if (user_callback_handle_rxflow(
wsi->protocol->callback,
wsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,
wsi->user_space, NULL, 0)) {
lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
return LWS_HPI_RET_HANDLED;
}
#endif
// if (lwsi_state(wsi) == LRS_ESTABLISHED)
// return LWS_HPI_RET_HANDLED;
#if !defined(LWS_NO_CLIENT)
if ((pollfd->revents & LWS_POLLOUT) &&
lws_handle_POLLOUT_event(wsi, pollfd)) {
lwsl_debug("POLLOUT event closed it\n");
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
if (lws_client_socket_service(wsi, pollfd, NULL))
return LWS_HPI_RET_WSI_ALREADY_DIED;
#endif
return LWS_HPI_RET_HANDLED;
}
int rops_handle_POLLOUT_h1(struct lws *wsi)
{
if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
return LWS_HP_RET_USER_SERVICE;
if (lwsi_role_client(wsi))
return LWS_HP_RET_USER_SERVICE;
return LWS_HP_RET_BAIL_OK;
}
static int
rops_write_role_protocol_h1(struct lws *wsi, unsigned char *buf, size_t len,
enum lws_write_protocol *wp)
{
#if 0
/* if not in a state to send stuff, then just send nothing */
if ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK)) {
//assert(0);
lwsl_debug("binning %d %d\n", lwsi_state(wsi), *wp);
return 0;
}
#endif
return lws_issue_raw(wsi, (unsigned char *)buf, len);
}
static int
rops_alpn_negotiated_h1(struct lws *wsi, const char *alpn)
{
lwsl_debug("%s: client %d\n", __func__, lwsi_role_client(wsi));
#if !defined(LWS_NO_CLIENT)
if (lwsi_role_client(wsi)) {
/*
* If alpn asserts it is http/1.1, server support for KA is
* mandatory.
*
* Knowing this lets us proceed with sending pipelined headers
* before we received the first response headers.
*/
wsi->keepalive_active = 1;
}
#endif
return 0;
}
static int
rops_destroy_role_h1(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
struct allocated_headers *ah;
/* we may not have an ah, but may be on the waiting list... */
lwsl_info("%s: ah det due to close\n", __func__);
__lws_header_table_detach(wsi, 0);
ah = pt->http.ah_list;
while (ah) {
if (ah->in_use && ah->wsi == wsi) {
lwsl_err("%s: ah leak: wsi %p\n", __func__, wsi);
ah->in_use = 0;
ah->wsi = NULL;
pt->http.ah_count_in_use--;
break;
}
ah = ah->next;
}
return 0;
}
struct lws_role_ops role_ops_h1 = {
/* role name */ "h1",
/* alpn id */ "http/1.1",
/* check_upgrades */ NULL,
/* init_context */ NULL,
/* init_vhost */ NULL,
/* destroy_vhost */ NULL,
/* periodic_checks */ NULL,
/* service_flag_pending */ NULL,
/* handle_POLLIN */ rops_handle_POLLIN_h1,
/* handle_POLLOUT */ rops_handle_POLLOUT_h1,
/* perform_user_POLLOUT */ NULL,
/* callback_on_writable */ NULL,
/* tx_credit */ NULL,
/* write_role_protocol */ rops_write_role_protocol_h1,
/* encapsulation_parent */ NULL,
/* alpn_negotiated */ rops_alpn_negotiated_h1,
/* close_via_role_protocol */ NULL,
/* close_role */ NULL,
/* close_kill_connection */ NULL,
/* destroy_role */ rops_destroy_role_h1,
/* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,
LWS_CALLBACK_HTTP_WRITEABLE },
/* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP,
LWS_CALLBACK_CLOSED_HTTP },
/* file_handle */ 0,
};

27
lib/roles/h1/private.h Normal file
View file

@ -0,0 +1,27 @@
/*
* 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_H1
*
* Most of the h1 business is defined in the h1 / h2 common roles/http dir
*/
extern struct lws_role_ops role_ops_h1;
#define lwsi_role_h1(wsi) (wsi->role_ops == &role_ops_h1)

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