Compare commits

..

966 commits

Author SHA1 Message Date
7f6db4fe8d fix Spec file by adding / removing new files to the package 2017-08-27 14:59:52 +02:00
Andy Green
debb7aa043 LWS_WITH_ACCESS_LOG: add referrer to log format and quotes around user agent
This also forces any double-quotes in the UA or referrer to be single-quotes.

This aligns to log to the "combined log format" described here

https://httpd.apache.org/docs/1.3/logs.html#combined
2017-08-27 20:18:48 +08:00
Andy Green
ca045d4a8e docs: lwsws under gdb / valgrind from ml 2017-08-27 09:50:04 +08:00
Andy Green
4ce725903d lwsws: survive LWS_WITHOUT_EXTENSIONS 2017-08-26 12:18:47 +08:00
Petar Paradzik
a2943ca41d cgi: fix killing cgi before draining its output
CGI's which don't have content-length nor they are explicitly chunked
are killed immediately after being reaped even if their output has not
being drained. This is fixed by deferring from killing them like those
which are explicitly chunked.

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-08-26 12:16:33 +08:00
Petar Paradzik
04134742f9 cgi: fix overriding 'PATH' environment variable
Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-08-26 12:15:51 +08:00
Andy Green
2e5110e731 cgi: provide constants for reason_bf 2017-08-26 12:15:40 +08:00
Petar Paradzik
afc9c0ac26 cgi: add support for manual chunking of CGI output
In cases when CGI output doesn't contain content-length nor it is
explicitly chunked, do manual chunking of CGI output.

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-08-26 12:04:15 +08:00
Petar Paradzik
5b23b8c99f cgi: remove and kill CGI after closing its STDOUT handler
Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-08-26 12:00:16 +08:00
Petar Paradzik
b66e8e1898 cgi: fix not getting POLLHUP on fd associated to CGI STDOUT
Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-08-26 11:59:58 +08:00
Mike Messina
0bb3646256 win32: skip delay in WSAWaitForMultipleEvents if POLLOUT expected
https://github.com/warmcat/libwebsockets/issues/994
2017-08-22 21:32:47 +08:00
Andy Green
c60b2413a4 ah: double-check timeouts on all active ah independent of wsi and dump info 2017-08-21 08:55:13 +08:00
Andy Green
58195fbc1e esp-idf v3: account for optional SHA256 when walking segments 2017-08-19 13:14:34 +08:00
Andy Green
c2abf59c68 esp32: align build system for esp-idf v3 2017-08-19 08:14:49 +08:00
Cory McWilliams
4b24369d64 Subject: Mismatched lws_zalloc / free 2017-08-17 07:30:23 +08:00
Andy Green
872e8d7e9d docs: swap _all_protocol_vhost cut and paste
https://github.com/warmcat/libwebsockets/issues/989
2017-08-16 15:21:22 +08:00
Andy Green
5da9ce2f06 ah: reuse at end of transaction has no timeout
If we complete a transaction but end up keeping the ah, we must force
a timeout on it.  Otherwise a bad bot could keep the socket open and
exhaust the ah pool.
2017-08-15 07:58:53 +08:00
lnmx
b93c057472 send content-type when LWS_WITH_RANGES=OFF
https://github.com/warmcat/libwebsockets/pull/987

With the RANGES feature disabled, lws_serve_http_file would
not add the content-type header to the response.
2017-08-12 20:50:25 +08:00
Andy Green
5a38d88fdd handle same vh protocol reinsert 2017-08-12 20:50:21 +08:00
Andy Green
a9f74f2dbe lwsws: remove no longer extant D option from help string
https://github.com/warmcat/libwebsockets/issues/986
2017-08-09 07:40:19 +08:00
Andy Green
219a367a4c esp32: allow return of default vhost at init time 2017-08-06 06:53:38 +08:00
Andy Green
93a5b586a3 lws_callback_all_protocol_vhost_args 2017-08-05 10:38:59 +08:00
Andy Green
040b408029 ping test app: avoid FPE when no packets received 2017-08-04 13:28:01 +08:00
Andy Green
16ef37ef5d close path: make sure a second time timeout and ssl buffered lists are scrubbed
lws_meta children can have a different close path
2017-08-04 13:27:34 +08:00
Andy Green
e6bd6296bd v2.3.0 2017-07-28 14:27:25 +08:00
Andy Green
4a9c23e9ec coverity 182069: coverity confused by use of bool as array index 2017-07-28 14:25:25 +08:00
Andy Green
c6233ce403 coverity 182068: 155650: unnecessary check against illegal NULL 2017-07-28 14:19:24 +08:00
Andy Green
7849c5a8ad pmd: autobahn fixes 2017-07-28 13:12:03 +08:00
Andy Green
414f114b8f attack.sh: adapt to changes 2017-07-28 07:04:54 +08:00
Andy Green
855f7e8712 log: downgrade logging for ah wait 2017-07-28 07:04:47 +08:00
Andy Green
9f31e94e09 correct status payload size 2017-07-28 07:03:57 +08:00
Andy Green
855453d1ae lws_meta: explicitly declare all of lws_protocols members for ESP32 2017-07-27 08:27:34 +08:00
Andy Green
d86641ed3a libevent: update to use static plugins and work with new libevent2
Plus fix broken indent style
2017-07-27 07:57:59 +08:00
Andy Green
41c15511eb test-server-libuv: add lws_meta 2017-07-27 07:29:56 +08:00
Andy Green
d766c99861 dummy handler: LWS_CALLBACK_HTTP_FILE_COMPLETION 2017-07-27 07:26:00 +08:00
Andy Green
ba45f7cf9f ah: allow configurable ah hold timeout 2017-07-26 11:49:41 +08:00
Andy Green
19a320a578 http2: remove cmake option leave code in for now
https://github.com/warmcat/libwebsockets/issues/979
2017-07-25 17:36:31 +08:00
namowen
61e58885f4 client: ipv6 reject when lws_getaddrinfo46 failed
https://github.com/warmcat/libwebsockets/issues/978
2017-07-25 17:14:37 +08:00
Andy Green
3562e441e3 client-fix-header-stash-leak-on-close-before-success 2017-07-21 21:49:24 +08:00
Andy Green
003bd7dcee client: fix hdr stash leak 2017-07-21 21:34:46 +08:00
Andy Green
75bbb3b2c0 client: always set port even if sockfd already created 2017-07-21 21:34:46 +08:00
Andy Green
8ccc64679f client: fix redirect ssl to ssl 2017-07-21 20:25:32 +08:00
Andy Green
6c09952065 url cleaning: leave // after http[s]: alone 2017-07-21 20:04:02 +08:00
Andy Green
09f3947b4c lws_intptr_t: fix ordering
https://github.com/warmcat/libwebsockets/issues/973
2017-07-21 19:25:41 +08:00
Andy Green
941e93ea33 test-server-libuv: also call context_destroy2 when using foreign loop
https://github.com/warmcat/libwebsockets/issues/972
2017-07-21 11:09:03 +08:00
Andy Green
c9da1ffa2e appveyor: remove cache 2017-07-19 15:29:38 +08:00
Andy Green
ad15082563 coverity-181580: supposedly dead code 2017-07-19 14:47:30 +08:00
Andy Green
2d313bdc02 coverity 181577: lejp_conf loop on calling uv_loop_close to keep coverity happy 2017-07-19 14:37:04 +08:00
Andy Green
3526fde154 coverity 181573: false positive since lws_is_ssl returns a bool 2017-07-19 14:37:04 +08:00
Andy Green
bd1dd7efd4 coverity 181576: remove dead code to keep coverity happy 2017-07-19 14:37:04 +08:00
Andy Green
1690581cd2 coverity 181574: confirm uri_ptr non-null before deref 2017-07-19 14:19:03 +08:00
Andy Green
3c360d5192 coverity 181579: check result of malloc as intended 2017-07-19 14:17:39 +08:00
Andy Green
8a4881a142 coverity 181575: check vhost iface non-null if using via bind_iface 2017-07-19 14:16:32 +08:00
Andy Green
6f11c1361a lws-meta 2017-07-19 08:59:42 +08:00
Andy Green
3b0066cb3f close: make close notification go through writable
Until now we took the approach if just writing the close notification
broke something, we didn't care because we were closing the connection
anyway.

But with lws_meta, breaking stuff in the parent connection would be a
sticky problem outliving the closing child connection.

So this adds a new wsi state LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION
and makes the send go via the writable callback mechanism.
2017-07-19 08:55:57 +08:00
Andy Green
faa1526b39 lws-vhost-destroy
Adds a new api lws_vhost_destroy(struct lws_vhost *) which allows dynamic removal of vhosts.

The external api calls two parts of internal helpers that get reused for context destroy.

The second part is called deferred by 5s... this is to ensure that event library objects
composed into structs owned by the vhost all have a chance to complete their close
asynchronously.  That should happen immediately, but it requires us to return to the
event loop first.

The vhost being removed is deleted from the context vhost list by the first part, and does
not block further removals or creation during the delay for the deferred freeing of the
vhost memory.

Part 1:

 - if the vhost owned a listen socket needed by other vhosts listening on same iface + port, the listen
   socket is first handed off to another vhost so it stays alive

 - all wsi still open on the vhost are forcibly closed (including any listen socket still attached)

 - inform all active protocols on the vhost they should destroy themselves

 - remove vhost from context vhost list (can no longer be found by incoming connections)

 - add to a "being destroyed" context list and schedule the second part to be called in 5s

Part 2:

 - remove us from the being destroyed list

 - free all allocations owned by the vhost

 - zero down the vhost and free the vhost itself


In libwebsockets-test-server, you can send it a SIGUSR1 to have it toggle the creation and destruction of
a second vhost on port + 1.
2017-07-19 08:51:43 +08:00
Andy Green
632a0acc99 clean: fixes for appveyor warnings 2017-07-19 04:25:20 +08:00
Andy Green
dbd9262ac5 ah: enforce waiting list detach 2017-07-19 04:19:25 +08:00
Andy Green
d5f960f14b valgrind: avoid complaints from plugin loading 2017-07-19 04:18:11 +08:00
Andy Green
5106e9141f explicit vhosts: only check context for flag
Although the test apps reuse the context info directly and so inherit the
flag state there when creating vhosts, users might generate a fresh info
without the flag for vhost creation.  So just go by what was given at
context creation time.
2017-07-19 04:12:26 +08:00
Jesse Engle
61cc61817e Subject: Eliminated 'unused variable' compiler warning generated with -DLWS_WITH_NO_LOGS=ON.
The unused variable was only declared for use in a log macro that's
compiled out with the above compiler switch. I removed the declaration
and casted the variable at each use in the block.

AG: convert to void case reference irrespective of logging enabled.
AG: travis.yml: add -DLWS_WITH_NO_LOGS=ON that also enables lwsws + cgi code
2017-07-19 04:11:57 +08:00
Andy Green
3077b7776e valgrind: stop openssl still reachable complaints 2017-07-19 04:11:30 +08:00
Andy Green
81d5899c89 valgrind: fix leak in caps handling 2017-07-19 04:10:36 +08:00
Andy Green
a15007269e libuv: add helper for clean valgrind with foreign loop 2017-07-19 04:10:07 +08:00
Andy Green
ffa5898afe esp32: increase dir depth for include bodge 2017-07-14 10:26:39 +08:00
Andy Green
6f2a470ee2 test-html: unify ws open function 2017-07-14 10:26:39 +08:00
Andy Green
e2a926de2f linkedlist helpers 2017-07-09 10:11:59 +08:00
Andy Green
05d74e45dc mirror: multiple mirror contexts by mirror= url arg
By default mirror acts the same as before.

However if you access the test server with a url containing "?mirror=<name>", the session will bind to
a mirror instance private to "?mirror=<name>".  Only sessions that used the same 'mirror=' name can
share the drawings, mirror instances with a different name (including the default "" name) are unaffected.
2017-07-08 16:03:40 +08:00
Andy Green
1b41322c28 test server: only init ssl when --ssl in use 2017-07-08 16:03:39 +08:00
Andy Green
9b4fa24909 client: reject init_client_ssl more than once 2017-07-08 16:03:38 +08:00
Andy Green
7262e14dc1 lws_intptr_t 2017-07-07 08:32:04 +08:00
Andy Green
6a89c7e931 lws_return_http_status: if not in HTTP/2, restrict to a single write 2017-06-29 11:26:22 +08:00
Andy Green
ff9a24de1c parsing: if we ended on a set of headers and read a new buffer mark as more_rx_waiting to avoid dropping the ah 2017-06-29 10:13:29 +08:00
Boutoukoat
e4d8acc85a client: use right state machine
https://github.com/warmcat/libwebsockets/issues/951
2017-06-28 22:25:57 +08:00
Andy Green
a637d8f41f file serve: defer transaction completed to HTTP_FILE_COMPLETION 2017-06-28 14:27:09 +08:00
Andy Green
8f4f692945 ah: require parsing complete before detach
Introduce helpers to force to detachable state and to test the ah is
in a detachable state.

Require not only the ah rx buffer is all used, but that the
wsi has completed a full set of headers.
2017-06-28 12:13:13 +08:00
Andy Green
0b629d4037 test-server-libev: disable Werror just on server-libev.c to workaround libev dirt 2017-06-28 11:37:15 +08:00
Andy Green
0e222ab084 test-server-libuv: add missing call to lws_context_destroy2() 2017-06-28 11:37:15 +08:00
Andy Green
5939d3a961 test servers: convert to static inclusion of plugins
This eliminates the duplicated implementations of the test protocols,
except dumb-increment (which requires libuv).

This has various advantages, including bringing all the test servers
up to the same set of protocols support.

Triggered by finding a bug in server status protocol that was long
ago fixed in the plugins version.
2017-06-28 11:16:48 +08:00
Andy Green
55d9037c32 test-server-status: increase tx size to avoid WRITEABLE loops 2017-06-28 10:35:40 +08:00
Andy Green
bd23a401f6 transaction_completed: ignore if parsing not complete 2017-06-28 10:04:57 +08:00
Andy Green
47da96664f test-server-http: no need to complete transaction early since FILE_COMPLETION will do it 2017-06-28 10:04:57 +08:00
Andy Green
04830cceef ah reset: enforce reset of parsing_complete 2017-06-28 10:04:57 +08:00
Andy Green
eb7233ae97 WITH_STATS: remove accidental dependency on ssl 2017-06-28 10:04:57 +08:00
Andy Green
34ef9743d2 serving: protect file sending from downgrading to waiting pipelined headers
https://github.com/warmcat/libwebsockets/issues/946
2017-06-28 10:04:57 +08:00
Andy Green
12a9592426 cmake: fix logic for individual test client build disable 2017-06-28 09:50:54 +08:00
Andy Green
ede9ad2b13 client: add libuv support to lws_client_reset
More direct solution to problem described in

https://github.com/warmcat/libwebsockets/pull/940
2017-06-26 08:36:49 +08:00
Andy Green
abc2a5cd2e windows: SO_EXCLUSIVEADDRUSE
https://github.com/warmcat/libwebsockets/issues/942
2017-06-26 08:36:49 +08:00
Andy Green
d6394b6dba client ssl: remove now meaningless check for non-server vhost 2017-06-21 08:24:44 +08:00
Andy Green
c70f6692f8 client: getaddrinfo refactor
https://github.com/warmcat/libwebsockets/issues/926
2017-06-20 15:56:48 +08:00
Andy Green
3ff720ff66 support openssl info callback
https://github.com/warmcat/libwebsockets/issues/936
2017-06-20 11:46:49 +08:00
Andy Green
1725332d47 pkgconfig
https://github.com/warmcat/libwebsockets/issues/933
2017-06-19 12:29:48 +08:00
Sergey Kovalevich
be4efcfe58 Subject: LWS_UNUSED 2017-06-19 10:57:39 +08:00
Andy Green
31e26a4fab esp32: make button debounce also available in factory 2017-06-15 07:59:22 +08:00
Andy Green
1e762dcde4 esp32: add ar ranlib and linker cross paths 2017-06-15 07:47:18 +08:00
Andy Green
581b86efd0 HTTP_PROXY: make usable 2017-06-14 09:53:09 +08:00
Andy Green
449eec9b54 client: add iface in connection info, dont use vhost iface 2017-06-14 09:45:30 +08:00
Andy Green
af718ff5c2 LWS_WITH_NO_LOGS: take care about unused array for log generation 2017-06-09 21:46:26 +08:00
WebsocketUser
0be9e98aae win32: enable 64-bit file lengths
https://github.com/warmcat/libwebsockets/issues/916

AG added more appveyor warning cleaning and stitched in cmake tests
2017-06-09 20:20:42 +08:00
Leonardo Maccari Rufino
568aae9c2d Subject: Fix compile error in GCC 4.1.2 2017-06-08 05:41:00 +08:00
Andy Green
02638f6758 large files: convert content-length to use lws_filepos_t 2017-06-07 08:20:18 +08:00
Andy Green
37053b3a9c content-length api: unsigned long to lws_filepos_t 2017-06-07 08:13:11 +08:00
Leonardo Maccari Rufino
393b38aed9 Subject: Support to bind accepted socket to device on Linux
AG: move new member to end of info,
    allow info member even on nonsupporting platform,
    document requires root,
    apply only to listen skt before we drop root,
    add -k to test server to allow testing
2017-06-07 08:13:11 +08:00
Andy Green
156363f3de capabilities support 2017-06-07 06:49:20 +08:00
Leonardo Maccari Rufino
af7f943e05 Subject: windows: support to bind to a specific IPv6 address 2017-06-06 07:44:50 +08:00
Leonardo Maccari Rufino
2ce39fe26c Subject: Support to IPv6 on Windows 2017-06-01 06:55:50 +08:00
Andy Green
a4f88d543e NULL protocol: make sure vhost same protocol linklist gets correct protocol index 2017-05-31 09:45:28 +08:00
Andy Ning
ce2e74e5e3 Subject: lws_stats: fix compile error on VS2013
Use LWS_INLINE instead of inline in libwebsockets.h to
make it compatible on both Linux and Windows.

Signed-off-by: Andy Ning <andy.ning@windriver.com>
2017-05-31 07:44:08 +08:00
Andy Green
978605b39e esp32: disable LWS_WITH_STATS by default 2017-05-30 09:01:32 +08:00
Sergey Kovalevich
989ff82ca0 Subject: Fixed value of LIBWEBSOCKETS_LIBRARIES_STATIC 2017-05-30 08:59:15 +08:00
Sergey Kovalevich
5d1d756106 Subject: Fixed build in scope of a project (add_subdirectory) 2017-05-30 08:58:37 +08:00
emptyVoid
348887ea53 Subject: Direct extension confirmation to the negotiated protocol's callback
Fixes #897

Notice this means if you want to globally ban an extension, you should
no longer include the extension at context creation time.
2017-05-29 08:30:26 +08:00
Petar Paradzik
3ec32b1762 Subject: libuv: Fix closing handle multiple times
Sometimes "Assertion failed: !uv__is_closing(handle)" happens because
handle is being closed multiple times. To fix this, "uv_is_closing"
is added before calling "uv_close".

Signed-off-by: Petar Paradzik <petar.paradzik@sartura.hr>
2017-05-23 23:49:14 +08:00
Andy Green
49769a7c24 esp32: otf and use group-role as hostname if present 2017-05-22 14:01:08 +08:00
Andy Green
decbbc506b esp32: debounced button events 2017-05-21 07:38:49 +08:00
Andy Green
c28f436098 esp32: led network state helper 2017-05-21 07:33:09 +08:00
Andy Green
a369b42910 cgi: close unforked std fds 2017-05-20 08:05:27 +08:00
Andy Green
d12b3df953 LICENSE: clarify exceptions also apply to LGPL self-refernences
via Mike Atamas, Counsel for Epic Games
2017-05-19 09:25:54 +08:00
Andy Green
3844988b6c cgi: apply chunked grace period only when explicitly chunked 2017-05-19 08:33:33 +08:00
Andy Green
a6a68785f7 cgi: handle no body to keep events coming 2017-05-18 23:20:12 +08:00
Andy Green
de12c860db cgi: allow time travelling headers to decide response code
https://github.com/warmcat/libwebsockets/issues/899
2017-05-18 21:19:57 +08:00
Andy Ning
ed92b6dfe7 client: added socks5 proxy support
AG:

 - move creation info members to end of struct
 - add LWS_WITH_SOCKS5 CMake var, defaults to OFF
 - cast away some warnings about signed / unsigned in strncpy

Signed-off-by: Andy Ning <andy.ning@windriver.com>
2017-05-17 06:18:45 +08:00
Andy Green
8f16f92cac plugins: group 2017-05-16 20:21:47 +08:00
Andy Green
3cf570ec52 esp32: group tracking 2017-05-16 19:35:55 +08:00
Andy Green
3198446d3c adoption: close socket manually if fails before add fds 2017-05-15 08:10:08 +08:00
Andy Green
fbc1ff6e7b stats: ah and ssl restriction stats 2017-05-15 07:30:06 +08:00
Andy Green
b778cc54ca esp32: group mdns 2017-05-14 14:55:15 +08:00
Andy Green
b2f8bc5638 esp32: multi ap slots 2017-05-13 10:26:59 +08:00
Andy Green
54236bd437 esp32: multi ap slots plus PEM certs and parallel build fixes 2017-05-11 15:02:01 +08:00
Andy Green
a7def3ce44 LWS_WITH_STATS 2017-05-09 14:19:43 +08:00
Andy Green
aff8d237f8 lws_write: report and reject suspicious lengths 2017-05-09 14:18:40 +08:00
Andy Green
2790d5b28c esp32: add leds sine fade helper 2017-05-09 14:18:40 +08:00
Andy Green
5468812946 esp32: reapply pending count just for esp32 2017-05-07 08:19:55 +08:00
Andy Green
57e020a826 plugin-standalone: refactor cmake part to ease multiple sources 2017-05-06 06:38:59 +08:00
Andy Green
e304d7bba7 esp32: mac build: use gstat if exists 2017-05-05 02:17:28 +08:00
Andy Green
a1210f73af non-ssl: return 0 on pending
https://github.com/warmcat/libwebsockets/issues/887
2017-05-03 21:28:26 +08:00
Andy Green
f13db3e722 snprintf: move contributed ssl patch to lws_snprintf 2017-05-03 20:25:16 +08:00
Andy Green
d1aa92011b esp32: client: SO_SNDBUF not usable 2017-04-29 00:55:06 +08:00
Andy Green
6384eb79e0 esp32: return random length from lws_get_random 2017-04-28 11:54:27 +08:00
Andy Green
98218bd6b7 esp32: return random count 2017-04-28 11:53:28 +08:00
Joel Winarske
991f6ec644 Subject: windows: fops write support 2017-04-20 07:24:09 +08:00
Martin Milata
aeb3397c8f Subject: ssl: stop spinning on close 2017-04-19 20:49:44 +08:00
dspname
ca6242a1d3 windows: constify first arg of plat fops open
https://github.com/warmcat/libwebsockets/issues/871
2017-04-19 20:28:48 +08:00
Andy Green
0b9686224d windows: align choked trunc checking with unix 2017-04-18 15:17:22 +08:00
Andy Green
ff151d0710 esp32: update for soc refactor in esp-idf 9edab21385b6349340d7e3ac27b730e44a127fd3 2017-04-17 07:40:15 +08:00
Gecko
6018c0519a Subject: Save copy of ah pointer even with WS client so that HTTP
error can be captured by calling lws_http_client_http_response.
2017-04-10 12:37:45 +08:00
Andy Green
f58241c4f2 client: allow 201 2017-04-09 07:56:41 +08:00
Andy Green
ba9d639792 ssl: only apply restriction if nonzero for vhost 2017-04-07 20:51:44 +08:00
Silas Parker
b4b3da06a1 bind protocol: fix for NULL names 2017-04-07 18:22:54 +08:00
Andy Green
4adf590e71 esp32-fix-random 2017-04-07 18:20:21 +08:00
Andy Green
8a74348839 esp32: workaround cant find TLS_client_method 2017-04-07 11:29:57 +08:00
Andy Green
47bbb044ad raw: defer creation callback until after fds inserted 2017-04-07 11:25:55 +08:00
Andy Green
2b9fff73f9 boilerplate: add back missing Lesser that cut-and-pasted itself around 2017-04-06 23:01:34 +08:00
Martin Milata
d7d8c081aa Subject: ssl: fix OpenSSL client method detection 2017-04-06 22:51:42 +08:00
Andy Green
19242db55b raw: take care about same vh protocol linked list 2017-04-06 13:49:17 +08:00
Andy Green
54c22623ab adopt: use default protocol in given vhost 2017-04-06 12:47:42 +08:00
Andy Green
00081a2b1f ssl: OpenSSL v1.1 deprecated TLSv1_2_client_method 2017-04-06 08:32:03 +08:00
Andy Green
6cae994750 adopt: LWS_SERVER_OPTION_ONLY_RAW to indicate a vhost only serves raw 2017-04-06 07:57:45 +08:00
Andy Green
a7326fc8b5 esp32: force bash 2017-04-06 04:44:25 +08:00
Andy Green
36e04f33f5 esp32: ROMFS use checksum as mtime so ETAG caching works 2017-04-05 10:44:28 +08:00
Andy Green
89212d6668 pollout: handle request for pollout during pollout service 2017-04-05 08:30:55 +08:00
Renyaow
4ae029c3a1 windows: _snprintf_s
https://github.com/warmcat/libwebsockets/issues/859
2017-04-05 01:55:38 +08:00
Andy Green
d1dda25c6d esp32: allow no factory upload info 2017-04-04 08:46:51 +08:00
Andy Green
34822f190d esp32: enforce ssl nonblocking 2017-04-03 14:09:37 +08:00
Andy Green
30195eb79d esp32: dont require factory button if no ssl certs yet 2017-04-03 11:56:33 +08:00
Andy Green
7faa71637f logs: reduce ah err to info 2017-04-02 13:01:07 +08:00
Andy Green
00ae90978b esp32: separate factory setup 2017-03-31 20:05:10 +08:00
paularmitt
422f56c9dc windows: need LWS_INLINE 2017-03-31 19:57:24 +08:00
Andy Green
db64bfcefa lws_remove_child_from_any_parent: clear parent pointer and fix failure message 2017-03-30 08:31:58 +08:00
Olivier Basson
73e12e7b93 ev: stop event listeners during context destroy
I think I've found a bug in libev backend, in function lws_libev_io(). I'm using latest version from master branch.

When deleting a context with active connections via lws_context_destroy(), context->being_destroyed is set to 1 early in the function, before the loop calling lws_close_free_wsi() on each active connection.
lws_close_free_wsi() calls remove_wsi_socket_from_fds(), which calls lws_libev_io(), and here is my problem :

lws_libev_io() returns without doing anything if context->being_destroyed is set, so libev callbacks for deleted connections file descriptors stay registered after context is destroyed, which may lead to segfault/undefined behaviour if these file descriptors get reused later (which would trigger the callbacks).

I think the "if (!pt->io_loop_ev || context->being_destroyed) return;" statement should be replaced with " if (!pt->io_loop_ev) return;"

This fixes the problem for me and I have not seen any side effect yet. Moreover, libuv backend does not have such a test.
2017-03-29 08:22:19 +08:00
Andy Green
7a0dead82a service: always restrict rx to serve_buf_size 2017-03-29 08:22:19 +08:00
Andy Green
d58353f98a pmd: handle case we are already on drain list
Provide internal helper for adding to list that takes care of the
case we are already on the list.

https://github.com/warmcat/libwebsockets/issues/847
2017-03-26 10:19:34 +08:00
Andy Green
7aadd14398 pmd: align client rx sm with server one 2017-03-25 08:48:23 +08:00
Andy Green
92f0200204 client: zero length read indicates peer shutdown 2017-03-25 08:47:30 +08:00
luk65
a4d23648f7 solaris: handle big-endian
https://github.com/warmcat/libwebsockets/issues/846
2017-03-24 06:51:06 +08:00
Andy Green
311e3a585d spa: reject junk after finalization 2017-03-23 19:30:29 +08:00
Andy Green
629e356bb2 test-server-v2.0: disable setting default protocol 2017-03-22 21:24:04 +08:00
Andy Green
73ff23e288 debug: reduce spew for debug log level 2017-03-22 21:24:04 +08:00
Andy Green
49036d571f cgi: avoid spin on php 2017-03-22 21:24:04 +08:00
Aditya Tirumala
ec50ebac12 Subject: Libevent: Initial Support
* Added libevent support functionality into lib/libevent.c
* Added test-server-libevent for testing
2017-03-22 05:36:25 +08:00
Andy Green
f84338ac1c test-server-v2.0: add string.h include
via Joel Winarske on ml
2017-03-22 05:29:22 +08:00
Andy Green
c35661c45c CMake: add option to avoid GISPIPE IGN for Android 7+ 2017-03-21 11:34:49 +08:00
Andy Green
9287f7d1b3 ext: pmd: improve dealing with partial input usage with drain
https://github.com/warmcat/libwebsockets/issues/841
2017-03-20 19:07:19 +08:00
f0c800ada6 rpm: added missing file to %files section of spec file 2017-03-18 11:20:12 +08:00
Andy Green
2e874dea50 windows: cannot use fstat 2017-03-17 11:43:45 +08:00
Andy Green
ed27be42c9 docs: lws_callback_all_protocol: fix cut-n-paste error and explain it is probably not what you want 2017-03-15 07:28:51 +08:00
Andy Green
e769af41db windows: don't use LWS_EXTERN outside of function declarations 2017-03-15 07:25:36 +08:00
Andy Green
102d40e6b6 esp32: openssl 2017-03-11 11:51:06 +08:00
Andy Green
e0572d3bef esp32: move helper code into lws 2017-03-10 14:31:43 +08:00
Silas Parker
4198c20920 ssl: close sometimes continuously asserting POLLIN until timeout
https://github.com/warmcat/libwebsockets/issues/831
2017-03-10 07:46:05 +08:00
Silas Parker
c1b5c8cabd fops_zip: require libz 2017-03-09 19:21:40 +08:00
Silas Parker
3c02868408 gcc-format-strings: 32-bit build 2017-03-09 18:52:10 +08:00
honjane
34842d7492 http2: fix log compile errors 2017-03-09 13:29:50 +08:00
Andy Green
6be573f2c9 v2.2.0 2017-03-08 14:15:01 +08:00
Andy Green
2f3b4c8f96 coverity 177526: pointer difference already scaled 2017-03-08 14:10:31 +08:00
Andy Green
3a09c3b7d6 esp32: align fops member name defines 2017-03-08 11:11:41 +08:00
Andy Green
c53a76f0ef reduce log spew on POST processing 2017-03-08 10:52:49 +08:00
Andy Green
4219a3281d server-name: add_server_header add LWSAHH_FLAG_NO_SERVER_NAME
Also clean up usage of status code defines in lws
2017-03-08 07:51:47 +08:00
Andy Green
b9dd61bb6f server-name: default to no server name unless set in context 2017-03-08 07:35:27 +08:00
Andy Green
205ccedf6e raw: enable server and client raw sockets 2017-03-07 16:06:05 +08:00
Andy Green
4578036b53 ext: use arguments and reply with them 2017-03-07 10:08:03 +08:00
Andy Green
b6e0c89750 coverity 177409: dont bother assigning ignored second SSL_shutdown return value
No security impact
2017-03-06 14:46:06 +08:00
Andy Green
688b819112 coverity 177410: check seek return value inside fops-zip
Truncated or corrupted zip on server side would read garbage instead of fail.
2017-03-06 14:46:05 +08:00
Andy Green
21d83b44ea test-client: only take data from LWS_CALLBACK_RECEIVE_CLIENT_HTTP 2017-03-06 14:46:03 +08:00
Andy Green
19cc7acb24 fops-zip 2017-03-05 15:32:55 +08:00
Andy Green
2f4dfa4ea7 rx flow: handle child state change during parent cb 2017-03-05 15:32:47 +08:00
Andy Green
753f1d642c junzip: style and migrate header into private-libwebsockets.h
Also introduce CMake LWS_WITH_ZIP_FOPS defaulting to ON that builds junzip.c and
make sure this is exported in lws_config.h (included by libwebsockets.h)

Improve lws handling of stdint.h import or simulate it.
2017-03-03 09:19:14 +08:00
Andy Green
cb35969fce junzip: import from DomTerm 912add15f3d0aec
From https://github.com/PerBothner/DomTerm.git
2017-03-03 08:44:04 +08:00
Andy Green
04054b4072 ev: fix build 2017-03-03 08:18:16 +08:00
Per Bothner
60f4569bb8 adopt: allow binding to parent at same time
- if protocol set, allocate own user_space

   If the child wsi wants the parent wsi user_space, it can use

       lws_wsi_user(lws_get_parent(child_wsi))

 - raw file close processing handles parent-child relationship
2017-03-03 07:36:08 +08:00
Michael Behrns-Miller [case-ubuntu]
2495afa604 Subject: Buffer index protection in the case where client does not
receive content length from HTTP server
2017-03-03 05:41:39 +08:00
Whisperbyte
faf260ccfa docs: mac: add info from github 2017-03-01 17:57:38 +08:00
Andy Green
1ada132932 fops: allow setting from context creation and introduce lws_select_fops_by_vfs_path
1) There's now a .fops pointer that can be set in the context creation info.  If set, the array of
fops it points to (terminated by an entry with .open = NULL) is walked to find out the best vfs filesystem
path match (comparing the vfs path to fops.path_prefix) for which fops to use.

If none given (.fops is NULL in info) then behaviour is as before, fops are the platform-provided one only.

2) The built in fileserving now walks any array of fops looking for the best fops match automatically.

3) lws_plat_file_... apis are renamed to lws_vfs_file_...
2017-03-01 14:59:48 +08:00
Andy Green
be8d791b5e adoption: make union for socket and file fds
This lets lws support adopting raw file FDs and raw socket fds.

A test plugin creates a FIFO and prints data sent on it, using
the lws event loop.
2017-03-01 10:01:53 +08:00
Andy Green
8bb3dffc86 fops: add path_prefix member 2017-03-01 10:01:53 +08:00
Lukas Geyer
16ee4b6f0d client: allow change externally-defined wsi user_data
https://github.com/warmcat/libwebsockets/issues/819

AG: add explanation in doxygen comment and check user_space was externally set
2017-02-28 21:17:25 +08:00
Andy Green
6d0c96e06a docs: remove in-tree version
It's a nice idea having them in-tree, but it leaves too much jumk
in the patches.

The up-to-date api for master will continue to be here

https://libwebsockets.org/lws-api-doc-master/html/index.html
2017-02-28 18:44:11 +08:00
Silas Parker
ab89246467 cosmetic fix trailing newline on log 2017-02-28 18:29:57 +08:00
Andy Green
4ca114fbcf cmake: create build/include 2017-02-28 07:51:56 +08:00
Andy Green
73dda1f765 client: take care of rx close during pending partial same as server does
https://github.com/warmcat/libwebsockets/issues/816
2017-02-28 04:03:26 +08:00
Andy Green
1789d0a483 fops: refactor around lws_fops_fd_t 2017-02-26 08:15:42 +08:00
Namowen
96b91cc7ec ssl: avoid EVP errors 2017-02-26 08:15:42 +08:00
Silas Parker
2e1dcc542e Prevent SSL downgrade during redirect 2017-02-23 08:23:21 +08:00
Silas Parker
3db9eca06a Add client support for relative reference redirects 2017-02-23 08:21:25 +08:00
Andy Green
d26d7b0e04 docs: catch up with decruft 2017-02-22 10:26:42 +08:00
Andy Green
a5488f9c27 mbedtls: remove abortive attempt to avoid confusing people
Basically we support openssl api compatibles only.

If we ever try something different we need a shim making it openssl api or a proper abstraction layer added first.
2017-02-22 09:54:47 +08:00
Andy Green
0aed7a06d5 mbed3: sayonara
The port was technically worthwhile and laid the groundwork for ESP support.

But now it is just useless cruft like mbed3 itself...
2017-02-22 09:50:11 +08:00
Joel Winarske
91593d8886 client: allow setting client ssl certs from lwsws and connection info separate from server ssl certs 2017-02-22 07:28:13 +08:00
Joel Winarske
390ba34400 ssl config for http client 2017-02-22 07:17:32 +08:00
Martin Milata
88d6c1a63b gcc format strings: couple more
Found on MIPS
2017-02-22 07:02:02 +08:00
Andy Green
3e0006c206 client: move redirects and c_port out of union
https://github.com/warmcat/libwebsockets/issues/810
2017-02-22 06:55:12 +08:00
Andy Green
3d6a1e11c2 client connect: oom4 clean up timeout list
https://github.com/warmcat/libwebsockets/issues/810
2017-02-21 23:38:40 +08:00
Andy Green
0db9b9f346 client redirect: choose correct error path after resetting client connection
https://github.com/warmcat/libwebsockets/issues/810
2017-02-21 22:59:00 +08:00
Silas Parker
dc4934d2bc ssl close improvement 2017-02-21 19:27:47 +08:00
Andy Green
297c0313fa raw: adoption and processing 2017-02-21 07:47:35 +08:00
Silas Parker
a5f2d8b688 url-parser: handle ipv6 [] addresses 2017-02-21 07:27:07 +08:00
Namowen
7832b236a4 vhost: also free per-vhost protocols list even when no PLUGINS 2017-02-20 06:20:56 +08:00
Andy Green
052a685435 ssl close: do explicit ssl shutdown instead of socket shutdown if ssl mode 2017-02-20 05:44:56 +08:00
Namowen
b8034bb1e5 cosmetic CR missing on some errs 2017-02-19 05:35:55 +08:00
Andy Green
c7c4ae0aa4 ESP32 platform
This is enough for all the test app features to work on ESP32 without
SSL.
2017-02-18 17:27:22 +08:00
Yuchen Xie
936bf08a0f Fix typo 2017-02-18 17:27:22 +08:00
Namowen
40d37e2105 client: fix X509_V_ERR_CERT_HAS_EXPIRED 2017-02-18 17:27:22 +08:00
Joel Winarske
4241af99cb client: direct _APPEND_HANDSHAKE_HEADER at wsi protocol 2017-02-18 17:27:22 +08:00
Yongwen Zhuang
4fd91fa60d Correct Cross compiling commandline 2017-02-18 17:27:22 +08:00
Andy Green
186ba832b3 client: allow http[s] to select targeted protocol name in vhost 2017-02-18 17:27:22 +08:00
Joel Winarske
238766be6b windows: changes to build with VS2015 2017-02-18 17:27:22 +08:00
Silas Parker
f2c6e48eb8 fix close packet index coding
https://github.com/warmcat/libwebsockets/issues/792
2017-02-18 17:27:22 +08:00
Silas Parker
f9f5a57607 gcc-format-strings: ipv6 2017-02-18 17:27:22 +08:00
Per Bothner
baa0f74bf2 gzip fixes 2017-02-18 17:27:22 +08:00
Andy Green
1d393aba9e client: close without spinning
https://github.com/warmcat/libwebsockets/issues/789
2017-02-18 17:27:22 +08:00
Andy Green
2e11efa84b file_ops: add compression flags and convert open flags to pointer
ihttps://libwebsockets.org/pipermail/libwebsockets/2017-February/003127.html
2017-02-12 18:15:15 +08:00
Andy Green
21faff3deb file_ops: use wrappers for names 2017-02-12 18:11:11 +08:00
Andy Green
0b3e9e62cb client redirect: make sure there is a leading / on path 2017-02-10 11:00:38 +08:00
Andy Green
807313b157 appveyor: make zip artifact 2017-02-10 07:37:35 +08:00
Andy Green
31c5130802 client: fix redirects and allow ssl / non-ssl redirects 2017-02-09 15:25:01 +08:00
Andy Green
5e25dc07c8 chunked http client: support in test-client and document
This improves the test client to

 - dump http content if INFO log level enabled
 - handle chunked content correctly
 - document lws_http_client_read()
2017-02-09 09:17:23 +08:00
Andy Green
c8b20910ec lwsl_visible 2017-02-09 09:11:17 +08:00
Johnny
388f6c97fb test client: some compilers dont accept void * as const char *
This fixes a conversion error from a void pointer to a string.
2017-02-08 07:02:00 +08:00
Andy Green
73f5e58682 gcc-format-strings: LWS_PLAT_OPTEE 2017-02-07 00:51:25 +08:00
Andy Green
9395eb62ce LWS_PLAT_OPTEE: Convert to use TEE_Malloc
OPTEE TAs should allocate via TEE_Malloc
2017-02-07 00:51:25 +08:00
Andy Green
066f4156d6 coverity 175437: forgot to add name to array for new USER log level 2017-02-07 00:51:25 +08:00
Andy Green
cde0371a83 coverity 175438: server status plugin: off-by-one if you generate 32KB of content 2017-02-07 00:51:25 +08:00
Andy Green
af9ddec2f7 coverity 175435: seems bogus 2017-02-07 00:51:25 +08:00
Andy Green
a17992b638 coverity 175436: dead cruft 2017-02-07 00:51:25 +08:00
Andy Green
fdb25fcd6e gcc format strings: http2 2017-02-07 00:51:25 +08:00
andSpace
7df395695b LWS_FALLBACK_GETHOSTBYNAME
Adapted by AG to not use-after-free and have LWS_FALLBACK_GETHOSTBYNAME
2017-02-05 22:30:27 +08:00
Andy Green
ce37ee9624 gcc- format strings: debug and extra plugins 2017-02-05 22:30:27 +08:00
Martin Milata
be1f0a3a92 Subject: gcc format strings: Make GCC check format strings, fix found problems 2017-02-05 21:32:30 +08:00
Martin Milata
e96b69887f docs: Correct string 2017-02-05 21:31:16 +08:00
Sven Hoffmann
b5ebd599b2 post processing: fix problem where hex cant straddle block correctly 2017-02-05 21:25:39 +08:00
Andy Green
24e77a04ab remove dump getaddrinfo result 2017-02-03 20:55:56 +08:00
Andy Green
ad945976f7 client: decruft extensions
https://github.com/warmcat/libwebsockets/issues/770
2017-02-03 10:39:37 +08:00
Andy Green
c2208640dd logging: cleanup and introduce LLL_USER 2017-01-31 10:50:15 +08:00
Andy Green
71bb400805 http_proxy: deal with redundant protocol leader
https://github.com/warmcat/libwebsockets/issues/764
2017-01-26 07:27:11 +08:00
Denis Osvald
76985f256d wsi remove unused 'upgraded' boolean field
It was introduced in 7df53c5550
but was never used...

Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2017-01-23 20:04:06 +08:00
Andy Green
716aaeeb8b context new option LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN
https://github.com/warmcat/libwebsockets/issues/757
2017-01-23 19:52:27 +08:00
Denis Osvald
cc806bb77e ssl pass real wsi to verify cert cb
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2017-01-23 19:35:00 +08:00
Denis Osvald
bcce73201f ssl expose public wsi->ssl getter
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2017-01-23 19:34:46 +08:00
Andy Green
1bc025cfa7 windows: reduce C99isms to something MS compiler can understand 2017-01-18 07:20:09 +08:00
Andy Green
51c96d8c2a plat-optee and boringssl adaptations 2017-01-17 07:01:02 +08:00
Andy Green
e680fb9193 win32 fixes
https://github.com/warmcat/libwebsockets/issues/750
2017-01-17 06:51:11 +08:00
Tobias
3f55e5e964 ignore leading spaces when checking for a suitable subprotocol
My Browsers send as Subprotocols e.g. chat, superchat, mySubprotocol (with spaces after the ,). Libwebsockets now checked if ' mySubprotocol' was equal to 'mySubprotocol' which failed. With this fix the leading space is ignored and uses 'mySubprotocol' for comparision.
2017-01-17 06:22:58 +08:00
Namowen
b837f93dcf ssl: add LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION 2017-01-10 09:31:23 +08:00
Andy Green
9e562fcf1b ssl: wolfssl doesn't have clear options
https://github.com/warmcat/libwebsockets/issues/741
2017-01-10 09:14:44 +08:00
Hai Vu
8b7704f1b3 ssl-correct-option-clear-availability-version
https://github.com/warmcat/libwebsockets/issues/744
2017-01-10 09:10:49 +08:00
Andy Green
a7bf178606 ssl: correct version detection 2017-01-07 11:29:32 +08:00
Andy Green
1e22719f5e polarssl: turn off missing tlsext 2017-01-07 10:24:16 +08:00
Andy Green
c001a15973 openssl: deal with missing OPENSSL_NO_TLSEXT on ancient versions 2017-01-06 09:49:28 +08:00
Andy Green
d79d8b792d cmake: boringssl helper 2017-01-04 20:23:10 +08:00
Andy Green
b3c2427cfd client: MORE_SERVICE is not an error 2017-01-04 19:59:38 +08:00
Andy Green
a0c4a0e253 cgi: 5s grace to send buffered if chunked 2017-01-03 08:18:37 +08:00
Denis Osvald
9b129c137a server: check listen(2) return value
The `listen` call can fail with EADDRINUSE after bind() succeeds, for
example because another process called listen on that port in the
meantime, or under some circumstances with IPv6-mapped-IPv4. This was
causing EINVAL on accept, with an infinite loop in case of libuv.

A reproducible example was to run nc -l -p 5555 ( OpenBSD netcat (Debian
patchlevel 1)) before starting test-server

Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2017-01-03 01:56:20 +08:00
Andy Green
58a26ebf45 lejp-conf: add timeout-secs 2017-01-02 19:57:54 +08:00
Andy Green
714f66f5b7 windows: remove preprocessor business for [v]snprintf
https://github.com/warmcat/libwebsockets/issues/731
2016-12-28 15:23:34 +08:00
namowen
85f0c283dd lws_plat_service_tsi: accessing context before checking for NULL
https://github.com/warmcat/libwebsockets/issues/730
2016-12-24 07:57:34 +08:00
Andy Green
a14be2664c test server: add -P secs to allow pingpong test 2016-12-24 07:37:40 +08:00
Andy Green
dd0dfaecb7 ESP8266: LWS_POSIX fixes for basic auth and deprecated context 2016-12-22 11:32:34 +08:00
Alan Conway
f3ad9540fa ssl-server: Add CONTEXT_PORT_NO_LISTEN_SERVER
Special port setting to disable listening for a server using socket adoption.
This contrasts with CONTEXT_PORT_NO_LISTEN which does the same for a client.

In particular, server-side SSL is not disabled by CONTEXT_PORT_NO_LISTEN_SERVER
as it is by CONTEXT_PORT_NO_LISTEN.
2016-12-21 09:32:44 +08:00
Alan Conway
63627e7e86 ssl: improved error reporting for SSL_accept.
The return value from SSL_get_error() is an integer switch value, not an error
code that can be interpreted by ERR_error_string()

Report the error code name, plus errno information if available for
SSL_ERROR_SYSCALL as per man page for SSL_get_error().
2016-12-21 09:32:25 +08:00
Alan Conway
acdf0c7066 server: expose lws_adopt_socket_vhost() as public API
Allows a socket to be adopted and associated with an existing vhost.
Also added corresponding  lws_adopt_socket_vhost_readbuf()
2016-12-21 09:32:16 +08:00
Andy Green
be9fb919d1 context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.

2) You can send this parent process a SIGHUP now to cause it to

 - close listening sockets in existing lwsws processes

 - mark those processes as to exit when the number of active connections
   on the falls to zero

 - spawn a fresh child process from scratch, using latest configuration
   file content, latest plugins, etc.  It can now reopen listening sockets
   if it chooses to, or open different listen ports or whatever.

Notes:

1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.

That's the purpose of the second part of the context destruction,
lws_context_destroy2().

For compatibility, if you are not using libuv, the first part calls the
second part.  However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-20 14:37:07 +08:00
Andy Green
0a3859f623 uv: dont try to touch watcher until after loop initialized 2016-12-20 14:37:07 +08:00
Namowen
8694d1bdbc echo: fix debug build
https://github.com/warmcat/libwebsockets/issues/716#issuecomment-267377856
2016-12-20 14:37:07 +08:00
Andy Green
5bc0343a85 client ssl hostname check: trim any port on host header 2016-12-20 14:37:07 +08:00
Andy Green
5767095208 test-client: fix broken protocol names 2016-12-20 14:37:07 +08:00
Andy Green
e0bed8da33 ipv6-allow-binding-to-ipv6-address-in-iface
ipv4 and ipv6 binding to a named interface works OK.  ipv4 binding to an IP also
works, but we need some extra ipv6 magic to identify the ipv6 interface from an
ipv6 address.

This patch based on code from "user3546716" at
http://stackoverflow.com/questions/13504934/binding-sockets-to-ipv6-addresses

adds the necessary magic.

https://github.com/warmcat/libwebsockets/issues/717
2016-12-20 14:37:07 +08:00
Andy Green
ad99232f8c client: if NULL protocol vhost same linked list entry
Lws maintains a linked-list of wsi that are on the same vhost protocol...
it walks it to perform ..._all_protocol() type apis.

Client connections also participate in this list, but in the case the
selected protocol is not given during negotation (a legal case where
the server default protocol is selected) we missed adding the new
ws negotiated client wsi to the list.

This patch makes sure we add the wsi to the vhost protocols[0] list
in that case.

https://github.com/warmcat/libwebsockets/issues/716
2016-12-20 14:37:07 +08:00
Andy Green
023ac896e9 client: avoid possible NULL deref on error path
https://github.com/warmcat/libwebsockets/issues/672
2016-12-20 14:37:07 +08:00
Andy Green
ed4acef481 RFC7233 HTTP Ranges support for server
This adds a serverside implementation of RFC7233 HTTP ranges.

 - LWS_WITH_RANGES is on by default at cmake

 - Accept-Ranges: bytes is added if LWS_WITH_RANGES is enabled

 - Both single ranges and multipart (2+) ranges are supported

Test with curl like this

Single

$ $ curl -s -r 64-95  http://localhost:7681/libwebsockets.org-logo.png  | hexdump -C
00000000  2e 01 fd 9d 12 27 00 00  00 19 74 45 58 74 53 6f  |.....'....tEXtSo|
00000010  66 74 77 61 72 65 00 77  77 77 2e 69 6e 6b 73 63  |ftware.www.inksc|

Multipart

$ curl -s -r 64-95,128-143  http://localhost:7681/libwebsockets.org-logo.png  | hexdump -C

00000000  5f 6c 77 73 0d 0a 43 6f  6e 74 65 6e 74 2d 54 79  |_lws..Content-Ty|
00000010  70 65 3a 20 69 6d 61 67  65 2f 70 6e 67 0d 0a 43  |pe: image/png..C|
00000020  6f 6e 74 65 6e 74 2d 52  61 6e 67 65 3a 20 62 79  |ontent-Range: by|
00000030  74 65 73 20 36 34 2d 39  35 2f 37 30 32 39 0d 0a  |tes 64-95/7029..|
00000040  0d 0a 2e 01 fd 9d 12 27  00 00 00 19 74 45 58 74  |.......'....tEXt|
00000050  53 6f 66 74 77 61 72 65  00 77 77 77 2e 69 6e 6b  |Software.www.ink|
00000060  73 63 5f 6c 77 73 0d 0a  43 6f 6e 74 65 6e 74 2d  |sc_lws..Content-|
00000070  54 79 70 65 3a 20 69 6d  61 67 65 2f 70 6e 67 0d  |Type: image/png.|
00000080  0a 43 6f 6e 74 65 6e 74  2d 52 61 6e 67 65 3a 20  |.Content-Range: |
00000090  62 79 74 65 73 20 31 32  38 2d 31 34 33 2f 37 30  |bytes 128-143/70|
000000a0  32 39 0d 0a 0d 0a 05 14  50 40 05 15 a5 c4 60 43  |29......P@....`C|
000000b0  91 c4 4a d4 c4 fc 5f 6c  77 73 0d 00              |..J..._lws..|

The corresponding header part is like this

	0x0030:            4854 5450 2f31 2e31 2032 3036      HTTP/1.1.206
	0x0040:  200d 0a73 6572 7665 723a 206c 7773 7773  ...server:.lwsws
	0x0050:  0d0a 636f 6e74 656e 742d 7479 7065 3a20  ..content-type:.
	0x0060:  6d75 6c74 6970 6172 742f 6279 7465 7261  multipart/bytera
	0x0070:  6e67 6573 0d0a 6163 6365 7074 2d72 616e  nges..accept-ran
	0x0080:  6765 733a 2062 7974 6573 0d0a 636f 6e74  ges:.bytes..cont
	0x0090:  656e 742d 6c65 6e67 7468 3a20 3138 380d  ent-length:.188.
	0x00a0:  0a63 6163 6865 2d63 6f6e 7472 6f6c 3a20  .cache-control:.
	0x00b0:  7072 6976 6174 6520 6d61 782d 6167 653a  private.max-age:
	0x00c0:  2036 300d 0a63 6f6e 6e65 6374 696f 6e3a  .60..connection:
	0x00d0:  206b 6565 702d 616c 6976 650d 0a65 7461  .keep-alive..eta
	0x00e0:  673a 2030 3030 3031 4237 3535 3444 3433  g:.00001B7554D43
	0x00f0:  3033 330d 0a0d 0a                        033....
2016-12-20 14:37:07 +08:00
Andy Green
fdddf5af51 clean: usused accidental global wsi
https://github.com/warmcat/libwebsockets/issues/708
2016-12-20 14:37:07 +08:00
Andy Green
b1d4d3bb9e lwsgt: fix check against forgot password flow defeating existing pw check
https://github.com/warmcat/libwebsockets/issues/706

This fixes a problem where the check for the existing pw was
skipped when a logged-in user is changing his password.

It's not good but because the user has to be logged in, it only affected
the situation someone changes his password on his logged in session.
2016-12-20 14:37:07 +08:00
Andy Green
ba8fb14e85 token:x-forwarded-for
https://github.com/warmcat/libwebsockets/issues/702
2016-12-20 14:37:07 +08:00
Andy Green
69c88d9f0c context: external_baggage_free_on_destroy
This adds a context creation-time member that points to something
that should be freed when the context is destroyed.

It's in preparation for context deprecation, when a context might
be destroyed asynchronously... a related external with the
lifetime of thee context should also be freed at that time.

Adapt lwsws to use it with the context "strings" (also used for
aligned structs created by the config) allocation.
2016-12-20 14:37:07 +08:00
Andy Green
ee94621b40 basic-auth 2016-12-16 22:08:13 +08:00
Andy Green
7a77c0b572 just finalize startup once 2016-12-16 22:08:13 +08:00
Bablooos
6e436dca39 vhost: allow adding vhosts after server init
This should allow adding vhosts "late", ie, after the server is up and
running with its initial vhost(s).  The necessary housekeeping is folded
into lws_create_vhost() itself so it should be transparent.

Notice though that at the point the server starts to do service after it
starts initially, if it was requested that the UID / GID change, that
is performed at that point and is not reversible.

So vhosts added "late" find themselves running under the unprivileged
UID / GID from the very start, whereas vhosts added "early" initially
run under the UID / GID the process started with.  If protocols the
vhost uses want to, eg, open privileged files at init and then use
them unprivileged, that will fail if the vhost is added late because
the initial privs are already gone.

AG: also deal with lws_protocol_init() on late vhost init (does the
callbacks for per vh protocol creation), add comments
2016-12-16 22:08:13 +08:00
Bablooos
fded366ea0 Update CMakeLists.txt for BSD + libdl
Fixing build failure of libwebsockets-test-fraggle  on FreeBSD when LWS_WITH_PLUGINS.
Solution: FreeBSD has no libdl
2016-12-16 22:08:13 +08:00
Andy Green
f2a130f9bf generic-sessions: move auth level check to after mount protocol selection 2016-12-16 22:08:13 +08:00
Andy Green
39ec84202d ws-server: restrict returned Sec-Websocket-Protocol to the chosen name only
https://libwebsockets.org/pipermail/libwebsockets/2016-November/002948.html

Updated to fix a problem with no protocol

https://github.com/warmcat/libwebsockets/issues/705
2016-12-16 22:08:13 +08:00
Iblis Lin
62a86ac9c9 server: portable option for setsockopt
From linux ipv6(7) manual (section `Note`):

        SOL_IP, SOL_IPV6, SOL_ICMPV6 and other SOL_* socket options are
        nonportable variants of IPPROTO_*.  See also ip(7).

Ref: http://man7.org/linux/man-pages/man7/ipv6.7.html
2016-12-16 22:08:13 +08:00
sjames1958gm
0fdca9f782 client stash: update path variable to larger size 2016-12-16 22:08:13 +08:00
Andy Green
53bed78950 lws_socket_bind: use lws_sockfd_type 2016-12-16 22:08:13 +08:00
Andy Green
2926553c41 client: protect againt losing ah by lws_client_connect_2 2016-12-16 22:08:13 +08:00
Andy Green
c541e2d7e3 post file upload: dont lose sight of end of upload just because we hit end of incoming post data 2016-12-16 22:08:13 +08:00
Andy Green
5466b9d253 docs update 2016-12-16 22:08:13 +08:00
Joachim Bauch
b3160f9fd0 Added flag to allow expired certificates. 2016-12-16 22:08:13 +08:00
Rainer Poisel
d2cef1515e Better support for MINW32 2016-12-16 22:08:13 +08:00
Yannick Kiekens
e7cc1ffc1e From 7f84bc3e864b52eb13c670362a4b53bc3505393e Mon Sep 17 00:00:00 2001
Subject: [PATCH] Fix typo in lws_create_context documentation
2016-12-16 22:08:13 +08:00
Andy Green
86ab060cd9 client: add lws_http_client_http_response api 2016-12-16 22:08:13 +08:00
Andy Green
fbe66065ff client: treat 304 like 200 2016-12-16 22:08:12 +08:00
Andy Green
b46c401245 alias lws_plat_service_tsi to lws_service_tsi in public api
Via Dosvald

lws_service_tsi() which has been around a while actually just
calls through to lws_plat_service_tsi(), meaning there is no
need to expose both apis.

Rename the internal lws_plat_service_tsi() to _lws_plat_service_tsi()
and replace the api export with a #define to lws_service_tsi for
compatibility's sake.
2016-12-16 22:08:12 +08:00
Andy Green
3b93e344f6 Add reject service keywords list 2016-10-13 06:32:57 +08:00
Andy Green
202e8a7428 adjust_timeout: with default lws_plat_service_tsi allow beings passed 0 timeout
Some people are calling service with zero timeout, taking care of
not busywaiting by some other external arrangements.

Adapt the forced service signalling to survive this.
2016-10-10 20:34:34 +08:00
Joerg Pommnitz
0733610c0e some compilers need void param explicitly 2016-10-10 20:10:39 +08:00
Yuchen Xie
63477ded0c Correct the library name of LIBHUBBUB_LIBRARIES
It should be `hubbub` in `find_library` to make the function work.
2016-10-10 20:10:34 +08:00
Andy Green
abe0c5e57e docs: explain lws_write handling of truncated sends better 2016-10-08 18:08:03 +08:00
Denis Osvald
4be9a5234d publicly document lws_service_fd timeout servicing
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-10-07 17:34:25 +08:00
Denis Osvald
14f994f52a test-server-extpoll: add 1-per-second timeout servicing
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-10-07 17:33:31 +08:00
Denis Osvald
3f8082fecf test-server-extpoll: check for forced service before looping again
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-10-07 17:04:33 +08:00
Andy Green
73557509bd v2.1.0
Bump soname to 9
2016-10-07 03:19:50 +08:00
Andy Green
443b31d012 extpoll: expose forced service apis 2016-10-07 03:19:45 +08:00
Andy Green
8b0dd360aa update attack.sh
Lws cares about trailing \n on a lot of these tests now.  Make it check it still cares on one and remove
the trailing \n on the others.

There's 2 changes in the results about /..//?, it seems to apply / to uri arg 1.  But it doesn't seem
to make a problem so just adapt the results for now.
2016-10-06 20:41:29 +08:00
Andy Green
4cfe81dfba ubuntu: fix uv detection for lwsws 2016-10-05 14:00:55 +08:00
Andy Green
4d79dc553e travis: explicitly point to openssl on osx
One day this started failing at CMake autofind. This forces it to look at the right place.
2016-10-05 10:15:24 +08:00
Andy Green
53ec6b1789 fix cosmetic if end if name tag error 2016-10-05 10:15:16 +08:00
Andy Green
6e8f360e58 closing drops any pending ah rx immediately 2016-10-04 18:05:10 +08:00
Andy Green
65135f2bc4 port forced service checking from libuv
Related to second part of

https://github.com/warmcat/libwebsockets/issues/638
2016-10-04 08:39:14 +08:00
Andy Green
8187c76943 post form parsing fix retry as new boundary start needed after mismatching boundary
https://github.com/warmcat/libwebsockets/issues/641
2016-10-04 08:26:17 +08:00
Peter Pentchev
939bb7f6e9 Remove the cleanup functions with OpenSSL 1.1.
The thread support in OpenSSL has been rewritten almost completely
and the cleanup functions are now executed automatically.
2016-10-04 08:26:13 +08:00
Peter Pentchev
fb71b790cd Subject: Fix some typographical and grammatical errors. 2016-10-03 21:31:27 +08:00
Andy Green
f94bef42c4 lws_header_table_reset: make caller responsibility to clear down ah rx buffer
There are two kinds of reaason to call lws_header_table_reset(), one is we are reallocating
a destroyed ah to another wsi, and the other is we are moving to the next pipelined header set
still on the same wsi, and we need a "weaker" reset that only clears down the state related
to the header parsing, not everything about the ah context including the ah rx buffer.

This patch moves the ah rxbuffer rxpos and rxlen resetting out of lws_header_table_reset() and to
be the responsibility of the caller.  Callers who are moving the ah to another wsi are
patched to deal with resetting rxpos and rxlen and lws_http_transaction_completed() who only
resets the ah when moving to the next pipelined headers, no longer wrongly clears the ah rxbuf.

https://github.com/warmcat/libwebsockets/issues/638
2016-09-29 10:31:06 +08:00
Brown, Matthew
b0ff623526 Added option to build the static library with PIC 2016-09-27 05:32:40 +08:00
Benjamin Ness
668a6fbf29 fix build problem on systems without X509_VERIFY_PARAM type 2016-09-27 05:28:11 +08:00
Patrick Gansterer
e468e15a2b Add SVG to lws_get_mimetype() 2016-09-23 06:31:24 +08:00
Andy Green
0c984014f0 lwsws license to cc0
https://github.com/warmcat/libwebsockets/issues/629
2016-09-19 19:16:47 +08:00
Andy Green
a496700b3a lws_snprintf
Thanks to Fabrice Gilot for reporting the problem that led to uncovering this.

Due to a misunderstanding of the return value of snprintf (it is not truncated according
to the max size passed in) in several places relying on snprintf to truncate the length
overflows are possible.

This patch wraps snprintf with a new lws_snprintf() which does truncate its length to allow
the buffer limiting scheme to work properly.

All users should update with these fixes.
2016-09-15 02:22:57 +08:00
Andy Green
c15714f35a handle rx flow control active when consuming payload
https://github.com/warmcat/libwebsockets/issues/622
2016-09-10 04:54:20 +08:00
Andy Green
b8199ba4ab generic-table: format-security
https://github.com/warmcat/libwebsockets/issues/624
2016-09-10 04:54:20 +08:00
Andy Green
2083674aa0 lws_status protect against pss list changing 2016-09-10 04:54:20 +08:00
Andy Green
9c7e84d346 adopt_socket_vhost: error path doesn't remove us from timeout list
As found by "github user 7"

https://github.com/warmcat/libwebsockets/issues/621
2016-09-10 04:54:20 +08:00
Fredrik Skogman
6ecbe3e93f Do not use ps fax on Solaris. 2016-09-10 04:53:31 +08:00
Fredrik Skogman
9c04a107c9 Updated test programs to build on Solaris. Some whitespaces cleanup. 2016-09-10 04:53:28 +08:00
Fredrik Skogman
316960b87d Added build support for Solaris. 2016-09-09 06:52:42 +08:00
Fredrik Skogman
9de43fc9d7 Check for sys/sockio.h. 2016-09-09 06:48:24 +08:00
Andy Green
723b3f16fa client http: extra read notification after close
https://github.com/warmcat/libwebsockets/issues/620
2016-09-06 15:36:51 +08:00
Andy Green
64dd359192 windows: WCHAR in lws_plat_inet_ntop needs double the final allocation
https://github.com/warmcat/libwebsockets/issues/619
2016-09-05 15:03:37 +08:00
Andy Green
72502e86f5 coverity 169276-9 - false positive assuming 8b char: char limits index size 2016-08-28 09:49:30 +08:00
Andy Green
d11bee7fc5 coverity 169274 - lwsgt dirlisting ignore files that cant be statted 2016-08-28 09:44:15 +08:00
Andy Green
01020b60f6 coverity 169273 - off-by-one on cgi chunking swallow limit 2016-08-28 09:44:15 +08:00
Andy Green
d6761e87b5 coverity 169272 - off-by-one possible on CGI buffer limit 2016-08-28 09:44:15 +08:00
Andy Green
d8e051dd97 coverity 169271 - take care about sockfd of -1 on close 2016-08-28 09:44:15 +08:00
Andy Green
2f863cf59a coverity 169269 - dont issue NULL cce 2016-08-28 09:44:15 +08:00
Andy Green
cd02a15669 coverity 169268 + 169270- dead code plus repeat NULL check on error path 2016-08-28 09:44:15 +08:00
Andy Green
e0212b8c85 coverity 169275 - lwsgt check string bounds 2016-08-28 09:44:15 +08:00
Andy Green
16f3e4cacf coverity 169267 - lwsgt strncpy may fill buffer without NUL 2016-08-28 09:10:40 +08:00
Andy Green
e35d91a860 per-vhost headers and lwsws conf support
This l;ets you add per-vhost arbitrary headers on served files, eg

     "headers": [{
        "X-Content-Type-Options": "nosniff",
        "X-XSS-Protection": "1; mode=block",
        "x-frame-options": "SAMEORIGIN"
        }],
2016-08-27 17:07:06 +08:00
Patrick Gansterer
46646f9ebf Update badges in README.md
Fix the link to Appveyor and use SVG images.
2016-08-27 05:57:18 +08:00
Patrick Gansterer
396b58ce70 Remove unneeded #include <stdint.h>
This fixes the build for Visual Studio 2008.
2016-08-26 18:31:09 +08:00
Andy Green
a5ec7212ff test html: js date uses ms 2016-08-23 18:29:17 +08:00
Andy Green
6fe25fd1fd cgi-retain-timeout-after-POST-send
Sending the POST data isn't enough to let us off the hook for timeout checks, if we
are doing a CGI on it.
2016-08-23 14:20:11 +08:00
Andy Green
b49b0fbafa quench logging 2016-08-22 07:07:10 +08:00
Andy Green
1172a89bb3 cgi: deal with all methods correctly 2016-08-20 06:46:17 +08:00
Andy Green
90f513b209 uv: dont handle SIGSEGV, SIGFPE
https://github.com/warmcat/libwebsockets/issues/609
2016-08-20 05:47:29 +08:00
Patrick Gansterer
fa9ebb394f Remove context from lws_context_per_thread for non-libuv builds
The variable is never read when built without libuv.
2016-08-14 20:55:24 +08:00
Patrick Gansterer
5162d876fd Add error handling for SSL_new() of clients
Do not access wsi->ssl if SSL_new() failed and log the error.
2016-08-14 19:53:20 +08:00
Patrick Gansterer
d0abf9bb5e Fix signature of lws_create_vhost() in README.coding.md 2016-08-14 19:48:59 +08:00
Patrick Gansterer
387e50bd8f Allow serving files without known mimetype
RFC2616 only says that any HTTP/1.1 message containing an entity-body
SHOULD include a Content-Type header field defining the media type of
that body.
RFC2119 defines SHOULD as: This word mean that there may exist valid
reasons in particular circumstances to ignore a particular item, but
the full implications must be understood and carefully weighed before
choosing a different course.

AG: this isn't an oversight, it's paranoia about sending out /etc/passwd
or /etc/shadow accidentally.

I agree it should be allowed if people really want to override it.  But
the default should remain like it is I think.

I adapted the patch to allow the extra mimetype "*": "" to be declared on
a mount, as a wildcard match that serves the file without a Content-Type.
2016-08-14 19:28:29 +08:00
Andy Green
3ffd0eb84a server max protocol element 64
https://github.com/warmcat/libwebsockets/issues/601
2016-08-11 05:36:08 +08:00
Andy Green
95fff47a91 client-add-PUSH-http-body-capability
Support HTTP body sending on client connections.

Add demo to test-client.

Run the normal test server, then

$ libwebsockets-test-client http://localhost:7681/formtest -o

It will post the string "hello" to the POST test using application/x-www-form-urlencoded

https://github.com/warmcat/libwebsockets/issues/598

Also ensures any of the Client HTTP transient modes will call back LWS_CALLBACK_CLOSED_CLIENT_HTTP
if they close

https://github.com/warmcat/libwebsockets/issues/600
2016-08-10 21:23:01 +08:00
Andy Green
bbf93693d8 client fixups after esp8266 2016-08-10 21:23:01 +08:00
Andy Green
7acf76cd3d esp8266 initial support 2016-08-10 21:20:23 +08:00
Andy Green
f7a1c7ed47 base64 decode fix lengths 2016-08-10 21:20:23 +08:00
Andy Green
4606ad4377 ah detach: don't print held time if actually detached
Confusingly an ah held time was printed for a previously detached ah.

Clear down the time field when detaching the ah so this can't happen any more.
2016-08-10 21:20:23 +08:00
Andy Green
f32d25051c ws ping pong on idle connections
This adds a new member to the context creation info struct "ws_ping_pong_interval".

If nonzero, it sets the number of seconds that established ws connections are
allowed to be idle before a PING is forced to be sent.  If zero (the default) then
tracking of idle connection is disabled for backwards compatibility.

Timeouts cover both the period between decision to send the ping and it being
sent (because it needs the socket to become writeable), and the period between
the ping being sent and the PONG coming back.

INFO debug logs are issues when the timeout stuff is operating.

You can test the server side by running the test server hacked to set ws_ping_pong_interval
and debug log mask of 15.  Both the mirror protocol and the server-status protocol are
idle if nothing is happening and will trigger the PING / PONG testing.  (You can also
test using lwsws and /etc/lwsws/conf with "ws-pingpong-secs": "20" in the global section)

For client, run the test client with -n -P 20 for 20s interval.  -n stops the test client
writing using the mirror protocol, so it will be idle and trigger the PING / PONGs.

The timeout interval may be up to +10s late, as lws checks for affected connections every
10s.
2016-08-10 21:20:23 +08:00
Andy Green
0fa6821214 Plugin server-status cleanup 2016-08-10 21:20:23 +08:00
Andy Green
2671b7469a plugins-dim-webpage-when-connection-lost 2016-07-14 08:57:28 +08:00
Andy Green
722cc4a366 plugin table dirlisting 2016-07-14 08:57:28 +08:00
Andy Green
02f880d9b6 doxygen use sections 2016-07-14 08:57:28 +08:00
Mike Messina
efef6bf9a0 Recheck slot when an FD gets closed and make formatting consistent 2016-07-14 08:57:28 +08:00
mmessina
fc0e52da9e Use one event for all sockets to avoid 64 limit and fix the single dispatch issue 2016-07-14 08:57:28 +08:00
Andy Green
5f73048d58 SNI-vhost-matching-fallback-to-wildcard 2016-07-14 08:57:28 +08:00
Andy Green
5ab523ec3f ah change default header len to 4096 and pool size 4
Users are starting to appear with clients sending more than the default max header
content buffer of 1024... with the advent of the ah pool the old reasons for keeping this
modest no longer apply, so up it to 4096 and reduce the default pool size to 4 from 16 to
keep the overall memory usage the same.
2016-07-14 08:57:28 +08:00
Andy Green
2b304a933f EXTPOLL export LWS_POLLXXX and document .events must be .revents 2016-07-14 08:57:28 +08:00
Andy Green
675c349cc5 client ssl add flag to control server cert hostname check 2016-07-14 08:57:28 +08:00
Andy Green
fee9f006f6 defer ESTABLISHED until after mode is WS_SERVING 2016-07-14 08:57:28 +08:00
Andy Green
0aa382f6fb lejp-conf: substitute _lws_ddir_ with install dir 2016-07-14 08:57:28 +08:00
Andy Green
cd6a70672b lejp_conf: pmo as alias for cgi-env
Since cgi-env only applies for cgi types, we can
re-use it for generic per-mount options (pmo)
2016-07-14 08:57:28 +08:00
Andy Green
ae0d8d74f9 win32-clean-and-use-do-while 2016-07-14 08:57:28 +08:00
Andy Green
906006e21e lws_get_mimetype 2016-07-14 08:57:27 +08:00
Andy Green
8860eddeb2 peer closes during partial buffered just hang up
https://github.com/warmcat/libwebsockets/issues/573
2016-07-14 08:57:27 +08:00
Andy Green
81c221ed35 CLIENT_CONNECTION_ERROR add strings
This clears up a couple of issues with client connect.

 - if CLIENT_CONNECTION_ERROR is coming, which of the many
   ways the rejection may have happened is documented in the
   in argument.  It's still possible if it just got hung up on
   in will be NULL, but now it has MANY more canned strings
   describing the issue available at the callback

	"getaddrinfo (ipv6) failed"
	"unknown address family"
	"getaddrinfo (ipv4) failed"
	"set socket opts failed"
	"insert wsi failed"
	"lws_ssl_client_connect1 failed"
	"lws_ssl_client_connect2 failed"
	"Peer hung up"
	"read failed"
	"HS: URI missing"
	"HS: Redirect code but no Location"
	"HS: URI did not parse"
	"HS: Redirect failed"
	"HS: Server did not return 200"
	"HS: OOM"
	"HS: disallowed by client filter"
	"HS: disallowed at ESTABLISHED"
	"HS: ACCEPT missing"
	"HS: ws upgrade response not 101"
	"HS: UPGRADE missing"
	"HS: Upgrade to something other than websocket"
	"HS: CONNECTION missing"
	"HS: UPGRADE malformed"
	"HS: PROTOCOL malformed"
	"HS: Cannot match protocol"
	"HS: EXT: list too big"
	"HS: EXT: failed setting defaults"
	"HS: EXT: failed parsing defaults"
	"HS: EXT: failed parsing options"
	"HS: EXT: Rejects server options"
	"HS: EXT: unknown ext"
	"HS: Accept hash wrong"
	"HS: Rejected by filter cb"
	"HS: OOM"
	"HS: SO_SNDBUF failed"
	"HS: Rejected at CLIENT_ESTABLISHED"

 - until now the user code did not get the new wsi that was created
    in the client connection action until it returned.  However the
    client connection action may provoke callbacks like
    CLIENT_CONNECTION_ERROR before then, if multiple client connections
    are initiated it makes it unknown to user code which one the callback
    applies to.  The wsi is provided in the callback but it has not yet
    returned from the client connect api to give that wsi to the user code.

    To solve that there is a new member added to client connect info struct,
    pwsi, which lets you pass a pointer to a struct wsi * in the user code
    that will get filled in with the new wsi.  That happens before any
    callbacks could be provoked, and it is updated to NULL if the connect
    action fails before returning from the client connect api.
2016-07-14 08:57:27 +08:00
Andy Green
e8439168dc remove polarssl mbedtls support
Just remove it from cmake
2016-07-14 08:57:27 +08:00
Andy Green
f25eefdd41 reduce log spew and document test-server variants 2016-07-14 08:57:27 +08:00
Robin Rowe
8fdff1053c fix __x86_64__ check
https://github.com/warmcat/libwebsockets/issues/574
2016-07-14 08:57:27 +08:00
Andy Green
9ec76d4b72 test-libev add server-status protocol 2016-07-14 08:57:27 +08:00
Andy Green
e7bf0aa1dc client confirm server hostname in cert
Openssl v1.0.2 and above have support for checking the hostname
the client side connected to against the hostname on the cert the
server presented.

This enables that feature if the necessary API is available in the
openssl version, meaning the connection will fail at ssl negotiation if the
cert isn't for the requested server

It's very easy to test, add a fake entry to /etc/hosts for the server IP with
a different name, using that will fail at ssl but using the correct dns name
matching the certificate will work.
2016-07-14 08:57:27 +08:00
Andy Piper
6ff571f854 windows detect client connection error 2016-07-14 08:57:27 +08:00
Andy Green
ca44730b36 dlfcn h only if plugins
Signed-off-by: None <andy@warmcat.com>
2016-07-14 08:57:27 +08:00
Andy Green
014481e912 documentation convert to doxygen
Signed-off-by: Andy Green <andy@warmcat.com>
2016-07-14 08:57:27 +08:00
Andy Green
4e75ae3b4e protocol-lws-messageboard
This is a simple messageboard built on top of lwsgs

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-28 14:51:44 +08:00
Andy Green
7f92ee802c lws_bind_protocol
When using http/1.1+ keepalive and mounts, the relationship between
a connection and a protocol becomes dynamic.  The same connection might
visit different bits of the url space served by different mounts using
different protocols.

This patch ensures protocols can cleanly manage their per-connection
allocations by using the following callbacks when the protocol changes

 LWS_CALLBACK_HTTP_BIND_PROTOCOL
 LWS_CALLBACK_HTTP_DROP_PROTOCOL

For example if the pss wants to malloc stuff at runtime, it should do it
in LWS_CALLBACK_HTTP_BIND_PROTOCOL or later, and clean it up in
...DROP_PROTOCOL.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-18 09:36:27 +08:00
Andy Green
7a2fc442b6 protocol generic sessions
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-18 06:44:31 +08:00
Andy Green
920daf10a1 lws_json_escape
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-18 06:43:30 +08:00
Andy Green
4bd5b96735 lejp conf report human readable errors
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-18 06:42:13 +08:00
Andy Green
36f87b068f lws_now_secs
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-17 09:41:22 +08:00
OndraCo
f1bdb0fefe Modified the 64 connections "backup" so that there is no wait if any event
is already ready.
2016-06-16 13:02:31 +08:00
OndraCo
528adbde7f Added a back-up in case of more than 64 connections. 2016-06-16 13:02:20 +08:00
Andy Green
3f17a8e252 lws_callback_vhost_protocols
This gives protocols a way to talk to each other via per-vhost callbacks,
one per protocol (including the sender).

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-15 10:46:58 +08:00
Andy Green
6cd27e8186 lws_process_html_args
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-14 12:35:23 +08:00
Andy Green
c55fd27d75 introduce urlencode decode and sql escape public apis
This adds

 - simple lws_urlencode()
 - simple lws_urldecode()
 - simple lws_sql_purify

Those expect the data to all be there and process it up until
the first '\0'.

There is also a larger opaque apis for handling POST_BODY urldecode.  To
enable these, you need to give cmake -DLWS_WITH_STATEFUL_URLDECODE=1 (or
arrange any larger feature that relies on it sets that in CMakeLists.txt)

 - stateful urldecode with parameter array

These have create / process / destroy semantics on a struct that maintains
decode state.

Stateful urldecode is capable of dealing with large POST data in multiple
POST_BODY callbacks cleanly, eg, file transfer by POST.

Stateful urldecode with parameter array wraps the above with a canned
callback that stores the urldecoded data and indexes them in a pointer
array matching an array of parameter names.

You may also pass it an optional callback when creating it, that will recieve
uploaded file content.

The test html is updated to support both urlencoded and multipart forms,
with some javascript to do clientside validation of an arbitrary 100KB
file size limit (there is no file size limit in the apis).

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-14 12:04:38 +08:00
Gadkari Mugdha
70c60d81ab fix for https connection code 2016-06-14 12:04:34 +08:00
Andy Green
1ec8ba893a openssl allow set clear of ssl options from info 2016-06-10 11:09:27 +08:00
Karl Palsson
d13c1471c1 non-openssl: only check for openssl ecdh in openssl builds
See also
https://github.com/warmcat/libwebsockets/issues/559

Signed-off-by: Karl Palsson <karlp@etactica.com>
2016-06-07 19:50:26 +08:00
Andy Green
a0d21c3abd client CONNECTION_ERROR also allow in LWSS_CLIENT_UNCONNECTED
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-07 17:20:57 +08:00
Andy Green
d5466c97ae lws_write escalate pending truncated to make the
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-07 16:46:41 +08:00
Andy Green
602d884028 smtp
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-07 09:49:59 +08:00
Alexander Bruines
ed7c63e07d Android: update to use SDK Python script
The latest Android SDK updates deprecate the shell script
 used to create a standalone toolchain. This patch updates the Makefile for
 the Android test-client to use the new Python script to create the standalone
 toolchains.
2016-06-07 02:42:32 +08:00
Andy Green
57513b7d62 ARRAY_SIZE dont redefine
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-06 20:35:42 +08:00
Andy Green
7201057d28 avoid illegal sockfd on timeout
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-04 09:02:09 +08:00
Andy Green
011f915dd0 ipv6only add lejp conf and flag docs
Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-04 09:01:28 +08:00
Denis Osvald
5780783c0c ipv6 don't ignore info.iface
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-06-04 08:37:50 +08:00
Denis Osvald
326c91b966 allow modifying ipv4 mapping option (IPV6_V6ONLY)
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-06-04 08:37:39 +08:00
Denis Osvald
33403a474e vhost creation print ipv6
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-06-04 08:37:31 +08:00
Denis Osvald
4507da486d fixup! ipv6 move disable to vhost option
client ipv6 check vhost not context
2016-06-04 08:37:19 +08:00
Denis Osvald
c16c6c8536 fixup! ipv6 move disable to vhost option
fix missing backslash in multiline macro
2016-06-04 08:37:08 +08:00
Andy Green
2dc7ddecfd ipv6 move disable to vhost option
Server ipv6 support disable is now controlled by vhost->options rather
than context->options, allowing it to be set per-vhost.



Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-03 21:19:40 +08:00
Andy Green
1dca916bec lws_get_urlarg_by_name
Adds a convenient way to directly get the value of a URL
argument like ...?x=y&v=1, regardless of position in the
parameter list.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-03 09:04:15 +08:00
Andy Green
4889566d5d add max_http_header_data2 and upgrade internal offsets from short to int
https://github.com/warmcat/libwebsockets/issues/550

Signed-off-by: Andy Green <andy@warmcat.com>
2016-06-02 13:03:35 +08:00
Martin C Drohmann
eda447e74a Revert changes in daemonize.c from commit 22d6f39e7f 2016-06-02 13:00:13 +08:00
Young
261f23622c update document for lws_get_context 2016-06-01 08:34:32 +08:00
Fabian Kurz
d3c33936b5 lwsl_timestamp month off by one 2016-06-01 08:32:18 +08:00
Alexander Bruines
d6fba75433 Adroid test-server client 2016-05-28 19:51:33 +08:00
Andy Green
3358c53445 lwsws conf.c migrate to lib
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-26 21:12:11 +08:00
Andy Green
488e05a6b4 plugins add win modifiers before libwebsockets.h for dll export flag
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-26 07:52:20 +08:00
Andy Green
9de9d0dacd windows adapt plugin name format vs functions
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-25 21:44:30 +08:00
Andy Green
5c27334b39 windows call plugins init on win32 plat
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-25 19:43:02 +08:00
Andy Green
264786db4d lws_get_peer_simple move to user api
https://github.com/warmcat/libwebsockets/issues/537

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-25 08:08:11 +08:00
Andy Green
3a42fb545a update discard pre 2016 date if post 2016 date seen
Otherwise lwsws started at boot on a system without an RTC
may feel it was started in 1970, and when the clock is set
by ntpd a little later, has been up for 16,000 days...

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-24 17:44:35 +08:00
Andy Green
92b0d8a614 lwsws fix settable conf dir and error paths
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-22 07:01:35 +08:00
Andy Green
82e883f7ad clean correct file comment at top and remove pointless LWS_CALLBACK_HTTP
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-21 15:18:30 +08:00
Andy Green
7c2d5964f7 lws_ssl_client_connect2 remove duplicate close on error path
https://github.com/warmcat/libwebsockets/issues/532

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-19 17:27:15 +08:00
Andy Green
e7c1c757cd replace LWS_MAX_SOCKET_IO_BUF with context creation info pt_serv_buf_size
This makes it easy for user code to choose the size of the per-thread
buffer used by various things in lws, including file transfer chunking.

Previously it was 4096, if you leave info.pt_serv_buf_size as zero that
is still the default.

With some caveats, you can increase transfer efficiency by increasing it
to, eg, 128KiB, if that makes sense for your memory situation.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-19 12:34:35 +08:00
Andy Green
9b03cb9828 lwsws localhost example conf add testcgi
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 21:53:53 +08:00
Andy Green
7ad94de897 lejp string chunk 255
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 21:27:24 +08:00
Andy Green
cbd11f749e lejp check callback errors and use reject path
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 21:22:06 +08:00
Andy Green
81a5a125f4 deprecate misnamed lws_vhost_get add lws_get_vhost
All the other simple accessors begin lws_get_...

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 13:47:44 +08:00
Andy Green
3dcbf6abc1 deprecate duplicated lws_protocol_get
Use the identical lws_get_protocol()

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 13:33:58 +08:00
Andy Green
34b8161e04 lwsws update example conf to be 7681 no ssl test server
It's going to be much easier to transition to lwsws
if the example config exactly matches what used to be
seen from the default test server.

# mkdir -p /etc/lwsws/conf.d /var/log/lwsws
# cp ./lwsws/etc-lwsws-conf-EXAMPLE /etc/lwsws/conf
# cp ./lwsws/etc-lwsws-conf.d-localhost-EXAMPLE /etc/lwsws/conf.d/test-server
# sudo lwsws

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-17 08:54:42 +08:00
Andy Green
fa34a82ca1 output size trimming with default rxbuf fix
5e203f78e2 accidentally
trashed the "zero rz_buffer_size means 4096" handling
for tx chunking.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-15 08:59:48 +08:00
Andy Green
7a9970f90d win fix warnings from appveyor
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-15 08:31:01 +08:00
Andy Green
0463895b1b appveyor libuv integration
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-14 10:44:11 +08:00
Andy Green
8ef14c0e0a lwsws conf mount extra mimetypes
This patch adds the ability to provide extra mimtypes on a mount.

lwsws conf learns how to do them.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-14 10:22:56 +08:00
Andy Green
d6c5bdb0f9 lejp handle name elements starting with dot
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-14 10:22:19 +08:00
Andy Green
6954daabd2 lwsws conf allow setting cipher list and ecdh curve
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-14 09:50:34 +08:00
Andy Green
d6be6776a8 win32 more build fixes
After alexgille

https://github.com/warmcat/libwebsockets/issues/526

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-14 06:49:29 +08:00
Andy Green
c64e4baccc lws_context_init_client_ssl ssl lib init already done in context
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-13 18:15:08 +08:00
Andy Green
54b856fea4 access_log ensure no reuse of freed log area
Valgrind caught http/1.1 pipelining using dead user agent alloc
for logging... NULL it when we free it since the wsi can be
reused with keepalive

==16208== Invalid free() / delete / delete[] / realloc()
==16208==    at 0x4847ACC: free (vg_replace_malloc.c:530)
==16208==    by 0x4888DC3: _realloc (alloc.c:8)
==16208==    by 0x4888DFF: lws_realloc (alloc.c:16)
==16208==    by 0x487DBCB: lws_access_log (libwebsockets.c:2352)
==16208==    by 0x48956DF: lws_http_transaction_completed (server.c:1245)
==16208==    by 0x4893757: lws_http_serve (server.c:340)
==16208==    by 0x48946EF: lws_http_action (server.c:748)
==16208==    by 0x4894CEF: lws_handshake_server (server.c:900)
==16208==    by 0x48786BF: lws_read (handshake.c:120)
==16208==    by 0x4896103: lws_server_socket_service (server.c:1580)
==16208==    by 0x487FB6B: lws_service_fd_tsi (service.c:779)
==16208==    by 0x48803B7: lws_service_fd (service.c:1079)
==16208==  Address 0x552e5f8 is 0 bytes inside a block of size 86 free'd
==16208==    at 0x4847ACC: free (vg_replace_malloc.c:530)
==16208==    by 0x4888DC3: _realloc (alloc.c:8)
==16208==    by 0x4888DFF: lws_realloc (alloc.c:16)
==16208==    by 0x487DBCB: lws_access_log (libwebsockets.c:2352)
==16208==    by 0x48956DF: lws_http_transaction_completed (server.c:1245)
==16208==    by 0x4893757: lws_http_serve (server.c:340)
==16208==    by 0x48946EF: lws_http_action (server.c:748)
==16208==    by 0x4894CEF: lws_handshake_server (server.c:900)
==16208==    by 0x48786BF: lws_read (handshake.c:120)
==16208==    by 0x4896103: lws_server_socket_service (server.c:1580)
==16208==    by 0x487FB6B: lws_service_fd_tsi (service.c:779)
==16208==    by 0x48803B7: lws_service_fd (service.c:1079)
==16208==  Block was alloc'd at
==16208==    at 0x4846498: malloc (vg_replace_malloc.c:298)
==16208==    by 0x4848D57: realloc (vg_replace_malloc.c:785)
==16208==    by 0x4888DA7: _realloc (alloc.c:6)
==16208==    by 0x4888DFF: lws_realloc (alloc.c:16)
==16208==    by 0x4893EAF: lws_http_action (server.c:565)
==16208==    by 0x4894CEF: lws_handshake_server (server.c:900)
==16208==    by 0x48786BF: lws_read (handshake.c:120)
==16208==    by 0x4896103: lws_server_socket_service (server.c:1580)
==16208==    by 0x487FB6B: lws_service_fd_tsi (service.c:779)
==16208==    by 0x48803B7: lws_service_fd (service.c:1079)
==16208==    by 0x48994B7: lws_io_cb (libuv.c:101)
==16208==    by 0x4AE7B1F: ??? (in /usr/lib/libuv.so.1.0.0)


Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-13 11:10:22 +08:00
Andy Green
b9c095db47 dummy http protocol should always do 404
We only got here if no mount matched, so we should
affirmitively send a 404

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-13 10:31:52 +08:00
Andy Green
97164368dd asserts log which
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-13 10:27:48 +08:00
Sterling Jensen
ecaed5ec94 Fix leak caused by undestroyed pthread mutex 2016-05-13 09:42:37 +08:00
Andy Green
bf31c1bc87 win32 libuv related build fixes
https://github.com/warmcat/libwebsockets/issues/526

On master, cleanups and refactor mean the last two problems already
don't exist (array is gone from main.c and http.c is deleted)

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-13 08:20:12 +08:00
Andy Green
2700d1c0c3 protocol_client_loopback_test
This is used to confirm that SSL client connections can coexist with
a vhost doing serving.

To set it up,

/*
 * This is a bit fiddly...
 *
 * 0) If you want the wss:// test to work, make sure the vhost is marked with
 *    enable-client-ssl if using lwsws, or call lws_init_vhost_client_ssl() on
 *    the vhost if you're doing it by hand.
 *
 * 1) enable the protocol on a vhost
 *
 *      "ws-protocols": [{
 *     "client-loopback-test": {
 *      "status": "ok"
 *     },  ...
 *
 *     the vhost should listen on 80 (ws://) or 443 (wss://)
 *
 * 2) mount the http part of the test one level down on the same vhost, eg
 *   {
 *      "mountpoint": "/c",
 *      "origin": "callback://client-loopback-test"
 *   }
 *
 * 3) Use a browser to visit the mountpoint with a URI attached for looping
 *    back, eg, if testing on localhost
 *
 *    http://localhost/c/ws://localhost
 *    https://localhost/c/wss://localhost
 *
 * 4) The HTTP part of this test protocol will try to do the requested
 *    ws client connection, to the same test protocol on the same
 *    server.
 */

Results should look like this

lwsws[29938]: client connection to localhost:443 with ssl: 1 started
lwsws[29938]: server part: LWS_CALLBACK_ESTABLISHED
lwsws[29938]: checking client ext permessage-deflate
lwsws[29938]: instantiating client ext permessage-deflate
lwsws[29938]: Client connection established
lwsws[29938]: Client connection received 7 from server 'Made it'

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-12 21:14:13 +08:00
Andy Green
fb8be0507e add lws_init_vhost_client_ssl api to allow client ssl use on a vhost
Also add lwsws "enable-client-ssl": "1" vhost option to match.

Client cert iclient ssl is not supported in lwsws, if someone wants it, it can be added.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-12 21:14:13 +08:00
Andy Green
b6d229d726 check oom on lws_malloc
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-12 21:04:33 +08:00
Andy Green
03e628b9a6 windows no chown on log file generation
https://github.com/warmcat/libwebsockets/issues/524

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-11 18:59:27 +08:00
Andy Green
5e203f78e2 output allow LWS_PRE+4 on top of rx_buffer_size for max send chunk
https://github.com/warmcat/libwebsockets/issues/522

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-10 21:35:38 +08:00
Andy Green
0a4da2c71f lwsws remove all protocols use lws default http
Actually lwsws doesn't need his own protocol handler even for http
any more.  The default http handler in lws should do everything.

Move the cgi routing into lws default http protocol handler, and
delete lwsws one.  Remove all protocols from lwsws so the lws
default one gets used.

With this, and the earlier move of lejp into lws, lwsws itself
becomes 15.5KB of x86_64 (mainly conf parsing).

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-10 11:28:30 +08:00
Andy Green
677d6c1516 lwsws cleanup and allocate config strings dynamically
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-10 10:42:19 +08:00
Andy Green
f530de84ac lwsws remove non libuv SIGINT handler
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-10 10:31:16 +08:00
Andy Green
5afc56770d lejp bring into lws
lejp is already in lws as part of lwsws.  However it's too generally useful
to just put directly in lwsws, it will lead to multiple copies of the source
in differet subprojects.

This moves it directly into lws, lwsws now gets it from there.

Like lwsws, by default at cmake it is disabled.  Selecting LWS_WITH_LWSWS now
selects LWS_WITH_LEJP and you can set that at cmake individually as well.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-10 09:47:10 +08:00
Andy Green
b293f52672 logging migrate user stimulated logging to info
https://github.com/warmcat/libwebsockets/issues/520

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-09 13:31:43 +08:00
Andy Green
b24aaeb822 add protocol plugin for post demo
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-09 10:05:43 +08:00
Andy Green
4010694d04 POST handling dont autocomplete transaction
Until now lws has finished the HTTP transaction when the POST body was
completely received.

However that needlessly makes it impossible to send a HTTP 200 and a
response without a redirect.

This changes lws behaviour after sending the LWS_CALLBACK_HTTP_BODY_COMPLETION
callback to no longer terminate the HTTP transaction.

If you want the old behaviour, you can terminate the transaction with
lws_http_transaction_completed() in the LWS_CALLBACK_HTTP_BODY_COMPLETION
callback.

Otherwise it's now possible to call lws_callback_on_writable() from
LWS_CALLBACK_HTTP_BODY_COMPLETION.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-09 09:37:44 +08:00
Andy Green
4e3b0ce973 libuv tidy up destroy and disable timer races during shutdown
Also don't make us wait 1s for init to finish.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-08 17:07:46 +08:00
Andy Green
584f7109a0 client fix for operation with libuv
- libuv socket init binding was missing for client

 - failures like no ws protocol match are deferred until the
   ah is bound.  Check for these failures and make sure we
   tell the guy trying to set up the client connection the
   wsi is NULL if it has already failed and closed.

 - pfd.events was not initialized for the client init path

With this general client function is more robust but also
client connections work properly with libuv.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-08 17:03:01 +08:00
Andy Green
11d8efef88 context only destroy protocols if init got sent
If for some reason we exit before the protocol init action
(which is delayed for libuv) we should not send the protocol
destroy messages

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-08 17:00:38 +08:00
Andy Green
ad45efa434 client handle connection fail at second phase properly
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-08 16:58:18 +08:00
Andy Green
43bfd951fa test server post also take care about POST len termination
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-08 16:56:34 +08:00
Andy Green
26019a2586 update api docs v2.0
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-07 14:24:36 +08:00
Andy Green
3db2c65157 fix %3d handling in path part and add attack.sh
https://github.com/warmcat/libwebsockets/issues/518

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-07 08:31:33 +08:00
Andy Green
6cca3fbb12 protocol plugins default also add example in test server v2.0
This shows how to deliver per-vhost, per-protocol option
name:value pairs using code.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-06 15:56:13 +08:00
Andy Green
f6585285cb protocol plugins set default
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-06 14:24:59 +08:00
Andy Green
f4767497d3 mimetypes add additional canned
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-06 08:02:57 +08:00
OndraCo
a709cbb2f5 MSVC from 2015 up has vsnprintf 2016-05-06 07:48:56 +08:00
OndraCo
6a81b08dc2 Disabled static link handling for WIN_CE to temporarily avoid the fact that it has no stat struct 2016-05-06 07:48:56 +08:00
OndraCo
624b23df5e Changes to enable WIN CE support 2016-05-06 07:48:23 +08:00
Enno Boland
b6e2ad6b50 lib/server.c: fix ipv6 support 2016-05-06 07:47:54 +08:00
Enno Boland
7731a3e575 lib/ssl.c: fix libre- and boringssl 2016-05-06 07:46:26 +08:00
Andy Green
aef3dc4ad7 snip changelog back to 1.7
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-06 07:45:19 +08:00
Andy Green
5500643f3c v2.0.0
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-05 09:40:18 +08:00
Andy Green
ad40037c80 recv revert treating zero as hangup
While checking with ab, I found
commit 30cdb3ac8f
Author: Justin Chen <justinchen00@github.invalid.com>
Date:   Thu Apr 14 21:40:53 2016 +0800

    recv treat zero return as error

    https://github.com/warmcat/libwebsockets/issues/475

turned ab performance to crap, reverting it made everything fast again.

recv manpage says there is three ways to get zero returned

1)       When a stream socket peer has performed an orderly shutdown, the return value will be 0  (the  traditional  "end-of-file"
       return).

2)       Datagram  sockets  in  various  domains  (e.g., the UNIX and Internet domains) permit zero-length datagrams.  When such a
       datagram is received, the return value is 0.

3)       The value 0 may also be returned if the requested number of bytes to receive from a stream socket was 0.

we can't just assume it means the peer shut down.

If the peer shut down, then the event loop should get an event on the socket like POLLHUP and deal with it that way.

So the patch mentioned above is simply reverted here.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-05 09:06:09 +08:00
Andy Green
c25b290b20 stats upgrade rx tx to long long
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-04 15:59:55 +08:00
Andy Green
8b02454634 cgi add generic wait as antizombie defence
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-04 12:23:27 +08:00
Andy Green
4eab21976c clean build warning on windows
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-04 11:11:15 +08:00
Andy Green
8554bd43bd appveyor openssl 1.0.2h
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-04 11:02:33 +08:00
Andy Green
5474221a0c libuv fixes for foreign loop test
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-04 08:40:46 +08:00
Denis Osvald
d098ba47cb test server foreign loop
AG: adapt to use switch on existing test-server-libuv app
AG: correct the foreign lifecycle to be valgrind clean

This adds a switch -f --foreign that makes the test-server-libuv test
create his own libuv loop outside of lws, and use lws on it before
destroying lws and then his own loop.

If selected, the foreign loop runs for 5s before lws and 10s after
lws is stopped, for testing purposes.

Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-05-04 06:00:37 +08:00
Andy Green
8ea8d08623 client provide user_space on LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER
https://github.com/warmcat/libwebsockets/issues/509

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-03 21:39:16 +08:00
Andy Green
f1fd882d57 client fix reaction to tls failure
https://github.com/warmcat/libwebsockets/issues/508

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-03 07:26:10 +08:00
Andy Green
cae57ad98d plugins_dir convert to array
If OOT lws plugins will be packaged as separate projects,
they're going to want to install their plugins somewhere
that makes sense for the package instead of one big lws
plugin dir.

This patch changes info to have a const char ** to a NULL
terminated array of directories it should search for
plugins.  lwsws knows about this and you can add to the
dir array using config fragments like

{
  "global": {
   "plugin-dir": "/usr/local/share/coherent-timeline/plugins"
  }
}

if the config fragment in /etc/lwsws/conf.d/ is also managed by the
package with the plugin, it can very cleanly add and remove itself
from lwsws based on package install status.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 10:03:25 +08:00
Andy Green
4d5ac9c910 plugin standalone example for oot build
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 07:49:29 +08:00
Andy Green
d5dc5dff13 provide default empty protocol zero handler if NULL protocols in info
Move the dummy stub protocol into the library as the default
if NULL protocols given, since that is likely to become popular.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 07:49:29 +08:00
Andy Green
2f72f67a65 libwebsockets-test-server-v2.0 showing how to use mounts and plugins
No dependency on test-server.h (just libwebsockets.h and getopt.h / syslog.h)

No need for any user callback code and all in one short file.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 06:03:10 +08:00
Andy Green
952fcdede1 per vhost options struct explicitly const
Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 06:01:59 +08:00
Andy Green
4664f71ed3 create_vhost move mounts list to info
There's no reason to not have the mounts linked list init also in the info
struct, rather than provide as a paramater to lws_create_vhost().  Now
is a good time to normalize that since this api only exists in master.

This also allows oldstyle "do everything at context creation time in one
vhost" guys to leverage mounts.

Also there's no reason the mounts linked-list pointer and all uses in lws
are non-const, so make them all explicitly const *.

Update the info struct docs to clarify which members are used when creating
a vhost and which for context creation.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-05-02 04:59:54 +08:00
Patrick Farrell
72e0e2a516 Add warn_unused_result check, attribute only supported by GCC 3.4 or later
warn_unused_result was introduced in GCC version 3.4.

Change-Id: I6c2cc938d2b868ddfe0889cc41d7fa9d70e1b907
2016-04-30 02:07:13 +08:00
Stephan Eberle
67729c954f Eliminated compile warning in test-client.c 2016-04-27 07:38:52 +08:00
Stephan Eberle
ff32d01742 Fixed misspelled size_t 2016-04-27 07:36:41 +08:00
Stephan Eberle
8087959acf Fixed build failure under Visual Studio 14 2015 2016-04-27 07:34:43 +08:00
Stephan Eberle
f14f7237dc Fixed build failure under Linux with GNU 4.8.4 2016-04-27 07:32:56 +08:00
Stephan Eberle
3f41a9de5b Fixes for building with MinGW
Added fixes to avoid compile errors and warnings when building under
Windows using MinGW
2016-04-27 07:31:22 +08:00
Andy Green
734f10a19b logs document logrotate configuration for lwsws
After discussion here

https://libwebsockets.org/pipermail/libwebsockets/2016-April/002304.html

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-26 19:07:17 +08:00
Andy Green
12aeba73c0 server status add getloadavg and lib version and more css
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-26 14:54:09 +08:00
Andy Green
f79534e0a4 post example in test server
https://github.com/warmcat/libwebsockets/issues/501

This demonstrates how to do a 303 redirect on POST and provide
the results there, in both libwebsockets-test-server and the
plugin version.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-25 10:04:49 +08:00
Andy Green
24208879ce docs vhosts and mounts
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-25 02:01:25 +08:00
Alexander Bruines
fe3f60da92 android make script contrib
This is based on

http://stackoverflow.com/questions/11929773/compiling-the-latest-openssl-for-android/
2016-04-23 18:28:03 +08:00
Alexander Bruines
119bdaadc2 Android needs sys/resource.h 2016-04-23 17:18:46 +08:00
Andy Green
8dd32ae7c1 http2 handle error path on ensure_user_space
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 09:36:18 +08:00
Andy Green
2f216280f8 pollin shouldnt always win over pollout
https://github.com/warmcat/libwebsockets/issues/501

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 09:26:11 +08:00
Andy Green
a2757c74b1 coverity 160162 check fcntl return
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 08:16:18 +08:00
Andy Green
16146cdad8 coverity 160163 LWS_WARN_UNUSED_RESULT needed on header apis
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 08:16:18 +08:00
Andy Green
516f388585 coverity 160166 readlink doesnt NUL terminate
Ah a real bug... well done coverity, that could have been nasty.

readlink unusually doesn't NUL terminate the result... take care about it.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 08:16:17 +08:00
Andy Green
5dd57a9430 coverity 160165 dead code
Yes... extension selection is not wired up yet

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 08:16:17 +08:00
Andy Green
09490aeb93 coverity 160167 resource_path set at cmake may overflow buffer
resource_path is configured at cmake time (it's like /usr/share/libwebsockets-test-server)
it's true if you gave a >255 char path there it would blow up.

It's fixed but again not network-accessible.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 08:16:17 +08:00
Andy Green
1536c5beb6 coverity 160160/160161 test server commanline args strncpy NUL management
Just test app argument string handling, it is "HIGH" impact as Coverity
says but it's not network-accessible or in the library.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 07:21:48 +08:00
Andy Green
5bf2ce193b debug spew reduce
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-23 07:14:03 +08:00
Andy Green
2110ce9727 cruft remove sigusr2 handling
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-22 21:57:10 +08:00
Andy Green
81bf43b34f client account for retries
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-22 12:29:44 +08:00
Andy Green
42e8b189dc http cache policy
This allows mounts to define the caching policy of the files inside them.

Support is added in lwsws for controlling it from the config files.

The api for serializing a mount struct opaquely is removed and lws_http_mount struct
made public... it was getting out of control trying to hide the options.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-22 08:53:49 +08:00
Andy Green
748a2210bd cgi lifecycle logging
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-21 17:50:58 +08:00
hgrundy
d02028bf25 Update libwebsockets.h
Fix for FreeBSD
2016-04-21 17:50:55 +08:00
Andy Green
5c3a3c5e95 access_log reset transaction size when using keepalive
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-20 05:58:01 +08:00
Andy Green
912c42f291 ah try for ah when adopting socket to vhost
https://github.com/warmcat/libwebsockets/issues/496

Even if no ah available, we will be on the ah waiting list and get triggered
when one is freed and we're next in line.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-19 10:10:53 +08:00
Andy Green
5f947cdcd9 windows another uv_poll_init
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-19 09:10:08 +08:00
Lucas Terra
1759e32c7b Conditionally define snprintf on Windows
snprintf is already defined in MSVC 2015. If you redefine it <stdio.h> will cry about it and abort the compilation.
2016-04-19 08:23:13 +08:00
Roger A. Light
c6496b2510 Script and config to make tracking ABI/API changes easy.
AG: enhance the script a bit and fix the make instructions
2016-04-19 06:48:51 +08:00
Andy Green
c673125ce0 client http
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-18 20:05:43 +08:00
Andy Green
451cee5d47 polarssl implementation
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-18 20:05:43 +08:00
Lucas Terra
19f61e59b0 Fix inet_ntop linking error on Windows 2016-04-18 20:05:43 +08:00
Andy Green
980614035f plugin lws server status
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-18 20:05:43 +08:00
Andy Green
4714cf02f4 vh doubly linked list for wsi on same protocol
This trades off a couple of wsi pointers for vastly increased speed
for the callback when writeable "all protocol" variants when there
are many kinds of wsi active.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-16 08:40:35 +08:00
Andy Green
7e2c3851bf redirects need fake content metadata
Chrome deals with it without on desktop, but Android chrome
waits for the connection to time out before actioning the
redirect, since it feels there might be html payload coming.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 20:09:36 +08:00
Andy Green
b21c20b5ff context settable server string
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 13:58:24 +08:00
Andy Green
cec2fd55a8 lws normalize http response
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 13:58:24 +08:00
Andy Green
2f0bc93d46 lws access log option and lwsws conf
This adds the ability to store apache-compatible logs to a file given at
vhost-creation time.

lwsws conf can set it per-vhost using "access-log": "<filepath>"

The feature defaults to disabled at cmake, it can be set independently but
LWS_WITH_LWSWS set it on.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 13:58:24 +08:00
Justin Chen
30cdb3ac8f recv treat zero return as error
https://github.com/warmcat/libwebsockets/issues/475
2016-04-15 13:58:24 +08:00
Andy Green
f3e9c7347e json dump vhost
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 13:57:22 +08:00
Andy Green
57f2007105 vhost collect rx tx stats
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-15 13:57:20 +08:00
Andy Green
868b9f2ecb strict transport security vhost option flag and lwsws conf support
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 21:27:38 +08:00
Andy Green
a1ab201436 lwsws conf unix sockets support vhost conf option
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 21:27:38 +08:00
Andy Green
144594dd43 unix sockets make vhost option
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 21:27:38 +08:00
Andy Green
1042b34127 lwsws conf move interface to be vhost attribute
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 21:27:38 +08:00
Andy Green
cc3c6fb047 vhost should have his own options
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 21:27:38 +08:00
Alexander Bruines
ddd9bfaaac Add testapps support for client certs and CRLs
AG: plumb into cmake to avoid travis mac blowing up
2016-04-14 20:59:16 +08:00
Yeonjun Lim
44182452c8 Support for abstract socket 2016-04-14 20:59:16 +08:00
Yeonjun Lim
3c6a8c1a24 Add unix domain socket 2016-04-14 20:59:16 +08:00
Andy Green
b46e4a866d vhost keepalive timeout
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 20:59:16 +08:00
Andy Green
5b95081000 ssl server init failure info
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 20:59:16 +08:00
Andy Green
5e799a45cc ssl store vhost in ssl private data not context
Only used by the ssl verify callback

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-14 20:59:16 +08:00
Andy Green
f5efa74cb7 cgi post
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:40 +08:00
Andy Green
22d6f39e7f http2 update integration
https://github.com/warmcat/libwebsockets/issues/489

This

1) fixes the vhost changes on master

2) works around the ah pool changes

3) fixes some other build problems that appeared

4) hacks out physical flow control for internal streams

5) updates the advertised protocol to h2 needed by, eg, chrome 51

That gets it able to serve small (<4K, ie, one packet) files over http2

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:40 +08:00
Andy Green
a5e73a1a4b lwsws cgi integration
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:40 +08:00
Andy Green
6e7b79b263 lwsws specifically signed char return lejp
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:40 +08:00
Andy Green
0a183545b2 lwsws conf and plugins convert to libuv apis
After discussion here

https://libwebsockets.org/pipermail/libwebsockets/2016-April/002268.html

scandir usage in

 - lwsws conf.c
 - lws plugin support

and

 - lws plugin apis for dl

are converted to us libuv apis so they can work cross-platform easily.

lws itself remains not requiring libuv, although it's an option.

 - LWS_WITH_LWSWS
 - LWS_WITH_PLUGINS

now force LWS_WITH_LIBUV if selected... both of these are new features
only in master atm and both are off by default in CMake.

There's a complication libuv can be too old to offer the necessary apis,
this is the case in Travis Trusty instance.  In that case, UV_VERSION_MAJOR ==0,
then the unix-only plugin implementation is used instead.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:40 +08:00
Andy Green
e01a551a17 vhost if explicit vhosts only enable protocols with pvo mentions
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-13 11:53:16 +08:00
Andy Green
d459a6fadc lwsws redirect and correct vhost selection before accept
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-08 18:38:06 +08:00
Andy Green
37098ae2a2 lwsws protocol filter and options
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-08 16:13:14 +08:00
Meir Yanovich
efcf496b45 windows snprintf is _snprintf
https://github.com/warmcat/libwebsockets/issues/411#issuecomment-207290650
2016-04-08 16:11:27 +08:00
Galen Ma
da77a6007d android fix rlimit
https://github.com/warmcat/libwebsockets/issues/488
2016-04-08 16:02:59 +08:00
Andy Green
c5376b141a extensions add api for user code option manipulation
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-08 09:45:49 +08:00
Andy Green
150233d61f urldecode forbid malformed
And update attack.sh to confirm the new test cases

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-07 10:08:35 +08:00
Andy Green
fd12fc2781 plugins also link lwsws against websockets_shared
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-07 09:38:08 +08:00
Denis Osvald
8d21c350fc plugins link libwebsockets dynamically
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-04-07 09:38:08 +08:00
Andy Green
020770566e plugins
This adds support for dynamically loaded plugins at runtime, which
can expose their own protocols or extensions transparently.

With these changes lwsws defaults to OFF in cmake, and if enabled it
automatically enables plugins and libuv support.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-07 09:38:08 +08:00
Andy Green
09998e3ad8 libuv add idle processing to force service where needed
https://github.com/warmcat/libwebsockets/issues/485

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-07 09:38:08 +08:00
Andy Green
e8009155ba lws_service_adjust_timeout optimize
Make it exit quicker if something is pending

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-07 09:38:08 +08:00
Andy Green
513580d1bd revert 622d9f2 ssl pending handcrank
https://github.com/warmcat/libwebsockets/issues/483

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-05 19:43:07 +08:00
Meir Yanovich
2e15d0ad57 win32 libuv build notes 2016-04-05 19:19:46 +08:00
Meir Yanovich
d5ff4bf1bc win32 needs strange strftime args 2016-04-05 19:15:34 +08:00
Andy Green
a19ff9b24d uri processing reject paths not starting with slash
https://github.com/warmcat/libwebsockets/issues/481

Return 403 Forbidden if we don't end up with a uri path starting with /

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-05 06:58:04 +08:00
Andy Green
45dead99e0 libuv report init failure
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-05 06:57:02 +08:00
Andy Green
f29fbdf78a test server libevuv set LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT on ssl
Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-05 06:56:59 +08:00
Meir Yanovich
9694ebefe8 libuv win32 fixes 2
https://github.com/warmcat/libwebsockets/issues/411#issuecomment-204284368
2016-04-01 15:56:48 +08:00
Andy Green
1c428c3154 test server align rxbuf with permessage deflate rx buf size
Add a test html button that will send 9KB of junk to confirm it

https://github.com/warmcat/libwebsockets/issues/480

permessage-deflate now checks the protocol rx buffer size for being
>=128, if not, permessage-deflate is disabled on that connection.

If it is >=128 but less than the zlib decompress buffer size, the
zlib decompress buffer size for that connection is reduced to the
nearest power of two of the protocol rx buf size.

To test this, dumb_increment is left violating the >= 128 rx buffer
size and permessage-deflte can be seen to be disabled on his
connections in the test html.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-04-01 08:47:05 +08:00
Andy Green
bbcd24167f libuv win32 fixes
Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-31 20:12:14 +08:00
Andy Green
02ff145f50 cmake libuv fix include forcing path
Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-31 09:02:25 +08:00
V.Krishn
7d5b1531ef Fix build with musl libc
Fix building libwebsockets with the musl C libary.

<sys/cdefs.h> is an internal glibc header and should be avoided in user code.

__P() was used for compatibility with some old K&R C compilers, when there were
no prototypes (which were introduced to C with C89). As supporting legacy
non-ANSI compilers is nowadays not necessary anymore get rid of the unnecessary
function prototype using __P().
2016-03-30 06:25:19 +08:00
Andy Green
1a3f17700e mbedtls and polarssl first part
polarssl is the old name for mbedtls.  Unfortunately the two are confused in eg,
Fedora.  For our purposes, polarssl or mbedtls < 2.0 has includes in
/usr/include/polarssl and polarssl_ apis and we call that "polarssl".

polarssl or mbedtls >=2.0 has includes in /usr/include/mbedtls and mbedtls_ apis,
we call that "mbedtls".

This has to be spelled out clearly because eg Fedora has a package "mbedtls" which
is 1.3.x and has the polarssl_ apis and include path.  We will deal with that as
"polarssl" despite the package name then.

This patch lets you use LWS_USE_POLARSSL or LWS_USE_MBEDTLS and set the include and
library path like this

cmake .. -DLWS_USE_POLARSSL=1 -DLWS_POLARSSL_INCLUDE_DIRS=/usr/include -DLWS_POLARSSL_LIBRARIES=/usr/lib64/libmbedtls.so.9

This patch adds the cmake support and adapts [private-]libwebsockets.h but doesn't
modify the apis in ssl[-*].c yet.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-29 21:49:18 +08:00
Andy Green
f632e449de revert cmake remove targets from install path
Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-29 21:44:30 +08:00
Andy Green
7fe02e3af5 ssl split out common server and client ssl sources
Most of ssl.c is under a #ifdef for client or server disabled...
let's get rid of it and have CMake just build the appropriate
files

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-29 08:51:42 +08:00
Andy Green
eefb13a0a7 ssl migrate client pieces to ssl.c
Some ssl-specific code was still hiding out in client.c instead of
going in ssl.c

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-28 12:43:55 +08:00
Andy Green
cd0c696a0d lwsws Libwebsockets Web Server
This makes a start on the LibWebSockets WebServer.

The app cmake build support and JSON config parsing are implemented and
the app can start, create the vhosts, listen and serve file:// mounts on
them.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-28 10:49:57 +08:00
Andy Green
d526c50c22 introduce vhosts
This patch splits out some lws_context members into a new lws_vhost struct.

 - ssl state and options per vhost
 - SSL_CTX for serving and client per vhost
 - protocols[] per vhost
 - extensions[] per vhost

lws_context maintains a linked list of lws_vhosts.

The same lws_context_creation_info struct is used to regulate both the
context creation and to create vhosts: for backward compatibility if you
didn't provide the new LWS_SERVER_OPTION_EXPLICIT_VHOSTS option, then
a default vhost is created at context creation time using the same info
data as the context itself.

If you will have multiple vhosts though, you should give the
LWS_SERVER_OPTION_EXPLICIT_VHOSTS option at context creation time,
create the context first and then the vhosts afterwards using

  lws_create_vhost(contest, &info);

Although there is a lot of housekeeping to implement this change, there
is almost no additional overhead if you don't use multiple vhosts and
very little api impact (no changes to test apps).

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-28 10:15:25 +08:00
Andy Green
e2cf3e1cc0 SNI for server side: receive callback
This takes tha callback and binds the lws_context to the SSL_CTX so we can
get the lws_context in the callback.

It just logs the incoming hostname atm.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-26 09:27:34 +08:00
Andy Green
476329f3f8 http2 build with alpn capable ssl no debug
Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-25 21:01:11 +08:00
Andy Green
c6fd360160 LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT to default to runtime ssl disable
https://github.com/warmcat/libwebsockets/issues/468

Adds lws_check_opt() to regularize multibit flag checking.

There's a new context creation flag LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT,
this is included automatically if you give any other SSL-related option flag.
If you give no SSL-related option flag, nor this one directly, then even
though SSL support may be compiled in, it is never initialized nor used for the
whole lifetime of the lws context.

Conversely in order to prepare the context to use SSL, even though, eg, you
are not listening on SSL but will use SSL client connections later, you can
give this flag explicitly to make sure SSL is initialized.

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-23 09:22:11 +08:00
Denis Osvald
f107e4bb85 libuv: sigint API cleanup
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-03-23 07:46:39 +08:00
Denis Osvald
de9f794b27 libuv: handle signals only if requested
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-03-23 07:44:06 +08:00
Andy Green
1a13885afd cgi env
Improve cgi support so it's capable of running cgit
2016-03-21 15:17:33 +08:00
Andy Green
1e5a9ad2dc proxy rewrite
If you enable -DLWS_WITH_HTTP_PROXY=1 at cmake, the test server has a
new URI path http://localhost:7681/proxytest If you visit here, a client
connection to http://example.com:80 is spawned, and the results piped on
to your original connection.

Also with LWS_WITH_HTTP_PROXY enabled at cmake, lws wants to link to an
additional library, "libhubbub".  This allows lws to do html rewriting on the
fly, adjusting proxied urls in a lightweight and fast way.
2016-03-20 11:59:53 +08:00
Andy Green
5c8906e931 client chunked transfer encoding
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-19 17:21:45 +08:00
Andy Green
c3c2d6d953 cgi header processing
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-19 17:21:45 +08:00
Andy Green
8007cc6829 b64decode correct decode of some strings
https://github.com/warmcat/libwebsockets/issues/467

Signed-off-by: Andy Green <andy@warmcat.com>
2016-03-19 07:43:22 +08:00
Andy Green
7bc87ab662 clean signed mismatches and protect ssl specific code
https://github.com/warmcat/libwebsockets/issues/466

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-18 23:55:59 +08:00
Alex Hultman
cea07d6f1f lws_write restrict chunk size 2016-03-18 15:02:27 +08:00
Andy Green
0f9904fedf ssl option for auto redir to https
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-17 15:27:18 +08:00
Alex Hultman
599cad9436 Optimize payload exhaustion
https://github.com/warmcat/libwebsockets/pull/462

AG refactor and do loop unrolling
2016-03-17 09:41:44 +08:00
Andy Green
e32c0ba29b appveyor update 1.0.2g
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-17 08:57:33 +08:00
Andy Green
5960158958 clang fixes 1
https://github.com/warmcat/libwebsockets/issues/461

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-15 21:49:32 +08:00
bjqiwei
04935e28b0 client perform WSI_CREATE callback 2016-03-15 16:24:58 +08:00
Andy Green
c793944f17 socket interface bind generalize to lws_socket_bind
Move the socket bind to interface code out of server into
libwebsockets.c and make a private api for it.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-13 08:50:55 +08:00
Joakim Soderberg
9a720bbb50 ssl ecdh adapt if missing ecdh.h include
https://github.com/warmcat/libwebsockets/issues/457
2016-03-12 08:48:41 +08:00
Andy Green
4f5ebec3ef client ext hdr skip if no arg
https://github.com/warmcat/libwebsockets/issues/453#event-583227314

abnf does say has to be at least one arg

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-09 23:13:31 +08:00
Andy Green
43befcba91 release checklist specfile install soname
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-09 10:51:06 +08:00
Andy Green
4a1c6297e8 rpm specfile so install list bump
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-09 10:50:25 +08:00
Andy Green
f66a12abb2 libev set foreign loop properly
After gaby64

https://github.com/warmcat/libwebsockets/issues/455

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-09 07:44:49 +08:00
Andy Green
7d22c29722 windows deal with no snprintf
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-04 10:53:51 +08:00
Andy Green
fb5f33bb2f test server http proxy
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-02 18:57:41 +08:00
Andy Green
494418abac add explicit parent child wsi relationships
wsi can have a full tree relationship with each other using
linked lists.  closing the parent ensures the children are
closed first.

Convert cgi to use this instead of his cgi-specific sub-wsi
management.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-03-02 18:57:41 +08:00
Alex Hultman
fe16003644 libuv.c: Service fd with LWS_POLLHUP on poll errors 2016-03-02 18:57:26 +08:00
Ondraco
442ae80b87 wince minor adaptations
https://github.com/warmcat/libwebsockets/issues/444
2016-03-01 07:50:18 +08:00
Daniel Santos
f3d1d41bdd satisfy apparently bogus compiler warning somewhere 2016-03-01 07:50:17 +08:00
Andy Green
a661ee5d53 client support http without ws
Server support for http[s] as well as ws[s] is implicit.
But until now client only supported ws[s].

This allows the user code to pass an explicit http method
like "GET" in the connect_info, disabling the ws upgrade logic.

Then you can also use lws client as http client, not just ws.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 18:41:36 +08:00
Andy Green
2d8d35a1be client accept connection request even if no free ah
It can join the free ah list and pick up client connect processing
later when the ah becomes available; this simplifies the code
doing the request since he won't have to deal with unexpected
failures / retries based on dynamic ah availability.

To do this though we have to handle that the connect_info members
may not have scope that lets them still exist after we return from
the first connect call, we stash them in a malloc'd buffer so the
connect processing can have them much later even so.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 14:19:16 +08:00
Andy Green
f859e2d3ed release checklist ab
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 11:26:13 +08:00
Andy Green
e3d141dae9 adopt readbuf do service
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 11:04:53 +08:00
Andy Green
ee699c0036 libuv when in use skip shutdown close phase
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 10:34:58 +08:00
Andy Green
442e1c850d windows listen for pollhup
After Ondraco

https://github.com/warmcat/libwebsockets/issues/441

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 10:34:58 +08:00
Andy Green
83af28a747 ah move more_rx_waiting to wsi scope
Originally this was alright in wsi->u.hdr, because ah implied header
processing.  But since we allowed ah to be held across http
keep-alive transactions if we saw we had more header data, it means
we were trying to read this union member out of scope after it had
transitioned.

Moving the more_rx_waiting member to be a 1-bit bifield in the wsi
solves it and lets us check the state any time later at http
transaction completion.

https://github.com/warmcat/libwebsockets/issues/441

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-29 10:34:58 +08:00
Andy Green
03384721e7 test server libuv support status protocol
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-27 13:17:26 +08:00
Andy Green
38a1cbb498 libuv create 1Hz background timeout check
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-27 11:04:04 +08:00
Kamil Rytarowski
f57a2b5a8c Don't include <sys/cdefs.h> for NetBSD
We needed it for the BSD symbol to be defined, while __NetBSD__ is defined
with a compiler.

Thanks Andy Green for the initial fix.

Signed-off-by: Kamil Rytarowski <n54@gmx.com>
2016-02-27 10:30:59 +08:00
Andy Green
c0495892bc netbsd netinet include
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-27 09:34:28 +08:00
Andy Green
1bcc110299 force service properly when unconsumed rxbuf in ah
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-26 10:48:51 +08:00
Andy Green
58cc41bc1c adopt readbuf fix no ah path
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-26 09:22:29 +08:00
Andy Green
4ba798dd7d close wsi must do detatch ah flow even if no ah
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 21:50:49 +08:00
Andy Green
897197146a improve timeout and ah list comments
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 21:43:29 +08:00
Andy Green
73321ccfb0 remove ah scan
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 21:43:09 +08:00
Andy Green
d8267a43b8 fix missing callback return check
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 20:27:10 +08:00
Andy Green
0a9bd7e971 unix privs change group before user
Otherwise we no longer have privs to change the group after doing the user

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 15:12:16 +08:00
Andy Green
a11018089f test server log LWS_CALLBACK_HTTP
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 15:06:37 +08:00
Andy Green
d61bed3ce6 logging timestamp creation expose as api
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 15:01:55 +08:00
Andy Green
6a8099b071 cgi
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:45:17 +08:00
Andy Green
dbfbbb41b1 user code must explicitly complete http transaction
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:45:17 +08:00
Andy Green
5c0bcf49a8 defeat POLLOUT if socket in shutdown wait
After andrejs.hanins@ubnt.com

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:45:17 +08:00
Andy Green
51d9afadd6 adopt variant with preamble rx
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:45:17 +08:00
Andy Green
26d4249a3f ws union member must have actual struct at start not pointer
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:45:17 +08:00
Andrejs Hanins
9dbfe07798 client callback closed if not upgraded also for server connection
https://github.com/warmcat/libwebsockets/issues/437
https://github.com/warmcat/libwebsockets/pull/440
2016-02-25 09:45:17 +08:00
Andrejs Hanins
cc551fc0bf client connect must init position_in_fds_table 2016-02-25 09:45:17 +08:00
Andy Green
3ccac4d583 windows doesnt have localtime_r
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:43:59 +08:00
Andy Green
401d49aadc test server status no ssl include time header
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-25 09:43:59 +08:00
Andy Green
0ad1a6e5bf test server add lws_status
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-21 11:09:40 +08:00
Andy Green
d9da2c469f LWS_BUILD_HASH improve
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-21 11:09:40 +08:00
Andy Green
aa85024f9a test html add tabs
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-21 11:07:22 +08:00
Andy Green
ce0326b839 libwebsockets.org url updates
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-21 10:42:46 +08:00
Sebastian Reimer
57a1df4c88 lwsl stderr sink use formatted time
AG change to use localtime_r for threadsafety and leave old method as fallback
2016-02-21 07:42:49 +08:00
Andy Green
0a3d31b8a6 ssl get rid of build_cert_chain
It's not supported on major distro SSL (Fedora is 1.1) and
libressl (on 2.x) doesn't have it either.

https://github.com/warmcat/libwebsockets/issues/435

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-21 07:08:28 +08:00
Alex Hultman
ecf7f00772 Stop uv loop in default signal handler, clean-ups 2016-02-20 23:06:56 +08:00
Alex Hultman
2904de5ff2 Make sure every handle is closed before destroying the uv loop 2016-02-20 22:48:34 +08:00
Alex Hultman
f84be14677 Don't destroy ev/uv loops if they haven't been created yet 2016-02-20 19:29:06 +08:00
Andy Green
0c3cc2efcb changelog sync
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-20 09:12:52 +08:00
Andy Green
d0249a82ec attack.sh add check for nonexistant file processing
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-20 08:12:31 +08:00
Andy Green
c6b0b607d4 lws_return_http_status send content length
If we're sending content, we must do so with a
content-length on http/1.1

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-20 07:53:24 +08:00
Andy Green
e34512e5e9 test server example systemd service file
Make sure you have daemonization enabled to use this

make .. -DLWS_WITHOUT_DAEMONIZE=0

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-19 12:46:36 +08:00
Andy Green
8582d47a00 daemonize work under systemd
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-19 12:44:59 +08:00
Andy Green
c1e6e38709 ah pool lifetime use dynamic rxpos
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-19 11:47:52 +08:00
Andy Green
9231afca4b test server allow set uid gid from cmdline
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-19 11:15:10 +08:00
Andy Green
3d4353650c windows plat correct assert test in lws_poll_listen_fd
After https://github.com/warmcat/libwebsockets/issues/430

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-18 21:01:27 +08:00
Andy Green
d7fddadaec context creation info add ecdh_curve
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-18 20:36:55 +08:00
Andy Green
79afbe34de test server SSL STS header delivery example
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-18 20:36:40 +08:00
Andy Green
a6d411fa92 test server allow only best quality ciphers
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-18 20:36:23 +08:00
Andy Green
78773b6da0 ssl add server ecdh curve init
Using "real" SSL certs requires some init for openssl ECDH
curve.  Add a default curve "prime256v1" and allow overriding it
at context creation time.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-18 20:36:09 +08:00
Andy Green
d32bb055ea test server allow external certs
Allow the test server to use external certs for testing

libwebsockets-test-server --ssl -C libwebsockets.org.crt -K libwebsockets.org.key -A libwebsockets.org.cer

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-17 11:58:51 +08:00
Andy Green
a0ca2d098b ssl ecdh check errors properly
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-17 11:09:58 +08:00
Andy Green
ff69648ae2 more documentation typos
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-17 07:46:27 +08:00
Peter Pentchev
834cb8525e Fix some typos. 2016-02-16 21:47:56 +08:00
Andy Green
bcb5ec5f4e debug reduce noise
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-16 18:48:10 +08:00
Andy Green
9c60ed9d07 v1.7.0
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-16 13:20:46 +08:00
Andy Green
722da0b500 appveyor update openssl url
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-16 13:20:30 +08:00
Andy Green
0b85a64bf0 coverity 158147 test ping sprintf bounds
Well, just in the test app arg processing, but yes...

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 21:09:00 +08:00
Andy Green
0fba8e6ef8 coverity 158146 test fraggle sprintf bounds
Well, just in the test app arg processing, but yes...

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 21:07:09 +08:00
Andy Green
27966c29e1 coverity 158145 lws_header_table_detatch must handle no ah attached
callers should protect it so this doesn't make a problem.  But
Coverity is correct the code is confused about it.

Make it okay if we close a connection before the ah got attached.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 21:03:51 +08:00
Andy Green
1e32c2b67a release checklist coverity
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 21:03:51 +08:00
Andy Green
c62d882939 api doc update pre 1.7
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 21:03:51 +08:00
Andy Green
200a6a296e timeout settable from info
This adds an info member that allows the user code to
set the library's network action timeout in seconds.

If left at the default 0, the build-time default
AWAITING_TIMEOUT continues to be used.

As suggested

https://github.com/warmcat/libwebsockets/issues/427

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 20:39:07 +08:00
Andy Green
eb91ad0964 release checklist api updates
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 20:39:07 +08:00
Andy Green
c9259876d2 http_transaction_completed handle two completions in detach reset order
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 19:05:43 +08:00
Andy Green
2c218e705f ah owns rxbuf
This is intended to solve a longstanding problem with the
relationship between http/1.1 keep-alive and the service
loop.

Ah now contain an rx buffer which is used during header
processing, and the ah may not be detached from the wsi
until the rx buffer is exhausted.

Having the rx buffer in the ah means we can delay using the
rx until a later service loop.

Ah which have pending rx force POLLIN service on the wsi
they are attached to automatically, so we can interleave
general service / connections with draining each ah rx
buffer.

The possible http/1.1 situations and their dispositions are:

 1) exactly one set of http headers come.  After processing,
    the ah is detached since no pending rx left.  If more
    headers come later, a fresh ah is aqcuired when available
    and the rx flow control blocks the read until then.

 2) more that one whole set of headers come and we remain in
    http mode (no upgrade).  The ah is left attached and
    returns to the service loop after the first set of headers.
    We will get forced service due to the ah having pending
    content (respecting flowcontrol) and process the pending
    rx in the ah.  If we use it all up, we will detach the
    ah.

 3) one set of http headers come with ws traffic appended.
    We service the headers, do the upgrade, and keep the ah
    until the remaining ws content is used.  When we
    exhausted the ws traffix in the ah rx buffer, we
    detach the ah.

Since there can be any amount of http/1.1 pipelining on a
connection, and each may be expensive to service, it's now
enforced there is a return to the service loop after each
header set is serviced on a connection.

When I added the forced service for ah with pending buffering,
I added support for it to the windows plat code.  However this
is untested.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 14:57:55 +08:00
Andy Green
8acdd2e7ed autobahn wait for all fork completion
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 14:57:55 +08:00
Andy Green
b414d52066 autobahn put back 2.10 2.11
The test ordering breaks if we try to skip them, just
leave them in.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 14:57:55 +08:00
Andy Green
42f93ffbfa attack.sh add http1.1 pipelining check
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 14:57:55 +08:00
Andy Green
21f128433b release checklist qa
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-15 12:37:23 +08:00
Andy Green
86ed65ff00 libuv integration
This gets the libuv stuff plumbed in and working.

Currently it's only workable for some service thread, and there
is an isolated valgrind problem left

==28425== 128 bytes in 1 blocks are definitely lost in loss record 3 of 3
==28425==    at 0x4C28C50: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==28425==    by 0x4C2AB1E: realloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==28425==    by 0x58BBB27: maybe_resize (core.c:748)
==28425==    by 0x58BBB27: uv__io_start (core.c:787)
==28425==    by 0x58C1B80: uv__signal_loop_once_init (signal.c:225)
==28425==    by 0x58C1B80: uv_signal_init (signal.c:260)
==28425==    by 0x58BF7A6: uv_loop_init (loop.c:66)
==28425==    by 0x4157F5: lws_uv_initloop (libuv.c:89)
==28425==    by 0x405536: main (test-server-libuv.c:284)

libuv wants to sign off on all libuv 'handles' that will close, and
callback to do the close confirmation asynchronously.  The wsi close function
is adapted when libuv is in use to work with libuv accordingly and exit the uv
loop the number of remaining wsi is zero.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-14 09:31:13 +08:00
Alex Hultman
19bb09133d port test-server-libuv.c from libev to libuv 2016-02-14 09:28:28 +08:00
Alex Hultman
a43c2ac272 libuv initial replace libev version 2016-02-14 07:31:44 +08:00
Andy Green
082e33b91f release-checklist 2016-02-13 12:08:30 +08:00
Dan Albert
cd5e7bff82 Ask sysconf for Android's getdtablesize. 2016-02-12 08:58:16 +08:00
f9267170a3 fixed LWS_PRE description 2016-02-09 15:52:32 +08:00
Andy Green
502521e728 specfile add fuzxy
https://github.com/warmcat/libwebsockets/issues/419

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-09 09:37:11 +08:00
Andy Green
892f03a7d8 lws_reset_header_table dont reset NULL
This is only a temporary, fragile solution.

https://github.com/warmcat/libwebsockets/issues/416

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-09 09:15:02 +08:00
Andy Green
083c73e7e9 license clarification and test apps CC zero
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-08 08:44:21 +08:00
Andy Green
d2c140c8ed lws_parse_uri fix test client use and add more docs
https://github.com/warmcat/libwebsockets/issues/414

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-07 07:28:21 +08:00
Andy Green
99e876d8f7 ah pool take care about freeing headers after parsing
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-06 09:00:09 +08:00
Andy Green
1b89b933a6 debug spew remove ah reset logging
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-03 07:06:52 +08:00
Andy Green
4939a708f8 LWS_WARN_DEPRECATED and fixup older test apps
Enforce no more internal use of deprecated apis (esp in the test apps)

Also signal clearly to users what is on the way out.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-02 09:02:24 +08:00
Andy Green
70e065b5db autobahn add test script
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-02-01 17:38:15 +08:00
Andy Green
8e1b7cb692 attack.sh exit 0 on success
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-31 11:53:49 +08:00
Andy Green
4019aab8da ah http1.1 deal with pipelined headers properly
Connections must hold an ah for the whole time they are
processing one header set, even if eg, the headers are
fragmented and it involves network roundtrip times.

However on http1.1 / keepalive, it must drop the ah when
there are no more header sets to deal with, and reacquire
the ah later when more data appears.  It's because the
time between header sets / http1.1 requests is unbounded
and the ah would be tied up forever.

But in the case that we got pipelined http1.1 requests,
even partial already buffered, we must keep the ah,
resetting it instead of dropping it.  Because we store
the rx data conveniently in a per-tsi buffer since it only
does one thing at a time per thread, we cannot go back to
the event loop to await a new ah inside one service action.

But no problem since we definitely already have an ah,
let's just reuse it at http completion time if more rx is
already buffered.

NB: attack.sh makes request with echo | nc, this
accidentally sends a trailing '\n' from the echo showing
this problem.  With this patch attack.sh can complete well.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-30 11:43:10 +08:00
Andy Green
1b2c9a23e1 clean pre 1.7
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 23:17:43 +08:00
Andy Green
ada3531aff coverity 157401 dont test SHUTDOWN state in mode switch
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 23:17:40 +08:00
Andy Green
e6dbaa236a coverity 157402 explicitly disallow handling invalid sockfd
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 15:40:08 +08:00
Andy Green
6f4e2d297b coverity 157403 fuzxy handle currently impossible error
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 15:35:01 +08:00
Andy Green
b39a151c9c coverity 157404 get rid of needless NULL check
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 15:30:05 +08:00
Andy Green
92f96f3edf cleanup test app startup messages
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 09:35:58 +08:00
Andy Green
4bcbfe1ad4 restrict SO_REUSEPORT to multithreading only so no unexpected change otherwise
SO_REUSEPORT means you don't get any error any more if another
server instance is already running, this will be quite unexpected
for singlethreaded users.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 09:20:00 +08:00
Andy Green
6dd7e86f23 http1.1 keepalive drop ah betweentimes
https://github.com/warmcat/libwebsockets/issues/404

Tested with

wget -O- http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html

and confirm no connection processing during that on server side

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 09:06:22 +08:00
Andy Green
9c444d2709 win dont redefine _WINSOCK_DEPRECATED_NO_WARNINGS
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-29 01:26:31 +08:00
Danomi Czaski
4e9c7f3504 lextable add x-real-ip
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-28 09:40:53 +08:00
Andy Green
8d5351a0c4 reduce debug logging mark socket dead when pollert
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-27 08:50:31 +08:00
Andy Green
ba119e9057 lws_adopt_socket
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-26 21:40:32 +08:00
Andy Green
8c1f6026a7 multithread stability
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-26 20:56:56 +08:00
Andy Green
dcbe30a28e mbed simplify
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-26 20:45:10 +08:00
Andy Green
aa3c8cd371 pthreads only as needed
This just lets you build lws 1.6 without pthreads if your OS / toolchain
makes that possible, in the case you don't build the test apps
(libwebsockets-test-server-pthreads needs it)

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-25 21:39:07 +08:00
Andy Green
0b09734953 fuzxy some toolchains need sys socket.h
https://github.com/warmcat/libwebsockets/issues/407

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-25 21:00:37 +08:00
Andy Green
9666e1d438 fuzzer handle junk after upgrade header
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-21 10:59:07 +08:00
Andy Green
5a0e7866d3 unify bounds checking in parser
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-21 10:57:39 +08:00
Andy Green
7d83bf93e4 fuzxy add test parser and some tests
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-21 10:56:26 +08:00
Andy Green
cb17ad8740 win add dummy LWS_WARN_UNUSED_RESULT
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-21 10:56:20 +08:00
Andy Green
5086597878 mbed align with pt changes
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-20 17:35:18 +08:00
Andy Green
e99a83cb96 introduce LWS_WARN_UNUSED_RESULT
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-20 16:56:06 +08:00
Andy Green
af607da082 header fragment reject empty early
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-20 09:44:04 +08:00
Andy Green
f38ad33ddf tolower remove
https://github.com/warmcat/libwebsockets/issues/403

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-20 07:55:41 +08:00
Andy Green
bc4012916d parser issue_char audit
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-20 07:40:13 +08:00
Andy Green
05ae649b66 timeout also fixup for0middle guy deletion case
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 23:11:39 +08:00
Andy Green
72dba09932 timeout check take copies since he may be deleted
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 22:34:44 +08:00
Andy Green
ed6b3961c5 test server http dont print junk if string too long
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 22:20:18 +08:00
Andy Green
1f8ec7c865 coverity 156864 fuzxy close socket if connect fails
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 21:44:20 +08:00
Andy Green
4ccc13d3ee coverity 156863 fuzxy handle cant get socket
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 21:42:13 +08:00
Andy Green
e34d4b4b35 coverity 156860 wsi dereference before NULL check
... bit in reality, wsi can never be NULL

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 21:33:52 +08:00
Andy Green
2e3bf06139 coverity 156861 context destroy dereference context before NULL check
Context could be NULL only if context creation failed in the first
place and user error path is bad... no network connectivity at that
point...

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 21:33:52 +08:00
Andy Green
2ec7c85e99 coverity 156862 unused return value
Not exactly a problem...

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 21:27:44 +08:00
Andy Green
d3a5505542 multithreaded service
This adds support for multithreaded service to lws without adding any
threading or locking code in the library.

At context creation time you can request split the service part of the
context into n service domains, which are load-balanced so that the most
idle one gets the next listen socket accept.

There's a single listen socket on one port still.

User code may then spawn n threads doing n service loops / poll()s
simultaneously.  Locking is only required (I think) in the existing
FD lock callbacks already handled by the pthreads server example,
and that locking takes place in user code.  So the library remains
completely agnostic about the threading / locking scheme.

And by default, it's completely compatible with one service thread
so no changes are required by people uninterested in multithreaded
service.

However for people interested in extremely lightweight mass http[s]/
ws[s] service with minimum provisioning, the library can now do
everything out of the box.

To test it, just try

$ libwebsockets-test-server-pthreads -j 8

where -j controls the number of service threads

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 20:02:36 +08:00
Andy Green
3f63560876 fix broken ext arg parsing
This gets us back to all the Autobahn tests passing

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 20:02:34 +08:00
Andy Green
d738f84ed1 timeout move to doubly linked list
In the case we have a lot of connections, checking them all for timeout state
once a second becomes burdensome.  At the moment if you have 100K connections,
once a second they all get checked for timeout in a loop.

This patch adds a doubly-linked list based in the context to each wsi, and
only wsi with pending timeouts appear on it.  At checking time, we traverse
the list, which costs nothing if empty because nobody has a pending timeout.

Similarly adding and removing from the list costs almost nothing since no
iteration is required no matter how big the list.

The extra 8 or 16 bytes in the wsi are offset a little bit by demoting .pps
from int to char (save 3 bytes).  And trim max act exts to 2, since we only
provide one, saving 8 /16 bytes by itself if exts enabled.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-19 04:32:14 +08:00
Andrew Cooks
15c92b1bf6 From 8dc784c121fb387f1871245b3194eae662bddfa3 Mon Sep 17 00:00:00 2001
Subject: [PATCH] bump version in rpm spec file.
2016-01-18 12:11:23 +08:00
Andrew Cooks
2a6df60fdc From 1e933db07bed7a0b5ac8dc49146815348ae37ded Mon Sep 17 00:00:00 2001
Subject: [PATCH] rpm specfile fixes: group, license, build requires, files
2016-01-18 12:10:34 +08:00
Andrew Cooks
cdcf5fc9c0 From 7f39ea761e98ea96f79a7e69d4cdeee0c39d316e Mon Sep 17 00:00:00 2001
Subject: [PATCH] fix missing ${LIB_SUFFIX} on cmake config dir
2016-01-18 12:09:43 +08:00
Peter Pentchev
2a0dbcf7ee Generate the API documentation in a reproducible way.
Sort the list of source files before passing them to the kernel-doc
script so that it always outputs the discovered functions and
structures in the same order.
2016-01-18 12:07:07 +08:00
Andy Green
9a9d5eaeeb avoid using deallocated things during context dedtroy
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-18 12:07:07 +08:00
Andy Green
4319b4d78d fuzxy
This is the initial push of a fuzzing proxy we will use for testing lws.

Run libwebsockets-test-fuzxy and the test server if it's local.

Then run the test client with

http_proxy=localhost:8880 libwebsockets-test-client localhost (or whatever)

Right now he only fuzzes one thing but he is operational as a proxy.
2016-01-18 11:16:25 +08:00
Andy Green
a547554aa2 cleaning
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-18 11:16:20 +08:00
Andy Green
1d719ec87f lws_interface_to_sa
Expose interface handling code as an api
2016-01-16 12:09:38 +08:00
Andy Green
d5060d25a1 use lws_close not raw close
https://github.com/warmcat/libwebsockets/issues/400

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-16 11:14:46 +08:00
David Andrade
ec1296a667 Subject: [PATCH] Make echo test app option consistent with the other test
options
2016-01-15 22:48:17 +08:00
Andy Green
29cfeebcde permessage deflate male callback VISIBLE
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-15 22:16:54 +08:00
Andy Green
7c507e4700 fix client getting hung up on at negotiation
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-15 19:34:33 +08:00
Andy Green
809d69aa85 redirect
This adds redirect support to the client side.  Lws will follow
server redirects (301) up to three deep.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-15 19:32:47 +08:00
Andy Green
7a0fcf2fc5 parse_uri
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-15 19:32:47 +08:00
Vijay Khurdiya
e73d446461 ssl add ECDH server support
(AG clean style add option flag and docs)
2016-01-15 16:21:51 +08:00
Andy Green
7c15eb1e42 only report CLIENT_CONNECTION_ERROR once on some paths
In most cases the close api will see it should send the CCE because
we are still in the waiting server reply state until the end of the
interpretation.  Only if we completed the interpretation and moved
on to ESTABLISHED do we need to handle sending it ourselves.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-14 11:38:31 +08:00
Andy Green
389a4bb9f0 remove double free attempts from no ACCEPT server response cleanup path
The generic wsi close code is smart enough to clean up after these allocations itself

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-13 18:48:50 +08:00
Andy Green
8308073464 fuzzer eliminate hsecond ah free path firing assert sentinel
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-13 05:01:17 +08:00
Andy Green
9928cccdb6 fuzzer rx overflow mitigate
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-12 23:05:02 +08:00
Andy Green
5b3736682d lws_client_connect_via_info
https://github.com/warmcat/libwebsockets/issues/396

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-12 17:22:06 +08:00
Andy Green
b6289d1153 test client remove abs modulo
llvm and gcc 5.3 do different things with it

https://github.com/warmcat/libwebsockets/issues/395

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-12 08:46:56 +08:00
Andy Green
6711266a50 extension permessage deflate
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-11 11:34:01 +08:00
Andy Green
58ad3d6e09 clean libev.c style
Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-09 08:13:55 +08:00
Andy Green
5783b1a169 libev set events to match revents
https://github.com/warmcat/libwebsockets/issues/393

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-09 04:12:46 +08:00
Andy Green
5eeb4bd0d3 partial always use partial buffering if send incomplete
After "rerelease"

https://github.com/warmcat/libwebsockets/issues/392#issuecomment-170003294

Since we introduced partial buffering a long while ago,
user code shold never see partial sends and very few
user callbsck attempt to deal with them.

Let's just eliminate the whole concept of user callback
partial send handling under any circumstances.

Signed-off-by: Andy Green <andy.green@linaro.org>
2016-01-08 21:52:47 +08:00
Denis Osvald
27174e64de properly skip protocols with NULL name
Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-01-07 06:57:59 +08:00
Denis Osvald
034e514a0d fix callback typedefs and declarations
Remove declarations of callback and extension_callback as these are
functions declared in header but not defined anywhere.

Also rename typedefs callback_function and extension_callback_function
to lws_callback_function and lws_extension_callback_function so all
symbolx exported by header have lws prefix;

Signed-off-by: Denis Osvald <denis.osvald@sartura.hr>
2016-01-07 06:44:38 +08:00
Andy Green
0c7e5a9418 doc improve docs around header access apis
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-30 14:53:50 +08:00
Andy Green
2b35e123f4 ext negotiation tolerate semicolon args
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-30 12:12:58 +08:00
Andy Green
86c1ef1e7c utf8 check compatible with extensions
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-30 11:43:36 +08:00
Andy Green
9b81d3c967 optimize utf8 check tests into state bit
Considering we go through it once per incoming char, the tests to see if we
should be checking utf-8 are too expensive... move them to a bit that lives
in the wsi and set them once per frame (except for CLOSE who has to update
after the close code has been skipped).

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-29 12:28:48 +08:00
Andy Green
44a7f65e1a introduce LWS_SERVER_OPTION_VALIDATE_UTF8
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-29 11:20:09 +08:00
Andy Green
0c7b38b144 autobahn check utf8 on TEXT and CLOSE
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-29 09:46:03 +08:00
Andy Green
7c2868486e autobahn reject forbidden close code ranges
Complain like autobahn expects if we get a funny close code

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 22:29:57 +08:00
Andy Green
fde3684384 autobahn README.test apps.md
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 22:29:57 +08:00
Andy Green
91d624e38a autobahn reject noncontinuation based on pending FIN state
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 22:29:57 +08:00
Andy Green
977734ee07 autobahn detect disordered continuation
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 22:29:57 +08:00
Andy Green
44e0b088fa autobahn add same serverside rxflow cache to client
Server side has had immediate RX flow control for quite a while.

But client side made do with RX continuing until what had been received was exhausted.

For what Autobahn tests, that's not enough.

This patch gives clientside RX flow control the same immediate effect as the server
side enjoys, re-using the same code.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 21:47:46 +08:00
Andy Green
f05167dee6 autobahn reject on reserved opcode or bits
Because extensions may use them, we didn't reject on reserved opc or bits set,
just ignored.  But the standard does say we should, so now we do.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 20:53:43 +08:00
Andy Green
d5be3bf749 autobahn test echo extend max echo and follow message boundary
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 13:43:59 +08:00
Andy Green
7dbf21efc2 autobahn extend max ping pong close payload to 125
We only supported those specific control packet payloads up to 124.
125 is the correct limit.

Lws was consistent about the wrong limit so there are no other
issues.  It doesn't affect user ABI correcting it either.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 13:40:54 +08:00
Andy Green
41434fad53 autobahn report final frag only on final rx bufload
If the final message fragment contains a payload that has to be
handled in multiple RX callbacks, until now we reported the ws
fragment header FIN state in lws_is_final_fragment().

That was correct, but it's kind of not useful to hear that the
intermediate bufferloads are "final".  So now we delay
reporting the logical ws fragment FIN until the final part of
his payload is delivered.

This gets us Autobahn 1.1.6 working.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 12:45:52 +08:00
Andy Green
4c9d895768 autobahn requires zero length tx allowed
Again we treat user code sending zero length things as a bug in user code.

But Autobahn insists to be able to do it, so now we allow it.

That buys us a pass on Autobahn test 1.1.1 (the first of a bazillion)

Reproduce with

libwebsockets-test-echo --client localhost --port 9001 -u "/runCase?case=1&agent=libwebsockets" -v -d 65535 -n 1

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 11:14:04 +08:00
Andy Green
20db310908 autobahn requires zero length rx allowed
We suppress zero length rx from getting to userland, but autobahn
requires it... oh well.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 11:12:05 +08:00
Andy Green
e7d8e20f56 autobahn test echo meddling
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-28 11:12:05 +08:00
Andy Green
07f194686f clean windows warnings
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-27 18:16:32 +08:00
Andy Green
40e607b876 test server libev
If we enabled libev support, generate a test server variant that uses it.

Libev has sets its face against fixing its warnings and says -Werror is
"stupid".  So we work around it for the problems their apis cause in
Travis.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-27 09:43:44 +08:00
Andy Green
4e2ac7685d clarify changelog
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 20:26:11 +08:00
Andy Green
daa6b8ff25 update api docs
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 17:36:29 +08:00
Andy Green
1fb95e8084 close add api to control sent close frame contents
This adds an api lws_close_reason() which lets you control what will
be sent in the close frame when the connection is closed by returning
nonzero from the user callback.

The test server demo is extended to prove it works in both directions.

With this, we should have nice close support.

https://github.com/warmcat/libwebsockets/issues/196

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 17:20:34 +08:00
Andy Green
066a7a1801 close add callback to get peer close reason
https://github.com/warmcat/libwebsockets/issues/196

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 15:47:06 +08:00
Andy Green
a24b40860f mbed3 remove forcing _DEBUG
It saves us ~4KB of lwsl_info / _debug etc strings.

The test app comes in at 114KB then, including 19KB of html, png and ico assets.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 15:07:45 +08:00
Andy Green
5b85e39d99 mbed3 remove unused allocations
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 13:23:11 +08:00
Andy Green
3246ebb3f5 deprecate LWS_SEND_BUFFER_POST_PADDING
The only guy who cared about this for a long while
(since I eliminated the pre-standard protocol variants)
was sending a close frame.

 - Set it to 0 so old code remains happy.  It only affects
user code buffer commit, if there's overcommit no harm
done so no effect directly on user ABI.

 - Remove all uses inside the library.  The sample apps
don't have it any more and that's the recommendation now.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 12:03:06 +08:00
Andy Green
de1a6a539f output eliminate redundant post calculation
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 10:07:17 +08:00
Andy Green
87bac69f55 remove MAX_MUX_RECURSION from PRE padding
The old google mux thing is long dead

This only affects app buffer sizing, if old apps overcommit, no worries.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 09:08:06 +08:00
Andy Green
aa775fd9b0 rearrange private struct packing 2
Further reduces lws size to 512 on x86_64 "for free"

Both this and the last patch only rearrange private struct members.

Also convert win32-specific member from BOOL to bitfield:1.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-26 08:56:58 +08:00
Peter Pentchev
da2eab3f66 Recognize Debian GNU/kFreeBSD as FreeBSD-ish.
Some of the FreeBSD-specific code in libwebsockets is related to
the FreeBSD kernel, not the general build environment.  Thus, it is
important to make this distinction, especially when building on
platforms that have a FreeBSD kernel and a non-FreeBSD userland build
environment, such as Debian GNU/kFreeBSD.

When checking for FreeBSD kernel features, also check for the newly
introduced __FreeBSD_kernel__ preprocessor constant; it is present in
the GNU/kFreeBSD kernel and also in FreeBSD itself since the 9.1 release
about three years ago.
2015-12-25 21:15:21 +08:00
Andy Green
de132b94b1 rearrange struct packing
Surprisingly it's enough to reduce wsi from 536 to 520 on x86_64

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-25 14:49:54 +08:00
Andy Green
3df580066b http header malloc pool implement pool
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-25 14:34:20 +08:00
Andy Green
b3d21f164d http header malloc pool allow listen accept flow control
Add a private api to enable and disable server listen socket POLLIN

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-25 14:34:13 +08:00
Andy Green
ba38a7e6b4 ws ping buffer allocate in wsi ws union member
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-25 14:34:10 +08:00
Andy Green
a4244f08ad info struct add padding pool
The info struct is too fragile against additions being able to keep soname.

Because if we add something, the library can't count on the user code being
built against latest headers with largest info struct size.  Then the user
code may not have zeroed down enough of the struct and give us junk in the
new members.

Add a pool at the end of the info struct that exists so it will be zeroed
down even though no current use for those future members, then later
library versions can compatibly use them without breaking soname if it is
understood 0 means default.

Because keeping sizeof info straight if you add something is now a thing,
also add an lwsl_info letting you confirm it easily.

It's fine if the size of info differs on different platforms.  But when
we add things to the struct we need to balance the padding using a scheme
like

       short  new_member;
       unsigned char _padding1[sizeof(void *) - sizeof(short)];

which is immune to differences in platform differences in sizeof void *.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-25 09:23:25 +08:00
Andy Green
c35b36b1cf detect service tid once and use wsi with valid context to do it
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-24 13:00:54 +08:00
Andy Green
400e5a7fed libev complete unix plat context init
https://github.com/warmcat/libwebsockets/issues/381

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-23 11:55:06 +08:00
gaby64
d8383ca5fc libev context destroy
https://github.com/warmcat/libwebsockets/issues/380
2015-12-22 12:41:12 +08:00
Andy Green
8933eaf2f3 test ping android compatibility
After "emptyVoid" at

https://github.com/warmcat/libwebsockets/issues/379

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-22 08:17:34 +08:00
Roger A. Light
cc5dff9bb1 Subject: [PATCH] Typo in macro name. 2015-12-19 10:47:12 +08:00
Andy Green
e974f0c252 uridecoding lws_hdr_fragment_length
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-19 07:35:23 +08:00
Andy Green
ab5ed3c8e1 whitespace tidy
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-19 07:18:55 +08:00
Andy Green
9f54c1ff73 uridecode no need to require length plus 2
length + 1 (for the '\0' is enough)

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 16:40:02 +08:00
Andy Green
3ba035dc2c uridecoding disallow uriencoded equals in name part
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 15:40:03 +08:00
Andy Green
8b9fe99dee uridecoding handle plus chars as space
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 15:23:31 +08:00
Andy Green
03d7e9d331 uridecoding support optional semicolon as delimiter
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 15:20:09 +08:00
Andy Green
f1cf5bec53 version 1.6.0
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 11:19:45 +08:00
Andy Green
1849e5e74a api doc v1.6.0
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 11:18:56 +08:00
Andy Green
9e8d148912 changelog pre v1.6.0
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 11:01:03 +08:00
Andy Green
4588e4e415 clean uri arg dump
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 10:46:24 +08:00
Andy Green
e70c63ba8f context protocol destroy provide nonnull wsi with context
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 01:08:14 +08:00
Andy Green
77ec61e44f server check cb return during establish
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 00:56:31 +08:00
Andy Green
da46eeea6b client check cb return during establish
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-18 00:50:14 +08:00
Andy Green
6d64539fcb lws_get_context not _ctx
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 18:25:25 +08:00
Andy Green
54806b1541 clean internal refactor
- Mainly symbol length reduction
 - Whitespace clean
 - Code refactor for linear flow
 - Audit @Context for API docs vs changes

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 17:03:59 +08:00
Andy Green
ddd79cbbb5 win cover clearing sock_send_blocking when external poll
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 15:35:41 +08:00
Andy Green
eee0d8af5f win solve various cranky problems with msvc
After emptyVoid at --->

https://github.com/warmcat/libwebsockets/issues/374

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 15:15:12 +08:00
Andy Green
71e267574d clean misc 1
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 08:30:06 +08:00
Andy Green
00c6d1579c public api remove context from user callback API BREAK
Since struct lws (wsi) now has his own context pointer,
we were able to remove the need for passing context
almost everywhere in the apis.

In turn, that means there's no real use for context being
passed to every callback; in the rare cases context is
needed user code can get it with lws_get_ctx(wsi)

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-17 07:54:44 +08:00
Andy Green
375a193ff4 windows fix wsapoll conflict on some toolchains
After EmptyVoid at github

https://github.com/warmcat/libwebsockets/issues/374

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-16 19:51:12 +08:00
Andy Green
11c05bfa09 public api remove superfluous context params API BREAK
Extend the cleanout caused by wsi having a context pointer
into the public api.

There's no point keeping the 1.5 compatibility work,
we have changed the api in several places and
rebuilt wasn't going to be enough a while ago.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-16 18:19:08 +08:00
Andy Green
8a97c06cd1 add LWS_INLINE to deal with crappy msvc
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-16 15:02:47 +08:00
Andy Green
4eb36373d7 http uri arguments process in fragments
This makes the URI argument processing split each parameter into
a "fragment".  Processing header content as fragments already exists
in lws, because it's legal to deliver header content by repeating
the header.

Now there's an api to access individual fragments, also add the
code to the test server to print each URI argument separately.

Adapt attack.sh to parse the fragments.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 23:07:31 +08:00
Andy Green
f35801b19d URIPS_ARGUMENTS is redundant
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 23:05:23 +08:00
Andy Green
fac92eb7cb document how the header fragments work
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 23:03:29 +08:00
Andy Green
566eb4381c introduce lws_hdr_copy_fragment
This adds a public API variant of the header copy api that lets you
choose which fragment you want copied.

Normally you want the existing one that aggregates the fragments.

But it can be useful to get each part in turn (that corresponds to
the content provided by each duplicated header normally).

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 22:59:23 +08:00
Andy Green
6b5de70f4f refactor needless context with wsi paramater passing
Now we bit the bullet and gave each wsi an lws_context *, many
internal apis that take both a context and wsi parameter only
need the wsi.

Also simplify parser code by making a temp var for
allocated_headers * instead of the longwinded
dereference chain everywhere.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 21:15:58 +08:00
Roger A. Light
e59908e7fc Subject: [PATCH] Add LWS_LIBRARY_VERSION_NUMBER to lws_config.h.
This changeset adds a few preprocessor macros to lws_config.h to allow
a user of libwebsockets to determine at compile time which version of
lws they are compiling against.

This exposes the already existing LWS_LIBRARY_VERSION_MAJOR and _MINOR
values, and adds LWS_LIBRARY_VERSION_PATCH. This suggests that future
minor bugfix release versions of lws would be e.g. 1.6.0 -> 1.6.1 rather
than the style used previously: 1.2 -> 1.21.

The way this is currently set up means new minor revisions (with
_PATCH==0) always end with .0 but I could change this if preferred.

The most important addition is LWS_LIBRARY_VERSION_NUMBER, which
produces a number of the form 1005001 for version 1.5.1 - i.e. each part
major, minor, patch can extend from 0-999. This macro allows a very easy
compile time comparison of version numbers.
2015-12-15 19:49:55 +08:00
Andrejs Hanins
ea592fa869 Fix corruption of close reason code in close frame
According to specification, close reason code is part of body
2015-12-15 19:46:33 +08:00
Andy Green
3decfe60ab mbed3 use new upstream mbed3 nagle disable
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-15 19:46:28 +08:00
Andy Green
38f3225b22 uriencoding deal with0uriencoded question mark properly
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 19:42:26 +08:00
Andy Green
ab4a94dd46 win align to recent changes
These were warnings seen on Appveyor

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 12:00:20 +08:00
Andy Green
1fa7685a28 mbed3 align to recent changes
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 11:17:16 +08:00
wmarquesr
c718ff30d8 Refactoring conditional directives for if wrappers 2015-12-14 09:39:56 +08:00
Andy Green
40110e84ab whitespace trailing mass cleanout
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 08:52:03 +08:00
Andy Green
a01fb52d09 libev take care about const context where possible
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 07:51:15 +08:00
Andy Green
3f59996e67 changelog update file api about wsi
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 07:21:42 +08:00
Andy Green
3f62870e67 introduce lws_wsi_user
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 07:16:32 +08:00
Andy Green
1a366bf946 changelog explain protocols related api changes
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 07:02:51 +08:00
Andy Green
891628b33c lws_plat_fd add wsi to fops and helpers
Having the lws_context alone doesn't let us track state or act different
by wsi, which is the most interesting usecase.  Eg not only simply track
file position / decompression state per wsi but also act differently
according to wsi authentication state / associated cookies.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 06:43:35 +08:00
Andy Green
d2ac22c27a make protocols const require explicit context API BREAK
The user protocols struct has not been const until now.

This has been painful for a while because the semantics of the protocols
struct look like it's going to be treated as const.

At context creation, the protocols struct has been getting marked with the context,
and three apis exploited that to only need to be passed a pointer to a protocol to
get access to the context.

This patch removes the two writeable members in the context (these were never directly
used by user code), changes all pointers to protocols to be const, and adds an explicit
first argument to the three affected apis so they can have access to context.

The three affected apis are these

 LWS_VISIBLE LWS_EXTERN int
-lws_callback_on_writable_all_protocol(const struct lws_protocols *protocol);
+lws_callback_on_writable_all_protocol(const struct lws_context *context,
+                                     const struct lws_protocols *protocol);

 LWS_VISIBLE LWS_EXTERN int
-lws_callback_all_protocol(const struct lws_protocols *protocol, int reason);
+lws_callback_all_protocol(struct lws_context *context,
+                         const struct lws_protocols *protocol, int reason);

 LWS_VISIBLE LWS_EXTERN void
-lws_rx_flow_allow_all_protocol(const struct lws_protocols *protocol);
+lws_rx_flow_allow_all_protocol(const struct lws_context *context,
+                              const struct lws_protocols *protocol);

unfortunately the original apis can no longer be emulated and users of them must update.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 06:43:26 +08:00
Andy Green
8203be6742 lws_get_ctx conversion
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 06:43:24 +08:00
Andy Green
0161f01082 win open flags 3 lsb are not bitfields
https://github.com/warmcat/libwebsockets/issues/367

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-14 06:40:53 +08:00
Yusuke Ishiguro
a951396cc2 Revert using AI_V4MAPPED flag to getaddrinfo for Android
Because getaddrinfo will return error on Android when
AI_V4MAPPED is specified. So we should use old implemntation.
Android support of IPv6 is very poor.
2015-12-11 17:37:49 +08:00
Andy Green
21da5613c6 windows fcntl.h
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 13:21:07 +08:00
Andy Green
cca0d7d27f LWS_O_RDONLY to hide perversions
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 13:03:10 +08:00
Andy Green
4812551234 osx fix unsigned signed compare error 2
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 13:00:15 +08:00
Andy Green
d1c6d0bb06 windows use right perversion flgs
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 12:56:46 +08:00
Andy Green
95181d96a7 osx fix unsigned signed compare error
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 12:50:10 +08:00
Andy Green
4e442b7743 lws_plat_fd implement platform default handlers
This is a rewrite of the patch from Soapyman here

https://github.com/warmcat/libwebsockets/pull/363

The main changes compared to Soapyman's original patch are

 - There's no new stuff in the info struct user code does any overrides
   it may want to do explicitly after lws_context_create returns

 - User overrides for file ops can call through (subclass) to the original
   platform implementation using lws_get_fops_plat()

 - A typedef is provided for plat-specific fd type

 - Public helpers are provided to allow user code to be platform-independent
   about file access, using the lws platform file operations underneath:

static inline lws_filefd_type
lws_plat_file_open(struct lws_plat_file_ops *fops, const char *filename,
		   unsigned long *filelen, int flags)

static inline int
lws_plat_file_close(struct lws_plat_file_ops *fops, lws_filefd_type fd)

static inline unsigned long
lws_plat_file_seek_cur(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		       long offset_from_cur_pos)

static inline int
lws_plat_file_read(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		   unsigned long *amount, unsigned char *buf, unsigned long len)

static inline int
lws_plat_file_write(struct lws_plat_file_ops *fops, lws_filefd_type fd,
		    unsigned long *amount, unsigned char *buf, unsigned long len)

There's example documentation and implementation in the test server.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 11:01:59 +08:00
SoapyMan
11260dac18 lws_plat_fd introduce struct
Originally from

https://github.com/warmcat/libwebsockets/pull/363

Modified by AG to change the emphasis to exporting lws plat
handlers for use by the user code portably
2015-12-10 11:01:59 +08:00
Andy Green
4e512dd3a9 test client remove spamming delays
Chrome seems to be able to deal with this (or my machine is now
muscular enough it doesn't care, anyway)

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 11:01:20 +08:00
Andy Green
510c3c86c0 clean comment style in libwebsockets.h
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:50:51 +08:00
Andy Green
4fd5ce3361 armour libwebsockets.h also put notices on abi structs in there
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:24:20 +08:00
Andy Green
4386e36b0b plat combine inits into single lws_plat_init and provide info
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-10 07:14:16 +08:00
Yusuke Ishiguro
4fee9de6bc Subject: [PATCH] fixed not to use IPv4-mapped address for IPv6 only node
Since IPv6 only node can not connect with IPv4-mapped address.
2015-12-09 19:04:17 +08:00
Andy Green
cb0cbfaec1 test client bit more cleaning
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-09 08:07:38 +08:00
Alexander Bruines
8266450ba2 Access to wsi->ssl at LWS_CALLBACK_ESTABLISHED 2015-12-09 07:27:15 +08:00
Andy Green
e97378960f test client reconnect if server disappears
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-09 07:10:03 +08:00
Yusuke Ishiguro
ee435adb7c fixed to initialize sockaddr_in6 by zero
Some environment has field of sin6_scope_id inside sockaddr_in6
and it should be set to zero.
2015-12-08 19:57:00 +08:00
Andy Green
ac19bf6801 soname bump
See

http://ml.libwebsockets.org/pipermail/libwebsockets/2015-December/002052.html

for the reason

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-08 11:06:28 +08:00
Andy Green
dc0731b3a5 armour libwebsockets.h against careless enum changes affecting abi
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-08 11:04:19 +08:00
Charles Prevot
90a0dd6b75 cmake additionally install cmake config 2015-12-08 10:26:57 +08:00
Andy Green
0fd63763f6 coverity 155650 medium possible write to null pointer
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 11:07:41 +08:00
Andy Green
e96b2c680a coverity 155649 medium possible write to null pointer
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 11:04:05 +08:00
Andy Green
ef951221d6 coverity 155648 low dead code daemonize disabled
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 11:00:36 +08:00
Andy Green
9b780806f0 travis coverity update
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 10:43:02 +08:00
Andy Green
576d3ec0df clean more whitespace 5
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 10:05:37 +08:00
Andy Green
dc8a3a817a clean more whitespace 4
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 09:15:27 +08:00
Andy Green
d478fb8c38 clean more whitespace 3
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 08:40:00 +08:00
Andy Green
1cc03887f4 clean reduce windows build warnings
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 08:00:03 +08:00
Andy Green
6ab6ee2c0f more server close processing error handling precisions
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 06:39:51 +08:00
Andy Green
819d418bf2 server socket service close when detected do right thing
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 05:55:52 +08:00
Andy Green
d607bb9bfe clean more whitespace 2
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-06 05:52:09 +08:00
Andy Green
e53f2ed5e6 clean more whitespace
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-05 21:52:41 +08:00
Andy Green
f012c4423c server socket service close fix fail detect
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-05 21:52:16 +08:00
Andy Green
dd6aaa898f windows align lws_service_fd return processing with unix
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-05 09:38:50 +08:00
Andy Green
dc6e47cafc cleanups after api changes and mbed update
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 16:54:12 +08:00
Andy Green
9c9f2180f3 attack.sh update for test server changes
Currently he can survive all the tests correctly

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 12:04:59 +08:00
Andy Green
cef609df80 fix Uri Args header name
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 11:57:48 +08:00
Andy Green
02d60d6d21 api rationalization fix replaces in compatibility defines
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 11:34:49 +08:00
Andy Green
5c9660da61 clean tidy the worst whitespace alignment probs due to mass token name length changes
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 11:30:53 +08:00
Andy Green
4b85c1d4ac api rationalization: eliminate all libwebsocket[s]_ prefixes
This nukes all the oldstyle prefixes except in the compatibility code.

struct libwebsockets becomes struct lws too.

The api docs are updated accordingly as are the READMEs that mention
those apis.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 11:08:32 +08:00
Andy Green
6d41720233 api rationalization add cmake switch to export old api wrappers
This is off by default, use

 -D LWS_WITH_OLD_API_WRAPPERS=1

on cmake to get the old api names exported from the library as wrappers
around the new api names.

This allows the library to continue to be compatible with apps that are
not rebuilt with the new libwebsockets.h api compatibility defines.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 10:39:23 +08:00
Andy Green
3ef579b4f9 api rationalization eliminate oldstyle internal api names
Between changing to lws_ a few years ago and the previous two
patches migrating the public apis, there are only a few
internal functions left using libwebsocket_*.

Change those to also use lws_ without regard to compatibility
since they were never visible outside the library.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 09:23:56 +08:00
Andy Green
6230476455 api rationalization use new names internally
Change all internal uses of rationalized public apis to reflect the
new names.

Theer are a few things that got changed as side effect of search/replace
matches, but these are almost all internal.  I added a compatibility define
for the public enum that got renamed.

Theoretically existing code should not notice the difference from these
two patches.  And new code will find the new names.

https://github.com/warmcat/libwebsockets/issues/357

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 08:43:54 +08:00
Andy Green
29a44cf2ab api rationalization: introduce public api compatibility defines
Just this is enough to be buildable and allow usage of new defines
for the public api.

https://github.com/warmcat/libwebsockets/issues/357

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 07:55:17 +08:00
Andy Green
112f9806ac assert.h move to private header
https://github.com/warmcat/libwebsockets/issues/356

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-04 07:22:44 +08:00
Peter Pentchev
bb085dab04 Fix some minor typos. 2015-12-03 22:32:59 +08:00
Andy Green
972eaf91a0 ssl zero return indicates shutdown
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-12-03 21:37:34 +08:00
Andrew Canaday
622d9f2ff3 Handle pending SSL reads which would otherwise not trigger a POLLIN. 2015-12-03 09:45:18 +08:00
Andrew Canaday
ad2248a207 ssl set ssl to NULL on close 2015-12-03 09:44:15 +08:00
Andrejs Hanins
765914cab1 Allow zero-length pong to be received by server
Client code already allows zero-length pongs
2015-12-01 20:59:52 +08:00
Andy Green
abc8635824 osx clang blows up if pthreads flag at link time 2
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-29 20:17:49 +08:00
Andy Green
09b8a71b14 osx clang blows up if pthreads flag at link time
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-29 19:41:13 +08:00
Andrew Canaday
f411d8e1d8 Define 'daemonize' flag if LWS_NO_DAEMONIZE is not defined. 2015-11-29 19:32:02 +08:00
Andrew Canaday
ffe64567da C89 tweaks as per #348. 2015-11-29 19:26:01 +08:00
Andrew Canaday
4cfc42cdaa Terminate truncated header strings. 2015-11-29 19:24:04 +08:00
Andy Green
1700265081 osx clang quench deprecated api errors
after approach by redhat

https://bugzilla.redhat.com/show_bug.cgi?id=1155181

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-29 19:19:09 +08:00
Andy Green
b39c22fc38 issue 352 clang is like gcc for cmake purposes
After ohauer

https://github.com/warmcat/libwebsockets/issues/352

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-29 18:52:37 +08:00
Andy Green
c78fe91564 test server h add newline
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-25 12:46:08 +08:00
Andy Green
87eeb0a8bd mbed3 working examples
You need sal-stack-lwip > 1.0.4 with the listen fix

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-25 08:22:08 +08:00
Andy Green
1c6e7bffb7 ssl treat WANT_READ and WRITE separately
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-24 16:54:55 +08:00
Andy Green
ae7b27c77a clean out mbed3 lwip hacks
this upstream mbed patch on sal-stack-lwip sorts the listen probs

a8adf15739.patch

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-24 16:07:18 +08:00
Andy Green
0a05792d8d http post zero content length
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-20 19:31:57 +08:00
Andy Green
c0b0c3d1e2 return AWAITING_TIMEOUT to 5s
Debugging MBED set it to 20 for a few days

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-20 18:52:48 +08:00
Andy Green
b7fed3532a test server pthreads
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-20 09:51:42 +08:00
Andy Green
5a3b1d307b CALLBACK_LOCK_POLL use len to differentiate locking on pollfd change
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-20 09:51:18 +08:00
Andy Green
adf9059d82 windows crappy tools dont know __func__
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-19 17:14:35 +08:00
Andy Green
eb15ea0198 refactor test server
Split test-server into four C files and a header

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-19 13:55:47 +08:00
Andy Green
9494c66d20 win remove piggybacked hack
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-19 08:43:51 +08:00
Andy Green
f2280d6ce7 proxy auth fix
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-18 19:32:01 +08:00
Andy Green
d55ac45c39 windows eliminate duplicate POLLOUT
After "hotcookie" on github

https://github.com/warmcat/libwebsockets/issues/345

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-18 09:51:07 +08:00
Andy Green
0779964ec1 test echo initial delay
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-17 09:30:36 +08:00
Andy Green
649f602a18 close actually close after send close ack
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-15 10:28:45 +08:00
Andy Green
974bed41b8 test html add open close buttons
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-15 09:24:25 +08:00
Andy Green
bdaa86ff3c README.build.md add mbed3 build info
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:47:42 +08:00
Andy Green
758d97fa10 update appveyor to get win openssl from own server
slproweb is down

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:33:21 +08:00
Andy Green
11f27345d2 mbed3 workable plus or minus mbed3 net stack bug
https://github.com/ARMmbed/sockets/issues/35

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:33:21 +08:00
Andy Green
8c0d3c035c mbed3 plat
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:31:59 +08:00
Andy Green
2cd3074746 mbed3 warning cleaning
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:31:01 +08:00
Andy Green
3b19386b10 introduce lws_sockfd_type
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 16:30:37 +08:00
Andy Green
5f2a8155f1 mbed3 build support
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 15:01:37 +08:00
Andy Green
618b7b75ac mbed3 add yotta JSON
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 15:01:37 +08:00
Andy Green
fc772ccc00 win use platform invalid socket api elsewhere too
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 13:48:58 +08:00
Andy Green
c53f7cad97 win client use platform invalid socket
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 07:35:27 +08:00
Andy Green
35fef0534b plat win be robust against NULL wsi_from_fd 3
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-14 07:02:38 +08:00
Andy Green
1064cd7619 plat win be robust against NULL wsi_from_fd 2
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-13 11:43:53 +08:00
Andy Green
46d9b8e06f plat win be robust against NULL wsi_from_fd
After "haitetra"

https://github.com/warmcat/libwebsockets/issues/343

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-13 10:14:50 +08:00
Andy Green
9ffb42efec cmake force internal SHA1 if no ssl
https://github.com/warmcat/libwebsockets/issues/342

You have to explicitly disable LWS_WITHOUT_BUILTIN_SHA1 Cmake option
alomg with SSL to disable SSL

cmake .. -DLWS_WITH_SSL=OFF -DLWS_WITHOUT_BUILTIN_SHA1=OFF

This makes that implicit with disabling SSL.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-09 04:24:46 +08:00
Andy Green
6e405565f5 proxy auth
Simplifies proxy code to use the existing libwebsocket_set_proxy.

Enables libwebsocket_set_proxy() to parse username:password@ at front of
servername in both http_proxy and info->http_proxy_address.

If given the base64 version of the credentials are sent in the CONNECT
header to the proxy.

Port is now taken from info->http_proxy_address server:port syntax, but if
a port is given in the now deprecated info->http_proxy_port (ie, is nonzero)
then it is allowed to be missed out and the info port used instead for
backwards compatibility.

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-08 10:15:01 +08:00
Andrejs Hanins
140ac6e9cb Subject: [PATCH] Fix for close ack sending
It was forgotten in two places that pending close ack should be
processed when wsi state is WSI_STATE_RETURNED_CLOSE_ALREADY, but
not WSI_STATE_ESTABLISHED. As a result, close ack wasn't sent out
to the peer.
2015-11-07 07:04:46 +08:00
Andy Green
79a3c5d425 non ssl on ssl port zero recv ambiguous
Signed-off-by: Andy Green <andy.green@linaro.org>
2015-11-06 08:23:05 +08:00
Andy Green
cc64fb535b non ssl on ssl port fixes
As found by 'github user 7'

https://github.com/warmcat/libwebsockets/issues/338

Signed-off-by: Andy Green <andy.green@linaro.org>
2015-10-31 06:49:05 +08:00
198 changed files with 55272 additions and 13136 deletions

3
.gitignore vendored
View file

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

View file

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

File diff suppressed because it is too large Load diff

32
Kconfig Normal file
View file

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

34
LICENSE
View file

@ -1,7 +1,13 @@
Libwebsockets and included programs are provided under the terms of the GNU
Library General Public License (LGPL) 2.1, with the following exceptions:
1) Static linking of programs with the libwebsockets library does not
1) Any reference, whether in these modifications or in the GNU
Library General Public License 2.1, to this License, these terms, the
GNU Lesser Public License, GNU Library General Public License, LGPL, or
any similar reference shall refer to the GNU Library General Public
License 2.1 as modified by these paragraphs 1) through 4).
2) Static linking of programs with the libwebsockets library does not
constitute a derivative work and does not require the author to provide
source code for the program, use the shared libwebsockets libraries, or
link their program against a user-supplied version of libwebsockets.
@ -10,7 +16,7 @@ If you link the program to a modified version of libwebsockets, then the
changes to libwebsockets must be provided under the terms of the LGPL in
sections 1, 2, and 4.
2) You do not have to provide a copy of the libwebsockets license with
3) You do not have to provide a copy of the libwebsockets license with
programs that are linked to the libwebsockets library, nor do you have to
identify the libwebsockets license in your program or documentation as
required by section 6 of the LGPL.
@ -20,8 +26,30 @@ following example statement can be included in user documentation to
satisfy this requirement:
"[program] is based in part on the work of the libwebsockets project
(http://libwebsockets.org)"
(https://libwebsockets.org)"
4) Some sources included have their own, more liberal licenses, or options
to get original sources with the liberal terms.
Original liberal license retained
- lib/sha-1.c - 3-clause BSD license retained, link to original
- win32port/zlib - ZLIB license (see zlib.h)
Relicensed to libwebsocket license
- lib/base64-decode.c - relicensed to LGPL2.1+SLE, link to original
- lib/daemonize.c - relicensed from Public Domain to LGPL2.1+SLE,
link to original Public Domain version
Public Domain (CC-zero) to simplify reuse
- test-server/*.c
- test-server/*.h
- lwsws/*
------ end of exceptions
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

View file

@ -1,5 +1,8 @@
Introduction to CMake
---------------------
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
@ -10,7 +13,7 @@ create elaborate clean scripts to get a clean source tree, instead you
simply remove your build directory.
Libwebsockets has been tested to build successfully on the following platforms
with SSL support (both OpenSSL/wolfSSL):
with SSL support (for OpenSSL/wolfSSL/BoringSSL):
- Windows (Visual Studio)
- Windows (MinGW)
@ -18,8 +21,8 @@ with SSL support (both OpenSSL/wolfSSL):
- OSX
- NetBSD
Building the library and test apps
----------------------------------
@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
@ -29,8 +32,8 @@ the available generators for your platform, simply run the "cmake" command.
Note that by default OpenSSL will be linked, if you don't want SSL support
see below on how to toggle compile options.
Building on Unix:
-----------------
@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)
@ -38,69 +41,109 @@ Building on Unix:
2. Install OpenSSL.
3. Generate the build files (default is Make files):
```
$ cd /path/to/src
$ mkdir build
$ cd build
$ cmake ..
```
```bash
$ 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 ".."
(**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 .
```
**NOTE2**:
A common option you may want to give is to set the install path, same
as --prefix= with autotools. It defaults to /usr/local.
You can do this by, eg
```bash
$ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ..
```
**NOTE3**:
On machines that want libraries in lib64, you can also add the
following to the cmake line
```bash
**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,
```bash
**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
```bash
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
```bash
$ make
```
```
Quirk of cmake
--------------
@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.
Building on Windows (Visual Studio)
-----------------------------------
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
@ -113,12 +156,12 @@ Building on Windows (Visual Studio)
3. Generate the Visual studio project by opening the Visual Studio cmd prompt:
```bash
cd <path to src>
md build
cd build
cmake -G "Visual Studio 10" ..
```
```
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)
@ -129,35 +172,49 @@ Building on Windows (Visual Studio)
4. Now you should have a generated Visual Studio Solution in your
`<path to src>/build` directory, which can be used to build.
Building on Windows (MinGW)
---------------------------
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) Add the following lines to C:\MinGW\include\winsock2.h:
```c
#if(_WIN32_WINNT >= 0x0600)
a) If still necessary, sdd the following lines to C:\MinGW\include\winsock2.h:
```
#if(_WIN32_WINNT >= 0x0600)
typedef struct pollfd {
typedef struct pollfd {
SOCKET fd;
SHORT events;
SHORT revents;
SOCKET fd;
SHORT events;
SHORT revents;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
WINSOCK_API_LINKAGE int WSAAPI WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout);
#endif // (_WIN32_WINNT >= 0x0600)
```
#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:
http://wine-unstable.sourcearchive.com/documentation/1.1.32/mstcpip_8h-source.html
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
@ -170,14 +227,12 @@ Building on Windows (MinGW)
<OpenSSL install location>\bin\openssl.cfg
5. Generate the build files (default is Make files) using MSYS shell:
```bash
$ cd /drive/path/to/src
$ mkdir build
$ cd build
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW ..
```
```
$ 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)
@ -186,27 +241,35 @@ Building on Windows (MinGW)
**NOTE2**:
To generate build files allowing to create libwebsockets binaries with debug information
set the CMAKE_BUILD_TYPE flag to DEBUG:
```bash
$ cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=C:/MinGW -DCMAKE_BUILD_TYPE=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:
```bash
$ make
$ make install
```
```
$ make
$ make install
```
Setting compile options
-----------------------
@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 command line.
or do it via the command line.
Command line
------------
To list avaialable options (ommit the H if you don't want the help text):
@subsection cmcocl Command line
To list available options (omit the H if you don't want the help text):
cmake -LH ..
@ -216,20 +279,21 @@ Then to set an option and build (for example turn off SSL support):
or
cmake -DLWS_WITH_SSL:BOOL=OFF ..
Unix GUI
--------
@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
Windows GUI
-----------
@subsection cmcowg Windows GUI
On windows CMake comes with a gui application:
Start -> Programs -> CMake -> CMake (cmake-gui)
wolfSSL/CyaSSL replacement for OpenSSL
--------------------------------------
@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
@ -239,57 +303,102 @@ 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.
Compiling libwebsockets with wolfSSL
------------------------------------
@section wolf1 Compiling libwebsockets with wolfSSL
```bash
cmake .. -DLWS_USE_WOLFSSL=1 \
-DLWS_WOLFSSL_INCLUDE_DIRS=/path/to/wolfssl \
-DLWS_WOLFSSL_LIBRARIES=/path/to/wolfssl/wolfssl.a ..
```
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.
Compiling libwebsockets with CyaSSL
-----------------------------------
@section cya Compiling libwebsockets with CyaSSL
```bash
cmake .. -DLWS_USE_CYASSL=1 \
-DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \
-DLWS_CYASSL_LIBRARIES=/path/to/wolfssl/cyassl.a ..
```
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.
Reproducing HTTP2.0 tests
-------------------------
@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
```bash
lwsts[4752]: Compiled with OpenSSL support
lwsts[4752]: Using SSL mode
lwsts[4752]: HTTP2 / ALPN enabled
```
lwsts[4752]: Compiled with OpenSSL support
lwsts[4752]: Using SSL mode
lwsts[4752]: HTTP2 / ALPN enabled
```
at lws startup.
For non-SSL HTTP2.0 upgrade
```bash
$ nghttp -nvasu http://localhost:7681/test.htm
```
$ nghttp -nvasu http://localhost:7681/test.htm
```
For SSL / ALPN HTTP2.0 upgrade
```
$ nghttp -nvas https://localhost:7681/test.html
$ nghttp -nvas https://localhost:7681/test.html
```
Cross compiling
---------------
@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
@ -299,13 +408,11 @@ to look for dependencies and such.
you can use as a starting point.
The commandline to configure for cross with this would look like
```bash
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
-DWITHOUT_EXTENSIONS=1 -DWITH_SSL=0
```
$ cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DCMAKE_TOOLCHAIN_FILE=../cross-arm-linux-gnueabihf.cmake \
-DLWS_WITHOUT_EXTENSIONS=1 -DLWS_WITH_SSL=0
```
The example shows how to build with no external cross lib dependencies, you
need to provide the cross libraries otherwise.
@ -315,25 +422,22 @@ need to provide the cross libraries otherwise.
Additional information on cross compilation with CMake:
http://www.vtk.org/Wiki/CMake_Cross_Compiling
Memory efficiency
-----------------
@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)
```bash
$ ./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
```
$ ./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.

View file

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

23
README.esp32.md Normal file
View file

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

34
README.esp8266.md Normal file
View file

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

373
README.generic-sessions.md Normal file
View file

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

219
README.generic-table.md Normal file
View file

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

192
README.lws-meta.md Normal file
View file

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

601
README.lwsws.md Normal file
View file

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

View file

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

43
README.problems.md Normal file
View file

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

View file

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

View file

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

41
autobahn-test.sh Executable file
View file

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

1392
changelog

File diff suppressed because it is too large Load diff

41
component.mk Normal file
View file

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

52
contrib/abi/README.md Normal file
View file

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

View file

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

17
contrib/abi/lws-abi-update.sh Executable file
View file

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

114
contrib/android-make-script.sh Executable file
View file

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

32
cross-aarch64.cmake Normal file
View file

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

46
cross-esp32.cmake Normal file
View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -1,9 +1,61 @@
#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)
return realloc(ptr, 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;
@ -28,3 +80,4 @@ void lws_set_allocator(void *(*cb)(void *ptr, size_t size))
{
_lws_realloc = cb;
}
#endif

View file

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

File diff suppressed because it is too large Load diff

View file

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

1382
lib/client.c Normal file → Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

669
lib/fops-zip.c Normal file
View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -49,26 +49,30 @@
#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
libwebsocket_read(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char *buf, size_t len)
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;
int body_chunk_len;
unsigned char *last_char;
lwsl_debug("%s: incoming len %d state %d\n", __func__, (int)len, wsi->state);
switch (wsi->state) {
#ifdef LWS_USE_HTTP2
case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
case WSI_STATE_HTTP2_ESTABLISHED:
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
n = 0;
while (n < len) {
/*
@ -83,55 +87,78 @@ libwebsocket_read(struct libwebsocket_context *context,
/* account for what we're using in rxflow buffer */
if (wsi->rxflow_buffer)
wsi->rxflow_pos++;
if (lws_http2_parser(context, wsi, buf[n++]))
if (lws_http2_parser(wsi, buf[n++])) {
lwsl_debug("%s: http2_parser bailed\n", __func__);
goto bail;
}
}
break;
#endif
http_new:
case WSI_STATE_HTTP:
case LWSS_HTTP_ISSUING_FILE:
return 0;
case LWSS_CLIENT_HTTP_ESTABLISHED:
break;
case LWSS_HTTP:
wsi->hdr_parsing_completed = 0;
/* fallthru */
case WSI_STATE_HTTP_ISSUING_FILE:
wsi->state = WSI_STATE_HTTP_HEADERS;
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
/* fallthru */
case WSI_STATE_HTTP_HEADERS:
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, len))
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
last_char = buf;
if (lws_handshake_server(context, wsi, &buf, len))
if (lws_handshake_server(wsi, &buf, (size_t)len))
/* Handshake indicates this session is done. */
goto bail;
/* It's possible that we've exhausted our data already, but
* lws_handshake_server doesn't update len for us. Figure out how
* much was read, so that we can proceed appropriately: */
/* 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 WSI_STATE_HTTP:
case WSI_STATE_HTTP_HEADERS:
goto http_complete;
case WSI_STATE_HTTP_ISSUING_FILE:
case LWSS_HTTP:
case LWSS_HTTP_HEADERS:
goto read_ok;
case WSI_STATE_HTTP_BODY:
wsi->u.http.content_remain = wsi->u.http.content_length;
goto http_postbody;
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 WSI_STATE_HTTP_BODY:
case LWSS_HTTP_BODY:
http_postbody:
while (len && wsi->u.http.content_remain) {
/* Copy as much as possible, up to the limit of:
@ -141,44 +168,75 @@ http_postbody:
body_chunk_len = min(wsi->u.http.content_remain,len);
wsi->u.http.content_remain -= body_chunk_len;
len -= body_chunk_len;
#ifdef LWS_WITH_CGI
if (wsi->cgi) {
struct lws_cgi_args args;
if (wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
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, body_chunk_len);
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;
}
buf += body_chunk_len;
if (!wsi->u.http.content_remain) {
/* he sent the content in time */
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
if (wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
if (n)
goto bail;
}
goto http_complete;
} else
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_HTTP_CONTENT,
AWAITING_TIMEOUT);
break;
}
break;
case WSI_STATE_ESTABLISHED:
case WSI_STATE_AWAITING_CLOSE_ACK:
if (lws_handshake_client(wsi, &buf, len))
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 LWS_CONNMODE_WS_SERVING:
case LWSCM_WS_SERVING:
if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
@ -186,35 +244,19 @@ http_postbody:
}
break;
default:
lwsl_err("libwebsocket_read: Unhandled state\n");
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
break;
}
read_ok:
/* Nothing more to do for now. */
lwsl_debug("libwebsocket_read: read_ok\n");
/* Nothing more to do for now */
lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
return 0;
http_complete:
lwsl_debug("libwebsocket_read: http_complete\n");
#ifndef LWS_NO_SERVER
/* Did the client want to keep the HTTP connection going? */
if (lws_http_transaction_completed(wsi))
goto bail;
#endif
/* If we have more data, loop back around: */
if (len)
goto http_new;
return 0;
return buf - oldbuf;
bail:
lwsl_debug("closing connection at libwebsocket_read bail:\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
//lwsl_notice("closing connection at lws_read bail:\n");
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

View file

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

View file

@ -189,51 +189,55 @@ static int huftable_decode(int pos, char c)
return pos + (lextable[q] << 1);
}
static int lws_hpack_update_table_size(struct libwebsocket *wsi, int idx)
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 libwebsocket *wsi, int hdr_token_idx)
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)
if (!hdr_token_idx) {
lwsl_err("%s: zero hdr_token_idx\n", __func__);
return 1;
if (ah->next_frag_index >= ARRAY_SIZE(ah->frag_index))
return 1;
ah->frags[ah->next_frag_index].offset = ah->pos;
ah->frags[ah->next_frag_index].len = 0;
ah->frags[ah->next_frag_index].next_frag_index = 0;
}
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;
ah->frag_index[hdr_token_idx] = ah->next_frag_index;
return 0;
}
static int lws_frag_append(struct libwebsocket *wsi, unsigned char c)
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->next_frag_index].len++;
return ah->pos >= sizeof(ah->data);
ah->frags[ah->nfrag].len++;
return ah->pos >= wsi->context->max_http_header_data;
}
static int lws_frag_end(struct libwebsocket *wsi)
static int lws_frag_end(struct lws *wsi)
{
if (lws_frag_append(wsi, 0))
return 1;
wsi->u.http2.http.ah->next_frag_index++;
wsi->u.http2.http.ah->nfrag++;
return 0;
}
static void lws_dump_header(struct libwebsocket *wsi, int hdr)
static void lws_dump_header(struct lws *wsi, int hdr)
{
char s[200];
int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
@ -241,14 +245,15 @@ static void lws_dump_header(struct libwebsocket *wsi, int hdr)
lwsl_info(" hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
}
static int lws_token_from_index(struct libwebsocket *wsi, int index, char **arg, int *len)
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))
@ -256,24 +261,25 @@ static int lws_token_from_index(struct libwebsocket *wsi, int index, char **arg,
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 libwebsocket *wsi, int token, char *arg, int len)
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;
@ -282,7 +288,7 @@ static int lws_hpack_add_dynamic_header(struct libwebsocket *wsi, int token, cha
if (!dyn)
return 1;
wsi->u.http2.hpack_dyn_table = dyn;
dyn->args = lws_malloc(1024);
if (!dyn->args)
goto bail1;
@ -292,24 +298,25 @@ static int lws_hpack_add_dynamic_header(struct libwebsocket *wsi, int token, cha
goto bail2;
dyn->num_entries = 20;
}
if (dyn->next == dyn->num_entries)
return 1;
if (dyn->args_length - dyn->pos < len)
return 1;
dyn->entries[dyn->next].token = token;
dyn->entries[dyn->next].arg_offset = dyn->pos;
if (len)
memcpy(dyn->args + dyn->pos, arg, len);
dyn->entries[dyn->next].arg_len = len;
lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n", __func__, dyn->next, token, lws_token_to_string(token), len);
lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
__func__, dyn->next, token, lws_token_to_string(token), len);
dyn->pos += len;
dyn->next++;
return 0;
bail2:
@ -321,12 +328,13 @@ bail1:
return ret;
}
static int lws_write_indexed_hdr(struct libwebsocket *wsi, int idx)
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));
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;
@ -339,19 +347,20 @@ static int lws_write_indexed_hdr(struct libwebsocket *wsi, int idx)
}
if (lws_frag_end(wsi))
return 1;
lws_dump_header(wsi, tok);
return 0;
}
int lws_hpack_interpret(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char c)
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;
@ -374,17 +383,18 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
/* 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;
@ -392,6 +402,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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 */
@ -403,6 +414,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
* 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;
@ -418,6 +430,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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;
@ -426,7 +439,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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
*/
@ -436,6 +449,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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;
@ -445,6 +459,7 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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;
@ -465,18 +480,21 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
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;
@ -492,10 +510,13 @@ int lws_hpack_interpret(struct libwebsocket_context *context,
if (wsi->u.http2.hpack_len < 0x7f) {
pre_data:
if (wsi->u.http2.value) {
if (lws_frag_start(wsi,
lws_token_from_index(wsi,
wsi->u.http2.header_index, NULL, NULL)))
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;
@ -504,7 +525,7 @@ pre_data:
wsi->u.http2.hpack_m = 0;
wsi->u.http2.hpack = HPKS_HLEN_EXT;
break;
case HPKS_HLEN_EXT:
wsi->u.http2.hpack_len += (c & 0x7f) <<
wsi->u.http2.hpack_m;
@ -518,10 +539,9 @@ pre_data:
for (n = 0; n < 8; n++) {
if (wsi->u.http2.huff) {
prev = wsi->u.http2.hpack_pos;
wsi->u.http2.hpack_pos =
huftable_decode(
wsi->u.http2.hpack_pos = huftable_decode(
wsi->u.http2.hpack_pos,
(c >> 7) & 1);
(c >> 7) & 1);
c <<= 1;
if (wsi->u.http2.hpack_pos == 0xffff)
return 1;
@ -529,7 +549,7 @@ pre_data:
continue;
c1 = wsi->u.http2.hpack_pos & 0x7fff;
wsi->u.http2.hpack_pos = 0;
if (!c1 && prev == HUFTABLE_0x100_PREV)
; /* EOT */
} else {
@ -537,39 +557,46 @@ pre_data:
c1 = c;
}
if (wsi->u.http2.value) { /* value */
if (lws_frag_append(wsi, c1))
return 1;
if (wsi->u.http2.header_index)
if (lws_frag_append(wsi, c1))
return 1;
} else { /* name */
if (libwebsocket_parse(context, wsi, c1))
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))
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;
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)
// 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)
//if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
wsi->u.http2.value = 1;
wsi->u.http2.hpack = HPKS_HLEN;
}
@ -581,11 +608,12 @@ pre_data:
wsi->u.http2.hpack = HPKS_TYPE;
break;
}
return 0;
}
static int lws_http2_num(int starting_bits, unsigned long num, unsigned char **p, unsigned char *end)
static int lws_http2_num(int starting_bits, unsigned long num,
unsigned char **p, unsigned char *end)
{
int mask = (1 << starting_bits) - 1;
@ -593,11 +621,11 @@ static int lws_http2_num(int starting_bits, unsigned long num, unsigned char **p
*((*p)++) |= num;
return *p >= end;
}
*((*p)++) |= mask;
if (*p >= end)
return 1;
num -= mask;
while (num >= 128) {
*((*p)++) = 0x80 | (num & 0x7f);
@ -605,22 +633,19 @@ static int lws_http2_num(int starting_bits, unsigned long num, unsigned char **p
return 1;
num >>= 7;
}
return 0;
}
int lws_add_http2_header_by_name(struct libwebsocket_context *context,
struct libwebsocket *wsi,
const unsigned char *name,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
int 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] == ':')
@ -640,43 +665,39 @@ int lws_add_http2_header_by_name(struct libwebsocket_context *context,
*(*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 libwebsocket_context *context,
struct libwebsocket *wsi,
enum lws_token_indexes token,
const unsigned char *value,
int length,
unsigned char **p,
unsigned char *end)
int lws_add_http2_header_by_token(struct 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(context, wsi, name, value, length, p, end);
return lws_add_http2_header_by_name(wsi, name, value, length, p, end);
}
int lws_add_http2_header_status(struct libwebsocket_context *context,
struct libwebsocket *wsi,
unsigned int code,
unsigned char **p,
unsigned char *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(context, wsi, WSI_TOKEN_HTTP_COLON_STATUS, status, n, p, end))
if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS,
status, n, p, end))
return 1;
return 0;

View file

@ -38,31 +38,33 @@ void lws_http2_init(struct http2_settings *settings)
memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings));
}
struct libwebsocket *
lws_http2_wsi_from_id(struct libwebsocket *wsi, unsigned int sid)
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 libwebsocket *
lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *parent_wsi, unsigned int sid)
struct lws *
lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi,
unsigned int sid)
{
struct libwebsocket *wsi = libwebsocket_create_new_server_wsi(context);
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])
return NULL;
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;
@ -72,49 +74,59 @@ lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebs
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 = WSI_STATE_HTTP2_ESTABLISHED;
wsi->mode = parent_wsi->mode;
wsi->protocol = &context->protocols[0];
libwebsocket_ensure_user_space(wsi);
lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space);
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 libwebsocket_context *context, struct libwebsocket *wsi)
int lws_remove_server_child_wsi(struct lws_context *context, struct lws *wsi)
{
struct libwebsocket **w = &wsi->u.http2.parent_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)
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) {
@ -125,24 +137,25 @@ lws_http2_interpret_settings_payload(struct http2_settings *settings, unsigned c
len -= LWS_HTTP2_SETTINGS_LENGTH;
buf += LWS_HTTP2_SETTINGS_LENGTH;
}
if (len)
return 1;
return 0;
}
struct libwebsocket *lws_http2_get_network_wsi(struct libwebsocket *wsi)
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 libwebsocket *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf)
int lws_http2_frame_write(struct lws *wsi, int type, int flags,
unsigned int sid, unsigned int len, unsigned char *buf)
{
struct libwebsocket *wsi_eff = lws_http2_get_network_wsi(wsi);
struct lws *wsi_eff = lws_http2_get_network_wsi(wsi);
unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
int n;
@ -155,24 +168,28 @@ int lws_http2_frame_write(struct libwebsocket *wsi, int type, int flags, unsigne
*p++ = sid >> 16;
*p++ = sid >> 8;
*p++ = sid;
lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d\n",
__func__, wsi, wsi_eff, type, flags, sid, len, wsi->u.http2.tx_credit);
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", len, wsi->u.http2.tx_credit);
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);
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 libwebsocket *wsi, int n, unsigned char *buf)
static void lws_http2_settings_write(struct lws *wsi, int n, unsigned char *buf)
{
*buf++ = n >> 8;
*buf++ = n;
@ -186,36 +203,35 @@ static const char * https_client_preface =
"PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a";
int
lws_http2_parser(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char c)
lws_http2_parser(struct lws *wsi, unsigned char c)
{
struct libwebsocket *swsi;
struct lws *swsi;
int n;
//dstruct libwebsocket *wsi_new;
switch (wsi->state) {
case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
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 = WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS;
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(context, wsi, LWS_PPS_HTTP2_MY_SETTINGS);
lws_set_protocol_write_pending(wsi,
LWS_PPS_HTTP2_MY_SETTINGS);
}
break;
case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
case WSI_STATE_HTTP2_ESTABLISHED:
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;
@ -233,8 +249,15 @@ lws_http2_parser(struct libwebsocket_context *context,
case LWS_HTTP2_FRAME_TYPE_CONTINUATION:
case LWS_HTTP2_FRAME_TYPE_HEADERS:
lwsl_info(" %02X\n", c);
if (lws_hpack_interpret(context, wsi->u.http2.stream_wsi, 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) {
@ -269,7 +292,7 @@ lws_http2_parser(struct libwebsocket_context *context,
}
if (wsi->u.http2.count != wsi->u.http2.length)
break;
/* end of frame */
wsi->u.http2.frame_state = 0;
@ -285,26 +308,26 @@ lws_http2_parser(struct libwebsocket_context *context,
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(context, swsi);
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(context, wsi, LWS_PPS_HTTP2_PONG);
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 ((long long)swsi->u.http2.tx_credit + (unsigned long long)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;
libwebsocket_callback_on_writable(context, swsi);
}
lws_callback_on_writable(swsi);
}
break;
}
break;
@ -336,21 +359,21 @@ lws_http2_parser(struct libwebsocket_context *context,
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(context, wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
lws_set_protocol_write_pending(wsi, LWS_PPS_HTTP2_ACK_SETTINGS);
break;
case LWS_HTTP2_FRAME_TYPE_PING:
if (wsi->u.http2.stream_id)
@ -367,23 +390,26 @@ lws_http2_parser(struct libwebsocket_context *context,
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(context, wsi, wsi->u.http2.stream_id);
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;
@ -409,29 +435,29 @@ update_end_headers:
}
break;
}
return 0;
}
int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsocket *wsi)
int lws_http2_do_pps_send(struct lws_context *context, struct lws *wsi)
{
unsigned char settings[LWS_SEND_BUFFER_PRE_PADDING + 6 * LWS_HTTP2_SETTINGS__COUNT];
struct libwebsocket *swsi;
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_SEND_BUFFER_PRE_PADDING + m]);
&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_SEND_BUFFER_PRE_PADDING]);
&settings[LWS_PRE]);
if (n != m) {
lwsl_info("send %d %d\n", n, m);
return 1;
@ -441,34 +467,35 @@ int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsoc
/* send ack ... always empty */
n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS,
1, LWS_HTTP2_STREAM_ID_MASTER, 0,
&settings[LWS_SEND_BUFFER_PRE_PADDING]);
&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 == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS) {
wsi->state = WSI_STATE_HTTP2_ESTABLISHED;
wsi->u.http.fd = LWS_INVALID_FILE;
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 nonexistant ssl upgrade headers\n");
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(context, wsi, 1);
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);
@ -476,15 +503,15 @@ int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsoc
/* demanded by HTTP2 */
swsi->u.http2.END_STREAM = 1;
lwsl_info("servicing initial http request\n");
return lws_http_action(context, swsi);
return lws_http_action(swsi);
}
break;
case LWS_PPS_HTTP2_PONG:
memcpy(&settings[LWS_SEND_BUFFER_PRE_PADDING], wsi->u.http2.ping_payload, 8);
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_SEND_BUFFER_PRE_PADDING]);
&settings[LWS_PRE]);
if (n != 8) {
lwsl_info("send %d %d\n", n, m);
return 1;
@ -493,11 +520,11 @@ int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsoc
default:
break;
}
return 0;
}
struct libwebsocket * lws_http2_get_nth_child(struct libwebsocket *wsi, int n)
struct lws * lws_http2_get_nth_child(struct lws *wsi, int n)
{
do {
wsi = wsi->u.http2.next_child_wsi;

916
lib/lejp-conf.c Normal file
View file

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

709
lib/lejp.c Normal file
View file

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

232
lib/lejp.h Normal file
View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -23,84 +23,92 @@
void lws_feature_status_libev(struct lws_context_creation_info *info)
{
if (info->options & LWS_SERVER_OPTION_LIBEV)
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
libwebsocket_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
static void
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct libwebsocket_pollfd eventfd;
struct lws_io_watcher *lws_io = container_of(watcher, struct lws_io_watcher, watcher);
struct libwebsocket_context *context = lws_io->context;
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)
if (revents & EV_READ) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
if (revents & EV_WRITE)
}
if (revents & EV_WRITE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
libwebsocket_service_fd(context, &eventfd);
}
lws_service_fd(context, &eventfd);
}
LWS_VISIBLE void
libwebsocket_sigint_cb(struct ev_loop *loop,
struct ev_signal *watcher, int revents)
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
{
ev_break(loop, EVBREAK_ALL);
}
LWS_VISIBLE int libwebsocket_sigint_cfg(
struct libwebsocket_context *context,
int use_ev_sigint,
lws_ev_signal_cb* cb)
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 ) {
if (cb)
context->lws_ev_sigint_cb = cb;
}
else {
context->lws_ev_sigint_cb = &libwebsocket_sigint_cb;
};
else
context->lws_ev_sigint_cb = &lws_ev_sigint_cb;
return 0;
};
}
LWS_VISIBLE int
libwebsocket_initloop(
struct libwebsocket_context *context,
struct ev_loop *loop)
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;
const char * backend_name;
struct ev_io *w_accept = &context->w_accept.watcher;
struct ev_signal *w_sigint = &context->w_sigint.watcher;
if (!loop)
loop = ev_default_loop(0);
loop = ev_loop_new(0);
else
context->pt[tsi].ev_loop_foreign = 1;
context->pt[tsi].io_loop_ev = loop;
context->io_loop = loop;
/*
* Initialize the accept w_accept with the listening socket
* and register a callback for read operations:
* Initialize the accept w_accept with all the listening sockets
* and register a callback for read operations
*/
ev_io_init(w_accept, libwebsocket_accept_cb,
context->listen_service_fd, EV_READ);
ev_io_start(context->io_loop,w_accept);
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 has indicated otherwise: */
if( context->use_ev_sigint ) {
/* 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->io_loop,w_sigint);
};
ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint);
}
backend = ev_backend(loop);
switch (backend) {
@ -125,70 +133,101 @@ libwebsocket_initloop(
default:
backend_name = "Unknown libev backend";
break;
};
}
lwsl_notice(" libev backend: %s\n", backend_name);
return status;
}
LWS_VISIBLE void
lws_libev_accept(struct libwebsocket_context *context,
struct libwebsocket *new_wsi, int accept_fd)
void
lws_libev_destroyloop(struct lws_context *context, int tsi)
{
struct ev_io *r = &new_wsi->w_read.watcher;
struct ev_io *w = &new_wsi->w_write.watcher;
struct lws_context_per_thread *pt = &context->pt[tsi];
if (!LWS_LIBEV_ENABLED(context))
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBEV))
return;
new_wsi->w_read.context = context;
new_wsi->w_write.context = context;
ev_io_init(r, libwebsocket_accept_cb, accept_fd, EV_READ);
ev_io_init(w, libwebsocket_accept_cb, accept_fd, EV_WRITE);
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_io(struct libwebsocket_context *context,
struct libwebsocket *wsi, int flags)
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 (!context->io_loop)
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)));
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
if (flags & LWS_EV_START) {
if (flags & LWS_EV_WRITE)
ev_io_start(context->io_loop, &wsi->w_write.watcher);
ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher);
if (flags & LWS_EV_READ)
ev_io_start(context->io_loop, &wsi->w_read.watcher);
ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher);
} else {
if (flags & LWS_EV_WRITE)
ev_io_stop(context->io_loop, &wsi->w_write.watcher);
ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher);
if (flags & LWS_EV_READ)
ev_io_stop(context->io_loop, &wsi->w_read.watcher);
ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher);
}
}
LWS_VISIBLE int
lws_libev_init_fd_table(struct libwebsocket_context *context)
lws_libev_init_fd_table(struct lws_context *context)
{
int n;
if (!LWS_LIBEV_ENABLED(context))
return 0;
context->w_accept.context = context;
context->w_sigint.context = context;
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(struct libwebsocket_context *context)
lws_libev_run(const struct lws_context *context, int tsi)
{
if (context->io_loop && LWS_LIBEV_ENABLED(context))
ev_run(context->io_loop, 0);
if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context))
ev_run(context->pt[tsi].io_loop_ev, 0);
}

249
lib/libevent.c Normal file
View file

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

723
lib/libuv.c Normal file
View file

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

3604
lib/libwebsockets.c Normal file → Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1739
lib/lws-plat-esp32.c Normal file

File diff suppressed because it is too large Load diff

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

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

329
lib/lws-plat-optee.c Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

211
lib/ranges.c Normal file
View file

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

52
lib/rewrite.c Normal file
View file

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

224
lib/romfs.c Normal file
View file

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

63
lib/romfs.h Normal file
View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

241
lib/smtp.c Normal file
View file

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

593
lib/ssl-client.c Normal file
View file

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

View file

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

439
lib/ssl-server.c Normal file
View file

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

1072
lib/ssl.c

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

317
libwebsockets.dox Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

325
lwsws/main.c Normal file
View file

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

View file

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

16
mainpage.md Normal file
View file

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

21
module.json Normal file
View file

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

View file

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

View file

@ -0,0 +1,152 @@
/*
* ws protocol handler plugin for "dumb increment"
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* These test plugins are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*
* This is a copy of dumb_increment adapted slightly to serve as the
* "example-standalone-protocol", to show how to build protocol plugins
* outside the library easily.
*/
#define LWS_DLL
#define LWS_INTERNAL
#include "../lib/libwebsockets.h"
#include <string.h>
struct per_vhost_data__dumb_increment {
uv_timer_t timeout_watcher;
struct lws_context *context;
struct lws_vhost *vhost;
const struct lws_protocols *protocol;
};
struct per_session_data__dumb_increment {
int number;
};
static void
uv_timeout_cb_dumb_increment(uv_timer_t *w
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct per_vhost_data__dumb_increment *vhd = lws_container_of(w,
struct per_vhost_data__dumb_increment, timeout_watcher);
lws_callback_on_writable_all_protocol_vhost(vhd->vhost, vhd->protocol);
}
static int
callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__dumb_increment *pss =
(struct per_session_data__dumb_increment *)user;
struct per_vhost_data__dumb_increment *vhd =
(struct per_vhost_data__dumb_increment *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
unsigned char buf[LWS_PRE + 512];
unsigned char *p = &buf[LWS_PRE];
int n, m;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi),
sizeof(struct per_vhost_data__dumb_increment));
vhd->context = lws_get_context(wsi);
vhd->protocol = lws_get_protocol(wsi);
vhd->vhost = lws_get_vhost(wsi);
uv_timer_init(lws_uv_getloop(vhd->context, 0),
&vhd->timeout_watcher);
uv_timer_start(&vhd->timeout_watcher,
uv_timeout_cb_dumb_increment, 50, 50);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
if (!vhd)
break;
uv_timer_stop(&vhd->timeout_watcher);
break;
case LWS_CALLBACK_ESTABLISHED:
pss->number = 0;
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
n = sprintf((char *)p, "%d", pss->number++);
m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
if (m < n) {
lwsl_err("ERROR %d writing to di socket\n", n);
return -1;
}
break;
case LWS_CALLBACK_RECEIVE:
if (len < 6)
break;
if (strcmp((const char *)in, "reset\n") == 0)
pss->number = 0;
if (strcmp((const char *)in, "closeme\n") == 0) {
lwsl_notice("dumb_inc: closing as requested\n");
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
(unsigned char *)"seeya", 5);
return -1;
}
break;
default:
break;
}
return 0;
}
static const struct lws_protocols protocols[] = {
{
"example-standalone-protocol",
callback_dumb_increment,
sizeof(struct per_session_data__dumb_increment),
10, /* rx buf size must be >= permessage-deflate rx size */
},
};
LWS_VISIBLE int
init_protocol_example_standalone(struct lws_context *context,
struct lws_plugin_capability *c)
{
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
c->api_magic);
return 1;
}
c->protocols = protocols;
c->count_protocols = ARRAY_SIZE(protocols);
c->extensions = NULL;
c->count_extensions = 0;
return 0;
}
LWS_VISIBLE int
destroy_protocol_example_standalone(struct lws_context *context)
{
return 0;
}

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

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